-
1
class ApplicationController < ActionController::Base
-
# Prevent CSRF attacks by raising an exception.
-
# For APIs, you may want to use :null_session instead.
-
1
protect_from_forgery with: :exception
-
-
1
def current_user
-
@current_user ||= User.find(session[:user_id]) if session[:user_id]
-
end
-
end
-
1
class TripsController < ApplicationController
-
1
before_action :set_trip, only: [:show, :edit, :update, :destroy]
-
-
1
def index
-
@trips = current_user.trips.all
-
end
-
-
1
def new
-
2
@trip = Trip.new
-
end
-
-
1
def edit
-
end
-
-
1
def update
-
if @trip.update(trip_params)
-
redirect_to @trip
-
else
-
render 'edit'
-
end
-
end
-
-
1
def create
-
2
@trip = current_user.trips.create(trip_params)
-
-
2
if @trip.save
-
# create a row in the travels table for each post within the selected dates
-
2
start_date = params["trip"]["start_date"]
-
2
end_date = params["trip"]["end_date"]
-
2
@trip.users.each do |user|
-
2
user.posts.each do |post|
-
if post.created_date >= start_date.to_datetime && post.created_date <= end_date.to_datetime
-
Travel.create!(post_id: post.id, trip_id: @trip.id)
-
end
-
end
-
end
-
2
redirect_to trip_path(@trip)
-
else
-
render :new
-
end
-
end
-
-
1
def show
-
@posts = @trip.posts.all.sort_by { |post| post.created_date }
-
@users = User.all
-
@comment = Comment.new
-
end
-
-
1
def destroy
-
if @trip.destroy
-
redirect_to trips_path, notice: 'Trip was successfully deleted.'
-
end
-
end
-
-
1
private
-
-
1
def trip_params
-
2
params.require(:trip).permit(:title, :start_date, :end_date, :user_id)
-
end
-
-
1
def set_trip
-
@trip = Trip.find(params[:id])
-
end
-
end
-
1
module ApplicationHelper
-
end
-
1
module HidepostsHelper
-
end
-
1
module PostsHelper
-
1
def wordy_time(post)
-
from_time = post.created_date.to_time
-
to_time = Time.now
-
distance_of_time_in_words(from_time, to_time, options = {include_seconds: true})
-
end
-
end
-
1
class Post < ActiveRecord::Base
-
1
belongs_to :user
-
1
has_many :travels
-
1
has_many :trips, through: :travels
-
1
has_many :comments
-
-
1
store_accessor :properties, :tweet_url, :tweet_date
-
-
# default_scope, -> { where("properties['created_date'] > ASC") }
-
-
1
def self.login_post_builder(response, user)
-
response["data"].each do |post|
-
unless Post.exists?(source_id: post["id"])
-
new_post = user.posts.new
-
-
new_post_attrs = {}
-
new_post_attrs["longitude"] = post["location"]["longitude"]
-
new_post_attrs["latitude"] = post["location"]["latitude"]
-
new_post_attrs["place_name"] = post["location"]["name"]
-
new_post_attrs["url"] = post["images"]["standard_resolution"]["url"]
-
new_post_attrs["caption"] = post["caption"]["text"] unless post["caption"] == nil
-
-
new_post.properties = new_post_attrs
-
new_post.source = "Instagram"
-
new_post.source_id = post["id"]
-
new_post.created_date = Time.at(post["caption"]["created_time"].to_i).to_datetime unless post["caption"] == nil
-
end
-
end
-
user.save
-
end
-
-
1
def self.tweet_post_builder(response, user)
-
response.each do |post|
-
unless Post.exists?(source_id: post["id"].to_s)
-
new_post = user.posts.new
-
-
new_post_attrs = {}
-
new_post_attrs["text"] = post["text"] unless post["text"] == nil
-
new_post_attrs["image_url"] = post["entities"]["media"].first["media_url"] unless post["entities"]["media"] == nil
-
new_post_attrs["latitude"] = post["geo"]["coordinates"].first unless post["geo"] == nil
-
new_post_attrs["longitude"] = post["geo"]["coordinates"].last unless post["geo"] == nil
-
-
new_post.properties = new_post_attrs
-
new_post.source = "Twitter"
-
new_post.source_id = post["id"].to_s
-
new_post.created_date = post["created_at"].to_datetime
-
end
-
end
-
user.save
-
end
-
end
-
1
class Traveling < ActiveRecord::Base
-
1
belongs_to :user
-
1
belongs_to :trip
-
end
-
1
class Trip < ActiveRecord::Base
-
1
validates :title, presence: true
-
1
validates :start_date, presence: true
-
1
validates :end_date, presence: true
-
-
1
has_many :travelings
-
1
has_many :users, through: :travelings
-
1
has_many :travels
-
1
has_many :posts, through: :travels
-
-
1
def format_start_date
-
self.start_date.strftime("%b %d, %Y")
-
end
-
-
1
def format_end_date
-
self.end_date.strftime("%b %d, %Y")
-
end
-
end
-
1
class User < ActiveRecord::Base
-
1
has_many :posts
-
1
has_many :travelings
-
1
has_many :trips, through: :travelings
-
1
has_many :comments
-
-
1
def self.from_omniauth(auth)
-
where(auth).first_or_create do |user|
-
user.provider = auth['provider']
-
user.uid = auth['uid'].to_i
-
user.name = auth['name']
-
user.nickname = auth['nickname']
-
user.image = auth['image']
-
end
-
end
-
end
-
1
require File.expand_path('../boot', __FILE__)
-
-
# Pick the frameworks you want:
-
1
require "active_model/railtie"
-
1
require "active_record/railtie"
-
1
require "action_controller/railtie"
-
1
require "action_mailer/railtie"
-
1
require "action_view/railtie"
-
1
require "sprockets/railtie"
-
# require "rails/test_unit/railtie"
-
-
# Require the gems listed in Gemfile, including any gems
-
# you've limited to :test, :development, or :production.
-
1
Bundler.require(*Rails.groups)
-
-
1
module TravelHub
-
1
class Application < Rails::Application
-
-
# config.active_record.schema_format=:sql
-
#
-
1
config.serve_static_assets = true
-
# Settings in config/environments/* take precedence over those specified here.
-
# Application configuration should go into files in config/initializers
-
# -- all .rb files in that directory are automatically loaded.
-
-
# Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
-
# Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
-
# config.time_zone = 'Central Time (US & Canada)'
-
-
# The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
-
# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
-
# config.i18n.default_locale = :de
-
-
end
-
end
-
# Set up gems listed in the Gemfile.
-
1
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
-
-
1
require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
-
# Load the Rails application.
-
1
require File.expand_path('../application', __FILE__)
-
-
# Initialize the Rails application.
-
1
Rails.application.initialize!
-
1
Rails.application.configure do
-
# Settings specified here will take precedence over those in config/application.rb.
-
-
# The test environment is used exclusively to run your application's
-
# test suite. You never need to work with it otherwise. Remember that
-
# your test database is "scratch space" for the test suite and is wiped
-
# and recreated between test runs. Don't rely on the data there!
-
1
config.cache_classes = true
-
-
# Do not eager load code on boot. This avoids loading your whole application
-
# just for the purpose of running a single test. If you are using a tool that
-
# preloads Rails for running tests, you may have to set it to true.
-
1
config.eager_load = false
-
-
# Configure static asset server for tests with Cache-Control for performance.
-
1
config.serve_static_assets = true
-
1
config.static_cache_control = 'public, max-age=3600'
-
-
# Show full error reports and disable caching.
-
1
config.consider_all_requests_local = true
-
1
config.action_controller.perform_caching = false
-
-
# Raise exceptions instead of rendering exception templates.
-
1
config.action_dispatch.show_exceptions = false
-
-
# Disable request forgery protection in test environment.
-
1
config.action_controller.allow_forgery_protection = false
-
-
# Tell Action Mailer not to deliver emails to the real world.
-
# The :test delivery method accumulates sent emails in the
-
# ActionMailer::Base.deliveries array.
-
1
config.action_mailer.delivery_method = :test
-
-
# Print deprecation notices to the stderr.
-
1
config.active_support.deprecation = :stderr
-
-
# Raises error for missing translations
-
# config.action_view.raise_on_missing_translations = true
-
end
-
# Be sure to restart your server when you modify this file.
-
-
# Version of your assets, change this if you want to expire all your assets.
-
1
Rails.application.config.assets.version = '1.0'
-
-
# Precompile additional assets.
-
# application.js, application.css, and all non-JS/CSS in app/assets folder are already added.
-
# Rails.application.config.assets.precompile += %w( search.js )
-
# Be sure to restart your server when you modify this file.
-
-
# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
-
# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
-
-
# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
-
# Rails.backtrace_cleaner.remove_silencers!
-
# Be sure to restart your server when you modify this file.
-
-
1
Rails.application.config.action_dispatch.cookies_serializer = :json
-
# Be sure to restart your server when you modify this file.
-
-
# Configure sensitive parameters which will be filtered from the log file.
-
1
Rails.application.config.filter_parameters += [:password]
-
# Be sure to restart your server when you modify this file.
-
-
# Add new inflection rules using the following format. Inflections
-
# are locale specific, and you may define rules for as many different
-
# locales as you wish. All of these examples are active by default:
-
# ActiveSupport::Inflector.inflections(:en) do |inflect|
-
# inflect.plural /^(ox)$/i, '\1en'
-
# inflect.singular /^(ox)en/i, '\1'
-
# inflect.irregular 'person', 'people'
-
# inflect.uncountable %w( fish sheep )
-
# end
-
-
# These inflection rules are supported but not enabled by default:
-
# ActiveSupport::Inflector.inflections(:en) do |inflect|
-
# inflect.acronym 'RESTful'
-
# end
-
# Be sure to restart your server when you modify this file.
-
-
# Add new mime types for use in respond_to blocks:
-
# Mime::Type.register "text/richtext", :rtf
-
1
Rails.application.config.middleware.use OmniAuth::Builder do
-
1
provider :instagram, ENV['INSTAGRAM_ID'], ENV['INSTAGRAM_SECRET']
-
1
provider :twitter, ENV['TWITTER_ID'], ENV['TWITTER_SECRET']
-
end
-
-
# Be sure to restart your server when you modify this file.
-
-
1
Rails.application.config.session_store :cookie_store, key: '_travel_hub_session'
-
# Be sure to restart your server when you modify this file.
-
-
# This file contains settings for ActionController::ParamsWrapper which
-
# is enabled by default.
-
-
# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
-
1
ActiveSupport.on_load(:action_controller) do
-
1
wrap_parameters format: [:json] if respond_to?(:wrap_parameters)
-
end
-
-
# To enable root element in JSON for ActiveRecord objects.
-
# ActiveSupport.on_load(:active_record) do
-
# self.include_root_in_json = true
-
# end
-
1
Rails.application.routes.draw do
-
1
get 'hideposts/index'
-
-
1
root to: 'welcome#home'
-
-
1
get 'feed', to: 'posts#feed', as: 'feed'
-
1
get '/login', to: 'sessions#bounce', as: :login
-
1
get '/logout', to: 'sessions#destroy', as: :logout
-
1
match '/auth/instagram/callback', to: 'sessions#create', via: [:get]
-
1
match '/auth/twitter/callback', to: 'sessions#twitter', via: [:get]
-
1
match '/auth/failure', to: 'sessions#failure', via: [:get]
-
-
1
resources :menu, only: [:index]
-
1
resources :trips
-
1
resources :travelings
-
1
resources :hideposts
-
1
resources :posts do
-
1
resources :comments
-
end
-
-
1
get '/profile/:id', to: 'profiles#show', as: 'profile'
-
-
1
namespace :api do
-
1
namespace :v1 do
-
1
resources :users, only: [:index, :show]
-
1
resources :posts, only: [:index, :show]
-
1
resources :trips, only: [:index, :show] do
-
1
resources :posts, only: [:index, :show]
-
end
-
end
-
end
-
-
end
-
1
module Builders
-
1
def trip_attributes
-
2
@attributes = { title: "Vegas Baby!, Baby?, Uh oh.",
-
start_date: "2014-10-30",
-
end_date: "2014-11-10" }
-
end
-
end
-
1
require 'rails_helper'
-
-
1
RSpec.describe TripsController, type: :controller do
-
1
include Builders
-
3
let(:user) { User.create!(name: "John Doe") }
-
-
1
describe "GET new" do
-
1
it "assigns a new trip @trip" do
-
1
get :new
-
1
expect(assigns(:trip)).to be_a_new(Trip)
-
end
-
-
1
it "renders the new template" do
-
1
get :new
-
1
expect(response).to render_template('new')
-
end
-
end
-
-
1
describe "POST create" do
-
1
describe "with valid params" do
-
1
it "saves the new trip in the database" do
-
1
stub_current_user
-
1
trip_attributes
-
-
2
expect { post :create, trip: @attributes}.to change(Trip, :count).by 1
-
end
-
-
1
it "redirects to the trip's show page" do
-
1
trip_attributes
-
1
stub_current_user
-
1
@trip = post :create, trip: @attributes
-
-
1
expect(response).to redirect_to trip_path(@trip)
-
end
-
end
-
-
1
describe "with invalid params" do
-
1
xit "does not save the new trip in the database" do
-
end
-
-
1
xit "re-renders the :new template" do
-
end
-
end
-
end
-
end
-
# This file is copied to spec/ when you run 'rails generate rspec:install'
-
1
ENV["RAILS_ENV"] ||= 'test'
-
1
require 'spec_helper'
-
1
require File.expand_path("../../config/environment", __FILE__)
-
1
require 'rspec/rails'
-
1
require 'build_helpers'
-
# Add additional requires below this line. Rails is not loaded until this point!
-
-
# Requires supporting ruby files with custom matchers and macros, etc, in
-
# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
-
# run as spec files by default. This means that files in spec/support that end
-
# in _spec.rb will both be required and run as specs, causing the specs to be
-
# run twice. It is recommended that you do not name files matching this glob to
-
# end with _spec.rb. You can configure this pattern with the --pattern
-
# option on the command line or in ~/.rspec, .rspec or `.rspec-local`.
-
#
-
# The following line is provided for convenience purposes. It has the downside
-
# of increasing the boot-up time by auto-requiring all files in the support
-
# directory. Alternatively, in the individual `*_spec.rb` files, manually
-
# require only the support files necessary.
-
-
3
Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
-
-
# Checks for pending migrations before tests are run.
-
# If you are not using ActiveRecord, you can remove this line.
-
1
ActiveRecord::Migration.maintain_test_schema!
-
-
1
RSpec.configure do |config|
-
1
config.include FeatureHelper
-
1
config.extend VCR::RSpec::Macros
-
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
-
1
config.fixture_path = "#{::Rails.root}/spec/fixtures"
-
# If you're not using ActiveRecord, or you'd prefer not to run each of your
-
# examples within a transaction, remove the following line or assign false
-
# instead of true.
-
1
config.use_transactional_fixtures = true
-
-
# RSpec Rails can automatically mix in different behaviours to your tests
-
# based on their file location, for example enabling you to call `get` and
-
# `post` in specs under `spec/controllers`.
-
#
-
# You can disable this behaviour by removing the line below, and instead
-
# explicitly tag your specs with their type, e.g.:
-
#
-
# RSpec.describe UsersController, :type => :controller do
-
# # ...
-
# end
-
#
-
# The different available types are documented in the features, such as in
-
# https://relishapp.com/rspec/rspec-rails/docs
-
1
config.infer_spec_type_from_file_location!
-
end
-
1
module FeatureHelper
-
1
def mock_auth
-
OmniAuth.config.mock_auth[:instagram] = OmniAuth::AuthHash.new(
-
{
-
"provider"=>"instagram",
-
"uid"=>"1546672945",
-
"info"=>
-
{
-
"nickname"=>"notfakemarc",
-
"name"=>"",
-
"image"=>"http://images.ak.instagram.com/profiles/anonymousUser.jpg",
-
"bio"=>"",
-
"website"=>""
-
},
-
"credentials"=>{"token"=>ENV["OAUTH_TEST_TOKEN"], "expires"=>false},
-
"extra"=>{}
-
})
-
end
-
-
1
def log_user_in
-
VCR.use_cassette('login') do
-
visit root_path
-
mock_auth
-
click_link "Sign In With Instagram"
-
end
-
end
-
-
1
def stub_current_user
-
2
allow_any_instance_of(ApplicationController)
-
.to receive(:current_user).and_return(user)
-
end
-
-
1
def instagram_api_response
-
{ "data" =>
-
[{"attribution"=>nil,
-
"tags"=>["besties"],
-
"type"=>"image",
-
"location"=>{"latitude"=>39.74962, "longitude"=>-105.000136111},
-
"comments"=>{"count"=>0, "data"=>[]},
-
"filter"=>"Rise",
-
"created_time"=>"1414804420",
-
"link"=>"http://instagram.com/p/u1pg_qLgtt/",
-
"likes"=>{"count"=>0, "data"=>[]},
-
"images"=>
-
{"low_resolution"=>
-
{"url"=>
-
"http://scontent-a.cdninstagram.com/hphotos-xaf1/t51.2885-15/10731736_396284220521663_1520459156_a.jpg",
-
"width"=>306,
-
"height"=>306},
-
"thumbnail"=>
-
{"url"=>
-
"http://scontent-a.cdninstagram.com/hphotos-xaf1/t51.2885-15/10731736_396284220521663_1520459156_s.jpg",
-
"width"=>150,
-
"height"=>150},
-
"standard_resolution"=>
-
{"url"=>
-
"http://scontent-a.cdninstagram.com/hphotos-xaf1/t51.2885-15/10731736_396284220521663_1520459156_n.jpg",
-
"width"=>640,
-
"height"=>640}},
-
"users_in_photo"=>[],
-
"caption"=>
-
{"created_time"=>"1414804420",
-
"text"=>"#besties",
-
"from"=>
-
{"username"=>"notfakemarc",
-
"profile_picture"=>"http://images.ak.instagram.com/profiles/anonymousUser.jpg",
-
"id"=>"1546672945",
-
"full_name"=>"notfakemarc"},
-
"id"=>"843763093550795252"},
-
"user_has_liked"=>false,
-
"id"=>"843763092485442413_1546672945",
-
"user"=>
-
{"username"=>"notfakemarc",
-
"website"=>"",
-
"profile_picture"=>"http://images.ak.instagram.com/profiles/anonymousUser.jpg",
-
"full_name"=>"notfakemarc",
-
"bio"=>"",
-
"id"=>"1546672945"}},
-
{"attribution"=>nil,
-
"tags"=>["dsa"],
-
"type"=>"image",
-
"location"=>
-
{"latitude"=>39.749574,
-
"name"=>"Turing School",
-
"longitude"=>-104.999971,
-
"id"=>451194042},
-
"comments"=>{"count"=>0, "data"=>[]},
-
"filter"=>"Hefe",
-
"created_time"=>"1414772194",
-
"link"=>"http://instagram.com/p/u0sDK4LghK/",
-
"likes"=>{"count"=>0, "data"=>[]},
-
"images"=>
-
{"low_resolution"=>
-
{"url"=>
-
"http://scontent-b.cdninstagram.com/hphotos-xaf1/t51.2885-15/10731669_937281106286155_1849634485_a.jpg",
-
"width"=>306,
-
"height"=>306},
-
"thumbnail"=>
-
{"url"=>
-
"http://scontent-b.cdninstagram.com/hphotos-xaf1/t51.2885-15/10731669_937281106286155_1849634485_s.jpg",
-
"width"=>150,
-
"height"=>150},
-
"standard_resolution"=>
-
{"url"=>
-
"http://scontent-b.cdninstagram.com/hphotos-xaf1/t51.2885-15/10731669_937281106286155_1849634485_n.jpg",
-
"width"=>640,
-
"height"=>640}},
-
"users_in_photo"=>[],
-
"caption"=>
-
{"created_time"=>"1414772194",
-
"text"=>"Rolando goes hard in the paint for #DSA morning meetups",
-
"from"=>
-
{"username"=>"notfakemarc",
-
"profile_picture"=>"http://images.ak.instagram.com/profiles/anonymousUser.jpg",
-
"id"=>"1546672945",
-
"full_name"=>"notfakemarc"},
-
"id"=>"843492763737393702"},
-
"user_has_liked"=>false,
-
"id"=>"843492762110003274_1546672945",
-
"user"=>
-
{"username"=>"notfakemarc",
-
"website"=>"",
-
"profile_picture"=>"http://images.ak.instagram.com/profiles/anonymousUser.jpg",
-
"full_name"=>"notfakemarc",
-
"bio"=>"",
-
"id"=>"1546672945"}}]
-
}
-
end
-
-
1
def twitter_api_response
-
[{"created_at"=>"Mon Nov 10 21:31:04 +0000 2014",
-
"id"=>531922010872377344,
-
"id_str"=>"531922010872377344",
-
"text"=>"demo #2",
-
"source"=>"<a href=\"http://twitter.com\" rel=\"nofollow\">Twitter Web Client</a>",
-
"truncated"=>false,
-
"in_reply_to_status_id"=>nil,
-
"in_reply_to_status_id_str"=>nil,
-
"in_reply_to_user_id"=>nil,
-
"in_reply_to_user_id_str"=>nil,
-
"in_reply_to_screen_name"=>nil,
-
"user"=>
-
{"id"=>2895994081,
-
"id_str"=>"2895994081",
-
"name"=>"Fake Marc",
-
"screen_name"=>"marcgaro",
-
"location"=>"",
-
"profile_location"=>nil,
-
"description"=>"",
-
"url"=>nil,
-
"entities"=>{"description"=>{"urls"=>[]}},
-
"protected"=>false,
-
"followers_count"=>0,
-
"friends_count"=>0,
-
"listed_count"=>0,
-
"created_at"=>"Mon Nov 10 21:23:45 +0000 2014",
-
"favourites_count"=>0,
-
"utc_offset"=>nil,
-
"time_zone"=>nil,
-
"geo_enabled"=>false,
-
"verified"=>false,
-
"statuses_count"=>2,
-
"lang"=>"en",
-
"contributors_enabled"=>false,
-
"is_translator"=>false,
-
"is_translation_enabled"=>false,
-
"profile_background_color"=>"C0DEED",
-
"profile_background_image_url"=>"http://abs.twimg.com/images/themes/theme1/bg.png",
-
"profile_background_image_url_https"=>"https://abs.twimg.com/images/themes/theme1/bg.png",
-
"profile_background_tile"=>false,
-
"profile_image_url"=>"http://abs.twimg.com/sticky/default_profile_images/default_profile_1_normal.png",
-
"profile_image_url_https"=>"https://abs.twimg.com/sticky/default_profile_images/default_profile_1_normal.png",
-
"profile_link_color"=>"0084B4",
-
"profile_sidebar_border_color"=>"C0DEED",
-
"profile_sidebar_fill_color"=>"DDEEF6",
-
"profile_text_color"=>"333333",
-
"profile_use_background_image"=>true,
-
"default_profile"=>true,
-
"default_profile_image"=>true,
-
"following"=>false,
-
"follow_request_sent"=>false,
-
"notifications"=>false},
-
"geo"=>nil,
-
"coordinates"=>nil,
-
"place"=>nil,
-
"contributors"=>nil,
-
"retweet_count"=>0,
-
"favorite_count"=>0,
-
"entities"=>{"hashtags"=>[], "symbols"=>[], "user_mentions"=>[], "urls"=>[]},
-
"favorited"=>false,
-
"retweeted"=>false,
-
"lang"=>"es"},
-
{"created_at"=>"Mon Nov 10 21:30:54 +0000 2014",
-
"id"=>531921968975446016,
-
"id_str"=>"531921968975446016",
-
"text"=>"demo #1",
-
"source"=>"<a href=\"http://twitter.com\" rel=\"nofollow\">Twitter Web Client</a>",
-
"truncated"=>false,
-
"in_reply_to_status_id"=>nil,
-
"in_reply_to_status_id_str"=>nil,
-
"in_reply_to_user_id"=>nil,
-
"in_reply_to_user_id_str"=>nil,
-
"in_reply_to_screen_name"=>nil,
-
"user"=>
-
{"id"=>2895994081,
-
"id_str"=>"2895994081",
-
"name"=>"Fake Marc",
-
"screen_name"=>"marcgaro",
-
"location"=>"",
-
"profile_location"=>nil,
-
"description"=>"",
-
"url"=>nil,
-
"entities"=>{"description"=>{"urls"=>[]}},
-
"protected"=>false,
-
"followers_count"=>0,
-
"friends_count"=>0,
-
"listed_count"=>0,
-
"created_at"=>"Mon Nov 10 21:23:45 +0000 2014",
-
"favourites_count"=>0,
-
"utc_offset"=>nil,
-
"time_zone"=>nil,
-
"geo_enabled"=>false,
-
"verified"=>false,
-
"statuses_count"=>2,
-
"lang"=>"en",
-
"contributors_enabled"=>false,
-
"is_translator"=>false,
-
"is_translation_enabled"=>false,
-
"profile_background_color"=>"C0DEED",
-
"profile_background_image_url"=>"http://abs.twimg.com/images/themes/theme1/bg.png",
-
"profile_background_image_url_https"=>"https://abs.twimg.com/images/themes/theme1/bg.png",
-
"profile_background_tile"=>false,
-
"profile_image_url"=>"http://abs.twimg.com/sticky/default_profile_images/default_profile_1_normal.png",
-
"profile_image_url_https"=>"https://abs.twimg.com/sticky/default_profile_images/default_profile_1_normal.png",
-
"profile_link_color"=>"0084B4",
-
"profile_sidebar_border_color"=>"C0DEED",
-
"profile_sidebar_fill_color"=>"DDEEF6",
-
"profile_text_color"=>"333333",
-
"profile_use_background_image"=>true,
-
"default_profile"=>true,
-
"default_profile_image"=>true,
-
"following"=>false,
-
"follow_request_sent"=>false,
-
"notifications"=>false},
-
"geo"=>nil,
-
"coordinates"=>nil,
-
"place"=>nil,
-
"contributors"=>nil,
-
"retweet_count"=>0,
-
"favorite_count"=>0,
-
"entities"=>{"hashtags"=>[], "symbols"=>[], "user_mentions"=>[], "urls"=>[]},
-
"favorited"=>false,
-
"retweeted"=>false,
-
"lang"=>"es"}]
-
end
-
-
end
-
1
require 'vcr'
-
-
1
VCR.configure do |c|
-
1
c.cassette_library_dir = 'spec/fixtures/vcr'
-
1
c.hook_into :webmock
-
1
c.configure_rspec_metadata!
-
#c.filter_sensitive_data('<API_KEY>') { ENV['RIOT_API'] }
-
#c.ignore_hosts 'codeclimate.com'
-
end
-
#--
-
# Copyright (c) 2004-2014 David Heinemeier Hansson
-
#
-
# Permission is hereby granted, free of charge, to any person obtaining
-
# a copy of this software and associated documentation files (the
-
# "Software"), to deal in the Software without restriction, including
-
# without limitation the rights to use, copy, modify, merge, publish,
-
# distribute, sublicense, and/or sell copies of the Software, and to
-
# permit persons to whom the Software is furnished to do so, subject to
-
# the following conditions:
-
#
-
# The above copyright notice and this permission notice shall be
-
# included in all copies or substantial portions of the Software.
-
#
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
#++
-
-
1
require 'abstract_controller'
-
1
require 'action_mailer/version'
-
-
# Common Active Support usage in Action Mailer
-
1
require 'active_support/rails'
-
1
require 'active_support/core_ext/class'
-
1
require 'active_support/core_ext/module/attr_internal'
-
1
require 'active_support/core_ext/string/inflections'
-
1
require 'active_support/lazy_load_hooks'
-
-
1
module ActionMailer
-
1
extend ::ActiveSupport::Autoload
-
-
1
eager_autoload do
-
1
autoload :Collector
-
end
-
-
1
autoload :Base
-
1
autoload :DeliveryMethods
-
1
autoload :MailHelper
-
1
autoload :Preview
-
1
autoload :Previews, 'action_mailer/preview'
-
1
autoload :TestCase
-
1
autoload :TestHelper
-
end
-
1
require 'mail'
-
1
require 'action_mailer/collector'
-
1
require 'active_support/core_ext/string/inflections'
-
1
require 'active_support/core_ext/hash/except'
-
1
require 'active_support/core_ext/module/anonymous'
-
-
1
require 'action_mailer/log_subscriber'
-
-
1
module ActionMailer
-
# Action Mailer allows you to send email from your application using a mailer model and views.
-
#
-
# = Mailer Models
-
#
-
# To use Action Mailer, you need to create a mailer model.
-
#
-
# $ rails generate mailer Notifier
-
#
-
# The generated model inherits from <tt>ActionMailer::Base</tt>. A mailer model defines methods
-
# used to generate an email message. In these methods, you can setup variables to be used in
-
# the mailer views, options on the mail itself such as the <tt>:from</tt> address, and attachments.
-
#
-
# class Notifier < ActionMailer::Base
-
# default from: 'no-reply@example.com',
-
# return_path: 'system@example.com'
-
#
-
# def welcome(recipient)
-
# @account = recipient
-
# mail(to: recipient.email_address_with_name,
-
# bcc: ["bcc@example.com", "Order Watcher <watcher@example.com>"])
-
# end
-
# end
-
#
-
# Within the mailer method, you have access to the following methods:
-
#
-
# * <tt>attachments[]=</tt> - Allows you to add attachments to your email in an intuitive
-
# manner; <tt>attachments['filename.png'] = File.read('path/to/filename.png')</tt>
-
#
-
# * <tt>attachments.inline[]=</tt> - Allows you to add an inline attachment to your email
-
# in the same manner as <tt>attachments[]=</tt>
-
#
-
# * <tt>headers[]=</tt> - Allows you to specify any header field in your email such
-
# as <tt>headers['X-No-Spam'] = 'True'</tt>. Note, while most fields like <tt>To:</tt>
-
# <tt>From:</tt> can only appear once in an email header, other fields like <tt>X-Anything</tt>
-
# can appear multiple times. If you want to change a field that can appear multiple times,
-
# you need to set it to nil first so that Mail knows you are replacing it and not adding
-
# another field of the same name.
-
#
-
# * <tt>headers(hash)</tt> - Allows you to specify multiple headers in your email such
-
# as <tt>headers({'X-No-Spam' => 'True', 'In-Reply-To' => '1234@message.id'})</tt>
-
#
-
# * <tt>mail</tt> - Allows you to specify email to be sent.
-
#
-
# The hash passed to the mail method allows you to specify any header that a <tt>Mail::Message</tt>
-
# will accept (any valid email header including optional fields).
-
#
-
# The mail method, if not passed a block, will inspect your views and send all the views with
-
# the same name as the method, so the above action would send the +welcome.text.erb+ view
-
# file as well as the +welcome.text.html.erb+ view file in a +multipart/alternative+ email.
-
#
-
# If you want to explicitly render only certain templates, pass a block:
-
#
-
# mail(to: user.email) do |format|
-
# format.text
-
# format.html
-
# end
-
#
-
# The block syntax is also useful in providing information specific to a part:
-
#
-
# mail(to: user.email) do |format|
-
# format.text(content_transfer_encoding: "base64")
-
# format.html
-
# end
-
#
-
# Or even to render a special view:
-
#
-
# mail(to: user.email) do |format|
-
# format.text
-
# format.html { render "some_other_template" }
-
# end
-
#
-
# = Mailer views
-
#
-
# Like Action Controller, each mailer class has a corresponding view directory in which each
-
# method of the class looks for a template with its name.
-
#
-
# To define a template to be used with a mailing, create an <tt>.erb</tt> file with the same
-
# name as the method in your mailer model. For example, in the mailer defined above, the template at
-
# <tt>app/views/notifier/welcome.text.erb</tt> would be used to generate the email.
-
#
-
# Variables defined in the model are accessible as instance variables in the view.
-
#
-
# Emails by default are sent in plain text, so a sample view for our model example might look like this:
-
#
-
# Hi <%= @account.name %>,
-
# Thanks for joining our service! Please check back often.
-
#
-
# You can even use Action Pack helpers in these views. For example:
-
#
-
# You got a new note!
-
# <%= truncate(@note.body, length: 25) %>
-
#
-
# If you need to access the subject, from or the recipients in the view, you can do that through message object:
-
#
-
# You got a new note from <%= message.from %>!
-
# <%= truncate(@note.body, length: 25) %>
-
#
-
#
-
# = Generating URLs
-
#
-
# URLs can be generated in mailer views using <tt>url_for</tt> or named routes. Unlike controllers from
-
# Action Pack, the mailer instance doesn't have any context about the incoming request, so you'll need
-
# to provide all of the details needed to generate a URL.
-
#
-
# When using <tt>url_for</tt> you'll need to provide the <tt>:host</tt>, <tt>:controller</tt>, and <tt>:action</tt>:
-
#
-
# <%= url_for(host: "example.com", controller: "welcome", action: "greeting") %>
-
#
-
# When using named routes you only need to supply the <tt>:host</tt>:
-
#
-
# <%= users_url(host: "example.com") %>
-
#
-
# You should use the <tt>named_route_url</tt> style (which generates absolute URLs) and avoid using the
-
# <tt>named_route_path</tt> style (which generates relative URLs), since clients reading the mail will
-
# have no concept of a current URL from which to determine a relative path.
-
#
-
# It is also possible to set a default host that will be used in all mailers by setting the <tt>:host</tt>
-
# option as a configuration option in <tt>config/application.rb</tt>:
-
#
-
# config.action_mailer.default_url_options = { host: "example.com" }
-
#
-
# When you decide to set a default <tt>:host</tt> for your mailers, then you need to make sure to use the
-
# <tt>only_path: false</tt> option when using <tt>url_for</tt>. Since the <tt>url_for</tt> view helper
-
# will generate relative URLs by default when a <tt>:host</tt> option isn't explicitly provided, passing
-
# <tt>only_path: false</tt> will ensure that absolute URLs are generated.
-
#
-
# = Sending mail
-
#
-
# Once a mailer action and template are defined, you can deliver your message or create it and save it
-
# for delivery later:
-
#
-
# Notifier.welcome(david).deliver # sends the email
-
# mail = Notifier.welcome(david) # => a Mail::Message object
-
# mail.deliver # sends the email
-
#
-
# You never instantiate your mailer class. Rather, you just call the method you defined on the class itself.
-
#
-
# = Multipart Emails
-
#
-
# Multipart messages can also be used implicitly because Action Mailer will automatically detect and use
-
# multipart templates, where each template is named after the name of the action, followed by the content
-
# type. Each such detected template will be added as a separate part to the message.
-
#
-
# For example, if the following templates exist:
-
# * signup_notification.text.erb
-
# * signup_notification.html.erb
-
# * signup_notification.xml.builder
-
# * signup_notification.yaml.erb
-
#
-
# Each would be rendered and added as a separate part to the message, with the corresponding content
-
# type. The content type for the entire message is automatically set to <tt>multipart/alternative</tt>,
-
# which indicates that the email contains multiple different representations of the same email
-
# body. The same instance variables defined in the action are passed to all email templates.
-
#
-
# Implicit template rendering is not performed if any attachments or parts have been added to the email.
-
# This means that you'll have to manually add each part to the email and set the content type of the email
-
# to <tt>multipart/alternative</tt>.
-
#
-
# = Attachments
-
#
-
# Sending attachment in emails is easy:
-
#
-
# class ApplicationMailer < ActionMailer::Base
-
# def welcome(recipient)
-
# attachments['free_book.pdf'] = File.read('path/to/file.pdf')
-
# mail(to: recipient, subject: "New account information")
-
# end
-
# end
-
#
-
# Which will (if it had both a <tt>welcome.text.erb</tt> and <tt>welcome.html.erb</tt>
-
# template in the view directory), send a complete <tt>multipart/mixed</tt> email with two parts,
-
# the first part being a <tt>multipart/alternative</tt> with the text and HTML email parts inside,
-
# and the second being a <tt>application/pdf</tt> with a Base64 encoded copy of the file.pdf book
-
# with the filename +free_book.pdf+.
-
#
-
# If you need to send attachments with no content, you need to create an empty view for it,
-
# or add an empty body parameter like this:
-
#
-
# class ApplicationMailer < ActionMailer::Base
-
# def welcome(recipient)
-
# attachments['free_book.pdf'] = File.read('path/to/file.pdf')
-
# mail(to: recipient, subject: "New account information", body: "")
-
# end
-
# end
-
#
-
# = Inline Attachments
-
#
-
# You can also specify that a file should be displayed inline with other HTML. This is useful
-
# if you want to display a corporate logo or a photo.
-
#
-
# class ApplicationMailer < ActionMailer::Base
-
# def welcome(recipient)
-
# attachments.inline['photo.png'] = File.read('path/to/photo.png')
-
# mail(to: recipient, subject: "Here is what we look like")
-
# end
-
# end
-
#
-
# And then to reference the image in the view, you create a <tt>welcome.html.erb</tt> file and
-
# make a call to +image_tag+ passing in the attachment you want to display and then call
-
# +url+ on the attachment to get the relative content id path for the image source:
-
#
-
# <h1>Please Don't Cringe</h1>
-
#
-
# <%= image_tag attachments['photo.png'].url -%>
-
#
-
# As we are using Action View's +image_tag+ method, you can pass in any other options you want:
-
#
-
# <h1>Please Don't Cringe</h1>
-
#
-
# <%= image_tag attachments['photo.png'].url, alt: 'Our Photo', class: 'photo' -%>
-
#
-
# = Observing and Intercepting Mails
-
#
-
# Action Mailer provides hooks into the Mail observer and interceptor methods. These allow you to
-
# register classes that are called during the mail delivery life cycle.
-
#
-
# An observer class must implement the <tt>:delivered_email(message)</tt> method which will be
-
# called once for every email sent after the email has been sent.
-
#
-
# An interceptor class must implement the <tt>:delivering_email(message)</tt> method which will be
-
# called before the email is sent, allowing you to make modifications to the email before it hits
-
# the delivery agents. Your class should make any needed modifications directly to the passed
-
# in <tt>Mail::Message</tt> instance.
-
#
-
# = Default Hash
-
#
-
# Action Mailer provides some intelligent defaults for your emails, these are usually specified in a
-
# default method inside the class definition:
-
#
-
# class Notifier < ActionMailer::Base
-
# default sender: 'system@example.com'
-
# end
-
#
-
# You can pass in any header value that a <tt>Mail::Message</tt> accepts. Out of the box,
-
# <tt>ActionMailer::Base</tt> sets the following:
-
#
-
# * <tt>mime_version: "1.0"</tt>
-
# * <tt>charset: "UTF-8",</tt>
-
# * <tt>content_type: "text/plain",</tt>
-
# * <tt>parts_order: [ "text/plain", "text/enriched", "text/html" ]</tt>
-
#
-
# <tt>parts_order</tt> and <tt>charset</tt> are not actually valid <tt>Mail::Message</tt> header fields,
-
# but Action Mailer translates them appropriately and sets the correct values.
-
#
-
# As you can pass in any header, you need to either quote the header as a string, or pass it in as
-
# an underscored symbol, so the following will work:
-
#
-
# class Notifier < ActionMailer::Base
-
# default 'Content-Transfer-Encoding' => '7bit',
-
# content_description: 'This is a description'
-
# end
-
#
-
# Finally, Action Mailer also supports passing <tt>Proc</tt> objects into the default hash, so you
-
# can define methods that evaluate as the message is being generated:
-
#
-
# class Notifier < ActionMailer::Base
-
# default 'X-Special-Header' => Proc.new { my_method }
-
#
-
# private
-
#
-
# def my_method
-
# 'some complex call'
-
# end
-
# end
-
#
-
# Note that the proc is evaluated right at the start of the mail message generation, so if you
-
# set something in the defaults using a proc, and then set the same thing inside of your
-
# mailer method, it will get over written by the mailer method.
-
#
-
# It is also possible to set these default options that will be used in all mailers through
-
# the <tt>default_options=</tt> configuration in <tt>config/application.rb</tt>:
-
#
-
# config.action_mailer.default_options = { from: "no-reply@example.org" }
-
#
-
# = Callbacks
-
#
-
# You can specify callbacks using before_action and after_action for configuring your messages.
-
# This may be useful, for example, when you want to add default inline attachments for all
-
# messages sent out by a certain mailer class:
-
#
-
# class Notifier < ActionMailer::Base
-
# before_action :add_inline_attachment!
-
#
-
# def welcome
-
# mail
-
# end
-
#
-
# private
-
#
-
# def add_inline_attachment!
-
# attachments.inline["footer.jpg"] = File.read('/path/to/filename.jpg')
-
# end
-
# end
-
#
-
# Callbacks in ActionMailer are implemented using AbstractController::Callbacks, so you
-
# can define and configure callbacks in the same manner that you would use callbacks in
-
# classes that inherit from ActionController::Base.
-
#
-
# Note that unless you have a specific reason to do so, you should prefer using before_action
-
# rather than after_action in your ActionMailer classes so that headers are parsed properly.
-
#
-
# = Previewing emails
-
#
-
# You can preview your email templates visually by adding a mailer preview file to the
-
# <tt>ActionMailer::Base.preview_path</tt>. Since most emails do something interesting
-
# with database data, you'll need to write some scenarios to load messages with fake data:
-
#
-
# class NotifierPreview < ActionMailer::Preview
-
# def welcome
-
# Notifier.welcome(User.first)
-
# end
-
# end
-
#
-
# Methods must return a <tt>Mail::Message</tt> object which can be generated by calling the mailer
-
# method without the additional <tt>deliver</tt>. The location of the mailer previews
-
# directory can be configured using the <tt>preview_path</tt> option which has a default
-
# of <tt>test/mailers/previews</tt>:
-
#
-
# config.action_mailer.preview_path = "#{Rails.root}/lib/mailer_previews"
-
#
-
# An overview of all previews is accessible at <tt>http://localhost:3000/rails/mailers</tt>
-
# on a running development server instance.
-
#
-
# Previews can also be intercepted in a similar manner as deliveries can be by registering
-
# a preview interceptor that has a <tt>previewing_email</tt> method:
-
#
-
# class CssInlineStyler
-
# def self.previewing_email(message)
-
# # inline CSS styles
-
# end
-
# end
-
#
-
# config.action_mailer.preview_interceptors :css_inline_styler
-
#
-
# Note that interceptors need to be registered both with <tt>register_interceptor</tt>
-
# and <tt>register_preview_interceptor</tt> if they should operate on both sending and
-
# previewing emails.
-
#
-
# = Configuration options
-
#
-
# These options are specified on the class level, like
-
# <tt>ActionMailer::Base.raise_delivery_errors = true</tt>
-
#
-
# * <tt>default_options</tt> - You can pass this in at a class level as well as within the class itself as
-
# per the above section.
-
#
-
# * <tt>logger</tt> - the logger is used for generating information on the mailing run if available.
-
# Can be set to +nil+ for no logging. Compatible with both Ruby's own +Logger+ and Log4r loggers.
-
#
-
# * <tt>smtp_settings</tt> - Allows detailed configuration for <tt>:smtp</tt> delivery method:
-
# * <tt>:address</tt> - Allows you to use a remote mail server. Just change it from its default
-
# "localhost" setting.
-
# * <tt>:port</tt> - On the off chance that your mail server doesn't run on port 25, you can change it.
-
# * <tt>:domain</tt> - If you need to specify a HELO domain, you can do it here.
-
# * <tt>:user_name</tt> - If your mail server requires authentication, set the username in this setting.
-
# * <tt>:password</tt> - If your mail server requires authentication, set the password in this setting.
-
# * <tt>:authentication</tt> - If your mail server requires authentication, you need to specify the
-
# authentication type here.
-
# This is a symbol and one of <tt>:plain</tt> (will send the password in the clear), <tt>:login</tt> (will
-
# send password Base64 encoded) or <tt>:cram_md5</tt> (combines a Challenge/Response mechanism to exchange
-
# information and a cryptographic Message Digest 5 algorithm to hash important information)
-
# * <tt>:enable_starttls_auto</tt> - When set to true, detects if STARTTLS is enabled in your SMTP server
-
# and starts to use it.
-
# * <tt>:openssl_verify_mode</tt> - When using TLS, you can set how OpenSSL checks the certificate. This is
-
# really useful if you need to validate a self-signed and/or a wildcard certificate. You can use the name
-
# of an OpenSSL verify constant (<tt>'none'</tt>, <tt>'peer'</tt>, <tt>'client_once'</tt>,
-
# <tt>'fail_if_no_peer_cert'</tt>) or directly the constant (<tt>OpenSSL::SSL::VERIFY_NONE</tt>,
-
# <tt>OpenSSL::SSL::VERIFY_PEER</tt>, ...).
-
#
-
# * <tt>sendmail_settings</tt> - Allows you to override options for the <tt>:sendmail</tt> delivery method.
-
# * <tt>:location</tt> - The location of the sendmail executable. Defaults to <tt>/usr/sbin/sendmail</tt>.
-
# * <tt>:arguments</tt> - The command line arguments. Defaults to <tt>-i -t</tt> with <tt>-f sender@address</tt>
-
# added automatically before the message is sent.
-
#
-
# * <tt>file_settings</tt> - Allows you to override options for the <tt>:file</tt> delivery method.
-
# * <tt>:location</tt> - The directory into which emails will be written. Defaults to the application
-
# <tt>tmp/mails</tt>.
-
#
-
# * <tt>raise_delivery_errors</tt> - Whether or not errors should be raised if the email fails to be delivered.
-
#
-
# * <tt>delivery_method</tt> - Defines a delivery method. Possible values are <tt>:smtp</tt> (default),
-
# <tt>:sendmail</tt>, <tt>:test</tt>, and <tt>:file</tt>. Or you may provide a custom delivery method
-
# object e.g. +MyOwnDeliveryMethodClass+. See the Mail gem documentation on the interface you need to
-
# implement for a custom delivery agent.
-
#
-
# * <tt>perform_deliveries</tt> - Determines whether emails are actually sent from Action Mailer when you
-
# call <tt>.deliver</tt> on an mail message or on an Action Mailer method. This is on by default but can
-
# be turned off to aid in functional testing.
-
#
-
# * <tt>deliveries</tt> - Keeps an array of all the emails sent out through the Action Mailer with
-
# <tt>delivery_method :test</tt>. Most useful for unit and functional testing.
-
1
class Base < AbstractController::Base
-
1
include DeliveryMethods
-
1
include Previews
-
-
1
abstract!
-
-
1
include AbstractController::Rendering
-
-
1
include AbstractController::Logger
-
1
include AbstractController::Helpers
-
1
include AbstractController::Translation
-
1
include AbstractController::AssetPaths
-
1
include AbstractController::Callbacks
-
-
1
include ActionView::Layouts
-
-
1
PROTECTED_IVARS = AbstractController::Rendering::DEFAULT_PROTECTED_INSTANCE_VARIABLES + [:@_action_has_layout]
-
-
1
def _protected_ivars # :nodoc:
-
PROTECTED_IVARS
-
end
-
-
1
helper ActionMailer::MailHelper
-
-
1
private_class_method :new #:nodoc:
-
-
1
class_attribute :default_params
-
1
self.default_params = {
-
mime_version: "1.0",
-
charset: "UTF-8",
-
content_type: "text/plain",
-
parts_order: [ "text/plain", "text/enriched", "text/html" ]
-
}.freeze
-
-
1
class << self
-
# Register one or more Observers which will be notified when mail is delivered.
-
1
def register_observers(*observers)
-
1
observers.flatten.compact.each { |observer| register_observer(observer) }
-
end
-
-
# Register one or more Interceptors which will be called before mail is sent.
-
1
def register_interceptors(*interceptors)
-
1
interceptors.flatten.compact.each { |interceptor| register_interceptor(interceptor) }
-
end
-
-
# Register an Observer which will be notified when mail is delivered.
-
# Either a class, string or symbol can be passed in as the Observer.
-
# If a string or symbol is passed in it will be camelized and constantized.
-
1
def register_observer(observer)
-
delivery_observer = case observer
-
when String, Symbol
-
observer.to_s.camelize.constantize
-
else
-
observer
-
end
-
-
Mail.register_observer(delivery_observer)
-
end
-
-
# Register an Interceptor which will be called before mail is sent.
-
# Either a class, string or symbol can be passed in as the Interceptor.
-
# If a string or symbol is passed in it will be camelized and constantized.
-
1
def register_interceptor(interceptor)
-
delivery_interceptor = case interceptor
-
when String, Symbol
-
interceptor.to_s.camelize.constantize
-
else
-
interceptor
-
end
-
-
Mail.register_interceptor(delivery_interceptor)
-
end
-
-
# Returns the name of current mailer. This method is also being used as a path for a view lookup.
-
# If this is an anonymous mailer, this method will return +anonymous+ instead.
-
1
def mailer_name
-
@mailer_name ||= anonymous? ? "anonymous" : name.underscore
-
end
-
# Allows to set the name of current mailer.
-
1
attr_writer :mailer_name
-
1
alias :controller_path :mailer_name
-
-
# Sets the defaults through app configuration:
-
#
-
# config.action_mailer.default { from: "no-reply@example.org" }
-
#
-
# Aliased by ::default_options=
-
1
def default(value = nil)
-
self.default_params = default_params.merge(value).freeze if value
-
default_params
-
end
-
# Allows to set defaults through app configuration:
-
#
-
# config.action_mailer.default_options = { from: "no-reply@example.org" }
-
1
alias :default_options= :default
-
-
# Receives a raw email, parses it into an email object, decodes it,
-
# instantiates a new mailer, and passes the email object to the mailer
-
# object's +receive+ method.
-
#
-
# If you want your mailer to be able to process incoming messages, you'll
-
# need to implement a +receive+ method that accepts the raw email string
-
# as a parameter:
-
#
-
# class MyMailer < ActionMailer::Base
-
# def receive(mail)
-
# # ...
-
# end
-
# end
-
1
def receive(raw_mail)
-
ActiveSupport::Notifications.instrument("receive.action_mailer") do |payload|
-
mail = Mail.new(raw_mail)
-
set_payload_for_mail(payload, mail)
-
new.receive(mail)
-
end
-
end
-
-
# Wraps an email delivery inside of <tt>ActiveSupport::Notifications</tt> instrumentation.
-
#
-
# This method is actually called by the <tt>Mail::Message</tt> object itself
-
# through a callback when you call <tt>:deliver</tt> on the <tt>Mail::Message</tt>,
-
# calling +deliver_mail+ directly and passing a <tt>Mail::Message</tt> will do
-
# nothing except tell the logger you sent the email.
-
1
def deliver_mail(mail) #:nodoc:
-
ActiveSupport::Notifications.instrument("deliver.action_mailer") do |payload|
-
set_payload_for_mail(payload, mail)
-
yield # Let Mail do the delivery actions
-
end
-
end
-
-
1
def respond_to?(method, include_private = false) #:nodoc:
-
3
super || action_methods.include?(method.to_s)
-
end
-
-
1
protected
-
-
1
def set_payload_for_mail(payload, mail) #:nodoc:
-
payload[:mailer] = name
-
payload[:message_id] = mail.message_id
-
payload[:subject] = mail.subject
-
payload[:to] = mail.to
-
payload[:from] = mail.from
-
payload[:bcc] = mail.bcc if mail.bcc.present?
-
payload[:cc] = mail.cc if mail.cc.present?
-
payload[:date] = mail.date
-
payload[:mail] = mail.encoded
-
end
-
-
1
def method_missing(method_name, *args) # :nodoc:
-
if respond_to?(method_name)
-
new(method_name, *args).message
-
else
-
super
-
end
-
end
-
end
-
-
1
attr_internal :message
-
-
# Instantiate a new mailer object. If +method_name+ is not +nil+, the mailer
-
# will be initialized according to the named method. If not, the mailer will
-
# remain uninitialized (useful when you only need to invoke the "receive"
-
# method, for instance).
-
1
def initialize(method_name=nil, *args)
-
super()
-
@_mail_was_called = false
-
@_message = Mail.new
-
process(method_name, *args) if method_name
-
end
-
-
1
def process(method_name, *args) #:nodoc:
-
payload = {
-
mailer: self.class.name,
-
action: method_name
-
}
-
-
ActiveSupport::Notifications.instrument("process.action_mailer", payload) do
-
lookup_context.skip_default_locale!
-
-
super
-
@_message = NullMail.new unless @_mail_was_called
-
end
-
end
-
-
1
class NullMail #:nodoc:
-
1
def body; '' end
-
-
1
def method_missing(*args)
-
nil
-
end
-
end
-
-
# Returns the name of the mailer object.
-
1
def mailer_name
-
self.class.mailer_name
-
end
-
-
# Allows you to pass random and unusual headers to the new <tt>Mail::Message</tt>
-
# object which will add them to itself.
-
#
-
# headers['X-Special-Domain-Specific-Header'] = "SecretValue"
-
#
-
# You can also pass a hash into headers of header field names and values,
-
# which will then be set on the <tt>Mail::Message</tt> object:
-
#
-
# headers 'X-Special-Domain-Specific-Header' => "SecretValue",
-
# 'In-Reply-To' => incoming.message_id
-
#
-
# The resulting <tt>Mail::Message</tt> will have the following in its header:
-
#
-
# X-Special-Domain-Specific-Header: SecretValue
-
1
def headers(args = nil)
-
if args
-
@_message.headers(args)
-
else
-
@_message
-
end
-
end
-
-
# Allows you to add attachments to an email, like so:
-
#
-
# mail.attachments['filename.jpg'] = File.read('/path/to/filename.jpg')
-
#
-
# If you do this, then Mail will take the file name and work out the mime type
-
# set the Content-Type, Content-Disposition, Content-Transfer-Encoding and
-
# base64 encode the contents of the attachment all for you.
-
#
-
# You can also specify overrides if you want by passing a hash instead of a string:
-
#
-
# mail.attachments['filename.jpg'] = {mime_type: 'application/x-gzip',
-
# content: File.read('/path/to/filename.jpg')}
-
#
-
# If you want to use a different encoding than Base64, you can pass an encoding in,
-
# but then it is up to you to pass in the content pre-encoded, and don't expect
-
# Mail to know how to decode this data:
-
#
-
# file_content = SpecialEncode(File.read('/path/to/filename.jpg'))
-
# mail.attachments['filename.jpg'] = {mime_type: 'application/x-gzip',
-
# encoding: 'SpecialEncoding',
-
# content: file_content }
-
#
-
# You can also search for specific attachments:
-
#
-
# # By Filename
-
# mail.attachments['filename.jpg'] # => Mail::Part object or nil
-
#
-
# # or by index
-
# mail.attachments[0] # => Mail::Part (first attachment)
-
#
-
1
def attachments
-
if @_mail_was_called
-
LateAttachmentsProxy.new(@_message.attachments)
-
else
-
@_message.attachments
-
end
-
end
-
-
1
class LateAttachmentsProxy < SimpleDelegator
-
1
def inline; _raise_error end
-
1
def []=(_name, _content); _raise_error end
-
-
1
private
-
1
def _raise_error
-
raise RuntimeError, "Can't add attachments after `mail` was called.\n" \
-
"Make sure to use `attachments[]=` before calling `mail`."
-
end
-
end
-
-
# The main method that creates the message and renders the email templates. There are
-
# two ways to call this method, with a block, or without a block.
-
#
-
# Both methods accept a headers hash. This hash allows you to specify the most used headers
-
# in an email message, these are:
-
#
-
# * +:subject+ - The subject of the message, if this is omitted, Action Mailer will
-
# ask the Rails I18n class for a translated +:subject+ in the scope of
-
# <tt>[mailer_scope, action_name]</tt> or if this is missing, will translate the
-
# humanized version of the +action_name+
-
# * +:to+ - Who the message is destined for, can be a string of addresses, or an array
-
# of addresses.
-
# * +:from+ - Who the message is from
-
# * +:cc+ - Who you would like to Carbon-Copy on this email, can be a string of addresses,
-
# or an array of addresses.
-
# * +:bcc+ - Who you would like to Blind-Carbon-Copy on this email, can be a string of
-
# addresses, or an array of addresses.
-
# * +:reply_to+ - Who to set the Reply-To header of the email to.
-
# * +:date+ - The date to say the email was sent on.
-
#
-
# You can set default values for any of the above headers (except +:date+)
-
# by using the ::default class method:
-
#
-
# class Notifier < ActionMailer::Base
-
# default from: 'no-reply@test.lindsaar.net',
-
# bcc: 'email_logger@test.lindsaar.net',
-
# reply_to: 'bounces@test.lindsaar.net'
-
# end
-
#
-
# If you need other headers not listed above, you can either pass them in
-
# as part of the headers hash or use the <tt>headers['name'] = value</tt>
-
# method.
-
#
-
# When a +:return_path+ is specified as header, that value will be used as
-
# the 'envelope from' address for the Mail message. Setting this is useful
-
# when you want delivery notifications sent to a different address than the
-
# one in +:from+. Mail will actually use the +:return_path+ in preference
-
# to the +:sender+ in preference to the +:from+ field for the 'envelope
-
# from' value.
-
#
-
# If you do not pass a block to the +mail+ method, it will find all
-
# templates in the view paths using by default the mailer name and the
-
# method name that it is being called from, it will then create parts for
-
# each of these templates intelligently, making educated guesses on correct
-
# content type and sequence, and return a fully prepared <tt>Mail::Message</tt>
-
# ready to call <tt>:deliver</tt> on to send.
-
#
-
# For example:
-
#
-
# class Notifier < ActionMailer::Base
-
# default from: 'no-reply@test.lindsaar.net'
-
#
-
# def welcome
-
# mail(to: 'mikel@test.lindsaar.net')
-
# end
-
# end
-
#
-
# Will look for all templates at "app/views/notifier" with name "welcome".
-
# If no welcome template exists, it will raise an ActionView::MissingTemplate error.
-
#
-
# However, those can be customized:
-
#
-
# mail(template_path: 'notifications', template_name: 'another')
-
#
-
# And now it will look for all templates at "app/views/notifications" with name "another".
-
#
-
# If you do pass a block, you can render specific templates of your choice:
-
#
-
# mail(to: 'mikel@test.lindsaar.net') do |format|
-
# format.text
-
# format.html
-
# end
-
#
-
# You can even render plain text directly without using a template:
-
#
-
# mail(to: 'mikel@test.lindsaar.net') do |format|
-
# format.text { render plain: "Hello Mikel!" }
-
# format.html { render html: "<h1>Hello Mikel!</h1>".html_safe }
-
# end
-
#
-
# Which will render a +multipart/alternative+ email with +text/plain+ and
-
# +text/html+ parts.
-
#
-
# The block syntax also allows you to customize the part headers if desired:
-
#
-
# mail(to: 'mikel@test.lindsaar.net') do |format|
-
# format.text(content_transfer_encoding: "base64")
-
# format.html
-
# end
-
#
-
1
def mail(headers = {}, &block)
-
return @_message if @_mail_was_called && headers.blank? && !block
-
-
@_mail_was_called = true
-
m = @_message
-
-
# At the beginning, do not consider class default for content_type
-
content_type = headers[:content_type]
-
-
# Call all the procs (if any)
-
default_values = {}
-
self.class.default.each do |k,v|
-
default_values[k] = v.is_a?(Proc) ? instance_eval(&v) : v
-
end
-
-
# Handle defaults
-
headers = headers.reverse_merge(default_values)
-
headers[:subject] ||= default_i18n_subject
-
-
# Apply charset at the beginning so all fields are properly quoted
-
m.charset = charset = headers[:charset]
-
-
# Set configure delivery behavior
-
wrap_delivery_behavior!(headers.delete(:delivery_method), headers.delete(:delivery_method_options))
-
-
# Assign all headers except parts_order, content_type and body
-
assignable = headers.except(:parts_order, :content_type, :body, :template_name, :template_path)
-
assignable.each { |k, v| m[k] = v }
-
-
# Render the templates and blocks
-
responses = collect_responses(headers, &block)
-
create_parts_from_responses(m, responses)
-
-
# Setup content type, reapply charset and handle parts order
-
m.content_type = set_content_type(m, content_type, headers[:content_type])
-
m.charset = charset
-
-
if m.multipart?
-
m.body.set_sort_order(headers[:parts_order])
-
m.body.sort_parts!
-
end
-
-
m
-
end
-
-
1
protected
-
-
# Used by #mail to set the content type of the message.
-
#
-
# It will use the given +user_content_type+, or multipart if the mail
-
# message has any attachments. If the attachments are inline, the content
-
# type will be "multipart/related", otherwise "multipart/mixed".
-
#
-
# If there is no content type passed in via headers, and there are no
-
# attachments, or the message is multipart, then the default content type is
-
# used.
-
1
def set_content_type(m, user_content_type, class_default)
-
params = m.content_type_parameters || {}
-
case
-
when user_content_type.present?
-
user_content_type
-
when m.has_attachments?
-
if m.attachments.detect { |a| a.inline? }
-
["multipart", "related", params]
-
else
-
["multipart", "mixed", params]
-
end
-
when m.multipart?
-
["multipart", "alternative", params]
-
else
-
m.content_type || class_default
-
end
-
end
-
-
# Translates the +subject+ using Rails I18n class under <tt>[mailer_scope, action_name]</tt> scope.
-
# If it does not find a translation for the +subject+ under the specified scope it will default to a
-
# humanized version of the <tt>action_name</tt>.
-
# If the subject has interpolations, you can pass them through the +interpolations+ parameter.
-
1
def default_i18n_subject(interpolations = {})
-
mailer_scope = self.class.mailer_name.tr('/', '.')
-
I18n.t(:subject, interpolations.merge(scope: [mailer_scope, action_name], default: action_name.humanize))
-
end
-
-
1
def collect_responses(headers) #:nodoc:
-
responses = []
-
-
if block_given?
-
collector = ActionMailer::Collector.new(lookup_context) { render(action_name) }
-
yield(collector)
-
responses = collector.responses
-
elsif headers[:body]
-
responses << {
-
body: headers.delete(:body),
-
content_type: self.class.default[:content_type] || "text/plain"
-
}
-
else
-
templates_path = headers.delete(:template_path) || self.class.mailer_name
-
templates_name = headers.delete(:template_name) || action_name
-
-
each_template(Array(templates_path), templates_name) do |template|
-
self.formats = template.formats
-
-
responses << {
-
body: render(template: template),
-
content_type: template.type.to_s
-
}
-
end
-
end
-
-
responses
-
end
-
-
1
def each_template(paths, name, &block) #:nodoc:
-
templates = lookup_context.find_all(name, paths)
-
if templates.empty?
-
raise ActionView::MissingTemplate.new(paths, name, paths, false, 'mailer')
-
else
-
templates.uniq { |t| t.formats }.each(&block)
-
end
-
end
-
-
1
def create_parts_from_responses(m, responses) #:nodoc:
-
if responses.size == 1 && !m.has_attachments?
-
responses[0].each { |k,v| m[k] = v }
-
elsif responses.size > 1 && m.has_attachments?
-
container = Mail::Part.new
-
container.content_type = "multipart/alternative"
-
responses.each { |r| insert_part(container, r, m.charset) }
-
m.add_part(container)
-
else
-
responses.each { |r| insert_part(m, r, m.charset) }
-
end
-
end
-
-
1
def insert_part(container, response, charset) #:nodoc:
-
response[:charset] ||= charset
-
part = Mail::Part.new(response)
-
container.add_part(part)
-
end
-
-
1
ActiveSupport.run_load_hooks(:action_mailer, self)
-
end
-
end
-
1
require 'abstract_controller/collector'
-
1
require 'active_support/core_ext/hash/reverse_merge'
-
1
require 'active_support/core_ext/array/extract_options'
-
-
1
module ActionMailer
-
1
class Collector
-
1
include AbstractController::Collector
-
1
attr_reader :responses
-
-
1
def initialize(context, &block)
-
@context = context
-
@responses = []
-
@default_render = block
-
end
-
-
1
def any(*args, &block)
-
options = args.extract_options!
-
raise ArgumentError, "You have to supply at least one format" if args.empty?
-
args.each { |type| send(type, options.dup, &block) }
-
end
-
1
alias :all :any
-
-
1
def custom(mime, options = {})
-
options.reverse_merge!(content_type: mime.to_s)
-
@context.formats = [mime.to_sym]
-
options[:body] = block_given? ? yield : @default_render.call
-
@responses << options
-
end
-
end
-
end
-
1
require 'tmpdir'
-
-
1
module ActionMailer
-
# This module handles everything related to mail delivery, from registering
-
# new delivery methods to configuring the mail object to be sent.
-
1
module DeliveryMethods
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
class_attribute :delivery_methods, :delivery_method
-
-
# Do not make this inheritable, because we always want it to propagate
-
1
cattr_accessor :raise_delivery_errors
-
1
self.raise_delivery_errors = true
-
-
1
cattr_accessor :perform_deliveries
-
1
self.perform_deliveries = true
-
-
1
self.delivery_methods = {}.freeze
-
1
self.delivery_method = :smtp
-
-
1
add_delivery_method :smtp, Mail::SMTP,
-
address: "localhost",
-
port: 25,
-
domain: 'localhost.localdomain',
-
user_name: nil,
-
password: nil,
-
authentication: nil,
-
enable_starttls_auto: true
-
-
1
add_delivery_method :file, Mail::FileDelivery,
-
location: defined?(Rails.root) ? "#{Rails.root}/tmp/mails" : "#{Dir.tmpdir}/mails"
-
-
1
add_delivery_method :sendmail, Mail::Sendmail,
-
location: '/usr/sbin/sendmail',
-
arguments: '-i -t'
-
-
1
add_delivery_method :test, Mail::TestMailer
-
end
-
-
# Helpers for creating and wrapping delivery behavior, used by DeliveryMethods.
-
1
module ClassMethods
-
# Provides a list of emails that have been delivered by Mail::TestMailer
-
1
delegate :deliveries, :deliveries=, to: Mail::TestMailer
-
-
# Adds a new delivery method through the given class using the given
-
# symbol as alias and the default options supplied.
-
#
-
# add_delivery_method :sendmail, Mail::Sendmail,
-
# location: '/usr/sbin/sendmail',
-
# arguments: '-i -t'
-
1
def add_delivery_method(symbol, klass, default_options={})
-
4
class_attribute(:"#{symbol}_settings") unless respond_to?(:"#{symbol}_settings")
-
4
send(:"#{symbol}_settings=", default_options)
-
4
self.delivery_methods = delivery_methods.merge(symbol.to_sym => klass).freeze
-
end
-
-
1
def wrap_delivery_behavior(mail, method=nil, options=nil) # :nodoc:
-
method ||= self.delivery_method
-
mail.delivery_handler = self
-
-
case method
-
when NilClass
-
raise "Delivery method cannot be nil"
-
when Symbol
-
if klass = delivery_methods[method]
-
mail.delivery_method(klass, (send(:"#{method}_settings") || {}).merge(options || {}))
-
else
-
raise "Invalid delivery method #{method.inspect}"
-
end
-
else
-
mail.delivery_method(method)
-
end
-
-
mail.perform_deliveries = perform_deliveries
-
mail.raise_delivery_errors = raise_delivery_errors
-
end
-
end
-
-
1
def wrap_delivery_behavior!(*args) # :nodoc:
-
self.class.wrap_delivery_behavior(message, *args)
-
end
-
end
-
end
-
1
module ActionMailer
-
# Returns the version of the currently loaded ActionMailer as a <tt>Gem::Version</tt>
-
1
def self.gem_version
-
Gem::Version.new VERSION::STRING
-
end
-
-
1
module VERSION
-
1
MAJOR = 4
-
1
MINOR = 1
-
1
TINY = 6
-
1
PRE = nil
-
-
1
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
-
end
-
end
-
1
require 'active_support/log_subscriber'
-
-
1
module ActionMailer
-
# Implements the ActiveSupport::LogSubscriber for logging notifications when
-
# email is delivered and received.
-
1
class LogSubscriber < ActiveSupport::LogSubscriber
-
# An email was delivered.
-
1
def deliver(event)
-
return unless logger.info?
-
recipients = Array(event.payload[:to]).join(', ')
-
info("\nSent mail to #{recipients} (#{event.duration.round(1)}ms)")
-
debug(event.payload[:mail])
-
end
-
-
# An email was received.
-
1
def receive(event)
-
return unless logger.info?
-
info("\nReceived mail (#{event.duration.round(1)}ms)")
-
debug(event.payload[:mail])
-
end
-
-
# An email was generated.
-
1
def process(event)
-
mailer = event.payload[:mailer]
-
action = event.payload[:action]
-
debug("\n#{mailer}##{action}: processed outbound mail in #{event.duration.round(1)}ms")
-
end
-
-
# Use the logger configured for ActionMailer::Base
-
1
def logger
-
ActionMailer::Base.logger
-
end
-
end
-
end
-
-
1
ActionMailer::LogSubscriber.attach_to :action_mailer
-
1
module ActionMailer
-
# Provides helper methods for ActionMailer::Base that can be used for easily
-
# formatting messages, accessing mailer or message instances, and the
-
# attachments list.
-
1
module MailHelper
-
# Take the text and format it, indented two spaces for each line, and
-
# wrapped at 72 columns.
-
1
def block_format(text)
-
formatted = text.split(/\n\r?\n/).collect { |paragraph|
-
format_paragraph(paragraph)
-
}.join("\n\n")
-
-
# Make list points stand on their own line
-
formatted.gsub!(/[ ]*([*]+) ([^*]*)/) { |s| " #{$1} #{$2.strip}\n" }
-
formatted.gsub!(/[ ]*([#]+) ([^#]*)/) { |s| " #{$1} #{$2.strip}\n" }
-
-
formatted
-
end
-
-
# Access the mailer instance.
-
1
def mailer
-
@_controller
-
end
-
-
# Access the message instance.
-
1
def message
-
@_message
-
end
-
-
# Access the message attachments list.
-
1
def attachments
-
@_message.attachments
-
end
-
-
# Returns +text+ wrapped at +len+ columns and indented +indent+ spaces.
-
#
-
# my_text = 'Here is a sample text with more than 40 characters'
-
#
-
# format_paragraph(my_text, 25, 4)
-
# # => " Here is a sample text with\n more than 40 characters"
-
1
def format_paragraph(text, len = 72, indent = 2)
-
sentences = [[]]
-
-
text.split.each do |word|
-
if sentences.first.present? && (sentences.last + [word]).join(' ').length > len
-
sentences << [word]
-
else
-
sentences.last << word
-
end
-
end
-
-
indentation = " " * indent
-
sentences.map! { |sentence|
-
"#{indentation}#{sentence.join(' ')}"
-
}.join "\n"
-
end
-
end
-
end
-
1
require "action_mailer"
-
1
require "rails"
-
1
require "abstract_controller/railties/routes_helpers"
-
-
1
module ActionMailer
-
1
class Railtie < Rails::Railtie # :nodoc:
-
1
config.action_mailer = ActiveSupport::OrderedOptions.new
-
1
config.eager_load_namespaces << ActionMailer
-
-
1
initializer "action_mailer.logger" do
-
2
ActiveSupport.on_load(:action_mailer) { self.logger ||= Rails.logger }
-
end
-
-
1
initializer "action_mailer.set_configs" do |app|
-
1
paths = app.config.paths
-
1
options = app.config.action_mailer
-
-
1
options.assets_dir ||= paths["public"].first
-
1
options.javascripts_dir ||= paths["public/javascripts"].first
-
1
options.stylesheets_dir ||= paths["public/stylesheets"].first
-
-
1
if Rails.env.development?
-
options.preview_path ||= defined?(Rails.root) ? "#{Rails.root}/test/mailers/previews" : nil
-
end
-
-
# make sure readers methods get compiled
-
1
options.asset_host ||= app.config.asset_host
-
1
options.relative_url_root ||= app.config.relative_url_root
-
-
1
ActiveSupport.on_load(:action_mailer) do
-
1
include AbstractController::UrlFor
-
1
extend ::AbstractController::Railties::RoutesHelpers.with(app.routes)
-
1
include app.routes.mounted_helpers
-
-
1
register_interceptors(options.delete(:interceptors))
-
1
register_preview_interceptors(options.delete(:preview_interceptors))
-
1
register_observers(options.delete(:observers))
-
-
7
options.each { |k,v| send("#{k}=", v) }
-
end
-
end
-
-
1
initializer "action_mailer.compile_config_methods" do
-
1
ActiveSupport.on_load(:action_mailer) do
-
1
config.compile_methods! if config.respond_to?(:compile_methods!)
-
end
-
end
-
-
1
config.after_initialize do
-
1
if ActionMailer::Base.preview_path
-
ActiveSupport::Dependencies.autoload_paths << ActionMailer::Base.preview_path
-
end
-
end
-
end
-
end
-
1
require 'active_support/test_case'
-
-
1
module ActionMailer
-
1
class NonInferrableMailerError < ::StandardError
-
1
def initialize(name)
-
super "Unable to determine the mailer to test from #{name}. " +
-
"You'll need to specify it using tests YourMailer in your " +
-
"test case definition"
-
end
-
end
-
-
1
class TestCase < ActiveSupport::TestCase
-
1
module Behavior
-
1
extend ActiveSupport::Concern
-
-
1
include ActiveSupport::Testing::ConstantLookup
-
1
include TestHelper
-
-
1
included do
-
1
class_attribute :_mailer_class
-
1
setup :initialize_test_deliveries
-
1
setup :set_expected_mail
-
end
-
-
1
module ClassMethods
-
1
def tests(mailer)
-
case mailer
-
when String, Symbol
-
self._mailer_class = mailer.to_s.camelize.constantize
-
when Module
-
self._mailer_class = mailer
-
else
-
raise NonInferrableMailerError.new(mailer)
-
end
-
end
-
-
1
def mailer_class
-
if mailer = self._mailer_class
-
mailer
-
else
-
tests determine_default_mailer(name)
-
end
-
end
-
-
1
def determine_default_mailer(name)
-
mailer = determine_constant_from_test_name(name) do |constant|
-
Class === constant && constant < ActionMailer::Base
-
end
-
raise NonInferrableMailerError.new(name) if mailer.nil?
-
mailer
-
end
-
end
-
-
1
protected
-
-
1
def initialize_test_deliveries
-
ActionMailer::Base.delivery_method = :test
-
ActionMailer::Base.perform_deliveries = true
-
ActionMailer::Base.deliveries.clear
-
end
-
-
1
def set_expected_mail
-
@expected = Mail.new
-
@expected.content_type ["text", "plain", { "charset" => charset }]
-
@expected.mime_version = '1.0'
-
end
-
-
1
private
-
-
1
def charset
-
"UTF-8"
-
end
-
-
1
def encode(subject)
-
Mail::Encodings.q_value_encode(subject, charset)
-
end
-
-
1
def read_fixture(action)
-
IO.readlines(File.join(Rails.root, 'test', 'fixtures', self.class.mailer_class.name.underscore, action))
-
end
-
end
-
-
1
include Behavior
-
end
-
end
-
1
module ActionMailer
-
1
module TestHelper
-
# Asserts that the number of emails sent matches the given number.
-
#
-
# def test_emails
-
# assert_emails 0
-
# ContactMailer.welcome.deliver
-
# assert_emails 1
-
# ContactMailer.welcome.deliver
-
# assert_emails 2
-
# end
-
#
-
# If a block is passed, that block should cause the specified number of
-
# emails to be sent.
-
#
-
# def test_emails_again
-
# assert_emails 1 do
-
# ContactMailer.welcome.deliver
-
# end
-
#
-
# assert_emails 2 do
-
# ContactMailer.welcome.deliver
-
# ContactMailer.welcome.deliver
-
# end
-
# end
-
1
def assert_emails(number)
-
if block_given?
-
original_count = ActionMailer::Base.deliveries.size
-
yield
-
new_count = ActionMailer::Base.deliveries.size
-
assert_equal original_count + number, new_count, "#{number} emails expected, but #{new_count - original_count} were sent"
-
else
-
assert_equal number, ActionMailer::Base.deliveries.size
-
end
-
end
-
-
# Assert that no emails have been sent.
-
#
-
# def test_emails
-
# assert_no_emails
-
# ContactMailer.welcome.deliver
-
# assert_emails 1
-
# end
-
#
-
# If a block is passed, that block should not cause any emails to be sent.
-
#
-
# def test_emails_again
-
# assert_no_emails do
-
# # No emails should be sent from this block
-
# end
-
# end
-
#
-
# Note: This assertion is simply a shortcut for:
-
#
-
# assert_emails 0
-
1
def assert_no_emails(&block)
-
assert_emails 0, &block
-
end
-
end
-
end
-
1
require_relative 'gem_version'
-
-
1
module ActionMailer
-
# Returns the version of the currently loaded ActionMailer as a <tt>Gem::Version</tt>
-
1
def self.version
-
gem_version
-
end
-
end
-
1
require 'action_pack'
-
1
require 'active_support/rails'
-
1
require 'active_support/core_ext/module/attr_internal'
-
1
require 'active_support/core_ext/module/anonymous'
-
1
require 'active_support/i18n'
-
-
1
module AbstractController
-
1
extend ActiveSupport::Autoload
-
-
1
autoload :Base
-
1
autoload :Callbacks
-
1
autoload :Collector
-
1
autoload :DoubleRenderError, "abstract_controller/rendering"
-
1
autoload :Helpers
-
1
autoload :Logger
-
1
autoload :Rendering
-
1
autoload :Translation
-
1
autoload :AssetPaths
-
1
autoload :UrlFor
-
end
-
1
module AbstractController
-
1
module AssetPaths #:nodoc:
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
2
config_accessor :asset_host, :assets_dir, :javascripts_dir,
-
:stylesheets_dir, :default_asset_host_protocol, :relative_url_root
-
end
-
end
-
end
-
1
require 'erubis'
-
1
require 'set'
-
1
require 'active_support/configurable'
-
1
require 'active_support/descendants_tracker'
-
1
require 'active_support/core_ext/module/anonymous'
-
-
1
module AbstractController
-
1
class Error < StandardError #:nodoc:
-
end
-
-
1
class ActionNotFound < StandardError #:nodoc:
-
end
-
-
# <tt>AbstractController::Base</tt> is a low-level API. Nobody should be
-
# using it directly, and subclasses (like ActionController::Base) are
-
# expected to provide their own +render+ method, since rendering means
-
# different things depending on the context.
-
1
class Base
-
1
attr_internal :response_body
-
1
attr_internal :action_name
-
1
attr_internal :formats
-
-
1
include ActiveSupport::Configurable
-
1
extend ActiveSupport::DescendantsTracker
-
-
1
undef_method :not_implemented
-
1
class << self
-
1
attr_reader :abstract
-
1
alias_method :abstract?, :abstract
-
-
# Define a controller as abstract. See internal_methods for more
-
# details.
-
1
def abstract!
-
4
@abstract = true
-
end
-
-
1
def inherited(klass) # :nodoc:
-
# Define the abstract ivar on subclasses so that we don't get
-
# uninitialized ivar warnings
-
6
unless klass.instance_variable_defined?(:@abstract)
-
6
klass.instance_variable_set(:@abstract, false)
-
end
-
6
super
-
end
-
-
# A list of all internal methods for a controller. This finds the first
-
# abstract superclass of a controller, and gets a list of all public
-
# instance methods on that abstract class. Public instance methods of
-
# a controller would normally be considered action methods, so methods
-
# declared on abstract classes are being removed.
-
# (ActionController::Metal and ActionController::Base are defined as abstract)
-
1
def internal_methods
-
2
controller = self
-
-
2
controller = controller.superclass until controller.abstract?
-
2
controller.public_instance_methods(true)
-
end
-
-
# The list of hidden actions. Defaults to an empty array.
-
# This can be modified by other modules or subclasses
-
# to specify particular actions as hidden.
-
#
-
# ==== Returns
-
# * <tt>Array</tt> - An array of method names that should not be considered actions.
-
1
def hidden_actions
-
1
[]
-
end
-
-
# A list of method names that should be considered actions. This
-
# includes all public instance methods on a controller, less
-
# any internal methods (see #internal_methods), adding back in
-
# any methods that are internal, but still exist on the class
-
# itself. Finally, #hidden_actions are removed.
-
#
-
# ==== Returns
-
# * <tt>Set</tt> - A set of all methods that should be considered actions.
-
1
def action_methods
-
@action_methods ||= begin
-
# All public instance methods of this class, including ancestors
-
2
methods = (public_instance_methods(true) -
-
# Except for public instance methods of Base and its ancestors
-
internal_methods +
-
# Be sure to include shadowed public instance methods of this class
-
150
public_instance_methods(false)).uniq.map { |x| x.to_s } -
-
# And always exclude explicitly hidden actions
-
hidden_actions.to_a
-
-
# Clear out AS callback method pollution
-
150
Set.new(methods.reject { |method| method =~ /_one_time_conditions/ })
-
2
end
-
end
-
-
# action_methods are cached and there is sometimes need to refresh
-
# them. clear_action_methods! allows you to do that, so next time
-
# you run action_methods, they will be recalculated
-
1
def clear_action_methods!
-
221
@action_methods = nil
-
end
-
-
# Returns the full controller name, underscored, without the ending Controller.
-
# For instance, MyApp::MyPostsController would return "my_app/my_posts" for
-
# controller_path.
-
#
-
# ==== Returns
-
# * <tt>String</tt>
-
1
def controller_path
-
19
@controller_path ||= name.sub(/Controller$/, '').underscore unless anonymous?
-
end
-
-
# Refresh the cached action_methods when a new action_method is added.
-
1
def method_added(name)
-
221
super
-
221
clear_action_methods!
-
end
-
end
-
-
1
abstract!
-
-
# Calls the action going through the entire action dispatch stack.
-
#
-
# The actual method that is called is determined by calling
-
# #method_for_action. If no method can handle the action, then an
-
# ActionNotFound error is raised.
-
#
-
# ==== Returns
-
# * <tt>self</tt>
-
1
def process(action, *args)
-
4
@_action_name = action_name = action.to_s
-
-
4
unless action_name = _find_action_name(action_name)
-
raise ActionNotFound, "The action '#{action}' could not be found for #{self.class.name}"
-
end
-
-
4
@_response_body = nil
-
-
4
process_action(action_name, *args)
-
end
-
-
# Delegates to the class' #controller_path
-
1
def controller_path
-
4
self.class.controller_path
-
end
-
-
# Delegates to the class' #action_methods
-
1
def action_methods
-
self.class.action_methods
-
end
-
-
# Returns true if a method for the action is available and
-
# can be dispatched, false otherwise.
-
#
-
# Notice that <tt>action_methods.include?("foo")</tt> may return
-
# false and <tt>available_action?("foo")</tt> returns true because
-
# this method considers actions that are also available
-
# through other means, for example, implicit render ones.
-
#
-
# ==== Parameters
-
# * <tt>action_name</tt> - The name of an action to be tested
-
#
-
# ==== Returns
-
# * <tt>TrueClass</tt>, <tt>FalseClass</tt>
-
1
def available_action?(action_name)
-
_find_action_name(action_name).present?
-
end
-
-
1
private
-
-
# Returns true if the name can be considered an action because
-
# it has a method defined in the controller.
-
#
-
# ==== Parameters
-
# * <tt>name</tt> - The name of an action to be tested
-
#
-
# ==== Returns
-
# * <tt>TrueClass</tt>, <tt>FalseClass</tt>
-
#
-
# :api: private
-
1
def action_method?(name)
-
4
self.class.action_methods.include?(name)
-
end
-
-
# Call the action. Override this in a subclass to modify the
-
# behavior around processing an action. This, and not #process,
-
# is the intended way to override action dispatching.
-
#
-
# Notice that the first argument is the method to be dispatched
-
# which is *not* necessarily the same as the action name.
-
1
def process_action(method_name, *args)
-
4
send_action(method_name, *args)
-
end
-
-
# Actually call the method associated with the action. Override
-
# this method if you wish to change how action methods are called,
-
# not to add additional behavior around it. For example, you would
-
# override #send_action if you want to inject arguments into the
-
# method.
-
1
alias send_action send
-
-
# If the action name was not found, but a method called "action_missing"
-
# was found, #method_for_action will return "_handle_action_missing".
-
# This method calls #action_missing with the current action name.
-
1
def _handle_action_missing(*args)
-
action_missing(@_action_name, *args)
-
end
-
-
# Takes an action name and returns the name of the method that will
-
# handle the action.
-
#
-
# It checks if the action name is valid and returns false otherwise.
-
#
-
# See method_for_action for more information.
-
#
-
# ==== Parameters
-
# * <tt>action_name</tt> - An action name to find a method name for
-
#
-
# ==== Returns
-
# * <tt>string</tt> - The name of the method that handles the action
-
# * false - No valid method name could be found. Raise ActionNotFound.
-
1
def _find_action_name(action_name)
-
4
_valid_action_name?(action_name) && method_for_action(action_name)
-
end
-
-
# Takes an action name and returns the name of the method that will
-
# handle the action. In normal cases, this method returns the same
-
# name as it receives. By default, if #method_for_action receives
-
# a name that is not an action, it will look for an #action_missing
-
# method and return "_handle_action_missing" if one is found.
-
#
-
# Subclasses may override this method to add additional conditions
-
# that should be considered an action. For instance, an HTTP controller
-
# with a template matching the action name is considered to exist.
-
#
-
# If you override this method to handle additional cases, you may
-
# also provide a method (like _handle_method_missing) to handle
-
# the case.
-
#
-
# If none of these conditions are true, and method_for_action
-
# returns nil, an ActionNotFound exception will be raised.
-
#
-
# ==== Parameters
-
# * <tt>action_name</tt> - An action name to find a method name for
-
#
-
# ==== Returns
-
# * <tt>string</tt> - The name of the method that handles the action
-
# * <tt>nil</tt> - No method name could be found.
-
1
def method_for_action(action_name)
-
4
if action_method?(action_name)
-
4
action_name
-
elsif respond_to?(:action_missing, true)
-
"_handle_action_missing"
-
end
-
end
-
-
# Checks if the action name is valid and returns false otherwise.
-
1
def _valid_action_name?(action_name)
-
4
!action_name.to_s.include? File::SEPARATOR
-
end
-
end
-
end
-
1
module AbstractController
-
1
module Callbacks
-
1
extend ActiveSupport::Concern
-
-
# Uses ActiveSupport::Callbacks as the base functionality. For
-
# more details on the whole callback system, read the documentation
-
# for ActiveSupport::Callbacks.
-
1
include ActiveSupport::Callbacks
-
-
1
included do
-
2
define_callbacks :process_action,
-
4
terminator: ->(controller,_) { controller.response_body },
-
skip_after_callbacks_if_terminated: true
-
end
-
-
# Override AbstractController::Base's process_action to run the
-
# process_action callbacks around the normal behavior.
-
1
def process_action(*args)
-
4
run_callbacks(:process_action) do
-
4
super
-
end
-
end
-
-
1
module ClassMethods
-
# If :only or :except are used, convert the options into the
-
# :unless and :if options of ActiveSupport::Callbacks.
-
# The basic idea is that :only => :index gets converted to
-
# :if => proc {|c| c.action_name == "index" }.
-
#
-
# ==== Options
-
# * <tt>only</tt> - The callback should be run only for this action
-
# * <tt>except</tt> - The callback should be run for all actions except this action
-
1
def _normalize_callback_options(options)
-
3
_normalize_callback_option(options, :only, :if)
-
3
_normalize_callback_option(options, :except, :unless)
-
end
-
-
1
def _normalize_callback_option(options, from, to) # :nodoc:
-
6
if from = options[from]
-
5
from = Array(from).map {|o| "action_name == '#{o}'"}.join(" || ")
-
1
options[to] = Array(options[to]).unshift(from)
-
end
-
end
-
-
# Skip before, after, and around action callbacks matching any of the names
-
# Aliased as skip_filter.
-
#
-
# ==== Parameters
-
# * <tt>names</tt> - A list of valid names that could be used for
-
# callbacks. Note that skipping uses Ruby equality, so it's
-
# impossible to skip a callback defined using an anonymous proc
-
# using #skip_filter
-
1
def skip_action_callback(*names)
-
skip_before_action(*names)
-
skip_after_action(*names)
-
skip_around_action(*names)
-
end
-
-
1
alias_method :skip_filter, :skip_action_callback
-
-
# Take callback names and an optional callback proc, normalize them,
-
# then call the block with each callback. This allows us to abstract
-
# the normalization across several methods that use it.
-
#
-
# ==== Parameters
-
# * <tt>callbacks</tt> - An array of callbacks, with an optional
-
# options hash as the last parameter.
-
# * <tt>block</tt> - A proc that should be added to the callbacks.
-
#
-
# ==== Block Parameters
-
# * <tt>name</tt> - The callback to be added
-
# * <tt>options</tt> - A hash of options to be used when adding the callback
-
1
def _insert_callbacks(callbacks, block = nil)
-
3
options = callbacks.extract_options!
-
3
_normalize_callback_options(options)
-
3
callbacks.push(block) if block
-
3
callbacks.each do |callback|
-
3
yield callback, options
-
end
-
end
-
-
##
-
# :method: before_action
-
#
-
# :call-seq: before_action(names, block)
-
#
-
# Append a callback before actions. See _insert_callbacks for parameter details.
-
# Aliased as before_filter.
-
-
##
-
# :method: prepend_before_action
-
#
-
# :call-seq: prepend_before_action(names, block)
-
#
-
# Prepend a callback before actions. See _insert_callbacks for parameter details.
-
# Aliased as prepend_before_filter.
-
-
##
-
# :method: skip_before_action
-
#
-
# :call-seq: skip_before_action(names)
-
#
-
# Skip a callback before actions. See _insert_callbacks for parameter details.
-
# Aliased as skip_before_filter.
-
-
##
-
# :method: append_before_action
-
#
-
# :call-seq: append_before_action(names, block)
-
#
-
# Append a callback before actions. See _insert_callbacks for parameter details.
-
# Aliased as append_before_filter.
-
-
##
-
# :method: after_action
-
#
-
# :call-seq: after_action(names, block)
-
#
-
# Append a callback after actions. See _insert_callbacks for parameter details.
-
# Aliased as after_filter.
-
-
##
-
# :method: prepend_after_action
-
#
-
# :call-seq: prepend_after_action(names, block)
-
#
-
# Prepend a callback after actions. See _insert_callbacks for parameter details.
-
# Aliased as prepend_after_filter.
-
-
##
-
# :method: skip_after_action
-
#
-
# :call-seq: skip_after_action(names)
-
#
-
# Skip a callback after actions. See _insert_callbacks for parameter details.
-
# Aliased as skip_after_filter.
-
-
##
-
# :method: append_after_action
-
#
-
# :call-seq: append_after_action(names, block)
-
#
-
# Append a callback after actions. See _insert_callbacks for parameter details.
-
# Aliased as append_after_filter.
-
-
##
-
# :method: around_action
-
#
-
# :call-seq: around_action(names, block)
-
#
-
# Append a callback around actions. See _insert_callbacks for parameter details.
-
# Aliased as around_filter.
-
-
##
-
# :method: prepend_around_action
-
#
-
# :call-seq: prepend_around_action(names, block)
-
#
-
# Prepend a callback around actions. See _insert_callbacks for parameter details.
-
# Aliased as prepend_around_filter.
-
-
##
-
# :method: skip_around_action
-
#
-
# :call-seq: skip_around_action(names)
-
#
-
# Skip a callback around actions. See _insert_callbacks for parameter details.
-
# Aliased as skip_around_filter.
-
-
##
-
# :method: append_around_action
-
#
-
# :call-seq: append_around_action(names, block)
-
#
-
# Append a callback around actions. See _insert_callbacks for parameter details.
-
# Aliased as append_around_filter.
-
-
# set up before_action, prepend_before_action, skip_before_action, etc.
-
# for each of before, after, and around.
-
1
[:before, :after, :around].each do |callback|
-
3
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
-
# Append a before, after or around callback. See _insert_callbacks
-
# for details on the allowed parameters.
-
def #{callback}_action(*names, &blk) # def before_action(*names, &blk)
-
_insert_callbacks(names, blk) do |name, options| # _insert_callbacks(names, blk) do |name, options|
-
set_callback(:process_action, :#{callback}, name, options) # set_callback(:process_action, :before, name, options)
-
end # end
-
end # end
-
-
alias_method :#{callback}_filter, :#{callback}_action
-
-
# Prepend a before, after or around callback. See _insert_callbacks
-
# for details on the allowed parameters.
-
def prepend_#{callback}_action(*names, &blk) # def prepend_before_action(*names, &blk)
-
_insert_callbacks(names, blk) do |name, options| # _insert_callbacks(names, blk) do |name, options|
-
set_callback(:process_action, :#{callback}, name, options.merge(:prepend => true)) # set_callback(:process_action, :before, name, options.merge(:prepend => true))
-
end # end
-
end # end
-
-
alias_method :prepend_#{callback}_filter, :prepend_#{callback}_action
-
-
# Skip a before, after or around callback. See _insert_callbacks
-
# for details on the allowed parameters.
-
def skip_#{callback}_action(*names) # def skip_before_action(*names)
-
_insert_callbacks(names) do |name, options| # _insert_callbacks(names) do |name, options|
-
skip_callback(:process_action, :#{callback}, name, options) # skip_callback(:process_action, :before, name, options)
-
end # end
-
end # end
-
-
alias_method :skip_#{callback}_filter, :skip_#{callback}_action
-
-
# *_action is the same as append_*_action
-
alias_method :append_#{callback}_action, :#{callback}_action # alias_method :append_before_action, :before_action
-
alias_method :append_#{callback}_filter, :#{callback}_action # alias_method :append_before_filter, :before_action
-
RUBY_EVAL
-
end
-
end
-
end
-
end
-
1
require "action_dispatch/http/mime_type"
-
-
1
module AbstractController
-
1
module Collector
-
1
def self.generate_method_for_mime(mime)
-
22
sym = mime.is_a?(Symbol) ? mime : mime.to_sym
-
22
const = sym.upcase
-
22
class_eval <<-RUBY, __FILE__, __LINE__ + 1
-
def #{sym}(*args, &block) # def html(*args, &block)
-
custom(Mime::#{const}, *args, &block) # custom(Mime::HTML, *args, &block)
-
end # end
-
RUBY
-
end
-
-
1
Mime::SET.each do |mime|
-
22
generate_method_for_mime(mime)
-
end
-
-
1
Mime::Type.register_callback do |mime|
-
generate_method_for_mime(mime) unless self.instance_methods.include?(mime.to_sym)
-
end
-
-
1
protected
-
-
1
def method_missing(symbol, &block)
-
const_name = symbol.upcase
-
-
unless Mime.const_defined?(const_name)
-
raise NoMethodError, "To respond to a custom format, register it as a MIME type first: " \
-
"http://guides.rubyonrails.org/action_controller_overview.html#restful-downloads. " \
-
"If you meant to respond to a variant like :tablet or :phone, not a custom format, " \
-
"be sure to nest your variant response within a format response: " \
-
"format.html { |html| html.tablet { ... } }"
-
end
-
-
mime_constant = Mime.const_get(const_name)
-
-
if Mime::SET.include?(mime_constant)
-
AbstractController::Collector.generate_method_for_mime(mime_constant)
-
send(symbol, &block)
-
else
-
super
-
end
-
end
-
end
-
end
-
1
require 'active_support/dependencies'
-
-
1
module AbstractController
-
1
module Helpers
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
3
class_attribute :_helpers
-
3
self._helpers = Module.new
-
-
3
class_attribute :_helper_methods
-
3
self._helper_methods = Array.new
-
end
-
-
1
class MissingHelperError < LoadError
-
1
def initialize(error, path)
-
2
@error = error
-
2
@path = "helpers/#{path}.rb"
-
2
set_backtrace error.backtrace
-
-
2
if error.path =~ /^#{path}(\.rb)?$/
-
2
super("Missing helper file helpers/%s.rb" % path)
-
else
-
raise error
-
end
-
end
-
end
-
-
1
module ClassMethods
-
1
MissingHelperError = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('AbstractController::Helpers::ClassMethods::MissingHelperError',
-
'AbstractController::Helpers::MissingHelperError')
-
-
# When a class is inherited, wrap its helper module in a new module.
-
# This ensures that the parent class's module can be changed
-
# independently of the child class's.
-
1
def inherited(klass)
-
3
helpers = _helpers
-
6
klass._helpers = Module.new { include helpers }
-
6
klass.class_eval { default_helper_module! } unless klass.anonymous?
-
3
super
-
end
-
-
# Declare a controller method as a helper. For example, the following
-
# makes the +current_user+ controller method available to the view:
-
# class ApplicationController < ActionController::Base
-
# helper_method :current_user, :logged_in?
-
#
-
# def current_user
-
# @current_user ||= User.find_by(id: session[:user])
-
# end
-
#
-
# def logged_in?
-
# current_user != nil
-
# end
-
# end
-
#
-
# In a view:
-
# <% if logged_in? -%>Welcome, <%= current_user.name %><% end -%>
-
#
-
# ==== Parameters
-
# * <tt>method[, method]</tt> - A name or names of a method on the controller
-
# to be made available on the view.
-
1
def helper_method(*meths)
-
6
meths.flatten!
-
6
self._helper_methods += meths
-
-
6
meths.each do |meth|
-
6
_helpers.class_eval <<-ruby_eval, __FILE__, __LINE__ + 1
-
def #{meth}(*args, &blk) # def current_user(*args, &blk)
-
controller.send(%(#{meth}), *args, &blk) # controller.send(:current_user, *args, &blk)
-
end # end
-
ruby_eval
-
end
-
end
-
-
# The +helper+ class method can take a series of helper module names, a block, or both.
-
#
-
# ==== Options
-
# * <tt>*args</tt> - Module, Symbol, String
-
# * <tt>block</tt> - A block defining helper methods
-
#
-
# When the argument is a module it will be included directly in the template class.
-
# helper FooHelper # => includes FooHelper
-
#
-
# When the argument is a string or symbol, the method will provide the "_helper" suffix, require the file
-
# and include the module in the template class. The second form illustrates how to include custom helpers
-
# when working with namespaced controllers, or other cases where the file containing the helper definition is not
-
# in one of Rails' standard load paths:
-
# helper :foo # => requires 'foo_helper' and includes FooHelper
-
# helper 'resources/foo' # => requires 'resources/foo_helper' and includes Resources::FooHelper
-
#
-
# Additionally, the +helper+ class method can receive and evaluate a block, making the methods defined available
-
# to the template.
-
#
-
# # One line
-
# helper { def hello() "Hello, world!" end }
-
#
-
# # Multi-line
-
# helper do
-
# def foo(bar)
-
# "#{bar} is the very best"
-
# end
-
# end
-
#
-
# Finally, all the above styles can be mixed together, and the +helper+ method can be invoked with a mix of
-
# +symbols+, +strings+, +modules+ and blocks.
-
#
-
# helper(:three, BlindHelper) { def mice() 'mice' end }
-
#
-
1
def helper(*args, &block)
-
6
modules_for_helpers(args).each do |mod|
-
8
add_template_helper(mod)
-
end
-
-
4
_helpers.module_eval(&block) if block_given?
-
end
-
-
# Clears up all existing helpers in this class, only keeping the helper
-
# with the same name as this class.
-
1
def clear_helpers
-
inherited_helper_methods = _helper_methods
-
self._helpers = Module.new
-
self._helper_methods = Array.new
-
-
inherited_helper_methods.each { |meth| helper_method meth }
-
default_helper_module! unless anonymous?
-
end
-
-
# Returns a list of modules, normalized from the acceptable kinds of
-
# helpers with the following behavior:
-
#
-
# String or Symbol:: :FooBar or "FooBar" becomes "foo_bar_helper",
-
# and "foo_bar_helper.rb" is loaded using require_dependency.
-
#
-
# Module:: No further processing
-
#
-
# After loading the appropriate files, the corresponding modules
-
# are returned.
-
#
-
# ==== Parameters
-
# * <tt>args</tt> - An array of helpers
-
#
-
# ==== Returns
-
# * <tt>Array</tt> - A normalized list of modules for the list of
-
# helpers provided.
-
1
def modules_for_helpers(args)
-
6
args.flatten.map! do |arg|
-
10
case arg
-
when String, Symbol
-
9
file_name = "#{arg.to_s.underscore}_helper"
-
9
begin
-
9
require_dependency(file_name)
-
rescue LoadError => e
-
2
raise AbstractController::Helpers::MissingHelperError.new(e, file_name)
-
end
-
7
file_name.camelize.constantize
-
when Module
-
1
arg
-
else
-
raise ArgumentError, "helper must be a String, Symbol, or Module"
-
end
-
end
-
end
-
-
1
private
-
# Makes all the (instance) methods in the helper module available to templates
-
# rendered through this controller.
-
#
-
# ==== Parameters
-
# * <tt>module</tt> - The module to include into the current helper module
-
# for the class
-
1
def add_template_helper(mod)
-
16
_helpers.module_eval { include mod }
-
end
-
-
1
def default_helper_module!
-
3
module_name = name.sub(/Controller$/, '')
-
3
module_path = module_name.underscore
-
3
helper module_path
-
rescue MissingSourceFile => e
-
2
raise e unless e.is_missing? "helpers/#{module_path}_helper"
-
rescue NameError => e
-
raise e unless e.missing_name? "#{module_name}Helper"
-
end
-
end
-
end
-
end
-
1
require "active_support/benchmarkable"
-
-
1
module AbstractController
-
1
module Logger #:nodoc:
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
2
config_accessor :logger
-
2
include ActiveSupport::Benchmarkable
-
end
-
end
-
end
-
1
module AbstractController
-
1
module Railties
-
1
module RoutesHelpers
-
1
def self.with(routes)
-
2
Module.new do
-
2
define_method(:inherited) do |klass|
-
3
super(klass)
-
8
if namespace = klass.parents.detect { |m| m.respond_to?(:railtie_routes_url_helpers) }
-
klass.send(:include, namespace.railtie_routes_url_helpers)
-
else
-
3
klass.send(:include, routes.url_helpers)
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/concern'
-
1
require 'active_support/core_ext/class/attribute'
-
1
require 'action_view'
-
1
require 'action_view/view_paths'
-
1
require 'set'
-
-
1
module AbstractController
-
1
class DoubleRenderError < Error
-
1
DEFAULT_MESSAGE = "Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like \"redirect_to(...) and return\"."
-
-
1
def initialize(message = nil)
-
super(message || DEFAULT_MESSAGE)
-
end
-
end
-
-
1
module Rendering
-
1
extend ActiveSupport::Concern
-
1
include ActionView::ViewPaths
-
-
# Normalize arguments, options and then delegates render_to_body and
-
# sticks the result in self.response_body.
-
# :api: public
-
1
def render(*args, &block)
-
2
options = _normalize_render(*args, &block)
-
2
self.response_body = render_to_body(options)
-
2
_process_format(rendered_format, options) if rendered_format
-
2
self.response_body
-
end
-
-
# Raw rendering of a template to a string.
-
#
-
# It is similar to render, except that it does not
-
# set the response_body and it should be guaranteed
-
# to always return a string.
-
#
-
# If a component extends the semantics of response_body
-
# (as Action Controller extends it to be anything that
-
# responds to the method each), this method needs to be
-
# overridden in order to still return a string.
-
# :api: plugin
-
1
def render_to_string(*args, &block)
-
options = _normalize_render(*args, &block)
-
render_to_body(options)
-
end
-
-
# Performs the actual template rendering.
-
# :api: public
-
1
def render_to_body(options = {})
-
end
-
-
# Returns Content-Type of rendered content
-
# :api: public
-
1
def rendered_format
-
Mime::TEXT
-
end
-
-
1
DEFAULT_PROTECTED_INSTANCE_VARIABLES = Set.new %w(
-
@_action_name @_response_body @_formats @_prefixes @_config
-
@_view_context_class @_view_renderer @_lookup_context
-
@_routes @_db_runtime
-
).map(&:to_sym)
-
-
# This method should return a hash with assigns.
-
# You can overwrite this configuration per controller.
-
# :api: public
-
1
def view_assigns
-
7
protected_vars = _protected_ivars
-
7
variables = instance_variables
-
-
139
variables.reject! { |s| protected_vars.include? s }
-
7
variables.each_with_object({}) { |name, hash|
-
16
hash[name.slice(1, name.length)] = instance_variable_get(name)
-
}
-
end
-
-
# Normalize args by converting render "foo" to render :action => "foo" and
-
# render "foo/bar" to render :file => "foo/bar".
-
# :api: plugin
-
1
def _normalize_args(action=nil, options={})
-
2
if action.is_a? Hash
-
action
-
else
-
2
options
-
end
-
end
-
-
# Normalize options.
-
# :api: plugin
-
1
def _normalize_options(options)
-
2
options
-
end
-
-
# Process extra options.
-
# :api: plugin
-
1
def _process_options(options)
-
2
options
-
end
-
-
# Process the rendered format.
-
# :api: private
-
1
def _process_format(format, options = {})
-
end
-
-
# Normalize args and options.
-
# :api: private
-
1
def _normalize_render(*args, &block)
-
2
options = _normalize_args(*args, &block)
-
#TODO: remove defined? when we restore AP <=> AV dependency
-
2
if defined?(request) && request && request.variant.present?
-
options[:variant] = request.variant
-
end
-
2
_normalize_options(options)
-
2
options
-
end
-
-
1
def _protected_ivars # :nodoc:
-
DEFAULT_PROTECTED_INSTANCE_VARIABLES
-
end
-
end
-
end
-
1
module AbstractController
-
1
module Translation
-
# Delegates to <tt>I18n.translate</tt>. Also aliased as <tt>t</tt>.
-
#
-
# When the given key starts with a period, it will be scoped by the current
-
# controller and action. So if you call <tt>translate(".foo")</tt> from
-
# <tt>PeopleController#index</tt>, it will convert the call to
-
# <tt>I18n.translate("people.index.foo")</tt>. This makes it less repetitive
-
# to translate many keys within the same controller / action and gives you a
-
# simple framework for scoping them consistently.
-
1
def translate(*args)
-
key = args.first
-
if key.is_a?(String) && (key[0] == '.')
-
key = "#{ controller_path.tr('/', '.') }.#{ action_name }#{ key }"
-
args[0] = key
-
end
-
-
I18n.translate(*args)
-
end
-
1
alias :t :translate
-
-
# Delegates to <tt>I18n.localize</tt>. Also aliased as <tt>l</tt>.
-
1
def localize(*args)
-
I18n.localize(*args)
-
end
-
1
alias :l :localize
-
end
-
end
-
1
module AbstractController
-
# Includes +url_for+ into the host class (e.g. an abstract controller or mailer). The class
-
# has to provide a +RouteSet+ by implementing the <tt>_routes</tt> methods. Otherwise, an
-
# exception will be raised.
-
#
-
# Note that this module is completely decoupled from HTTP - the only requirement is a valid
-
# <tt>_routes</tt> implementation.
-
1
module UrlFor
-
1
extend ActiveSupport::Concern
-
1
include ActionDispatch::Routing::UrlFor
-
-
1
def _routes
-
raise "In order to use #url_for, you must include routing helpers explicitly. " \
-
"For instance, `include Rails.application.routes.url_helpers"
-
end
-
-
1
module ClassMethods
-
1
def _routes
-
nil
-
end
-
-
1
def action_methods
-
@action_methods ||= begin
-
1
if _routes
-
1
super - _routes.named_routes.helper_names
-
else
-
super
-
end
-
4
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/rails'
-
1
require 'abstract_controller'
-
1
require 'action_dispatch'
-
1
require 'action_controller/metal/live'
-
1
require 'action_controller/metal/strong_parameters'
-
-
1
module ActionController
-
1
extend ActiveSupport::Autoload
-
-
1
autoload :Base
-
1
autoload :Caching
-
1
autoload :Metal
-
1
autoload :Middleware
-
-
1
autoload_under "metal" do
-
1
autoload :Compatibility
-
1
autoload :ConditionalGet
-
1
autoload :Cookies
-
1
autoload :DataStreaming
-
1
autoload :Flash
-
1
autoload :ForceSSL
-
1
autoload :Head
-
1
autoload :Helpers
-
1
autoload :HideActions
-
1
autoload :HttpAuthentication
-
1
autoload :ImplicitRender
-
1
autoload :Instrumentation
-
1
autoload :MimeResponds
-
1
autoload :ParamsWrapper
-
1
autoload :RackDelegation
-
1
autoload :Redirecting
-
1
autoload :Renderers
-
1
autoload :Rendering
-
1
autoload :RequestForgeryProtection
-
1
autoload :Rescue
-
1
autoload :Responder
-
1
autoload :Streaming
-
1
autoload :StrongParameters
-
1
autoload :Testing
-
1
autoload :UrlFor
-
end
-
-
1
autoload :TestCase, 'action_controller/test_case'
-
1
autoload :TemplateAssertions, 'action_controller/test_case'
-
-
1
def self.eager_load!
-
super
-
ActionController::Caching.eager_load!
-
end
-
end
-
-
# Common Active Support usage in Action Controller
-
1
require 'active_support/core_ext/module/attribute_accessors'
-
1
require 'active_support/core_ext/load_error'
-
1
require 'active_support/core_ext/module/attr_internal'
-
1
require 'active_support/core_ext/name_error'
-
1
require 'active_support/core_ext/uri'
-
1
require 'active_support/inflector'
-
1
require 'action_view'
-
1
require "action_controller/log_subscriber"
-
1
require "action_controller/metal/params_wrapper"
-
-
1
module ActionController
-
# Action Controllers are the core of a web request in \Rails. They are made up of one or more actions that are executed
-
# on request and then either it renders a template or redirects to another action. An action is defined as a public method
-
# on the controller, which will automatically be made accessible to the web-server through \Rails Routes.
-
#
-
# By default, only the ApplicationController in a \Rails application inherits from <tt>ActionController::Base</tt>. All other
-
# controllers in turn inherit from ApplicationController. This gives you one class to configure things such as
-
# request forgery protection and filtering of sensitive request parameters.
-
#
-
# A sample controller could look like this:
-
#
-
# class PostsController < ApplicationController
-
# def index
-
# @posts = Post.all
-
# end
-
#
-
# def create
-
# @post = Post.create params[:post]
-
# redirect_to posts_path
-
# end
-
# end
-
#
-
# Actions, by default, render a template in the <tt>app/views</tt> directory corresponding to the name of the controller and action
-
# after executing code in the action. For example, the +index+ action of the PostsController would render the
-
# template <tt>app/views/posts/index.html.erb</tt> by default after populating the <tt>@posts</tt> instance variable.
-
#
-
# Unlike index, the create action will not render a template. After performing its main purpose (creating a
-
# new post), it initiates a redirect instead. This redirect works by returning an external
-
# "302 Moved" HTTP response that takes the user to the index action.
-
#
-
# These two methods represent the two basic action archetypes used in Action Controllers. Get-and-show and do-and-redirect.
-
# Most actions are variations on these themes.
-
#
-
# == Requests
-
#
-
# For every request, the router determines the value of the +controller+ and +action+ keys. These determine which controller
-
# and action are called. The remaining request parameters, the session (if one is available), and the full request with
-
# all the HTTP headers are made available to the action through accessor methods. Then the action is performed.
-
#
-
# The full request object is available via the request accessor and is primarily used to query for HTTP headers:
-
#
-
# def server_ip
-
# location = request.env["SERVER_ADDR"]
-
# render plain: "This server hosted at #{location}"
-
# end
-
#
-
# == Parameters
-
#
-
# All request parameters, whether they come from a GET or POST request, or from the URL, are available through the params method
-
# which returns a hash. For example, an action that was performed through <tt>/posts?category=All&limit=5</tt> will include
-
# <tt>{ "category" => "All", "limit" => "5" }</tt> in params.
-
#
-
# It's also possible to construct multi-dimensional parameter hashes by specifying keys using brackets, such as:
-
#
-
# <input type="text" name="post[name]" value="david">
-
# <input type="text" name="post[address]" value="hyacintvej">
-
#
-
# A request stemming from a form holding these inputs will include <tt>{ "post" => { "name" => "david", "address" => "hyacintvej" } }</tt>.
-
# If the address input had been named <tt>post[address][street]</tt>, the params would have included
-
# <tt>{ "post" => { "address" => { "street" => "hyacintvej" } } }</tt>. There's no limit to the depth of the nesting.
-
#
-
# == Sessions
-
#
-
# Sessions allow you to store objects in between requests. This is useful for objects that are not yet ready to be persisted,
-
# such as a Signup object constructed in a multi-paged process, or objects that don't change much and are needed all the time, such
-
# as a User object for a system that requires login. The session should not be used, however, as a cache for objects where it's likely
-
# they could be changed unknowingly. It's usually too much work to keep it all synchronized -- something databases already excel at.
-
#
-
# You can place objects in the session by using the <tt>session</tt> method, which accesses a hash:
-
#
-
# session[:person] = Person.authenticate(user_name, password)
-
#
-
# And retrieved again through the same hash:
-
#
-
# Hello #{session[:person]}
-
#
-
# For removing objects from the session, you can either assign a single key to +nil+:
-
#
-
# # removes :person from session
-
# session[:person] = nil
-
#
-
# or you can remove the entire session with +reset_session+.
-
#
-
# Sessions are stored by default in a browser cookie that's cryptographically signed, but unencrypted.
-
# This prevents the user from tampering with the session but also allows them to see its contents.
-
#
-
# Do not put secret information in cookie-based sessions!
-
#
-
# == Responses
-
#
-
# Each action results in a response, which holds the headers and document to be sent to the user's browser. The actual response
-
# object is generated automatically through the use of renders and redirects and requires no user intervention.
-
#
-
# == Renders
-
#
-
# Action Controller sends content to the user by using one of five rendering methods. The most versatile and common is the rendering
-
# of a template. Included in the Action Pack is the Action View, which enables rendering of ERB templates. It's automatically configured.
-
# The controller passes objects to the view by assigning instance variables:
-
#
-
# def show
-
# @post = Post.find(params[:id])
-
# end
-
#
-
# Which are then automatically available to the view:
-
#
-
# Title: <%= @post.title %>
-
#
-
# You don't have to rely on the automated rendering. For example, actions that could result in the rendering of different templates
-
# will use the manual rendering methods:
-
#
-
# def search
-
# @results = Search.find(params[:query])
-
# case @results.count
-
# when 0 then render action: "no_results"
-
# when 1 then render action: "show"
-
# when 2..10 then render action: "show_many"
-
# end
-
# end
-
#
-
# Read more about writing ERB and Builder templates in ActionView::Base.
-
#
-
# == Redirects
-
#
-
# Redirects are used to move from one action to another. For example, after a <tt>create</tt> action, which stores a blog entry to the
-
# database, we might like to show the user the new entry. Because we're following good DRY principles (Don't Repeat Yourself), we're
-
# going to reuse (and redirect to) a <tt>show</tt> action that we'll assume has already been created. The code might look like this:
-
#
-
# def create
-
# @entry = Entry.new(params[:entry])
-
# if @entry.save
-
# # The entry was saved correctly, redirect to show
-
# redirect_to action: 'show', id: @entry.id
-
# else
-
# # things didn't go so well, do something else
-
# end
-
# end
-
#
-
# In this case, after saving our new entry to the database, the user is redirected to the <tt>show</tt> method, which is then executed.
-
# Note that this is an external HTTP-level redirection which will cause the browser to make a second request (a GET to the show action),
-
# and not some internal re-routing which calls both "create" and then "show" within one request.
-
#
-
# Learn more about <tt>redirect_to</tt> and what options you have in ActionController::Redirecting.
-
#
-
# == Calling multiple redirects or renders
-
#
-
# An action may contain only a single render or a single redirect. Attempting to try to do either again will result in a DoubleRenderError:
-
#
-
# def do_something
-
# redirect_to action: "elsewhere"
-
# render action: "overthere" # raises DoubleRenderError
-
# end
-
#
-
# If you need to redirect on the condition of something, then be sure to add "and return" to halt execution.
-
#
-
# def do_something
-
# redirect_to(action: "elsewhere") and return if monkeys.nil?
-
# render action: "overthere" # won't be called if monkeys is nil
-
# end
-
#
-
1
class Base < Metal
-
1
abstract!
-
-
# We document the request and response methods here because albeit they are
-
# implemented in ActionController::Metal, the type of the returned objects
-
# is unknown at that level.
-
-
##
-
# :method: request
-
#
-
# Returns an ActionDispatch::Request instance that represents the
-
# current request.
-
-
##
-
# :method: response
-
#
-
# Returns an ActionDispatch::Response that represents the current
-
# response.
-
-
# Shortcut helper that returns all the modules included in
-
# ActionController::Base except the ones passed as arguments:
-
#
-
# class MetalController
-
# ActionController::Base.without_modules(:ParamsWrapper, :Streaming).each do |left|
-
# include left
-
# end
-
# end
-
#
-
# This gives better control over what you want to exclude and makes it
-
# easier to create a bare controller class, instead of listing the modules
-
# required manually.
-
1
def self.without_modules(*modules)
-
modules = modules.map do |m|
-
m.is_a?(Symbol) ? ActionController.const_get(m) : m
-
end
-
-
MODULES - modules
-
end
-
-
1
MODULES = [
-
AbstractController::Rendering,
-
AbstractController::Translation,
-
AbstractController::AssetPaths,
-
-
Helpers,
-
HideActions,
-
UrlFor,
-
Redirecting,
-
ActionView::Layouts,
-
Rendering,
-
Renderers::All,
-
ConditionalGet,
-
RackDelegation,
-
Caching,
-
MimeResponds,
-
ImplicitRender,
-
StrongParameters,
-
-
Cookies,
-
Flash,
-
RequestForgeryProtection,
-
ForceSSL,
-
Streaming,
-
DataStreaming,
-
HttpAuthentication::Basic::ControllerMethods,
-
HttpAuthentication::Digest::ControllerMethods,
-
HttpAuthentication::Token::ControllerMethods,
-
-
# Before callbacks should also be executed the earliest as possible, so
-
# also include them at the bottom.
-
AbstractController::Callbacks,
-
-
# Append rescue at the bottom to wrap as much as possible.
-
Rescue,
-
-
# Add instrumentations hooks at the bottom, to ensure they instrument
-
# all the methods properly.
-
Instrumentation,
-
-
# Params wrapper should come before instrumentation so they are
-
# properly showed in logs
-
ParamsWrapper
-
]
-
-
1
MODULES.each do |mod|
-
29
include mod
-
end
-
-
# Define some internal variables that should not be propagated to the view.
-
1
PROTECTED_IVARS = AbstractController::Rendering::DEFAULT_PROTECTED_INSTANCE_VARIABLES + [
-
:@_status, :@_headers, :@_params, :@_env, :@_response, :@_request,
-
:@_view_runtime, :@_stream, :@_url_options, :@_action_has_layout ]
-
-
1
def _protected_ivars # :nodoc:
-
7
PROTECTED_IVARS
-
end
-
-
1
def self.protected_instance_variables
-
PROTECTED_IVARS
-
end
-
-
1
ActiveSupport.run_load_hooks(:action_controller, self)
-
end
-
end
-
1
require 'fileutils'
-
1
require 'uri'
-
1
require 'set'
-
-
1
module ActionController
-
# \Caching is a cheap way of speeding up slow applications by keeping the result of
-
# calculations, renderings, and database calls around for subsequent requests.
-
#
-
# You can read more about each approach by clicking the modules below.
-
#
-
# Note: To turn off all caching, set
-
# config.action_controller.perform_caching = false
-
#
-
# == \Caching stores
-
#
-
# All the caching stores from ActiveSupport::Cache are available to be used as backends
-
# for Action Controller caching.
-
#
-
# Configuration examples (MemoryStore is the default):
-
#
-
# config.action_controller.cache_store = :memory_store
-
# config.action_controller.cache_store = :file_store, '/path/to/cache/directory'
-
# config.action_controller.cache_store = :mem_cache_store, 'localhost'
-
# config.action_controller.cache_store = :mem_cache_store, Memcached::Rails.new('localhost:11211')
-
# config.action_controller.cache_store = MyOwnStore.new('parameter')
-
1
module Caching
-
1
extend ActiveSupport::Concern
-
1
extend ActiveSupport::Autoload
-
-
1
eager_autoload do
-
1
autoload :Fragments
-
end
-
-
1
module ConfigMethods
-
1
def cache_store
-
config.cache_store
-
end
-
-
1
def cache_store=(store)
-
1
config.cache_store = ActiveSupport::Cache.lookup_store(store)
-
end
-
-
1
private
-
1
def cache_configured?
-
perform_caching && cache_store
-
end
-
end
-
-
1
include RackDelegation
-
1
include AbstractController::Callbacks
-
-
1
include ConfigMethods
-
1
include Fragments
-
-
1
included do
-
1
extend ConfigMethods
-
-
1
config_accessor :default_static_extension
-
1
self.default_static_extension ||= '.html'
-
-
1
config_accessor :perform_caching
-
1
self.perform_caching = true if perform_caching.nil?
-
-
1
class_attribute :_view_cache_dependencies
-
1
self._view_cache_dependencies = []
-
1
helper_method :view_cache_dependencies if respond_to?(:helper_method)
-
end
-
-
1
module ClassMethods
-
1
def view_cache_dependency(&dependency)
-
self._view_cache_dependencies += [dependency]
-
end
-
end
-
-
1
def view_cache_dependencies
-
self.class._view_cache_dependencies.map { |dep| instance_exec(&dep) }.compact
-
end
-
-
1
protected
-
# Convenience accessor.
-
1
def cache(key, options = {}, &block)
-
if cache_configured?
-
cache_store.fetch(ActiveSupport::Cache.expand_cache_key(key, :controller), options, &block)
-
else
-
yield
-
end
-
end
-
end
-
end
-
1
module ActionController
-
1
module Caching
-
# Fragment caching is used for caching various blocks within
-
# views without caching the entire action as a whole. This is
-
# useful when certain elements of an action change frequently or
-
# depend on complicated state while other parts rarely change or
-
# can be shared amongst multiple parties. The caching is done using
-
# the +cache+ helper available in the Action View. See
-
# ActionView::Helpers::CacheHelper for more information.
-
#
-
# While it's strongly recommended that you use key-based cache
-
# expiration (see links in CacheHelper for more information),
-
# it is also possible to manually expire caches. For example:
-
#
-
# expire_fragment('name_of_cache')
-
1
module Fragments
-
# Given a key (as described in +expire_fragment+), returns
-
# a key suitable for use in reading, writing, or expiring a
-
# cached fragment. All keys are prefixed with <tt>views/</tt> and uses
-
# ActiveSupport::Cache.expand_cache_key for the expansion.
-
1
def fragment_cache_key(key)
-
ActiveSupport::Cache.expand_cache_key(key.is_a?(Hash) ? url_for(key).split("://").last : key, :views)
-
end
-
-
# Writes +content+ to the location signified by
-
# +key+ (see +expire_fragment+ for acceptable formats).
-
1
def write_fragment(key, content, options = nil)
-
return content unless cache_configured?
-
-
key = fragment_cache_key(key)
-
instrument_fragment_cache :write_fragment, key do
-
content = content.to_str
-
cache_store.write(key, content, options)
-
end
-
content
-
end
-
-
# Reads a cached fragment from the location signified by +key+
-
# (see +expire_fragment+ for acceptable formats).
-
1
def read_fragment(key, options = nil)
-
return unless cache_configured?
-
-
key = fragment_cache_key(key)
-
instrument_fragment_cache :read_fragment, key do
-
result = cache_store.read(key, options)
-
result.respond_to?(:html_safe) ? result.html_safe : result
-
end
-
end
-
-
# Check if a cached fragment from the location signified by
-
# +key+ exists (see +expire_fragment+ for acceptable formats).
-
1
def fragment_exist?(key, options = nil)
-
return unless cache_configured?
-
key = fragment_cache_key(key)
-
-
instrument_fragment_cache :exist_fragment?, key do
-
cache_store.exist?(key, options)
-
end
-
end
-
-
# Removes fragments from the cache.
-
#
-
# +key+ can take one of three forms:
-
#
-
# * String - This would normally take the form of a path, like
-
# <tt>pages/45/notes</tt>.
-
# * Hash - Treated as an implicit call to +url_for+, like
-
# <tt>{ controller: 'pages', action: 'notes', id: 45}</tt>
-
# * Regexp - Will remove any fragment that matches, so
-
# <tt>%r{pages/\d*/notes}</tt> might remove all notes. Make sure you
-
# don't use anchors in the regex (<tt>^</tt> or <tt>$</tt>) because
-
# the actual filename matched looks like
-
# <tt>./cache/filename/path.cache</tt>. Note: Regexp expiration is
-
# only supported on caches that can iterate over all keys (unlike
-
# memcached).
-
#
-
# +options+ is passed through to the cache store's +delete+
-
# method (or <tt>delete_matched</tt>, for Regexp keys).
-
1
def expire_fragment(key, options = nil)
-
return unless cache_configured?
-
key = fragment_cache_key(key) unless key.is_a?(Regexp)
-
-
instrument_fragment_cache :expire_fragment, key do
-
if key.is_a?(Regexp)
-
cache_store.delete_matched(key, options)
-
else
-
cache_store.delete(key, options)
-
end
-
end
-
end
-
-
1
def instrument_fragment_cache(name, key) # :nodoc:
-
ActiveSupport::Notifications.instrument("#{name}.action_controller", :key => key){ yield }
-
end
-
end
-
end
-
end
-
-
1
module ActionController
-
1
class LogSubscriber < ActiveSupport::LogSubscriber
-
1
INTERNAL_PARAMS = %w(controller action format _method only_path)
-
-
1
def start_processing(event)
-
4
return unless logger.info?
-
-
4
payload = event.payload
-
4
params = payload[:params].except(*INTERNAL_PARAMS)
-
4
format = payload[:format]
-
4
format = format.to_s.upcase if format.is_a?(Symbol)
-
-
4
info "Processing by #{payload[:controller]}##{payload[:action]} as #{format}"
-
4
info " Parameters: #{params.inspect}" unless params.empty?
-
end
-
-
1
def process_action(event)
-
4
return unless logger.info?
-
-
4
payload = event.payload
-
4
additions = ActionController::Base.log_process_action(payload)
-
-
4
status = payload[:status]
-
4
if status.nil? && payload[:exception].present?
-
exception_class_name = payload[:exception].first
-
status = ActionDispatch::ExceptionWrapper.status_code_for_exception(exception_class_name)
-
end
-
4
message = "Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in #{event.duration.round}ms"
-
4
message << " (#{additions.join(" | ")})" unless additions.blank?
-
-
4
info(message)
-
end
-
-
1
def halted_callback(event)
-
info("Filter chain halted as #{event.payload[:filter].inspect} rendered or redirected")
-
end
-
-
1
def send_file(event)
-
info("Sent file #{event.payload[:path]} (#{event.duration.round(1)}ms)")
-
end
-
-
1
def redirect_to(event)
-
2
info("Redirected to #{event.payload[:location]}")
-
end
-
-
1
def send_data(event)
-
info("Sent data #{event.payload[:filename]} (#{event.duration.round(1)}ms)")
-
end
-
-
1
def unpermitted_parameters(event)
-
unpermitted_keys = event.payload[:keys]
-
debug("Unpermitted parameters: #{unpermitted_keys.join(", ")}")
-
end
-
-
1
def deep_munge(event)
-
message = "Value for params[:#{event.payload[:keys].join('][:')}] was set "\
-
"to nil, because it was one of [], [null] or [null, null, ...]. "\
-
"Go to http://guides.rubyonrails.org/security.html#unsafe-query-generation "\
-
"for more information."\
-
-
debug(message)
-
end
-
-
%w(write_fragment read_fragment exist_fragment?
-
1
expire_fragment expire_page write_page).each do |method|
-
6
class_eval <<-METHOD, __FILE__, __LINE__ + 1
-
def #{method}(event)
-
return unless logger.info?
-
key_or_path = event.payload[:key] || event.payload[:path]
-
human_name = #{method.to_s.humanize.inspect}
-
info("\#{human_name} \#{key_or_path} (\#{event.duration.round(1)}ms)")
-
end
-
METHOD
-
end
-
-
1
def logger
-
52
ActionController::Base.logger
-
end
-
end
-
end
-
-
1
ActionController::LogSubscriber.attach_to :action_controller
-
1
require 'active_support/core_ext/array/extract_options'
-
1
require 'action_dispatch/middleware/stack'
-
-
1
module ActionController
-
# Extend ActionDispatch middleware stack to make it aware of options
-
# allowing the following syntax in controllers:
-
#
-
# class PostsController < ApplicationController
-
# use AuthenticationMiddleware, except: [:index, :show]
-
# end
-
#
-
1
class MiddlewareStack < ActionDispatch::MiddlewareStack #:nodoc:
-
1
class Middleware < ActionDispatch::MiddlewareStack::Middleware #:nodoc:
-
1
def initialize(klass, *args, &block)
-
options = args.extract_options!
-
@only = Array(options.delete(:only)).map(&:to_s)
-
@except = Array(options.delete(:except)).map(&:to_s)
-
args << options unless options.empty?
-
super
-
end
-
-
1
def valid?(action)
-
if @only.present?
-
@only.include?(action)
-
elsif @except.present?
-
!@except.include?(action)
-
else
-
true
-
end
-
end
-
end
-
-
1
def build(action, app=nil, &block)
-
app ||= block
-
action = action.to_s
-
raise "MiddlewareStack#build requires an app" unless app
-
-
middlewares.reverse.inject(app) do |a, middleware|
-
middleware.valid?(action) ? middleware.build(a) : a
-
end
-
end
-
end
-
-
# <tt>ActionController::Metal</tt> is the simplest possible controller, providing a
-
# valid Rack interface without the additional niceties provided by
-
# <tt>ActionController::Base</tt>.
-
#
-
# A sample metal controller might look like this:
-
#
-
# class HelloController < ActionController::Metal
-
# def index
-
# self.response_body = "Hello World!"
-
# end
-
# end
-
#
-
# And then to route requests to your metal controller, you would add
-
# something like this to <tt>config/routes.rb</tt>:
-
#
-
# get 'hello', to: HelloController.action(:index)
-
#
-
# The +action+ method returns a valid Rack application for the \Rails
-
# router to dispatch to.
-
#
-
# == Rendering Helpers
-
#
-
# <tt>ActionController::Metal</tt> by default provides no utilities for rendering
-
# views, partials, or other responses aside from explicitly calling of
-
# <tt>response_body=</tt>, <tt>content_type=</tt>, and <tt>status=</tt>. To
-
# add the render helpers you're used to having in a normal controller, you
-
# can do the following:
-
#
-
# class HelloController < ActionController::Metal
-
# include AbstractController::Rendering
-
# include ActionView::Layouts
-
# append_view_path "#{Rails.root}/app/views"
-
#
-
# def index
-
# render "hello/index"
-
# end
-
# end
-
#
-
# == Redirection Helpers
-
#
-
# To add redirection helpers to your metal controller, do the following:
-
#
-
# class HelloController < ActionController::Metal
-
# include ActionController::Redirecting
-
# include Rails.application.routes.url_helpers
-
#
-
# def index
-
# redirect_to root_url
-
# end
-
# end
-
#
-
# == Other Helpers
-
#
-
# You can refer to the modules included in <tt>ActionController::Base</tt> to see
-
# other features you can bring into your metal controller.
-
#
-
1
class Metal < AbstractController::Base
-
1
abstract!
-
-
1
attr_internal_writer :env
-
-
1
def env
-
21
@_env ||= {}
-
end
-
-
# Returns the last part of the controller's name, underscored, without the ending
-
# <tt>Controller</tt>. For instance, PostsController returns <tt>posts</tt>.
-
# Namespaces are left out, so Admin::PostsController returns <tt>posts</tt> as well.
-
#
-
# ==== Returns
-
# * <tt>string</tt>
-
1
def self.controller_name
-
@controller_name ||= name.demodulize.sub(/Controller$/, '').underscore
-
end
-
-
# Delegates to the class' <tt>controller_name</tt>
-
1
def controller_name
-
self.class.controller_name
-
end
-
-
# The details below can be overridden to support a specific
-
# Request and Response object. The default ActionController::Base
-
# implementation includes RackDelegation, which makes a request
-
# and response object available. You might wish to control the
-
# environment and response manually for performance reasons.
-
-
1
attr_internal :headers, :response, :request
-
1
delegate :session, :to => "@_request"
-
-
1
def initialize
-
4
@_headers = {"Content-Type" => "text/html"}
-
4
@_status = 200
-
4
@_request = nil
-
4
@_response = nil
-
4
@_routes = nil
-
4
super
-
end
-
-
1
def params
-
@_params ||= request.parameters
-
end
-
-
1
def params=(val)
-
@_params = val
-
end
-
-
# Basic implementations for content_type=, location=, and headers are
-
# provided to reduce the dependency on the RackDelegation module
-
# in Renderer and Redirector.
-
-
1
def content_type=(type)
-
headers["Content-Type"] = type.to_s
-
end
-
-
1
def content_type
-
headers["Content-Type"]
-
end
-
-
1
def location
-
headers["Location"]
-
end
-
-
1
def location=(url)
-
headers["Location"] = url
-
end
-
-
# basic url_for that can be overridden for more robust functionality
-
1
def url_for(string)
-
string
-
end
-
-
1
def status
-
@_status
-
end
-
-
1
def status=(status)
-
@_status = Rack::Utils.status_code(status)
-
end
-
-
1
def response_body=(body)
-
4
body = [body] unless body.nil? || body.respond_to?(:each)
-
4
super
-
end
-
-
1
def performed?
-
4
response_body || (response && response.committed?)
-
end
-
-
1
def dispatch(name, request) #:nodoc:
-
@_request = request
-
@_env = request.env
-
@_env['action_controller.instance'] = self
-
process(name)
-
to_a
-
end
-
-
1
def to_a #:nodoc:
-
response ? response.to_a : [status, headers, response_body]
-
end
-
-
1
class_attribute :middleware_stack
-
1
self.middleware_stack = ActionController::MiddlewareStack.new
-
-
1
def self.inherited(base) # :nodoc:
-
4
base.middleware_stack = middleware_stack.dup
-
4
super
-
end
-
-
# Pushes the given Rack middleware and its arguments to the bottom of the
-
# middleware stack.
-
1
def self.use(*args, &block)
-
middleware_stack.use(*args, &block)
-
end
-
-
# Alias for +middleware_stack+.
-
1
def self.middleware
-
middleware_stack
-
end
-
-
# Makes the controller a Rack endpoint that runs the action in the given
-
# +env+'s +action_dispatch.request.path_parameters+ key.
-
1
def self.call(env)
-
action(env['action_dispatch.request.path_parameters'][:action]).call(env)
-
end
-
-
# Returns a Rack endpoint for the given action name.
-
1
def self.action(name, klass = ActionDispatch::Request)
-
middleware_stack.build(name.to_s) do |env|
-
new.dispatch(name, klass.new(env))
-
end
-
end
-
-
1
def _status_code
-
@_status
-
end
-
end
-
end
-
1
require 'active_support/core_ext/hash/keys'
-
-
1
module ActionController
-
1
module ConditionalGet
-
1
extend ActiveSupport::Concern
-
-
1
include RackDelegation
-
1
include Head
-
-
1
included do
-
1
class_attribute :etaggers
-
1
self.etaggers = []
-
end
-
-
1
module ClassMethods
-
# Allows you to consider additional controller-wide information when generating an etag.
-
# For example, if you serve pages tailored depending on who's logged in at the moment, you
-
# may want to add the current user id to be part of the etag to prevent authorized displaying
-
# of cached pages.
-
#
-
# class InvoicesController < ApplicationController
-
# etag { current_user.try :id }
-
#
-
# def show
-
# # Etag will differ even for the same invoice when it's viewed by a different current_user
-
# @invoice = Invoice.find(params[:id])
-
# fresh_when(@invoice)
-
# end
-
# end
-
1
def etag(&etagger)
-
self.etaggers += [etagger]
-
end
-
end
-
-
# Sets the etag, +last_modified+, or both on the response and renders a
-
# <tt>304 Not Modified</tt> response if the request is already fresh.
-
#
-
# === Parameters:
-
#
-
# * <tt>:etag</tt>.
-
# * <tt>:last_modified</tt>.
-
# * <tt>:public</tt> By default the Cache-Control header is private, set this to
-
# +true+ if you want your application to be cachable by other devices (proxy caches).
-
#
-
# === Example:
-
#
-
# def show
-
# @article = Article.find(params[:id])
-
# fresh_when(etag: @article, last_modified: @article.created_at, public: true)
-
# end
-
#
-
# This will render the show template if the request isn't sending a matching etag or
-
# If-Modified-Since header and just a <tt>304 Not Modified</tt> response if there's a match.
-
#
-
# You can also just pass a record where +last_modified+ will be set by calling
-
# +updated_at+ and the etag by passing the object itself.
-
#
-
# def show
-
# @article = Article.find(params[:id])
-
# fresh_when(@article)
-
# end
-
#
-
# When passing a record, you can still set whether the public header:
-
#
-
# def show
-
# @article = Article.find(params[:id])
-
# fresh_when(@article, public: true)
-
# end
-
1
def fresh_when(record_or_options, additional_options = {})
-
if record_or_options.is_a? Hash
-
options = record_or_options
-
options.assert_valid_keys(:etag, :last_modified, :public)
-
else
-
record = record_or_options
-
options = { etag: record, last_modified: record.try(:updated_at) }.merge!(additional_options)
-
end
-
-
response.etag = combine_etags(options[:etag]) if options[:etag]
-
response.last_modified = options[:last_modified] if options[:last_modified]
-
response.cache_control[:public] = true if options[:public]
-
-
head :not_modified if request.fresh?(response)
-
end
-
-
# Sets the +etag+ and/or +last_modified+ on the response and checks it against
-
# the client request. If the request doesn't match the options provided, the
-
# request is considered stale and should be generated from scratch. Otherwise,
-
# it's fresh and we don't need to generate anything and a reply of <tt>304 Not Modified</tt> is sent.
-
#
-
# === Parameters:
-
#
-
# * <tt>:etag</tt>.
-
# * <tt>:last_modified</tt>.
-
# * <tt>:public</tt> By default the Cache-Control header is private, set this to
-
# +true+ if you want your application to be cachable by other devices (proxy caches).
-
#
-
# === Example:
-
#
-
# def show
-
# @article = Article.find(params[:id])
-
#
-
# if stale?(etag: @article, last_modified: @article.created_at)
-
# @statistics = @article.really_expensive_call
-
# respond_to do |format|
-
# # all the supported formats
-
# end
-
# end
-
# end
-
#
-
# You can also just pass a record where +last_modified+ will be set by calling
-
# updated_at and the etag by passing the object itself.
-
#
-
# def show
-
# @article = Article.find(params[:id])
-
#
-
# if stale?(@article)
-
# @statistics = @article.really_expensive_call
-
# respond_to do |format|
-
# # all the supported formats
-
# end
-
# end
-
# end
-
#
-
# When passing a record, you can still set whether the public header:
-
#
-
# def show
-
# @article = Article.find(params[:id])
-
#
-
# if stale?(@article, public: true)
-
# @statistics = @article.really_expensive_call
-
# respond_to do |format|
-
# # all the supported formats
-
# end
-
# end
-
# end
-
1
def stale?(record_or_options, additional_options = {})
-
fresh_when(record_or_options, additional_options)
-
!request.fresh?(response)
-
end
-
-
# Sets a HTTP 1.1 Cache-Control header. Defaults to issuing a +private+
-
# instruction, so that intermediate caches must not cache the response.
-
#
-
# expires_in 20.minutes
-
# expires_in 3.hours, public: true
-
# expires_in 3.hours, public: true, must_revalidate: true
-
#
-
# This method will overwrite an existing Cache-Control header.
-
# See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more possibilities.
-
#
-
# The method will also ensure a HTTP Date header for client compatibility.
-
1
def expires_in(seconds, options = {})
-
response.cache_control.merge!(
-
:max_age => seconds,
-
:public => options.delete(:public),
-
:must_revalidate => options.delete(:must_revalidate)
-
)
-
options.delete(:private)
-
-
response.cache_control[:extras] = options.map {|k,v| "#{k}=#{v}"}
-
response.date = Time.now unless response.date?
-
end
-
-
# Sets a HTTP 1.1 Cache-Control header of <tt>no-cache</tt> so no caching should
-
# occur by the browser or intermediate caches (like caching proxy servers).
-
1
def expires_now
-
response.cache_control.replace(:no_cache => true)
-
end
-
-
1
private
-
1
def combine_etags(etag)
-
[ etag, *etaggers.map { |etagger| instance_exec(&etagger) }.compact ]
-
end
-
end
-
end
-
1
module ActionController #:nodoc:
-
1
module Cookies
-
1
extend ActiveSupport::Concern
-
-
1
include RackDelegation
-
-
1
included do
-
1
helper_method :cookies
-
end
-
-
1
private
-
1
def cookies
-
request.cookie_jar
-
end
-
end
-
end
-
1
require 'action_controller/metal/exceptions'
-
-
1
module ActionController #:nodoc:
-
# Methods for sending arbitrary data and for streaming files to the browser,
-
# instead of rendering.
-
1
module DataStreaming
-
1
extend ActiveSupport::Concern
-
-
1
include ActionController::Rendering
-
-
1
DEFAULT_SEND_FILE_TYPE = 'application/octet-stream'.freeze #:nodoc:
-
1
DEFAULT_SEND_FILE_DISPOSITION = 'attachment'.freeze #:nodoc:
-
-
1
protected
-
# Sends the file. This uses a server-appropriate method (such as X-Sendfile)
-
# via the Rack::Sendfile middleware. The header to use is set via
-
# +config.action_dispatch.x_sendfile_header+.
-
# Your server can also configure this for you by setting the X-Sendfile-Type header.
-
#
-
# Be careful to sanitize the path parameter if it is coming from a web
-
# page. <tt>send_file(params[:path])</tt> allows a malicious user to
-
# download any file on your server.
-
#
-
# Options:
-
# * <tt>:filename</tt> - suggests a filename for the browser to use.
-
# Defaults to <tt>File.basename(path)</tt>.
-
# * <tt>:type</tt> - specifies an HTTP content type.
-
# You can specify either a string or a symbol for a registered type register with
-
# <tt>Mime::Type.register</tt>, for example :json
-
# If omitted, type will be guessed from the file extension specified in <tt>:filename</tt>.
-
# If no content type is registered for the extension, default type 'application/octet-stream' will be used.
-
# * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
-
# Valid values are 'inline' and 'attachment' (default).
-
# * <tt>:status</tt> - specifies the status code to send with the response. Defaults to 200.
-
# * <tt>:url_based_filename</tt> - set to +true+ if you want the browser guess the filename from
-
# the URL, which is necessary for i18n filenames on certain browsers
-
# (setting <tt>:filename</tt> overrides this option).
-
#
-
# The default Content-Type and Content-Disposition headers are
-
# set to download arbitrary binary files in as many browsers as
-
# possible. IE versions 4, 5, 5.5, and 6 are all known to have
-
# a variety of quirks (especially when downloading over SSL).
-
#
-
# Simple download:
-
#
-
# send_file '/path/to.zip'
-
#
-
# Show a JPEG in the browser:
-
#
-
# send_file '/path/to.jpeg', type: 'image/jpeg', disposition: 'inline'
-
#
-
# Show a 404 page in the browser:
-
#
-
# send_file '/path/to/404.html', type: 'text/html; charset=utf-8', status: 404
-
#
-
# Read about the other Content-* HTTP headers if you'd like to
-
# provide the user with more information (such as Content-Description) in
-
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11.
-
#
-
# Also be aware that the document may be cached by proxies and browsers.
-
# The Pragma and Cache-Control headers declare how the file may be cached
-
# by intermediaries. They default to require clients to validate with
-
# the server before releasing cached responses. See
-
# http://www.mnot.net/cache_docs/ for an overview of web caching and
-
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
-
# for the Cache-Control header spec.
-
1
def send_file(path, options = {}) #:doc:
-
raise MissingFile, "Cannot read file #{path}" unless File.file?(path) and File.readable?(path)
-
-
options[:filename] ||= File.basename(path) unless options[:url_based_filename]
-
send_file_headers! options
-
-
self.status = options[:status] || 200
-
self.content_type = options[:content_type] if options.key?(:content_type)
-
self.response_body = FileBody.new(path)
-
end
-
-
# Avoid having to pass an open file handle as the response body.
-
# Rack::Sendfile will usually intercept the response and uses
-
# the path directly, so there is no reason to open the file.
-
1
class FileBody #:nodoc:
-
1
attr_reader :to_path
-
-
1
def initialize(path)
-
@to_path = path
-
end
-
-
# Stream the file's contents if Rack::Sendfile isn't present.
-
1
def each
-
File.open(to_path, 'rb') do |file|
-
while chunk = file.read(16384)
-
yield chunk
-
end
-
end
-
end
-
end
-
-
# Sends the given binary data to the browser. This method is similar to
-
# <tt>render plain: data</tt>, but also allows you to specify whether
-
# the browser should display the response as a file attachment (i.e. in a
-
# download dialog) or as inline data. You may also set the content type,
-
# the apparent file name, and other things.
-
#
-
# Options:
-
# * <tt>:filename</tt> - suggests a filename for the browser to use.
-
# * <tt>:type</tt> - specifies an HTTP content type. Defaults to 'application/octet-stream'. You can specify
-
# either a string or a symbol for a registered type register with <tt>Mime::Type.register</tt>, for example :json
-
# If omitted, type will be guessed from the file extension specified in <tt>:filename</tt>.
-
# If no content type is registered for the extension, default type 'application/octet-stream' will be used.
-
# * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
-
# Valid values are 'inline' and 'attachment' (default).
-
# * <tt>:status</tt> - specifies the status code to send with the response. Defaults to 200.
-
#
-
# Generic data download:
-
#
-
# send_data buffer
-
#
-
# Download a dynamically-generated tarball:
-
#
-
# send_data generate_tgz('dir'), filename: 'dir.tgz'
-
#
-
# Display an image Active Record in the browser:
-
#
-
# send_data image.data, type: image.content_type, disposition: 'inline'
-
#
-
# See +send_file+ for more information on HTTP Content-* headers and caching.
-
1
def send_data(data, options = {}) #:doc:
-
send_file_headers! options
-
render options.slice(:status, :content_type).merge(:text => data)
-
end
-
-
1
private
-
1
def send_file_headers!(options)
-
type_provided = options.has_key?(:type)
-
-
content_type = options.fetch(:type, DEFAULT_SEND_FILE_TYPE)
-
raise ArgumentError, ":type option required" if content_type.nil?
-
-
if content_type.is_a?(Symbol)
-
extension = Mime[content_type]
-
raise ArgumentError, "Unknown MIME type #{options[:type]}" unless extension
-
self.content_type = extension
-
else
-
if !type_provided && options[:filename]
-
# If type wasn't provided, try guessing from file extension.
-
content_type = Mime::Type.lookup_by_extension(File.extname(options[:filename]).downcase.delete('.')) || content_type
-
end
-
self.content_type = content_type
-
end
-
-
disposition = options.fetch(:disposition, DEFAULT_SEND_FILE_DISPOSITION)
-
unless disposition.nil?
-
disposition = disposition.to_s
-
disposition += %(; filename="#{options[:filename]}") if options[:filename]
-
headers['Content-Disposition'] = disposition
-
end
-
-
headers['Content-Transfer-Encoding'] = 'binary'
-
-
response.sending_file = true
-
-
# Fix a problem with IE 6.0 on opening downloaded files:
-
# If Cache-Control: no-cache is set (which Rails does by default),
-
# IE removes the file it just downloaded from its cache immediately
-
# after it displays the "open/save" dialog, which means that if you
-
# hit "open" the file isn't there anymore when the application that
-
# is called for handling the download is run, so let's workaround that
-
response.cache_control[:public] ||= false
-
end
-
end
-
end
-
1
module ActionController
-
1
class ActionControllerError < StandardError #:nodoc:
-
end
-
-
1
class BadRequest < ActionControllerError #:nodoc:
-
1
attr_reader :original_exception
-
-
1
def initialize(type = nil, e = nil)
-
return super() unless type && e
-
-
super("Invalid #{type} parameters: #{e.message}")
-
@original_exception = e
-
set_backtrace e.backtrace
-
end
-
end
-
-
1
class RenderError < ActionControllerError #:nodoc:
-
end
-
-
1
class RoutingError < ActionControllerError #:nodoc:
-
1
attr_reader :failures
-
1
def initialize(message, failures=[])
-
super(message)
-
@failures = failures
-
end
-
end
-
-
1
class ActionController::UrlGenerationError < RoutingError #:nodoc:
-
end
-
-
1
class MethodNotAllowed < ActionControllerError #:nodoc:
-
1
def initialize(*allowed_methods)
-
super("Only #{allowed_methods.to_sentence(:locale => :en)} requests are allowed.")
-
end
-
end
-
-
1
class NotImplemented < MethodNotAllowed #:nodoc:
-
end
-
-
1
class UnknownController < ActionControllerError #:nodoc:
-
end
-
-
1
class MissingFile < ActionControllerError #:nodoc:
-
end
-
-
1
class SessionOverflowError < ActionControllerError #:nodoc:
-
1
DEFAULT_MESSAGE = 'Your session data is larger than the data column in which it is to be stored. You must increase the size of your data column if you intend to store large data.'
-
-
1
def initialize(message = nil)
-
super(message || DEFAULT_MESSAGE)
-
end
-
end
-
-
1
class UnknownHttpMethod < ActionControllerError #:nodoc:
-
end
-
-
1
class UnknownFormat < ActionControllerError #:nodoc:
-
end
-
end
-
1
module ActionController #:nodoc:
-
1
module Flash
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
class_attribute :_flash_types, instance_accessor: false
-
1
self._flash_types = []
-
-
1
delegate :flash, to: :request
-
1
add_flash_types(:alert, :notice)
-
end
-
-
1
module ClassMethods
-
# Creates new flash types. You can pass as many types as you want to create
-
# flash types other than the default <tt>alert</tt> and <tt>notice</tt> in
-
# your controllers and views. For instance:
-
#
-
# # in application_controller.rb
-
# class ApplicationController < ActionController::Base
-
# add_flash_types :warning
-
# end
-
#
-
# # in your controller
-
# redirect_to user_path(@user), warning: "Incomplete profile"
-
#
-
# # in your view
-
# <%= warning %>
-
#
-
# This method will automatically define a new method for each of the given
-
# names, and it will be available in your views.
-
1
def add_flash_types(*types)
-
1
types.each do |type|
-
2
next if _flash_types.include?(type)
-
-
2
define_method(type) do
-
request.flash[type]
-
end
-
2
helper_method type
-
-
2
self._flash_types += [type]
-
end
-
end
-
end
-
-
1
protected
-
1
def redirect_to(options = {}, response_status_and_flash = {}) #:doc:
-
2
self.class._flash_types.each do |flash_type|
-
4
if type = response_status_and_flash.delete(flash_type)
-
flash[flash_type] = type
-
end
-
end
-
-
2
if other_flashes = response_status_and_flash.delete(:flash)
-
flash.update(other_flashes)
-
end
-
-
2
super(options, response_status_and_flash)
-
end
-
end
-
end
-
1
require 'active_support/core_ext/hash/except'
-
1
require 'active_support/core_ext/hash/slice'
-
-
1
module ActionController
-
# This module provides a method which will redirect browser to use HTTPS
-
# protocol. This will ensure that user's sensitive information will be
-
# transferred safely over the internet. You _should_ always force browser
-
# to use HTTPS when you're transferring sensitive information such as
-
# user authentication, account information, or credit card information.
-
#
-
# Note that if you are really concerned about your application security,
-
# you might consider using +config.force_ssl+ in your config file instead.
-
# That will ensure all the data transferred via HTTPS protocol and prevent
-
# user from getting session hijacked when accessing the site under unsecured
-
# HTTP protocol.
-
1
module ForceSSL
-
1
extend ActiveSupport::Concern
-
1
include AbstractController::Callbacks
-
-
1
ACTION_OPTIONS = [:only, :except, :if, :unless]
-
1
URL_OPTIONS = [:protocol, :host, :domain, :subdomain, :port, :path]
-
1
REDIRECT_OPTIONS = [:status, :flash, :alert, :notice]
-
-
1
module ClassMethods
-
# Force the request to this particular controller or specified actions to be
-
# under HTTPS protocol.
-
#
-
# If you need to disable this for any reason (e.g. development) then you can use
-
# an +:if+ or +:unless+ condition.
-
#
-
# class AccountsController < ApplicationController
-
# force_ssl if: :ssl_configured?
-
#
-
# def ssl_configured?
-
# !Rails.env.development?
-
# end
-
# end
-
#
-
# ==== URL Options
-
# You can pass any of the following options to affect the redirect url
-
# * <tt>host</tt> - Redirect to a different host name
-
# * <tt>subdomain</tt> - Redirect to a different subdomain
-
# * <tt>domain</tt> - Redirect to a different domain
-
# * <tt>port</tt> - Redirect to a non-standard port
-
# * <tt>path</tt> - Redirect to a different path
-
#
-
# ==== Redirect Options
-
# You can pass any of the following options to affect the redirect status and response
-
# * <tt>status</tt> - Redirect with a custom status (default is 301 Moved Permanently)
-
# * <tt>flash</tt> - Set a flash message when redirecting
-
# * <tt>alert</tt> - Set an alert message when redirecting
-
# * <tt>notice</tt> - Set a notice message when redirecting
-
#
-
# ==== Action Options
-
# You can pass any of the following options to affect the before_action callback
-
# * <tt>only</tt> - The callback should be run only for this action
-
# * <tt>except</tt> - The callback should be run for all actions except this action
-
# * <tt>if</tt> - A symbol naming an instance method or a proc; the callback
-
# will be called only when it returns a true value.
-
# * <tt>unless</tt> - A symbol naming an instance method or a proc; the callback
-
# will be called only when it returns a false value.
-
1
def force_ssl(options = {})
-
action_options = options.slice(*ACTION_OPTIONS)
-
redirect_options = options.except(*ACTION_OPTIONS)
-
before_action(action_options) do
-
force_ssl_redirect(redirect_options)
-
end
-
end
-
end
-
-
# Redirect the existing request to use the HTTPS protocol.
-
#
-
# ==== Parameters
-
# * <tt>host_or_options</tt> - Either a host name or any of the url & redirect options
-
# available to the <tt>force_ssl</tt> method.
-
1
def force_ssl_redirect(host_or_options = nil)
-
unless request.ssl?
-
options = {
-
:protocol => 'https://',
-
:host => request.host,
-
:path => request.fullpath,
-
:status => :moved_permanently
-
}
-
-
if host_or_options.is_a?(Hash)
-
options.merge!(host_or_options)
-
elsif host_or_options
-
options.merge!(:host => host_or_options)
-
end
-
-
secure_url = ActionDispatch::Http::URL.url_for(options.slice(*URL_OPTIONS))
-
flash.keep if respond_to?(:flash)
-
redirect_to secure_url, options.slice(*REDIRECT_OPTIONS)
-
end
-
end
-
end
-
end
-
1
module ActionController
-
1
module Head
-
# Returns a response that has no content (merely headers). The options
-
# argument is interpreted to be a hash of header names and values.
-
# This allows you to easily return a response that consists only of
-
# significant headers:
-
#
-
# head :created, location: person_path(@person)
-
#
-
# head :created, location: @person
-
#
-
# It can also be used to return exceptional conditions:
-
#
-
# return head(:method_not_allowed) unless request.post?
-
# return head(:bad_request) unless valid_request?
-
# render
-
1
def head(status, options = {})
-
options, status = status, nil if status.is_a?(Hash)
-
status ||= options.delete(:status) || :ok
-
location = options.delete(:location)
-
content_type = options.delete(:content_type)
-
-
options.each do |key, value|
-
headers[key.to_s.dasherize.split('-').each { |v| v[0] = v[0].chr.upcase }.join('-')] = value.to_s
-
end
-
-
self.status = status
-
self.location = url_for(location) if location
-
-
if include_content?(self._status_code)
-
self.content_type = content_type || (Mime[formats.first] if formats)
-
self.response.charset = false if self.response
-
self.response_body = " "
-
else
-
headers.delete('Content-Type')
-
headers.delete('Content-Length')
-
self.response_body = ""
-
end
-
end
-
-
1
private
-
# :nodoc:
-
1
def include_content?(status)
-
case status
-
when 100..199
-
false
-
when 204, 205, 304
-
false
-
else
-
true
-
end
-
end
-
end
-
end
-
1
module ActionController
-
# The \Rails framework provides a large number of helpers for working with assets, dates, forms,
-
# numbers and model objects, to name a few. These helpers are available to all templates
-
# by default.
-
#
-
# In addition to using the standard template helpers provided, creating custom helpers to
-
# extract complicated logic or reusable functionality is strongly encouraged. By default, each controller
-
# will include all helpers. These helpers are only accessible on the controller through <tt>.helpers</tt>
-
#
-
# In previous versions of \Rails the controller will include a helper whose
-
# name matches that of the controller, e.g., <tt>MyController</tt> will automatically
-
# include <tt>MyHelper</tt>. To return old behavior set +config.action_controller.include_all_helpers+ to +false+.
-
#
-
# Additional helpers can be specified using the +helper+ class method in ActionController::Base or any
-
# controller which inherits from it.
-
#
-
# The +to_s+ method from the \Time class can be wrapped in a helper method to display a custom message if
-
# a \Time object is blank:
-
#
-
# module FormattedTimeHelper
-
# def format_time(time, format=:long, blank_message=" ")
-
# time.blank? ? blank_message : time.to_s(format)
-
# end
-
# end
-
#
-
# FormattedTimeHelper can now be included in a controller, using the +helper+ class method:
-
#
-
# class EventsController < ActionController::Base
-
# helper FormattedTimeHelper
-
# def index
-
# @events = Event.all
-
# end
-
# end
-
#
-
# Then, in any view rendered by <tt>EventController</tt>, the <tt>format_time</tt> method can be called:
-
#
-
# <% @events.each do |event| -%>
-
# <p>
-
# <%= format_time(event.time, :short, "N/A") %> | <%= event.name %>
-
# </p>
-
# <% end -%>
-
#
-
# Finally, assuming we have two event instances, one which has a time and one which does not,
-
# the output might look like this:
-
#
-
# 23 Aug 11:30 | Carolina Railhawks Soccer Match
-
# N/A | Carolina Railhaws Training Workshop
-
#
-
1
module Helpers
-
1
extend ActiveSupport::Concern
-
-
2
class << self; attr_accessor :helpers_path; end
-
1
include AbstractController::Helpers
-
-
1
included do
-
1
class_attribute :helpers_path, :include_all_helpers
-
1
self.helpers_path ||= []
-
1
self.include_all_helpers = true
-
end
-
-
1
module ClassMethods
-
# Declares helper accessors for controller attributes. For example, the
-
# following adds new +name+ and <tt>name=</tt> instance methods to a
-
# controller and makes them available to the view:
-
# attr_accessor :name
-
# helper_attr :name
-
#
-
# ==== Parameters
-
# * <tt>attrs</tt> - Names of attributes to be converted into helpers.
-
1
def helper_attr(*attrs)
-
attrs.flatten.each { |attr| helper_method(attr, "#{attr}=") }
-
end
-
-
# Provides a proxy to access helpers methods from outside the view.
-
1
def helpers
-
@helper_proxy ||= begin
-
proxy = ActionView::Base.new
-
proxy.config = config.inheritable_copy
-
proxy.extend(_helpers)
-
end
-
end
-
-
# Overwrite modules_for_helpers to accept :all as argument, which loads
-
# all helpers in helpers_path.
-
#
-
# ==== Parameters
-
# * <tt>args</tt> - A list of helpers
-
#
-
# ==== Returns
-
# * <tt>array</tt> - A normalized list of modules for the list of helpers provided.
-
1
def modules_for_helpers(args)
-
5
args += all_application_helpers if args.delete(:all)
-
5
super(args)
-
end
-
-
1
def all_helpers_from_path(path)
-
2
helpers = Array(path).flat_map do |_path|
-
2
extract = /^#{Regexp.quote(_path.to_s)}\/?(.*)_helper.rb$/
-
8
names = Dir["#{_path}/**/*_helper.rb"].map { |file| file.sub(extract, '\1') }
-
2
names.sort!
-
end
-
2
helpers.uniq!
-
2
helpers
-
end
-
-
1
private
-
# Extract helper names from files in <tt>app/helpers/**/*_helper.rb</tt>
-
1
def all_application_helpers
-
2
all_helpers_from_path(helpers_path)
-
end
-
end
-
end
-
end
-
-
1
module ActionController
-
# Adds the ability to prevent public methods on a controller to be called as actions.
-
1
module HideActions
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
class_attribute :hidden_actions
-
1
self.hidden_actions = Set.new.freeze
-
end
-
-
1
private
-
-
# Overrides AbstractController::Base#action_method? to return false if the
-
# action name is in the list of hidden actions.
-
1
def method_for_action(action_name)
-
4
self.class.visible_action?(action_name) && super
-
end
-
-
1
module ClassMethods
-
# Sets all of the actions passed in as hidden actions.
-
#
-
# ==== Parameters
-
# * <tt>args</tt> - A list of actions
-
1
def hide_action(*args)
-
self.hidden_actions = hidden_actions.dup.merge(args.map(&:to_s)).freeze
-
end
-
-
1
def visible_action?(action_name)
-
4
not hidden_actions.include?(action_name)
-
end
-
-
# Overrides AbstractController::Base#action_methods to remove any methods
-
# that are listed as hidden methods.
-
1
def action_methods
-
85
@action_methods ||= Set.new(super.reject { |name| hidden_actions.include?(name) }).freeze
-
end
-
end
-
end
-
end
-
1
require 'base64'
-
-
1
module ActionController
-
# Makes it dead easy to do HTTP Basic, Digest and Token authentication.
-
1
module HttpAuthentication
-
# Makes it dead easy to do HTTP \Basic authentication.
-
#
-
# === Simple \Basic example
-
#
-
# class PostsController < ApplicationController
-
# http_basic_authenticate_with name: "dhh", password: "secret", except: :index
-
#
-
# def index
-
# render plain: "Everyone can see me!"
-
# end
-
#
-
# def edit
-
# render plain: "I'm only accessible if you know the password"
-
# end
-
# end
-
#
-
# === Advanced \Basic example
-
#
-
# Here is a more advanced \Basic example where only Atom feeds and the XML API is protected by HTTP authentication,
-
# the regular HTML interface is protected by a session approach:
-
#
-
# class ApplicationController < ActionController::Base
-
# before_action :set_account, :authenticate
-
#
-
# protected
-
# def set_account
-
# @account = Account.find_by(url_name: request.subdomains.first)
-
# end
-
#
-
# def authenticate
-
# case request.format
-
# when Mime::XML, Mime::ATOM
-
# if user = authenticate_with_http_basic { |u, p| @account.users.authenticate(u, p) }
-
# @current_user = user
-
# else
-
# request_http_basic_authentication
-
# end
-
# else
-
# if session_authenticated?
-
# @current_user = @account.users.find(session[:authenticated][:user_id])
-
# else
-
# redirect_to(login_url) and return false
-
# end
-
# end
-
# end
-
# end
-
#
-
# In your integration tests, you can do something like this:
-
#
-
# def test_access_granted_from_xml
-
# get(
-
# "/notes/1.xml", nil,
-
# 'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Basic.encode_credentials(users(:dhh).name, users(:dhh).password)
-
# )
-
#
-
# assert_equal 200, status
-
# end
-
1
module Basic
-
1
extend self
-
-
1
module ControllerMethods
-
1
extend ActiveSupport::Concern
-
-
1
module ClassMethods
-
1
def http_basic_authenticate_with(options = {})
-
before_action(options.except(:name, :password, :realm)) do
-
authenticate_or_request_with_http_basic(options[:realm] || "Application") do |name, password|
-
name == options[:name] && password == options[:password]
-
end
-
end
-
end
-
end
-
-
1
def authenticate_or_request_with_http_basic(realm = "Application", &login_procedure)
-
authenticate_with_http_basic(&login_procedure) || request_http_basic_authentication(realm)
-
end
-
-
1
def authenticate_with_http_basic(&login_procedure)
-
HttpAuthentication::Basic.authenticate(request, &login_procedure)
-
end
-
-
1
def request_http_basic_authentication(realm = "Application")
-
HttpAuthentication::Basic.authentication_request(self, realm)
-
end
-
end
-
-
1
def authenticate(request, &login_procedure)
-
if has_basic_credentials?(request)
-
login_procedure.call(*user_name_and_password(request))
-
end
-
end
-
-
1
def has_basic_credentials?(request)
-
request.authorization.present? && (auth_scheme(request) == 'Basic')
-
end
-
-
1
def user_name_and_password(request)
-
decode_credentials(request).split(':', 2)
-
end
-
-
1
def decode_credentials(request)
-
::Base64.decode64(auth_param(request) || '')
-
end
-
-
1
def auth_scheme(request)
-
request.authorization.split(' ', 2).first
-
end
-
-
1
def auth_param(request)
-
request.authorization.split(' ', 2).second
-
end
-
-
1
def encode_credentials(user_name, password)
-
"Basic #{::Base64.strict_encode64("#{user_name}:#{password}")}"
-
end
-
-
1
def authentication_request(controller, realm)
-
controller.headers["WWW-Authenticate"] = %(Basic realm="#{realm.gsub(/"/, "")}")
-
controller.status = 401
-
controller.response_body = "HTTP Basic: Access denied.\n"
-
end
-
end
-
-
# Makes it dead easy to do HTTP \Digest authentication.
-
#
-
# === Simple \Digest example
-
#
-
# require 'digest/md5'
-
# class PostsController < ApplicationController
-
# REALM = "SuperSecret"
-
# USERS = {"dhh" => "secret", #plain text password
-
# "dap" => Digest::MD5.hexdigest(["dap",REALM,"secret"].join(":"))} #ha1 digest password
-
#
-
# before_action :authenticate, except: [:index]
-
#
-
# def index
-
# render plain: "Everyone can see me!"
-
# end
-
#
-
# def edit
-
# render plain: "I'm only accessible if you know the password"
-
# end
-
#
-
# private
-
# def authenticate
-
# authenticate_or_request_with_http_digest(REALM) do |username|
-
# USERS[username]
-
# end
-
# end
-
# end
-
#
-
# === Notes
-
#
-
# The +authenticate_or_request_with_http_digest+ block must return the user's password
-
# or the ha1 digest hash so the framework can appropriately hash to check the user's
-
# credentials. Returning +nil+ will cause authentication to fail.
-
#
-
# Storing the ha1 hash: MD5(username:realm:password), is better than storing a plain password. If
-
# the password file or database is compromised, the attacker would be able to use the ha1 hash to
-
# authenticate as the user at this +realm+, but would not have the user's password to try using at
-
# other sites.
-
#
-
# In rare instances, web servers or front proxies strip authorization headers before
-
# they reach your application. You can debug this situation by logging all environment
-
# variables, and check for HTTP_AUTHORIZATION, amongst others.
-
1
module Digest
-
1
extend self
-
-
1
module ControllerMethods
-
1
def authenticate_or_request_with_http_digest(realm = "Application", &password_procedure)
-
authenticate_with_http_digest(realm, &password_procedure) || request_http_digest_authentication(realm)
-
end
-
-
# Authenticate with HTTP Digest, returns true or false
-
1
def authenticate_with_http_digest(realm = "Application", &password_procedure)
-
HttpAuthentication::Digest.authenticate(request, realm, &password_procedure)
-
end
-
-
# Render output including the HTTP Digest authentication header
-
1
def request_http_digest_authentication(realm = "Application", message = nil)
-
HttpAuthentication::Digest.authentication_request(self, realm, message)
-
end
-
end
-
-
# Returns false on a valid response, true otherwise
-
1
def authenticate(request, realm, &password_procedure)
-
request.authorization && validate_digest_response(request, realm, &password_procedure)
-
end
-
-
# Returns false unless the request credentials response value matches the expected value.
-
# First try the password as a ha1 digest password. If this fails, then try it as a plain
-
# text password.
-
1
def validate_digest_response(request, realm, &password_procedure)
-
secret_key = secret_token(request)
-
credentials = decode_credentials_header(request)
-
valid_nonce = validate_nonce(secret_key, request, credentials[:nonce])
-
-
if valid_nonce && realm == credentials[:realm] && opaque(secret_key) == credentials[:opaque]
-
password = password_procedure.call(credentials[:username])
-
return false unless password
-
-
method = request.env['rack.methodoverride.original_method'] || request.env['REQUEST_METHOD']
-
uri = credentials[:uri]
-
-
[true, false].any? do |trailing_question_mark|
-
[true, false].any? do |password_is_ha1|
-
_uri = trailing_question_mark ? uri + "?" : uri
-
expected = expected_response(method, _uri, credentials, password, password_is_ha1)
-
expected == credentials[:response]
-
end
-
end
-
end
-
end
-
-
# Returns the expected response for a request of +http_method+ to +uri+ with the decoded +credentials+ and the expected +password+
-
# Optional parameter +password_is_ha1+ is set to +true+ by default, since best practice is to store ha1 digest instead
-
# of a plain-text password.
-
1
def expected_response(http_method, uri, credentials, password, password_is_ha1=true)
-
ha1 = password_is_ha1 ? password : ha1(credentials, password)
-
ha2 = ::Digest::MD5.hexdigest([http_method.to_s.upcase, uri].join(':'))
-
::Digest::MD5.hexdigest([ha1, credentials[:nonce], credentials[:nc], credentials[:cnonce], credentials[:qop], ha2].join(':'))
-
end
-
-
1
def ha1(credentials, password)
-
::Digest::MD5.hexdigest([credentials[:username], credentials[:realm], password].join(':'))
-
end
-
-
1
def encode_credentials(http_method, credentials, password, password_is_ha1)
-
credentials[:response] = expected_response(http_method, credentials[:uri], credentials, password, password_is_ha1)
-
"Digest " + credentials.sort_by {|x| x[0].to_s }.map {|v| "#{v[0]}='#{v[1]}'" }.join(', ')
-
end
-
-
1
def decode_credentials_header(request)
-
decode_credentials(request.authorization)
-
end
-
-
1
def decode_credentials(header)
-
ActiveSupport::HashWithIndifferentAccess[header.to_s.gsub(/^Digest\s+/, '').split(',').map do |pair|
-
key, value = pair.split('=', 2)
-
[key.strip, value.to_s.gsub(/^"|"$/,'').delete('\'')]
-
end]
-
end
-
-
1
def authentication_header(controller, realm)
-
secret_key = secret_token(controller.request)
-
nonce = self.nonce(secret_key)
-
opaque = opaque(secret_key)
-
controller.headers["WWW-Authenticate"] = %(Digest realm="#{realm}", qop="auth", algorithm=MD5, nonce="#{nonce}", opaque="#{opaque}")
-
end
-
-
1
def authentication_request(controller, realm, message = nil)
-
message ||= "HTTP Digest: Access denied.\n"
-
authentication_header(controller, realm)
-
controller.status = 401
-
controller.response_body = message
-
end
-
-
1
def secret_token(request)
-
key_generator = request.env["action_dispatch.key_generator"]
-
http_auth_salt = request.env["action_dispatch.http_auth_salt"]
-
key_generator.generate_key(http_auth_salt)
-
end
-
-
# Uses an MD5 digest based on time to generate a value to be used only once.
-
#
-
# A server-specified data string which should be uniquely generated each time a 401 response is made.
-
# It is recommended that this string be base64 or hexadecimal data.
-
# Specifically, since the string is passed in the header lines as a quoted string, the double-quote character is not allowed.
-
#
-
# The contents of the nonce are implementation dependent.
-
# The quality of the implementation depends on a good choice.
-
# A nonce might, for example, be constructed as the base 64 encoding of
-
#
-
# time-stamp H(time-stamp ":" ETag ":" private-key)
-
#
-
# where time-stamp is a server-generated time or other non-repeating value,
-
# ETag is the value of the HTTP ETag header associated with the requested entity,
-
# and private-key is data known only to the server.
-
# With a nonce of this form a server would recalculate the hash portion after receiving the client authentication header and
-
# reject the request if it did not match the nonce from that header or
-
# if the time-stamp value is not recent enough. In this way the server can limit the time of the nonce's validity.
-
# The inclusion of the ETag prevents a replay request for an updated version of the resource.
-
# (Note: including the IP address of the client in the nonce would appear to offer the server the ability
-
# to limit the reuse of the nonce to the same client that originally got it.
-
# However, that would break proxy farms, where requests from a single user often go through different proxies in the farm.
-
# Also, IP address spoofing is not that hard.)
-
#
-
# An implementation might choose not to accept a previously used nonce or a previously used digest, in order to
-
# protect against a replay attack. Or, an implementation might choose to use one-time nonces or digests for
-
# POST, PUT, or PATCH requests and a time-stamp for GET requests. For more details on the issues involved see Section 4
-
# of this document.
-
#
-
# The nonce is opaque to the client. Composed of Time, and hash of Time with secret
-
# key from the Rails session secret generated upon creation of project. Ensures
-
# the time cannot be modified by client.
-
1
def nonce(secret_key, time = Time.now)
-
t = time.to_i
-
hashed = [t, secret_key]
-
digest = ::Digest::MD5.hexdigest(hashed.join(":"))
-
::Base64.strict_encode64("#{t}:#{digest}")
-
end
-
-
# Might want a shorter timeout depending on whether the request
-
# is a PATCH, PUT, or POST, and if client is browser or web service.
-
# Can be much shorter if the Stale directive is implemented. This would
-
# allow a user to use new nonce without prompting user again for their
-
# username and password.
-
1
def validate_nonce(secret_key, request, value, seconds_to_timeout=5*60)
-
return false if value.nil?
-
t = ::Base64.decode64(value).split(":").first.to_i
-
nonce(secret_key, t) == value && (t - Time.now.to_i).abs <= seconds_to_timeout
-
end
-
-
# Opaque based on random generation - but changing each request?
-
1
def opaque(secret_key)
-
::Digest::MD5.hexdigest(secret_key)
-
end
-
-
end
-
-
# Makes it dead easy to do HTTP Token authentication.
-
#
-
# Simple Token example:
-
#
-
# class PostsController < ApplicationController
-
# TOKEN = "secret"
-
#
-
# before_action :authenticate, except: [ :index ]
-
#
-
# def index
-
# render plain: "Everyone can see me!"
-
# end
-
#
-
# def edit
-
# render plain: "I'm only accessible if you know the password"
-
# end
-
#
-
# private
-
# def authenticate
-
# authenticate_or_request_with_http_token do |token, options|
-
# token == TOKEN
-
# end
-
# end
-
# end
-
#
-
#
-
# Here is a more advanced Token example where only Atom feeds and the XML API is protected by HTTP token authentication,
-
# the regular HTML interface is protected by a session approach:
-
#
-
# class ApplicationController < ActionController::Base
-
# before_action :set_account, :authenticate
-
#
-
# protected
-
# def set_account
-
# @account = Account.find_by(url_name: request.subdomains.first)
-
# end
-
#
-
# def authenticate
-
# case request.format
-
# when Mime::XML, Mime::ATOM
-
# if user = authenticate_with_http_token { |t, o| @account.users.authenticate(t, o) }
-
# @current_user = user
-
# else
-
# request_http_token_authentication
-
# end
-
# else
-
# if session_authenticated?
-
# @current_user = @account.users.find(session[:authenticated][:user_id])
-
# else
-
# redirect_to(login_url) and return false
-
# end
-
# end
-
# end
-
# end
-
#
-
#
-
# In your integration tests, you can do something like this:
-
#
-
# def test_access_granted_from_xml
-
# get(
-
# "/notes/1.xml", nil,
-
# 'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Token.encode_credentials(users(:dhh).token)
-
# )
-
#
-
# assert_equal 200, status
-
# end
-
#
-
#
-
# On shared hosts, Apache sometimes doesn't pass authentication headers to
-
# FCGI instances. If your environment matches this description and you cannot
-
# authenticate, try this rule in your Apache setup:
-
#
-
# RewriteRule ^(.*)$ dispatch.fcgi [E=X-HTTP_AUTHORIZATION:%{HTTP:Authorization},QSA,L]
-
1
module Token
-
1
TOKEN_REGEX = /^Token /
-
1
AUTHN_PAIR_DELIMITERS = /(?:,|;|\t+)/
-
1
extend self
-
-
1
module ControllerMethods
-
1
def authenticate_or_request_with_http_token(realm = "Application", &login_procedure)
-
authenticate_with_http_token(&login_procedure) || request_http_token_authentication(realm)
-
end
-
-
1
def authenticate_with_http_token(&login_procedure)
-
Token.authenticate(self, &login_procedure)
-
end
-
-
1
def request_http_token_authentication(realm = "Application")
-
Token.authentication_request(self, realm)
-
end
-
end
-
-
# If token Authorization header is present, call the login
-
# procedure with the present token and options.
-
#
-
# [controller]
-
# ActionController::Base instance for the current request.
-
#
-
# [login_procedure]
-
# Proc to call if a token is present. The Proc should take two arguments:
-
#
-
# authenticate(controller) { |token, options| ... }
-
#
-
# Returns the return value of <tt>login_procedure</tt> if a
-
# token is found. Returns <tt>nil</tt> if no token is found.
-
-
1
def authenticate(controller, &login_procedure)
-
token, options = token_and_options(controller.request)
-
unless token.blank?
-
login_procedure.call(token, options)
-
end
-
end
-
-
# Parses the token and options out of the token authorization header. If
-
# the header looks like this:
-
# Authorization: Token token="abc", nonce="def"
-
# Then the returned token is "abc", and the options is {nonce: "def"}
-
#
-
# request - ActionDispatch::Request instance with the current headers.
-
#
-
# Returns an Array of [String, Hash] if a token is present.
-
# Returns nil if no token is found.
-
1
def token_and_options(request)
-
authorization_request = request.authorization.to_s
-
if authorization_request[TOKEN_REGEX]
-
params = token_params_from authorization_request
-
[params.shift[1], Hash[params].with_indifferent_access]
-
end
-
end
-
-
1
def token_params_from(auth)
-
rewrite_param_values params_array_from raw_params auth
-
end
-
-
# Takes raw_params and turns it into an array of parameters
-
1
def params_array_from(raw_params)
-
raw_params.map { |param| param.split %r/=(.+)?/ }
-
end
-
-
# This removes the `"` characters wrapping the value.
-
1
def rewrite_param_values(array_params)
-
array_params.each { |param| (param[1] || "").gsub! %r/^"|"$/, '' }
-
end
-
-
# This method takes an authorization body and splits up the key-value
-
# pairs by the standardized `:`, `;`, or `\t` delimiters defined in
-
# `AUTHN_PAIR_DELIMITERS`.
-
1
def raw_params(auth)
-
auth.sub(TOKEN_REGEX, '').split(/"\s*#{AUTHN_PAIR_DELIMITERS}\s*/)
-
end
-
-
# Encodes the given token and options into an Authorization header value.
-
#
-
# token - String token.
-
# options - optional Hash of the options.
-
#
-
# Returns String.
-
1
def encode_credentials(token, options = {})
-
values = ["token=#{token.to_s.inspect}"] + options.map do |key, value|
-
"#{key}=#{value.to_s.inspect}"
-
end
-
"Token #{values * ", "}"
-
end
-
-
# Sets a WWW-Authenticate to let the client know a token is desired.
-
#
-
# controller - ActionController::Base instance for the outgoing response.
-
# realm - String realm to use in the header.
-
#
-
# Returns nothing.
-
1
def authentication_request(controller, realm)
-
controller.headers["WWW-Authenticate"] = %(Token realm="#{realm.gsub(/"/, "")}")
-
controller.__send__ :render, :text => "HTTP Token: Access denied.\n", :status => :unauthorized
-
end
-
end
-
end
-
end
-
1
module ActionController
-
1
module ImplicitRender
-
1
def send_action(method, *args)
-
4
ret = super
-
4
default_render unless performed?
-
4
ret
-
end
-
-
1
def default_render(*args)
-
2
render(*args)
-
end
-
-
1
def method_for_action(action_name)
-
super || if template_exists?(action_name.to_s, _prefixes)
-
"default_render"
-
4
end
-
end
-
end
-
end
-
1
require 'benchmark'
-
1
require 'abstract_controller/logger'
-
-
1
module ActionController
-
# Adds instrumentation to several ends in ActionController::Base. It also provides
-
# some hooks related with process_action, this allows an ORM like Active Record
-
# and/or DataMapper to plug in ActionController and show related information.
-
#
-
# Check ActiveRecord::Railties::ControllerRuntime for an example.
-
1
module Instrumentation
-
1
extend ActiveSupport::Concern
-
-
1
include AbstractController::Logger
-
1
include ActionController::RackDelegation
-
-
1
attr_internal :view_runtime
-
-
1
def process_action(*args)
-
4
raw_payload = {
-
:controller => self.class.name,
-
:action => self.action_name,
-
:params => request.filtered_parameters,
-
:format => request.format.try(:ref),
-
:method => request.method,
-
4
:path => (request.fullpath rescue "unknown")
-
}
-
-
4
ActiveSupport::Notifications.instrument("start_processing.action_controller", raw_payload.dup)
-
-
4
ActiveSupport::Notifications.instrument("process_action.action_controller", raw_payload) do |payload|
-
4
result = super
-
4
payload[:status] = response.status
-
4
append_info_to_payload(payload)
-
4
result
-
end
-
end
-
-
1
def render(*args)
-
2
render_output = nil
-
2
self.view_runtime = cleanup_view_runtime do
-
4
Benchmark.ms { render_output = super }
-
end
-
2
render_output
-
end
-
-
1
def send_file(path, options={})
-
ActiveSupport::Notifications.instrument("send_file.action_controller",
-
options.merge(:path => path)) do
-
super
-
end
-
end
-
-
1
def send_data(data, options = {})
-
ActiveSupport::Notifications.instrument("send_data.action_controller", options) do
-
super
-
end
-
end
-
-
1
def redirect_to(*args)
-
2
ActiveSupport::Notifications.instrument("redirect_to.action_controller") do |payload|
-
2
result = super
-
2
payload[:status] = response.status
-
2
payload[:location] = response.filtered_location
-
2
result
-
end
-
end
-
-
1
private
-
-
# A hook invoked every time a before callback is halted.
-
1
def halted_callback_hook(filter)
-
ActiveSupport::Notifications.instrument("halted_callback.action_controller", :filter => filter)
-
end
-
-
# A hook which allows you to clean up any time taken into account in
-
# views wrongly, like database querying time.
-
#
-
# def cleanup_view_runtime
-
# super - time_taken_in_something_expensive
-
# end
-
#
-
# :api: plugin
-
1
def cleanup_view_runtime #:nodoc:
-
2
yield
-
end
-
-
# Every time after an action is processed, this method is invoked
-
# with the payload, so you can add more information.
-
# :api: plugin
-
1
def append_info_to_payload(payload) #:nodoc:
-
4
payload[:view_runtime] = view_runtime
-
end
-
-
1
module ClassMethods
-
# A hook which allows other frameworks to log what happened during
-
# controller process action. This method should return an array
-
# with the messages to be added.
-
# :api: plugin
-
1
def log_process_action(payload) #:nodoc:
-
4
messages, view_runtime = [], payload[:view_runtime]
-
4
messages << ("Views: %.1fms" % view_runtime.to_f) if view_runtime
-
4
messages
-
end
-
end
-
end
-
end
-
1
require 'action_dispatch/http/response'
-
1
require 'delegate'
-
1
require 'active_support/json'
-
-
1
module ActionController
-
# Mix this module in to your controller, and all actions in that controller
-
# will be able to stream data to the client as it's written.
-
#
-
# class MyController < ActionController::Base
-
# include ActionController::Live
-
#
-
# def stream
-
# response.headers['Content-Type'] = 'text/event-stream'
-
# 100.times {
-
# response.stream.write "hello world\n"
-
# sleep 1
-
# }
-
# ensure
-
# response.stream.close
-
# end
-
# end
-
#
-
# There are a few caveats with this use. You *cannot* write headers after the
-
# response has been committed (Response#committed? will return truthy).
-
# Calling +write+ or +close+ on the response stream will cause the response
-
# object to be committed. Make sure all headers are set before calling write
-
# or close on your stream.
-
#
-
# You *must* call close on your stream when you're finished, otherwise the
-
# socket may be left open forever.
-
#
-
# The final caveat is that your actions are executed in a separate thread than
-
# the main thread. Make sure your actions are thread safe, and this shouldn't
-
# be a problem (don't share state across threads, etc).
-
1
module Live
-
# This class provides the ability to write an SSE (Server Sent Event)
-
# to an IO stream. The class is initialized with a stream and can be used
-
# to either write a JSON string or an object which can be converted to JSON.
-
#
-
# Writing an object will convert it into standard SSE format with whatever
-
# options you have configured. You may choose to set the following options:
-
#
-
# 1) Event. If specified, an event with this name will be dispatched on
-
# the browser.
-
# 2) Retry. The reconnection time in milliseconds used when attempting
-
# to send the event.
-
# 3) Id. If the connection dies while sending an SSE to the browser, then
-
# the server will receive a +Last-Event-ID+ header with value equal to +id+.
-
#
-
# After setting an option in the constructor of the SSE object, all future
-
# SSEs sent across the stream will use those options unless overridden.
-
#
-
# Example Usage:
-
#
-
# class MyController < ActionController::Base
-
# include ActionController::Live
-
#
-
# def index
-
# response.headers['Content-Type'] = 'text/event-stream'
-
# sse = SSE.new(response.stream, retry: 300, event: "event-name")
-
# sse.write({ name: 'John'})
-
# sse.write({ name: 'John'}, id: 10)
-
# sse.write({ name: 'John'}, id: 10, event: "other-event")
-
# sse.write({ name: 'John'}, id: 10, event: "other-event", retry: 500)
-
# ensure
-
# sse.close
-
# end
-
# end
-
#
-
# Note: SSEs are not currently supported by IE. However, they are supported
-
# by Chrome, Firefox, Opera, and Safari.
-
1
class SSE
-
-
1
WHITELISTED_OPTIONS = %w( retry event id )
-
-
1
def initialize(stream, options = {})
-
@stream = stream
-
@options = options
-
end
-
-
1
def close
-
@stream.close
-
end
-
-
1
def write(object, options = {})
-
case object
-
when String
-
perform_write(object, options)
-
else
-
perform_write(ActiveSupport::JSON.encode(object), options)
-
end
-
end
-
-
1
private
-
-
1
def perform_write(json, options)
-
current_options = @options.merge(options).stringify_keys
-
-
WHITELISTED_OPTIONS.each do |option_name|
-
if (option_value = current_options[option_name])
-
@stream.write "#{option_name}: #{option_value}\n"
-
end
-
end
-
-
@stream.write "data: #{json}\n\n"
-
end
-
end
-
-
1
class Buffer < ActionDispatch::Response::Buffer #:nodoc:
-
1
include MonitorMixin
-
-
1
def initialize(response)
-
@error_callback = lambda { true }
-
@cv = new_cond
-
super(response, SizedQueue.new(10))
-
end
-
-
1
def write(string)
-
unless @response.committed?
-
@response.headers["Cache-Control"] = "no-cache"
-
@response.headers.delete "Content-Length"
-
end
-
-
super
-
end
-
-
1
def each
-
@response.sending!
-
while str = @buf.pop
-
yield str
-
end
-
@response.sent!
-
end
-
-
1
def close
-
synchronize do
-
super
-
@buf.push nil
-
@cv.broadcast
-
end
-
end
-
-
1
def await_close
-
synchronize do
-
@cv.wait_until { @closed }
-
end
-
end
-
-
1
def on_error(&block)
-
@error_callback = block
-
end
-
-
1
def call_on_error
-
@error_callback.call
-
end
-
end
-
-
1
class Response < ActionDispatch::Response #:nodoc: all
-
1
class Header < DelegateClass(Hash)
-
1
def initialize(response, header)
-
@response = response
-
super(header)
-
end
-
-
1
def []=(k,v)
-
if @response.committed?
-
raise ActionDispatch::IllegalStateError, 'header already sent'
-
end
-
-
super
-
end
-
-
1
def merge(other)
-
self.class.new @response, __getobj__.merge(other)
-
end
-
-
1
def to_hash
-
__getobj__.dup
-
end
-
end
-
-
1
private
-
-
1
def before_committed
-
super
-
jar = request.cookie_jar
-
# The response can be committed multiple times
-
jar.write self unless committed?
-
end
-
-
1
def before_sending
-
super
-
request.cookie_jar.commit!
-
headers.freeze
-
end
-
-
1
def build_buffer(response, body)
-
buf = Live::Buffer.new response
-
body.each { |part| buf.write part }
-
buf
-
end
-
-
1
def merge_default_headers(original, default)
-
Header.new self, super
-
end
-
-
1
def handle_conditional_get!
-
super unless committed?
-
end
-
end
-
-
1
def process(name)
-
t1 = Thread.current
-
locals = t1.keys.map { |key| [key, t1[key]] }
-
-
error = nil
-
# This processes the action in a child thread. It lets us return the
-
# response code and headers back up the rack stack, and still process
-
# the body in parallel with sending data to the client
-
Thread.new {
-
t2 = Thread.current
-
t2.abort_on_exception = true
-
-
# Since we're processing the view in a different thread, copy the
-
# thread locals from the main thread to the child thread. :'(
-
locals.each { |k,v| t2[k] = v }
-
-
begin
-
super(name)
-
rescue => e
-
if @_response.committed?
-
begin
-
@_response.stream.write(ActionView::Base.streaming_completion_on_exception) if request.format == :html
-
@_response.stream.call_on_error
-
rescue => exception
-
log_error(exception)
-
ensure
-
log_error(e)
-
@_response.stream.close
-
end
-
else
-
error = e
-
end
-
ensure
-
@_response.commit!
-
end
-
}
-
-
@_response.await_commit
-
raise error if error
-
end
-
-
1
def log_error(exception)
-
logger = ActionController::Base.logger
-
return unless logger
-
-
message = "\n#{exception.class} (#{exception.message}):\n"
-
message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code)
-
message << " " << exception.backtrace.join("\n ")
-
logger.fatal("#{message}\n\n")
-
end
-
-
1
def response_body=(body)
-
super
-
response.close if response
-
end
-
-
1
def set_response!(request)
-
if request.env["HTTP_VERSION"] == "HTTP/1.0"
-
super
-
else
-
@_response = Live::Response.new
-
@_response.request = request
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/array/extract_options'
-
1
require 'abstract_controller/collector'
-
-
1
module ActionController #:nodoc:
-
1
module MimeResponds
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
class_attribute :responder, :mimes_for_respond_to
-
1
self.responder = ActionController::Responder
-
1
clear_respond_to
-
end
-
-
1
module ClassMethods
-
# Defines mime types that are rendered by default when invoking
-
# <tt>respond_with</tt>.
-
#
-
# respond_to :html, :xml, :json
-
#
-
# Specifies that all actions in the controller respond to requests
-
# for <tt>:html</tt>, <tt>:xml</tt> and <tt>:json</tt>.
-
#
-
# To specify on per-action basis, use <tt>:only</tt> and
-
# <tt>:except</tt> with an array of actions or a single action:
-
#
-
# respond_to :html
-
# respond_to :xml, :json, except: [ :edit ]
-
#
-
# This specifies that all actions respond to <tt>:html</tt>
-
# and all actions except <tt>:edit</tt> respond to <tt>:xml</tt> and
-
# <tt>:json</tt>.
-
#
-
# respond_to :json, only: :create
-
#
-
# This specifies that the <tt>:create</tt> action and no other responds
-
# to <tt>:json</tt>.
-
1
def respond_to(*mimes)
-
options = mimes.extract_options!
-
-
only_actions = Array(options.delete(:only)).map(&:to_s)
-
except_actions = Array(options.delete(:except)).map(&:to_s)
-
-
new = mimes_for_respond_to.dup
-
mimes.each do |mime|
-
mime = mime.to_sym
-
new[mime] = {}
-
new[mime][:only] = only_actions unless only_actions.empty?
-
new[mime][:except] = except_actions unless except_actions.empty?
-
end
-
self.mimes_for_respond_to = new.freeze
-
end
-
-
# Clear all mime types in <tt>respond_to</tt>.
-
#
-
1
def clear_respond_to
-
1
self.mimes_for_respond_to = Hash.new.freeze
-
end
-
end
-
-
# Without web-service support, an action which collects the data for displaying a list of people
-
# might look something like this:
-
#
-
# def index
-
# @people = Person.all
-
# end
-
#
-
# Here's the same action, with web-service support baked in:
-
#
-
# def index
-
# @people = Person.all
-
#
-
# respond_to do |format|
-
# format.html
-
# format.xml { render xml: @people }
-
# end
-
# end
-
#
-
# What that says is, "if the client wants HTML in response to this action, just respond as we
-
# would have before, but if the client wants XML, return them the list of people in XML format."
-
# (Rails determines the desired response format from the HTTP Accept header submitted by the client.)
-
#
-
# Supposing you have an action that adds a new person, optionally creating their company
-
# (by name) if it does not already exist, without web-services, it might look like this:
-
#
-
# def create
-
# @company = Company.find_or_create_by(name: params[:company][:name])
-
# @person = @company.people.create(params[:person])
-
#
-
# redirect_to(person_list_url)
-
# end
-
#
-
# Here's the same action, with web-service support baked in:
-
#
-
# def create
-
# company = params[:person].delete(:company)
-
# @company = Company.find_or_create_by(name: company[:name])
-
# @person = @company.people.create(params[:person])
-
#
-
# respond_to do |format|
-
# format.html { redirect_to(person_list_url) }
-
# format.js
-
# format.xml { render xml: @person.to_xml(include: @company) }
-
# end
-
# end
-
#
-
# If the client wants HTML, we just redirect them back to the person list. If they want JavaScript,
-
# then it is an Ajax request and we render the JavaScript template associated with this action.
-
# Lastly, if the client wants XML, we render the created person as XML, but with a twist: we also
-
# include the person's company in the rendered XML, so you get something like this:
-
#
-
# <person>
-
# <id>...</id>
-
# ...
-
# <company>
-
# <id>...</id>
-
# <name>...</name>
-
# ...
-
# </company>
-
# </person>
-
#
-
# Note, however, the extra bit at the top of that action:
-
#
-
# company = params[:person].delete(:company)
-
# @company = Company.find_or_create_by(name: company[:name])
-
#
-
# This is because the incoming XML document (if a web-service request is in process) can only contain a
-
# single root-node. So, we have to rearrange things so that the request looks like this (url-encoded):
-
#
-
# person[name]=...&person[company][name]=...&...
-
#
-
# And, like this (xml-encoded):
-
#
-
# <person>
-
# <name>...</name>
-
# <company>
-
# <name>...</name>
-
# </company>
-
# </person>
-
#
-
# In other words, we make the request so that it operates on a single entity's person. Then, in the action,
-
# we extract the company data from the request, find or create the company, and then create the new person
-
# with the remaining data.
-
#
-
# Note that you can define your own XML parameter parser which would allow you to describe multiple entities
-
# in a single request (i.e., by wrapping them all in a single root node), but if you just go with the flow
-
# and accept Rails' defaults, life will be much easier.
-
#
-
# If you need to use a MIME type which isn't supported by default, you can register your own handlers in
-
# config/initializers/mime_types.rb as follows.
-
#
-
# Mime::Type.register "image/jpg", :jpg
-
#
-
# Respond to also allows you to specify a common block for different formats by using any:
-
#
-
# def index
-
# @people = Person.all
-
#
-
# respond_to do |format|
-
# format.html
-
# format.any(:xml, :json) { render request.format.to_sym => @people }
-
# end
-
# end
-
#
-
# In the example above, if the format is xml, it will render:
-
#
-
# render xml: @people
-
#
-
# Or if the format is json:
-
#
-
# render json: @people
-
#
-
# Since this is a common pattern, you can use the class method respond_to
-
# with the respond_with method to have the same results:
-
#
-
# class PeopleController < ApplicationController
-
# respond_to :html, :xml, :json
-
#
-
# def index
-
# @people = Person.all
-
# respond_with(@people)
-
# end
-
# end
-
#
-
# Formats can have different variants.
-
#
-
# The request variant is a specialization of the request format, like <tt>:tablet</tt>,
-
# <tt>:phone</tt>, or <tt>:desktop</tt>.
-
#
-
# We often want to render different html/json/xml templates for phones,
-
# tablets, and desktop browsers. Variants make it easy.
-
#
-
# You can set the variant in a +before_action+:
-
#
-
# request.variant = :tablet if request.user_agent =~ /iPad/
-
#
-
# Respond to variants in the action just like you respond to formats:
-
#
-
# respond_to do |format|
-
# format.html do |variant|
-
# variant.tablet # renders app/views/projects/show.html+tablet.erb
-
# variant.phone { extra_setup; render ... }
-
# variant.none { special_setup } # executed only if there is no variant set
-
# end
-
# end
-
#
-
# Provide separate templates for each format and variant:
-
#
-
# app/views/projects/show.html.erb
-
# app/views/projects/show.html+tablet.erb
-
# app/views/projects/show.html+phone.erb
-
#
-
# When you're not sharing any code within the format, you can simplify defining variants
-
# using the inline syntax:
-
#
-
# respond_to do |format|
-
# format.js { render "trash" }
-
# format.html.phone { redirect_to progress_path }
-
# format.html.none { render "trash" }
-
# end
-
#
-
# Variants also support common `any`/`all` block that formats have.
-
#
-
# It works for both inline:
-
#
-
# respond_to do |format|
-
# format.html.any { render text: "any" }
-
# format.html.phone { render text: "phone" }
-
# end
-
#
-
# and block syntax:
-
#
-
# respond_to do |format|
-
# format.html do |variant|
-
# variant.any(:tablet, :phablet){ render text: "any" }
-
# variant.phone { render text: "phone" }
-
# end
-
# end
-
#
-
# You can also set an array of variants:
-
#
-
# request.variant = [:tablet, :phone]
-
#
-
# which will work similarly to formats and MIME types negotiation. If there will be no
-
# :tablet variant declared, :phone variant will be picked:
-
#
-
# respond_to do |format|
-
# format.html.none
-
# format.html.phone # this gets rendered
-
# end
-
#
-
# Be sure to check the documentation of +respond_with+ and
-
# <tt>ActionController::MimeResponds.respond_to</tt> for more examples.
-
1
def respond_to(*mimes, &block)
-
raise ArgumentError, "respond_to takes either types or a block, never both" if mimes.any? && block_given?
-
-
if collector = retrieve_collector_from_mimes(mimes, &block)
-
response = collector.response
-
response ? response.call : render({})
-
end
-
end
-
-
# For a given controller action, respond_with generates an appropriate
-
# response based on the mime-type requested by the client.
-
#
-
# If the method is called with just a resource, as in this example -
-
#
-
# class PeopleController < ApplicationController
-
# respond_to :html, :xml, :json
-
#
-
# def index
-
# @people = Person.all
-
# respond_with @people
-
# end
-
# end
-
#
-
# then the mime-type of the response is typically selected based on the
-
# request's Accept header and the set of available formats declared
-
# by previous calls to the controller's class method +respond_to+. Alternatively
-
# the mime-type can be selected by explicitly setting <tt>request.format</tt> in
-
# the controller.
-
#
-
# If an acceptable format is not identified, the application returns a
-
# '406 - not acceptable' status. Otherwise, the default response is to render
-
# a template named after the current action and the selected format,
-
# e.g. <tt>index.html.erb</tt>. If no template is available, the behavior
-
# depends on the selected format:
-
#
-
# * for an html response - if the request method is +get+, an exception
-
# is raised but for other requests such as +post+ the response
-
# depends on whether the resource has any validation errors (i.e.
-
# assuming that an attempt has been made to save the resource,
-
# e.g. by a +create+ action) -
-
# 1. If there are no errors, i.e. the resource
-
# was saved successfully, the response +redirect+'s to the resource
-
# i.e. its +show+ action.
-
# 2. If there are validation errors, the response
-
# renders a default action, which is <tt>:new</tt> for a
-
# +post+ request or <tt>:edit</tt> for +patch+ or +put+.
-
# Thus an example like this -
-
#
-
# respond_to :html, :xml
-
#
-
# def create
-
# @user = User.new(params[:user])
-
# flash[:notice] = 'User was successfully created.' if @user.save
-
# respond_with(@user)
-
# end
-
#
-
# is equivalent, in the absence of <tt>create.html.erb</tt>, to -
-
#
-
# def create
-
# @user = User.new(params[:user])
-
# respond_to do |format|
-
# if @user.save
-
# flash[:notice] = 'User was successfully created.'
-
# format.html { redirect_to(@user) }
-
# format.xml { render xml: @user }
-
# else
-
# format.html { render action: "new" }
-
# format.xml { render xml: @user }
-
# end
-
# end
-
# end
-
#
-
# * for a javascript request - if the template isn't found, an exception is
-
# raised.
-
# * for other requests - i.e. data formats such as xml, json, csv etc, if
-
# the resource passed to +respond_with+ responds to <code>to_<format></code>,
-
# the method attempts to render the resource in the requested format
-
# directly, e.g. for an xml request, the response is equivalent to calling
-
# <code>render xml: resource</code>.
-
#
-
# === Nested resources
-
#
-
# As outlined above, the +resources+ argument passed to +respond_with+
-
# can play two roles. It can be used to generate the redirect url
-
# for successful html requests (e.g. for +create+ actions when
-
# no template exists), while for formats other than html and javascript
-
# it is the object that gets rendered, by being converted directly to the
-
# required format (again assuming no template exists).
-
#
-
# For redirecting successful html requests, +respond_with+ also supports
-
# the use of nested resources, which are supplied in the same way as
-
# in <code>form_for</code> and <code>polymorphic_url</code>. For example -
-
#
-
# def create
-
# @project = Project.find(params[:project_id])
-
# @task = @project.comments.build(params[:task])
-
# flash[:notice] = 'Task was successfully created.' if @task.save
-
# respond_with(@project, @task)
-
# end
-
#
-
# This would cause +respond_with+ to redirect to <code>project_task_url</code>
-
# instead of <code>task_url</code>. For request formats other than html or
-
# javascript, if multiple resources are passed in this way, it is the last
-
# one specified that is rendered.
-
#
-
# === Customizing response behavior
-
#
-
# Like +respond_to+, +respond_with+ may also be called with a block that
-
# can be used to overwrite any of the default responses, e.g. -
-
#
-
# def create
-
# @user = User.new(params[:user])
-
# flash[:notice] = "User was successfully created." if @user.save
-
#
-
# respond_with(@user) do |format|
-
# format.html { render }
-
# end
-
# end
-
#
-
# The argument passed to the block is an ActionController::MimeResponds::Collector
-
# object which stores the responses for the formats defined within the
-
# block. Note that formats with responses defined explicitly in this way
-
# do not have to first be declared using the class method +respond_to+.
-
#
-
# Also, a hash passed to +respond_with+ immediately after the specified
-
# resource(s) is interpreted as a set of options relevant to all
-
# formats. Any option accepted by +render+ can be used, e.g.
-
# respond_with @people, status: 200
-
# However, note that these options are ignored after an unsuccessful attempt
-
# to save a resource, e.g. when automatically rendering <tt>:new</tt>
-
# after a post request.
-
#
-
# Two additional options are relevant specifically to +respond_with+ -
-
# 1. <tt>:location</tt> - overwrites the default redirect location used after
-
# a successful html +post+ request.
-
# 2. <tt>:action</tt> - overwrites the default render action used after an
-
# unsuccessful html +post+ request.
-
1
def respond_with(*resources, &block)
-
if self.class.mimes_for_respond_to.empty?
-
raise "In order to use respond_with, first you need to declare the " \
-
"formats your controller responds to in the class level."
-
end
-
-
if collector = retrieve_collector_from_mimes(&block)
-
options = resources.size == 1 ? {} : resources.extract_options!
-
options = options.clone
-
options[:default_response] = collector.response
-
(options.delete(:responder) || self.class.responder).call(self, resources, options)
-
end
-
end
-
-
1
protected
-
-
# Collect mimes declared in the class method respond_to valid for the
-
# current action.
-
1
def collect_mimes_from_class_level #:nodoc:
-
action = action_name.to_s
-
-
self.class.mimes_for_respond_to.keys.select do |mime|
-
config = self.class.mimes_for_respond_to[mime]
-
-
if config[:except]
-
!config[:except].include?(action)
-
elsif config[:only]
-
config[:only].include?(action)
-
else
-
true
-
end
-
end
-
end
-
-
# Returns a Collector object containing the appropriate mime-type response
-
# for the current request, based on the available responses defined by a block.
-
# In typical usage this is the block passed to +respond_with+ or +respond_to+.
-
#
-
# Sends :not_acceptable to the client and returns nil if no suitable format
-
# is available.
-
1
def retrieve_collector_from_mimes(mimes=nil, &block) #:nodoc:
-
mimes ||= collect_mimes_from_class_level
-
collector = Collector.new(mimes, request.variant)
-
block.call(collector) if block_given?
-
format = collector.negotiate_format(request)
-
-
if format
-
_process_format(format)
-
collector
-
else
-
raise ActionController::UnknownFormat
-
end
-
end
-
-
# A container for responses available from the current controller for
-
# requests for different mime-types sent to a particular action.
-
#
-
# The public controller methods +respond_with+ and +respond_to+ may be called
-
# with a block that is used to define responses to different mime-types, e.g.
-
# for +respond_to+ :
-
#
-
# respond_to do |format|
-
# format.html
-
# format.xml { render xml: @people }
-
# end
-
#
-
# In this usage, the argument passed to the block (+format+ above) is an
-
# instance of the ActionController::MimeResponds::Collector class. This
-
# object serves as a container in which available responses can be stored by
-
# calling any of the dynamically generated, mime-type-specific methods such
-
# as +html+, +xml+ etc on the Collector. Each response is represented by a
-
# corresponding block if present.
-
#
-
# A subsequent call to #negotiate_format(request) will enable the Collector
-
# to determine which specific mime-type it should respond with for the current
-
# request, with this response then being accessible by calling #response.
-
1
class Collector
-
1
include AbstractController::Collector
-
1
attr_accessor :format
-
-
1
def initialize(mimes, variant = nil)
-
@responses = {}
-
@variant = variant
-
-
mimes.each { |mime| @responses["Mime::#{mime.upcase}".constantize] = nil }
-
end
-
-
1
def any(*args, &block)
-
if args.any?
-
args.each { |type| send(type, &block) }
-
else
-
custom(Mime::ALL, &block)
-
end
-
end
-
1
alias :all :any
-
-
1
def custom(mime_type, &block)
-
mime_type = Mime::Type.lookup(mime_type.to_s) unless mime_type.is_a?(Mime::Type)
-
@responses[mime_type] ||= if block_given?
-
block
-
else
-
VariantCollector.new(@variant)
-
end
-
end
-
-
1
def response
-
response = @responses.fetch(format, @responses[Mime::ALL])
-
if response.is_a?(VariantCollector) # `format.html.phone` - variant inline syntax
-
response.variant
-
elsif response.nil? || response.arity == 0 # `format.html` - just a format, call its block
-
response
-
else # `format.html{ |variant| variant.phone }` - variant block syntax
-
variant_collector = VariantCollector.new(@variant)
-
response.call(variant_collector) # call format block with variants collector
-
variant_collector.variant
-
end
-
end
-
-
1
def negotiate_format(request)
-
@format = request.negotiate_mime(@responses.keys)
-
end
-
-
1
class VariantCollector #:nodoc:
-
1
def initialize(variant = nil)
-
@variant = variant
-
@variants = {}
-
end
-
-
1
def any(*args, &block)
-
if block_given?
-
if args.any? && args.none?{ |a| a == @variant }
-
args.each{ |v| @variants[v] = block }
-
else
-
@variants[:any] = block
-
end
-
end
-
end
-
1
alias :all :any
-
-
1
def method_missing(name, *args, &block)
-
@variants[name] = block if block_given?
-
end
-
-
1
def variant
-
if @variant.nil?
-
@variants[:none] || @variants[:any]
-
elsif (@variants.keys & @variant).any?
-
@variant.each do |v|
-
return @variants[v] if @variants.key?(v)
-
end
-
else
-
@variants[:any]
-
end
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/hash/slice'
-
1
require 'active_support/core_ext/hash/except'
-
1
require 'active_support/core_ext/module/anonymous'
-
1
require 'active_support/core_ext/struct'
-
1
require 'action_dispatch/http/mime_type'
-
-
1
module ActionController
-
# Wraps the parameters hash into a nested hash. This will allow clients to submit
-
# POST requests without having to specify any root elements.
-
#
-
# This functionality is enabled in +config/initializers/wrap_parameters.rb+
-
# and can be customized. If you are upgrading to \Rails 3.1, this file will
-
# need to be created for the functionality to be enabled.
-
#
-
# You could also turn it on per controller by setting the format array to
-
# a non-empty array:
-
#
-
# class UsersController < ApplicationController
-
# wrap_parameters format: [:json, :xml]
-
# end
-
#
-
# If you enable +ParamsWrapper+ for +:json+ format, instead of having to
-
# send JSON parameters like this:
-
#
-
# {"user": {"name": "Konata"}}
-
#
-
# You can send parameters like this:
-
#
-
# {"name": "Konata"}
-
#
-
# And it will be wrapped into a nested hash with the key name matching the
-
# controller's name. For example, if you're posting to +UsersController+,
-
# your new +params+ hash will look like this:
-
#
-
# {"name" => "Konata", "user" => {"name" => "Konata"}}
-
#
-
# You can also specify the key in which the parameters should be wrapped to,
-
# and also the list of attributes it should wrap by using either +:include+ or
-
# +:exclude+ options like this:
-
#
-
# class UsersController < ApplicationController
-
# wrap_parameters :person, include: [:username, :password]
-
# end
-
#
-
# On ActiveRecord models with no +:include+ or +:exclude+ option set,
-
# it will only wrap the parameters returned by the class method
-
# <tt>attribute_names</tt>.
-
#
-
# If you're going to pass the parameters to an +ActiveModel+ object (such as
-
# <tt>User.new(params[:user])</tt>), you might consider passing the model class to
-
# the method instead. The +ParamsWrapper+ will actually try to determine the
-
# list of attribute names from the model and only wrap those attributes:
-
#
-
# class UsersController < ApplicationController
-
# wrap_parameters Person
-
# end
-
#
-
# You still could pass +:include+ and +:exclude+ to set the list of attributes
-
# you want to wrap.
-
#
-
# By default, if you don't specify the key in which the parameters would be
-
# wrapped to, +ParamsWrapper+ will actually try to determine if there's
-
# a model related to it or not. This controller, for example:
-
#
-
# class Admin::UsersController < ApplicationController
-
# end
-
#
-
# will try to check if <tt>Admin::User</tt> or +User+ model exists, and use it to
-
# determine the wrapper key respectively. If both models don't exist,
-
# it will then fallback to use +user+ as the key.
-
1
module ParamsWrapper
-
1
extend ActiveSupport::Concern
-
-
1
EXCLUDE_PARAMETERS = %w(authenticity_token _method utf8)
-
-
1
require 'mutex_m'
-
-
1
class Options < Struct.new(:name, :format, :include, :exclude, :klass, :model) # :nodoc:
-
1
include Mutex_m
-
-
1
def self.from_hash(hash)
-
2
name = hash[:name]
-
2
format = Array(hash[:format])
-
2
include = hash[:include] && Array(hash[:include]).collect(&:to_s)
-
2
exclude = hash[:exclude] && Array(hash[:exclude]).collect(&:to_s)
-
2
new name, format, include, exclude, nil, nil
-
end
-
-
1
def initialize(name, format, include, exclude, klass, model) # nodoc
-
2
super
-
2
@include_set = include
-
2
@name_set = name
-
end
-
-
1
def model
-
super || synchronize { super || self.model = _default_wrap_model }
-
end
-
-
1
def include
-
return super if @include_set
-
-
m = model
-
synchronize do
-
return super if @include_set
-
-
@include_set = true
-
-
unless super || exclude
-
if m.respond_to?(:attribute_names) && m.attribute_names.any?
-
self.include = m.attribute_names
-
end
-
end
-
end
-
end
-
-
1
def name
-
return super if @name_set
-
-
m = model
-
synchronize do
-
return super if @name_set
-
-
@name_set = true
-
-
unless super || klass.anonymous?
-
self.name = m ? m.to_s.demodulize.underscore :
-
klass.controller_name.singularize
-
end
-
end
-
end
-
-
1
private
-
# Determine the wrapper model from the controller's name. By convention,
-
# this could be done by trying to find the defined model that has the
-
# same singularize name as the controller. For example, +UsersController+
-
# will try to find if the +User+ model exists.
-
#
-
# This method also does namespace lookup. Foo::Bar::UsersController will
-
# try to find Foo::Bar::User, Foo::User and finally User.
-
1
def _default_wrap_model #:nodoc:
-
return nil if klass.anonymous?
-
model_name = klass.name.sub(/Controller$/, '').classify
-
-
begin
-
if model_klass = model_name.safe_constantize
-
model_klass
-
else
-
namespaces = model_name.split("::")
-
namespaces.delete_at(-2)
-
break if namespaces.last == model_name
-
model_name = namespaces.join("::")
-
end
-
end until model_klass
-
-
model_klass
-
end
-
end
-
-
1
included do
-
1
class_attribute :_wrapper_options
-
1
self._wrapper_options = Options.from_hash(format: [])
-
end
-
-
1
module ClassMethods
-
1
def _set_wrapper_options(options)
-
self._wrapper_options = Options.from_hash(options)
-
end
-
-
# Sets the name of the wrapper key, or the model which +ParamsWrapper+
-
# would use to determine the attribute names from.
-
#
-
# ==== Examples
-
# wrap_parameters format: :xml
-
# # enables the parameter wrapper for XML format
-
#
-
# wrap_parameters :person
-
# # wraps parameters into +params[:person]+ hash
-
#
-
# wrap_parameters Person
-
# # wraps parameters by determining the wrapper key from Person class
-
# (+person+, in this case) and the list of attribute names
-
#
-
# wrap_parameters include: [:username, :title]
-
# # wraps only +:username+ and +:title+ attributes from parameters.
-
#
-
# wrap_parameters false
-
# # disables parameters wrapping for this controller altogether.
-
#
-
# ==== Options
-
# * <tt>:format</tt> - The list of formats in which the parameters wrapper
-
# will be enabled.
-
# * <tt>:include</tt> - The list of attribute names which parameters wrapper
-
# will wrap into a nested hash.
-
# * <tt>:exclude</tt> - The list of attribute names which parameters wrapper
-
# will exclude from a nested hash.
-
1
def wrap_parameters(name_or_model_or_options, options = {})
-
1
model = nil
-
-
1
case name_or_model_or_options
-
when Hash
-
1
options = name_or_model_or_options
-
when false
-
options = options.merge(:format => [])
-
when Symbol, String
-
options = options.merge(:name => name_or_model_or_options)
-
else
-
model = name_or_model_or_options
-
end
-
-
1
opts = Options.from_hash _wrapper_options.to_h.slice(:format).merge(options)
-
1
opts.model = model
-
1
opts.klass = self
-
-
1
self._wrapper_options = opts
-
end
-
-
# Sets the default wrapper key or model which will be used to determine
-
# wrapper key and attribute names. Will be called automatically when the
-
# module is inherited.
-
1
def inherited(klass)
-
3
if klass._wrapper_options.format.any?
-
3
params = klass._wrapper_options.dup
-
3
params.klass = klass
-
3
klass._wrapper_options = params
-
end
-
3
super
-
end
-
end
-
-
# Performs parameters wrapping upon the request. Will be called automatically
-
# by the metal call stack.
-
1
def process_action(*args)
-
4
if _wrapper_enabled?
-
if request.parameters[_wrapper_key].present?
-
wrapped_hash = _extract_parameters(request.parameters)
-
else
-
wrapped_hash = _wrap_parameters request.request_parameters
-
end
-
-
wrapped_keys = request.request_parameters.keys
-
wrapped_filtered_hash = _wrap_parameters request.filtered_parameters.slice(*wrapped_keys)
-
-
# This will make the wrapped hash accessible from controller and view
-
request.parameters.merge! wrapped_hash
-
request.request_parameters.merge! wrapped_hash
-
-
# This will make the wrapped hash displayed in the log file
-
request.filtered_parameters.merge! wrapped_filtered_hash
-
end
-
4
super
-
end
-
-
1
private
-
-
# Returns the wrapper key which will use to stored wrapped parameters.
-
1
def _wrapper_key
-
_wrapper_options.name
-
end
-
-
# Returns the list of enabled formats.
-
1
def _wrapper_formats
-
4
_wrapper_options.format
-
end
-
-
# Returns the list of parameters which will be selected for wrapped.
-
1
def _wrap_parameters(parameters)
-
{ _wrapper_key => _extract_parameters(parameters) }
-
end
-
-
1
def _extract_parameters(parameters)
-
if include_only = _wrapper_options.include
-
parameters.slice(*include_only)
-
else
-
exclude = _wrapper_options.exclude || []
-
parameters.except(*(exclude + EXCLUDE_PARAMETERS))
-
end
-
end
-
-
# Checks if we should perform parameters wrapping.
-
1
def _wrapper_enabled?
-
4
ref = request.content_mime_type.try(:ref)
-
4
_wrapper_formats.include?(ref) && _wrapper_key && !request.request_parameters[_wrapper_key]
-
end
-
end
-
end
-
1
require 'action_dispatch/http/request'
-
1
require 'action_dispatch/http/response'
-
-
1
module ActionController
-
1
module RackDelegation
-
1
extend ActiveSupport::Concern
-
-
1
delegate :headers, :status=, :location=, :content_type=,
-
:status, :location, :content_type, :_status_code, :to => "@_response"
-
-
1
def dispatch(action, request)
-
set_response!(request)
-
super(action, request)
-
end
-
-
1
def response_body=(body)
-
4
response.body = body if response
-
4
super
-
end
-
-
1
def reset_session
-
@_request.reset_session
-
end
-
-
1
private
-
-
1
def set_response!(request)
-
@_response = ActionDispatch::Response.new
-
@_response.request = request
-
end
-
end
-
end
-
1
module ActionController
-
1
class RedirectBackError < AbstractController::Error #:nodoc:
-
1
DEFAULT_MESSAGE = 'No HTTP_REFERER was set in the request to this action, so redirect_to :back could not be called successfully. If this is a test, make sure to specify request.env["HTTP_REFERER"].'
-
-
1
def initialize(message = nil)
-
super(message || DEFAULT_MESSAGE)
-
end
-
end
-
-
1
module Redirecting
-
1
extend ActiveSupport::Concern
-
-
1
include AbstractController::Logger
-
1
include ActionController::RackDelegation
-
1
include ActionController::UrlFor
-
-
# Redirects the browser to the target specified in +options+. This parameter can take one of three forms:
-
#
-
# * <tt>Hash</tt> - The URL will be generated by calling url_for with the +options+.
-
# * <tt>Record</tt> - The URL will be generated by calling url_for with the +options+, which will reference a named URL for that record.
-
# * <tt>String</tt> starting with <tt>protocol://</tt> (like <tt>http://</tt>) or a protocol relative reference (like <tt>//</tt>) - Is passed straight through as the target for redirection.
-
# * <tt>String</tt> not containing a protocol - The current protocol and host is prepended to the string.
-
# * <tt>Proc</tt> - A block that will be executed in the controller's context. Should return any option accepted by +redirect_to+.
-
# * <tt>:back</tt> - Back to the page that issued the request. Useful for forms that are triggered from multiple places.
-
# Short-hand for <tt>redirect_to(request.env["HTTP_REFERER"])</tt>
-
#
-
# redirect_to action: "show", id: 5
-
# redirect_to post
-
# redirect_to "http://www.rubyonrails.org"
-
# redirect_to "/images/screenshot.jpg"
-
# redirect_to articles_url
-
# redirect_to :back
-
# redirect_to proc { edit_post_url(@post) }
-
#
-
# The redirection happens as a "302 Found" header unless otherwise specified.
-
#
-
# redirect_to post_url(@post), status: :found
-
# redirect_to action: 'atom', status: :moved_permanently
-
# redirect_to post_url(@post), status: 301
-
# redirect_to action: 'atom', status: 302
-
#
-
# The status code can either be a standard {HTTP Status code}[http://www.iana.org/assignments/http-status-codes] as an
-
# integer, or a symbol representing the downcased, underscored and symbolized description.
-
# Note that the status code must be a 3xx HTTP code, or redirection will not occur.
-
#
-
# If you are using XHR requests other than GET or POST and redirecting after the
-
# request then some browsers will follow the redirect using the original request
-
# method. This may lead to undesirable behavior such as a double DELETE. To work
-
# around this you can return a <tt>303 See Other</tt> status code which will be
-
# followed using a GET request.
-
#
-
# redirect_to posts_url, status: :see_other
-
# redirect_to action: 'index', status: 303
-
#
-
# It is also possible to assign a flash message as part of the redirection. There are two special accessors for the commonly used flash names
-
# +alert+ and +notice+ as well as a general purpose +flash+ bucket.
-
#
-
# redirect_to post_url(@post), alert: "Watch it, mister!"
-
# redirect_to post_url(@post), status: :found, notice: "Pay attention to the road"
-
# redirect_to post_url(@post), status: 301, flash: { updated_post_id: @post.id }
-
# redirect_to({ action: 'atom' }, alert: "Something serious happened")
-
#
-
# When using <tt>redirect_to :back</tt>, if there is no referrer, ActionController::RedirectBackError will be raised. You may specify some fallback
-
# behavior for this case by rescuing ActionController::RedirectBackError.
-
1
def redirect_to(options = {}, response_status = {}) #:doc:
-
2
raise ActionControllerError.new("Cannot redirect to nil!") unless options
-
2
raise ActionControllerError.new("Cannot redirect to a parameter hash!") if options.is_a?(ActionController::Parameters)
-
2
raise AbstractController::DoubleRenderError if response_body
-
-
2
self.status = _extract_redirect_to_status(options, response_status)
-
2
self.location = _compute_redirect_to_location(options)
-
2
self.response_body = "<html><body>You are being <a href=\"#{ERB::Util.h(location)}\">redirected</a>.</body></html>"
-
end
-
-
1
def _compute_redirect_to_location(options) #:nodoc:
-
case options
-
# The scheme name consist of a letter followed by any combination of
-
# letters, digits, and the plus ("+"), period ("."), or hyphen ("-")
-
# characters; and is terminated by a colon (":").
-
# See http://tools.ietf.org/html/rfc3986#section-3.1
-
# The protocol relative scheme starts with a double slash "//".
-
when /\A([a-z][a-z\d\-+\.]*:|\/\/).*/i
-
1
options
-
when String
-
3
request.protocol + request.host_with_port + options
-
when :back
-
request.headers["Referer"] or raise RedirectBackError
-
when Proc
-
_compute_redirect_to_location options.call
-
else
-
url_for(options)
-
4
end.delete("\0\r\n")
-
end
-
-
1
private
-
1
def _extract_redirect_to_status(options, response_status)
-
2
if options.is_a?(Hash) && options.key?(:status)
-
Rack::Utils.status_code(options.delete(:status))
-
2
elsif response_status.key?(:status)
-
Rack::Utils.status_code(response_status[:status])
-
else
-
2
302
-
end
-
end
-
end
-
end
-
1
require 'set'
-
-
1
module ActionController
-
# See <tt>Renderers.add</tt>
-
1
def self.add_renderer(key, &block)
-
Renderers.add(key, &block)
-
end
-
-
1
class MissingRenderer < LoadError
-
1
def initialize(format)
-
super "No renderer defined for format: #{format}"
-
end
-
end
-
-
1
module Renderers
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
class_attribute :_renderers
-
1
self._renderers = Set.new.freeze
-
end
-
-
1
module ClassMethods
-
1
def use_renderers(*args)
-
renderers = _renderers + args
-
self._renderers = renderers.freeze
-
end
-
1
alias use_renderer use_renderers
-
end
-
-
1
def render_to_body(options)
-
2
_handle_render_options(options) || super
-
end
-
-
1
def _handle_render_options(options)
-
2
_renderers.each do |name|
-
6
if options.key?(name)
-
_process_options(options)
-
return send("_render_option_#{name}", options.delete(name), options)
-
end
-
end
-
nil
-
end
-
-
# Hash of available renderers, mapping a renderer name to its proc.
-
# Default keys are <tt>:json</tt>, <tt>:js</tt>, <tt>:xml</tt>.
-
1
RENDERERS = Set.new
-
-
# Adds a new renderer to call within controller actions.
-
# A renderer is invoked by passing its name as an option to
-
# <tt>AbstractController::Rendering#render</tt>. To create a renderer
-
# pass it a name and a block. The block takes two arguments, the first
-
# is the value paired with its key and the second is the remaining
-
# hash of options passed to +render+.
-
#
-
# Create a csv renderer:
-
#
-
# ActionController::Renderers.add :csv do |obj, options|
-
# filename = options[:filename] || 'data'
-
# str = obj.respond_to?(:to_csv) ? obj.to_csv : obj.to_s
-
# send_data str, type: Mime::CSV,
-
# disposition: "attachment; filename=#{filename}.csv"
-
# end
-
#
-
# Note that we used Mime::CSV for the csv mime type as it comes with Rails.
-
# For a custom renderer, you'll need to register a mime type with
-
# <tt>Mime::Type.register</tt>.
-
#
-
# To use the csv renderer in a controller action:
-
#
-
# def show
-
# @csvable = Csvable.find(params[:id])
-
# respond_to do |format|
-
# format.html
-
# format.csv { render csv: @csvable, filename: @csvable.name }
-
# }
-
# end
-
# To use renderers and their mime types in more concise ways, see
-
# <tt>ActionController::MimeResponds::ClassMethods.respond_to</tt> and
-
# <tt>ActionController::MimeResponds#respond_with</tt>
-
1
def self.add(key, &block)
-
3
define_method("_render_option_#{key}", &block)
-
3
RENDERERS << key.to_sym
-
end
-
-
1
module All
-
1
extend ActiveSupport::Concern
-
1
include Renderers
-
-
1
included do
-
1
self._renderers = RENDERERS
-
end
-
end
-
-
1
add :json do |json, options|
-
json = json.to_json(options) unless json.kind_of?(String)
-
-
if options[:callback].present?
-
if self.content_type.nil? || self.content_type == Mime::JSON
-
self.content_type = Mime::JS
-
end
-
-
"/**/#{options[:callback]}(#{json})"
-
else
-
self.content_type ||= Mime::JSON
-
json
-
end
-
end
-
-
1
add :js do |js, options|
-
self.content_type ||= Mime::JS
-
js.respond_to?(:to_js) ? js.to_js(options) : js
-
end
-
-
1
add :xml do |xml, options|
-
self.content_type ||= Mime::XML
-
xml.respond_to?(:to_xml) ? xml.to_xml(options) : xml
-
end
-
end
-
end
-
1
module ActionController
-
1
module Rendering
-
1
extend ActiveSupport::Concern
-
-
1
RENDER_FORMATS_IN_PRIORITY = [:body, :text, :plain, :html]
-
-
# Before processing, set the request formats in current controller formats.
-
1
def process_action(*) #:nodoc:
-
4
self.formats = request.formats.map(&:ref).compact
-
4
super
-
end
-
-
# Check for double render errors and set the content_type after rendering.
-
1
def render(*args) #:nodoc:
-
2
raise ::AbstractController::DoubleRenderError if self.response_body
-
2
super
-
end
-
-
# Overwrite render_to_string because body can now be set to a rack body.
-
1
def render_to_string(*)
-
result = super
-
if result.respond_to?(:each)
-
string = ""
-
result.each { |r| string << r }
-
string
-
else
-
result
-
end
-
end
-
-
1
def render_to_body(options = {})
-
2
super || _render_in_priorities(options) || ' '
-
end
-
-
1
private
-
-
1
def _render_in_priorities(options)
-
RENDER_FORMATS_IN_PRIORITY.each do |format|
-
return options[format] if options.key?(format)
-
end
-
-
nil
-
end
-
-
1
def _process_format(format, options = {})
-
2
super
-
-
2
if options[:plain]
-
self.content_type = Mime::TEXT
-
else
-
2
self.content_type ||= format.to_s
-
end
-
end
-
-
# Normalize arguments by catching blocks and setting them on :update.
-
1
def _normalize_args(action=nil, options={}, &blk) #:nodoc:
-
2
options = super
-
2
options[:update] = blk if block_given?
-
2
options
-
end
-
-
# Normalize both text and status options.
-
1
def _normalize_options(options) #:nodoc:
-
2
_normalize_text(options)
-
-
2
if options[:html]
-
options[:html] = ERB::Util.html_escape(options[:html])
-
end
-
-
2
if options.delete(:nothing) || _any_render_format_is_nil?(options)
-
options[:body] = " "
-
end
-
-
2
if options[:status]
-
options[:status] = Rack::Utils.status_code(options[:status])
-
end
-
-
2
super
-
end
-
-
1
def _normalize_text(options)
-
2
RENDER_FORMATS_IN_PRIORITY.each do |format|
-
8
if options.key?(format) && options[format].respond_to?(:to_text)
-
options[format] = options[format].to_text
-
end
-
end
-
end
-
-
1
def _any_render_format_is_nil?(options)
-
10
RENDER_FORMATS_IN_PRIORITY.any? { |format| options.key?(format) && options[format].nil? }
-
end
-
-
# Process controller specific options, as status, content-type and location.
-
1
def _process_options(options) #:nodoc:
-
2
status, content_type, location = options.values_at(:status, :content_type, :location)
-
-
2
self.status = status if status
-
2
self.content_type = content_type if content_type
-
2
self.headers["Location"] = url_for(location) if location
-
-
2
super
-
end
-
end
-
end
-
1
require 'rack/session/abstract/id'
-
1
require 'action_controller/metal/exceptions'
-
-
1
module ActionController #:nodoc:
-
1
class InvalidAuthenticityToken < ActionControllerError #:nodoc:
-
end
-
-
1
class InvalidCrossOriginRequest < ActionControllerError #:nodoc:
-
end
-
-
# Controller actions are protected from Cross-Site Request Forgery (CSRF) attacks
-
# by including a token in the rendered html for your application. This token is
-
# stored as a random string in the session, to which an attacker does not have
-
# access. When a request reaches your application, \Rails verifies the received
-
# token with the token in the session. Only HTML and JavaScript requests are checked,
-
# so this will not protect your XML API (presumably you'll have a different
-
# authentication scheme there anyway).
-
#
-
# GET requests are not protected since they don't have side effects like writing
-
# to the database and don't leak sensitive information. JavaScript requests are
-
# an exception: a third-party site can use a <script> tag to reference a JavaScript
-
# URL on your site. When your JavaScript response loads on their site, it executes.
-
# With carefully crafted JavaScript on their end, sensitive data in your JavaScript
-
# response may be extracted. To prevent this, only XmlHttpRequest (known as XHR or
-
# Ajax) requests are allowed to make GET requests for JavaScript responses.
-
#
-
# It's important to remember that XML or JSON requests are also affected and if
-
# you're building an API you'll need something like:
-
#
-
# class ApplicationController < ActionController::Base
-
# protect_from_forgery
-
# skip_before_action :verify_authenticity_token, if: :json_request?
-
#
-
# protected
-
#
-
# def json_request?
-
# request.format.json?
-
# end
-
# end
-
#
-
# CSRF protection is turned on with the <tt>protect_from_forgery</tt> method,
-
# which checks the token and resets the session if it doesn't match what was expected.
-
# A call to this method is generated for new \Rails applications by default.
-
#
-
# The token parameter is named <tt>authenticity_token</tt> by default. The name and
-
# value of this token must be added to every layout that renders forms by including
-
# <tt>csrf_meta_tags</tt> in the html +head+.
-
#
-
# Learn more about CSRF attacks and securing your application in the
-
# {Ruby on Rails Security Guide}[http://guides.rubyonrails.org/security.html].
-
1
module RequestForgeryProtection
-
1
extend ActiveSupport::Concern
-
-
1
include AbstractController::Helpers
-
1
include AbstractController::Callbacks
-
-
1
included do
-
# Sets the token parameter name for RequestForgery. Calling +protect_from_forgery+
-
# sets it to <tt>:authenticity_token</tt> by default.
-
1
config_accessor :request_forgery_protection_token
-
1
self.request_forgery_protection_token ||= :authenticity_token
-
-
# Holds the class which implements the request forgery protection.
-
1
config_accessor :forgery_protection_strategy
-
1
self.forgery_protection_strategy = nil
-
-
# Controls whether request forgery protection is turned on or not. Turned off by default only in test mode.
-
1
config_accessor :allow_forgery_protection
-
1
self.allow_forgery_protection = true if allow_forgery_protection.nil?
-
-
1
helper_method :form_authenticity_token
-
1
helper_method :protect_against_forgery?
-
end
-
-
1
module ClassMethods
-
# Turn on request forgery protection. Bear in mind that only non-GET, HTML/JavaScript requests are checked.
-
#
-
# class ApplicationController < ActionController::Base
-
# protect_from_forgery
-
# end
-
#
-
# class FooController < ApplicationController
-
# protect_from_forgery except: :index
-
#
-
# You can disable CSRF protection on controller by skipping the verification before_action:
-
# skip_before_action :verify_authenticity_token
-
#
-
# Valid Options:
-
#
-
# * <tt>:only/:except</tt> - Passed to the <tt>before_action</tt> call. Set which actions are verified.
-
# * <tt>:with</tt> - Set the method to handle unverified request.
-
#
-
# Valid unverified request handling methods are:
-
# * <tt>:exception</tt> - Raises ActionController::InvalidAuthenticityToken exception.
-
# * <tt>:reset_session</tt> - Resets the session.
-
# * <tt>:null_session</tt> - Provides an empty session during request but doesn't reset it completely. Used as default if <tt>:with</tt> option is not specified.
-
1
def protect_from_forgery(options = {})
-
1
self.forgery_protection_strategy = protection_method_class(options[:with] || :null_session)
-
1
self.request_forgery_protection_token ||= :authenticity_token
-
1
prepend_before_action :verify_authenticity_token, options
-
1
append_after_action :verify_same_origin_request
-
end
-
-
1
private
-
-
1
def protection_method_class(name)
-
1
ActionController::RequestForgeryProtection::ProtectionMethods.const_get(name.to_s.classify)
-
rescue NameError
-
raise ArgumentError, 'Invalid request forgery protection method, use :null_session, :exception, or :reset_session'
-
end
-
end
-
-
1
module ProtectionMethods
-
1
class NullSession
-
1
def initialize(controller)
-
@controller = controller
-
end
-
-
# This is the method that defines the application behavior when a request is found to be unverified.
-
1
def handle_unverified_request
-
request = @controller.request
-
request.session = NullSessionHash.new(request.env)
-
request.env['action_dispatch.request.flash_hash'] = nil
-
request.env['rack.session.options'] = { skip: true }
-
request.env['action_dispatch.cookies'] = NullCookieJar.build(request)
-
end
-
-
1
protected
-
-
1
class NullSessionHash < Rack::Session::Abstract::SessionHash #:nodoc:
-
1
def initialize(env)
-
super(nil, env)
-
@data = {}
-
@loaded = true
-
end
-
-
# no-op
-
1
def destroy; end
-
-
1
def exists?
-
true
-
end
-
end
-
-
1
class NullCookieJar < ActionDispatch::Cookies::CookieJar #:nodoc:
-
1
def self.build(request)
-
key_generator = request.env[ActionDispatch::Cookies::GENERATOR_KEY]
-
host = request.host
-
secure = request.ssl?
-
-
new(key_generator, host, secure, options_for_env({}))
-
end
-
-
1
def write(*)
-
# nothing
-
end
-
end
-
end
-
-
1
class ResetSession
-
1
def initialize(controller)
-
@controller = controller
-
end
-
-
1
def handle_unverified_request
-
@controller.reset_session
-
end
-
end
-
-
1
class Exception
-
1
def initialize(controller)
-
@controller = controller
-
end
-
-
1
def handle_unverified_request
-
raise ActionController::InvalidAuthenticityToken
-
end
-
end
-
end
-
-
1
protected
-
# The actual before_action that is used to verify the CSRF token.
-
# Don't override this directly. Provide your own forgery protection
-
# strategy instead. If you override, you'll disable same-origin
-
# `<script>` verification.
-
#
-
# Lean on the protect_from_forgery declaration to mark which actions are
-
# due for same-origin request verification. If protect_from_forgery is
-
# enabled on an action, this before_action flags its after_action to
-
# verify that JavaScript responses are for XHR requests, ensuring they
-
# follow the browser's same-origin policy.
-
1
def verify_authenticity_token
-
4
mark_for_same_origin_verification!
-
-
4
if !verified_request?
-
logger.warn "Can't verify CSRF token authenticity" if logger
-
handle_unverified_request
-
end
-
end
-
-
1
def handle_unverified_request
-
forgery_protection_strategy.new(self).handle_unverified_request
-
end
-
-
1
CROSS_ORIGIN_JAVASCRIPT_WARNING = "Security warning: an embedded " \
-
"<script> tag on another site requested protected JavaScript. " \
-
"If you know what you're doing, go ahead and disable forgery " \
-
"protection on this action to permit cross-origin JavaScript embedding."
-
1
private_constant :CROSS_ORIGIN_JAVASCRIPT_WARNING
-
-
# If `verify_authenticity_token` was run (indicating that we have
-
# forgery protection enabled for this request) then also verify that
-
# we aren't serving an unauthorized cross-origin response.
-
1
def verify_same_origin_request
-
4
if marked_for_same_origin_verification? && non_xhr_javascript_response?
-
logger.warn CROSS_ORIGIN_JAVASCRIPT_WARNING if logger
-
raise ActionController::InvalidCrossOriginRequest, CROSS_ORIGIN_JAVASCRIPT_WARNING
-
end
-
end
-
-
# GET requests are checked for cross-origin JavaScript after rendering.
-
1
def mark_for_same_origin_verification!
-
4
@marked_for_same_origin_verification = request.get?
-
end
-
-
# If the `verify_authenticity_token` before_action ran, verify that
-
# JavaScript responses are only served to same-origin GET requests.
-
1
def marked_for_same_origin_verification?
-
4
@marked_for_same_origin_verification ||= false
-
end
-
-
# Check for cross-origin JavaScript responses.
-
1
def non_xhr_javascript_response?
-
2
content_type =~ %r(\Atext/javascript) && !request.xhr?
-
end
-
-
# Returns true or false if a request is verified. Checks:
-
#
-
# * is it a GET or HEAD request? Gets should be safe and idempotent
-
# * Does the form_authenticity_token match the given token value from the params?
-
# * Does the X-CSRF-Token header match the form_authenticity_token
-
1
def verified_request?
-
4
!protect_against_forgery? || request.get? || request.head? ||
-
form_authenticity_token == params[request_forgery_protection_token] ||
-
form_authenticity_token == request.headers['X-CSRF-Token']
-
end
-
-
# Sets the token value for the current session.
-
1
def form_authenticity_token
-
session[:_csrf_token] ||= SecureRandom.base64(32)
-
end
-
-
# The form's authenticity parameter. Override to provide your own.
-
1
def form_authenticity_param
-
params[request_forgery_protection_token]
-
end
-
-
# Checks if the controller allows forgery protection.
-
1
def protect_against_forgery?
-
4
allow_forgery_protection
-
end
-
end
-
end
-
1
module ActionController #:nodoc:
-
# This module is responsible to provide `rescue_from` helpers
-
# to controllers and configure when detailed exceptions must be
-
# shown.
-
1
module Rescue
-
1
extend ActiveSupport::Concern
-
1
include ActiveSupport::Rescuable
-
-
1
def rescue_with_handler(exception)
-
if (exception.respond_to?(:original_exception) &&
-
(orig_exception = exception.original_exception) &&
-
handler_for_rescue(orig_exception))
-
exception = orig_exception
-
end
-
super(exception)
-
end
-
-
# Override this method if you want to customize when detailed
-
# exceptions must be shown. This method is only called when
-
# consider_all_requests_local is false. By default, it returns
-
# false, but someone may set it to `request.local?` so local
-
# requests in production still shows the detailed exception pages.
-
1
def show_detailed_exceptions?
-
false
-
end
-
-
1
private
-
1
def process_action(*args)
-
4
super
-
rescue Exception => exception
-
request.env['action_dispatch.show_detailed_exceptions'] ||= show_detailed_exceptions?
-
rescue_with_handler(exception) || raise(exception)
-
end
-
end
-
end
-
1
require 'active_support/json'
-
-
1
module ActionController #:nodoc:
-
# Responsible for exposing a resource to different mime requests,
-
# usually depending on the HTTP verb. The responder is triggered when
-
# <code>respond_with</code> is called. The simplest case to study is a GET request:
-
#
-
# class PeopleController < ApplicationController
-
# respond_to :html, :xml, :json
-
#
-
# def index
-
# @people = Person.all
-
# respond_with(@people)
-
# end
-
# end
-
#
-
# When a request comes in, for example for an XML response, three steps happen:
-
#
-
# 1) the responder searches for a template at people/index.xml;
-
#
-
# 2) if the template is not available, it will invoke <code>#to_xml</code> on the given resource;
-
#
-
# 3) if the responder does not <code>respond_to :to_xml</code>, call <code>#to_format</code> on it.
-
#
-
# === Builtin HTTP verb semantics
-
#
-
# The default \Rails responder holds semantics for each HTTP verb. Depending on the
-
# content type, verb and the resource status, it will behave differently.
-
#
-
# Using \Rails default responder, a POST request for creating an object could
-
# be written as:
-
#
-
# def create
-
# @user = User.new(params[:user])
-
# flash[:notice] = 'User was successfully created.' if @user.save
-
# respond_with(@user)
-
# end
-
#
-
# Which is exactly the same as:
-
#
-
# def create
-
# @user = User.new(params[:user])
-
#
-
# respond_to do |format|
-
# if @user.save
-
# flash[:notice] = 'User was successfully created.'
-
# format.html { redirect_to(@user) }
-
# format.xml { render xml: @user, status: :created, location: @user }
-
# else
-
# format.html { render action: "new" }
-
# format.xml { render xml: @user.errors, status: :unprocessable_entity }
-
# end
-
# end
-
# end
-
#
-
# The same happens for PATCH/PUT and DELETE requests.
-
#
-
# === Nested resources
-
#
-
# You can supply nested resources as you do in <code>form_for</code> and <code>polymorphic_url</code>.
-
# Consider the project has many tasks example. The create action for
-
# TasksController would be like:
-
#
-
# def create
-
# @project = Project.find(params[:project_id])
-
# @task = @project.tasks.build(params[:task])
-
# flash[:notice] = 'Task was successfully created.' if @task.save
-
# respond_with(@project, @task)
-
# end
-
#
-
# Giving several resources ensures that the responder will redirect to
-
# <code>project_task_url</code> instead of <code>task_url</code>.
-
#
-
# Namespaced and singleton resources require a symbol to be given, as in
-
# polymorphic urls. If a project has one manager which has many tasks, it
-
# should be invoked as:
-
#
-
# respond_with(@project, :manager, @task)
-
#
-
# Note that if you give an array, it will be treated as a collection,
-
# so the following is not equivalent:
-
#
-
# respond_with [@project, :manager, @task]
-
#
-
# === Custom options
-
#
-
# <code>respond_with</code> also allows you to pass options that are forwarded
-
# to the underlying render call. Those options are only applied for success
-
# scenarios. For instance, you can do the following in the create method above:
-
#
-
# def create
-
# @project = Project.find(params[:project_id])
-
# @task = @project.tasks.build(params[:task])
-
# flash[:notice] = 'Task was successfully created.' if @task.save
-
# respond_with(@project, @task, status: 201)
-
# end
-
#
-
# This will return status 201 if the task was saved successfully. If not,
-
# it will simply ignore the given options and return status 422 and the
-
# resource errors. You can also override the location to redirect to:
-
#
-
# respond_with(@project, location: root_path)
-
#
-
# To customize the failure scenario, you can pass a block to
-
# <code>respond_with</code>:
-
#
-
# def create
-
# @project = Project.find(params[:project_id])
-
# @task = @project.tasks.build(params[:task])
-
# respond_with(@project, @task, status: 201) do |format|
-
# if @task.save
-
# flash[:notice] = 'Task was successfully created.'
-
# else
-
# format.html { render "some_special_template" }
-
# end
-
# end
-
# end
-
#
-
# Using <code>respond_with</code> with a block follows the same syntax as <code>respond_to</code>.
-
1
class Responder
-
1
attr_reader :controller, :request, :format, :resource, :resources, :options
-
-
1
DEFAULT_ACTIONS_FOR_VERBS = {
-
:post => :new,
-
:patch => :edit,
-
:put => :edit
-
}
-
-
1
def initialize(controller, resources, options={})
-
@controller = controller
-
@request = @controller.request
-
@format = @controller.formats.first
-
@resource = resources.last
-
@resources = resources
-
@options = options
-
@action = options.delete(:action)
-
@default_response = options.delete(:default_response)
-
end
-
-
1
delegate :head, :render, :redirect_to, :to => :controller
-
1
delegate :get?, :post?, :patch?, :put?, :delete?, :to => :request
-
-
# Undefine :to_json and :to_yaml since it's defined on Object
-
1
undef_method(:to_json) if method_defined?(:to_json)
-
1
undef_method(:to_yaml) if method_defined?(:to_yaml)
-
-
# Initializes a new responder and invokes the proper format. If the format is
-
# not defined, call to_format.
-
#
-
1
def self.call(*args)
-
new(*args).respond
-
end
-
-
# Main entry point for responder responsible to dispatch to the proper format.
-
#
-
1
def respond
-
method = "to_#{format}"
-
respond_to?(method) ? send(method) : to_format
-
end
-
-
# HTML format does not render the resource, it always attempt to render a
-
# template.
-
#
-
1
def to_html
-
default_render
-
rescue ActionView::MissingTemplate => e
-
navigation_behavior(e)
-
end
-
-
# to_js simply tries to render a template. If no template is found, raises the error.
-
1
def to_js
-
default_render
-
end
-
-
# All other formats follow the procedure below. First we try to render a
-
# template, if the template is not available, we verify if the resource
-
# responds to :to_format and display it.
-
#
-
1
def to_format
-
if get? || !has_errors? || response_overridden?
-
default_render
-
else
-
display_errors
-
end
-
rescue ActionView::MissingTemplate => e
-
api_behavior(e)
-
end
-
-
1
protected
-
-
# This is the common behavior for formats associated with browsing, like :html, :iphone and so forth.
-
1
def navigation_behavior(error)
-
if get?
-
raise error
-
elsif has_errors? && default_action
-
render :action => default_action
-
else
-
redirect_to navigation_location
-
end
-
end
-
-
# This is the common behavior for formats associated with APIs, such as :xml and :json.
-
1
def api_behavior(error)
-
raise error unless resourceful?
-
raise MissingRenderer.new(format) unless has_renderer?
-
-
if get?
-
display resource
-
elsif post?
-
display resource, :status => :created, :location => api_location
-
else
-
head :no_content
-
end
-
end
-
-
# Checks whether the resource responds to the current format or not.
-
#
-
1
def resourceful?
-
resource.respond_to?("to_#{format}")
-
end
-
-
# Returns the resource location by retrieving it from the options or
-
# returning the resources array.
-
#
-
1
def resource_location
-
options[:location] || resources
-
end
-
1
alias :navigation_location :resource_location
-
1
alias :api_location :resource_location
-
-
# If a response block was given, use it, otherwise call render on
-
# controller.
-
#
-
1
def default_render
-
if @default_response
-
@default_response.call(options)
-
else
-
controller.default_render(options)
-
end
-
end
-
-
# Display is just a shortcut to render a resource with the current format.
-
#
-
# display @user, status: :ok
-
#
-
# For XML requests it's equivalent to:
-
#
-
# render xml: @user, status: :ok
-
#
-
# Options sent by the user are also used:
-
#
-
# respond_with(@user, status: :created)
-
# display(@user, status: :ok)
-
#
-
# Results in:
-
#
-
# render xml: @user, status: :created
-
#
-
1
def display(resource, given_options={})
-
controller.render given_options.merge!(options).merge!(format => resource)
-
end
-
-
1
def display_errors
-
controller.render format => resource_errors, :status => :unprocessable_entity
-
end
-
-
# Check whether the resource has errors.
-
#
-
1
def has_errors?
-
resource.respond_to?(:errors) && !resource.errors.empty?
-
end
-
-
# Check whether the necessary Renderer is available
-
1
def has_renderer?
-
Renderers::RENDERERS.include?(format)
-
end
-
-
# By default, render the <code>:edit</code> action for HTML requests with errors, unless
-
# the verb was POST.
-
#
-
1
def default_action
-
@action ||= DEFAULT_ACTIONS_FOR_VERBS[request.request_method_symbol]
-
end
-
-
1
def resource_errors
-
respond_to?("#{format}_resource_errors", true) ? send("#{format}_resource_errors") : resource.errors
-
end
-
-
1
def json_resource_errors
-
{:errors => resource.errors}
-
end
-
-
1
def response_overridden?
-
@default_response.present?
-
end
-
end
-
end
-
1
require 'rack/chunked'
-
-
1
module ActionController #:nodoc:
-
# Allows views to be streamed back to the client as they are rendered.
-
#
-
# The default way Rails renders views is by first rendering the template
-
# and then the layout. The response is sent to the client after the whole
-
# template is rendered, all queries are made, and the layout is processed.
-
#
-
# Streaming inverts the rendering flow by rendering the layout first and
-
# streaming each part of the layout as they are processed. This allows the
-
# header of the HTML (which is usually in the layout) to be streamed back
-
# to client very quickly, allowing JavaScripts and stylesheets to be loaded
-
# earlier than usual.
-
#
-
# This approach was introduced in Rails 3.1 and is still improving. Several
-
# Rack middlewares may not work and you need to be careful when streaming.
-
# Those points are going to be addressed soon.
-
#
-
# In order to use streaming, you will need to use a Ruby version that
-
# supports fibers (fibers are supported since version 1.9.2 of the main
-
# Ruby implementation).
-
#
-
# Streaming can be added to a given template easily, all you need to do is
-
# to pass the :stream option.
-
#
-
# class PostsController
-
# def index
-
# @posts = Post.all
-
# render stream: true
-
# end
-
# end
-
#
-
# == When to use streaming
-
#
-
# Streaming may be considered to be overkill for lightweight actions like
-
# +new+ or +edit+. The real benefit of streaming is on expensive actions
-
# that, for example, do a lot of queries on the database.
-
#
-
# In such actions, you want to delay queries execution as much as you can.
-
# For example, imagine the following +dashboard+ action:
-
#
-
# def dashboard
-
# @posts = Post.all
-
# @pages = Page.all
-
# @articles = Article.all
-
# end
-
#
-
# Most of the queries here are happening in the controller. In order to benefit
-
# from streaming you would want to rewrite it as:
-
#
-
# def dashboard
-
# # Allow lazy execution of the queries
-
# @posts = Post.all
-
# @pages = Page.all
-
# @articles = Article.all
-
# render stream: true
-
# end
-
#
-
# Notice that :stream only works with templates. Rendering :json
-
# or :xml with :stream won't work.
-
#
-
# == Communication between layout and template
-
#
-
# When streaming, rendering happens top-down instead of inside-out.
-
# Rails starts with the layout, and the template is rendered later,
-
# when its +yield+ is reached.
-
#
-
# This means that, if your application currently relies on instance
-
# variables set in the template to be used in the layout, they won't
-
# work once you move to streaming. The proper way to communicate
-
# between layout and template, regardless of whether you use streaming
-
# or not, is by using +content_for+, +provide+ and +yield+.
-
#
-
# Take a simple example where the layout expects the template to tell
-
# which title to use:
-
#
-
# <html>
-
# <head><title><%= yield :title %></title></head>
-
# <body><%= yield %></body>
-
# </html>
-
#
-
# You would use +content_for+ in your template to specify the title:
-
#
-
# <%= content_for :title, "Main" %>
-
# Hello
-
#
-
# And the final result would be:
-
#
-
# <html>
-
# <head><title>Main</title></head>
-
# <body>Hello</body>
-
# </html>
-
#
-
# However, if +content_for+ is called several times, the final result
-
# would have all calls concatenated. For instance, if we have the following
-
# template:
-
#
-
# <%= content_for :title, "Main" %>
-
# Hello
-
# <%= content_for :title, " page" %>
-
#
-
# The final result would be:
-
#
-
# <html>
-
# <head><title>Main page</title></head>
-
# <body>Hello</body>
-
# </html>
-
#
-
# This means that, if you have <code>yield :title</code> in your layout
-
# and you want to use streaming, you would have to render the whole template
-
# (and eventually trigger all queries) before streaming the title and all
-
# assets, which kills the purpose of streaming. For this reason Rails 3.1
-
# introduces a new helper called +provide+ that does the same as +content_for+
-
# but tells the layout to stop searching for other entries and continue rendering.
-
#
-
# For instance, the template above using +provide+ would be:
-
#
-
# <%= provide :title, "Main" %>
-
# Hello
-
# <%= content_for :title, " page" %>
-
#
-
# Giving:
-
#
-
# <html>
-
# <head><title>Main</title></head>
-
# <body>Hello</body>
-
# </html>
-
#
-
# That said, when streaming, you need to properly check your templates
-
# and choose when to use +provide+ and +content_for+.
-
#
-
# == Headers, cookies, session and flash
-
#
-
# When streaming, the HTTP headers are sent to the client right before
-
# it renders the first line. This means that, modifying headers, cookies,
-
# session or flash after the template starts rendering will not propagate
-
# to the client.
-
#
-
# == Middlewares
-
#
-
# Middlewares that need to manipulate the body won't work with streaming.
-
# You should disable those middlewares whenever streaming in development
-
# or production. For instance, <tt>Rack::Bug</tt> won't work when streaming as it
-
# needs to inject contents in the HTML body.
-
#
-
# Also <tt>Rack::Cache</tt> won't work with streaming as it does not support
-
# streaming bodies yet. Whenever streaming Cache-Control is automatically
-
# set to "no-cache".
-
#
-
# == Errors
-
#
-
# When it comes to streaming, exceptions get a bit more complicated. This
-
# happens because part of the template was already rendered and streamed to
-
# the client, making it impossible to render a whole exception page.
-
#
-
# Currently, when an exception happens in development or production, Rails
-
# will automatically stream to the client:
-
#
-
# "><script>window.location = "/500.html"</script></html>
-
#
-
# The first two characters (">) are required in case the exception happens
-
# while rendering attributes for a given tag. You can check the real cause
-
# for the exception in your logger.
-
#
-
# == Web server support
-
#
-
# Not all web servers support streaming out-of-the-box. You need to check
-
# the instructions for each of them.
-
#
-
# ==== Unicorn
-
#
-
# Unicorn supports streaming but it needs to be configured. For this, you
-
# need to create a config file as follow:
-
#
-
# # unicorn.config.rb
-
# listen 3000, tcp_nopush: false
-
#
-
# And use it on initialization:
-
#
-
# unicorn_rails --config-file unicorn.config.rb
-
#
-
# You may also want to configure other parameters like <tt>:tcp_nodelay</tt>.
-
# Please check its documentation for more information: http://unicorn.bogomips.org/Unicorn/Configurator.html#method-i-listen
-
#
-
# If you are using Unicorn with Nginx, you may need to tweak Nginx.
-
# Streaming should work out of the box on Rainbows.
-
#
-
# ==== Passenger
-
#
-
# To be described.
-
#
-
1
module Streaming
-
1
extend ActiveSupport::Concern
-
-
1
protected
-
-
# Set proper cache control and transfer encoding when streaming
-
1
def _process_options(options) #:nodoc:
-
2
super
-
2
if options[:stream]
-
if env["HTTP_VERSION"] == "HTTP/1.0"
-
options.delete(:stream)
-
else
-
headers["Cache-Control"] ||= "no-cache"
-
headers["Transfer-Encoding"] = "chunked"
-
headers.delete("Content-Length")
-
end
-
end
-
end
-
-
# Call render_body if we are streaming instead of usual +render+.
-
1
def _render_template(options) #:nodoc:
-
2
if options.delete(:stream)
-
Rack::Chunked::Body.new view_renderer.render_body(view_context, options)
-
else
-
2
super
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/hash/indifferent_access'
-
1
require 'active_support/core_ext/array/wrap'
-
1
require 'active_support/rescuable'
-
1
require 'action_dispatch/http/upload'
-
1
require 'stringio'
-
1
require 'set'
-
-
1
module ActionController
-
# Raised when a required parameter is missing.
-
#
-
# params = ActionController::Parameters.new(a: {})
-
# params.fetch(:b)
-
# # => ActionController::ParameterMissing: param not found: b
-
# params.require(:a)
-
# # => ActionController::ParameterMissing: param not found: a
-
1
class ParameterMissing < KeyError
-
1
attr_reader :param # :nodoc:
-
-
1
def initialize(param) # :nodoc:
-
@param = param
-
super("param is missing or the value is empty: #{param}")
-
end
-
end
-
-
# Raised when a supplied parameter is not expected.
-
#
-
# params = ActionController::Parameters.new(a: "123", b: "456")
-
# params.permit(:c)
-
# # => ActionController::UnpermittedParameters: found unexpected keys: a, b
-
1
class UnpermittedParameters < IndexError
-
1
attr_reader :params # :nodoc:
-
-
1
def initialize(params) # :nodoc:
-
@params = params
-
super("found unpermitted parameters: #{params.join(", ")}")
-
end
-
end
-
-
# == Action Controller \Parameters
-
#
-
# Allows to choose which attributes should be whitelisted for mass updating
-
# and thus prevent accidentally exposing that which shouldn’t be exposed.
-
# Provides two methods for this purpose: #require and #permit. The former is
-
# used to mark parameters as required. The latter is used to set the parameter
-
# as permitted and limit which attributes should be allowed for mass updating.
-
#
-
# params = ActionController::Parameters.new({
-
# person: {
-
# name: 'Francesco',
-
# age: 22,
-
# role: 'admin'
-
# }
-
# })
-
#
-
# permitted = params.require(:person).permit(:name, :age)
-
# permitted # => {"name"=>"Francesco", "age"=>22}
-
# permitted.class # => ActionController::Parameters
-
# permitted.permitted? # => true
-
#
-
# Person.first.update!(permitted)
-
# # => #<Person id: 1, name: "Francesco", age: 22, role: "user">
-
#
-
# It provides two options that controls the top-level behavior of new instances:
-
#
-
# * +permit_all_parameters+ - If it's +true+, all the parameters will be
-
# permitted by default. The default is +false+.
-
# * +action_on_unpermitted_parameters+ - Allow to control the behavior when parameters
-
# that are not explicitly permitted are found. The values can be <tt>:log</tt> to
-
# write a message on the logger or <tt>:raise</tt> to raise
-
# ActionController::UnpermittedParameters exception. The default value is <tt>:log</tt>
-
# in test and development environments, +false+ otherwise.
-
#
-
# Examples:
-
#
-
# params = ActionController::Parameters.new
-
# params.permitted? # => false
-
#
-
# ActionController::Parameters.permit_all_parameters = true
-
#
-
# params = ActionController::Parameters.new
-
# params.permitted? # => true
-
#
-
# params = ActionController::Parameters.new(a: "123", b: "456")
-
# params.permit(:c)
-
# # => {}
-
#
-
# ActionController::Parameters.action_on_unpermitted_parameters = :raise
-
#
-
# params = ActionController::Parameters.new(a: "123", b: "456")
-
# params.permit(:c)
-
# # => ActionController::UnpermittedParameters: found unpermitted keys: a, b
-
#
-
# <tt>ActionController::Parameters</tt> is inherited from
-
# <tt>ActiveSupport::HashWithIndifferentAccess</tt>, this means
-
# that you can fetch values using either <tt>:key</tt> or <tt>"key"</tt>.
-
#
-
# params = ActionController::Parameters.new(key: 'value')
-
# params[:key] # => "value"
-
# params["key"] # => "value"
-
1
class Parameters < ActiveSupport::HashWithIndifferentAccess
-
1
cattr_accessor :permit_all_parameters, instance_accessor: false
-
1
cattr_accessor :action_on_unpermitted_parameters, instance_accessor: false
-
-
# Never raise an UnpermittedParameters exception because of these params
-
# are present. They are added by Rails and it's of no concern.
-
1
NEVER_UNPERMITTED_PARAMS = %w( controller action )
-
-
# Returns a new instance of <tt>ActionController::Parameters</tt>.
-
# Also, sets the +permitted+ attribute to the default value of
-
# <tt>ActionController::Parameters.permit_all_parameters</tt>.
-
#
-
# class Person < ActiveRecord::Base
-
# end
-
#
-
# params = ActionController::Parameters.new(name: 'Francesco')
-
# params.permitted? # => false
-
# Person.new(params) # => ActiveModel::ForbiddenAttributesError
-
#
-
# ActionController::Parameters.permit_all_parameters = true
-
#
-
# params = ActionController::Parameters.new(name: 'Francesco')
-
# params.permitted? # => true
-
# Person.new(params) # => #<Person id: nil, name: "Francesco">
-
1
def initialize(attributes = nil)
-
12
super(attributes)
-
12
@permitted = self.class.permit_all_parameters
-
end
-
-
# Attribute that keeps track of converted arrays, if any, to avoid double
-
# looping in the common use case permit + mass-assignment. Defined in a
-
# method to instantiate it only if needed.
-
1
def converted_arrays
-
@converted_arrays ||= Set.new
-
end
-
-
# Returns +true+ if the parameter is permitted, +false+ otherwise.
-
#
-
# params = ActionController::Parameters.new
-
# params.permitted? # => false
-
# params.permit!
-
# params.permitted? # => true
-
1
def permitted?
-
2
@permitted
-
end
-
-
# Sets the +permitted+ attribute to +true+. This can be used to pass
-
# mass assignment. Returns +self+.
-
#
-
# class Person < ActiveRecord::Base
-
# end
-
#
-
# params = ActionController::Parameters.new(name: 'Francesco')
-
# params.permitted? # => false
-
# Person.new(params) # => ActiveModel::ForbiddenAttributesError
-
# params.permit!
-
# params.permitted? # => true
-
# Person.new(params) # => #<Person id: nil, name: "Francesco">
-
1
def permit!
-
2
each_pair do |key, value|
-
6
value = convert_hashes_to_parameters(key, value)
-
6
Array.wrap(value).each do |_|
-
6
_.permit! if _.respond_to? :permit!
-
end
-
end
-
-
2
@permitted = true
-
2
self
-
end
-
-
# Ensures that a parameter is present. If it's present, returns
-
# the parameter at the given +key+, otherwise raises an
-
# <tt>ActionController::ParameterMissing</tt> error.
-
#
-
# ActionController::Parameters.new(person: { name: 'Francesco' }).require(:person)
-
# # => {"name"=>"Francesco"}
-
#
-
# ActionController::Parameters.new(person: nil).require(:person)
-
# # => ActionController::ParameterMissing: param not found: person
-
#
-
# ActionController::Parameters.new(person: {}).require(:person)
-
# # => ActionController::ParameterMissing: param not found: person
-
1
def require(key)
-
2
value = self[key]
-
2
if value.present? || value == false
-
2
value
-
else
-
raise ParameterMissing.new(key)
-
end
-
end
-
-
# Alias of #require.
-
1
alias :required :require
-
-
# Returns a new <tt>ActionController::Parameters</tt> instance that
-
# includes only the given +filters+ and sets the +permitted+ attribute
-
# for the object to +true+. This is useful for limiting which attributes
-
# should be allowed for mass updating.
-
#
-
# params = ActionController::Parameters.new(user: { name: 'Francesco', age: 22, role: 'admin' })
-
# permitted = params.require(:user).permit(:name, :age)
-
# permitted.permitted? # => true
-
# permitted.has_key?(:name) # => true
-
# permitted.has_key?(:age) # => true
-
# permitted.has_key?(:role) # => false
-
#
-
# Only permitted scalars pass the filter. For example, given
-
#
-
# params.permit(:name)
-
#
-
# +:name+ passes it is a key of +params+ whose associated value is of type
-
# +String+, +Symbol+, +NilClass+, +Numeric+, +TrueClass+, +FalseClass+,
-
# +Date+, +Time+, +DateTime+, +StringIO+, +IO+,
-
# +ActionDispatch::Http::UploadedFile+ or +Rack::Test::UploadedFile+.
-
# Otherwise, the key +:name+ is filtered out.
-
#
-
# You may declare that the parameter should be an array of permitted scalars
-
# by mapping it to an empty array:
-
#
-
# params = ActionController::Parameters.new(tags: ['rails', 'parameters'])
-
# params.permit(tags: [])
-
#
-
# You can also use +permit+ on nested parameters, like:
-
#
-
# params = ActionController::Parameters.new({
-
# person: {
-
# name: 'Francesco',
-
# age: 22,
-
# pets: [{
-
# name: 'Purplish',
-
# category: 'dogs'
-
# }]
-
# }
-
# })
-
#
-
# permitted = params.permit(person: [ :name, { pets: :name } ])
-
# permitted.permitted? # => true
-
# permitted[:person][:name] # => "Francesco"
-
# permitted[:person][:age] # => nil
-
# permitted[:person][:pets][0][:name] # => "Purplish"
-
# permitted[:person][:pets][0][:category] # => nil
-
#
-
# Note that if you use +permit+ in a key that points to a hash,
-
# it won't allow all the hash. You also need to specify which
-
# attributes inside the hash should be whitelisted.
-
#
-
# params = ActionController::Parameters.new({
-
# person: {
-
# contact: {
-
# email: 'none@test.com',
-
# phone: '555-1234'
-
# }
-
# }
-
# })
-
#
-
# params.require(:person).permit(:contact)
-
# # => {}
-
#
-
# params.require(:person).permit(contact: :phone)
-
# # => {"contact"=>{"phone"=>"555-1234"}}
-
#
-
# params.require(:person).permit(contact: [ :email, :phone ])
-
# # => {"contact"=>{"email"=>"none@test.com", "phone"=>"555-1234"}}
-
1
def permit(*filters)
-
2
params = self.class.new
-
-
2
filters.flatten.each do |filter|
-
8
case filter
-
when Symbol, String
-
8
permitted_scalar_filter(params, filter)
-
when Hash then
-
hash_filter(params, filter)
-
end
-
end
-
-
2
unpermitted_parameters!(params) if self.class.action_on_unpermitted_parameters
-
-
2
params.permit!
-
end
-
-
# Returns a parameter for the given +key+. If not found,
-
# returns +nil+.
-
#
-
# params = ActionController::Parameters.new(person: { name: 'Francesco' })
-
# params[:person] # => {"name"=>"Francesco"}
-
# params[:none] # => nil
-
1
def [](key)
-
36
convert_hashes_to_parameters(key, super)
-
end
-
-
# Returns a parameter for the given +key+. If the +key+
-
# can't be found, there are several options: With no other arguments,
-
# it will raise an <tt>ActionController::ParameterMissing</tt> error;
-
# if more arguments are given, then that will be returned; if a block
-
# is given, then that will be run and its result returned.
-
#
-
# params = ActionController::Parameters.new(person: { name: 'Francesco' })
-
# params.fetch(:person) # => {"name"=>"Francesco"}
-
# params.fetch(:none) # => ActionController::ParameterMissing: param not found: none
-
# params.fetch(:none, 'Francesco') # => "Francesco"
-
# params.fetch(:none) { 'Francesco' } # => "Francesco"
-
1
def fetch(key, *args)
-
convert_hashes_to_parameters(key, super, false)
-
rescue KeyError
-
raise ActionController::ParameterMissing.new(key)
-
end
-
-
# Returns a new <tt>ActionController::Parameters</tt> instance that
-
# includes only the given +keys+. If the given +keys+
-
# don't exist, returns an empty hash.
-
#
-
# params = ActionController::Parameters.new(a: 1, b: 2, c: 3)
-
# params.slice(:a, :b) # => {"a"=>1, "b"=>2}
-
# params.slice(:d) # => {}
-
1
def slice(*keys)
-
self.class.new(super).tap do |new_instance|
-
new_instance.permitted = @permitted
-
end
-
end
-
-
# Returns an exact copy of the <tt>ActionController::Parameters</tt>
-
# instance. +permitted+ state is kept on the duped object.
-
#
-
# params = ActionController::Parameters.new(a: 1)
-
# params.permit!
-
# params.permitted? # => true
-
# copy_params = params.dup # => {"a"=>1}
-
# copy_params.permitted? # => true
-
1
def dup
-
2
super.tap do |duplicate|
-
2
duplicate.permitted = @permitted
-
end
-
end
-
-
1
protected
-
1
def permitted=(new_permitted)
-
2
@permitted = new_permitted
-
end
-
-
1
private
-
1
def convert_hashes_to_parameters(key, value, assign_if_converted=true)
-
42
converted = convert_value_to_parameters(value)
-
42
self[key] = converted if assign_if_converted && !converted.equal?(value)
-
42
converted
-
end
-
-
1
def convert_value_to_parameters(value)
-
42
if value.is_a?(Array) && !converted_arrays.member?(value)
-
converted = value.map { |_| convert_value_to_parameters(_) }
-
converted_arrays << converted
-
converted
-
42
elsif value.is_a?(Parameters) || !value.is_a?(Hash)
-
40
value
-
else
-
2
self.class.new(value)
-
end
-
end
-
-
1
def each_element(object)
-
if object.is_a?(Array)
-
object.map { |el| yield el }.compact
-
elsif fields_for_style?(object)
-
hash = object.class.new
-
object.each { |k,v| hash[k] = yield v }
-
hash
-
else
-
yield object
-
end
-
end
-
-
1
def fields_for_style?(object)
-
object.is_a?(Hash) && object.all? { |k, v| k =~ /\A-?\d+\z/ && v.is_a?(Hash) }
-
end
-
-
1
def unpermitted_parameters!(params)
-
2
unpermitted_keys = unpermitted_keys(params)
-
2
if unpermitted_keys.any?
-
case self.class.action_on_unpermitted_parameters
-
when :log
-
name = "unpermitted_parameters.action_controller"
-
ActiveSupport::Notifications.instrument(name, keys: unpermitted_keys)
-
when :raise
-
raise ActionController::UnpermittedParameters.new(unpermitted_keys)
-
end
-
end
-
end
-
-
1
def unpermitted_keys(params)
-
2
self.keys - params.keys - NEVER_UNPERMITTED_PARAMS
-
end
-
-
#
-
# --- Filtering ----------------------------------------------------------
-
#
-
-
# This is a white list of permitted scalar types that includes the ones
-
# supported in XML and JSON requests.
-
#
-
# This list is in particular used to filter ordinary requests, String goes
-
# as first element to quickly short-circuit the common case.
-
#
-
# If you modify this collection please update the API of +permit+ above.
-
1
PERMITTED_SCALAR_TYPES = [
-
String,
-
Symbol,
-
NilClass,
-
Numeric,
-
TrueClass,
-
FalseClass,
-
Date,
-
Time,
-
# DateTimes are Dates, we document the type but avoid the redundant check.
-
StringIO,
-
IO,
-
ActionDispatch::Http::UploadedFile,
-
Rack::Test::UploadedFile,
-
]
-
-
1
def permitted_scalar?(value)
-
12
PERMITTED_SCALAR_TYPES.any? {|type| value.is_a?(type)}
-
end
-
-
1
def permitted_scalar_filter(params, key)
-
8
if has_key?(key) && permitted_scalar?(self[key])
-
6
params[key] = self[key]
-
end
-
-
8
keys.grep(/\A#{Regexp.escape(key)}\(\d+[if]?\)\z/) do |k|
-
if permitted_scalar?(self[k])
-
params[k] = self[k]
-
end
-
end
-
end
-
-
1
def array_of_permitted_scalars?(value)
-
if value.is_a?(Array)
-
value.all? {|element| permitted_scalar?(element)}
-
end
-
end
-
-
1
def array_of_permitted_scalars_filter(params, key)
-
if has_key?(key) && array_of_permitted_scalars?(self[key])
-
params[key] = self[key]
-
end
-
end
-
-
1
EMPTY_ARRAY = []
-
1
def hash_filter(params, filter)
-
filter = filter.with_indifferent_access
-
-
# Slicing filters out non-declared keys.
-
slice(*filter.keys).each do |key, value|
-
next unless value
-
-
if filter[key] == EMPTY_ARRAY
-
# Declaration { comment_ids: [] }.
-
array_of_permitted_scalars_filter(params, key)
-
else
-
# Declaration { user: :name } or { user: [:name, :age, { address: ... }] }.
-
params[key] = each_element(value) do |element|
-
if element.is_a?(Hash)
-
element = self.class.new(element) unless element.respond_to?(:permit)
-
element.permit(*Array.wrap(filter[key]))
-
end
-
end
-
end
-
end
-
end
-
end
-
-
# == Strong \Parameters
-
#
-
# It provides an interface for protecting attributes from end-user
-
# assignment. This makes Action Controller parameters forbidden
-
# to be used in Active Model mass assignment until they have been
-
# whitelisted.
-
#
-
# In addition, parameters can be marked as required and flow through a
-
# predefined raise/rescue flow to end up as a 400 Bad Request with no
-
# effort.
-
#
-
# class PeopleController < ActionController::Base
-
# # Using "Person.create(params[:person])" would raise an
-
# # ActiveModel::ForbiddenAttributes exception because it'd
-
# # be using mass assignment without an explicit permit step.
-
# # This is the recommended form:
-
# def create
-
# Person.create(person_params)
-
# end
-
#
-
# # This will pass with flying colors as long as there's a person key in the
-
# # parameters, otherwise it'll raise an ActionController::MissingParameter
-
# # exception, which will get caught by ActionController::Base and turned
-
# # into a 400 Bad Request reply.
-
# def update
-
# redirect_to current_account.people.find(params[:id]).tap { |person|
-
# person.update!(person_params)
-
# }
-
# end
-
#
-
# private
-
# # Using a private method to encapsulate the permissible parameters is
-
# # just a good pattern since you'll be able to reuse the same permit
-
# # list between create and update. Also, you can specialize this method
-
# # with per-user checking of permissible attributes.
-
# def person_params
-
# params.require(:person).permit(:name, :age)
-
# end
-
# end
-
#
-
# In order to use <tt>accepts_nested_attributes_for</tt> with Strong \Parameters, you
-
# will need to specify which nested attributes should be whitelisted.
-
#
-
# class Person
-
# has_many :pets
-
# accepts_nested_attributes_for :pets
-
# end
-
#
-
# class PeopleController < ActionController::Base
-
# def create
-
# Person.create(person_params)
-
# end
-
#
-
# ...
-
#
-
# private
-
#
-
# def person_params
-
# # It's mandatory to specify the nested attributes that should be whitelisted.
-
# # If you use `permit` with just the key that points to the nested attributes hash,
-
# # it will return an empty hash.
-
# params.require(:person).permit(:name, :age, pets_attributes: [ :name, :category ])
-
# end
-
# end
-
#
-
# See ActionController::Parameters.require and ActionController::Parameters.permit
-
# for more information.
-
1
module StrongParameters
-
1
extend ActiveSupport::Concern
-
1
include ActiveSupport::Rescuable
-
-
# Returns a new ActionController::Parameters object that
-
# has been instantiated with the <tt>request.parameters</tt>.
-
1
def params
-
6
@_params ||= Parameters.new(request.parameters)
-
end
-
-
# Assigns the given +value+ to the +params+ hash. If +value+
-
# is a Hash, this will create an ActionController::Parameters
-
# object that has been instantiated with the given +value+ hash.
-
1
def params=(value)
-
12
@_params = value.is_a?(Hash) ? Parameters.new(value) : value
-
end
-
end
-
end
-
1
module ActionController
-
1
module Testing
-
1
extend ActiveSupport::Concern
-
-
1
include RackDelegation
-
-
# TODO : Rewrite tests using controller.headers= to use Rack env
-
1
def headers=(new_headers)
-
@_response ||= ActionDispatch::Response.new
-
@_response.headers.replace(new_headers)
-
end
-
-
# Behavior specific to functional tests
-
1
module Functional # :nodoc:
-
1
def set_response!(request)
-
end
-
-
1
def recycle!
-
8
@_url_options = nil
-
8
self.formats = nil
-
8
self.params = nil
-
end
-
end
-
-
1
module ClassMethods
-
1
def before_filters
-
_process_action_callbacks.find_all{|x| x.kind == :before}.map{|x| x.name}
-
end
-
end
-
end
-
end
-
1
module ActionController
-
# Includes +url_for+ into the host class. The class has to provide a +RouteSet+ by implementing
-
# the <tt>_routes</tt> method. Otherwise, an exception will be raised.
-
#
-
# In addition to <tt>AbstractController::UrlFor</tt>, this module accesses the HTTP layer to define
-
# url options like the +host+. In order to do so, this module requires the host class
-
# to implement +env+ and +request+, which need to be a Rack-compatible.
-
#
-
# class RootUrl
-
# include ActionController::UrlFor
-
# include Rails.application.routes.url_helpers
-
#
-
# delegate :env, :request, to: :controller
-
#
-
# def initialize(controller)
-
# @controller = controller
-
# @url = root_path # named route from the application.
-
# end
-
# end
-
1
module UrlFor
-
1
extend ActiveSupport::Concern
-
-
1
include AbstractController::UrlFor
-
-
1
def url_options
-
@_url_options ||= super.reverse_merge(
-
:host => request.host,
-
:port => request.optional_port,
-
:protocol => request.protocol,
-
:_recall => request.symbolized_path_parameters
-
7
).freeze
-
-
7
if (same_origin = _routes.equal?(env["action_dispatch.routes"])) ||
-
14
(script_name = env["ROUTES_#{_routes.object_id}_SCRIPT_NAME"]) ||
-
7
(original_script_name = env['ORIGINAL_SCRIPT_NAME'])
-
-
@_url_options.dup.tap do |options|
-
if original_script_name
-
options[:original_script_name] = original_script_name
-
else
-
options[:script_name] = same_origin ? request.script_name.dup : script_name
-
end
-
options.freeze
-
end
-
else
-
7
@_url_options
-
end
-
end
-
end
-
end
-
1
module ActionController
-
1
module ModelNaming
-
# Converts the given object to an ActiveModel compliant one.
-
1
def convert_to_model(object)
-
object.respond_to?(:to_model) ? object.to_model : object
-
end
-
-
1
def model_name_from_record_or_class(record_or_class)
-
(record_or_class.is_a?(Class) ? record_or_class : convert_to_model(record_or_class).class).model_name
-
end
-
end
-
end
-
1
require "rails"
-
1
require "action_controller"
-
1
require "action_dispatch/railtie"
-
1
require "abstract_controller/railties/routes_helpers"
-
1
require "action_controller/railties/helpers"
-
1
require "action_view/railtie"
-
-
1
module ActionController
-
1
class Railtie < Rails::Railtie #:nodoc:
-
1
config.action_controller = ActiveSupport::OrderedOptions.new
-
-
1
config.eager_load_namespaces << ActionController
-
-
1
initializer "action_controller.assets_config", :group => :all do |app|
-
1
app.config.action_controller.assets_dir ||= app.config.paths["public"].first
-
end
-
-
1
initializer "action_controller.set_helpers_path" do |app|
-
1
ActionController::Helpers.helpers_path = app.helpers_paths
-
end
-
-
1
initializer "action_controller.parameters_config" do |app|
-
1
options = app.config.action_controller
-
-
2
ActionController::Parameters.permit_all_parameters = options.delete(:permit_all_parameters) { false }
-
1
ActionController::Parameters.action_on_unpermitted_parameters = options.delete(:action_on_unpermitted_parameters) do
-
1
(Rails.env.test? || Rails.env.development?) ? :log : false
-
end
-
end
-
-
1
initializer "action_controller.set_configs" do |app|
-
1
paths = app.config.paths
-
1
options = app.config.action_controller
-
-
1
options.logger ||= Rails.logger
-
1
options.cache_store ||= Rails.cache
-
-
1
options.javascripts_dir ||= paths["public/javascripts"].first
-
1
options.stylesheets_dir ||= paths["public/stylesheets"].first
-
-
# Ensure readers methods get compiled
-
1
options.asset_host ||= app.config.asset_host
-
1
options.relative_url_root ||= app.config.relative_url_root
-
-
1
ActiveSupport.on_load(:action_controller) do
-
1
include app.routes.mounted_helpers
-
1
extend ::AbstractController::Railties::RoutesHelpers.with(app.routes)
-
1
extend ::ActionController::Railties::Helpers
-
-
1
options.each do |k,v|
-
9
k = "#{k}="
-
9
if respond_to?(k)
-
9
send(k, v)
-
elsif !Base.respond_to?(k)
-
raise "Invalid option key: #{k}"
-
end
-
end
-
end
-
end
-
-
1
initializer "action_controller.compile_config_methods" do
-
1
ActiveSupport.on_load(:action_controller) do
-
1
config.compile_methods! if config.respond_to?(:compile_methods!)
-
end
-
end
-
end
-
end
-
1
module ActionController
-
1
module Railties
-
1
module Helpers
-
1
def inherited(klass)
-
3
super
-
3
return unless klass.respond_to?(:helpers_path=)
-
-
8
if namespace = klass.parents.detect { |m| m.respond_to?(:railtie_helpers_paths) }
-
paths = namespace.railtie_helpers_paths
-
else
-
3
paths = ActionController::Helpers.helpers_path
-
end
-
-
3
klass.helpers_path = paths
-
-
3
if klass.superclass == ActionController::Base && ActionController::Base.include_all_helpers
-
2
klass.helper :all
-
end
-
end
-
end
-
end
-
end
-
1
require 'rack/session/abstract/id'
-
1
require 'active_support/core_ext/object/to_query'
-
1
require 'active_support/core_ext/module/anonymous'
-
1
require 'active_support/core_ext/hash/keys'
-
-
1
module ActionController
-
1
module TemplateAssertions
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
4
setup :setup_subscriptions
-
4
teardown :teardown_subscriptions
-
end
-
-
1
def setup_subscriptions
-
4
@_partials = Hash.new(0)
-
4
@_templates = Hash.new(0)
-
4
@_layouts = Hash.new(0)
-
4
@_files = Hash.new(0)
-
-
4
ActiveSupport::Notifications.subscribe("render_template.action_view") do |_name, _start, _finish, _id, payload|
-
2
path = payload[:layout]
-
2
if path
-
2
@_layouts[path] += 1
-
2
if path =~ /^layouts\/(.*)/
-
2
@_layouts[$1] += 1
-
end
-
end
-
end
-
-
4
ActiveSupport::Notifications.subscribe("!render_template.action_view") do |_name, _start, _finish, _id, payload|
-
4
path = payload[:virtual_path]
-
4
next unless path
-
4
partial = path =~ /^.*\/_[^\/]*$/
-
-
4
if partial
-
@_partials[path] += 1
-
@_partials[path.split("/").last] += 1
-
end
-
-
4
@_templates[path] += 1
-
end
-
-
4
ActiveSupport::Notifications.subscribe("!render_template.action_view") do |_name, _start, _finish, _id, payload|
-
4
next if payload[:virtual_path] # files don't have virtual path
-
-
path = payload[:identifier]
-
if path
-
@_files[path] += 1
-
@_files[path.split("/").last] += 1
-
end
-
end
-
end
-
-
1
def teardown_subscriptions
-
4
ActiveSupport::Notifications.unsubscribe("render_template.action_view")
-
4
ActiveSupport::Notifications.unsubscribe("!render_template.action_view")
-
end
-
-
1
def process(*args)
-
4
@_partials = Hash.new(0)
-
4
@_templates = Hash.new(0)
-
4
@_layouts = Hash.new(0)
-
4
super
-
end
-
-
# Asserts that the request was rendered with the appropriate template file or partials.
-
#
-
# # assert that the "new" view template was rendered
-
# assert_template "new"
-
#
-
# # assert that the exact template "admin/posts/new" was rendered
-
# assert_template %r{\Aadmin/posts/new\Z}
-
#
-
# # assert that the layout 'admin' was rendered
-
# assert_template layout: 'admin'
-
# assert_template layout: 'layouts/admin'
-
# assert_template layout: :admin
-
#
-
# # assert that no layout was rendered
-
# assert_template layout: nil
-
# assert_template layout: false
-
#
-
# # assert that the "_customer" partial was rendered twice
-
# assert_template partial: '_customer', count: 2
-
#
-
# # assert that no partials were rendered
-
# assert_template partial: false
-
#
-
# In a view test case, you can also assert that specific locals are passed
-
# to partials:
-
#
-
# # assert that the "_customer" partial was rendered with a specific object
-
# assert_template partial: '_customer', locals: { customer: @customer }
-
1
def assert_template(options = {}, message = nil)
-
# Force body to be read in case the template is being streamed.
-
1
response.body
-
-
1
case options
-
when NilClass, Regexp, String, Symbol
-
1
options = options.to_s if Symbol === options
-
1
rendered = @_templates
-
1
msg = message || sprintf("expecting <%s> but rendering with <%s>",
-
options.inspect, rendered.keys)
-
1
matches_template =
-
case options
-
when String
-
!options.empty? && rendered.any? do |t, num|
-
1
options_splited = options.split(File::SEPARATOR)
-
1
t_splited = t.split(File::SEPARATOR)
-
1
t_splited.last(options_splited.size) == options_splited
-
1
end
-
when Regexp
-
rendered.any? { |t,num| t.match(options) }
-
when NilClass
-
rendered.blank?
-
end
-
1
assert matches_template, msg
-
when Hash
-
options.assert_valid_keys(:layout, :partial, :locals, :count, :file)
-
-
if options.key?(:layout)
-
expected_layout = options[:layout]
-
msg = message || sprintf("expecting layout <%s> but action rendered <%s>",
-
expected_layout, @_layouts.keys)
-
-
case expected_layout
-
when String, Symbol
-
assert_includes @_layouts.keys, expected_layout.to_s, msg
-
when Regexp
-
assert(@_layouts.keys.any? {|l| l =~ expected_layout }, msg)
-
when nil, false
-
assert(@_layouts.empty?, msg)
-
end
-
end
-
-
if options[:file]
-
assert_includes @_files.keys, options[:file]
-
end
-
-
if expected_partial = options[:partial]
-
if expected_locals = options[:locals]
-
if defined?(@_rendered_views)
-
view = expected_partial.to_s.sub(/^_/, '').sub(/\/_(?=[^\/]+\z)/, '/')
-
-
partial_was_not_rendered_msg = "expected %s to be rendered but it was not." % view
-
assert_includes @_rendered_views.rendered_views, view, partial_was_not_rendered_msg
-
-
msg = 'expecting %s to be rendered with %s but was with %s' % [expected_partial,
-
expected_locals,
-
@_rendered_views.locals_for(view)]
-
assert(@_rendered_views.view_rendered?(view, options[:locals]), msg)
-
else
-
warn "the :locals option to #assert_template is only supported in a ActionView::TestCase"
-
end
-
elsif expected_count = options[:count]
-
actual_count = @_partials[expected_partial]
-
msg = message || sprintf("expecting %s to be rendered %s time(s) but rendered %s time(s)",
-
expected_partial, expected_count, actual_count)
-
assert(actual_count == expected_count.to_i, msg)
-
else
-
msg = message || sprintf("expecting partial <%s> but action rendered <%s>",
-
options[:partial], @_partials.keys)
-
assert_includes @_partials, expected_partial, msg
-
end
-
elsif options.key?(:partial)
-
assert @_partials.empty?,
-
"Expected no partials to be rendered"
-
end
-
else
-
raise ArgumentError, "assert_template only accepts a String, Symbol, Hash, Regexp, or nil"
-
end
-
end
-
end
-
-
1
class TestRequest < ActionDispatch::TestRequest #:nodoc:
-
1
DEFAULT_ENV = ActionDispatch::TestRequest::DEFAULT_ENV.dup
-
1
DEFAULT_ENV.delete 'PATH_INFO'
-
-
1
def initialize(env = {})
-
4
super
-
-
4
self.session = TestSession.new
-
4
self.session_options = TestSession::DEFAULT_OPTIONS.merge(:id => SecureRandom.hex(16))
-
end
-
-
1
def assign_parameters(routes, controller_path, action, parameters = {})
-
4
parameters = parameters.symbolize_keys.merge(:controller => controller_path, :action => action)
-
4
extra_keys = routes.extra_keys(parameters)
-
4
non_path_parameters = get? ? query_parameters : request_parameters
-
4
parameters.each do |key, value|
-
10
if value.is_a?(Array) && (value.frozen? || value.any?(&:frozen?))
-
value = value.map{ |v| v.duplicable? ? v.dup : v }
-
6
elsif value.is_a?(Hash) && (value.frozen? || value.any?{ |k,v| v.frozen? })
-
value = Hash[value.map{ |k,v| [k, v.duplicable? ? v.dup : v] }]
-
elsif value.frozen? && value.duplicable?
-
value = value.dup
-
end
-
-
10
if extra_keys.include?(key.to_sym)
-
2
non_path_parameters[key] = value
-
else
-
8
if value.is_a?(Array)
-
value = value.map(&:to_param)
-
else
-
8
value = value.to_param
-
end
-
-
8
path_parameters[key.to_s] = value
-
end
-
end
-
-
# Clear the combined params hash in case it was already referenced.
-
4
@env.delete("action_dispatch.request.parameters")
-
-
# Clear the filter cache variables so they're not stale
-
4
@filtered_parameters = @filtered_env = @filtered_path = nil
-
-
4
params = self.request_parameters.dup
-
4
%w(controller action only_path).each do |k|
-
12
params.delete(k)
-
12
params.delete(k.to_sym)
-
end
-
4
data = params.to_query
-
-
4
@env['CONTENT_LENGTH'] = data.length.to_s
-
4
@env['rack.input'] = StringIO.new(data)
-
end
-
-
1
def recycle!
-
4
@formats = nil
-
140
@env.delete_if { |k, v| k =~ /^(action_dispatch|rack)\.request/ }
-
140
@env.delete_if { |k, v| k =~ /^action_dispatch\.rescue/ }
-
4
@symbolized_path_params = nil
-
4
@method = @request_method = nil
-
4
@fullpath = @ip = @remote_ip = @protocol = nil
-
4
@env['action_dispatch.request.query_parameters'] = {}
-
4
@set_cookies ||= {}
-
4
@set_cookies.update(Hash[cookie_jar.instance_variable_get("@set_cookies").map{ |k,o| [k,o[:value]] }])
-
4
deleted_cookies = cookie_jar.instance_variable_get("@delete_cookies")
-
4
@set_cookies.reject!{ |k,v| deleted_cookies.include?(k) }
-
4
cookie_jar.update(rack_cookies)
-
4
cookie_jar.update(cookies)
-
4
cookie_jar.update(@set_cookies)
-
4
cookie_jar.recycle!
-
end
-
-
1
private
-
-
1
def default_env
-
4
DEFAULT_ENV
-
end
-
end
-
-
1
class TestResponse < ActionDispatch::TestResponse
-
1
def recycle!
-
4
initialize
-
end
-
end
-
-
1
class LiveTestResponse < Live::Response
-
1
def recycle!
-
@body = nil
-
initialize
-
end
-
-
1
def body
-
@body ||= super
-
end
-
-
# Was the response successful?
-
1
alias_method :success?, :successful?
-
-
# Was the URL not found?
-
1
alias_method :missing?, :not_found?
-
-
# Were we redirected?
-
1
alias_method :redirect?, :redirection?
-
-
# Was there a server-side error?
-
1
alias_method :error?, :server_error?
-
end
-
-
# Methods #destroy and #load! are overridden to avoid calling methods on the
-
# @store object, which does not exist for the TestSession class.
-
1
class TestSession < Rack::Session::Abstract::SessionHash #:nodoc:
-
1
DEFAULT_OPTIONS = Rack::Session::Abstract::ID::DEFAULT_OPTIONS
-
-
1
def initialize(session = {})
-
4
super(nil, nil)
-
4
@id = SecureRandom.hex(16)
-
4
@data = stringify_keys(session)
-
4
@loaded = true
-
end
-
-
1
def exists?
-
true
-
end
-
-
1
def keys
-
@data.keys
-
end
-
-
1
def values
-
@data.values
-
end
-
-
1
def destroy
-
clear
-
end
-
-
1
private
-
-
1
def load!
-
@id
-
end
-
end
-
-
# Superclass for ActionController functional tests. Functional tests allow you to
-
# test a single controller action per test method. This should not be confused with
-
# integration tests (see ActionDispatch::IntegrationTest), which are more like
-
# "stories" that can involve multiple controllers and multiple actions (i.e. multiple
-
# different HTTP requests).
-
#
-
# == Basic example
-
#
-
# Functional tests are written as follows:
-
# 1. First, one uses the +get+, +post+, +patch+, +put+, +delete+ or +head+ method to simulate
-
# an HTTP request.
-
# 2. Then, one asserts whether the current state is as expected. "State" can be anything:
-
# the controller's HTTP response, the database contents, etc.
-
#
-
# For example:
-
#
-
# class BooksControllerTest < ActionController::TestCase
-
# def test_create
-
# # Simulate a POST response with the given HTTP parameters.
-
# post(:create, book: { title: "Love Hina" })
-
#
-
# # Assert that the controller tried to redirect us to
-
# # the created book's URI.
-
# assert_response :found
-
#
-
# # Assert that the controller really put the book in the database.
-
# assert_not_nil Book.find_by(title: "Love Hina")
-
# end
-
# end
-
#
-
# You can also send a real document in the simulated HTTP request.
-
#
-
# def test_create
-
# json = {book: { title: "Love Hina" }}.to_json
-
# post :create, json
-
# end
-
#
-
# == Special instance variables
-
#
-
# ActionController::TestCase will also automatically provide the following instance
-
# variables for use in the tests:
-
#
-
# <b>@controller</b>::
-
# The controller instance that will be tested.
-
# <b>@request</b>::
-
# An ActionController::TestRequest, representing the current HTTP
-
# request. You can modify this object before sending the HTTP request. For example,
-
# you might want to set some session properties before sending a GET request.
-
# <b>@response</b>::
-
# An ActionController::TestResponse object, representing the response
-
# of the last HTTP response. In the above example, <tt>@response</tt> becomes valid
-
# after calling +post+. If the various assert methods are not sufficient, then you
-
# may use this object to inspect the HTTP response in detail.
-
#
-
# (Earlier versions of \Rails required each functional test to subclass
-
# Test::Unit::TestCase and define @controller, @request, @response in +setup+.)
-
#
-
# == Controller is automatically inferred
-
#
-
# ActionController::TestCase will automatically infer the controller under test
-
# from the test class name. If the controller cannot be inferred from the test
-
# class name, you can explicitly set it with +tests+.
-
#
-
# class SpecialEdgeCaseWidgetsControllerTest < ActionController::TestCase
-
# tests WidgetController
-
# end
-
#
-
# == \Testing controller internals
-
#
-
# In addition to these specific assertions, you also have easy access to various collections that the regular test/unit assertions
-
# can be used against. These collections are:
-
#
-
# * assigns: Instance variables assigned in the action that are available for the view.
-
# * session: Objects being saved in the session.
-
# * flash: The flash objects currently in the session.
-
# * cookies: \Cookies being sent to the user on this request.
-
#
-
# These collections can be used just like any other hash:
-
#
-
# assert_not_nil assigns(:person) # makes sure that a @person instance variable was set
-
# assert_equal "Dave", cookies[:name] # makes sure that a cookie called :name was set as "Dave"
-
# assert flash.empty? # makes sure that there's nothing in the flash
-
#
-
# For historic reasons, the assigns hash uses string-based keys. So <tt>assigns[:person]</tt> won't work, but <tt>assigns["person"]</tt> will. To
-
# appease our yearning for symbols, though, an alternative accessor has been devised using a method call instead of index referencing.
-
# So <tt>assigns(:person)</tt> will work just like <tt>assigns["person"]</tt>, but again, <tt>assigns[:person]</tt> will not work.
-
#
-
# On top of the collections, you have the complete url that a given action redirected to available in <tt>redirect_to_url</tt>.
-
#
-
# For redirects within the same controller, you can even call follow_redirect and the redirect will be followed, triggering another
-
# action call which can then be asserted against.
-
#
-
# == Manipulating session and cookie variables
-
#
-
# Sometimes you need to set up the session and cookie variables for a test.
-
# To do this just assign a value to the session or cookie collection:
-
#
-
# session[:key] = "value"
-
# cookies[:key] = "value"
-
#
-
# To clear the cookies for a test just clear the cookie collection:
-
#
-
# cookies.clear
-
#
-
# == \Testing named routes
-
#
-
# If you're using named routes, they can be easily tested using the original named routes' methods straight in the test case.
-
#
-
# assert_redirected_to page_url(title: 'foo')
-
1
class TestCase < ActiveSupport::TestCase
-
1
module Behavior
-
1
extend ActiveSupport::Concern
-
1
include ActionDispatch::TestProcess
-
1
include ActiveSupport::Testing::ConstantLookup
-
-
1
attr_reader :response, :request
-
-
1
module ClassMethods
-
-
# Sets the controller class name. Useful if the name can't be inferred from test class.
-
# Normalizes +controller_class+ before using.
-
#
-
# tests WidgetController
-
# tests :widget
-
# tests 'widget'
-
1
def tests(controller_class)
-
case controller_class
-
when String, Symbol
-
self.controller_class = "#{controller_class.to_s.camelize}Controller".constantize
-
when Class
-
self.controller_class = controller_class
-
else
-
raise ArgumentError, "controller class must be a String, Symbol, or Class"
-
end
-
end
-
-
1
def controller_class=(new_class)
-
prepare_controller_class(new_class) if new_class
-
self._controller_class = new_class
-
end
-
-
1
def controller_class
-
if current_controller_class = self._controller_class
-
current_controller_class
-
else
-
self.controller_class = determine_default_controller_class(name)
-
end
-
end
-
-
1
def determine_default_controller_class(name)
-
determine_constant_from_test_name(name) do |constant|
-
Class === constant && constant < ActionController::Metal
-
end
-
end
-
-
1
def prepare_controller_class(new_class)
-
new_class.send :include, ActionController::TestCase::RaiseActionExceptions
-
end
-
-
end
-
-
# Simulate a GET request with the given parameters.
-
#
-
# - +action+: The controller action to call.
-
# - +parameters+: The HTTP parameters that you want to pass. This may
-
# be +nil+, a hash, or a string that is appropriately encoded
-
# (<tt>application/x-www-form-urlencoded</tt> or <tt>multipart/form-data</tt>).
-
# - +session+: A hash of parameters to store in the session. This may be +nil+.
-
# - +flash+: A hash of parameters to store in the flash. This may be +nil+.
-
#
-
# You can also simulate POST, PATCH, PUT, DELETE, HEAD, and OPTIONS requests with
-
# +post+, +patch+, +put+, +delete+, +head+, and +options+.
-
#
-
# Note that the request method is not verified. The different methods are
-
# available to make the tests more expressive.
-
1
def get(action, *args)
-
2
process(action, "GET", *args)
-
end
-
-
# Simulate a POST request with the given parameters and set/volley the response.
-
# See +get+ for more details.
-
1
def post(action, *args)
-
2
process(action, "POST", *args)
-
end
-
-
# Simulate a PATCH request with the given parameters and set/volley the response.
-
# See +get+ for more details.
-
1
def patch(action, *args)
-
process(action, "PATCH", *args)
-
end
-
-
# Simulate a PUT request with the given parameters and set/volley the response.
-
# See +get+ for more details.
-
1
def put(action, *args)
-
process(action, "PUT", *args)
-
end
-
-
# Simulate a DELETE request with the given parameters and set/volley the response.
-
# See +get+ for more details.
-
1
def delete(action, *args)
-
process(action, "DELETE", *args)
-
end
-
-
# Simulate a HEAD request with the given parameters and set/volley the response.
-
# See +get+ for more details.
-
1
def head(action, *args)
-
process(action, "HEAD", *args)
-
end
-
-
1
def xml_http_request(request_method, action, parameters = nil, session = nil, flash = nil)
-
@request.env['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
-
@request.env['HTTP_ACCEPT'] ||= [Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ')
-
__send__(request_method, action, parameters, session, flash).tap do
-
@request.env.delete 'HTTP_X_REQUESTED_WITH'
-
@request.env.delete 'HTTP_ACCEPT'
-
end
-
end
-
1
alias xhr :xml_http_request
-
-
1
def paramify_values(hash_or_array_or_value)
-
12
case hash_or_array_or_value
-
when Hash
-
12
Hash[hash_or_array_or_value.map{|key, value| [key, paramify_values(value)] }]
-
when Array
-
hash_or_array_or_value.map {|i| paramify_values(i)}
-
when Rack::Test::UploadedFile, ActionDispatch::Http::UploadedFile
-
hash_or_array_or_value
-
else
-
8
hash_or_array_or_value.to_param
-
end
-
end
-
-
1
def process(action, http_method = 'GET', *args)
-
4
check_required_ivars
-
-
4
if args.first.is_a?(String) && http_method != 'HEAD'
-
@request.env['RAW_POST_DATA'] = args.shift
-
end
-
-
4
parameters, session, flash = args
-
-
# Ensure that numbers and symbols passed as params are converted to
-
# proper params, as is the case when engaging rack.
-
4
parameters = paramify_values(parameters) if html_format?(parameters)
-
-
4
@html_document = nil
-
-
4
unless @controller.respond_to?(:recycle!)
-
4
@controller.extend(Testing::Functional)
-
8
@controller.class.class_eval { include Testing }
-
end
-
-
4
@request.recycle!
-
4
@response.recycle!
-
4
@controller.recycle!
-
-
4
@request.env['REQUEST_METHOD'] = http_method
-
-
4
parameters ||= {}
-
4
controller_class_name = @controller.class.anonymous? ?
-
"anonymous" :
-
@controller.class.controller_path
-
-
4
@request.assign_parameters(@routes, controller_class_name, action.to_s, parameters)
-
-
4
@request.session.update(session) if session
-
4
@request.flash.update(flash || {})
-
-
4
@controller.request = @request
-
4
@controller.response = @response
-
-
4
build_request_uri(action, parameters)
-
-
4
name = @request.parameters[:action]
-
-
4
@controller.recycle!
-
4
@controller.process(name)
-
-
4
if cookies = @request.env['action_dispatch.cookies']
-
4
unless @response.committed?
-
4
cookies.write(@response)
-
end
-
end
-
4
@response.prepare!
-
-
4
@assigns = @controller.respond_to?(:view_assigns) ? @controller.view_assigns : {}
-
4
@request.session['flash'] = @request.flash.to_session_value
-
4
@request.session.delete('flash') if @request.session['flash'].blank?
-
4
@response
-
end
-
-
1
def setup_controller_request_and_response
-
4
@controller = nil unless defined? @controller
-
-
4
response_klass = TestResponse
-
-
4
if klass = self.class.controller_class
-
4
if klass < ActionController::Live
-
response_klass = LiveTestResponse
-
end
-
4
unless @controller
-
4
begin
-
4
@controller = klass.new
-
rescue
-
warn "could not construct controller #{klass}" if $VERBOSE
-
end
-
end
-
end
-
-
4
@request = build_request
-
4
@response = build_response response_klass
-
4
@response.request = @request
-
-
4
if @controller
-
4
@controller.request = @request
-
4
@controller.params = {}
-
end
-
end
-
-
1
def build_request
-
4
TestRequest.new
-
end
-
-
1
def build_response(klass)
-
4
klass.new
-
end
-
-
1
included do
-
2
include ActionController::TemplateAssertions
-
2
include ActionDispatch::Assertions
-
2
class_attribute :_controller_class
-
2
setup :setup_controller_request_and_response
-
end
-
-
1
private
-
1
def check_required_ivars
-
# Sanity check for required instance variables so we can give an
-
# understandable error message.
-
4
[:@routes, :@controller, :@request, :@response].each do |iv_name|
-
16
if !instance_variable_defined?(iv_name) || instance_variable_get(iv_name).nil?
-
raise "#{iv_name} is nil: make sure you set it in your test's setup method."
-
end
-
end
-
end
-
-
1
def build_request_uri(action, parameters)
-
4
unless @request.env["PATH_INFO"]
-
4
options = @controller.respond_to?(:url_options) ? @controller.__send__(:url_options).merge(parameters) : parameters
-
4
options.update(
-
:only_path => true,
-
:action => action,
-
:relative_url_root => nil,
-
:_recall => @request.symbolized_path_parameters)
-
-
4
url, query_string = @routes.url_for(options).split("?", 2)
-
-
4
@request.env["SCRIPT_NAME"] = @controller.config.relative_url_root
-
4
@request.env["PATH_INFO"] = url
-
4
@request.env["QUERY_STRING"] = query_string || ""
-
end
-
end
-
-
1
def html_format?(parameters)
-
4
return true unless parameters.is_a?(Hash)
-
4
Mime.fetch(parameters[:format]) { Mime['html'] }.html?
-
end
-
end
-
-
# When the request.remote_addr remains the default for testing, which is 0.0.0.0, the exception is simply raised inline
-
# (skipping the regular exception handling from rescue_action). If the request.remote_addr is anything else, the regular
-
# rescue_action process takes place. This means you can test your rescue_action code by setting remote_addr to something else
-
# than 0.0.0.0.
-
#
-
# The exception is stored in the exception accessor for further inspection.
-
1
module RaiseActionExceptions
-
1
def self.included(base) #:nodoc:
-
unless base.method_defined?(:exception) && base.method_defined?(:exception=)
-
base.class_eval do
-
attr_accessor :exception
-
protected :exception, :exception=
-
end
-
end
-
end
-
-
1
protected
-
1
def rescue_action_without_handler(e)
-
self.exception = e
-
-
if request.remote_addr == "0.0.0.0"
-
raise(e)
-
else
-
super(e)
-
end
-
end
-
end
-
-
1
include Behavior
-
end
-
end
-
#--
-
# Copyright (c) 2004-2014 David Heinemeier Hansson
-
#
-
# Permission is hereby granted, free of charge, to any person obtaining
-
# a copy of this software and associated documentation files (the
-
# "Software"), to deal in the Software without restriction, including
-
# without limitation the rights to use, copy, modify, merge, publish,
-
# distribute, sublicense, and/or sell copies of the Software, and to
-
# permit persons to whom the Software is furnished to do so, subject to
-
# the following conditions:
-
#
-
# The above copyright notice and this permission notice shall be
-
# included in all copies or substantial portions of the Software.
-
#
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
#++
-
-
1
require 'active_support'
-
1
require 'active_support/rails'
-
1
require 'active_support/core_ext/module/attribute_accessors'
-
-
1
require 'action_pack'
-
1
require 'rack'
-
-
1
module Rack
-
1
autoload :Test, 'rack/test'
-
end
-
-
1
module ActionDispatch
-
1
extend ActiveSupport::Autoload
-
-
1
class IllegalStateError < StandardError
-
end
-
-
1
eager_autoload do
-
1
autoload_under 'http' do
-
1
autoload :Request
-
1
autoload :Response
-
end
-
end
-
-
1
autoload_under 'middleware' do
-
1
autoload :RequestId
-
1
autoload :Callbacks
-
1
autoload :Cookies
-
1
autoload :DebugExceptions
-
1
autoload :ExceptionWrapper
-
1
autoload :Flash
-
1
autoload :ParamsParser
-
1
autoload :PublicExceptions
-
1
autoload :Reloader
-
1
autoload :RemoteIp
-
1
autoload :ShowExceptions
-
1
autoload :SSL
-
1
autoload :Static
-
end
-
-
1
autoload :Journey
-
1
autoload :MiddlewareStack, 'action_dispatch/middleware/stack'
-
1
autoload :Routing
-
-
1
module Http
-
1
extend ActiveSupport::Autoload
-
-
1
autoload :Cache
-
1
autoload :Headers
-
1
autoload :MimeNegotiation
-
1
autoload :Parameters
-
1
autoload :ParameterFilter
-
1
autoload :Upload
-
1
autoload :UploadedFile, 'action_dispatch/http/upload'
-
1
autoload :URL
-
end
-
-
1
module Session
-
1
autoload :AbstractStore, 'action_dispatch/middleware/session/abstract_store'
-
1
autoload :CookieStore, 'action_dispatch/middleware/session/cookie_store'
-
1
autoload :MemCacheStore, 'action_dispatch/middleware/session/mem_cache_store'
-
1
autoload :CacheStore, 'action_dispatch/middleware/session/cache_store'
-
end
-
-
1
mattr_accessor :test_app
-
-
1
autoload_under 'testing' do
-
1
autoload :Assertions
-
1
autoload :Integration
-
1
autoload :IntegrationTest, 'action_dispatch/testing/integration'
-
1
autoload :TestProcess
-
1
autoload :TestRequest
-
1
autoload :TestResponse
-
end
-
end
-
-
1
autoload :Mime, 'action_dispatch/http/mime_type'
-
-
1
ActiveSupport.on_load(:action_view) do
-
1
ActionView::Base.default_formats ||= Mime::SET.symbols
-
1
ActionView::Template::Types.delegate_to Mime
-
end
-
-
1
module ActionDispatch
-
1
module Http
-
1
module Cache
-
1
module Request
-
-
1
HTTP_IF_MODIFIED_SINCE = 'HTTP_IF_MODIFIED_SINCE'.freeze
-
1
HTTP_IF_NONE_MATCH = 'HTTP_IF_NONE_MATCH'.freeze
-
-
1
def if_modified_since
-
if since = env[HTTP_IF_MODIFIED_SINCE]
-
Time.rfc2822(since) rescue nil
-
end
-
end
-
-
1
def if_none_match
-
env[HTTP_IF_NONE_MATCH]
-
end
-
-
1
def if_none_match_etags
-
(if_none_match ? if_none_match.split(/\s*,\s*/) : []).collect do |etag|
-
etag.gsub(/^\"|\"$/, "")
-
end
-
end
-
-
1
def not_modified?(modified_at)
-
if_modified_since && modified_at && if_modified_since >= modified_at
-
end
-
-
1
def etag_matches?(etag)
-
if etag
-
etag = etag.gsub(/^\"|\"$/, "")
-
if_none_match_etags.include?(etag)
-
end
-
end
-
-
# Check response freshness (Last-Modified and ETag) against request
-
# If-Modified-Since and If-None-Match conditions. If both headers are
-
# supplied, both must match, or the request is not considered fresh.
-
1
def fresh?(response)
-
last_modified = if_modified_since
-
etag = if_none_match
-
-
return false unless last_modified || etag
-
-
success = true
-
success &&= not_modified?(response.last_modified) if last_modified
-
success &&= etag_matches?(response.etag) if etag
-
success
-
end
-
end
-
-
1
module Response
-
1
attr_reader :cache_control, :etag
-
1
alias :etag? :etag
-
-
1
def last_modified
-
if last = headers[LAST_MODIFIED]
-
Time.httpdate(last)
-
end
-
end
-
-
1
def last_modified?
-
4
headers.include?(LAST_MODIFIED)
-
end
-
-
1
def last_modified=(utc_time)
-
headers[LAST_MODIFIED] = utc_time.httpdate
-
end
-
-
1
def date
-
if date_header = headers['Date']
-
Time.httpdate(date_header)
-
end
-
end
-
-
1
def date?
-
headers.include?('Date')
-
end
-
-
1
def date=(utc_time)
-
headers['Date'] = utc_time.httpdate
-
end
-
-
1
def etag=(etag)
-
key = ActiveSupport::Cache.expand_cache_key(etag)
-
@etag = self[ETAG] = %("#{Digest::MD5.hexdigest(key)}")
-
end
-
-
1
private
-
-
1
LAST_MODIFIED = "Last-Modified".freeze
-
1
ETAG = "ETag".freeze
-
1
CACHE_CONTROL = "Cache-Control".freeze
-
1
SPECIAL_KEYS = %w[extras no-cache max-age public must-revalidate]
-
-
1
def cache_control_segments
-
8
if cache_control = self[CACHE_CONTROL]
-
cache_control.delete(' ').split(',')
-
else
-
8
[]
-
end
-
end
-
-
1
def cache_control_headers
-
8
cache_control = {}
-
-
8
cache_control_segments.each do |segment|
-
directive, argument = segment.split('=', 2)
-
-
if SPECIAL_KEYS.include? directive
-
key = directive.tr('-', '_')
-
cache_control[key.to_sym] = argument || true
-
else
-
cache_control[:extras] ||= []
-
cache_control[:extras] << segment
-
end
-
end
-
-
8
cache_control
-
end
-
-
1
def prepare_cache_control!
-
8
@cache_control = cache_control_headers
-
8
@etag = self[ETAG]
-
end
-
-
1
def handle_conditional_get!
-
4
if etag? || last_modified? || !@cache_control.empty?
-
set_conditional_cache_control!
-
end
-
end
-
-
1
DEFAULT_CACHE_CONTROL = "max-age=0, private, must-revalidate".freeze
-
1
NO_CACHE = "no-cache".freeze
-
1
PUBLIC = "public".freeze
-
1
PRIVATE = "private".freeze
-
1
MUST_REVALIDATE = "must-revalidate".freeze
-
-
1
def set_conditional_cache_control!
-
control = {}
-
cc_headers = cache_control_headers
-
if extras = cc_headers.delete(:extras)
-
@cache_control[:extras] ||= []
-
@cache_control[:extras] += extras
-
@cache_control[:extras].uniq!
-
end
-
-
control.merge! cc_headers
-
control.merge! @cache_control
-
-
if control.empty?
-
headers[CACHE_CONTROL] = DEFAULT_CACHE_CONTROL
-
elsif control[:no_cache]
-
headers[CACHE_CONTROL] = NO_CACHE
-
if control[:extras]
-
headers[CACHE_CONTROL] += ", #{control[:extras].join(', ')}"
-
end
-
else
-
extras = control[:extras]
-
max_age = control[:max_age]
-
-
options = []
-
options << "max-age=#{max_age.to_i}" if max_age
-
options << (control[:public] ? PUBLIC : PRIVATE)
-
options << MUST_REVALIDATE if control[:must_revalidate]
-
options.concat(extras) if extras
-
-
headers[CACHE_CONTROL] = options.join(", ")
-
end
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/hash/keys'
-
1
require 'active_support/core_ext/object/duplicable'
-
1
require 'action_dispatch/http/parameter_filter'
-
-
1
module ActionDispatch
-
1
module Http
-
# Allows you to specify sensitive parameters which will be replaced from
-
# the request log by looking in the query string of the request and all
-
# subhashes of the params hash to filter. If a block is given, each key and
-
# value of the params hash and all subhashes is passed to it, the value
-
# or key can be replaced using String#replace or similar method.
-
#
-
# env["action_dispatch.parameter_filter"] = [:password]
-
# => replaces the value to all keys matching /password/i with "[FILTERED]"
-
#
-
# env["action_dispatch.parameter_filter"] = [:foo, "bar"]
-
# => replaces the value to all keys matching /foo|bar/i with "[FILTERED]"
-
#
-
# env["action_dispatch.parameter_filter"] = lambda do |k,v|
-
# v.reverse! if k =~ /secret/i
-
# end
-
# => reverses the value to all keys matching /secret/i
-
1
module FilterParameters
-
1
ENV_MATCH = [/RAW_POST_DATA/, "rack.request.form_vars"] # :nodoc:
-
1
NULL_PARAM_FILTER = ParameterFilter.new # :nodoc:
-
1
NULL_ENV_FILTER = ParameterFilter.new ENV_MATCH # :nodoc:
-
-
1
def initialize(env)
-
4
super
-
4
@filtered_parameters = nil
-
4
@filtered_env = nil
-
4
@filtered_path = nil
-
end
-
-
# Return a hash of parameters with all sensitive data replaced.
-
1
def filtered_parameters
-
4
@filtered_parameters ||= parameter_filter.filter(parameters)
-
end
-
-
# Return a hash of request.env with all sensitive data replaced.
-
1
def filtered_env
-
@filtered_env ||= env_filter.filter(@env)
-
end
-
-
# Reconstructed a path with all sensitive GET parameters replaced.
-
1
def filtered_path
-
@filtered_path ||= query_string.empty? ? path : "#{path}?#{filtered_query_string}"
-
end
-
-
1
protected
-
-
1
def parameter_filter
-
4
parameter_filter_for @env.fetch("action_dispatch.parameter_filter") {
-
return NULL_PARAM_FILTER
-
}
-
end
-
-
1
def env_filter
-
user_key = @env.fetch("action_dispatch.parameter_filter") {
-
return NULL_ENV_FILTER
-
}
-
parameter_filter_for(Array(user_key) + ENV_MATCH)
-
end
-
-
1
def parameter_filter_for(filters)
-
4
ParameterFilter.new(filters)
-
end
-
-
1
KV_RE = '[^&;=]+'
-
1
PAIR_RE = %r{(#{KV_RE})=(#{KV_RE})}
-
1
def filtered_query_string
-
query_string.gsub(PAIR_RE) do |_|
-
parameter_filter.filter([[$1, $2]]).first.join("=")
-
end
-
end
-
end
-
end
-
end
-
1
module ActionDispatch
-
1
module Http
-
1
module FilterRedirect
-
-
1
FILTERED = '[FILTERED]'.freeze # :nodoc:
-
-
1
def filtered_location
-
2
filters = location_filter
-
2
if !filters.empty? && location_filter_match?(filters)
-
FILTERED
-
else
-
2
location
-
end
-
end
-
-
1
private
-
-
1
def location_filter
-
2
if request
-
2
request.env['action_dispatch.redirect_filter'] || []
-
else
-
[]
-
end
-
end
-
-
1
def location_filter_match?(filters)
-
filters.any? do |filter|
-
if String === filter
-
location.include?(filter)
-
elsif Regexp === filter
-
location.match(filter)
-
end
-
end
-
end
-
-
end
-
end
-
end
-
1
module ActionDispatch
-
1
module Http
-
1
class Headers
-
1
CGI_VARIABLES = %w(
-
CONTENT_TYPE CONTENT_LENGTH
-
HTTPS AUTH_TYPE GATEWAY_INTERFACE
-
PATH_INFO PATH_TRANSLATED QUERY_STRING
-
REMOTE_ADDR REMOTE_HOST REMOTE_IDENT REMOTE_USER
-
REQUEST_METHOD SCRIPT_NAME
-
SERVER_NAME SERVER_PORT SERVER_PROTOCOL SERVER_SOFTWARE
-
)
-
1
HTTP_HEADER = /\A[A-Za-z0-9-]+\z/
-
-
1
include Enumerable
-
1
attr_reader :env
-
-
1
def initialize(env = {})
-
@env = env
-
end
-
-
1
def [](key)
-
@env[env_name(key)]
-
end
-
-
1
def []=(key, value)
-
@env[env_name(key)] = value
-
end
-
-
1
def key?(key)
-
@env.key? env_name(key)
-
end
-
1
alias :include? :key?
-
-
1
def fetch(key, *args, &block)
-
@env.fetch env_name(key), *args, &block
-
end
-
-
1
def each(&block)
-
@env.each(&block)
-
end
-
-
1
def merge(headers_or_env)
-
headers = Http::Headers.new(env.dup)
-
headers.merge!(headers_or_env)
-
headers
-
end
-
-
1
def merge!(headers_or_env)
-
headers_or_env.each do |key, value|
-
self[env_name(key)] = value
-
end
-
end
-
-
1
private
-
1
def env_name(key)
-
key = key.to_s
-
if key =~ HTTP_HEADER
-
key = key.upcase.tr('-', '_')
-
key = "HTTP_" + key unless CGI_VARIABLES.include?(key)
-
end
-
key
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/module/attribute_accessors'
-
-
1
module ActionDispatch
-
1
module Http
-
1
module MimeNegotiation
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
mattr_accessor :ignore_accept_header
-
1
self.ignore_accept_header = false
-
end
-
-
1
attr_reader :variant
-
-
# The MIME type of the HTTP request, such as Mime::XML.
-
#
-
# For backward compatibility, the post \format is extracted from the
-
# X-Post-Data-Format HTTP header if present.
-
1
def content_mime_type
-
12
@env["action_dispatch.request.content_type"] ||= begin
-
12
if @env['CONTENT_TYPE'] =~ /^([^,\;]*)/
-
Mime::Type.lookup($1.strip.downcase)
-
else
-
12
nil
-
end
-
end
-
end
-
-
1
def content_type
-
content_mime_type && content_mime_type.to_s
-
end
-
-
# Returns the accepted MIME type for the request.
-
1
def accepts
-
@env["action_dispatch.request.accepts"] ||= begin
-
header = @env['HTTP_ACCEPT'].to_s.strip
-
-
if header.empty?
-
[content_mime_type]
-
else
-
Mime::Type.parse(header)
-
end
-
end
-
end
-
-
# Returns the MIME type for the \format used in the request.
-
#
-
# GET /posts/5.xml | request.format => Mime::XML
-
# GET /posts/5.xhtml | request.format => Mime::HTML
-
# GET /posts/5 | request.format => Mime::HTML or MIME::JS, or request.accepts.first
-
#
-
1
def format(view_path = [])
-
4
formats.first || Mime::NullType.instance
-
end
-
-
1
def formats
-
8
@env["action_dispatch.request.formats"] ||=
-
if parameters[:format]
-
Array(Mime[parameters[:format]])
-
elsif use_accept_header && valid_accept_header
-
accepts
-
elsif xhr?
-
[Mime::JS]
-
else
-
4
[Mime::HTML]
-
end
-
end
-
-
# Sets the \variant for template.
-
1
def variant=(variant)
-
if variant.is_a?(Symbol)
-
@variant = [variant]
-
elsif variant.is_a?(Array) && variant.any? && variant.all?{ |v| v.is_a?(Symbol) }
-
@variant = variant
-
else
-
raise ArgumentError, "request.variant must be set to a Symbol or an Array of Symbols, not a #{variant.class}. " \
-
"For security reasons, never directly set the variant to a user-provided value, " \
-
"like params[:variant].to_sym. Check user-provided value against a whitelist first, " \
-
"then set the variant: request.variant = :tablet if params[:variant] == 'tablet'"
-
end
-
end
-
-
# Sets the \format by string extension, which can be used to force custom formats
-
# that are not controlled by the extension.
-
#
-
# class ApplicationController < ActionController::Base
-
# before_action :adjust_format_for_iphone
-
#
-
# private
-
# def adjust_format_for_iphone
-
# request.format = :iphone if request.env["HTTP_USER_AGENT"][/iPhone/]
-
# end
-
# end
-
1
def format=(extension)
-
parameters[:format] = extension.to_s
-
@env["action_dispatch.request.formats"] = [Mime::Type.lookup_by_extension(parameters[:format])]
-
end
-
-
# Sets the \formats by string extensions. This differs from #format= by allowing you
-
# to set multiple, ordered formats, which is useful when you want to have a fallback.
-
#
-
# In this example, the :iphone format will be used if it's available, otherwise it'll fallback
-
# to the :html format.
-
#
-
# class ApplicationController < ActionController::Base
-
# before_action :adjust_format_for_iphone_with_html_fallback
-
#
-
# private
-
# def adjust_format_for_iphone_with_html_fallback
-
# request.formats = [ :iphone, :html ] if request.env["HTTP_USER_AGENT"][/iPhone/]
-
# end
-
# end
-
1
def formats=(extensions)
-
parameters[:format] = extensions.first.to_s
-
@env["action_dispatch.request.formats"] = extensions.collect do |extension|
-
Mime::Type.lookup_by_extension(extension)
-
end
-
end
-
-
# Receives an array of mimes and return the first user sent mime that
-
# matches the order array.
-
#
-
1
def negotiate_mime(order)
-
formats.each do |priority|
-
if priority == Mime::ALL
-
return order.first
-
elsif order.include?(priority)
-
return priority
-
end
-
end
-
-
order.include?(Mime::ALL) ? format : nil
-
end
-
-
1
protected
-
-
1
BROWSER_LIKE_ACCEPTS = /,\s*\*\/\*|\*\/\*\s*,/
-
-
1
def valid_accept_header
-
4
(xhr? && (accept.present? || content_mime_type)) ||
-
8
(accept.present? && accept !~ BROWSER_LIKE_ACCEPTS)
-
end
-
-
1
def use_accept_header
-
4
!self.class.ignore_accept_header
-
end
-
end
-
end
-
end
-
1
require 'set'
-
1
require 'singleton'
-
1
require 'active_support/core_ext/module/attribute_accessors'
-
1
require 'active_support/core_ext/string/starts_ends_with'
-
-
1
module Mime
-
1
class Mimes < Array
-
1
def symbols
-
25
@symbols ||= map { |m| m.to_sym }
-
end
-
-
%w(<< concat shift unshift push pop []= clear compact! collect!
-
delete delete_at delete_if flatten! map! insert reject! reverse!
-
1
replace slice! sort! uniq!).each do |method|
-
22
module_eval <<-CODE, __FILE__, __LINE__ + 1
-
def #{method}(*)
-
@symbols = nil
-
super
-
end
-
CODE
-
end
-
end
-
-
1
SET = Mimes.new
-
1
EXTENSION_LOOKUP = {}
-
1
LOOKUP = Hash.new { |h, k| h[k] = Type.new(k) unless k.blank? }
-
-
1
class << self
-
1
def [](type)
-
8
return type if type.is_a?(Type)
-
8
Type.lookup_by_extension(type)
-
end
-
-
1
def fetch(type)
-
2
return type if type.is_a?(Type)
-
4
EXTENSION_LOOKUP.fetch(type.to_s) { |k| yield k }
-
end
-
end
-
-
# Encapsulates the notion of a mime type. Can be used at render time, for example, with:
-
#
-
# class PostsController < ActionController::Base
-
# def show
-
# @post = Post.find(params[:id])
-
#
-
# respond_to do |format|
-
# format.html
-
# format.ics { render text: post.to_ics, mime_type: Mime::Type["text/calendar"] }
-
# format.xml { render xml: @people }
-
# end
-
# end
-
# end
-
1
class Type
-
1
@@html_types = Set.new [:html, :all]
-
1
cattr_reader :html_types
-
-
1
attr_reader :symbol
-
-
1
@register_callbacks = []
-
-
# A simple helper class used in parsing the accept header
-
1
class AcceptItem #:nodoc:
-
1
attr_accessor :index, :name, :q
-
1
alias :to_s :name
-
-
1
def initialize(index, name, q = nil)
-
@index = index
-
@name = name
-
q ||= 0.0 if @name == Mime::ALL.to_s # default wildcard match to end of list
-
@q = ((q || 1.0).to_f * 100).to_i
-
end
-
-
1
def <=>(item)
-
result = item.q <=> @q
-
result = @index <=> item.index if result == 0
-
result
-
end
-
-
1
def ==(item)
-
@name == item.to_s
-
end
-
end
-
-
1
class AcceptList < Array #:nodoc:
-
1
def assort!
-
sort!
-
-
# Take care of the broken text/xml entry by renaming or deleting it
-
if text_xml_idx && app_xml_idx
-
app_xml.q = [text_xml.q, app_xml.q].max # set the q value to the max of the two
-
exchange_xml_items if app_xml_idx > text_xml_idx # make sure app_xml is ahead of text_xml in the list
-
delete_at(text_xml_idx) # delete text_xml from the list
-
elsif text_xml_idx
-
text_xml.name = Mime::XML.to_s
-
end
-
-
# Look for more specific XML-based types and sort them ahead of app/xml
-
if app_xml_idx
-
idx = app_xml_idx
-
-
while idx < length
-
type = self[idx]
-
break if type.q < app_xml.q
-
-
if type.name.ends_with? '+xml'
-
self[app_xml_idx], self[idx] = self[idx], app_xml
-
@app_xml_idx = idx
-
end
-
idx += 1
-
end
-
end
-
-
map! { |i| Mime::Type.lookup(i.name) }.uniq!
-
to_a
-
end
-
-
1
private
-
1
def text_xml_idx
-
@text_xml_idx ||= index('text/xml')
-
end
-
-
1
def app_xml_idx
-
@app_xml_idx ||= index(Mime::XML.to_s)
-
end
-
-
1
def text_xml
-
self[text_xml_idx]
-
end
-
-
1
def app_xml
-
self[app_xml_idx]
-
end
-
-
1
def exchange_xml_items
-
self[app_xml_idx], self[text_xml_idx] = text_xml, app_xml
-
@app_xml_idx, @text_xml_idx = text_xml_idx, app_xml_idx
-
end
-
end
-
-
1
class << self
-
1
TRAILING_STAR_REGEXP = /(text|application)\/\*/
-
1
PARAMETER_SEPARATOR_REGEXP = /;\s*\w+="?\w+"?/
-
-
1
def register_callback(&block)
-
1
@register_callbacks << block
-
end
-
-
1
def lookup(string)
-
LOOKUP[string]
-
end
-
-
1
def lookup_by_extension(extension)
-
8
EXTENSION_LOOKUP[extension.to_s]
-
end
-
-
# Registers an alias that's not used on mime type lookup, but can be referenced directly. Especially useful for
-
# rendering different HTML versions depending on the user agent, like an iPhone.
-
1
def register_alias(string, symbol, extension_synonyms = [])
-
register(string, symbol, [], extension_synonyms, true)
-
end
-
-
1
def register(string, symbol, mime_type_synonyms = [], extension_synonyms = [], skip_lookup = false)
-
22
Mime.const_set(symbol.upcase, Type.new(string, symbol, mime_type_synonyms))
-
-
22
new_mime = Mime.const_get(symbol.upcase)
-
22
SET << new_mime
-
-
52
([string] + mime_type_synonyms).each { |str| LOOKUP[str] = SET.last } unless skip_lookup
-
60
([symbol] + extension_synonyms).each { |ext| EXTENSION_LOOKUP[ext.to_s] = SET.last }
-
-
22
@register_callbacks.each do |callback|
-
callback.call(new_mime)
-
end
-
end
-
-
1
def parse(accept_header)
-
if !accept_header.include?(',')
-
accept_header = accept_header.split(PARAMETER_SEPARATOR_REGEXP).first
-
parse_trailing_star(accept_header) || [Mime::Type.lookup(accept_header)].compact
-
else
-
list, index = AcceptList.new, 0
-
accept_header.split(',').each do |header|
-
params, q = header.split(PARAMETER_SEPARATOR_REGEXP)
-
if params.present?
-
params.strip!
-
-
params = parse_trailing_star(params) || [params]
-
-
params.each do |m|
-
list << AcceptItem.new(index, m.to_s, q)
-
index += 1
-
end
-
end
-
end
-
list.assort!
-
end
-
end
-
-
1
def parse_trailing_star(accept_header)
-
parse_data_with_trailing_star($1) if accept_header =~ TRAILING_STAR_REGEXP
-
end
-
-
# For an input of <tt>'text'</tt>, returns <tt>[Mime::JSON, Mime::XML, Mime::ICS,
-
# Mime::HTML, Mime::CSS, Mime::CSV, Mime::JS, Mime::YAML, Mime::TEXT]</tt>.
-
#
-
# For an input of <tt>'application'</tt>, returns <tt>[Mime::HTML, Mime::JS,
-
# Mime::XML, Mime::YAML, Mime::ATOM, Mime::JSON, Mime::RSS, Mime::URL_ENCODED_FORM]</tt>.
-
1
def parse_data_with_trailing_star(input)
-
Mime::SET.select { |m| m =~ input }
-
end
-
-
# This method is opposite of register method.
-
#
-
# Usage:
-
#
-
# Mime::Type.unregister(:mobile)
-
1
def unregister(symbol)
-
symbol = symbol.upcase
-
mime = Mime.const_get(symbol)
-
Mime.instance_eval { remove_const(symbol) }
-
-
SET.delete_if { |v| v.eql?(mime) }
-
LOOKUP.delete_if { |_,v| v.eql?(mime) }
-
EXTENSION_LOOKUP.delete_if { |_,v| v.eql?(mime) }
-
end
-
end
-
-
1
def initialize(string, symbol = nil, synonyms = [])
-
23
@symbol, @synonyms = symbol, synonyms
-
23
@string = string
-
end
-
-
1
def to_s
-
4
@string
-
end
-
-
1
def to_str
-
to_s
-
end
-
-
1
def to_sym
-
58
@symbol
-
end
-
-
1
def ref
-
10
to_sym || to_s
-
end
-
-
1
def ===(list)
-
if list.is_a?(Array)
-
(@synonyms + [ self ]).any? { |synonym| list.include?(synonym) }
-
else
-
super
-
end
-
end
-
-
1
def ==(mime_type)
-
return false if mime_type.blank?
-
(@synonyms + [ self ]).any? do |synonym|
-
synonym.to_s == mime_type.to_s || synonym.to_sym == mime_type.to_sym
-
end
-
end
-
-
1
def =~(mime_type)
-
return false if mime_type.blank?
-
regexp = Regexp.new(Regexp.quote(mime_type.to_s))
-
(@synonyms + [ self ]).any? do |synonym|
-
synonym.to_s =~ regexp
-
end
-
end
-
-
1
def html?
-
2
@@html_types.include?(to_sym) || @string =~ /html/
-
end
-
-
-
1
private
-
-
1
def to_ary; end
-
1
def to_a; end
-
-
1
def method_missing(method, *args)
-
if method.to_s.ends_with? '?'
-
method[0..-2].downcase.to_sym == to_sym
-
else
-
super
-
end
-
end
-
-
1
def respond_to_missing?(method, include_private = false) #:nodoc:
-
method.to_s.ends_with? '?'
-
end
-
end
-
-
1
class NullType
-
1
include Singleton
-
-
1
def nil?
-
true
-
end
-
-
1
def ref; end
-
-
1
def respond_to_missing?(method, include_private = false)
-
method.to_s.ends_with? '?'
-
end
-
-
1
private
-
1
def method_missing(method, *args)
-
false if method.to_s.ends_with? '?'
-
end
-
end
-
end
-
-
1
require 'action_dispatch/http/mime_types'
-
# Build list of Mime types for HTTP responses
-
# http://www.iana.org/assignments/media-types/
-
-
1
Mime::Type.register "text/html", :html, %w( application/xhtml+xml ), %w( xhtml )
-
1
Mime::Type.register "text/plain", :text, [], %w(txt)
-
1
Mime::Type.register "text/javascript", :js, %w( application/javascript application/x-javascript )
-
1
Mime::Type.register "text/css", :css
-
1
Mime::Type.register "text/calendar", :ics
-
1
Mime::Type.register "text/csv", :csv
-
1
Mime::Type.register "text/vcard", :vcf
-
-
1
Mime::Type.register "image/png", :png, [], %w(png)
-
1
Mime::Type.register "image/jpeg", :jpeg, [], %w(jpg jpeg jpe pjpeg)
-
1
Mime::Type.register "image/gif", :gif, [], %w(gif)
-
1
Mime::Type.register "image/bmp", :bmp, [], %w(bmp)
-
1
Mime::Type.register "image/tiff", :tiff, [], %w(tif tiff)
-
-
1
Mime::Type.register "video/mpeg", :mpeg, [], %w(mpg mpeg mpe)
-
-
1
Mime::Type.register "application/xml", :xml, %w( text/xml application/x-xml )
-
1
Mime::Type.register "application/rss+xml", :rss
-
1
Mime::Type.register "application/atom+xml", :atom
-
1
Mime::Type.register "application/x-yaml", :yaml, %w( text/yaml )
-
-
1
Mime::Type.register "multipart/form-data", :multipart_form
-
1
Mime::Type.register "application/x-www-form-urlencoded", :url_encoded_form
-
-
# http://www.ietf.org/rfc/rfc4627.txt
-
# http://www.json.org/JSONRequest.html
-
1
Mime::Type.register "application/json", :json, %w( text/x-json application/jsonrequest )
-
-
1
Mime::Type.register "application/pdf", :pdf, [], %w(pdf)
-
1
Mime::Type.register "application/zip", :zip, [], %w(zip)
-
-
# Create Mime::ALL but do not add it to the SET.
-
1
Mime::ALL = Mime::Type.new("*/*", :all, [])
-
1
module ActionDispatch
-
1
module Http
-
1
class ParameterFilter
-
1
FILTERED = '[FILTERED]'.freeze # :nodoc:
-
-
1
def initialize(filters = [])
-
6
@filters = filters
-
end
-
-
1
def filter(params)
-
4
compiled_filter.call(params)
-
end
-
-
1
private
-
-
1
def compiled_filter
-
4
@compiled_filter ||= CompiledFilter.compile(@filters)
-
end
-
-
1
class CompiledFilter # :nodoc:
-
1
def self.compile(filters)
-
4
return lambda { |params| params.dup } if filters.empty?
-
-
4
strings, regexps, blocks = [], [], []
-
-
4
filters.each do |item|
-
4
case item
-
when Proc
-
blocks << item
-
when Regexp
-
regexps << item
-
else
-
4
strings << item.to_s
-
end
-
end
-
-
4
regexps << Regexp.new(strings.join('|'), true) unless strings.empty?
-
4
new regexps, blocks
-
end
-
-
1
attr_reader :regexps, :blocks
-
-
1
def initialize(regexps, blocks)
-
4
@regexps = regexps
-
4
@blocks = blocks
-
end
-
-
1
def call(original_params)
-
6
filtered_params = {}
-
-
6
original_params.each do |key, value|
-
32
if regexps.any? { |r| key =~ r }
-
value = FILTERED
-
elsif value.is_a?(Hash)
-
2
value = call(value)
-
elsif value.is_a?(Array)
-
value = value.map { |v| v.is_a?(Hash) ? call(v) : v }
-
elsif blocks.any?
-
key = key.dup
-
value = value.dup if value.duplicable?
-
blocks.each { |b| b.call(key, value) }
-
end
-
-
16
filtered_params[key] = value
-
end
-
-
6
filtered_params
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/hash/keys'
-
1
require 'active_support/core_ext/hash/indifferent_access'
-
-
1
module ActionDispatch
-
1
module Http
-
1
module Parameters
-
1
def initialize(env)
-
4
super
-
4
@symbolized_path_params = nil
-
end
-
-
# Returns both GET and POST \parameters in a single hash.
-
1
def parameters
-
14
@env["action_dispatch.request.parameters"] ||= begin
-
4
params = begin
-
4
request_parameters.merge(query_parameters)
-
rescue EOFError
-
query_parameters.dup
-
end
-
4
params.merge!(path_parameters)
-
4
params.with_indifferent_access
-
end
-
end
-
1
alias :params :parameters
-
-
1
def path_parameters=(parameters) #:nodoc:
-
@symbolized_path_params = nil
-
@env.delete("action_dispatch.request.parameters")
-
@env["action_dispatch.request.path_parameters"] = parameters
-
end
-
-
# The same as <tt>path_parameters</tt> with explicitly symbolized keys.
-
1
def symbolized_path_parameters
-
10
@symbolized_path_params ||= path_parameters.symbolize_keys
-
end
-
-
# Returns a hash with the \parameters used to form the \path of the request.
-
# Returned hash keys are strings:
-
#
-
# {'action' => 'my_action', 'controller' => 'my_controller'}
-
#
-
# See <tt>symbolized_path_parameters</tt> for symbolized keys.
-
1
def path_parameters
-
16
@env["action_dispatch.request.path_parameters"] ||= {}
-
end
-
-
1
def reset_parameters #:nodoc:
-
@env.delete("action_dispatch.request.parameters")
-
end
-
-
1
private
-
-
# Convert nested Hash to HashWithIndifferentAccess
-
# and UTF-8 encode both keys and values in nested Hash.
-
#
-
# TODO: Validate that the characters are UTF-8. If they aren't,
-
# you'll get a weird error down the road, but our form handling
-
# should really prevent that from happening
-
1
def normalize_encode_params(params)
-
4
case params
-
when String
-
params.force_encoding(Encoding::UTF_8).encode!
-
when Hash
-
4
if params.has_key?(:tempfile)
-
UploadedFile.new(params)
-
else
-
params.each_with_object({}) do |(key, val), new_hash|
-
new_key = key.is_a?(String) ? key.dup.force_encoding(Encoding::UTF_8).encode! : key
-
new_hash[new_key] = if val.is_a?(Array)
-
val.map! { |el| normalize_encode_params(el) }
-
else
-
normalize_encode_params(val)
-
end
-
4
end.with_indifferent_access
-
end
-
else
-
params
-
end
-
end
-
end
-
end
-
end
-
1
require 'stringio'
-
-
1
require 'active_support/inflector'
-
1
require 'action_dispatch/http/headers'
-
1
require 'action_controller/metal/exceptions'
-
1
require 'rack/request'
-
1
require 'action_dispatch/http/cache'
-
1
require 'action_dispatch/http/mime_negotiation'
-
1
require 'action_dispatch/http/parameters'
-
1
require 'action_dispatch/http/filter_parameters'
-
1
require 'action_dispatch/http/upload'
-
1
require 'action_dispatch/http/url'
-
1
require 'active_support/core_ext/array/conversions'
-
-
1
module ActionDispatch
-
1
class Request < Rack::Request
-
1
include ActionDispatch::Http::Cache::Request
-
1
include ActionDispatch::Http::MimeNegotiation
-
1
include ActionDispatch::Http::Parameters
-
1
include ActionDispatch::Http::FilterParameters
-
1
include ActionDispatch::Http::URL
-
-
1
autoload :Session, 'action_dispatch/request/session'
-
1
autoload :Utils, 'action_dispatch/request/utils'
-
-
1
LOCALHOST = Regexp.union [/^127\.0\.0\.\d{1,3}$/, /^::1$/, /^0:0:0:0:0:0:0:1(%.*)?$/]
-
-
1
ENV_METHODS = %w[ AUTH_TYPE GATEWAY_INTERFACE
-
PATH_TRANSLATED REMOTE_HOST
-
REMOTE_IDENT REMOTE_USER REMOTE_ADDR
-
SERVER_NAME SERVER_PROTOCOL
-
-
HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING
-
HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL HTTP_FROM
-
HTTP_NEGOTIATE HTTP_PRAGMA ].freeze
-
-
1
ENV_METHODS.each do |env|
-
17
class_eval <<-METHOD, __FILE__, __LINE__ + 1
-
def #{env.sub(/^HTTP_/n, '').downcase} # def accept_charset
-
@env["#{env}"] # @env["HTTP_ACCEPT_CHARSET"]
-
end # end
-
METHOD
-
end
-
-
1
def initialize(env)
-
4
super
-
4
@method = nil
-
4
@request_method = nil
-
4
@remote_ip = nil
-
4
@original_fullpath = nil
-
4
@fullpath = nil
-
4
@ip = nil
-
4
@uuid = nil
-
end
-
-
1
def key?(key)
-
@env.key?(key)
-
end
-
-
# List of HTTP request methods from the following RFCs:
-
# Hypertext Transfer Protocol -- HTTP/1.1 (http://www.ietf.org/rfc/rfc2616.txt)
-
# HTTP Extensions for Distributed Authoring -- WEBDAV (http://www.ietf.org/rfc/rfc2518.txt)
-
# Versioning Extensions to WebDAV (http://www.ietf.org/rfc/rfc3253.txt)
-
# Ordered Collections Protocol (WebDAV) (http://www.ietf.org/rfc/rfc3648.txt)
-
# Web Distributed Authoring and Versioning (WebDAV) Access Control Protocol (http://www.ietf.org/rfc/rfc3744.txt)
-
# Web Distributed Authoring and Versioning (WebDAV) SEARCH (http://www.ietf.org/rfc/rfc5323.txt)
-
# PATCH Method for HTTP (http://www.ietf.org/rfc/rfc5789.txt)
-
1
RFC2616 = %w(OPTIONS GET HEAD POST PUT DELETE TRACE CONNECT)
-
1
RFC2518 = %w(PROPFIND PROPPATCH MKCOL COPY MOVE LOCK UNLOCK)
-
1
RFC3253 = %w(VERSION-CONTROL REPORT CHECKOUT CHECKIN UNCHECKOUT MKWORKSPACE UPDATE LABEL MERGE BASELINE-CONTROL MKACTIVITY)
-
1
RFC3648 = %w(ORDERPATCH)
-
1
RFC3744 = %w(ACL)
-
1
RFC5323 = %w(SEARCH)
-
1
RFC5789 = %w(PATCH)
-
-
1
HTTP_METHODS = RFC2616 + RFC2518 + RFC3253 + RFC3648 + RFC3744 + RFC5323 + RFC5789
-
-
1
HTTP_METHOD_LOOKUP = {}
-
-
# Populate the HTTP method lookup cache
-
1
HTTP_METHODS.each { |method|
-
30
HTTP_METHOD_LOOKUP[method] = method.underscore.to_sym
-
}
-
-
# Returns the HTTP \method that the application should see.
-
# In the case where the \method was overridden by a middleware
-
# (for instance, if a HEAD request was converted to a GET,
-
# or if a _method parameter was used to determine the \method
-
# the application should use), this \method returns the overridden
-
# value, not the original.
-
1
def request_method
-
8
@request_method ||= check_method(env["REQUEST_METHOD"])
-
end
-
-
# Returns a symbol form of the #request_method
-
1
def request_method_symbol
-
HTTP_METHOD_LOOKUP[request_method]
-
end
-
-
# Returns the original value of the environment's REQUEST_METHOD,
-
# even if it was overridden by middleware. See #request_method for
-
# more information.
-
1
def method
-
4
@method ||= check_method(env["rack.methodoverride.original_method"] || env['REQUEST_METHOD'])
-
end
-
-
# Returns a symbol form of the #method
-
1
def method_symbol
-
HTTP_METHOD_LOOKUP[method]
-
end
-
-
# Is this a GET (or HEAD) request?
-
# Equivalent to <tt>request.request_method_symbol == :get</tt>.
-
1
def get?
-
8
HTTP_METHOD_LOOKUP[request_method] == :get
-
end
-
-
# Is this a POST request?
-
# Equivalent to <tt>request.request_method_symbol == :post</tt>.
-
1
def post?
-
HTTP_METHOD_LOOKUP[request_method] == :post
-
end
-
-
# Is this a PATCH request?
-
# Equivalent to <tt>request.request_method == :patch</tt>.
-
1
def patch?
-
HTTP_METHOD_LOOKUP[request_method] == :patch
-
end
-
-
# Is this a PUT request?
-
# Equivalent to <tt>request.request_method_symbol == :put</tt>.
-
1
def put?
-
HTTP_METHOD_LOOKUP[request_method] == :put
-
end
-
-
# Is this a DELETE request?
-
# Equivalent to <tt>request.request_method_symbol == :delete</tt>.
-
1
def delete?
-
HTTP_METHOD_LOOKUP[request_method] == :delete
-
end
-
-
# Is this a HEAD request?
-
# Equivalent to <tt>request.request_method_symbol == :head</tt>.
-
1
def head?
-
HTTP_METHOD_LOOKUP[request_method] == :head
-
end
-
-
# Provides access to the request's HTTP headers, for example:
-
#
-
# request.headers["Content-Type"] # => "text/plain"
-
1
def headers
-
Http::Headers.new(@env)
-
end
-
-
1
def original_fullpath
-
@original_fullpath ||= (env["ORIGINAL_FULLPATH"] || fullpath)
-
end
-
-
# Returns the +String+ full path including params of the last URL requested.
-
#
-
# # get "/articles"
-
# request.fullpath # => "/articles"
-
#
-
# # get "/articles?page=2"
-
# request.fullpath # => "/articles?page=2"
-
1
def fullpath
-
4
@fullpath ||= super
-
end
-
-
# Returns the original request URL as a +String+.
-
#
-
# # get "/articles?page=2"
-
# request.original_url # => "http://www.example.com/articles?page=2"
-
1
def original_url
-
base_url + original_fullpath
-
end
-
-
# The +String+ MIME type of the request.
-
#
-
# # get "/articles"
-
# request.media_type # => "application/x-www-form-urlencoded"
-
1
def media_type
-
4
content_mime_type.to_s
-
end
-
-
# Returns the content length of the request as an integer.
-
1
def content_length
-
super.to_i
-
end
-
-
# Returns true if the "X-Requested-With" header contains "XMLHttpRequest"
-
# (case-insensitive). All major JavaScript libraries send this header with
-
# every Ajax request.
-
1
def xml_http_request?
-
8
@env['HTTP_X_REQUESTED_WITH'] =~ /XMLHttpRequest/i
-
end
-
1
alias :xhr? :xml_http_request?
-
-
1
def ip
-
@ip ||= super
-
end
-
-
# Originating IP address, usually set by the RemoteIp middleware.
-
1
def remote_ip
-
@remote_ip ||= (@env["action_dispatch.remote_ip"] || ip).to_s
-
end
-
-
# Returns the unique request id, which is based off either the X-Request-Id header that can
-
# be generated by a firewall, load balancer, or web server or by the RequestId middleware
-
# (which sets the action_dispatch.request_id environment variable).
-
#
-
# This unique ID is useful for tracing a request from end-to-end as part of logging or debugging.
-
# This relies on the rack variable set by the ActionDispatch::RequestId middleware.
-
1
def uuid
-
@uuid ||= env["action_dispatch.request_id"]
-
end
-
-
# Returns the lowercase name of the HTTP server software.
-
1
def server_software
-
(@env['SERVER_SOFTWARE'] && /^([a-zA-Z]+)/ =~ @env['SERVER_SOFTWARE']) ? $1.downcase : nil
-
end
-
-
# Read the request \body. This is useful for web services that need to
-
# work with raw requests directly.
-
1
def raw_post
-
unless @env.include? 'RAW_POST_DATA'
-
raw_post_body = body
-
@env['RAW_POST_DATA'] = raw_post_body.read(content_length)
-
raw_post_body.rewind if raw_post_body.respond_to?(:rewind)
-
end
-
@env['RAW_POST_DATA']
-
end
-
-
# The request body is an IO input stream. If the RAW_POST_DATA environment
-
# variable is already set, wrap it in a StringIO.
-
1
def body
-
if raw_post = @env['RAW_POST_DATA']
-
raw_post.force_encoding(Encoding::BINARY)
-
StringIO.new(raw_post)
-
else
-
@env['rack.input']
-
end
-
end
-
-
1
def form_data?
-
4
FORM_DATA_MEDIA_TYPES.include?(content_mime_type.to_s)
-
end
-
-
1
def body_stream #:nodoc:
-
@env['rack.input']
-
end
-
-
# TODO This should be broken apart into AD::Request::Session and probably
-
# be included by the session middleware.
-
1
def reset_session
-
if session && session.respond_to?(:destroy)
-
session.destroy
-
else
-
self.session = {}
-
end
-
@env['action_dispatch.request.flash_hash'] = nil
-
end
-
-
1
def session=(session) #:nodoc:
-
4
Session.set @env, session
-
end
-
-
1
def session_options=(options)
-
4
Session::Options.set @env, options
-
end
-
-
# Override Rack's GET method to support indifferent access
-
1
def GET
-
6
@env["action_dispatch.request.query_parameters"] ||= Utils.deep_munge((normalize_encode_params(super) || {}))
-
rescue TypeError => e
-
raise ActionController::BadRequest.new(:query, e)
-
end
-
1
alias :query_parameters :GET
-
-
# Override Rack's POST method to support indifferent access
-
1
def POST
-
10
@env["action_dispatch.request.request_parameters"] ||= Utils.deep_munge((normalize_encode_params(super) || {}))
-
rescue TypeError => e
-
raise ActionController::BadRequest.new(:request, e)
-
end
-
1
alias :request_parameters :POST
-
-
# Returns the authorization header regardless of whether it was specified directly or through one of the
-
# proxy alternatives.
-
1
def authorization
-
@env['HTTP_AUTHORIZATION'] ||
-
@env['X-HTTP_AUTHORIZATION'] ||
-
@env['X_HTTP_AUTHORIZATION'] ||
-
@env['REDIRECT_X_HTTP_AUTHORIZATION']
-
end
-
-
# True if the request came from localhost, 127.0.0.1.
-
1
def local?
-
LOCALHOST =~ remote_addr && LOCALHOST =~ remote_ip
-
end
-
-
# Extracted into ActionDispatch::Request::Utils.deep_munge, but kept here for backwards compatibility.
-
1
def deep_munge(hash)
-
ActiveSupport::Deprecation.warn(
-
"This method has been extracted into ActionDispatch::Request::Utils.deep_munge. Please start using that instead."
-
)
-
-
Utils.deep_munge(hash)
-
end
-
-
1
protected
-
1
def parse_query(qs)
-
Utils.deep_munge(super)
-
end
-
-
1
private
-
1
def check_method(name)
-
8
HTTP_METHOD_LOOKUP[name] || raise(ActionController::UnknownHttpMethod, "#{name}, accepted HTTP methods are #{HTTP_METHODS.to_sentence(:locale => :en)}")
-
8
name
-
end
-
end
-
end
-
1
require 'active_support/core_ext/module/attribute_accessors'
-
1
require 'action_dispatch/http/filter_redirect'
-
1
require 'monitor'
-
-
1
module ActionDispatch # :nodoc:
-
# Represents an HTTP response generated by a controller action. Use it to
-
# retrieve the current state of the response, or customize the response. It can
-
# either represent a real HTTP response (i.e. one that is meant to be sent
-
# back to the web browser) or a TestResponse (i.e. one that is generated
-
# from integration tests).
-
#
-
# \Response is mostly a Ruby on \Rails framework implementation detail, and
-
# should never be used directly in controllers. Controllers should use the
-
# methods defined in ActionController::Base instead. For example, if you want
-
# to set the HTTP response's content MIME type, then use
-
# ActionControllerBase#headers instead of Response#headers.
-
#
-
# Nevertheless, integration tests may want to inspect controller responses in
-
# more detail, and that's when \Response can be useful for application
-
# developers. Integration test methods such as
-
# ActionDispatch::Integration::Session#get and
-
# ActionDispatch::Integration::Session#post return objects of type
-
# TestResponse (which are of course also of type \Response).
-
#
-
# For example, the following demo integration test prints the body of the
-
# controller response to the console:
-
#
-
# class DemoControllerTest < ActionDispatch::IntegrationTest
-
# def test_print_root_path_to_console
-
# get('/')
-
# puts response.body
-
# end
-
# end
-
1
class Response
-
# The request that the response is responding to.
-
1
attr_accessor :request
-
-
# The HTTP status code.
-
1
attr_reader :status
-
-
1
attr_writer :sending_file
-
-
# Get and set headers for this response.
-
1
attr_accessor :header
-
-
1
alias_method :headers=, :header=
-
1
alias_method :headers, :header
-
-
1
delegate :[], :[]=, :to => :@header
-
1
delegate :each, :to => :@stream
-
-
# Sets the HTTP response's content MIME type. For example, in the controller
-
# you could write this:
-
#
-
# response.content_type = "text/plain"
-
#
-
# If a character set has been defined for this response (see charset=) then
-
# the character set information will also be included in the content type
-
# information.
-
1
attr_reader :content_type
-
-
# The charset of the response. HTML wants to know the encoding of the
-
# content you're giving them, so we need to send that along.
-
1
attr_accessor :charset
-
-
1
CONTENT_TYPE = "Content-Type".freeze
-
1
SET_COOKIE = "Set-Cookie".freeze
-
1
LOCATION = "Location".freeze
-
1
NO_CONTENT_CODES = [204, 304]
-
-
3
cattr_accessor(:default_charset) { "utf-8" }
-
1
cattr_accessor(:default_headers)
-
-
1
include Rack::Response::Helpers
-
1
include ActionDispatch::Http::FilterRedirect
-
1
include ActionDispatch::Http::Cache::Response
-
1
include MonitorMixin
-
-
1
class Buffer # :nodoc:
-
1
def initialize(response, buf)
-
12
@response = response
-
12
@buf = buf
-
12
@closed = false
-
end
-
-
1
def write(string)
-
raise IOError, "closed stream" if closed?
-
-
@response.commit!
-
@buf.push string
-
end
-
-
1
def each(&block)
-
1
@response.sending!
-
1
x = @buf.each(&block)
-
1
@response.sent!
-
1
x
-
end
-
-
1
def close
-
@response.commit!
-
@closed = true
-
end
-
-
1
def closed?
-
@closed
-
end
-
end
-
-
# The underlying body, as a streamable object.
-
1
attr_reader :stream
-
-
1
def initialize(status = 200, header = {}, body = [])
-
8
super()
-
-
8
header = merge_default_headers(header, self.class.default_headers)
-
-
8
self.body, self.header, self.status = body, header, status
-
-
8
@sending_file = false
-
8
@blank = false
-
8
@cv = new_cond
-
8
@committed = false
-
8
@sending = false
-
8
@sent = false
-
8
@content_type = nil
-
8
@charset = nil
-
-
8
if content_type = self[CONTENT_TYPE]
-
type, charset = content_type.split(/;\s*charset=/)
-
@content_type = Mime::Type.lookup(type)
-
@charset = charset || self.class.default_charset
-
end
-
-
8
prepare_cache_control!
-
-
8
yield self if block_given?
-
end
-
-
1
def await_commit
-
synchronize do
-
@cv.wait_until { @committed }
-
end
-
end
-
-
1
def await_sent
-
synchronize { @cv.wait_until { @sent } }
-
end
-
-
1
def commit!
-
synchronize do
-
before_committed
-
@committed = true
-
@cv.broadcast
-
end
-
end
-
-
1
def sending!
-
1
synchronize do
-
1
before_sending
-
1
@sending = true
-
1
@cv.broadcast
-
end
-
end
-
-
1
def sent!
-
1
synchronize do
-
1
@sent = true
-
1
@cv.broadcast
-
end
-
end
-
-
1
def sending?; synchronize { @sending }; end
-
13
def committed?; synchronize { @committed }; end
-
1
def sent?; synchronize { @sent }; end
-
-
# Sets the HTTP status code.
-
1
def status=(status)
-
10
@status = Rack::Utils.status_code(status)
-
end
-
-
# Sets the HTTP content type.
-
1
def content_type=(content_type)
-
2
@content_type = content_type.to_s
-
end
-
-
# The response code of the request.
-
1
def response_code
-
1
@status
-
end
-
-
# Returns a string to ensure compatibility with <tt>Net::HTTPResponse</tt>.
-
1
def code
-
@status.to_s
-
end
-
-
# Returns the corresponding message for the current HTTP status code:
-
#
-
# response.status = 200
-
# response.message # => "OK"
-
#
-
# response.status = 404
-
# response.message # => "Not Found"
-
#
-
1
def message
-
Rack::Utils::HTTP_STATUS_CODES[@status]
-
end
-
1
alias_method :status_message, :message
-
-
1
def respond_to?(method, include_private = false)
-
if method.to_s == 'to_path'
-
stream.respond_to?(method)
-
else
-
super
-
end
-
end
-
-
1
def to_path
-
stream.to_path
-
end
-
-
# Returns the content of the response as a string. This contains the contents
-
# of any calls to <tt>render</tt>.
-
1
def body
-
1
strings = []
-
2
each { |part| strings << part.to_s }
-
1
strings.join
-
end
-
-
1
EMPTY = " "
-
-
# Allows you to manually set or override the response body.
-
1
def body=(body)
-
12
@blank = true if body == EMPTY
-
-
12
if body.respond_to?(:to_path)
-
@stream = body
-
else
-
12
synchronize do
-
12
@stream = build_buffer self, munge_body_object(body)
-
end
-
end
-
end
-
-
1
def body_parts
-
parts = []
-
@stream.each { |x| parts << x }
-
parts
-
end
-
-
1
def set_cookie(key, value)
-
::Rack::Utils.set_cookie_header!(header, key, value)
-
end
-
-
1
def delete_cookie(key, value={})
-
::Rack::Utils.delete_cookie_header!(header, key, value)
-
end
-
-
# The location header we'll be responding with.
-
1
def location
-
6
headers[LOCATION]
-
end
-
1
alias_method :redirect_url, :location
-
-
# Sets the location header we'll be responding with.
-
1
def location=(url)
-
2
headers[LOCATION] = url
-
end
-
-
1
def close
-
stream.close if stream.respond_to?(:close)
-
end
-
-
# Turns the Response into a Rack-compatible array of the status, headers,
-
# and body.
-
1
def to_a
-
4
rack_response @status, @header.to_hash
-
end
-
1
alias prepare! to_a
-
1
alias to_ary to_a
-
-
# Returns the response cookies, converted to a Hash of (name => value) pairs
-
#
-
# assert_equal 'AuthorOfNewPage', r.cookies['author']
-
1
def cookies
-
cookies = {}
-
if header = self[SET_COOKIE]
-
header = header.split("\n") if header.respond_to?(:to_str)
-
header.each do |cookie|
-
if pair = cookie.split(';').first
-
key, value = pair.split("=").map { |v| Rack::Utils.unescape(v) }
-
cookies[key] = value
-
end
-
end
-
end
-
cookies
-
end
-
-
1
def _status_code
-
@status
-
end
-
1
private
-
-
1
def before_committed
-
end
-
-
1
def before_sending
-
end
-
-
1
def merge_default_headers(original, default)
-
8
return original unless default.respond_to?(:merge)
-
-
8
default.merge(original)
-
end
-
-
1
def build_buffer(response, body)
-
12
Buffer.new response, body
-
end
-
-
1
def munge_body_object(body)
-
12
body.respond_to?(:each) ? body : [body]
-
end
-
-
1
def assign_default_content_type_and_charset!(headers)
-
4
return if headers[CONTENT_TYPE].present?
-
-
4
@content_type ||= Mime::HTML
-
4
@charset ||= self.class.default_charset unless @charset == false
-
-
4
type = @content_type.to_s.dup
-
4
type << "; charset=#{@charset}" if append_charset?
-
-
4
headers[CONTENT_TYPE] = type
-
end
-
-
1
def append_charset?
-
4
!@sending_file && @charset != false
-
end
-
-
1
def rack_response(status, header)
-
4
assign_default_content_type_and_charset!(header)
-
4
handle_conditional_get!
-
-
4
header[SET_COOKIE] = header[SET_COOKIE].join("\n") if header[SET_COOKIE].respond_to?(:join)
-
-
4
if NO_CONTENT_CODES.include?(@status)
-
header.delete CONTENT_TYPE
-
[status, header, []]
-
else
-
4
[status, header, Rack::BodyProxy.new(self){}]
-
end
-
end
-
end
-
end
-
1
module ActionDispatch
-
1
module Http
-
# Models uploaded files.
-
#
-
# The actual file is accessible via the +tempfile+ accessor, though some
-
# of its interface is available directly for convenience.
-
#
-
# Uploaded files are temporary files whose lifespan is one request. When
-
# the object is finalized Ruby unlinks the file, so there is no need to
-
# clean them with a separate maintenance task.
-
1
class UploadedFile
-
# The basename of the file in the client.
-
1
attr_accessor :original_filename
-
-
# A string with the MIME type of the file.
-
1
attr_accessor :content_type
-
-
# A +Tempfile+ object with the actual uploaded file. Note that some of
-
# its interface is available directly.
-
1
attr_accessor :tempfile
-
-
# A string with the headers of the multipart request.
-
1
attr_accessor :headers
-
-
1
def initialize(hash) # :nodoc:
-
@tempfile = hash[:tempfile]
-
raise(ArgumentError, ':tempfile is required') unless @tempfile
-
-
@original_filename = encode_filename(hash[:filename])
-
@content_type = hash[:type]
-
@headers = hash[:head]
-
end
-
-
# Shortcut for +tempfile.read+.
-
1
def read(length=nil, buffer=nil)
-
@tempfile.read(length, buffer)
-
end
-
-
# Shortcut for +tempfile.open+.
-
1
def open
-
@tempfile.open
-
end
-
-
# Shortcut for +tempfile.close+.
-
1
def close(unlink_now=false)
-
@tempfile.close(unlink_now)
-
end
-
-
# Shortcut for +tempfile.path+.
-
1
def path
-
@tempfile.path
-
end
-
-
# Shortcut for +tempfile.rewind+.
-
1
def rewind
-
@tempfile.rewind
-
end
-
-
# Shortcut for +tempfile.size+.
-
1
def size
-
@tempfile.size
-
end
-
-
# Shortcut for +tempfile.eof?+.
-
1
def eof?
-
@tempfile.eof?
-
end
-
-
1
private
-
-
1
def encode_filename(filename)
-
# Encode the filename in the utf8 encoding, unless it is nil
-
filename.force_encoding(Encoding::UTF_8).encode! if filename
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/module/attribute_accessors'
-
1
require 'active_support/core_ext/hash/slice'
-
-
1
module ActionDispatch
-
1
module Http
-
1
module URL
-
1
IP_HOST_REGEXP = /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/
-
1
HOST_REGEXP = /(^.*:\/\/)?([^:]+)(?::(\d+$))?/
-
1
PROTOCOL_REGEXP = /^([^:]+)(:)?(\/\/)?$/
-
-
1
mattr_accessor :tld_length
-
1
self.tld_length = 1
-
-
1
class << self
-
1
def extract_domain(host, tld_length = @@tld_length)
-
host.split('.').last(1 + tld_length).join('.') if named_host?(host)
-
end
-
-
1
def extract_subdomains(host, tld_length = @@tld_length)
-
if named_host?(host)
-
parts = host.split('.')
-
parts[0..-(tld_length + 2)]
-
else
-
[]
-
end
-
end
-
-
1
def extract_subdomain(host, tld_length = @@tld_length)
-
extract_subdomains(host, tld_length).join('.')
-
end
-
-
1
def url_for(options = {})
-
7
options = options.dup
-
7
path = options.delete(:script_name).to_s.chomp("/")
-
7
path << options.delete(:path).to_s
-
-
7
add_trailing_slash(path) if options[:trailing_slash]
-
-
7
params = options[:params].is_a?(Hash) ? options[:params] : options.slice(:params)
-
13
params.reject! { |_,v| v.to_param.nil? }
-
-
7
result = build_host_url(options)
-
-
7
result << path
-
-
7
result << "?#{params.to_query}" unless params.empty?
-
7
result << "##{Journey::Router::Utils.escape_fragment(options[:anchor].to_param.to_s)}" if options[:anchor]
-
7
result
-
end
-
-
1
private
-
-
1
def add_trailing_slash(path)
-
# includes querysting
-
if path.include?('?')
-
path.sub!(/\?/, '/\&')
-
# does not have a .format
-
elsif !path.include?(".")
-
path.sub!(/[^\/]\z|\A\z/, '\&/')
-
end
-
-
path
-
end
-
-
1
def build_host_url(options)
-
7
if options[:host].blank? && options[:only_path].blank?
-
raise ArgumentError, 'Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true'
-
end
-
-
7
result = ""
-
-
7
unless options[:only_path]
-
if match = options[:host].match(HOST_REGEXP)
-
options[:protocol] ||= match[1] unless options[:protocol] == false
-
options[:host] = match[2]
-
options[:port] = match[3] unless options.key?(:port)
-
end
-
-
options[:protocol] = normalize_protocol(options)
-
options[:host] = normalize_host(options)
-
options[:port] = normalize_port(options)
-
-
result << options[:protocol]
-
result << rewrite_authentication(options)
-
result << options[:host]
-
result << ":#{options[:port]}" if options[:port]
-
end
-
7
result
-
end
-
-
1
def named_host?(host)
-
host && IP_HOST_REGEXP !~ host
-
end
-
-
1
def same_host?(options)
-
(options[:subdomain] == true || !options.key?(:subdomain)) && options[:domain].nil?
-
end
-
-
1
def rewrite_authentication(options)
-
if options[:user] && options[:password]
-
"#{Rack::Utils.escape(options[:user])}:#{Rack::Utils.escape(options[:password])}@"
-
else
-
""
-
end
-
end
-
-
1
def normalize_protocol(options)
-
case options[:protocol]
-
when nil
-
"http://"
-
when false, "//"
-
"//"
-
when PROTOCOL_REGEXP
-
"#{$1}://"
-
else
-
raise ArgumentError, "Invalid :protocol option: #{options[:protocol].inspect}"
-
end
-
end
-
-
1
def normalize_host(options)
-
return options[:host] if !named_host?(options[:host]) || same_host?(options)
-
-
tld_length = options[:tld_length] || @@tld_length
-
-
host = ""
-
if options[:subdomain] == true || !options.key?(:subdomain)
-
host << extract_subdomain(options[:host], tld_length).to_param
-
elsif options[:subdomain].present?
-
host << options[:subdomain].to_param
-
end
-
host << "." unless host.empty?
-
host << (options[:domain] || extract_domain(options[:host], tld_length))
-
host
-
end
-
-
1
def normalize_port(options)
-
return nil if options[:port].nil? || options[:port] == false
-
-
case options[:protocol]
-
when "//"
-
options[:port]
-
when "https://"
-
options[:port].to_i == 443 ? nil : options[:port]
-
else
-
options[:port].to_i == 80 ? nil : options[:port]
-
end
-
end
-
end
-
-
1
def initialize(env)
-
4
super
-
4
@protocol = nil
-
4
@port = nil
-
end
-
-
# Returns the complete URL used for this request.
-
1
def url
-
protocol + host_with_port + fullpath
-
end
-
-
# Returns 'https://' if this is an SSL request and 'http://' otherwise.
-
1
def protocol
-
22
@protocol ||= ssl? ? 'https://' : 'http://'
-
end
-
-
# Returns the \host for this request, such as "example.com".
-
1
def raw_host_with_port
-
17
if forwarded = env["HTTP_X_FORWARDED_HOST"]
-
forwarded.split(/,\s?/).last
-
else
-
17
env['HTTP_HOST'] || "#{env['SERVER_NAME'] || env['SERVER_ADDR']}:#{env['SERVER_PORT']}"
-
end
-
end
-
-
# Returns the host for this request, such as example.com.
-
1
def host
-
13
raw_host_with_port.sub(/:\d+$/, '')
-
end
-
-
# Returns a \host:\port string for this request, such as "example.com" or
-
# "example.com:8080".
-
1
def host_with_port
-
3
"#{host}#{port_string}"
-
end
-
-
# Returns the port number of this request as an integer.
-
1
def port
-
@port ||= begin
-
4
if raw_host_with_port =~ /:(\d+)$/
-
$1.to_i
-
else
-
4
standard_port
-
end
-
9
end
-
end
-
-
# Returns the standard \port number for this request's protocol.
-
1
def standard_port
-
13
case protocol
-
when 'https://' then 443
-
13
else 80
-
end
-
end
-
-
# Returns whether this request is using the standard port
-
1
def standard_port?
-
9
port == standard_port
-
end
-
-
# Returns a number \port suffix like 8080 if the \port number of this request
-
# is not the default HTTP \port 80 or HTTPS \port 443.
-
1
def optional_port
-
6
standard_port? ? nil : port
-
end
-
-
# Returns a string \port suffix, including colon, like ":8080" if the \port
-
# number of this request is not the default HTTP \port 80 or HTTPS \port 443.
-
1
def port_string
-
3
standard_port? ? '' : ":#{port}"
-
end
-
-
1
def server_port
-
@env['SERVER_PORT'].to_i
-
end
-
-
# Returns the \domain part of a \host, such as "rubyonrails.org" in "www.rubyonrails.org". You can specify
-
# a different <tt>tld_length</tt>, such as 2 to catch rubyonrails.co.uk in "www.rubyonrails.co.uk".
-
1
def domain(tld_length = @@tld_length)
-
ActionDispatch::Http::URL.extract_domain(host, tld_length)
-
end
-
-
# Returns all the \subdomains as an array, so <tt>["dev", "www"]</tt> would be
-
# returned for "dev.www.rubyonrails.org". You can specify a different <tt>tld_length</tt>,
-
# such as 2 to catch <tt>["www"]</tt> instead of <tt>["www", "rubyonrails"]</tt>
-
# in "www.rubyonrails.co.uk".
-
1
def subdomains(tld_length = @@tld_length)
-
ActionDispatch::Http::URL.extract_subdomains(host, tld_length)
-
end
-
-
# Returns all the \subdomains as a string, so <tt>"dev.www"</tt> would be
-
# returned for "dev.www.rubyonrails.org". You can specify a different <tt>tld_length</tt>,
-
# such as 2 to catch <tt>"www"</tt> instead of <tt>"www.rubyonrails"</tt>
-
# in "www.rubyonrails.co.uk".
-
1
def subdomain(tld_length = @@tld_length)
-
ActionDispatch::Http::URL.extract_subdomain(host, tld_length)
-
end
-
end
-
end
-
end
-
1
require 'action_dispatch/journey/router'
-
1
require 'action_dispatch/journey/gtg/builder'
-
1
require 'action_dispatch/journey/gtg/simulator'
-
1
require 'action_dispatch/journey/nfa/builder'
-
1
require 'action_dispatch/journey/nfa/simulator'
-
1
require 'action_controller/metal/exceptions'
-
-
1
module ActionDispatch
-
1
module Journey
-
# The Formatter class is used for formatting URLs. For example, parameters
-
# passed to +url_for+ in Rails will eventually call Formatter#generate.
-
1
class Formatter # :nodoc:
-
1
attr_reader :routes
-
-
1
def initialize(routes)
-
1
@routes = routes
-
1
@cache = nil
-
end
-
-
1
def generate(type, name, options, recall = {}, parameterize = nil)
-
8
constraints = recall.merge(options)
-
8
missing_keys = []
-
-
8
match_route(name, constraints) do |route|
-
8
parameterized_parts = extract_parameterized_parts(route, options, recall, parameterize)
-
-
# Skip this route unless a name has been provided or it is a
-
# standard Rails route since we can't determine whether an options
-
# hash passed to url_for matches a Rack application or a redirect.
-
8
next unless name || route.dispatcher?
-
-
8
missing_keys = missing_keys(route, parameterized_parts)
-
8
next unless missing_keys.empty?
-
8
params = options.dup.delete_if do |key, _|
-
24
parameterized_parts.key?(key) || route.defaults.key?(key)
-
end
-
-
8
return [route.format(parameterized_parts), params]
-
end
-
-
message = "No route matches #{Hash[constraints.sort].inspect}"
-
message << " missing required keys: #{missing_keys.sort.inspect}" if name
-
-
raise ActionController::UrlGenerationError, message
-
end
-
-
1
def clear
-
1
@cache = nil
-
end
-
-
1
private
-
-
1
def extract_parameterized_parts(route, options, recall, parameterize = nil)
-
8
parameterized_parts = recall.merge(options)
-
-
8
keys_to_keep = route.parts.reverse.drop_while { |part|
-
8
!options.key?(part) || (options[part] || recall[part]).nil?
-
} | route.required_parts
-
-
8
(parameterized_parts.keys - keys_to_keep).each do |bad_key|
-
24
parameterized_parts.delete(bad_key)
-
end
-
-
8
if parameterize
-
8
parameterized_parts.each do |k, v|
-
parameterized_parts[k] = parameterize.call(k, v)
-
end
-
end
-
-
8
parameterized_parts.keep_if { |_, v| v }
-
8
parameterized_parts
-
end
-
-
1
def named_routes
-
8
routes.named_routes
-
end
-
-
1
def match_route(name, options)
-
8
if named_routes.key?(name)
-
yield named_routes[name]
-
else
-
8
routes = non_recursive(cache, options.to_a)
-
-
24
hash = routes.group_by { |_, r| r.score(options) }
-
-
8
hash.keys.sort.reverse_each do |score|
-
8
next if score < 0
-
-
16
hash[score].sort_by { |i, _| i }.each do |_, route|
-
8
yield route
-
end
-
end
-
end
-
end
-
-
1
def non_recursive(cache, options)
-
8
routes = []
-
8
stack = [cache]
-
-
8
while stack.any?
-
24
c = stack.shift
-
24
routes.concat(c[:___routes]) if c.key?(:___routes)
-
-
24
options.each do |pair|
-
72
stack << c[pair] if c.key?(pair)
-
end
-
end
-
-
8
routes
-
end
-
-
# Returns an array populated with missing keys if any are present.
-
1
def missing_keys(route, parts)
-
8
missing_keys = []
-
8
tests = route.path.requirements
-
8
route.required_parts.each { |key|
-
if tests.key?(key)
-
missing_keys << key unless /\A#{tests[key]}\Z/ === parts[key]
-
else
-
missing_keys << key unless parts[key]
-
end
-
}
-
8
missing_keys
-
end
-
-
1
def possibles(cache, options, depth = 0)
-
cache.fetch(:___routes) { [] } + options.find_all { |pair|
-
cache.key?(pair)
-
}.map { |pair|
-
possibles(cache[pair], options, depth + 1)
-
}.flatten(1)
-
end
-
-
# Returns +true+ if no missing keys are present, otherwise +false+.
-
1
def verify_required_parts!(route, parts)
-
missing_keys(route, parts).empty?
-
end
-
-
1
def build_cache
-
1
root = { ___routes: [] }
-
1
routes.each_with_index do |route, i|
-
59
leaf = route.required_defaults.inject(root) do |h, tuple|
-
116
h[tuple] ||= {}
-
end
-
59
(leaf[:___routes] ||= []) << [i, route]
-
end
-
1
root
-
end
-
-
1
def cache
-
8
@cache ||= build_cache
-
end
-
end
-
end
-
end
-
1
require 'action_dispatch/journey/gtg/transition_table'
-
-
1
module ActionDispatch
-
1
module Journey # :nodoc:
-
1
module GTG # :nodoc:
-
1
class Builder # :nodoc:
-
1
DUMMY = Nodes::Dummy.new
-
-
1
attr_reader :root, :ast, :endpoints
-
-
1
def initialize(root)
-
59
@root = root
-
59
@ast = Nodes::Cat.new root, DUMMY
-
59
@followpos = nil
-
end
-
-
1
def transition_table
-
dtrans = TransitionTable.new
-
marked = {}
-
state_id = Hash.new { |h,k| h[k] = h.length }
-
-
start = firstpos(root)
-
dstates = [start]
-
until dstates.empty?
-
s = dstates.shift
-
next if marked[s]
-
marked[s] = true # mark s
-
-
s.group_by { |state| symbol(state) }.each do |sym, ps|
-
u = ps.map { |l| followpos(l) }.flatten
-
next if u.empty?
-
-
if u.uniq == [DUMMY]
-
from = state_id[s]
-
to = state_id[Object.new]
-
dtrans[from, to] = sym
-
-
dtrans.add_accepting(to)
-
ps.each { |state| dtrans.add_memo(to, state.memo) }
-
else
-
dtrans[state_id[s], state_id[u]] = sym
-
-
if u.include?(DUMMY)
-
to = state_id[u]
-
-
accepting = ps.find_all { |l| followpos(l).include?(DUMMY) }
-
-
accepting.each { |accepting_state|
-
dtrans.add_memo(to, accepting_state.memo)
-
}
-
-
dtrans.add_accepting(state_id[u])
-
end
-
end
-
-
dstates << u
-
end
-
end
-
-
dtrans
-
end
-
-
1
def nullable?(node)
-
1352
case node
-
when Nodes::Group
-
57
true
-
when Nodes::Star
-
true
-
when Nodes::Or
-
node.children.any? { |c| nullable?(c) }
-
when Nodes::Cat
-
nullable?(node.left) && nullable?(node.right)
-
when Nodes::Terminal
-
1295
!node.left
-
when Nodes::Unary
-
nullable?(node.left)
-
else
-
raise ArgumentError, 'unknown nullable: %s' % node.class.name
-
end
-
end
-
-
1
def firstpos(node)
-
565
case node
-
when Nodes::Star
-
firstpos(node.left)
-
when Nodes::Cat
-
57
if nullable?(node.left)
-
firstpos(node.left) | firstpos(node.right)
-
else
-
57
firstpos(node.left)
-
end
-
when Nodes::Or
-
node.children.map { |c| firstpos(c) }.flatten.uniq
-
when Nodes::Unary
-
57
firstpos(node.left)
-
when Nodes::Terminal
-
451
nullable?(node) ? [] : [node]
-
else
-
raise ArgumentError, 'unknown firstpos: %s' % node.class.name
-
end
-
end
-
-
1
def lastpos(node)
-
901
case node
-
when Nodes::Star
-
firstpos(node.left)
-
when Nodes::Or
-
node.children.map { |c| lastpos(c) }.flatten.uniq
-
when Nodes::Cat
-
393
if nullable?(node.right)
-
57
lastpos(node.left) | lastpos(node.right)
-
else
-
336
lastpos(node.right)
-
end
-
when Nodes::Terminal
-
451
nullable?(node) ? [] : [node]
-
when Nodes::Unary
-
57
lastpos(node.left)
-
else
-
raise ArgumentError, 'unknown lastpos: %s' % node.class.name
-
end
-
end
-
-
1
def followpos(node)
-
197
followpos_table[node]
-
end
-
-
1
private
-
-
1
def followpos_table
-
197
@followpos ||= build_followpos
-
end
-
-
1
def build_followpos
-
452
table = Hash.new { |h, k| h[k] = [] }
-
58
@ast.each do |n|
-
903
case n
-
when Nodes::Cat
-
394
lastpos(n.left).each do |i|
-
451
table[i] += firstpos(n.right)
-
end
-
when Nodes::Star
-
lastpos(n).each do |i|
-
table[i] += firstpos(n)
-
end
-
end
-
end
-
58
table
-
end
-
-
1
def symbol(edge)
-
case edge
-
when Journey::Nodes::Symbol
-
edge.regexp
-
else
-
edge.left
-
end
-
end
-
end
-
end
-
end
-
end
-
1
require 'strscan'
-
-
1
module ActionDispatch
-
1
module Journey # :nodoc:
-
1
module GTG # :nodoc:
-
1
class MatchData # :nodoc:
-
1
attr_reader :memos
-
-
1
def initialize(memos)
-
@memos = memos
-
end
-
end
-
-
1
class Simulator # :nodoc:
-
1
attr_reader :tt
-
-
1
def initialize(transition_table)
-
@tt = transition_table
-
end
-
-
1
def simulate(string)
-
input = StringScanner.new(string)
-
state = [0]
-
while sym = input.scan(%r([/.?]|[^/.?]+))
-
state = tt.move(state, sym)
-
end
-
-
acceptance_states = state.find_all { |s|
-
tt.accepting? s
-
}
-
-
return if acceptance_states.empty?
-
-
memos = acceptance_states.map { |x| tt.memo(x) }.flatten.compact
-
-
MatchData.new(memos)
-
end
-
-
1
alias :=~ :simulate
-
1
alias :match :simulate
-
end
-
end
-
end
-
end
-
1
require 'action_dispatch/journey/nfa/dot'
-
-
1
module ActionDispatch
-
1
module Journey # :nodoc:
-
1
module GTG # :nodoc:
-
1
class TransitionTable # :nodoc:
-
1
include Journey::NFA::Dot
-
-
1
attr_reader :memos
-
-
1
def initialize
-
@regexp_states = {}
-
@string_states = {}
-
@accepting = {}
-
@memos = Hash.new { |h,k| h[k] = [] }
-
end
-
-
1
def add_accepting(state)
-
@accepting[state] = true
-
end
-
-
1
def accepting_states
-
@accepting.keys
-
end
-
-
1
def accepting?(state)
-
@accepting[state]
-
end
-
-
1
def add_memo(idx, memo)
-
@memos[idx] << memo
-
end
-
-
1
def memo(idx)
-
@memos[idx]
-
end
-
-
1
def eclosure(t)
-
Array(t)
-
end
-
-
1
def move(t, a)
-
move_string(t, a).concat(move_regexp(t, a))
-
end
-
-
1
def as_json(options = nil)
-
simple_regexp = Hash.new { |h,k| h[k] = {} }
-
-
@regexp_states.each do |from, hash|
-
hash.each do |re, to|
-
simple_regexp[from][re.source] = to
-
end
-
end
-
-
{
-
regexp_states: simple_regexp,
-
string_states: @string_states,
-
accepting: @accepting
-
}
-
end
-
-
1
def to_svg
-
svg = IO.popen('dot -Tsvg', 'w+') { |f|
-
f.write(to_dot)
-
f.close_write
-
f.readlines
-
}
-
3.times { svg.shift }
-
svg.join.sub(/width="[^"]*"/, '').sub(/height="[^"]*"/, '')
-
end
-
-
1
def visualizer(paths, title = 'FSM')
-
viz_dir = File.join File.dirname(__FILE__), '..', 'visualizer'
-
fsm_js = File.read File.join(viz_dir, 'fsm.js')
-
fsm_css = File.read File.join(viz_dir, 'fsm.css')
-
erb = File.read File.join(viz_dir, 'index.html.erb')
-
states = "function tt() { return #{to_json}; }"
-
-
fun_routes = paths.shuffle.first(3).map do |ast|
-
ast.map { |n|
-
case n
-
when Nodes::Symbol
-
case n.left
-
when ':id' then rand(100).to_s
-
when ':format' then %w{ xml json }.shuffle.first
-
else
-
'omg'
-
end
-
when Nodes::Terminal then n.symbol
-
else
-
nil
-
end
-
}.compact.join
-
end
-
-
stylesheets = [fsm_css]
-
svg = to_svg
-
javascripts = [states, fsm_js]
-
-
# Annoying hack for 1.9 warnings
-
fun_routes = fun_routes
-
stylesheets = stylesheets
-
svg = svg
-
javascripts = javascripts
-
-
require 'erb'
-
template = ERB.new erb
-
template.result(binding)
-
end
-
-
1
def []=(from, to, sym)
-
to_mappings = states_hash_for(sym)[from] ||= {}
-
to_mappings[sym] = to
-
end
-
-
1
def states
-
ss = @string_states.keys + @string_states.values.map(&:values).flatten
-
rs = @regexp_states.keys + @regexp_states.values.map(&:values).flatten
-
(ss + rs).uniq
-
end
-
-
1
def transitions
-
@string_states.map { |from, hash|
-
hash.map { |s, to| [from, s, to] }
-
}.flatten(1) + @regexp_states.map { |from, hash|
-
hash.map { |s, to| [from, s, to] }
-
}.flatten(1)
-
end
-
-
1
private
-
-
1
def states_hash_for(sym)
-
case sym
-
when String
-
@string_states
-
when Regexp
-
@regexp_states
-
else
-
raise ArgumentError, 'unknown symbol: %s' % sym.class
-
end
-
end
-
-
1
def move_regexp(t, a)
-
return [] if t.empty?
-
-
t.map { |s|
-
if states = @regexp_states[s]
-
states.map { |re, v| re === a ? v : nil }
-
end
-
}.flatten.compact.uniq
-
end
-
-
1
def move_string(t, a)
-
return [] if t.empty?
-
-
t.map do |s|
-
if states = @string_states[s]
-
states[a]
-
end
-
end.compact
-
end
-
end
-
end
-
end
-
end
-
1
require 'action_dispatch/journey/nfa/transition_table'
-
1
require 'action_dispatch/journey/gtg/transition_table'
-
-
1
module ActionDispatch
-
1
module Journey # :nodoc:
-
1
module NFA # :nodoc:
-
1
class Visitor < Visitors::Visitor # :nodoc:
-
1
def initialize(tt)
-
@tt = tt
-
@i = -1
-
end
-
-
1
def visit_CAT(node)
-
left = visit(node.left)
-
right = visit(node.right)
-
-
@tt.merge(left.last, right.first)
-
-
[left.first, right.last]
-
end
-
-
1
def visit_GROUP(node)
-
from = @i += 1
-
left = visit(node.left)
-
to = @i += 1
-
-
@tt.accepting = to
-
-
@tt[from, left.first] = nil
-
@tt[left.last, to] = nil
-
@tt[from, to] = nil
-
-
[from, to]
-
end
-
-
1
def visit_OR(node)
-
from = @i += 1
-
children = node.children.map { |c| visit(c) }
-
to = @i += 1
-
-
children.each do |child|
-
@tt[from, child.first] = nil
-
@tt[child.last, to] = nil
-
end
-
-
@tt.accepting = to
-
-
[from, to]
-
end
-
-
1
def terminal(node)
-
from_i = @i += 1 # new state
-
to_i = @i += 1 # new state
-
-
@tt[from_i, to_i] = node
-
@tt.accepting = to_i
-
@tt.add_memo(to_i, node.memo)
-
-
[from_i, to_i]
-
end
-
end
-
-
1
class Builder # :nodoc:
-
1
def initialize(ast)
-
@ast = ast
-
end
-
-
1
def transition_table
-
tt = TransitionTable.new
-
Visitor.new(tt).accept(@ast)
-
tt
-
end
-
end
-
end
-
end
-
end
-
# encoding: utf-8
-
-
1
module ActionDispatch
-
1
module Journey # :nodoc:
-
1
module NFA # :nodoc:
-
1
module Dot # :nodoc:
-
1
def to_dot
-
edges = transitions.map { |from, sym, to|
-
" #{from} -> #{to} [label=\"#{sym || 'ε'}\"];"
-
}
-
-
#memo_nodes = memos.values.flatten.map { |n|
-
# label = n
-
# if Journey::Route === n
-
# label = "#{n.verb.source} #{n.path.spec}"
-
# end
-
# " #{n.object_id} [label=\"#{label}\", shape=box];"
-
#}
-
#memo_edges = memos.map { |k, memos|
-
# (memos || []).map { |v| " #{k} -> #{v.object_id};" }
-
#}.flatten.uniq
-
-
<<-eodot
-
digraph nfa {
-
rankdir=LR;
-
node [shape = doublecircle];
-
#{accepting_states.join ' '};
-
node [shape = circle];
-
#{edges.join "\n"}
-
}
-
eodot
-
end
-
end
-
end
-
end
-
end
-
1
require 'strscan'
-
-
1
module ActionDispatch
-
1
module Journey # :nodoc:
-
1
module NFA # :nodoc:
-
1
class MatchData # :nodoc:
-
1
attr_reader :memos
-
-
1
def initialize(memos)
-
@memos = memos
-
end
-
end
-
-
1
class Simulator # :nodoc:
-
1
attr_reader :tt
-
-
1
def initialize(transition_table)
-
@tt = transition_table
-
end
-
-
1
def simulate(string)
-
input = StringScanner.new(string)
-
state = tt.eclosure(0)
-
until input.eos?
-
sym = input.scan(%r([/.?]|[^/.?]+))
-
-
# FIXME: tt.eclosure is not needed for the GTG
-
state = tt.eclosure(tt.move(state, sym))
-
end
-
-
acceptance_states = state.find_all { |s|
-
tt.accepting?(tt.eclosure(s).sort.last)
-
}
-
-
return if acceptance_states.empty?
-
-
memos = acceptance_states.map { |x| tt.memo(x) }.flatten.compact
-
-
MatchData.new(memos)
-
end
-
-
1
alias :=~ :simulate
-
1
alias :match :simulate
-
end
-
end
-
end
-
end
-
1
require 'action_dispatch/journey/nfa/dot'
-
-
1
module ActionDispatch
-
1
module Journey # :nodoc:
-
1
module NFA # :nodoc:
-
1
class TransitionTable # :nodoc:
-
1
include Journey::NFA::Dot
-
-
1
attr_accessor :accepting
-
1
attr_reader :memos
-
-
1
def initialize
-
@table = Hash.new { |h,f| h[f] = {} }
-
@memos = {}
-
@accepting = nil
-
@inverted = nil
-
end
-
-
1
def accepting?(state)
-
accepting == state
-
end
-
-
1
def accepting_states
-
[accepting]
-
end
-
-
1
def add_memo(idx, memo)
-
@memos[idx] = memo
-
end
-
-
1
def memo(idx)
-
@memos[idx]
-
end
-
-
1
def []=(i, f, s)
-
@table[f][i] = s
-
end
-
-
1
def merge(left, right)
-
@memos[right] = @memos.delete(left)
-
@table[right] = @table.delete(left)
-
end
-
-
1
def states
-
(@table.keys + @table.values.map(&:keys).flatten).uniq
-
end
-
-
# Returns a generalized transition graph with reduced states. The states
-
# are reduced like a DFA, but the table must be simulated like an NFA.
-
#
-
# Edges of the GTG are regular expressions.
-
1
def generalized_table
-
gt = GTG::TransitionTable.new
-
marked = {}
-
state_id = Hash.new { |h,k| h[k] = h.length }
-
alphabet = self.alphabet
-
-
stack = [eclosure(0)]
-
-
until stack.empty?
-
state = stack.pop
-
next if marked[state] || state.empty?
-
-
marked[state] = true
-
-
alphabet.each do |alpha|
-
next_state = eclosure(following_states(state, alpha))
-
next if next_state.empty?
-
-
gt[state_id[state], state_id[next_state]] = alpha
-
stack << next_state
-
end
-
end
-
-
final_groups = state_id.keys.find_all { |s|
-
s.sort.last == accepting
-
}
-
-
final_groups.each do |states|
-
id = state_id[states]
-
-
gt.add_accepting(id)
-
save = states.find { |s|
-
@memos.key?(s) && eclosure(s).sort.last == accepting
-
}
-
-
gt.add_memo(id, memo(save))
-
end
-
-
gt
-
end
-
-
# Returns set of NFA states to which there is a transition on ast symbol
-
# +a+ from some state +s+ in +t+.
-
1
def following_states(t, a)
-
Array(t).map { |s| inverted[s][a] }.flatten.uniq
-
end
-
-
# Returns set of NFA states to which there is a transition on ast symbol
-
# +a+ from some state +s+ in +t+.
-
1
def move(t, a)
-
Array(t).map { |s|
-
inverted[s].keys.compact.find_all { |sym|
-
sym === a
-
}.map { |sym| inverted[s][sym] }
-
}.flatten.uniq
-
end
-
-
1
def alphabet
-
inverted.values.map(&:keys).flatten.compact.uniq.sort_by { |x| x.to_s }
-
end
-
-
# Returns a set of NFA states reachable from some NFA state +s+ in set
-
# +t+ on nil-transitions alone.
-
1
def eclosure(t)
-
stack = Array(t)
-
seen = {}
-
children = []
-
-
until stack.empty?
-
s = stack.pop
-
next if seen[s]
-
-
seen[s] = true
-
children << s
-
-
stack.concat(inverted[s][nil])
-
end
-
-
children.uniq
-
end
-
-
1
def transitions
-
@table.map { |to, hash|
-
hash.map { |from, sym| [from, sym, to] }
-
}.flatten(1)
-
end
-
-
1
private
-
-
1
def inverted
-
return @inverted if @inverted
-
-
@inverted = Hash.new { |h, from|
-
h[from] = Hash.new { |j, s| j[s] = [] }
-
}
-
-
@table.each { |to, hash|
-
hash.each { |from, sym|
-
if sym
-
sym = Nodes::Symbol === sym ? sym.regexp : sym.left
-
end
-
-
@inverted[from][sym] << to
-
}
-
}
-
-
@inverted
-
end
-
end
-
end
-
end
-
end
-
#
-
# DO NOT MODIFY!!!!
-
# This file is automatically generated by Racc 1.4.9
-
# from Racc grammar file "".
-
#
-
-
1
require 'racc/parser.rb'
-
-
-
1
require 'action_dispatch/journey/parser_extras'
-
1
module ActionDispatch
-
1
module Journey # :nodoc:
-
1
class Parser < Racc::Parser # :nodoc:
-
##### State transition tables begin ###
-
-
1
racc_action_table = [
-
17, 21, 13, 15, 14, 7, nil, 16, 8, 19,
-
13, 15, 14, 7, 23, 16, 8, 19, 13, 15,
-
14, 7, nil, 16, 8, 13, 15, 14, 7, nil,
-
16, 8, 13, 15, 14, 7, nil, 16, 8 ]
-
-
1
racc_action_check = [
-
1, 17, 1, 1, 1, 1, nil, 1, 1, 1,
-
20, 20, 20, 20, 20, 20, 20, 20, 7, 7,
-
7, 7, nil, 7, 7, 19, 19, 19, 19, nil,
-
19, 19, 0, 0, 0, 0, nil, 0, 0 ]
-
-
1
racc_action_pointer = [
-
30, 0, nil, nil, nil, nil, nil, 16, nil, nil,
-
nil, nil, nil, nil, nil, nil, nil, 1, nil, 23,
-
8, nil, nil, nil ]
-
-
1
racc_action_default = [
-
-18, -18, -2, -3, -4, -5, -6, -18, -9, -10,
-
-11, -12, -13, -14, -15, -16, -17, -18, -1, -18,
-
-18, 24, -8, -7 ]
-
-
1
racc_goto_table = [
-
18, 1, nil, nil, nil, nil, nil, nil, 20, nil,
-
nil, nil, nil, nil, nil, nil, nil, nil, 22, 18 ]
-
-
1
racc_goto_check = [
-
2, 1, nil, nil, nil, nil, nil, nil, 1, nil,
-
nil, nil, nil, nil, nil, nil, nil, nil, 2, 2 ]
-
-
1
racc_goto_pointer = [
-
nil, 1, -1, nil, nil, nil, nil, nil, nil, nil,
-
nil ]
-
-
1
racc_goto_default = [
-
nil, nil, 2, 3, 4, 5, 6, 9, 10, 11,
-
12 ]
-
-
1
racc_reduce_table = [
-
0, 0, :racc_error,
-
2, 11, :_reduce_1,
-
1, 11, :_reduce_2,
-
1, 11, :_reduce_none,
-
1, 12, :_reduce_none,
-
1, 12, :_reduce_none,
-
1, 12, :_reduce_none,
-
3, 15, :_reduce_7,
-
3, 13, :_reduce_8,
-
1, 16, :_reduce_9,
-
1, 14, :_reduce_none,
-
1, 14, :_reduce_none,
-
1, 14, :_reduce_none,
-
1, 14, :_reduce_none,
-
1, 19, :_reduce_14,
-
1, 17, :_reduce_15,
-
1, 18, :_reduce_16,
-
1, 20, :_reduce_17 ]
-
-
1
racc_reduce_n = 18
-
-
1
racc_shift_n = 24
-
-
1
racc_token_table = {
-
false => 0,
-
:error => 1,
-
:SLASH => 2,
-
:LITERAL => 3,
-
:SYMBOL => 4,
-
:LPAREN => 5,
-
:RPAREN => 6,
-
:DOT => 7,
-
:STAR => 8,
-
:OR => 9 }
-
-
1
racc_nt_base = 10
-
-
1
racc_use_result_var = true
-
-
1
Racc_arg = [
-
racc_action_table,
-
racc_action_check,
-
racc_action_default,
-
racc_action_pointer,
-
racc_goto_table,
-
racc_goto_check,
-
racc_goto_default,
-
racc_goto_pointer,
-
racc_nt_base,
-
racc_reduce_table,
-
racc_token_table,
-
racc_shift_n,
-
racc_reduce_n,
-
racc_use_result_var ]
-
-
1
Racc_token_to_s_table = [
-
"$end",
-
"error",
-
"SLASH",
-
"LITERAL",
-
"SYMBOL",
-
"LPAREN",
-
"RPAREN",
-
"DOT",
-
"STAR",
-
"OR",
-
"$start",
-
"expressions",
-
"expression",
-
"or",
-
"terminal",
-
"group",
-
"star",
-
"symbol",
-
"literal",
-
"slash",
-
"dot" ]
-
-
1
Racc_debug_parser = false
-
-
##### State transition tables end #####
-
-
# reduce 0 omitted
-
-
1
def _reduce_1(val, _values, result)
-
672
result = Cat.new(val.first, val.last)
-
672
result
-
end
-
-
1
def _reduce_2(val, _values, result)
-
232
result = val.first
-
232
result
-
end
-
-
# reduce 3 omitted
-
-
# reduce 4 omitted
-
-
# reduce 5 omitted
-
-
# reduce 6 omitted
-
-
1
def _reduce_7(val, _values, result)
-
114
result = Group.new(val[1])
-
114
result
-
end
-
-
1
def _reduce_8(val, _values, result)
-
result = Or.new([val.first, val.last])
-
result
-
end
-
-
1
def _reduce_9(val, _values, result)
-
result = Star.new(Symbol.new(val.last))
-
result
-
end
-
-
# reduce 10 omitted
-
-
# reduce 11 omitted
-
-
# reduce 12 omitted
-
-
# reduce 13 omitted
-
-
1
def _reduce_14(val, _values, result)
-
282
result = Slash.new('/')
-
282
result
-
end
-
-
1
def _reduce_15(val, _values, result)
-
194
result = Symbol.new(val.first)
-
194
result
-
end
-
-
1
def _reduce_16(val, _values, result)
-
200
result = Literal.new(val.first)
-
200
result
-
end
-
-
1
def _reduce_17(val, _values, result)
-
114
result = Dot.new(val.first)
-
114
result
-
end
-
-
1
def _reduce_none(val, _values, result)
-
val[0]
-
end
-
-
end # class Parser
-
end # module Journey
-
end # module ActionDispatch
-
1
require 'action_dispatch/journey/scanner'
-
1
require 'action_dispatch/journey/nodes/node'
-
-
1
module ActionDispatch
-
1
module Journey # :nodoc:
-
1
class Parser < Racc::Parser # :nodoc:
-
1
include Journey::Nodes
-
-
1
def initialize
-
118
@scanner = Scanner.new
-
end
-
-
1
def parse(string)
-
118
@scanner.scan_setup(string)
-
118
do_parse
-
end
-
-
1
def next_token
-
1136
@scanner.next_token
-
end
-
end
-
end
-
end
-
1
module ActionDispatch
-
1
module Journey # :nodoc:
-
1
module Path # :nodoc:
-
1
class Pattern # :nodoc:
-
1
attr_reader :spec, :requirements, :anchored
-
-
1
def initialize(strexp)
-
118
parser = Journey::Parser.new
-
-
118
@anchored = true
-
-
118
case strexp
-
when String
-
@spec = parser.parse(strexp)
-
@requirements = {}
-
@separators = "/.?"
-
when Router::Strexp
-
118
@spec = parser.parse(strexp.path)
-
118
@requirements = strexp.requirements
-
118
@separators = strexp.separators.join
-
118
@anchored = strexp.anchor
-
else
-
raise ArgumentError, "Bad expression: #{strexp}"
-
end
-
-
118
@names = nil
-
118
@optional_names = nil
-
118
@required_names = nil
-
118
@re = nil
-
118
@offsets = nil
-
end
-
-
1
def ast
-
@spec.grep(Nodes::Symbol).each do |node|
-
re = @requirements[node.to_sym]
-
node.regexp = re if re
-
end
-
-
@spec.grep(Nodes::Star).each do |node|
-
node = node.left
-
node.regexp = @requirements[node.to_sym] || /(.+)/
-
end
-
-
@spec
-
end
-
-
1
def names
-
426
@names ||= spec.grep(Nodes::Symbol).map { |n| n.name }
-
end
-
-
1
def required_names
-
55
@required_names ||= names - optional_names
-
end
-
-
1
def optional_names
-
@optional_names ||= spec.grep(Nodes::Group).map { |group|
-
38
group.grep(Nodes::Symbol)
-
78
}.flatten.map { |n| n.name }.uniq
-
end
-
-
1
class RegexpOffsets < Journey::Visitors::Visitor # :nodoc:
-
1
attr_reader :offsets
-
-
1
def initialize(matchers)
-
@matchers = matchers
-
@capture_count = [0]
-
end
-
-
1
def visit(node)
-
super
-
@capture_count
-
end
-
-
1
def visit_SYMBOL(node)
-
node = node.to_sym
-
-
if @matchers.key?(node)
-
re = /#{@matchers[node]}|/
-
@capture_count.push((re.match('').length - 1) + (@capture_count.last || 0))
-
else
-
@capture_count << (@capture_count.last || 0)
-
end
-
end
-
end
-
-
1
class AnchoredRegexp < Journey::Visitors::Visitor # :nodoc:
-
1
def initialize(separator, matchers)
-
@separator = separator
-
@matchers = matchers
-
@separator_re = "([^#{separator}]+)"
-
super()
-
end
-
-
1
def accept(node)
-
%r{\A#{visit node}\Z}
-
end
-
-
1
def visit_CAT(node)
-
[visit(node.left), visit(node.right)].join
-
end
-
-
1
def visit_SYMBOL(node)
-
node = node.to_sym
-
-
return @separator_re unless @matchers.key?(node)
-
-
re = @matchers[node]
-
"(#{re})"
-
end
-
-
1
def visit_GROUP(node)
-
"(?:#{visit node.left})?"
-
end
-
-
1
def visit_LITERAL(node)
-
Regexp.escape(node.left)
-
end
-
1
alias :visit_DOT :visit_LITERAL
-
-
1
def visit_SLASH(node)
-
node.left
-
end
-
-
1
def visit_STAR(node)
-
re = @matchers[node.left.to_sym] || '.+'
-
"(#{re})"
-
end
-
end
-
-
1
class UnanchoredRegexp < AnchoredRegexp # :nodoc:
-
1
def accept(node)
-
%r{\A#{visit node}}
-
end
-
end
-
-
1
class MatchData # :nodoc:
-
1
attr_reader :names
-
-
1
def initialize(names, offsets, match)
-
@names = names
-
@offsets = offsets
-
@match = match
-
end
-
-
1
def captures
-
(length - 1).times.map { |i| self[i + 1] }
-
end
-
-
1
def [](x)
-
idx = @offsets[x - 1] + x
-
@match[idx]
-
end
-
-
1
def length
-
@offsets.length
-
end
-
-
1
def post_match
-
@match.post_match
-
end
-
-
1
def to_s
-
@match.to_s
-
end
-
end
-
-
1
def match(other)
-
return unless match = to_regexp.match(other)
-
MatchData.new(names, offsets, match)
-
end
-
1
alias :=~ :match
-
-
1
def source
-
to_regexp.source
-
end
-
-
1
def to_regexp
-
@re ||= regexp_visitor.new(@separators, @requirements).accept spec
-
end
-
-
1
private
-
-
1
def regexp_visitor
-
@anchored ? AnchoredRegexp : UnanchoredRegexp
-
end
-
-
1
def offsets
-
return @offsets if @offsets
-
-
viz = RegexpOffsets.new(@requirements)
-
@offsets = viz.accept(spec)
-
end
-
end
-
end
-
end
-
end
-
1
module ActionDispatch
-
1
module Journey # :nodoc:
-
1
class Route # :nodoc:
-
1
attr_reader :app, :path, :defaults, :name
-
-
1
attr_reader :constraints
-
1
alias :conditions :constraints
-
-
1
attr_accessor :precedence
-
-
##
-
# +path+ is a path constraint.
-
# +constraints+ is a hash of constraints to be applied to this route.
-
1
def initialize(name, app, path, constraints, defaults = {})
-
59
@name = name
-
59
@app = app
-
59
@path = path
-
-
# Unwrap any constraints so we can see what's inside for route generation.
-
# This allows the formatter to skip over any mounted applications or redirects
-
# that shouldn't be matched when using a url_for without a route name.
-
59
while app.is_a?(Routing::Mapper::Constraints) do
-
app = app.app
-
end
-
59
@dispatcher = app.is_a?(Routing::RouteSet::Dispatcher)
-
-
59
@constraints = constraints
-
59
@defaults = defaults
-
59
@required_defaults = nil
-
59
@required_parts = nil
-
59
@parts = nil
-
59
@decorated_ast = nil
-
59
@precedence = 0
-
end
-
-
1
def ast
-
@decorated_ast ||= begin
-
decorated_ast = path.ast
-
decorated_ast.grep(Nodes::Terminal).each { |n| n.memo = self }
-
decorated_ast
-
end
-
end
-
-
1
def requirements # :nodoc:
-
# needed for rails `rake routes`
-
76
path.requirements.merge(@defaults).delete_if { |_,v|
-
152
/.+?/ == v
-
}
-
end
-
-
1
def segments
-
58
path.names
-
end
-
-
1
def required_keys
-
required_parts + required_defaults.keys
-
end
-
-
1
def score(constraints)
-
16
required_keys = path.required_names
-
64
supplied_keys = constraints.map { |k,v| v && k.to_s }.compact
-
-
16
return -1 unless (required_keys - supplied_keys).empty?
-
-
16
score = (supplied_keys & path.names).length
-
16
score + (required_defaults.length * 2)
-
end
-
-
1
def parts
-
297
@parts ||= segments.map { |n| n.to_sym }
-
end
-
1
alias :segment_keys :parts
-
-
1
def format(path_options)
-
8
path_options.delete_if do |key, value|
-
value.to_s == defaults[key].to_s && !required_parts.include?(key)
-
end
-
-
8
Visitors::Formatter.new(path_options).accept(path.spec)
-
end
-
-
1
def optimized_path
-
76
Visitors::OptimizedPath.new.accept(path.spec)
-
end
-
-
1
def optional_parts
-
path.optional_names.map { |n| n.to_sym }
-
end
-
-
1
def required_parts
-
113
@required_parts ||= path.required_names.map { |n| n.to_sym }
-
end
-
-
1
def required_default?(key)
-
116
(constraints[:required_defaults] || []).include?(key)
-
end
-
-
1
def required_defaults
-
@required_defaults ||= @defaults.dup.delete_if do |k,_|
-
116
parts.include?(k) || !required_default?(k)
-
75
end
-
end
-
-
1
def glob?
-
76
!path.spec.grep(Nodes::Star).empty?
-
end
-
-
1
def dispatcher?
-
8
@dispatcher
-
end
-
-
1
def matches?(request)
-
constraints.all? do |method, value|
-
next true unless request.respond_to?(method)
-
-
case value
-
when Regexp, String
-
value === request.send(method).to_s
-
when Array
-
value.include?(request.send(method))
-
when TrueClass
-
request.send(method).present?
-
when FalseClass
-
request.send(method).blank?
-
else
-
value === request.send(method)
-
end
-
end
-
end
-
-
1
def ip
-
constraints[:ip] || //
-
end
-
-
1
def verb
-
constraints[:request_method] || //
-
end
-
end
-
end
-
end
-
1
require 'action_dispatch/journey/router/utils'
-
1
require 'action_dispatch/journey/router/strexp'
-
1
require 'action_dispatch/journey/routes'
-
1
require 'action_dispatch/journey/formatter'
-
-
1
before = $-w
-
1
$-w = false
-
1
require 'action_dispatch/journey/parser'
-
1
$-w = before
-
-
1
require 'action_dispatch/journey/route'
-
1
require 'action_dispatch/journey/path/pattern'
-
-
1
module ActionDispatch
-
1
module Journey # :nodoc:
-
1
class Router # :nodoc:
-
1
class RoutingError < ::StandardError # :nodoc:
-
end
-
-
# :nodoc:
-
1
VERSION = '2.0.0'
-
-
1
class NullReq # :nodoc:
-
1
attr_reader :env
-
1
def initialize(env)
-
@env = env
-
end
-
-
1
def request_method
-
env['REQUEST_METHOD']
-
end
-
-
1
def path_info
-
env['PATH_INFO']
-
end
-
-
1
def ip
-
env['REMOTE_ADDR']
-
end
-
-
1
def [](k)
-
env[k]
-
end
-
end
-
-
1
attr_reader :request_class, :formatter
-
1
attr_accessor :routes
-
-
1
def initialize(routes, options)
-
1
@options = options
-
1
@params_key = options[:parameters_key]
-
1
@request_class = options[:request_class] || NullReq
-
1
@routes = routes
-
end
-
-
1
def call(env)
-
env['PATH_INFO'] = Utils.normalize_path(env['PATH_INFO'])
-
-
find_routes(env).each do |match, parameters, route|
-
script_name, path_info, set_params = env.values_at('SCRIPT_NAME',
-
'PATH_INFO',
-
@params_key)
-
-
unless route.path.anchored
-
env['SCRIPT_NAME'] = (script_name.to_s + match.to_s).chomp('/')
-
path_info = match.post_match
-
env['PATH_INFO'] = path_info
-
env['PATH_INFO'] = "/" + path_info unless path_info.start_with? "/"
-
end
-
-
env[@params_key] = (set_params || {}).merge parameters
-
-
status, headers, body = route.app.call(env)
-
-
if 'pass' == headers['X-Cascade']
-
env['SCRIPT_NAME'] = script_name
-
env['PATH_INFO'] = path_info
-
env[@params_key] = set_params
-
next
-
end
-
-
return [status, headers, body]
-
end
-
-
return [404, {'X-Cascade' => 'pass'}, ['Not Found']]
-
end
-
-
1
def recognize(req)
-
find_routes(req.env).each do |match, parameters, route|
-
unless route.path.anchored
-
req.env['SCRIPT_NAME'] = match.to_s
-
req.env['PATH_INFO'] = match.post_match.sub(/^([^\/])/, '/\1')
-
end
-
-
yield(route, nil, parameters)
-
end
-
end
-
-
1
def visualizer
-
tt = GTG::Builder.new(ast).transition_table
-
groups = partitioned_routes.first.map(&:ast).group_by { |a| a.to_s }
-
asts = groups.values.map { |v| v.first }
-
tt.visualizer(asts)
-
end
-
-
1
private
-
-
1
def partitioned_routes
-
routes.partitioned_routes
-
end
-
-
1
def ast
-
routes.ast
-
end
-
-
1
def simulator
-
routes.simulator
-
end
-
-
1
def custom_routes
-
partitioned_routes.last
-
end
-
-
1
def filter_routes(path)
-
return [] unless ast
-
data = simulator.match(path)
-
data ? data.memos : []
-
end
-
-
1
def find_routes env
-
req = request_class.new(env)
-
-
routes = filter_routes(req.path_info).concat custom_routes.find_all { |r|
-
r.path.match(req.path_info)
-
}
-
routes.concat get_routes_as_head(routes)
-
-
routes.sort_by!(&:precedence).select! { |r| r.matches?(req) }
-
-
routes.map! { |r|
-
match_data = r.path.match(req.path_info)
-
match_names = match_data.names.map { |n| n.to_sym }
-
match_values = match_data.captures.map { |v| v && Utils.unescape_uri(v) }
-
info = Hash[match_names.zip(match_values).find_all { |_, y| y }]
-
-
[match_data, r.defaults.merge(info), r]
-
}
-
end
-
-
1
def get_routes_as_head(routes)
-
precedence = (routes.map(&:precedence).max || 0) + 1
-
routes = routes.select { |r|
-
r.verb === "GET" && !(r.verb === "HEAD")
-
}.map! { |r|
-
Route.new(r.name,
-
r.app,
-
r.path,
-
r.conditions.merge(request_method: "HEAD"),
-
r.defaults).tap do |route|
-
route.precedence = r.precedence + precedence
-
end
-
}
-
routes.flatten!
-
routes
-
end
-
end
-
end
-
end
-
1
module ActionDispatch
-
1
module Journey # :nodoc:
-
1
class Router # :nodoc:
-
1
class Strexp # :nodoc:
-
1
class << self
-
1
alias :compile :new
-
end
-
-
1
attr_reader :path, :requirements, :separators, :anchor
-
-
1
def initialize(path, requirements, separators, anchor = true)
-
118
@path = path
-
118
@requirements = requirements
-
118
@separators = separators
-
118
@anchor = anchor
-
end
-
-
1
def names
-
@path.scan(/:\w+/).map { |s| s.tr(':', '') }
-
end
-
end
-
end
-
end
-
end
-
1
module ActionDispatch
-
1
module Journey # :nodoc:
-
1
class Router # :nodoc:
-
1
class Utils # :nodoc:
-
# Normalizes URI path.
-
#
-
# Strips off trailing slash and ensures there is a leading slash.
-
# Also converts downcase url encoded string to uppercase.
-
#
-
# normalize_path("/foo") # => "/foo"
-
# normalize_path("/foo/") # => "/foo"
-
# normalize_path("foo") # => "/foo"
-
# normalize_path("") # => "/"
-
# normalize_path("/%ab") # => "/%AB"
-
1
def self.normalize_path(path)
-
130
path = "/#{path}"
-
130
path.squeeze!('/')
-
130
path.sub!(%r{/+\Z}, '')
-
130
path.gsub!(/(%[a-f0-9]{2})/) { $1.upcase }
-
130
path = '/' if path == ''
-
130
path
-
end
-
-
# URI path and fragment escaping
-
# http://tools.ietf.org/html/rfc3986
-
1
class UriEncoder # :nodoc:
-
1
ENCODE = "%%%02X".freeze
-
1
US_ASCII = Encoding::US_ASCII
-
1
UTF_8 = Encoding::UTF_8
-
1
EMPTY = "".force_encoding(US_ASCII).freeze
-
513
DEC2HEX = (0..255).to_a.map{ |i| ENCODE % i }.map{ |s| s.force_encoding(US_ASCII) }
-
-
1
ALPHA = "a-zA-Z".freeze
-
1
DIGIT = "0-9".freeze
-
1
UNRESERVED = "#{ALPHA}#{DIGIT}\\-\\._~".freeze
-
1
SUB_DELIMS = "!\\$&'\\(\\)\\*\\+,;=".freeze
-
-
1
ESCAPED = /%[a-zA-Z0-9]{2}/.freeze
-
-
1
FRAGMENT = /[^#{UNRESERVED}#{SUB_DELIMS}:@\/\?]/.freeze
-
1
SEGMENT = /[^#{UNRESERVED}#{SUB_DELIMS}:@]/.freeze
-
1
PATH = /[^#{UNRESERVED}#{SUB_DELIMS}:@\/]/.freeze
-
-
1
def escape_fragment(fragment)
-
escape(fragment, FRAGMENT)
-
end
-
-
1
def escape_path(path)
-
escape(path, PATH)
-
end
-
-
1
def escape_segment(segment)
-
3
escape(segment, SEGMENT)
-
end
-
-
1
def unescape_uri(uri)
-
encoding = uri.encoding == US_ASCII ? UTF_8 : uri.encoding
-
uri.gsub(ESCAPED) { [$&[1, 2].hex].pack('C') }.force_encoding(encoding)
-
end
-
-
1
protected
-
1
def escape(component, pattern)
-
6
component.gsub(pattern){ |unsafe| percent_encode(unsafe) }.force_encoding(US_ASCII)
-
end
-
-
1
def percent_encode(unsafe)
-
3
safe = EMPTY.dup
-
6
unsafe.each_byte { |b| safe << DEC2HEX[b] }
-
3
safe
-
end
-
end
-
-
1
ENCODER = UriEncoder.new
-
-
1
def self.escape_path(path)
-
ENCODER.escape_path(path.to_s)
-
end
-
-
1
def self.escape_segment(segment)
-
3
ENCODER.escape_segment(segment.to_s)
-
end
-
-
1
def self.escape_fragment(fragment)
-
ENCODER.escape_fragment(fragment.to_s)
-
end
-
-
1
def self.unescape_uri(uri)
-
ENCODER.unescape_uri(uri)
-
end
-
end
-
end
-
end
-
end
-
1
module ActionDispatch
-
1
module Journey # :nodoc:
-
# The Routing table. Contains all routes for a system. Routes can be
-
# added to the table by calling Routes#add_route.
-
1
class Routes # :nodoc:
-
1
include Enumerable
-
-
1
attr_reader :routes, :named_routes
-
-
1
def initialize
-
1
@routes = []
-
1
@named_routes = {}
-
1
@ast = nil
-
1
@partitioned_routes = nil
-
1
@simulator = nil
-
end
-
-
1
def length
-
routes.length
-
end
-
1
alias :size :length
-
-
1
def last
-
routes.last
-
end
-
-
1
def each(&block)
-
55
routes.each(&block)
-
end
-
-
1
def clear
-
1
routes.clear
-
1
named_routes.clear
-
end
-
-
1
def partitioned_routes
-
@partitioned_routes ||= routes.partition do |r|
-
r.path.anchored && r.ast.grep(Nodes::Symbol).all?(&:default_regexp?)
-
end
-
end
-
-
1
def ast
-
@ast ||= begin
-
asts = partitioned_routes.first.map(&:ast)
-
Nodes::Or.new(asts) unless asts.empty?
-
end
-
end
-
-
1
def simulator
-
@simulator ||= begin
-
gtg = GTG::Builder.new(ast).transition_table
-
GTG::Simulator.new(gtg)
-
end
-
end
-
-
# Add a route to the routing table.
-
1
def add_route(app, path, conditions, defaults, name = nil)
-
59
route = Route.new(name, app, path, conditions, defaults)
-
-
59
route.precedence = routes.length
-
59
routes << route
-
59
named_routes[name] = route if name && !named_routes[name]
-
59
clear_cache!
-
59
route
-
end
-
-
1
private
-
-
1
def clear_cache!
-
59
@ast = nil
-
59
@partitioned_routes = nil
-
59
@simulator = nil
-
end
-
end
-
end
-
end
-
1
require 'strscan'
-
-
1
module ActionDispatch
-
1
module Journey # :nodoc:
-
1
class Scanner # :nodoc:
-
1
def initialize
-
118
@ss = nil
-
end
-
-
1
def scan_setup(str)
-
118
@ss = StringScanner.new(str)
-
end
-
-
1
def eos?
-
@ss.eos?
-
end
-
-
1
def pos
-
@ss.pos
-
end
-
-
1
def pre_match
-
@ss.pre_match
-
end
-
-
1
def next_token
-
1136
return if @ss.eos?
-
-
1018
until token = scan || @ss.eos?; end
-
1018
token
-
end
-
-
1
private
-
-
1
def scan
-
case
-
# /
-
when text = @ss.scan(/\//)
-
282
[:SLASH, text]
-
when text = @ss.scan(/\*\w+/)
-
[:STAR, text]
-
when text = @ss.scan(/\(/)
-
114
[:LPAREN, text]
-
when text = @ss.scan(/\)/)
-
114
[:RPAREN, text]
-
when text = @ss.scan(/\|/)
-
[:OR, text]
-
when text = @ss.scan(/\./)
-
114
[:DOT, text]
-
when text = @ss.scan(/:\w+/)
-
194
[:SYMBOL, text]
-
when text = @ss.scan(/[\w%\-~]+/)
-
200
[:LITERAL, text]
-
# any char
-
when text = @ss.scan(/./)
-
[:LITERAL, text]
-
1018
end
-
end
-
end
-
end
-
end
-
# encoding: utf-8
-
-
1
require 'thread_safe'
-
-
1
module ActionDispatch
-
1
module Journey # :nodoc:
-
1
module Visitors # :nodoc:
-
1
class Visitor # :nodoc:
-
1
DISPATCH_CACHE = ThreadSafe::Cache.new { |h,k|
-
6
h[k] = :"visit_#{k}"
-
}
-
-
1
def accept(node)
-
456
visit(node)
-
end
-
-
1
private
-
-
1
def visit node
-
6661
send(DISPATCH_CACHE[node.type], node)
-
end
-
-
1
def binary(node)
-
2467
visit(node.left)
-
2467
visit(node.right)
-
end
-
2468
def visit_CAT(n); binary(n); end
-
-
1
def nary(node)
-
node.children.each { |c| visit(c) }
-
end
-
1
def visit_OR(n); nary(n); end
-
-
1
def unary(node)
-
435
visit(node.left)
-
end
-
436
def visit_GROUP(n); unary(n); end
-
1
def visit_STAR(n); unary(n); end
-
-
1
def terminal(node); end
-
1
%w{ LITERAL SYMBOL SLASH DOT }.each do |t|
-
4
class_eval %{ def visit_#{t}(n); terminal(n); end }, __FILE__, __LINE__
-
end
-
end
-
-
# Loop through the requirements AST
-
1
class Each < Visitor # :nodoc:
-
1
attr_reader :block
-
-
1
def initialize(block)
-
448
@block = block
-
end
-
-
1
def visit(node)
-
5817
super
-
5817
block.call(node)
-
end
-
end
-
-
1
class String < Visitor # :nodoc:
-
1
private
-
-
1
def binary(node)
-
[visit(node.left), visit(node.right)].join
-
end
-
-
1
def nary(node)
-
node.children.map { |c| visit(c) }.join '|'
-
end
-
-
1
def terminal(node)
-
node.left
-
end
-
-
1
def visit_GROUP(node)
-
"(#{visit(node.left)})"
-
end
-
end
-
-
1
class OptimizedPath < Visitor # :nodoc:
-
1
def accept(node)
-
76
Array(visit(node))
-
end
-
-
1
private
-
-
1
def visit_CAT(node)
-
384
[visit(node.left), visit(node.right)].flatten
-
end
-
-
1
def visit_SYMBOL(node)
-
42
node.left[1..-1].to_sym
-
end
-
-
1
def visit_STAR(node)
-
visit(node.left)
-
end
-
-
1
def visit_GROUP(node)
-
74
[]
-
end
-
-
1
%w{ LITERAL SLASH DOT }.each do |t|
-
3
class_eval %{ def visit_#{t}(n); n.left; end }, __FILE__, __LINE__
-
end
-
end
-
-
# Used for formatting urls (url_for)
-
1
class Formatter < Visitor # :nodoc:
-
1
attr_reader :options
-
-
1
def initialize(options)
-
8
@options = options
-
end
-
-
1
private
-
1
def escape_path(value)
-
Router::Utils.escape_path(value)
-
end
-
-
1
def escape_segment(value)
-
Router::Utils.escape_segment(value)
-
end
-
-
1
def visit(node, optional = false)
-
80
case node.type
-
when :LITERAL, :SLASH, :DOT
-
32
node.left
-
when :STAR
-
visit_STAR(node.left)
-
when :GROUP
-
8
visit(node.left, true)
-
when :CAT
-
32
visit_CAT(node, optional)
-
when :SYMBOL
-
8
visit_SYMBOL(node, node.to_sym)
-
end
-
end
-
-
1
def visit_CAT(node, optional)
-
32
left = visit(node.left, optional)
-
32
right = visit(node.right, optional)
-
-
32
if optional && !(right && left)
-
8
""
-
else
-
24
[left, right].join
-
end
-
end
-
-
1
def visit_STAR(node)
-
if value = options[node.to_sym]
-
escape_path(value)
-
end
-
end
-
-
1
def visit_SYMBOL(node, name)
-
8
if value = options[name]
-
name == :controller ? escape_path(value) : escape_segment(value)
-
end
-
end
-
end
-
-
1
class Dot < Visitor # :nodoc:
-
1
def initialize
-
@nodes = []
-
@edges = []
-
end
-
-
1
def accept(node)
-
super
-
<<-eodot
-
digraph parse_tree {
-
size="8,5"
-
node [shape = none];
-
edge [dir = none];
-
#{@nodes.join "\n"}
-
#{@edges.join("\n")}
-
}
-
eodot
-
end
-
-
1
private
-
-
1
def binary(node)
-
node.children.each do |c|
-
@edges << "#{node.object_id} -> #{c.object_id};"
-
end
-
super
-
end
-
-
1
def nary(node)
-
node.children.each do |c|
-
@edges << "#{node.object_id} -> #{c.object_id};"
-
end
-
super
-
end
-
-
1
def unary(node)
-
@edges << "#{node.object_id} -> #{node.left.object_id};"
-
super
-
end
-
-
1
def visit_GROUP(node)
-
@nodes << "#{node.object_id} [label=\"()\"];"
-
super
-
end
-
-
1
def visit_CAT(node)
-
@nodes << "#{node.object_id} [label=\"○\"];"
-
super
-
end
-
-
1
def visit_STAR(node)
-
@nodes << "#{node.object_id} [label=\"*\"];"
-
super
-
end
-
-
1
def visit_OR(node)
-
@nodes << "#{node.object_id} [label=\"|\"];"
-
super
-
end
-
-
1
def terminal(node)
-
value = node.left
-
-
@nodes << "#{node.object_id} [label=\"#{value}\"];"
-
end
-
end
-
end
-
end
-
end
-
-
1
module ActionDispatch
-
# Provide callbacks to be executed before and after the request dispatch.
-
1
class Callbacks
-
1
include ActiveSupport::Callbacks
-
-
1
define_callbacks :call
-
-
1
class << self
-
1
delegate :to_prepare, :to_cleanup, :to => "ActionDispatch::Reloader"
-
-
1
def before(*args, &block)
-
set_callback(:call, :before, *args, &block)
-
end
-
-
1
def after(*args, &block)
-
set_callback(:call, :after, *args, &block)
-
end
-
end
-
-
1
def initialize(app)
-
1
@app = app
-
end
-
-
1
def call(env)
-
error = nil
-
result = run_callbacks :call do
-
begin
-
@app.call(env)
-
rescue => error
-
end
-
end
-
raise error if error
-
result
-
end
-
end
-
end
-
1
require 'active_support/core_ext/hash/keys'
-
1
require 'active_support/core_ext/module/attribute_accessors'
-
1
require 'active_support/core_ext/object/blank'
-
1
require 'active_support/key_generator'
-
1
require 'active_support/message_verifier'
-
-
1
module ActionDispatch
-
1
class Request < Rack::Request
-
1
def cookie_jar
-
24
env['action_dispatch.cookies'] ||= Cookies::CookieJar.build(self)
-
end
-
end
-
-
# \Cookies are read and written through ActionController#cookies.
-
#
-
# The cookies being read are the ones received along with the request, the cookies
-
# being written will be sent out with the response. Reading a cookie does not get
-
# the cookie object itself back, just the value it holds.
-
#
-
# Examples of writing:
-
#
-
# # Sets a simple session cookie.
-
# # This cookie will be deleted when the user's browser is closed.
-
# cookies[:user_name] = "david"
-
#
-
# # Cookie values are String based. Other data types need to be serialized.
-
# cookies[:lat_lon] = JSON.generate([47.68, -122.37])
-
#
-
# # Sets a cookie that expires in 1 hour.
-
# cookies[:login] = { value: "XJ-122", expires: 1.hour.from_now }
-
#
-
# # Sets a signed cookie, which prevents users from tampering with its value.
-
# # The cookie is signed by your app's `secrets.secret_key_base` value.
-
# # It can be read using the signed method `cookies.signed[:name]`
-
# cookies.signed[:user_id] = current_user.id
-
#
-
# # Sets a "permanent" cookie (which expires in 20 years from now).
-
# cookies.permanent[:login] = "XJ-122"
-
#
-
# # You can also chain these methods:
-
# cookies.permanent.signed[:login] = "XJ-122"
-
#
-
# Examples of reading:
-
#
-
# cookies[:user_name] # => "david"
-
# cookies.size # => 2
-
# JSON.parse(cookies[:lat_lon]) # => [47.68, -122.37]
-
# cookies.signed[:login] # => "XJ-122"
-
#
-
# Example for deleting:
-
#
-
# cookies.delete :user_name
-
#
-
# Please note that if you specify a :domain when setting a cookie, you must also specify the domain when deleting the cookie:
-
#
-
# cookies[:name] = {
-
# value: 'a yummy cookie',
-
# expires: 1.year.from_now,
-
# domain: 'domain.com'
-
# }
-
#
-
# cookies.delete(:name, domain: 'domain.com')
-
#
-
# The option symbols for setting cookies are:
-
#
-
# * <tt>:value</tt> - The cookie's value.
-
# * <tt>:path</tt> - The path for which this cookie applies. Defaults to the root
-
# of the application.
-
# * <tt>:domain</tt> - The domain for which this cookie applies so you can
-
# restrict to the domain level. If you use a schema like www.example.com
-
# and want to share session with user.example.com set <tt>:domain</tt>
-
# to <tt>:all</tt>. Make sure to specify the <tt>:domain</tt> option with
-
# <tt>:all</tt> again when deleting cookies.
-
#
-
# domain: nil # Does not sets cookie domain. (default)
-
# domain: :all # Allow the cookie for the top most level
-
# domain and subdomains.
-
#
-
# * <tt>:expires</tt> - The time at which this cookie expires, as a \Time object.
-
# * <tt>:secure</tt> - Whether this cookie is only transmitted to HTTPS servers.
-
# Default is +false+.
-
# * <tt>:httponly</tt> - Whether this cookie is accessible via scripting or
-
# only HTTP. Defaults to +false+.
-
1
class Cookies
-
1
HTTP_HEADER = "Set-Cookie".freeze
-
1
GENERATOR_KEY = "action_dispatch.key_generator".freeze
-
1
SIGNED_COOKIE_SALT = "action_dispatch.signed_cookie_salt".freeze
-
1
ENCRYPTED_COOKIE_SALT = "action_dispatch.encrypted_cookie_salt".freeze
-
1
ENCRYPTED_SIGNED_COOKIE_SALT = "action_dispatch.encrypted_signed_cookie_salt".freeze
-
1
SECRET_TOKEN = "action_dispatch.secret_token".freeze
-
1
SECRET_KEY_BASE = "action_dispatch.secret_key_base".freeze
-
1
COOKIES_SERIALIZER = "action_dispatch.cookies_serializer".freeze
-
-
# Cookies can typically store 4096 bytes.
-
1
MAX_COOKIE_SIZE = 4096
-
-
# Raised when storing more than 4K of session data.
-
1
CookieOverflow = Class.new StandardError
-
-
# Include in a cookie jar to allow chaining, e.g. cookies.permanent.signed
-
1
module ChainedCookieJars
-
# Returns a jar that'll automatically set the assigned cookies to have an expiration date 20 years from now. Example:
-
#
-
# cookies.permanent[:prefers_open_id] = true
-
# # => Set-Cookie: prefers_open_id=true; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT
-
#
-
# This jar is only meant for writing. You'll read permanent cookies through the regular accessor.
-
#
-
# This jar allows chaining with the signed jar as well, so you can set permanent, signed cookies. Examples:
-
#
-
# cookies.permanent.signed[:remember_me] = current_user.id
-
# # => Set-Cookie: remember_me=BAhU--848956038e692d7046deab32b7131856ab20e14e; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT
-
1
def permanent
-
@permanent ||= PermanentCookieJar.new(self, @key_generator, @options)
-
end
-
-
# Returns a jar that'll automatically generate a signed representation of cookie value and verify it when reading from
-
# the cookie again. This is useful for creating cookies with values that the user is not supposed to change. If a signed
-
# cookie was tampered with by the user (or a 3rd party), nil will be returned.
-
#
-
# If +secrets.secret_key_base+ and +config.secret_token+ (deprecated) are both set,
-
# legacy cookies signed with the old key generator will be transparently upgraded.
-
#
-
# This jar requires that you set a suitable secret for the verification on your app's +secrets.secret_key_base+.
-
#
-
# Example:
-
#
-
# cookies.signed[:discount] = 45
-
# # => Set-Cookie: discount=BAhpMg==--2c1c6906c90a3bc4fd54a51ffb41dffa4bf6b5f7; path=/
-
#
-
# cookies.signed[:discount] # => 45
-
1
def signed
-
@signed ||=
-
if @options[:upgrade_legacy_signed_cookies]
-
UpgradeLegacySignedCookieJar.new(self, @key_generator, @options)
-
else
-
SignedCookieJar.new(self, @key_generator, @options)
-
end
-
end
-
-
# Returns a jar that'll automatically encrypt cookie values before sending them to the client and will decrypt them for read.
-
# If the cookie was tampered with by the user (or a 3rd party), nil will be returned.
-
#
-
# If +secrets.secret_key_base+ and +config.secret_token+ (deprecated) are both set,
-
# legacy cookies signed with the old key generator will be transparently upgraded.
-
#
-
# This jar requires that you set a suitable secret for the verification on your app's +secrets.secret_key_base+.
-
#
-
# Example:
-
#
-
# cookies.encrypted[:discount] = 45
-
# # => Set-Cookie: discount=ZS9ZZ1R4cG1pcUJ1bm80anhQang3dz09LS1mbDZDSU5scGdOT3ltQ2dTdlhSdWpRPT0%3D--ab54663c9f4e3bc340c790d6d2b71e92f5b60315; path=/
-
#
-
# cookies.encrypted[:discount] # => 45
-
1
def encrypted
-
@encrypted ||=
-
if @options[:upgrade_legacy_signed_cookies]
-
UpgradeLegacyEncryptedCookieJar.new(self, @key_generator, @options)
-
else
-
EncryptedCookieJar.new(self, @key_generator, @options)
-
end
-
end
-
-
# Returns the +signed+ or +encrypted+ jar, preferring +encrypted+ if +secret_key_base+ is set.
-
# Used by ActionDispatch::Session::CookieStore to avoid the need to introduce new cookie stores.
-
1
def signed_or_encrypted
-
@signed_or_encrypted ||=
-
if @options[:secret_key_base].present?
-
encrypted
-
else
-
signed
-
end
-
end
-
end
-
-
1
module VerifyAndUpgradeLegacySignedMessage
-
1
def initialize(*args)
-
super
-
@legacy_verifier = ActiveSupport::MessageVerifier.new(@options[:secret_token], serializer: NullSerializer)
-
end
-
-
1
def verify_and_upgrade_legacy_signed_message(name, signed_message)
-
deserialize(name, @legacy_verifier.verify(signed_message)).tap do |value|
-
self[name] = { value: value }
-
end
-
rescue ActiveSupport::MessageVerifier::InvalidSignature
-
nil
-
end
-
end
-
-
1
class CookieJar #:nodoc:
-
1
include Enumerable, ChainedCookieJars
-
-
# This regular expression is used to split the levels of a domain.
-
# The top level domain can be any string without a period or
-
# **.**, ***.** style TLDs like co.uk or com.au
-
#
-
# www.example.co.uk gives:
-
# $& => example.co.uk
-
#
-
# example.com gives:
-
# $& => example.com
-
#
-
# lots.of.subdomains.example.local gives:
-
# $& => example.local
-
1
DOMAIN_REGEXP = /[^.]*\.([^.]*|..\...|...\...)$/
-
-
1
def self.options_for_env(env) #:nodoc:
-
{ signed_cookie_salt: env[SIGNED_COOKIE_SALT] || '',
-
encrypted_cookie_salt: env[ENCRYPTED_COOKIE_SALT] || '',
-
encrypted_signed_cookie_salt: env[ENCRYPTED_SIGNED_COOKIE_SALT] || '',
-
secret_token: env[SECRET_TOKEN],
-
secret_key_base: env[SECRET_KEY_BASE],
-
upgrade_legacy_signed_cookies: env[SECRET_TOKEN].present? && env[SECRET_KEY_BASE].present?,
-
serializer: env[COOKIES_SERIALIZER]
-
4
}
-
end
-
-
1
def self.build(request)
-
4
env = request.env
-
4
key_generator = env[GENERATOR_KEY]
-
4
options = options_for_env env
-
-
4
host = request.host
-
4
secure = request.ssl?
-
-
4
new(key_generator, host, secure, options).tap do |hash|
-
4
hash.update(request.cookies)
-
end
-
end
-
-
1
def initialize(key_generator, host = nil, secure = false, options = {})
-
4
@key_generator = key_generator
-
4
@set_cookies = {}
-
4
@delete_cookies = {}
-
4
@host = host
-
4
@secure = secure
-
4
@options = options
-
4
@cookies = {}
-
4
@committed = false
-
end
-
-
1
def committed?; @committed; end
-
-
1
def commit!
-
@committed = true
-
@set_cookies.freeze
-
@delete_cookies.freeze
-
end
-
-
1
def each(&block)
-
@cookies.each(&block)
-
end
-
-
# Returns the value of the cookie by +name+, or +nil+ if no such cookie exists.
-
1
def [](name)
-
@cookies[name.to_s]
-
end
-
-
1
def fetch(name, *args, &block)
-
@cookies.fetch(name.to_s, *args, &block)
-
end
-
-
1
def key?(name)
-
@cookies.key?(name.to_s)
-
end
-
1
alias :has_key? :key?
-
-
1
def update(other_hash)
-
16
@cookies.update other_hash.stringify_keys
-
16
self
-
end
-
-
1
def handle_options(options) #:nodoc:
-
options[:path] ||= "/"
-
-
if options[:domain] == :all
-
# if there is a provided tld length then we use it otherwise default domain regexp
-
domain_regexp = options[:tld_length] ? /([^.]+\.?){#{options[:tld_length]}}$/ : DOMAIN_REGEXP
-
-
# if host is not ip and matches domain regexp
-
# (ip confirms to domain regexp so we explicitly check for ip)
-
options[:domain] = if (@host !~ /^[\d.]+$/) && (@host =~ domain_regexp)
-
".#{$&}"
-
end
-
elsif options[:domain].is_a? Array
-
# if host matches one of the supplied domains without a dot in front of it
-
options[:domain] = options[:domain].find {|domain| @host.include? domain.sub(/^\./, '') }
-
end
-
end
-
-
# Sets the cookie named +name+. The second argument may be the very cookie
-
# value, or a hash of options as documented above.
-
1
def []=(name, options)
-
if options.is_a?(Hash)
-
options.symbolize_keys!
-
value = options[:value]
-
else
-
value = options
-
options = { :value => value }
-
end
-
-
handle_options(options)
-
-
if @cookies[name.to_s] != value or options[:expires]
-
@cookies[name.to_s] = value
-
@set_cookies[name.to_s] = options
-
@delete_cookies.delete(name.to_s)
-
end
-
-
value
-
end
-
-
# Removes the cookie on the client machine by setting the value to an empty string
-
# and the expiration date in the past. Like <tt>[]=</tt>, you can pass in
-
# an options hash to delete cookies with extra data such as a <tt>:path</tt>.
-
1
def delete(name, options = {})
-
return unless @cookies.has_key? name.to_s
-
-
options.symbolize_keys!
-
handle_options(options)
-
-
value = @cookies.delete(name.to_s)
-
@delete_cookies[name.to_s] = options
-
value
-
end
-
-
# Whether the given cookie is to be deleted by this CookieJar.
-
# Like <tt>[]=</tt>, you can pass in an options hash to test if a
-
# deletion applies to a specific <tt>:path</tt>, <tt>:domain</tt> etc.
-
1
def deleted?(name, options = {})
-
options.symbolize_keys!
-
handle_options(options)
-
@delete_cookies[name.to_s] == options
-
end
-
-
# Removes all cookies on the client machine by calling <tt>delete</tt> for each cookie
-
1
def clear(options = {})
-
@cookies.each_key{ |k| delete(k, options) }
-
end
-
-
1
def write(headers)
-
4
@set_cookies.each { |k, v| ::Rack::Utils.set_cookie_header!(headers, k, v) if write_cookie?(v) }
-
4
@delete_cookies.each { |k, v| ::Rack::Utils.delete_cookie_header!(headers, k, v) }
-
end
-
-
1
def recycle! #:nodoc:
-
4
@set_cookies = {}
-
4
@delete_cookies = {}
-
end
-
-
1
mattr_accessor :always_write_cookie
-
1
self.always_write_cookie = false
-
-
1
private
-
1
def write_cookie?(cookie)
-
@secure || !cookie[:secure] || always_write_cookie
-
end
-
end
-
-
1
class PermanentCookieJar #:nodoc:
-
1
include ChainedCookieJars
-
-
1
def initialize(parent_jar, key_generator, options = {})
-
@parent_jar = parent_jar
-
@key_generator = key_generator
-
@options = options
-
end
-
-
1
def [](name)
-
@parent_jar[name.to_s]
-
end
-
-
1
def []=(name, options)
-
if options.is_a?(Hash)
-
options.symbolize_keys!
-
else
-
options = { :value => options }
-
end
-
-
options[:expires] = 20.years.from_now
-
@parent_jar[name] = options
-
end
-
end
-
-
1
class JsonSerializer
-
1
def self.load(value)
-
JSON.parse(value, quirks_mode: true)
-
end
-
-
1
def self.dump(value)
-
JSON.generate(value, quirks_mode: true)
-
end
-
end
-
-
# Passing the NullSerializer downstream to the Message{Encryptor,Verifier}
-
# allows us to handle the (de)serialization step within the cookie jar,
-
# which gives us the opportunity to detect and migrate legacy cookies.
-
1
class NullSerializer
-
1
def self.load(value)
-
value
-
end
-
-
1
def self.dump(value)
-
value
-
end
-
end
-
-
1
module SerializedCookieJars
-
1
MARSHAL_SIGNATURE = "\x04\x08".freeze
-
-
1
protected
-
1
def needs_migration?(value)
-
@options[:serializer] == :hybrid && value.start_with?(MARSHAL_SIGNATURE)
-
end
-
-
1
def serialize(name, value)
-
serializer.dump(value)
-
end
-
-
1
def deserialize(name, value)
-
if value
-
if needs_migration?(value)
-
Marshal.load(value).tap do |v|
-
self[name] = { value: v }
-
end
-
else
-
serializer.load(value)
-
end
-
end
-
end
-
-
1
def serializer
-
serializer = @options[:serializer] || :marshal
-
case serializer
-
when :marshal
-
Marshal
-
when :json, :hybrid
-
JsonSerializer
-
else
-
serializer
-
end
-
end
-
end
-
-
1
class SignedCookieJar #:nodoc:
-
1
include ChainedCookieJars
-
1
include SerializedCookieJars
-
-
1
def initialize(parent_jar, key_generator, options = {})
-
@parent_jar = parent_jar
-
@options = options
-
secret = key_generator.generate_key(@options[:signed_cookie_salt])
-
@verifier = ActiveSupport::MessageVerifier.new(secret, serializer: NullSerializer)
-
end
-
-
1
def [](name)
-
if signed_message = @parent_jar[name]
-
deserialize name, verify(signed_message)
-
end
-
end
-
-
1
def []=(name, options)
-
if options.is_a?(Hash)
-
options.symbolize_keys!
-
options[:value] = @verifier.generate(serialize(name, options[:value]))
-
else
-
options = { :value => @verifier.generate(serialize(name, options)) }
-
end
-
-
raise CookieOverflow if options[:value].size > MAX_COOKIE_SIZE
-
@parent_jar[name] = options
-
end
-
-
1
private
-
1
def verify(signed_message)
-
@verifier.verify(signed_message)
-
rescue ActiveSupport::MessageVerifier::InvalidSignature
-
nil
-
end
-
end
-
-
# UpgradeLegacySignedCookieJar is used instead of SignedCookieJar if
-
# config.secret_token and secrets.secret_key_base are both set. It reads
-
# legacy cookies signed with the old dummy key generator and re-saves
-
# them using the new key generator to provide a smooth upgrade path.
-
1
class UpgradeLegacySignedCookieJar < SignedCookieJar #:nodoc:
-
1
include VerifyAndUpgradeLegacySignedMessage
-
-
1
def [](name)
-
if signed_message = @parent_jar[name]
-
deserialize(name, verify(signed_message)) || verify_and_upgrade_legacy_signed_message(name, signed_message)
-
end
-
end
-
end
-
-
1
class EncryptedCookieJar #:nodoc:
-
1
include ChainedCookieJars
-
1
include SerializedCookieJars
-
-
1
def initialize(parent_jar, key_generator, options = {})
-
if ActiveSupport::LegacyKeyGenerator === key_generator
-
raise "You didn't set secrets.secret_key_base, which is required for this cookie jar. " +
-
"Read the upgrade documentation to learn more about this new config option."
-
end
-
-
@parent_jar = parent_jar
-
@options = options
-
secret = key_generator.generate_key(@options[:encrypted_cookie_salt])
-
sign_secret = key_generator.generate_key(@options[:encrypted_signed_cookie_salt])
-
@encryptor = ActiveSupport::MessageEncryptor.new(secret, sign_secret, serializer: NullSerializer)
-
end
-
-
1
def [](name)
-
if encrypted_message = @parent_jar[name]
-
deserialize name, decrypt_and_verify(encrypted_message)
-
end
-
end
-
-
1
def []=(name, options)
-
if options.is_a?(Hash)
-
options.symbolize_keys!
-
else
-
options = { :value => options }
-
end
-
-
options[:value] = @encryptor.encrypt_and_sign(serialize(name, options[:value]))
-
-
raise CookieOverflow if options[:value].size > MAX_COOKIE_SIZE
-
@parent_jar[name] = options
-
end
-
-
1
private
-
1
def decrypt_and_verify(encrypted_message)
-
@encryptor.decrypt_and_verify(encrypted_message)
-
rescue ActiveSupport::MessageVerifier::InvalidSignature, ActiveSupport::MessageEncryptor::InvalidMessage
-
nil
-
end
-
end
-
-
# UpgradeLegacyEncryptedCookieJar is used by ActionDispatch::Session::CookieStore
-
# instead of EncryptedCookieJar if config.secret_token and secrets.secret_key_base
-
# are both set. It reads legacy cookies signed with the old dummy key generator and
-
# encrypts and re-saves them using the new key generator to provide a smooth upgrade path.
-
1
class UpgradeLegacyEncryptedCookieJar < EncryptedCookieJar #:nodoc:
-
1
include VerifyAndUpgradeLegacySignedMessage
-
-
1
def [](name)
-
if encrypted_or_signed_message = @parent_jar[name]
-
deserialize(name, decrypt_and_verify(encrypted_or_signed_message)) || verify_and_upgrade_legacy_signed_message(name, encrypted_or_signed_message)
-
end
-
end
-
end
-
-
1
def initialize(app)
-
1
@app = app
-
end
-
-
1
def call(env)
-
status, headers, body = @app.call(env)
-
-
if cookie_jar = env['action_dispatch.cookies']
-
unless cookie_jar.committed?
-
cookie_jar.write(headers)
-
if headers[HTTP_HEADER].respond_to?(:join)
-
headers[HTTP_HEADER] = headers[HTTP_HEADER].join("\n")
-
end
-
end
-
end
-
-
[status, headers, body]
-
end
-
end
-
end
-
1
require 'action_dispatch/http/request'
-
1
require 'action_dispatch/middleware/exception_wrapper'
-
1
require 'action_dispatch/routing/inspector'
-
-
1
module ActionDispatch
-
# This middleware is responsible for logging exceptions and
-
# showing a debugging page in case the request is local.
-
1
class DebugExceptions
-
1
RESCUES_TEMPLATE_PATH = File.expand_path('../templates', __FILE__)
-
-
1
def initialize(app, routes_app = nil)
-
1
@app = app
-
1
@routes_app = routes_app
-
end
-
-
1
def call(env)
-
_, headers, body = response = @app.call(env)
-
-
if headers['X-Cascade'] == 'pass'
-
body.close if body.respond_to?(:close)
-
raise ActionController::RoutingError, "No route matches [#{env['REQUEST_METHOD']}] #{env['PATH_INFO'].inspect}"
-
end
-
-
response
-
rescue Exception => exception
-
raise exception if env['action_dispatch.show_exceptions'] == false
-
render_exception(env, exception)
-
end
-
-
1
private
-
-
1
def render_exception(env, exception)
-
wrapper = ExceptionWrapper.new(env, exception)
-
log_error(env, wrapper)
-
-
if env['action_dispatch.show_detailed_exceptions']
-
request = Request.new(env)
-
template = ActionView::Base.new([RESCUES_TEMPLATE_PATH],
-
request: request,
-
exception: wrapper.exception,
-
application_trace: wrapper.application_trace,
-
framework_trace: wrapper.framework_trace,
-
full_trace: wrapper.full_trace,
-
routes_inspector: routes_inspector(exception),
-
source_extract: wrapper.source_extract,
-
line_number: wrapper.line_number,
-
file: wrapper.file
-
)
-
file = "rescues/#{wrapper.rescue_template}"
-
-
if request.xhr?
-
body = template.render(template: file, layout: false, formats: [:text])
-
format = "text/plain"
-
else
-
body = template.render(template: file, layout: 'rescues/layout')
-
format = "text/html"
-
end
-
render(wrapper.status_code, body, format)
-
else
-
raise exception
-
end
-
end
-
-
1
def render(status, body, format)
-
[status, {'Content-Type' => "#{format}; charset=#{Response.default_charset}", 'Content-Length' => body.bytesize.to_s}, [body]]
-
end
-
-
1
def log_error(env, wrapper)
-
logger = logger(env)
-
return unless logger
-
-
exception = wrapper.exception
-
-
trace = wrapper.application_trace
-
trace = wrapper.framework_trace if trace.empty?
-
-
ActiveSupport::Deprecation.silence do
-
message = "\n#{exception.class} (#{exception.message}):\n"
-
message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code)
-
message << " " << trace.join("\n ")
-
logger.fatal("#{message}\n\n")
-
end
-
end
-
-
1
def logger(env)
-
env['action_dispatch.logger'] || stderr_logger
-
end
-
-
1
def stderr_logger
-
@stderr_logger ||= ActiveSupport::Logger.new($stderr)
-
end
-
-
1
def routes_inspector(exception)
-
if @routes_app.respond_to?(:routes) && (exception.is_a?(ActionController::RoutingError) || exception.is_a?(ActionView::Template::Error))
-
ActionDispatch::Routing::RoutesInspector.new(@routes_app.routes.routes)
-
end
-
end
-
end
-
end
-
1
require 'action_controller/metal/exceptions'
-
1
require 'active_support/core_ext/module/attribute_accessors'
-
-
1
module ActionDispatch
-
1
class ExceptionWrapper
-
1
cattr_accessor :rescue_responses
-
1
@@rescue_responses = Hash.new(:internal_server_error)
-
1
@@rescue_responses.merge!(
-
'ActionController::RoutingError' => :not_found,
-
'AbstractController::ActionNotFound' => :not_found,
-
'ActionController::MethodNotAllowed' => :method_not_allowed,
-
'ActionController::UnknownHttpMethod' => :method_not_allowed,
-
'ActionController::NotImplemented' => :not_implemented,
-
'ActionController::UnknownFormat' => :not_acceptable,
-
'ActionController::InvalidAuthenticityToken' => :unprocessable_entity,
-
'ActionDispatch::ParamsParser::ParseError' => :bad_request,
-
'ActionController::BadRequest' => :bad_request,
-
'ActionController::ParameterMissing' => :bad_request
-
)
-
-
1
cattr_accessor :rescue_templates
-
1
@@rescue_templates = Hash.new('diagnostics')
-
1
@@rescue_templates.merge!(
-
'ActionView::MissingTemplate' => 'missing_template',
-
'ActionController::RoutingError' => 'routing_error',
-
'AbstractController::ActionNotFound' => 'unknown_action',
-
'ActionView::Template::Error' => 'template_error'
-
)
-
-
1
attr_reader :env, :exception, :line_number, :file
-
-
1
def initialize(env, exception)
-
@env = env
-
@exception = original_exception(exception)
-
-
expand_backtrace if exception.is_a?(SyntaxError) || exception.try(:original_exception).try(:is_a?, SyntaxError)
-
end
-
-
1
def rescue_template
-
@@rescue_templates[@exception.class.name]
-
end
-
-
1
def status_code
-
self.class.status_code_for_exception(@exception.class.name)
-
end
-
-
1
def application_trace
-
clean_backtrace(:silent)
-
end
-
-
1
def framework_trace
-
clean_backtrace(:noise)
-
end
-
-
1
def full_trace
-
clean_backtrace(:all)
-
end
-
-
1
def self.status_code_for_exception(class_name)
-
Rack::Utils.status_code(@@rescue_responses[class_name])
-
end
-
-
1
def source_extract
-
if application_trace && trace = application_trace.first
-
file, line, _ = trace.split(":")
-
@file = file
-
@line_number = line.to_i
-
source_fragment(@file, @line_number)
-
end
-
end
-
-
1
private
-
-
1
def original_exception(exception)
-
if registered_original_exception?(exception)
-
exception.original_exception
-
else
-
exception
-
end
-
end
-
-
1
def registered_original_exception?(exception)
-
exception.respond_to?(:original_exception) && @@rescue_responses.has_key?(exception.original_exception.class.name)
-
end
-
-
1
def clean_backtrace(*args)
-
if backtrace_cleaner
-
backtrace_cleaner.clean(@exception.backtrace, *args)
-
else
-
@exception.backtrace
-
end
-
end
-
-
1
def backtrace_cleaner
-
@backtrace_cleaner ||= @env['action_dispatch.backtrace_cleaner']
-
end
-
-
1
def source_fragment(path, line)
-
return unless Rails.respond_to?(:root) && Rails.root
-
full_path = Rails.root.join(path)
-
if File.exist?(full_path)
-
File.open(full_path, "r") do |file|
-
start = [line - 3, 0].max
-
lines = file.each_line.drop(start).take(6)
-
Hash[*(start+1..(lines.count+start)).zip(lines).flatten]
-
end
-
end
-
end
-
-
1
def expand_backtrace
-
@exception.backtrace.unshift(
-
@exception.to_s.split("\n")
-
).flatten!
-
end
-
end
-
end
-
1
require 'active_support/core_ext/hash/keys'
-
-
1
module ActionDispatch
-
1
class Request < Rack::Request
-
# Access the contents of the flash. Use <tt>flash["notice"]</tt> to
-
# read a notice you put there or <tt>flash["notice"] = "hello"</tt>
-
# to put a new one.
-
1
def flash
-
8
@env[Flash::KEY] ||= Flash::FlashHash.from_session_value(session["flash"])
-
end
-
end
-
-
# The flash provides a way to pass temporary objects between actions. Anything you place in the flash will be exposed
-
# to the very next action and then cleared out. This is a great way of doing notices and alerts, such as a create
-
# action that sets <tt>flash[:notice] = "Post successfully created"</tt> before redirecting to a display action that can
-
# then expose the flash to its template. Actually, that exposure is automatically done.
-
#
-
# class PostsController < ActionController::Base
-
# def create
-
# # save post
-
# flash[:notice] = "Post successfully created"
-
# redirect_to @post
-
# end
-
#
-
# def show
-
# # doesn't need to assign the flash notice to the template, that's done automatically
-
# end
-
# end
-
#
-
# show.html.erb
-
# <% if flash[:notice] %>
-
# <div class="notice"><%= flash[:notice] %></div>
-
# <% end %>
-
#
-
# Since the +notice+ and +alert+ keys are a common idiom, convenience accessors are available:
-
#
-
# flash.alert = "You must be logged in"
-
# flash.notice = "Post successfully created"
-
#
-
# This example just places a string in the flash, but you can put any object in there. And of course, you can put as
-
# many as you like at a time too. Just remember: They'll be gone by the time the next action has been performed.
-
#
-
# See docs on the FlashHash class for more details about the flash.
-
1
class Flash
-
1
KEY = 'action_dispatch.request.flash_hash'.freeze
-
-
1
class FlashNow #:nodoc:
-
1
attr_accessor :flash
-
-
1
def initialize(flash)
-
@flash = flash
-
end
-
-
1
def []=(k, v)
-
k = k.to_s
-
@flash[k] = v
-
@flash.discard(k)
-
v
-
end
-
-
1
def [](k)
-
@flash[k.to_s]
-
end
-
-
# Convenience accessor for <tt>flash.now[:alert]=</tt>.
-
1
def alert=(message)
-
self[:alert] = message
-
end
-
-
# Convenience accessor for <tt>flash.now[:notice]=</tt>.
-
1
def notice=(message)
-
self[:notice] = message
-
end
-
end
-
-
1
class FlashHash
-
1
include Enumerable
-
-
1
def self.from_session_value(value)
-
4
flash = case value
-
when FlashHash # Rails 3.1, 3.2
-
new(value.instance_variable_get(:@flashes), value.instance_variable_get(:@used))
-
when Hash # Rails 4.0
-
new(value['flashes'], value['discard'])
-
else
-
4
new
-
end
-
-
4
flash.tap(&:sweep)
-
end
-
-
1
def to_session_value
-
4
return nil if empty?
-
{'discard' => @discard.to_a, 'flashes' => @flashes}
-
end
-
-
1
def initialize(flashes = {}, discard = []) #:nodoc:
-
4
@discard = Set.new(stringify_array(discard))
-
4
@flashes = flashes.stringify_keys
-
4
@now = nil
-
end
-
-
1
def initialize_copy(other)
-
if other.now_is_loaded?
-
@now = other.now.dup
-
@now.flash = self
-
end
-
super
-
end
-
-
1
def []=(k, v)
-
k = k.to_s
-
@discard.delete k
-
@flashes[k] = v
-
end
-
-
1
def [](k)
-
@flashes[k.to_s]
-
end
-
-
1
def update(h) #:nodoc:
-
4
@discard.subtract stringify_array(h.keys)
-
4
@flashes.update h.stringify_keys
-
4
self
-
end
-
-
1
def keys
-
@flashes.keys
-
end
-
-
1
def key?(name)
-
@flashes.key? name
-
end
-
-
1
def delete(key)
-
key = key.to_s
-
@discard.delete key
-
@flashes.delete key
-
self
-
end
-
-
1
def to_hash
-
@flashes.dup
-
end
-
-
1
def empty?
-
4
@flashes.empty?
-
end
-
-
1
def clear
-
@discard.clear
-
@flashes.clear
-
end
-
-
1
def each(&block)
-
@flashes.each(&block)
-
end
-
-
1
alias :merge! :update
-
-
1
def replace(h) #:nodoc:
-
@discard.clear
-
@flashes.replace h.stringify_keys
-
self
-
end
-
-
# Sets a flash that will not be available to the next action, only to the current.
-
#
-
# flash.now[:message] = "Hello current action"
-
#
-
# This method enables you to use the flash as a central messaging system in your app.
-
# When you need to pass an object to the next action, you use the standard flash assign (<tt>[]=</tt>).
-
# When you need to pass an object to the current action, you use <tt>now</tt>, and your object will
-
# vanish when the current action is done.
-
#
-
# Entries set via <tt>now</tt> are accessed the same way as standard entries: <tt>flash['my-key']</tt>.
-
#
-
# Also, brings two convenience accessors:
-
#
-
# flash.now.alert = "Beware now!"
-
# # Equivalent to flash.now[:alert] = "Beware now!"
-
#
-
# flash.now.notice = "Good luck now!"
-
# # Equivalent to flash.now[:notice] = "Good luck now!"
-
1
def now
-
@now ||= FlashNow.new(self)
-
end
-
-
# Keeps either the entire current flash or a specific flash entry available for the next action:
-
#
-
# flash.keep # keeps the entire flash
-
# flash.keep(:notice) # keeps only the "notice" entry, the rest of the flash is discarded
-
1
def keep(k = nil)
-
k = k.to_s if k
-
@discard.subtract Array(k || keys)
-
k ? self[k] : self
-
end
-
-
# Marks the entire flash or a single flash entry to be discarded by the end of the current action:
-
#
-
# flash.discard # discard the entire flash at the end of the current action
-
# flash.discard(:warning) # discard only the "warning" entry at the end of the current action
-
1
def discard(k = nil)
-
k = k.to_s if k
-
@discard.merge Array(k || keys)
-
k ? self[k] : self
-
end
-
-
# Mark for removal entries that were kept, and delete unkept ones.
-
#
-
# This method is called automatically by filters, so you generally don't need to care about it.
-
1
def sweep #:nodoc:
-
4
@discard.each { |k| @flashes.delete k }
-
4
@discard.replace @flashes.keys
-
end
-
-
# Convenience accessor for <tt>flash[:alert]</tt>.
-
1
def alert
-
self[:alert]
-
end
-
-
# Convenience accessor for <tt>flash[:alert]=</tt>.
-
1
def alert=(message)
-
self[:alert] = message
-
end
-
-
# Convenience accessor for <tt>flash[:notice]</tt>.
-
1
def notice
-
self[:notice]
-
end
-
-
# Convenience accessor for <tt>flash[:notice]=</tt>.
-
1
def notice=(message)
-
self[:notice] = message
-
end
-
-
1
protected
-
1
def now_is_loaded?
-
@now
-
end
-
-
1
def stringify_array(array)
-
8
array.map do |item|
-
item.kind_of?(Symbol) ? item.to_s : item
-
end
-
end
-
end
-
-
1
def initialize(app)
-
1
@app = app
-
end
-
-
1
def call(env)
-
@app.call(env)
-
ensure
-
session = Request::Session.find(env) || {}
-
flash_hash = env[KEY]
-
-
if flash_hash && (flash_hash.present? || session.key?('flash'))
-
session["flash"] = flash_hash.to_session_value
-
env[KEY] = flash_hash.dup
-
end
-
-
if (!session.respond_to?(:loaded?) || session.loaded?) && # (reset_session uses {}, which doesn't implement #loaded?)
-
session.key?('flash') && session['flash'].nil?
-
session.delete('flash')
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/hash/conversions'
-
1
require 'action_dispatch/http/request'
-
1
require 'active_support/core_ext/hash/indifferent_access'
-
-
1
module ActionDispatch
-
1
class ParamsParser
-
1
class ParseError < StandardError
-
1
attr_reader :original_exception
-
-
1
def initialize(message, original_exception)
-
super(message)
-
@original_exception = original_exception
-
end
-
end
-
-
1
DEFAULT_PARSERS = { Mime::JSON => :json }
-
-
1
def initialize(app, parsers = {})
-
1
@app, @parsers = app, DEFAULT_PARSERS.merge(parsers)
-
end
-
-
1
def call(env)
-
if params = parse_formatted_parameters(env)
-
env["action_dispatch.request.request_parameters"] = params
-
end
-
-
@app.call(env)
-
end
-
-
1
private
-
1
def parse_formatted_parameters(env)
-
request = Request.new(env)
-
-
return false if request.content_length.zero?
-
-
strategy = @parsers[request.content_mime_type]
-
-
return false unless strategy
-
-
case strategy
-
when Proc
-
strategy.call(request.raw_post)
-
when :json
-
data = ActiveSupport::JSON.decode(request.raw_post)
-
data = {:_json => data} unless data.is_a?(Hash)
-
Request::Utils.deep_munge(data).with_indifferent_access
-
else
-
false
-
end
-
rescue Exception => e # JSON or Ruby code block errors
-
logger(env).debug "Error occurred while parsing request parameters.\nContents:\n\n#{request.raw_post}"
-
-
raise ParseError.new(e.message, e)
-
end
-
-
1
def logger(env)
-
env['action_dispatch.logger'] || ActiveSupport::Logger.new($stderr)
-
end
-
end
-
end
-
1
module ActionDispatch
-
1
class PublicExceptions
-
1
attr_accessor :public_path
-
-
1
def initialize(public_path)
-
1
@public_path = public_path
-
end
-
-
1
def call(env)
-
status = env["PATH_INFO"][1..-1]
-
request = ActionDispatch::Request.new(env)
-
content_type = request.formats.first
-
body = { :status => status, :error => Rack::Utils::HTTP_STATUS_CODES.fetch(status.to_i, Rack::Utils::HTTP_STATUS_CODES[500]) }
-
-
render(status, content_type, body)
-
end
-
-
1
private
-
-
1
def render(status, content_type, body)
-
format = "to_#{content_type.to_sym}" if content_type
-
if format && body.respond_to?(format)
-
render_format(status, content_type, body.public_send(format))
-
else
-
render_html(status)
-
end
-
end
-
-
1
def render_format(status, content_type, body)
-
[status, {'Content-Type' => "#{content_type}; charset=#{ActionDispatch::Response.default_charset}",
-
'Content-Length' => body.bytesize.to_s}, [body]]
-
end
-
-
1
def render_html(status)
-
found = false
-
path = "#{public_path}/#{status}.#{I18n.locale}.html" if I18n.locale
-
path = "#{public_path}/#{status}.html" unless path && (found = File.exist?(path))
-
-
if found || File.exist?(path)
-
render_format(status, 'text/html', File.read(path))
-
else
-
[404, { "X-Cascade" => "pass" }, []]
-
end
-
end
-
end
-
end
-
1
require 'active_support/deprecation/reporting'
-
-
1
module ActionDispatch
-
# ActionDispatch::Reloader provides prepare and cleanup callbacks,
-
# intended to assist with code reloading during development.
-
#
-
# Prepare callbacks are run before each request, and cleanup callbacks
-
# after each request. In this respect they are analogs of ActionDispatch::Callback's
-
# before and after callbacks. However, cleanup callbacks are not called until the
-
# request is fully complete -- that is, after #close has been called on
-
# the response body. This is important for streaming responses such as the
-
# following:
-
#
-
# self.response_body = lambda { |response, output|
-
# # code here which refers to application models
-
# }
-
#
-
# Cleanup callbacks will not be called until after the response_body lambda
-
# is evaluated, ensuring that it can refer to application models and other
-
# classes before they are unloaded.
-
#
-
# By default, ActionDispatch::Reloader is included in the middleware stack
-
# only in the development environment; specifically, when +config.cache_classes+
-
# is false. Callbacks may be registered even when it is not included in the
-
# middleware stack, but are executed only when <tt>ActionDispatch::Reloader.prepare!</tt>
-
# or <tt>ActionDispatch::Reloader.cleanup!</tt> are called manually.
-
#
-
1
class Reloader
-
1
include ActiveSupport::Callbacks
-
1
include ActiveSupport::Deprecation::Reporting
-
-
1
define_callbacks :prepare
-
1
define_callbacks :cleanup
-
-
# Add a prepare callback. Prepare callbacks are run before each request, prior
-
# to ActionDispatch::Callback's before callbacks.
-
1
def self.to_prepare(*args, &block)
-
4
unless block_given?
-
warn "to_prepare without a block is deprecated. Please use a block"
-
end
-
4
set_callback(:prepare, *args, &block)
-
end
-
-
# Add a cleanup callback. Cleanup callbacks are run after each request is
-
# complete (after #close is called on the response body).
-
1
def self.to_cleanup(*args, &block)
-
unless block_given?
-
warn "to_cleanup without a block is deprecated. Please use a block"
-
end
-
set_callback(:cleanup, *args, &block)
-
end
-
-
# Execute all prepare callbacks.
-
1
def self.prepare!
-
1
new(nil).prepare!
-
end
-
-
# Execute all cleanup callbacks.
-
1
def self.cleanup!
-
new(nil).cleanup!
-
end
-
-
1
def initialize(app, condition=nil)
-
1
@app = app
-
1
@condition = condition || lambda { true }
-
1
@validated = true
-
end
-
-
1
def call(env)
-
@validated = @condition.call
-
prepare!
-
-
response = @app.call(env)
-
response[2] = ::Rack::BodyProxy.new(response[2]) { cleanup! }
-
-
response
-
rescue Exception
-
cleanup!
-
raise
-
end
-
-
1
def prepare! #:nodoc:
-
1
run_callbacks :prepare if validated?
-
end
-
-
1
def cleanup! #:nodoc:
-
run_callbacks :cleanup if validated?
-
ensure
-
@validated = true
-
end
-
-
1
private
-
-
1
def validated? #:nodoc:
-
1
@validated
-
end
-
end
-
end
-
1
module ActionDispatch
-
# This middleware calculates the IP address of the remote client that is
-
# making the request. It does this by checking various headers that could
-
# contain the address, and then picking the last-set address that is not
-
# on the list of trusted IPs. This follows the precedent set by e.g.
-
# {the Tomcat server}[https://issues.apache.org/bugzilla/show_bug.cgi?id=50453],
-
# with {reasoning explained at length}[http://blog.gingerlime.com/2012/rails-ip-spoofing-vulnerabilities-and-protection]
-
# by @gingerlime. A more detailed explanation of the algorithm is given
-
# at GetIp#calculate_ip.
-
#
-
# Some Rack servers concatenate repeated headers, like {HTTP RFC 2616}[http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2]
-
# requires. Some Rack servers simply drop preceding headers, and only report
-
# the value that was {given in the last header}[http://andre.arko.net/2011/12/26/repeated-headers-and-ruby-web-servers].
-
# If you are behind multiple proxy servers (like Nginx to HAProxy to Unicorn)
-
# then you should test your Rack server to make sure your data is good.
-
#
-
# IF YOU DON'T USE A PROXY, THIS MAKES YOU VULNERABLE TO IP SPOOFING.
-
# This middleware assumes that there is at least one proxy sitting around
-
# and setting headers with the client's remote IP address. If you don't use
-
# a proxy, because you are hosted on e.g. Heroku without SSL, any client can
-
# claim to have any IP address by setting the X-Forwarded-For header. If you
-
# care about that, then you need to explicitly drop or ignore those headers
-
# sometime before this middleware runs.
-
1
class RemoteIp
-
1
class IpSpoofAttackError < StandardError; end
-
-
# The default trusted IPs list simply includes IP addresses that are
-
# guaranteed by the IP specification to be private addresses. Those will
-
# not be the ultimate client IP in production, and so are discarded. See
-
# http://en.wikipedia.org/wiki/Private_network for details.
-
1
TRUSTED_PROXIES = %r{
-
^127\.0\.0\.1$ | # localhost IPv4
-
^::1$ | # localhost IPv6
-
^fc00: | # private IPv6 range fc00
-
^10\. | # private IPv4 range 10.x.x.x
-
^172\.(1[6-9]|2[0-9]|3[0-1])\.| # private IPv4 range 172.16.0.0 .. 172.31.255.255
-
^192\.168\. # private IPv4 range 192.168.x.x
-
}x
-
-
1
attr_reader :check_ip, :proxies
-
-
# Create a new +RemoteIp+ middleware instance.
-
#
-
# The +check_ip_spoofing+ option is on by default. When on, an exception
-
# is raised if it looks like the client is trying to lie about its own IP
-
# address. It makes sense to turn off this check on sites aimed at non-IP
-
# clients (like WAP devices), or behind proxies that set headers in an
-
# incorrect or confusing way (like AWS ELB).
-
#
-
# The +custom_proxies+ argument can take a regex, which will be used
-
# instead of +TRUSTED_PROXIES+, or a string, which will be used in addition
-
# to +TRUSTED_PROXIES+. Any proxy setup will put the value you want in the
-
# middle (or at the beginning) of the X-Forwarded-For list, with your proxy
-
# servers after it. If your proxies aren't removed, pass them in via the
-
# +custom_proxies+ parameter. That way, the middleware will ignore those
-
# IP addresses, and return the one that you want.
-
1
def initialize(app, check_ip_spoofing = true, custom_proxies = nil)
-
1
@app = app
-
1
@check_ip = check_ip_spoofing
-
1
@proxies = case custom_proxies
-
when Regexp
-
custom_proxies
-
when nil
-
1
TRUSTED_PROXIES
-
else
-
Regexp.union(TRUSTED_PROXIES, custom_proxies)
-
end
-
end
-
-
# Since the IP address may not be needed, we store the object here
-
# without calculating the IP to keep from slowing down the majority of
-
# requests. For those requests that do need to know the IP, the
-
# GetIp#calculate_ip method will calculate the memoized client IP address.
-
1
def call(env)
-
env["action_dispatch.remote_ip"] = GetIp.new(env, self)
-
@app.call(env)
-
end
-
-
# The GetIp class exists as a way to defer processing of the request data
-
# into an actual IP address. If the ActionDispatch::Request#remote_ip method
-
# is called, this class will calculate the value and then memoize it.
-
1
class GetIp
-
-
# This constant contains a regular expression that validates every known
-
# form of IP v4 and v6 address, with or without abbreviations, adapted
-
# from {this gist}[https://gist.github.com/gazay/1289635].
-
1
VALID_IP = %r{
-
(^(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[0-9]{1,2})(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[0-9]{1,2})){3}$) | # ip v4
-
(^(
-
(([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}) | # ip v6 not abbreviated
-
(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4}) | # ip v6 with double colon in the end
-
(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4}) | # - ip addresses v6
-
(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4}) | # - with
-
(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4}) | # - double colon
-
(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4}) | # - in the middle
-
(([0-9A-Fa-f]{1,4}:){6} ((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3} (\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4
-
(([0-9A-Fa-f]{1,4}:){1,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4
-
(([0-9A-Fa-f]{1,4}:){1}:([0-9A-Fa-f]{1,4}:){0,4}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4
-
(([0-9A-Fa-f]{1,4}:){0,2}:([0-9A-Fa-f]{1,4}:){0,3}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4
-
(([0-9A-Fa-f]{1,4}:){0,3}:([0-9A-Fa-f]{1,4}:){0,2}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4
-
(([0-9A-Fa-f]{1,4}:){0,4}:([0-9A-Fa-f]{1,4}:){1}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4
-
(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d) |(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)) | # ip v6 with compatible to v4
-
([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4}) | # ip v6 with compatible to v4
-
(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4}) | # ip v6 with double colon at the beginning
-
(([0-9A-Fa-f]{1,4}:){1,7}:) # ip v6 without ending
-
)$)
-
}x
-
-
1
def initialize(env, middleware)
-
@env = env
-
@check_ip = middleware.check_ip
-
@proxies = middleware.proxies
-
end
-
-
# Sort through the various IP address headers, looking for the IP most
-
# likely to be the address of the actual remote client making this
-
# request.
-
#
-
# REMOTE_ADDR will be correct if the request is made directly against the
-
# Ruby process, on e.g. Heroku. When the request is proxied by another
-
# server like HAProxy or Nginx, the IP address that made the original
-
# request will be put in an X-Forwarded-For header. If there are multiple
-
# proxies, that header may contain a list of IPs. Other proxy services
-
# set the Client-Ip header instead, so we check that too.
-
#
-
# As discussed in {this post about Rails IP Spoofing}[http://blog.gingerlime.com/2012/rails-ip-spoofing-vulnerabilities-and-protection/],
-
# while the first IP in the list is likely to be the "originating" IP,
-
# it could also have been set by the client maliciously.
-
#
-
# In order to find the first address that is (probably) accurate, we
-
# take the list of IPs, remove known and trusted proxies, and then take
-
# the last address left, which was presumably set by one of those proxies.
-
1
def calculate_ip
-
# Set by the Rack web server, this is a single value.
-
remote_addr = ips_from('REMOTE_ADDR').last
-
-
# Could be a CSV list and/or repeated headers that were concatenated.
-
client_ips = ips_from('HTTP_CLIENT_IP').reverse
-
forwarded_ips = ips_from('HTTP_X_FORWARDED_FOR').reverse
-
-
# +Client-Ip+ and +X-Forwarded-For+ should not, generally, both be set.
-
# If they are both set, it means that this request passed through two
-
# proxies with incompatible IP header conventions, and there is no way
-
# for us to determine which header is the right one after the fact.
-
# Since we have no idea, we give up and explode.
-
should_check_ip = @check_ip && client_ips.last && forwarded_ips.last
-
if should_check_ip && !forwarded_ips.include?(client_ips.last)
-
# We don't know which came from the proxy, and which from the user
-
raise IpSpoofAttackError, "IP spoofing attack?! " +
-
"HTTP_CLIENT_IP=#{@env['HTTP_CLIENT_IP'].inspect} " +
-
"HTTP_X_FORWARDED_FOR=#{@env['HTTP_X_FORWARDED_FOR'].inspect}"
-
end
-
-
# We assume these things about the IP headers:
-
#
-
# - X-Forwarded-For will be a list of IPs, one per proxy, or blank
-
# - Client-Ip is propagated from the outermost proxy, or is blank
-
# - REMOTE_ADDR will be the IP that made the request to Rack
-
ips = [forwarded_ips, client_ips, remote_addr].flatten.compact
-
-
# If every single IP option is in the trusted list, just return REMOTE_ADDR
-
filter_proxies(ips).first || remote_addr
-
end
-
-
# Memoizes the value returned by #calculate_ip and returns it for
-
# ActionDispatch::Request to use.
-
1
def to_s
-
@ip ||= calculate_ip
-
end
-
-
1
protected
-
-
1
def ips_from(header)
-
# Split the comma-separated list into an array of strings
-
ips = @env[header] ? @env[header].strip.split(/[,\s]+/) : []
-
# Only return IPs that are valid according to the regex
-
ips.select{ |ip| ip =~ VALID_IP }
-
end
-
-
1
def filter_proxies(ips)
-
ips.reject { |ip| ip =~ @proxies }
-
end
-
-
end
-
-
end
-
end
-
1
require 'securerandom'
-
1
require 'active_support/core_ext/string/access'
-
-
1
module ActionDispatch
-
# Makes a unique request id available to the action_dispatch.request_id env variable (which is then accessible through
-
# ActionDispatch::Request#uuid) and sends the same id to the client via the X-Request-Id header.
-
#
-
# The unique request id is either based off the X-Request-Id header in the request, which would typically be generated
-
# by a firewall, load balancer, or the web server, or, if this header is not available, a random uuid. If the
-
# header is accepted from the outside world, we sanitize it to a max of 255 chars and alphanumeric and dashes only.
-
#
-
# The unique request id can be used to trace a request end-to-end and would typically end up being part of log files
-
# from multiple pieces of the stack.
-
1
class RequestId
-
1
def initialize(app)
-
1
@app = app
-
end
-
-
1
def call(env)
-
env["action_dispatch.request_id"] = external_request_id(env) || internal_request_id
-
@app.call(env).tap { |_status, headers, _body| headers["X-Request-Id"] = env["action_dispatch.request_id"] }
-
end
-
-
1
private
-
1
def external_request_id(env)
-
if request_id = env["HTTP_X_REQUEST_ID"].presence
-
request_id.gsub(/[^\w\-]/, "").first(255)
-
end
-
end
-
-
1
def internal_request_id
-
SecureRandom.uuid
-
end
-
end
-
end
-
1
require 'rack/utils'
-
1
require 'rack/request'
-
1
require 'rack/session/abstract/id'
-
1
require 'action_dispatch/middleware/cookies'
-
1
require 'action_dispatch/request/session'
-
-
1
module ActionDispatch
-
1
module Session
-
1
class SessionRestoreError < StandardError #:nodoc:
-
1
attr_reader :original_exception
-
-
1
def initialize(const_error)
-
@original_exception = const_error
-
-
super("Session contains objects whose class definition isn't available.\n" +
-
"Remember to require the classes for all objects kept in the session.\n" +
-
"(Original exception: #{const_error.message} [#{const_error.class}])\n")
-
end
-
end
-
-
1
module Compatibility
-
1
def initialize(app, options = {})
-
1
options[:key] ||= '_session_id'
-
1
super
-
end
-
-
1
def generate_sid
-
sid = SecureRandom.hex(16)
-
sid.encode!(Encoding::UTF_8)
-
sid
-
end
-
-
1
protected
-
-
1
def initialize_sid
-
1
@default_options.delete(:sidbits)
-
1
@default_options.delete(:secure_random)
-
end
-
end
-
-
1
module StaleSessionCheck
-
1
def load_session(env)
-
stale_session_check! { super }
-
end
-
-
1
def extract_session_id(env)
-
stale_session_check! { super }
-
end
-
-
1
def stale_session_check!
-
yield
-
rescue ArgumentError => argument_error
-
if argument_error.message =~ %r{undefined class/module ([\w:]*\w)}
-
begin
-
# Note that the regexp does not allow $1 to end with a ':'
-
$1.constantize
-
rescue LoadError, NameError => e
-
raise ActionDispatch::Session::SessionRestoreError, e, e.backtrace
-
end
-
retry
-
else
-
raise
-
end
-
end
-
end
-
-
1
module SessionObject # :nodoc:
-
1
def prepare_session(env)
-
Request::Session.create(self, env, @default_options)
-
end
-
-
1
def loaded_session?(session)
-
!session.is_a?(Request::Session) || session.loaded?
-
end
-
end
-
-
1
class AbstractStore < Rack::Session::Abstract::ID
-
1
include Compatibility
-
1
include StaleSessionCheck
-
1
include SessionObject
-
-
1
private
-
-
1
def set_cookie(env, session_id, cookie)
-
request = ActionDispatch::Request.new(env)
-
request.cookie_jar[key] = cookie
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/hash/keys'
-
1
require 'action_dispatch/middleware/session/abstract_store'
-
1
require 'rack/session/cookie'
-
-
1
module ActionDispatch
-
1
module Session
-
# This cookie-based session store is the Rails default. It is
-
# dramatically faster than the alternatives.
-
#
-
# Sessions typically contain at most a user_id and flash message; both fit
-
# within the 4K cookie size limit. A CookieOverflow exception is raised if
-
# you attempt to store more than 4K of data.
-
#
-
# The cookie jar used for storage is automatically configured to be the
-
# best possible option given your application's configuration.
-
#
-
# If you only have secret_token set, your cookies will be signed, but
-
# not encrypted. This means a user cannot alter their +user_id+ without
-
# knowing your app's secret key, but can easily read their +user_id+. This
-
# was the default for Rails 3 apps.
-
#
-
# If you have secret_key_base set, your cookies will be encrypted. This
-
# goes a step further than signed cookies in that encrypted cookies cannot
-
# be altered or read by users. This is the default starting in Rails 4.
-
#
-
# If you have both secret_token and secret_key base set, your cookies will
-
# be encrypted, and signed cookies generated by Rails 3 will be
-
# transparently read and encrypted to provide a smooth upgrade path.
-
#
-
# Configure your session store in config/initializers/session_store.rb:
-
#
-
# Rails.application.config.session_store :cookie_store, key: '_your_app_session'
-
#
-
# Configure your secret key in config/secrets.yml:
-
#
-
# development:
-
# secret_key_base: 'secret key'
-
#
-
# To generate a secret key for an existing application, run `rake secret`.
-
#
-
# If you are upgrading an existing Rails 3 app, you should leave your
-
# existing secret_token in place and simply add the new secret_key_base.
-
# Note that you should wait to set secret_key_base until you have 100% of
-
# your userbase on Rails 4 and are reasonably sure you will not need to
-
# rollback to Rails 3. This is because cookies signed based on the new
-
# secret_key_base in Rails 4 are not backwards compatible with Rails 3.
-
# You are free to leave your existing secret_token in place, not set the
-
# new secret_key_base, and ignore the deprecation warnings until you are
-
# reasonably sure that your upgrade is otherwise complete. Additionally,
-
# you should take care to make sure you are not relying on the ability to
-
# decode signed cookies generated by your app in external applications or
-
# Javascript before upgrading.
-
#
-
# Note that changing the secret key will invalidate all existing sessions!
-
1
class CookieStore < Rack::Session::Abstract::ID
-
1
include Compatibility
-
1
include StaleSessionCheck
-
1
include SessionObject
-
-
1
def initialize(app, options={})
-
1
super(app, options.merge!(:cookie_only => true))
-
end
-
-
1
def destroy_session(env, session_id, options)
-
new_sid = generate_sid unless options[:drop]
-
# Reset hash and Assign the new session id
-
env["action_dispatch.request.unsigned_session_cookie"] = new_sid ? { "session_id" => new_sid } : {}
-
new_sid
-
end
-
-
1
def load_session(env)
-
stale_session_check! do
-
data = unpacked_cookie_data(env)
-
data = persistent_session_id!(data)
-
[data["session_id"], data]
-
end
-
end
-
-
1
private
-
-
1
def extract_session_id(env)
-
stale_session_check! do
-
unpacked_cookie_data(env)["session_id"]
-
end
-
end
-
-
1
def unpacked_cookie_data(env)
-
env["action_dispatch.request.unsigned_session_cookie"] ||= begin
-
stale_session_check! do
-
if data = get_cookie(env)
-
data.stringify_keys!
-
end
-
data || {}
-
end
-
end
-
end
-
-
1
def persistent_session_id!(data, sid=nil)
-
data ||= {}
-
data["session_id"] ||= sid || generate_sid
-
data
-
end
-
-
1
def set_session(env, sid, session_data, options)
-
session_data["session_id"] = sid
-
session_data
-
end
-
-
1
def set_cookie(env, session_id, cookie)
-
cookie_jar(env)[@key] = cookie
-
end
-
-
1
def get_cookie(env)
-
cookie_jar(env)[@key]
-
end
-
-
1
def cookie_jar(env)
-
request = ActionDispatch::Request.new(env)
-
request.cookie_jar.signed_or_encrypted
-
end
-
end
-
end
-
end
-
1
require 'action_dispatch/http/request'
-
1
require 'action_dispatch/middleware/exception_wrapper'
-
-
1
module ActionDispatch
-
# This middleware rescues any exception returned by the application
-
# and calls an exceptions app that will wrap it in a format for the end user.
-
#
-
# The exceptions app should be passed as parameter on initialization
-
# of ShowExceptions. Every time there is an exception, ShowExceptions will
-
# store the exception in env["action_dispatch.exception"], rewrite the
-
# PATH_INFO to the exception status code and call the rack app.
-
#
-
# If the application returns a "X-Cascade" pass response, this middleware
-
# will send an empty response as result with the correct status code.
-
# If any exception happens inside the exceptions app, this middleware
-
# catches the exceptions and returns a FAILSAFE_RESPONSE.
-
1
class ShowExceptions
-
1
FAILSAFE_RESPONSE = [500, { 'Content-Type' => 'text/plain' },
-
["500 Internal Server Error\n" \
-
"If you are the administrator of this website, then please read this web " \
-
"application's log file and/or the web server's log file to find out what " \
-
"went wrong."]]
-
-
1
def initialize(app, exceptions_app)
-
1
@app = app
-
1
@exceptions_app = exceptions_app
-
end
-
-
1
def call(env)
-
@app.call(env)
-
rescue Exception => exception
-
if env['action_dispatch.show_exceptions'] == false
-
raise exception
-
else
-
render_exception(env, exception)
-
end
-
end
-
-
1
private
-
-
1
def render_exception(env, exception)
-
wrapper = ExceptionWrapper.new(env, exception)
-
status = wrapper.status_code
-
env["action_dispatch.exception"] = wrapper.exception
-
env["PATH_INFO"] = "/#{status}"
-
response = @exceptions_app.call(env)
-
response[1]['X-Cascade'] == 'pass' ? pass_response(status) : response
-
rescue Exception => failsafe_error
-
$stderr.puts "Error during failsafe response: #{failsafe_error}\n #{failsafe_error.backtrace * "\n "}"
-
FAILSAFE_RESPONSE
-
end
-
-
1
def pass_response(status)
-
[status, {"Content-Type" => "text/html; charset=#{Response.default_charset}", "Content-Length" => "0"}, []]
-
end
-
end
-
end
-
1
require "active_support/inflector/methods"
-
1
require "active_support/dependencies"
-
-
1
module ActionDispatch
-
1
class MiddlewareStack
-
1
class Middleware
-
1
attr_reader :args, :block, :name, :classcache
-
-
1
def initialize(klass_or_name, *args, &block)
-
21
@klass = nil
-
-
21
if klass_or_name.respond_to?(:name)
-
19
@klass = klass_or_name
-
19
@name = @klass.name
-
else
-
2
@name = klass_or_name.to_s
-
end
-
-
21
@classcache = ActiveSupport::Dependencies::Reference
-
21
@args, @block = args, block
-
end
-
-
1
def klass
-
42
@klass || classcache[@name]
-
end
-
-
1
def ==(middleware)
-
44
case middleware
-
when Middleware
-
klass == middleware.klass
-
when Class
-
21
klass == middleware
-
else
-
23
normalize(@name) == normalize(middleware)
-
end
-
end
-
-
1
def inspect
-
klass.to_s
-
end
-
-
1
def build(app)
-
21
klass.new(app, *args, &block)
-
end
-
-
1
private
-
-
1
def normalize(object)
-
46
object.to_s.strip.sub(/^::/, '')
-
end
-
end
-
-
1
include Enumerable
-
-
1
attr_accessor :middlewares
-
-
1
def initialize(*args)
-
2
@middlewares = []
-
2
yield(self) if block_given?
-
end
-
-
1
def each
-
22
@middlewares.each { |x| yield x }
-
end
-
-
1
def size
-
middlewares.size
-
end
-
-
1
def last
-
middlewares.last
-
end
-
-
1
def [](i)
-
middlewares[i]
-
end
-
-
1
def unshift(*args, &block)
-
middleware = self.class::Middleware.new(*args, &block)
-
middlewares.unshift(middleware)
-
end
-
-
1
def initialize_copy(other)
-
4
self.middlewares = other.middlewares.dup
-
end
-
-
1
def insert(index, *args, &block)
-
3
index = assert_index(index, :before)
-
3
middleware = self.class::Middleware.new(*args, &block)
-
3
middlewares.insert(index, middleware)
-
end
-
-
1
alias_method :insert_before, :insert
-
-
1
def insert_after(index, *args, &block)
-
2
index = assert_index(index, :after)
-
2
insert(index + 1, *args, &block)
-
end
-
-
1
def swap(target, *args, &block)
-
index = assert_index(target, :before)
-
insert(index, *args, &block)
-
middlewares.delete_at(index + 1)
-
end
-
-
1
def delete(target)
-
middlewares.delete target
-
end
-
-
1
def use(*args, &block)
-
18
middleware = self.class::Middleware.new(*args, &block)
-
18
middlewares.push(middleware)
-
end
-
-
1
def build(app = nil, &block)
-
1
app ||= block
-
1
raise "MiddlewareStack#build requires an app" unless app
-
22
middlewares.freeze.reverse.inject(app) { |a, e| e.build(a) }
-
end
-
-
1
protected
-
-
1
def assert_index(index, where)
-
5
i = index.is_a?(Integer) ? index : middlewares.index(index)
-
5
raise "No such middleware to insert #{where}: #{index.inspect}" unless i
-
5
i
-
end
-
end
-
end
-
1
require 'rack/utils'
-
1
require 'active_support/core_ext/uri'
-
-
1
module ActionDispatch
-
1
class FileHandler
-
1
def initialize(root, cache_control)
-
1
@root = root.chomp('/')
-
1
@compiled_root = /^#{Regexp.escape(root)}/
-
1
headers = cache_control && { 'Cache-Control' => cache_control }
-
1
@file_server = ::Rack::File.new(@root, headers)
-
end
-
-
1
def match?(path)
-
path = unescape_path(path)
-
return false unless path.valid_encoding?
-
-
full_path = path.empty? ? @root : File.join(@root, escape_glob_chars(path))
-
paths = "#{full_path}#{ext}"
-
-
matches = Dir[paths]
-
match = matches.detect { |m| File.file?(m) }
-
if match
-
match.sub!(@compiled_root, '')
-
::Rack::Utils.escape(match)
-
end
-
end
-
-
1
def call(env)
-
@file_server.call(env)
-
end
-
-
1
def ext
-
@ext ||= begin
-
ext = ::ActionController::Base.default_static_extension
-
"{,#{ext},/index#{ext}}"
-
end
-
end
-
-
1
def unescape_path(path)
-
URI.parser.unescape(path)
-
end
-
-
1
def escape_glob_chars(path)
-
path.gsub(/[*?{}\[\]]/, "\\\\\\&")
-
end
-
end
-
-
1
class Static
-
1
def initialize(app, path, cache_control=nil)
-
1
@app = app
-
1
@file_handler = FileHandler.new(path, cache_control)
-
end
-
-
1
def call(env)
-
case env['REQUEST_METHOD']
-
when 'GET', 'HEAD'
-
path = env['PATH_INFO'].chomp('/')
-
if match = @file_handler.match?(path)
-
env["PATH_INFO"] = match
-
return @file_handler.call(env)
-
end
-
end
-
-
@app.call(env)
-
end
-
end
-
end
-
1
require "action_dispatch"
-
-
1
module ActionDispatch
-
1
class Railtie < Rails::Railtie # :nodoc:
-
1
config.action_dispatch = ActiveSupport::OrderedOptions.new
-
1
config.action_dispatch.x_sendfile_header = nil
-
1
config.action_dispatch.ip_spoofing_check = true
-
1
config.action_dispatch.show_exceptions = true
-
1
config.action_dispatch.tld_length = 1
-
1
config.action_dispatch.ignore_accept_header = false
-
1
config.action_dispatch.rescue_templates = { }
-
1
config.action_dispatch.rescue_responses = { }
-
1
config.action_dispatch.default_charset = nil
-
1
config.action_dispatch.rack_cache = false
-
1
config.action_dispatch.http_auth_salt = 'http authentication'
-
1
config.action_dispatch.signed_cookie_salt = 'signed cookie'
-
1
config.action_dispatch.encrypted_cookie_salt = 'encrypted cookie'
-
1
config.action_dispatch.encrypted_signed_cookie_salt = 'signed encrypted cookie'
-
1
config.action_dispatch.perform_deep_munge = true
-
-
1
config.action_dispatch.default_headers = {
-
'X-Frame-Options' => 'SAMEORIGIN',
-
'X-XSS-Protection' => '1; mode=block',
-
'X-Content-Type-Options' => 'nosniff'
-
}
-
-
1
config.eager_load_namespaces << ActionDispatch
-
-
1
initializer "action_dispatch.configure" do |app|
-
1
ActionDispatch::Http::URL.tld_length = app.config.action_dispatch.tld_length
-
1
ActionDispatch::Request.ignore_accept_header = app.config.action_dispatch.ignore_accept_header
-
1
ActionDispatch::Request::Utils.perform_deep_munge = app.config.action_dispatch.perform_deep_munge
-
1
ActionDispatch::Response.default_charset = app.config.action_dispatch.default_charset || app.config.encoding
-
1
ActionDispatch::Response.default_headers = app.config.action_dispatch.default_headers
-
-
1
ActionDispatch::ExceptionWrapper.rescue_responses.merge!(config.action_dispatch.rescue_responses)
-
1
ActionDispatch::ExceptionWrapper.rescue_templates.merge!(config.action_dispatch.rescue_templates)
-
-
1
config.action_dispatch.always_write_cookie = Rails.env.development? if config.action_dispatch.always_write_cookie.nil?
-
1
ActionDispatch::Cookies::CookieJar.always_write_cookie = config.action_dispatch.always_write_cookie
-
-
1
ActionDispatch.test_app = app
-
end
-
end
-
end
-
1
require 'rack/session/abstract/id'
-
-
1
module ActionDispatch
-
1
class Request < Rack::Request
-
# Session is responsible for lazily loading the session from store.
-
1
class Session # :nodoc:
-
1
ENV_SESSION_KEY = Rack::Session::Abstract::ENV_SESSION_KEY # :nodoc:
-
1
ENV_SESSION_OPTIONS_KEY = Rack::Session::Abstract::ENV_SESSION_OPTIONS_KEY # :nodoc:
-
-
# Singleton object used to determine if an optional param wasn't specified
-
1
Unspecified = Object.new
-
-
1
def self.create(store, env, default_options)
-
session_was = find env
-
session = Request::Session.new(store, env)
-
session.merge! session_was if session_was
-
-
set(env, session)
-
Options.set(env, Request::Session::Options.new(store, env, default_options))
-
session
-
end
-
-
1
def self.find(env)
-
env[ENV_SESSION_KEY]
-
end
-
-
1
def self.set(env, session)
-
4
env[ENV_SESSION_KEY] = session
-
end
-
-
1
class Options #:nodoc:
-
1
def self.set(env, options)
-
4
env[ENV_SESSION_OPTIONS_KEY] = options
-
end
-
-
1
def self.find(env)
-
env[ENV_SESSION_OPTIONS_KEY]
-
end
-
-
1
def initialize(by, env, default_options)
-
@by = by
-
@env = env
-
@delegate = default_options.dup
-
end
-
-
1
def [](key)
-
if key == :id
-
@delegate.fetch(key) {
-
@delegate[:id] = @by.send(:extract_session_id, @env)
-
}
-
else
-
@delegate[key]
-
end
-
end
-
-
1
def []=(k,v); @delegate[k] = v; end
-
1
def to_hash; @delegate.dup; end
-
1
def values_at(*args); @delegate.values_at(*args); end
-
end
-
-
1
def initialize(by, env)
-
@by = by
-
@env = env
-
@delegate = {}
-
@loaded = false
-
@exists = nil # we haven't checked yet
-
end
-
-
1
def id
-
options[:id]
-
end
-
-
1
def options
-
Options.find @env
-
end
-
-
1
def destroy
-
clear
-
options = self.options || {}
-
new_sid = @by.send(:destroy_session, @env, options[:id], options)
-
options[:id] = new_sid # Reset session id with a new value or nil
-
-
# Load the new sid to be written with the response
-
@loaded = false
-
load_for_write!
-
end
-
-
1
def [](key)
-
load_for_read!
-
@delegate[key.to_s]
-
end
-
-
1
def has_key?(key)
-
load_for_read!
-
@delegate.key?(key.to_s)
-
end
-
1
alias :key? :has_key?
-
1
alias :include? :has_key?
-
-
1
def keys
-
@delegate.keys
-
end
-
-
1
def values
-
@delegate.values
-
end
-
-
1
def []=(key, value)
-
load_for_write!
-
@delegate[key.to_s] = value
-
end
-
-
1
def clear
-
load_for_write!
-
@delegate.clear
-
end
-
-
1
def to_hash
-
load_for_read!
-
@delegate.dup.delete_if { |_,v| v.nil? }
-
end
-
-
1
def update(hash)
-
load_for_write!
-
@delegate.update stringify_keys(hash)
-
end
-
-
1
def delete(key)
-
load_for_write!
-
@delegate.delete key.to_s
-
end
-
-
1
def fetch(key, default=Unspecified, &block)
-
load_for_read!
-
if default == Unspecified
-
@delegate.fetch(key.to_s, &block)
-
else
-
@delegate.fetch(key.to_s, default, &block)
-
end
-
end
-
-
1
def inspect
-
if loaded?
-
super
-
else
-
"#<#{self.class}:0x#{(object_id << 1).to_s(16)} not yet loaded>"
-
end
-
end
-
-
1
def exists?
-
return @exists unless @exists.nil?
-
@exists = @by.send(:session_exists?, @env)
-
end
-
-
1
def loaded?
-
@loaded
-
end
-
-
1
def empty?
-
load_for_read!
-
@delegate.empty?
-
end
-
-
1
def merge!(other)
-
load_for_write!
-
@delegate.merge!(other)
-
end
-
-
1
private
-
-
1
def load_for_read!
-
load! if !loaded? && exists?
-
end
-
-
1
def load_for_write!
-
load! unless loaded?
-
end
-
-
1
def load!
-
id, session = @by.load_session @env
-
options[:id] = id
-
@delegate.replace(stringify_keys(session))
-
@loaded = true
-
end
-
-
1
def stringify_keys(other)
-
other.each_with_object({}) { |(key, value), hash|
-
hash[key.to_s] = value
-
}
-
end
-
end
-
end
-
end
-
1
module ActionDispatch
-
1
class Request < Rack::Request
-
1
class Utils # :nodoc:
-
-
1
mattr_accessor :perform_deep_munge
-
1
self.perform_deep_munge = true
-
-
1
class << self
-
# Remove nils from the params hash
-
1
def deep_munge(hash, keys = [])
-
4
return hash unless perform_deep_munge
-
-
4
hash.each do |k, v|
-
keys << k
-
case v
-
when Array
-
v.grep(Hash) { |x| deep_munge(x, keys) }
-
v.compact!
-
if v.empty?
-
hash[k] = nil
-
ActiveSupport::Notifications.instrument("deep_munge.action_controller", keys: keys)
-
end
-
when Hash
-
deep_munge(v, keys)
-
end
-
keys.pop
-
end
-
-
4
hash
-
end
-
end
-
end
-
end
-
end
-
-
# encoding: UTF-8
-
1
require 'active_support/core_ext/object/to_param'
-
1
require 'active_support/core_ext/regexp'
-
1
require 'active_support/dependencies/autoload'
-
-
1
module ActionDispatch
-
# The routing module provides URL rewriting in native Ruby. It's a way to
-
# redirect incoming requests to controllers and actions. This replaces
-
# mod_rewrite rules. Best of all, Rails' \Routing works with any web server.
-
# Routes are defined in <tt>config/routes.rb</tt>.
-
#
-
# Think of creating routes as drawing a map for your requests. The map tells
-
# them where to go based on some predefined pattern:
-
#
-
# Rails.application.routes.draw do
-
# Pattern 1 tells some request to go to one place
-
# Pattern 2 tell them to go to another
-
# ...
-
# end
-
#
-
# The following symbols are special:
-
#
-
# :controller maps to your controller name
-
# :action maps to an action with your controllers
-
#
-
# Other names simply map to a parameter as in the case of <tt>:id</tt>.
-
#
-
# == Resources
-
#
-
# Resource routing allows you to quickly declare all of the common routes
-
# for a given resourceful controller. Instead of declaring separate routes
-
# for your +index+, +show+, +new+, +edit+, +create+, +update+ and +destroy+
-
# actions, a resourceful route declares them in a single line of code:
-
#
-
# resources :photos
-
#
-
# Sometimes, you have a resource that clients always look up without
-
# referencing an ID. A common example, /profile always shows the profile of
-
# the currently logged in user. In this case, you can use a singular resource
-
# to map /profile (rather than /profile/:id) to the show action.
-
#
-
# resource :profile
-
#
-
# It's common to have resources that are logically children of other
-
# resources:
-
#
-
# resources :magazines do
-
# resources :ads
-
# end
-
#
-
# You may wish to organize groups of controllers under a namespace. Most
-
# commonly, you might group a number of administrative controllers under
-
# an +admin+ namespace. You would place these controllers under the
-
# <tt>app/controllers/admin</tt> directory, and you can group them together
-
# in your router:
-
#
-
# namespace "admin" do
-
# resources :posts, :comments
-
# end
-
#
-
# Alternately, you can add prefixes to your path without using a separate
-
# directory by using +scope+. +scope+ takes additional options which
-
# apply to all enclosed routes.
-
#
-
# scope path: "/cpanel", as: 'admin' do
-
# resources :posts, :comments
-
# end
-
#
-
# For more, see <tt>Routing::Mapper::Resources#resources</tt>,
-
# <tt>Routing::Mapper::Scoping#namespace</tt>, and
-
# <tt>Routing::Mapper::Scoping#scope</tt>.
-
#
-
# == Non-resourceful routes
-
#
-
# For routes that don't fit the <tt>resources</tt> mold, you can use the HTTP helper
-
# methods <tt>get</tt>, <tt>post</tt>, <tt>patch</tt>, <tt>put</tt> and <tt>delete</tt>.
-
#
-
# get 'post/:id' => 'posts#show'
-
# post 'post/:id' => 'posts#create_comment'
-
#
-
# If your route needs to respond to more than one HTTP method (or all methods) then using the
-
# <tt>:via</tt> option on <tt>match</tt> is preferable.
-
#
-
# match 'post/:id' => 'posts#show', via: [:get, :post]
-
#
-
# Now, if you POST to <tt>/posts/:id</tt>, it will route to the <tt>create_comment</tt> action. A GET on the same
-
# URL will route to the <tt>show</tt> action.
-
#
-
# == Named routes
-
#
-
# Routes can be named by passing an <tt>:as</tt> option,
-
# allowing for easy reference within your source as +name_of_route_url+
-
# for the full URL and +name_of_route_path+ for the URI path.
-
#
-
# Example:
-
#
-
# # In routes.rb
-
# get '/login' => 'accounts#login', as: 'login'
-
#
-
# # With render, redirect_to, tests, etc.
-
# redirect_to login_url
-
#
-
# Arguments can be passed as well.
-
#
-
# redirect_to show_item_path(id: 25)
-
#
-
# Use <tt>root</tt> as a shorthand to name a route for the root path "/".
-
#
-
# # In routes.rb
-
# root to: 'blogs#index'
-
#
-
# # would recognize http://www.example.com/ as
-
# params = { controller: 'blogs', action: 'index' }
-
#
-
# # and provide these named routes
-
# root_url # => 'http://www.example.com/'
-
# root_path # => '/'
-
#
-
# Note: when using +controller+, the route is simply named after the
-
# method you call on the block parameter rather than map.
-
#
-
# # In routes.rb
-
# controller :blog do
-
# get 'blog/show' => :list
-
# get 'blog/delete' => :delete
-
# get 'blog/edit/:id' => :edit
-
# end
-
#
-
# # provides named routes for show, delete, and edit
-
# link_to @article.title, show_path(id: @article.id)
-
#
-
# == Pretty URLs
-
#
-
# Routes can generate pretty URLs. For example:
-
#
-
# get '/articles/:year/:month/:day' => 'articles#find_by_id', constraints: {
-
# year: /\d{4}/,
-
# month: /\d{1,2}/,
-
# day: /\d{1,2}/
-
# }
-
#
-
# Using the route above, the URL "http://localhost:3000/articles/2005/11/06"
-
# maps to
-
#
-
# params = {year: '2005', month: '11', day: '06'}
-
#
-
# == Regular Expressions and parameters
-
# You can specify a regular expression to define a format for a parameter.
-
#
-
# controller 'geocode' do
-
# get 'geocode/:postalcode' => :show, constraints: {
-
# postalcode: /\d{5}(-\d{4})?/
-
# }
-
#
-
# Constraints can include the 'ignorecase' and 'extended syntax' regular
-
# expression modifiers:
-
#
-
# controller 'geocode' do
-
# get 'geocode/:postalcode' => :show, constraints: {
-
# postalcode: /hx\d\d\s\d[a-z]{2}/i
-
# }
-
# end
-
#
-
# controller 'geocode' do
-
# get 'geocode/:postalcode' => :show, constraints: {
-
# postalcode: /# Postcode format
-
# \d{5} #Prefix
-
# (-\d{4})? #Suffix
-
# /x
-
# }
-
# end
-
#
-
# Using the multiline modifier will raise an +ArgumentError+.
-
# Encoding regular expression modifiers are silently ignored. The
-
# match will always use the default encoding or ASCII.
-
#
-
# == External redirects
-
#
-
# You can redirect any path to another path using the redirect helper in your router:
-
#
-
# get "/stories" => redirect("/posts")
-
#
-
# == Unicode character routes
-
#
-
# You can specify unicode character routes in your router:
-
#
-
# get "こんにちは" => "welcome#index"
-
#
-
# == Routing to Rack Applications
-
#
-
# Instead of a String, like <tt>posts#index</tt>, which corresponds to the
-
# index action in the PostsController, you can specify any Rack application
-
# as the endpoint for a matcher:
-
#
-
# get "/application.js" => Sprockets
-
#
-
# == Reloading routes
-
#
-
# You can reload routes if you feel you must:
-
#
-
# Rails.application.reload_routes!
-
#
-
# This will clear all named routes and reload routes.rb if the file has been modified from
-
# last load. To absolutely force reloading, use <tt>reload!</tt>.
-
#
-
# == Testing Routes
-
#
-
# The two main methods for testing your routes:
-
#
-
# === +assert_routing+
-
#
-
# def test_movie_route_properly_splits
-
# opts = {controller: "plugin", action: "checkout", id: "2"}
-
# assert_routing "plugin/checkout/2", opts
-
# end
-
#
-
# +assert_routing+ lets you test whether or not the route properly resolves into options.
-
#
-
# === +assert_recognizes+
-
#
-
# def test_route_has_options
-
# opts = {controller: "plugin", action: "show", id: "12"}
-
# assert_recognizes opts, "/plugins/show/12"
-
# end
-
#
-
# Note the subtle difference between the two: +assert_routing+ tests that
-
# a URL fits options while +assert_recognizes+ tests that a URL
-
# breaks into parameters properly.
-
#
-
# In tests you can simply pass the URL or named route to +get+ or +post+.
-
#
-
# def send_to_jail
-
# get '/jail'
-
# assert_response :success
-
# assert_template "jail/front"
-
# end
-
#
-
# def goes_to_login
-
# get login_url
-
# #...
-
# end
-
#
-
# == View a list of all your routes
-
#
-
# rake routes
-
#
-
# Target specific controllers by prefixing the command with <tt>CONTROLLER=x</tt>.
-
#
-
1
module Routing
-
1
extend ActiveSupport::Autoload
-
-
1
autoload :Mapper
-
1
autoload :RouteSet
-
1
autoload :RoutesProxy
-
1
autoload :UrlFor
-
1
autoload :PolymorphicRoutes
-
-
1
SEPARATORS = %w( / . ? ) #:nodoc:
-
1
HTTP_METHODS = [:get, :head, :post, :patch, :put, :delete, :options] #:nodoc:
-
end
-
end
-
1
require 'delegate'
-
1
require 'active_support/core_ext/string/strip'
-
-
1
module ActionDispatch
-
1
module Routing
-
1
class RouteWrapper < SimpleDelegator
-
1
def endpoint
-
rack_app ? rack_app.inspect : "#{controller}##{action}"
-
end
-
-
1
def constraints
-
requirements.except(:controller, :action)
-
end
-
-
1
def rack_app(app = self.app)
-
@rack_app ||= begin
-
class_name = app.class.name.to_s
-
if class_name == "ActionDispatch::Routing::Mapper::Constraints"
-
rack_app(app.app)
-
elsif ActionDispatch::Routing::Redirect === app || class_name !~ /^ActionDispatch::Routing/
-
app
-
end
-
end
-
end
-
-
1
def verb
-
super.source.gsub(/[$^]/, '')
-
end
-
-
1
def path
-
super.spec.to_s
-
end
-
-
1
def name
-
super.to_s
-
end
-
-
1
def regexp
-
__getobj__.path.to_regexp
-
end
-
-
1
def json_regexp
-
str = regexp.inspect.
-
sub('\\A' , '^').
-
sub('\\Z' , '$').
-
sub('\\z' , '$').
-
sub(/^\// , '').
-
sub(/\/[a-z]*$/ , '').
-
gsub(/\(\?#.+\)/ , '').
-
gsub(/\(\?-\w+:/ , '(').
-
gsub(/\s/ , '')
-
Regexp.new(str).source
-
end
-
-
1
def reqs
-
@reqs ||= begin
-
reqs = endpoint
-
reqs += " #{constraints.to_s}" unless constraints.empty?
-
reqs
-
end
-
end
-
-
1
def controller
-
requirements[:controller] || ':controller'
-
end
-
-
1
def action
-
requirements[:action] || ':action'
-
end
-
-
1
def internal?
-
controller.to_s =~ %r{\Arails/(info|mailers|welcome)} || path =~ %r{\A#{Rails.application.config.assets.prefix}\z}
-
end
-
-
1
def engine?
-
rack_app && rack_app.respond_to?(:routes)
-
end
-
end
-
-
##
-
# This class is just used for displaying route information when someone
-
# executes `rake routes` or looks at the RoutingError page.
-
# People should not use this class.
-
1
class RoutesInspector # :nodoc:
-
1
def initialize(routes)
-
@engines = {}
-
@routes = routes
-
end
-
-
1
def format(formatter, filter = nil)
-
routes_to_display = filter_routes(filter)
-
-
routes = collect_routes(routes_to_display)
-
-
if routes.none?
-
formatter.no_routes
-
return formatter.result
-
end
-
-
formatter.header routes
-
formatter.section routes
-
-
@engines.each do |name, engine_routes|
-
formatter.section_title "Routes for #{name}"
-
formatter.section engine_routes
-
end
-
-
formatter.result
-
end
-
-
1
private
-
-
1
def filter_routes(filter)
-
if filter
-
@routes.select { |route| route.defaults[:controller] == filter }
-
else
-
@routes
-
end
-
end
-
-
1
def collect_routes(routes)
-
routes.collect do |route|
-
RouteWrapper.new(route)
-
end.reject do |route|
-
route.internal?
-
end.collect do |route|
-
collect_engine_routes(route)
-
-
{ name: route.name,
-
verb: route.verb,
-
path: route.path,
-
reqs: route.reqs,
-
regexp: route.json_regexp }
-
end
-
end
-
-
1
def collect_engine_routes(route)
-
name = route.endpoint
-
return unless route.engine?
-
return if @engines[name]
-
-
routes = route.rack_app.routes
-
if routes.is_a?(ActionDispatch::Routing::RouteSet)
-
@engines[name] = collect_routes(routes.routes)
-
end
-
end
-
end
-
-
1
class ConsoleFormatter
-
1
def initialize
-
@buffer = []
-
end
-
-
1
def result
-
@buffer.join("\n")
-
end
-
-
1
def section_title(title)
-
@buffer << "\n#{title}:"
-
end
-
-
1
def section(routes)
-
@buffer << draw_section(routes)
-
end
-
-
1
def header(routes)
-
@buffer << draw_header(routes)
-
end
-
-
1
def no_routes
-
@buffer << <<-MESSAGE.strip_heredoc
-
You don't have any routes defined!
-
-
Please add some routes in config/routes.rb.
-
-
For more information about routes, see the Rails guide: http://guides.rubyonrails.org/routing.html.
-
MESSAGE
-
end
-
-
1
private
-
1
def draw_section(routes)
-
header_lengths = ['Prefix', 'Verb', 'URI Pattern'].map(&:length)
-
name_width, verb_width, path_width = widths(routes).zip(header_lengths).map(&:max)
-
-
routes.map do |r|
-
"#{r[:name].rjust(name_width)} #{r[:verb].ljust(verb_width)} #{r[:path].ljust(path_width)} #{r[:reqs]}"
-
end
-
end
-
-
1
def draw_header(routes)
-
name_width, verb_width, path_width = widths(routes)
-
-
"#{"Prefix".rjust(name_width)} #{"Verb".ljust(verb_width)} #{"URI Pattern".ljust(path_width)} Controller#Action"
-
end
-
-
1
def widths(routes)
-
[routes.map { |r| r[:name].length }.max || 0,
-
routes.map { |r| r[:verb].length }.max || 0,
-
routes.map { |r| r[:path].length }.max || 0]
-
end
-
end
-
-
1
class HtmlTableFormatter
-
1
def initialize(view)
-
@view = view
-
@buffer = []
-
end
-
-
1
def section_title(title)
-
@buffer << %(<tr><th colspan="4">#{title}</th></tr>)
-
end
-
-
1
def section(routes)
-
@buffer << @view.render(partial: "routes/route", collection: routes)
-
end
-
-
# the header is part of the HTML page, so we don't construct it here.
-
1
def header(routes)
-
end
-
-
1
def no_routes
-
@buffer << <<-MESSAGE.strip_heredoc
-
<p>You don't have any routes defined!</p>
-
<ul>
-
<li>Please add some routes in <tt>config/routes.rb</tt>.</li>
-
<li>
-
For more information about routes, please see the Rails guide
-
<a href="http://guides.rubyonrails.org/routing.html">Rails Routing from the Outside In</a>.
-
</li>
-
</ul>
-
MESSAGE
-
end
-
-
1
def result
-
@view.raw @view.render(layout: "routes/table") {
-
@view.raw @buffer.join("\n")
-
}
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/hash/except'
-
1
require 'active_support/core_ext/hash/reverse_merge'
-
1
require 'active_support/core_ext/hash/slice'
-
1
require 'active_support/core_ext/enumerable'
-
1
require 'active_support/core_ext/array/extract_options'
-
1
require 'active_support/core_ext/module/remove_method'
-
1
require 'active_support/inflector'
-
1
require 'action_dispatch/routing/redirection'
-
-
1
module ActionDispatch
-
1
module Routing
-
1
class Mapper
-
1
URL_OPTIONS = [:protocol, :subdomain, :domain, :host, :port]
-
1
SCOPE_OPTIONS = [:path, :shallow_path, :as, :shallow_prefix, :module,
-
:controller, :action, :path_names, :constraints,
-
:shallow, :blocks, :defaults, :options]
-
-
1
class Constraints #:nodoc:
-
1
def self.new(app, constraints, request = Rack::Request)
-
59
if constraints.any?
-
super(app, constraints, request)
-
else
-
59
app
-
end
-
end
-
-
1
attr_reader :app, :constraints
-
-
1
def initialize(app, constraints, request)
-
@app, @constraints, @request = app, constraints, request
-
end
-
-
1
def matches?(env)
-
req = @request.new(env)
-
-
@constraints.all? do |constraint|
-
(constraint.respond_to?(:matches?) && constraint.matches?(req)) ||
-
(constraint.respond_to?(:call) && constraint.call(*constraint_args(constraint, req)))
-
end
-
ensure
-
req.reset_parameters
-
end
-
-
1
def call(env)
-
matches?(env) ? @app.call(env) : [ 404, {'X-Cascade' => 'pass'}, [] ]
-
end
-
-
1
private
-
1
def constraint_args(constraint, request)
-
constraint.arity == 1 ? [request] : [request.symbolized_path_parameters, request]
-
end
-
end
-
-
1
class Mapping #:nodoc:
-
1
IGNORE_OPTIONS = [:to, :as, :via, :on, :constraints, :defaults, :only, :except, :anchor, :shallow, :shallow_path, :shallow_prefix, :format]
-
1
ANCHOR_CHARACTERS_REGEX = %r{\A(\\A|\^)|(\\Z|\\z|\$)\Z}
-
1
WILDCARD_PATH = %r{\*([^/\)]+)\)?$}
-
-
1
attr_reader :scope, :path, :options, :requirements, :conditions, :defaults
-
-
1
def initialize(set, scope, path, options)
-
59
@set, @scope, @path, @options = set, scope, path, options
-
59
@requirements, @conditions, @defaults = {}, {}, {}
-
-
59
normalize_options!
-
59
normalize_path!
-
59
normalize_requirements!
-
59
normalize_conditions!
-
59
normalize_defaults!
-
end
-
-
1
def to_route
-
59
[ app, conditions, requirements, defaults, options[:as], options[:anchor] ]
-
end
-
-
1
private
-
-
1
def normalize_path!
-
59
raise ArgumentError, "path is required" if @path.blank?
-
59
@path = Mapper.normalize_path(@path)
-
-
59
if required_format?
-
@path = "#{@path}.:format"
-
59
elsif optional_format?
-
57
@path = "#{@path}(.:format)"
-
end
-
end
-
-
1
def required_format?
-
59
options[:format] == true
-
end
-
-
1
def optional_format?
-
59
options[:format] != false && !path.include?(':format') && !path.end_with?('/')
-
end
-
-
1
def normalize_options!
-
59
@options.reverse_merge!(scope[:options]) if scope[:options]
-
59
path_without_format = path.sub(/\(\.:format\)$/, '')
-
-
# Add a constraint for wildcard route to make it non-greedy and match the
-
# optional format part of the route by default
-
59
if path_without_format.match(WILDCARD_PATH) && @options[:format] != false
-
@options[$1.to_sym] ||= /.+?/
-
end
-
-
59
if path_without_format.match(':controller')
-
raise ArgumentError, ":controller segment is not allowed within a namespace block" if scope[:module]
-
-
# Add a default constraint for :controller path segments that matches namespaced
-
# controllers with default routes like :controller/:action/:id(.:format), e.g:
-
# GET /admin/products/show/1
-
# => { controller: 'admin/products', action: 'show', id: '1' }
-
@options[:controller] ||= /.+?/
-
end
-
-
59
@options.merge!(default_controller_and_action)
-
end
-
-
1
def normalize_requirements!
-
59
constraints.each do |key, requirement|
-
next unless segment_keys.include?(key) || key == :controller
-
verify_regexp_requirement(requirement) if requirement.is_a?(Regexp)
-
@requirements[key] = requirement
-
end
-
-
59
if options[:format] == true
-
@requirements[:format] ||= /.+/
-
59
elsif Regexp === options[:format]
-
@requirements[:format] = options[:format]
-
59
elsif String === options[:format]
-
@requirements[:format] = Regexp.compile(options[:format])
-
end
-
end
-
-
1
def verify_regexp_requirement(requirement)
-
if requirement.source =~ ANCHOR_CHARACTERS_REGEX
-
raise ArgumentError, "Regexp anchor characters are not allowed in routing requirements: #{requirement.inspect}"
-
end
-
-
if requirement.multiline?
-
raise ArgumentError, "Regexp multiline option is not allowed in routing requirements: #{requirement.inspect}"
-
end
-
end
-
-
1
def normalize_defaults!
-
59
@defaults.merge!(scope[:defaults]) if scope[:defaults]
-
59
@defaults.merge!(options[:defaults]) if options[:defaults]
-
-
59
options.each do |key, default|
-
302
unless Regexp === default || IGNORE_OPTIONS.include?(key)
-
116
@defaults[key] = default
-
end
-
end
-
-
59
if options[:constraints].is_a?(Hash)
-
options[:constraints].each do |key, default|
-
if URL_OPTIONS.include?(key) && (String === default || Fixnum === default)
-
@defaults[key] ||= default
-
end
-
end
-
end
-
-
59
if Regexp === options[:format]
-
@defaults[:format] = nil
-
59
elsif String === options[:format]
-
@defaults[:format] = options[:format]
-
end
-
end
-
-
1
def normalize_conditions!
-
59
@conditions[:path_info] = path
-
-
59
constraints.each do |key, condition|
-
unless segment_keys.include?(key) || key == :controller
-
@conditions[key] = condition
-
end
-
end
-
-
59
required_defaults = []
-
59
options.each do |key, required_default|
-
303
unless segment_keys.include?(key) || IGNORE_OPTIONS.include?(key) || Regexp === required_default
-
116
required_defaults << key
-
end
-
end
-
59
@conditions[:required_defaults] = required_defaults
-
-
59
via_all = options.delete(:via) if options[:via] == :all
-
-
59
if !via_all && options[:via].blank?
-
msg = "You should not use the `match` method in your router without specifying an HTTP method.\n" \
-
"If you want to expose your action to both GET and POST, add `via: [:get, :post]` option.\n" \
-
"If you want to expose your action to GET, use `get` in the router:\n" \
-
" Instead of: match \"controller#action\"\n" \
-
" Do: get \"controller#action\""
-
raise msg
-
end
-
-
59
if via = options[:via]
-
116
@conditions[:request_method] = Array(via).map { |m| m.to_s.dasherize.upcase }
-
end
-
end
-
-
1
def app
-
59
Constraints.new(endpoint, blocks, @set.request_class)
-
end
-
-
1
def default_controller_and_action
-
59
if to.respond_to?(:call)
-
1
{ }
-
else
-
58
if to.is_a?(String)
-
9
controller, action = to.split('#')
-
elsif to.is_a?(Symbol)
-
action = to.to_s
-
end
-
-
58
controller ||= default_controller
-
58
action ||= default_action
-
-
58
if @scope[:module] && !controller.is_a?(Regexp)
-
8
if controller =~ %r{\A/}
-
controller = controller[1..-1]
-
else
-
8
controller = [@scope[:module], controller].compact.join("/").presence
-
end
-
end
-
-
58
if controller.is_a?(String) && controller =~ %r{\A/}
-
raise ArgumentError, "controller name should not start with a slash"
-
end
-
-
58
controller = controller.to_s unless controller.is_a?(Regexp)
-
58
action = action.to_s unless action.is_a?(Regexp)
-
-
58
if controller.blank? && segment_keys.exclude?(:controller)
-
message = "Missing :controller key on routes definition, please check your routes."
-
raise ArgumentError, message
-
end
-
-
58
if action.blank? && segment_keys.exclude?(:action)
-
message = "Missing :action key on routes definition, please check your routes."
-
raise ArgumentError, message
-
end
-
-
58
if controller.is_a?(String) && controller !~ /\A[a-z_0-9\/]*\z/
-
message = "'#{controller}' is not a supported controller name. This can lead to potential routing problems."
-
message << " See http://guides.rubyonrails.org/routing.html#specifying-a-controller-to-use"
-
raise ArgumentError, message
-
end
-
-
58
hash = {}
-
58
hash[:controller] = controller unless controller.blank?
-
58
hash[:action] = action unless action.blank?
-
58
hash
-
end
-
end
-
-
1
def blocks
-
59
if options[:constraints].present? && !options[:constraints].is_a?(Hash)
-
[options[:constraints]]
-
else
-
59
scope[:blocks] || []
-
end
-
end
-
-
1
def constraints
-
@constraints ||= {}.tap do |constraints|
-
59
constraints.merge!(scope[:constraints]) if scope[:constraints]
-
-
59
options.except(*IGNORE_OPTIONS).each do |key, option|
-
116
constraints[key] = option if Regexp === option
-
end
-
-
59
constraints.merge!(options[:constraints]) if options[:constraints].is_a?(Hash)
-
118
end
-
end
-
-
1
def segment_keys
-
400
@segment_keys ||= path_pattern.names.map{ |s| s.to_sym }
-
end
-
-
1
def path_pattern
-
59
Journey::Path::Pattern.new(strexp)
-
end
-
-
1
def strexp
-
59
Journey::Router::Strexp.compile(path, requirements, SEPARATORS)
-
end
-
-
1
def endpoint
-
59
to.respond_to?(:call) ? to : dispatcher
-
end
-
-
1
def dispatcher
-
58
Routing::RouteSet::Dispatcher.new(:defaults => defaults)
-
end
-
-
1
def to
-
235
options[:to]
-
end
-
-
1
def default_controller
-
49
options[:controller] || scope[:controller]
-
end
-
-
1
def default_action
-
49
options[:action] || scope[:action]
-
end
-
end
-
-
# Invokes Journey::Router::Utils.normalize_path and ensure that
-
# (:locale) becomes (/:locale) instead of /(:locale). Except
-
# for root cases, where the latter is the correct one.
-
1
def self.normalize_path(path)
-
130
path = Journey::Router::Utils.normalize_path(path)
-
130
path.gsub!(%r{/(\(+)/?}, '\1/') unless path =~ %r{^/\(+[^)]+\)$}
-
130
path
-
end
-
-
1
def self.normalize_name(name)
-
15
normalize_path(name)[1..-1].tr("/", "_")
-
end
-
-
1
module Base
-
# You can specify what Rails should route "/" to with the root method:
-
#
-
# root to: 'pages#main'
-
#
-
# For options, see +match+, as +root+ uses it internally.
-
#
-
# You can also pass a string which will expand
-
#
-
# root 'pages#main'
-
#
-
# You should put the root route at the top of <tt>config/routes.rb</tt>,
-
# because this means it will be matched first. As this is the most popular route
-
# of most Rails applications, this is beneficial.
-
1
def root(options = {})
-
1
match '/', { :as => :root, :via => :get }.merge!(options)
-
end
-
-
# Matches a url pattern to one or more routes.
-
#
-
# You should not use the `match` method in your router
-
# without specifying an HTTP method.
-
#
-
# If you want to expose your action to both GET and POST, use:
-
#
-
# # sets :controller, :action and :id in params
-
# match ':controller/:action/:id', via: [:get, :post]
-
#
-
# Note that +:controller+, +:action+, +:id+ are interpreted as url query
-
# parameters and thus available as +params+
-
# in an action.
-
#
-
# If you want to expose your action to GET, use `get` in the router:
-
#
-
# Instead of:
-
#
-
# match ":controller/:action/:id"
-
#
-
# Do:
-
#
-
# get ":controller/:action/:id"
-
#
-
# Two of these symbols are special, +:controller+ maps to the controller
-
# and +:action+ to the controller's action. A pattern can also map
-
# wildcard segments (globs) to params:
-
#
-
# get 'songs/*category/:title', to: 'songs#show'
-
#
-
# # 'songs/rock/classic/stairway-to-heaven' sets
-
# # params[:category] = 'rock/classic'
-
# # params[:title] = 'stairway-to-heaven'
-
#
-
# When a pattern points to an internal route, the route's +:action+ and
-
# +:controller+ should be set in options or hash shorthand. Examples:
-
#
-
# match 'photos/:id' => 'photos#show', via: [:get]
-
# match 'photos/:id', to: 'photos#show', via: [:get]
-
# match 'photos/:id', controller: 'photos', action: 'show', via: [:get]
-
#
-
# A pattern can also point to a +Rack+ endpoint i.e. anything that
-
# responds to +call+:
-
#
-
# match 'photos/:id', to: lambda {|hash| [200, {}, ["Coming soon"]] }, via: [:get]
-
# match 'photos/:id', to: PhotoRackApp, via: [:get]
-
# # Yes, controller actions are just rack endpoints
-
# match 'photos/:id', to: PhotosController.action(:show), via: [:get]
-
#
-
# Because requesting various HTTP verbs with a single action has security
-
# implications, you must either specify the actions in
-
# the via options or use one of the HtttpHelpers[rdoc-ref:HttpHelpers]
-
# instead +match+
-
#
-
# === Options
-
#
-
# Any options not seen here are passed on as params with the url.
-
#
-
# [:controller]
-
# The route's controller.
-
#
-
# [:action]
-
# The route's action.
-
#
-
# [:param]
-
# Overrides the default resource identifier `:id` (name of the
-
# dynamic segment used to generate the routes).
-
# You can access that segment from your controller using
-
# <tt>params[<:param>]</tt>.
-
#
-
# [:path]
-
# The path prefix for the routes.
-
#
-
# [:module]
-
# The namespace for :controller.
-
#
-
# match 'path', to: 'c#a', module: 'sekret', controller: 'posts', via: [:get]
-
# # => Sekret::PostsController
-
#
-
# See <tt>Scoping#namespace</tt> for its scope equivalent.
-
#
-
# [:as]
-
# The name used to generate routing helpers.
-
#
-
# [:via]
-
# Allowed HTTP verb(s) for route.
-
#
-
# match 'path', to: 'c#a', via: :get
-
# match 'path', to: 'c#a', via: [:get, :post]
-
# match 'path', to: 'c#a', via: :all
-
#
-
# [:to]
-
# Points to a +Rack+ endpoint. Can be an object that responds to
-
# +call+ or a string representing a controller's action.
-
#
-
# match 'path', to: 'controller#action', via: [:get]
-
# match 'path', to: lambda { |env| [200, {}, ["Success!"]] }, via: [:get]
-
# match 'path', to: RackApp, via: [:get]
-
#
-
# [:on]
-
# Shorthand for wrapping routes in a specific RESTful context. Valid
-
# values are +:member+, +:collection+, and +:new+. Only use within
-
# <tt>resource(s)</tt> block. For example:
-
#
-
# resource :bar do
-
# match 'foo', to: 'c#a', on: :member, via: [:get, :post]
-
# end
-
#
-
# Is equivalent to:
-
#
-
# resource :bar do
-
# member do
-
# match 'foo', to: 'c#a', via: [:get, :post]
-
# end
-
# end
-
#
-
# [:constraints]
-
# Constrains parameters with a hash of regular expressions
-
# or an object that responds to <tt>matches?</tt>. In addition, constraints
-
# other than path can also be specified with any object
-
# that responds to <tt>===</tt> (eg. String, Array, Range, etc.).
-
#
-
# match 'path/:id', constraints: { id: /[A-Z]\d{5}/ }, via: [:get]
-
#
-
# match 'json_only', constraints: { format: 'json' }, via: [:get]
-
#
-
# class Whitelist
-
# def matches?(request) request.remote_ip == '1.2.3.4' end
-
# end
-
# match 'path', to: 'c#a', constraints: Whitelist.new, via: [:get]
-
#
-
# See <tt>Scoping#constraints</tt> for more examples with its scope
-
# equivalent.
-
#
-
# [:defaults]
-
# Sets defaults for parameters
-
#
-
# # Sets params[:format] to 'jpg' by default
-
# match 'path', to: 'c#a', defaults: { format: 'jpg' }, via: [:get]
-
#
-
# See <tt>Scoping#defaults</tt> for its scope equivalent.
-
#
-
# [:anchor]
-
# Boolean to anchor a <tt>match</tt> pattern. Default is true. When set to
-
# false, the pattern matches any request prefixed with the given path.
-
#
-
# # Matches any request starting with 'path'
-
# match 'path', to: 'c#a', anchor: false, via: [:get]
-
#
-
# [:format]
-
# Allows you to specify the default value for optional +format+
-
# segment or disable it by supplying +false+.
-
1
def match(path, options=nil)
-
end
-
-
# Mount a Rack-based application to be used within the application.
-
#
-
# mount SomeRackApp, at: "some_route"
-
#
-
# Alternatively:
-
#
-
# mount(SomeRackApp => "some_route")
-
#
-
# For options, see +match+, as +mount+ uses it internally.
-
#
-
# All mounted applications come with routing helpers to access them.
-
# These are named after the class specified, so for the above example
-
# the helper is either +some_rack_app_path+ or +some_rack_app_url+.
-
# To customize this helper's name, use the +:as+ option:
-
#
-
# mount(SomeRackApp => "some_route", as: "exciting")
-
#
-
# This will generate the +exciting_path+ and +exciting_url+ helpers
-
# which can be used to navigate to this mounted app.
-
1
def mount(app, options = nil)
-
1
if options
-
path = options.delete(:at)
-
else
-
1
unless Hash === app
-
raise ArgumentError, "must be called with mount point"
-
end
-
-
1
options = app
-
2
app, path = options.find { |k, _| k.respond_to?(:call) }
-
1
options.delete(app) if app
-
end
-
-
1
raise "A rack application must be specified" unless path
-
-
1
options[:as] ||= app_name(app)
-
1
target_as = name_for_action(options[:as], path)
-
1
options[:via] ||= :all
-
-
1
match(path, options.merge(:to => app, :anchor => false, :format => false))
-
-
1
define_generate_prefix(app, target_as)
-
1
self
-
end
-
-
1
def default_url_options=(options)
-
@set.default_url_options = options
-
end
-
1
alias_method :default_url_options, :default_url_options=
-
-
1
def with_default_scope(scope, &block)
-
scope(scope) do
-
instance_exec(&block)
-
end
-
end
-
-
# Query if the following named route was already defined.
-
1
def has_named_route?(name)
-
@set.named_routes.routes[name.to_sym]
-
end
-
-
1
private
-
1
def app_name(app)
-
1
return unless app.respond_to?(:routes)
-
-
if app.respond_to?(:railtie_name)
-
app.railtie_name
-
else
-
class_name = app.class.is_a?(Class) ? app.name : app.class.name
-
ActiveSupport::Inflector.underscore(class_name).tr("/", "_")
-
end
-
end
-
-
1
def define_generate_prefix(app, name)
-
1
return unless app.respond_to?(:routes) && app.routes.respond_to?(:define_mounted_helper)
-
-
_route = @set.named_routes.routes[name.to_sym]
-
_routes = @set
-
app.routes.define_mounted_helper(name)
-
app.routes.singleton_class.class_eval do
-
redefine_method :mounted? do
-
true
-
end
-
-
redefine_method :_generate_prefix do |options|
-
prefix_options = options.slice(*_route.segment_keys)
-
# we must actually delete prefix segment keys to avoid passing them to next url_for
-
_route.segment_keys.each { |k| options.delete(k) }
-
_routes.url_helpers.send("#{name}_path", prefix_options)
-
end
-
end
-
end
-
end
-
-
1
module HttpHelpers
-
# Define a route that only recognizes HTTP GET.
-
# For supported arguments, see match[rdoc-ref:Base#match]
-
#
-
# get 'bacon', to: 'food#bacon'
-
1
def get(*args, &block)
-
34
map_method(:get, args, &block)
-
end
-
-
# Define a route that only recognizes HTTP POST.
-
# For supported arguments, see match[rdoc-ref:Base#match]
-
#
-
# post 'bacon', to: 'food#bacon'
-
1
def post(*args, &block)
-
5
map_method(:post, args, &block)
-
end
-
-
# Define a route that only recognizes HTTP PATCH.
-
# For supported arguments, see match[rdoc-ref:Base#match]
-
#
-
# patch 'bacon', to: 'food#bacon'
-
1
def patch(*args, &block)
-
5
map_method(:patch, args, &block)
-
end
-
-
# Define a route that only recognizes HTTP PUT.
-
# For supported arguments, see match[rdoc-ref:Base#match]
-
#
-
# put 'bacon', to: 'food#bacon'
-
1
def put(*args, &block)
-
5
map_method(:put, args, &block)
-
end
-
-
# Define a route that only recognizes HTTP DELETE.
-
# For supported arguments, see match[rdoc-ref:Base#match]
-
#
-
# delete 'broccoli', to: 'food#broccoli'
-
1
def delete(*args, &block)
-
5
map_method(:delete, args, &block)
-
end
-
-
1
private
-
1
def map_method(method, args, &block)
-
54
options = args.extract_options!
-
54
options[:via] = method
-
54
match(*args, options, &block)
-
54
self
-
end
-
end
-
-
# You may wish to organize groups of controllers under a namespace.
-
# Most commonly, you might group a number of administrative controllers
-
# under an +admin+ namespace. You would place these controllers under
-
# the <tt>app/controllers/admin</tt> directory, and you can group them
-
# together in your router:
-
#
-
# namespace "admin" do
-
# resources :posts, :comments
-
# end
-
#
-
# This will create a number of routes for each of the posts and comments
-
# controller. For <tt>Admin::PostsController</tt>, Rails will create:
-
#
-
# GET /admin/posts
-
# GET /admin/posts/new
-
# POST /admin/posts
-
# GET /admin/posts/1
-
# GET /admin/posts/1/edit
-
# PATCH/PUT /admin/posts/1
-
# DELETE /admin/posts/1
-
#
-
# If you want to route /posts (without the prefix /admin) to
-
# <tt>Admin::PostsController</tt>, you could use
-
#
-
# scope module: "admin" do
-
# resources :posts
-
# end
-
#
-
# or, for a single case
-
#
-
# resources :posts, module: "admin"
-
#
-
# If you want to route /admin/posts to +PostsController+
-
# (without the Admin:: module prefix), you could use
-
#
-
# scope "/admin" do
-
# resources :posts
-
# end
-
#
-
# or, for a single case
-
#
-
# resources :posts, path: "/admin/posts"
-
#
-
# In each of these cases, the named routes remain the same as if you did
-
# not use scope. In the last case, the following paths map to
-
# +PostsController+:
-
#
-
# GET /admin/posts
-
# GET /admin/posts/new
-
# POST /admin/posts
-
# GET /admin/posts/1
-
# GET /admin/posts/1/edit
-
# PATCH/PUT /admin/posts/1
-
# DELETE /admin/posts/1
-
1
module Scoping
-
# Scopes a set of routes to the given default options.
-
#
-
# Take the following route definition as an example:
-
#
-
# scope path: ":account_id", as: "account" do
-
# resources :projects
-
# end
-
#
-
# This generates helpers such as +account_projects_path+, just like +resources+ does.
-
# The difference here being that the routes generated are like /:account_id/projects,
-
# rather than /accounts/:account_id/projects.
-
#
-
# === Options
-
#
-
# Takes same options as <tt>Base#match</tt> and <tt>Resources#resources</tt>.
-
#
-
# # route /posts (without the prefix /admin) to <tt>Admin::PostsController</tt>
-
# scope module: "admin" do
-
# resources :posts
-
# end
-
#
-
# # prefix the posts resource's requests with '/admin'
-
# scope path: "/admin" do
-
# resources :posts
-
# end
-
#
-
# # prefix the routing helper name: +sekret_posts_path+ instead of +posts_path+
-
# scope as: "sekret" do
-
# resources :posts
-
# end
-
1
def scope(*args)
-
39
options = args.extract_options!.dup
-
39
recover = {}
-
-
39
options[:path] = args.flatten.join('/') if args.any?
-
39
options[:constraints] ||= {}
-
-
39
unless nested_scope?
-
37
options[:shallow_path] ||= options[:path] if options.key?(:path)
-
37
options[:shallow_prefix] ||= options[:as] if options.key?(:as)
-
end
-
-
39
if options[:constraints].is_a?(Hash)
-
39
defaults = options[:constraints].select do
-
|k, v| URL_OPTIONS.include?(k) && (v.is_a?(String) || v.is_a?(Fixnum))
-
end
-
-
39
(options[:defaults] ||= {}).reverse_merge!(defaults)
-
else
-
block, options[:constraints] = options[:constraints], {}
-
end
-
-
39
SCOPE_OPTIONS.each do |option|
-
507
if option == :blocks
-
39
value = block
-
elsif option == :options
-
39
value = options
-
else
-
429
value = options.delete(option)
-
end
-
-
507
if value
-
191
recover[option] = @scope[option]
-
191
@scope[option] = send("merge_#{option}_scope", @scope[option], value)
-
end
-
end
-
-
39
yield
-
39
self
-
ensure
-
39
@scope.merge!(recover)
-
end
-
-
# Scopes routes to a specific controller
-
#
-
# controller "food" do
-
# match "bacon", action: "bacon"
-
# end
-
1
def controller(controller, options={})
-
options[:controller] = controller
-
scope(options) { yield }
-
end
-
-
# Scopes routes to a specific namespace. For example:
-
#
-
# namespace :admin do
-
# resources :posts
-
# end
-
#
-
# This generates the following routes:
-
#
-
# admin_posts GET /admin/posts(.:format) admin/posts#index
-
# admin_posts POST /admin/posts(.:format) admin/posts#create
-
# new_admin_post GET /admin/posts/new(.:format) admin/posts#new
-
# edit_admin_post GET /admin/posts/:id/edit(.:format) admin/posts#edit
-
# admin_post GET /admin/posts/:id(.:format) admin/posts#show
-
# admin_post PATCH/PUT /admin/posts/:id(.:format) admin/posts#update
-
# admin_post DELETE /admin/posts/:id(.:format) admin/posts#destroy
-
#
-
# === Options
-
#
-
# The +:path+, +:as+, +:module+, +:shallow_path+ and +:shallow_prefix+
-
# options all default to the name of the namespace.
-
#
-
# For options, see <tt>Base#match</tt>. For +:shallow_path+ option, see
-
# <tt>Resources#resources</tt>.
-
#
-
# # accessible through /sekret/posts rather than /admin/posts
-
# namespace :admin, path: "sekret" do
-
# resources :posts
-
# end
-
#
-
# # maps to <tt>Sekret::PostsController</tt> rather than <tt>Admin::PostsController</tt>
-
# namespace :admin, module: "sekret" do
-
# resources :posts
-
# end
-
#
-
# # generates +sekret_posts_path+ rather than +admin_posts_path+
-
# namespace :admin, as: "sekret" do
-
# resources :posts
-
# end
-
1
def namespace(path, options = {})
-
2
path = path.to_s
-
-
2
defaults = {
-
module: path,
-
path: options.fetch(:path, path),
-
as: options.fetch(:as, path),
-
shallow_path: options.fetch(:path, path),
-
shallow_prefix: options.fetch(:as, path)
-
}
-
-
4
scope(defaults.merge!(options)) { yield }
-
end
-
-
# === Parameter Restriction
-
# Allows you to constrain the nested routes based on a set of rules.
-
# For instance, in order to change the routes to allow for a dot character in the +id+ parameter:
-
#
-
# constraints(id: /\d+\.\d+/) do
-
# resources :posts
-
# end
-
#
-
# Now routes such as +/posts/1+ will no longer be valid, but +/posts/1.1+ will be.
-
# The +id+ parameter must match the constraint passed in for this example.
-
#
-
# You may use this to also restrict other parameters:
-
#
-
# resources :posts do
-
# constraints(post_id: /\d+\.\d+/) do
-
# resources :comments
-
# end
-
# end
-
#
-
# === Restricting based on IP
-
#
-
# Routes can also be constrained to an IP or a certain range of IP addresses:
-
#
-
# constraints(ip: /192\.168\.\d+\.\d+/) do
-
# resources :posts
-
# end
-
#
-
# Any user connecting from the 192.168.* range will be able to see this resource,
-
# where as any user connecting outside of this range will be told there is no such route.
-
#
-
# === Dynamic request matching
-
#
-
# Requests to routes can be constrained based on specific criteria:
-
#
-
# constraints(lambda { |req| req.env["HTTP_USER_AGENT"] =~ /iPhone/ }) do
-
# resources :iphones
-
# end
-
#
-
# You are able to move this logic out into a class if it is too complex for routes.
-
# This class must have a +matches?+ method defined on it which either returns +true+
-
# if the user should be given access to that route, or +false+ if the user should not.
-
#
-
# class Iphone
-
# def self.matches?(request)
-
# request.env["HTTP_USER_AGENT"] =~ /iPhone/
-
# end
-
# end
-
#
-
# An expected place for this code would be +lib/constraints+.
-
#
-
# This class is then used like this:
-
#
-
# constraints(Iphone) do
-
# resources :iphones
-
# end
-
1
def constraints(constraints = {})
-
scope(:constraints => constraints) { yield }
-
end
-
-
# Allows you to set default parameters for a route, such as this:
-
# defaults id: 'home' do
-
# match 'scoped_pages/(:id)', to: 'pages#show'
-
# end
-
# Using this, the +:id+ parameter here will default to 'home'.
-
1
def defaults(defaults = {})
-
scope(:defaults => defaults) { yield }
-
end
-
-
1
private
-
1
def merge_path_scope(parent, child) #:nodoc:
-
29
Mapper.normalize_path("#{parent}/#{child}")
-
end
-
-
1
def merge_shallow_path_scope(parent, child) #:nodoc:
-
27
Mapper.normalize_path("#{parent}/#{child}")
-
end
-
-
1
def merge_as_scope(parent, child) #:nodoc:
-
4
parent ? "#{parent}_#{child}" : child
-
end
-
-
1
def merge_shallow_prefix_scope(parent, child) #:nodoc:
-
2
parent ? "#{parent}_#{child}" : child
-
end
-
-
1
def merge_module_scope(parent, child) #:nodoc:
-
2
parent ? "#{parent}/#{child}" : child
-
end
-
-
1
def merge_controller_scope(parent, child) #:nodoc:
-
10
child
-
end
-
-
1
def merge_action_scope(parent, child) #:nodoc:
-
child
-
end
-
-
1
def merge_path_names_scope(parent, child) #:nodoc:
-
merge_options_scope(parent, child)
-
end
-
-
1
def merge_constraints_scope(parent, child) #:nodoc:
-
39
merge_options_scope(parent, child)
-
end
-
-
1
def merge_defaults_scope(parent, child) #:nodoc:
-
39
merge_options_scope(parent, child)
-
end
-
-
1
def merge_blocks_scope(parent, child) #:nodoc:
-
merged = parent ? parent.dup : []
-
merged << child if child
-
merged
-
end
-
-
1
def merge_options_scope(parent, child) #:nodoc:
-
117
(parent || {}).except(*override_keys(child)).merge!(child)
-
end
-
-
1
def merge_shallow_scope(parent, child) #:nodoc:
-
child ? true : false
-
end
-
-
1
def override_keys(child) #:nodoc:
-
117
child.key?(:only) || child.key?(:except) ? [:only, :except] : []
-
end
-
end
-
-
# Resource routing allows you to quickly declare all of the common routes
-
# for a given resourceful controller. Instead of declaring separate routes
-
# for your +index+, +show+, +new+, +edit+, +create+, +update+ and +destroy+
-
# actions, a resourceful route declares them in a single line of code:
-
#
-
# resources :photos
-
#
-
# Sometimes, you have a resource that clients always look up without
-
# referencing an ID. A common example, /profile always shows the profile of
-
# the currently logged in user. In this case, you can use a singular resource
-
# to map /profile (rather than /profile/:id) to the show action.
-
#
-
# resource :profile
-
#
-
# It's common to have resources that are logically children of other
-
# resources:
-
#
-
# resources :magazines do
-
# resources :ads
-
# end
-
#
-
# You may wish to organize groups of controllers under a namespace. Most
-
# commonly, you might group a number of administrative controllers under
-
# an +admin+ namespace. You would place these controllers under the
-
# <tt>app/controllers/admin</tt> directory, and you can group them together
-
# in your router:
-
#
-
# namespace "admin" do
-
# resources :posts, :comments
-
# end
-
#
-
# By default the +:id+ parameter doesn't accept dots. If you need to
-
# use dots as part of the +:id+ parameter add a constraint which
-
# overrides this restriction, e.g:
-
#
-
# resources :articles, id: /[^\/]+/
-
#
-
# This allows any character other than a slash as part of your +:id+.
-
#
-
1
module Resources
-
# CANONICAL_ACTIONS holds all actions that does not need a prefix or
-
# a path appended since they fit properly in their scope level.
-
1
VALID_ON_OPTIONS = [:new, :collection, :member]
-
1
RESOURCE_OPTIONS = [:as, :controller, :path, :only, :except, :param, :concerns]
-
1
CANONICAL_ACTIONS = %w(index create new show update destroy)
-
1
RESOURCE_METHOD_SCOPES = [:collection, :member, :new]
-
1
RESOURCE_SCOPES = [:resource, :resources]
-
-
1
class Resource #:nodoc:
-
1
attr_reader :controller, :path, :options, :param
-
-
1
def initialize(entities, options = {})
-
10
@name = entities.to_s
-
10
@path = (options[:path] || @name).to_s
-
10
@controller = (options[:controller] || @name).to_s
-
10
@as = options[:as]
-
10
@param = (options[:param] || :id).to_sym
-
10
@options = options
-
10
@shallow = false
-
end
-
-
1
def default_actions
-
35
[:index, :create, :new, :show, :update, :destroy, :edit]
-
end
-
-
1
def actions
-
70
if only = @options[:only]
-
35
Array(only).map(&:to_sym)
-
35
elsif except = @options[:except]
-
default_actions - Array(except).map(&:to_sym)
-
else
-
35
default_actions
-
end
-
end
-
-
1
def name
-
20
@as || @name
-
end
-
-
1
def plural
-
98
@plural ||= name.to_s
-
end
-
-
1
def singular
-
102
@singular ||= name.to_s.singularize
-
end
-
-
1
alias :member_name :singular
-
-
# Checks for uncountable plurals, and appends "_index" if the plural
-
# and singular form are the same.
-
1
def collection_name
-
49
singular == plural ? "#{plural}_index" : plural
-
end
-
-
1
def resource_scope
-
10
{ :controller => controller }
-
end
-
-
1
alias :collection_scope :path
-
-
1
def member_scope
-
10
"#{path}/:#{param}"
-
end
-
-
1
alias :shallow_scope :member_scope
-
-
1
def new_scope(new_path)
-
5
"#{path}/#{new_path}"
-
end
-
-
1
def nested_param
-
2
:"#{singular}_#{param}"
-
end
-
-
1
def nested_scope
-
2
"#{path}/:#{nested_param}"
-
end
-
-
1
def shallow=(value)
-
10
@shallow = value
-
end
-
-
1
def shallow?
-
@shallow
-
end
-
end
-
-
1
class SingletonResource < Resource #:nodoc:
-
1
def initialize(entities, options)
-
super
-
@as = nil
-
@controller = (options[:controller] || plural).to_s
-
@as = options[:as]
-
end
-
-
1
def default_actions
-
[:show, :create, :update, :destroy, :new, :edit]
-
end
-
-
1
def plural
-
@plural ||= name.to_s.pluralize
-
end
-
-
1
def singular
-
@singular ||= name.to_s
-
end
-
-
1
alias :member_name :singular
-
1
alias :collection_name :singular
-
-
1
alias :member_scope :path
-
1
alias :nested_scope :path
-
end
-
-
1
def resources_path_names(options)
-
@scope[:path_names].merge!(options)
-
end
-
-
# Sometimes, you have a resource that clients always look up without
-
# referencing an ID. A common example, /profile always shows the
-
# profile of the currently logged in user. In this case, you can use
-
# a singular resource to map /profile (rather than /profile/:id) to
-
# the show action:
-
#
-
# resource :profile
-
#
-
# creates six different routes in your application, all mapping to
-
# the +Profiles+ controller (note that the controller is named after
-
# the plural):
-
#
-
# GET /profile/new
-
# POST /profile
-
# GET /profile
-
# GET /profile/edit
-
# PATCH/PUT /profile
-
# DELETE /profile
-
#
-
# === Options
-
# Takes same options as +resources+.
-
1
def resource(*resources, &block)
-
options = resources.extract_options!.dup
-
-
if apply_common_behavior_for(:resource, resources, options, &block)
-
return self
-
end
-
-
resource_scope(:resource, SingletonResource.new(resources.pop, options)) do
-
yield if block_given?
-
-
concerns(options[:concerns]) if options[:concerns]
-
-
collection do
-
post :create
-
end if parent_resource.actions.include?(:create)
-
-
new do
-
get :new
-
end if parent_resource.actions.include?(:new)
-
-
set_member_mappings_for_resource
-
end
-
-
self
-
end
-
-
# In Rails, a resourceful route provides a mapping between HTTP verbs
-
# and URLs and controller actions. By convention, each action also maps
-
# to particular CRUD operations in a database. A single entry in the
-
# routing file, such as
-
#
-
# resources :photos
-
#
-
# creates seven different routes in your application, all mapping to
-
# the +Photos+ controller:
-
#
-
# GET /photos
-
# GET /photos/new
-
# POST /photos
-
# GET /photos/:id
-
# GET /photos/:id/edit
-
# PATCH/PUT /photos/:id
-
# DELETE /photos/:id
-
#
-
# Resources can also be nested infinitely by using this block syntax:
-
#
-
# resources :photos do
-
# resources :comments
-
# end
-
#
-
# This generates the following comments routes:
-
#
-
# GET /photos/:photo_id/comments
-
# GET /photos/:photo_id/comments/new
-
# POST /photos/:photo_id/comments
-
# GET /photos/:photo_id/comments/:id
-
# GET /photos/:photo_id/comments/:id/edit
-
# PATCH/PUT /photos/:photo_id/comments/:id
-
# DELETE /photos/:photo_id/comments/:id
-
#
-
# === Options
-
# Takes same options as <tt>Base#match</tt> as well as:
-
#
-
# [:path_names]
-
# Allows you to change the segment component of the +edit+ and +new+ actions.
-
# Actions not specified are not changed.
-
#
-
# resources :posts, path_names: { new: "brand_new" }
-
#
-
# The above example will now change /posts/new to /posts/brand_new
-
#
-
# [:path]
-
# Allows you to change the path prefix for the resource.
-
#
-
# resources :posts, path: 'postings'
-
#
-
# The resource and all segments will now route to /postings instead of /posts
-
#
-
# [:only]
-
# Only generate routes for the given actions.
-
#
-
# resources :cows, only: :show
-
# resources :cows, only: [:show, :index]
-
#
-
# [:except]
-
# Generate all routes except for the given actions.
-
#
-
# resources :cows, except: :show
-
# resources :cows, except: [:show, :index]
-
#
-
# [:shallow]
-
# Generates shallow routes for nested resource(s). When placed on a parent resource,
-
# generates shallow routes for all nested resources.
-
#
-
# resources :posts, shallow: true do
-
# resources :comments
-
# end
-
#
-
# Is the same as:
-
#
-
# resources :posts do
-
# resources :comments, except: [:show, :edit, :update, :destroy]
-
# end
-
# resources :comments, only: [:show, :edit, :update, :destroy]
-
#
-
# This allows URLs for resources that otherwise would be deeply nested such
-
# as a comment on a blog post like <tt>/posts/a-long-permalink/comments/1234</tt>
-
# to be shortened to just <tt>/comments/1234</tt>.
-
#
-
# [:shallow_path]
-
# Prefixes nested shallow routes with the specified path.
-
#
-
# scope shallow_path: "sekret" do
-
# resources :posts do
-
# resources :comments, shallow: true
-
# end
-
# end
-
#
-
# The +comments+ resource here will have the following routes generated for it:
-
#
-
# post_comments GET /posts/:post_id/comments(.:format)
-
# post_comments POST /posts/:post_id/comments(.:format)
-
# new_post_comment GET /posts/:post_id/comments/new(.:format)
-
# edit_comment GET /sekret/comments/:id/edit(.:format)
-
# comment GET /sekret/comments/:id(.:format)
-
# comment PATCH/PUT /sekret/comments/:id(.:format)
-
# comment DELETE /sekret/comments/:id(.:format)
-
#
-
# [:shallow_prefix]
-
# Prefixes nested shallow route names with specified prefix.
-
#
-
# scope shallow_prefix: "sekret" do
-
# resources :posts do
-
# resources :comments, shallow: true
-
# end
-
# end
-
#
-
# The +comments+ resource here will have the following routes generated for it:
-
#
-
# post_comments GET /posts/:post_id/comments(.:format)
-
# post_comments POST /posts/:post_id/comments(.:format)
-
# new_post_comment GET /posts/:post_id/comments/new(.:format)
-
# edit_sekret_comment GET /comments/:id/edit(.:format)
-
# sekret_comment GET /comments/:id(.:format)
-
# sekret_comment PATCH/PUT /comments/:id(.:format)
-
# sekret_comment DELETE /comments/:id(.:format)
-
#
-
# [:format]
-
# Allows you to specify the default value for optional +format+
-
# segment or disable it by supplying +false+.
-
#
-
# === Examples
-
#
-
# # routes call <tt>Admin::PostsController</tt>
-
# resources :posts, module: "admin"
-
#
-
# # resource actions are at /admin/posts.
-
# resources :posts, path: "admin/posts"
-
1
def resources(*resources, &block)
-
12
options = resources.extract_options!.dup
-
-
12
if apply_common_behavior_for(:resources, resources, options, &block)
-
2
return self
-
end
-
-
10
resource_scope(:resources, Resource.new(resources.pop, options)) do
-
10
yield if block_given?
-
-
10
concerns(options[:concerns]) if options[:concerns]
-
-
10
collection do
-
10
get :index if parent_resource.actions.include?(:index)
-
10
post :create if parent_resource.actions.include?(:create)
-
end
-
-
new do
-
5
get :new
-
10
end if parent_resource.actions.include?(:new)
-
-
10
set_member_mappings_for_resource
-
end
-
-
10
self
-
end
-
-
# To add a route to the collection:
-
#
-
# resources :photos do
-
# collection do
-
# get 'search'
-
# end
-
# end
-
#
-
# This will enable Rails to recognize paths such as <tt>/photos/search</tt>
-
# with GET, and route to the search action of +PhotosController+. It will also
-
# create the <tt>search_photos_url</tt> and <tt>search_photos_path</tt>
-
# route helpers.
-
1
def collection
-
10
unless resource_scope?
-
raise ArgumentError, "can't use collection outside resource(s) scope"
-
end
-
-
10
with_scope_level(:collection) do
-
10
scope(parent_resource.collection_scope) do
-
10
yield
-
end
-
end
-
end
-
-
# To add a member route, add a member block into the resource block:
-
#
-
# resources :photos do
-
# member do
-
# get 'preview'
-
# end
-
# end
-
#
-
# This will recognize <tt>/photos/1/preview</tt> with GET, and route to the
-
# preview action of +PhotosController+. It will also create the
-
# <tt>preview_photo_url</tt> and <tt>preview_photo_path</tt> helpers.
-
1
def member
-
10
unless resource_scope?
-
raise ArgumentError, "can't use member outside resource(s) scope"
-
end
-
-
10
with_scope_level(:member) do
-
10
if shallow?
-
shallow_scope(parent_resource.member_scope) { yield }
-
else
-
20
scope(parent_resource.member_scope) { yield }
-
end
-
end
-
end
-
-
1
def new
-
5
unless resource_scope?
-
raise ArgumentError, "can't use new outside resource(s) scope"
-
end
-
-
5
with_scope_level(:new) do
-
5
scope(parent_resource.new_scope(action_path(:new))) do
-
5
yield
-
end
-
end
-
end
-
-
1
def nested
-
2
unless resource_scope?
-
raise ArgumentError, "can't use nested outside resource(s) scope"
-
end
-
-
2
with_scope_level(:nested) do
-
2
if shallow? && shallow_nesting_depth >= 1
-
shallow_scope(parent_resource.nested_scope, nested_options) { yield }
-
else
-
4
scope(parent_resource.nested_scope, nested_options) { yield }
-
end
-
end
-
end
-
-
# See ActionDispatch::Routing::Mapper::Scoping#namespace
-
1
def namespace(path, options = {})
-
2
if resource_scope?
-
nested { super }
-
else
-
2
super
-
end
-
end
-
-
1
def shallow
-
scope(:shallow => true) do
-
yield
-
end
-
end
-
-
1
def shallow?
-
12
parent_resource.instance_of?(Resource) && @scope[:shallow]
-
end
-
-
# match 'path' => 'controller#action'
-
# match 'path', to: 'controller#action'
-
# match 'path', 'otherpath', on: :member, via: :get
-
1
def match(path, *rest)
-
59
if rest.empty? && Hash === path
-
options = path
-
path, to = options.find { |name, _value| name.is_a?(String) }
-
options[:to] = to
-
options.delete(path)
-
paths = [path]
-
else
-
59
options = rest.pop || {}
-
59
paths = [path] + rest
-
end
-
-
59
options[:anchor] = true unless options.key?(:anchor)
-
-
59
if options[:on] && !VALID_ON_OPTIONS.include?(options[:on])
-
raise ArgumentError, "Unknown scope #{on.inspect} given to :on"
-
end
-
-
59
if @scope[:controller] && @scope[:action]
-
options[:to] ||= "#{@scope[:controller]}##{@scope[:action]}"
-
end
-
-
59
paths.each do |_path|
-
59
route_options = options.dup
-
59
route_options[:path] ||= _path if _path.is_a?(String)
-
-
59
path_without_format = _path.to_s.sub(/\(\.:format\)$/, '')
-
59
if using_match_shorthand?(path_without_format, route_options)
-
1
route_options[:to] ||= path_without_format.gsub(%r{^/}, "").sub(%r{/([^/]*)$}, '#\1')
-
1
route_options[:to].tr!("-", "_")
-
end
-
-
59
decomposed_match(_path, route_options)
-
end
-
59
self
-
end
-
-
1
def using_match_shorthand?(path, options)
-
59
path && (options[:to] || options[:action]).nil? && path =~ %r{/[\w/]+$}
-
end
-
-
1
def decomposed_match(path, options) # :nodoc:
-
59
if on = options.delete(:on)
-
send(on) { decomposed_match(path, options) }
-
else
-
59
case @scope[:scope_level]
-
when :resources
-
nested { decomposed_match(path, options) }
-
when :resource
-
member { decomposed_match(path, options) }
-
else
-
59
add_route(path, options)
-
end
-
end
-
end
-
-
1
def add_route(action, options) # :nodoc:
-
59
path = path_for_action(action, options.delete(:path))
-
59
action = action.to_s.dup
-
-
59
if action =~ /^[\w\-\/]+$/
-
58
options[:action] ||= action.tr('-', '_') unless action.include?("/")
-
else
-
1
action = nil
-
end
-
-
59
if !options.fetch(:as, true)
-
1
options.delete(:as)
-
else
-
58
options[:as] = name_for_action(options[:as], action)
-
end
-
-
59
mapping = Mapping.new(@set, @scope, URI.parser.escape(path), options)
-
59
app, conditions, requirements, defaults, as, anchor = mapping.to_route
-
59
@set.add_route(app, conditions, requirements, defaults, as, anchor)
-
end
-
-
1
def root(path, options={})
-
1
if path.is_a?(String)
-
options[:to] = path
-
elsif path.is_a?(Hash) and options.empty?
-
1
options = path
-
else
-
raise ArgumentError, "must be called with a path and/or options"
-
end
-
-
1
if @scope[:scope_level] == :resources
-
with_scope_level(:root) do
-
scope(parent_resource.path) do
-
super(options)
-
end
-
end
-
else
-
1
super(options)
-
end
-
end
-
-
1
protected
-
-
1
def parent_resource #:nodoc:
-
280
@scope[:scope_level_resource]
-
end
-
-
1
def apply_common_behavior_for(method, resources, options, &block) #:nodoc:
-
12
if resources.length > 1
-
resources.each { |r| send(method, r, options, &block) }
-
return true
-
end
-
-
12
if options.delete(:shallow)
-
shallow do
-
send(method, resources.pop, options, &block)
-
end
-
return true
-
end
-
-
12
if resource_scope?
-
4
nested { send(method, resources.pop, options, &block) }
-
2
return true
-
end
-
-
10
options.keys.each do |k|
-
5
(options[:constraints] ||= {})[k] = options.delete(k) if options[k].is_a?(Regexp)
-
end
-
-
10
scope_options = options.slice!(*RESOURCE_OPTIONS)
-
10
unless scope_options.empty?
-
scope(scope_options) do
-
send(method, resources.pop, options, &block)
-
end
-
return true
-
end
-
-
10
unless action_options?(options)
-
5
options.merge!(scope_action_options) if scope_action_options?
-
end
-
-
10
false
-
end
-
-
1
def action_options?(options) #:nodoc:
-
10
options[:only] || options[:except]
-
end
-
-
1
def scope_action_options? #:nodoc:
-
5
@scope[:options] && (@scope[:options][:only] || @scope[:options][:except])
-
end
-
-
1
def scope_action_options #:nodoc:
-
@scope[:options].slice(:only, :except)
-
end
-
-
1
def resource_scope? #:nodoc:
-
41
RESOURCE_SCOPES.include? @scope[:scope_level]
-
end
-
-
1
def resource_method_scope? #:nodoc:
-
98
RESOURCE_METHOD_SCOPES.include? @scope[:scope_level]
-
end
-
-
1
def nested_scope? #:nodoc:
-
39
@scope[:scope_level] == :nested
-
end
-
-
1
def with_exclusive_scope
-
begin
-
old_name_prefix, old_path = @scope[:as], @scope[:path]
-
@scope[:as], @scope[:path] = nil, nil
-
-
with_scope_level(:exclusive) do
-
yield
-
end
-
ensure
-
@scope[:as], @scope[:path] = old_name_prefix, old_path
-
end
-
end
-
-
1
def with_scope_level(kind)
-
37
old, @scope[:scope_level] = @scope[:scope_level], kind
-
37
yield
-
ensure
-
37
@scope[:scope_level] = old
-
end
-
-
1
def resource_scope(kind, resource) #:nodoc:
-
10
resource.shallow = @scope[:shallow]
-
10
old_resource, @scope[:scope_level_resource] = @scope[:scope_level_resource], resource
-
10
@nesting.push(resource)
-
-
10
with_scope_level(kind) do
-
20
scope(parent_resource.resource_scope) { yield }
-
end
-
ensure
-
10
@nesting.pop
-
10
@scope[:scope_level_resource] = old_resource
-
end
-
-
1
def nested_options #:nodoc:
-
2
options = { :as => parent_resource.member_name }
-
options[:constraints] = {
-
parent_resource.nested_param => param_constraint
-
2
} if param_constraint?
-
-
2
options
-
end
-
-
1
def nesting_depth #:nodoc:
-
@nesting.size
-
end
-
-
1
def shallow_nesting_depth #:nodoc:
-
@nesting.select(&:shallow?).size
-
end
-
-
1
def param_constraint? #:nodoc:
-
2
@scope[:constraints] && @scope[:constraints][parent_resource.param].is_a?(Regexp)
-
end
-
-
1
def param_constraint #:nodoc:
-
@scope[:constraints][parent_resource.param]
-
end
-
-
1
def canonical_action?(action, flag) #:nodoc:
-
113
flag && resource_method_scope? && CANONICAL_ACTIONS.include?(action.to_s)
-
end
-
-
1
def shallow_scope(path, options = {}) #:nodoc:
-
old_name_prefix, old_path = @scope[:as], @scope[:path]
-
@scope[:as], @scope[:path] = @scope[:shallow_prefix], @scope[:shallow_path]
-
-
scope(path, options) { yield }
-
ensure
-
@scope[:as], @scope[:path] = old_name_prefix, old_path
-
end
-
-
1
def path_for_action(action, path) #:nodoc:
-
59
if canonical_action?(action, path.blank?)
-
44
@scope[:path].to_s
-
else
-
15
"#{@scope[:path]}/#{action_path(action, path)}"
-
end
-
end
-
-
1
def action_path(name, path = nil) #:nodoc:
-
20
name = name.to_sym if name.is_a?(String)
-
20
path || @scope[:path_names][name] || name.to_s
-
end
-
-
1
def prefix_name_for_action(as, action) #:nodoc:
-
59
if as
-
5
prefix = as
-
elsif !canonical_action?(action, @scope[:scope_level])
-
10
prefix = action
-
end
-
59
prefix.to_s.tr('-', '_') if prefix
-
end
-
-
1
def name_for_action(as, action) #:nodoc:
-
59
prefix = prefix_name_for_action(as, action)
-
59
prefix = Mapper.normalize_name(prefix) if prefix
-
59
name_prefix = @scope[:as]
-
-
59
if parent_resource
-
49
return nil unless as || action
-
-
49
collection_name = parent_resource.collection_name
-
49
member_name = parent_resource.member_name
-
end
-
-
59
name = case @scope[:scope_level]
-
when :nested
-
[name_prefix, prefix]
-
when :collection
-
15
[prefix, name_prefix, collection_name]
-
when :new
-
5
[prefix, :new, name_prefix, member_name]
-
when :member
-
29
[prefix, name_prefix, member_name]
-
when :root
-
[name_prefix, collection_name, prefix]
-
else
-
10
[name_prefix, member_name, prefix]
-
end
-
-
59
if candidate = name.select(&:present?).join("_").presence
-
# If a name was not explicitly given, we check if it is valid
-
# and return nil in case it isn't. Otherwise, we pass the invalid name
-
# forward so the underlying router engine treats it and raises an exception.
-
59
if as.nil?
-
1686
candidate unless @set.routes.find { |r| r.name == candidate } || candidate !~ /\A[_a-z]/i
-
else
-
5
candidate
-
end
-
end
-
end
-
-
1
def set_member_mappings_for_resource
-
10
member do
-
10
get :edit if parent_resource.actions.include?(:edit)
-
10
get :show if parent_resource.actions.include?(:show)
-
10
if parent_resource.actions.include?(:update)
-
5
patch :update
-
5
put :update
-
end
-
10
delete :destroy if parent_resource.actions.include?(:destroy)
-
end
-
end
-
end
-
-
# Routing Concerns allow you to declare common routes that can be reused
-
# inside others resources and routes.
-
#
-
# concern :commentable do
-
# resources :comments
-
# end
-
#
-
# concern :image_attachable do
-
# resources :images, only: :index
-
# end
-
#
-
# These concerns are used in Resources routing:
-
#
-
# resources :messages, concerns: [:commentable, :image_attachable]
-
#
-
# or in a scope or namespace:
-
#
-
# namespace :posts do
-
# concerns :commentable
-
# end
-
1
module Concerns
-
# Define a routing concern using a name.
-
#
-
# Concerns may be defined inline, using a block, or handled by
-
# another object, by passing that object as the second parameter.
-
#
-
# The concern object, if supplied, should respond to <tt>call</tt>,
-
# which will receive two parameters:
-
#
-
# * The current mapper
-
# * A hash of options which the concern object may use
-
#
-
# Options may also be used by concerns defined in a block by accepting
-
# a block parameter. So, using a block, you might do something as
-
# simple as limit the actions available on certain resources, passing
-
# standard resource options through the concern:
-
#
-
# concern :commentable do |options|
-
# resources :comments, options
-
# end
-
#
-
# resources :posts, concerns: :commentable
-
# resources :archived_posts do
-
# # Don't allow comments on archived posts
-
# concerns :commentable, only: [:index, :show]
-
# end
-
#
-
# Or, using a callable object, you might implement something more
-
# specific to your application, which would be out of place in your
-
# routes file.
-
#
-
# # purchasable.rb
-
# class Purchasable
-
# def initialize(defaults = {})
-
# @defaults = defaults
-
# end
-
#
-
# def call(mapper, options = {})
-
# options = @defaults.merge(options)
-
# mapper.resources :purchases
-
# mapper.resources :receipts
-
# mapper.resources :returns if options[:returnable]
-
# end
-
# end
-
#
-
# # routes.rb
-
# concern :purchasable, Purchasable.new(returnable: true)
-
#
-
# resources :toys, concerns: :purchasable
-
# resources :electronics, concerns: :purchasable
-
# resources :pets do
-
# concerns :purchasable, returnable: false
-
# end
-
#
-
# Any routing helpers can be used inside a concern. If using a
-
# callable, they're accessible from the Mapper that's passed to
-
# <tt>call</tt>.
-
1
def concern(name, callable = nil, &block)
-
callable ||= lambda { |mapper, options| mapper.instance_exec(options, &block) }
-
@concerns[name] = callable
-
end
-
-
# Use the named concerns
-
#
-
# resources :posts do
-
# concerns :commentable
-
# end
-
#
-
# concerns also work in any routes helper that you want to use:
-
#
-
# namespace :posts do
-
# concerns :commentable
-
# end
-
1
def concerns(*args)
-
options = args.extract_options!
-
args.flatten.each do |name|
-
if concern = @concerns[name]
-
concern.call(self, options)
-
else
-
raise ArgumentError, "No concern named #{name} was found!"
-
end
-
end
-
end
-
end
-
-
1
def initialize(set) #:nodoc:
-
2
@set = set
-
2
@scope = { :path_names => @set.resources_path_names }
-
2
@concerns = {}
-
2
@nesting = []
-
end
-
-
1
include Base
-
1
include HttpHelpers
-
1
include Redirection
-
1
include Scoping
-
1
include Concerns
-
1
include Resources
-
end
-
end
-
end
-
1
require 'action_controller/model_naming'
-
-
1
module ActionDispatch
-
1
module Routing
-
# Polymorphic URL helpers are methods for smart resolution to a named route call when
-
# given an Active Record model instance. They are to be used in combination with
-
# ActionController::Resources.
-
#
-
# These methods are useful when you want to generate correct URL or path to a RESTful
-
# resource without having to know the exact type of the record in question.
-
#
-
# Nested resources and/or namespaces are also supported, as illustrated in the example:
-
#
-
# polymorphic_url([:admin, @article, @comment])
-
#
-
# results in:
-
#
-
# admin_article_comment_url(@article, @comment)
-
#
-
# == Usage within the framework
-
#
-
# Polymorphic URL helpers are used in a number of places throughout the \Rails framework:
-
#
-
# * <tt>url_for</tt>, so you can use it with a record as the argument, e.g.
-
# <tt>url_for(@article)</tt>;
-
# * ActionView::Helpers::FormHelper uses <tt>polymorphic_path</tt>, so you can write
-
# <tt>form_for(@article)</tt> without having to specify <tt>:url</tt> parameter for the form
-
# action;
-
# * <tt>redirect_to</tt> (which, in fact, uses <tt>url_for</tt>) so you can write
-
# <tt>redirect_to(post)</tt> in your controllers;
-
# * ActionView::Helpers::AtomFeedHelper, so you don't have to explicitly specify URLs
-
# for feed entries.
-
#
-
# == Prefixed polymorphic helpers
-
#
-
# In addition to <tt>polymorphic_url</tt> and <tt>polymorphic_path</tt> methods, a
-
# number of prefixed helpers are available as a shorthand to <tt>action: "..."</tt>
-
# in options. Those are:
-
#
-
# * <tt>edit_polymorphic_url</tt>, <tt>edit_polymorphic_path</tt>
-
# * <tt>new_polymorphic_url</tt>, <tt>new_polymorphic_path</tt>
-
#
-
# Example usage:
-
#
-
# edit_polymorphic_path(@post) # => "/posts/1/edit"
-
# polymorphic_path(@post, format: :pdf) # => "/posts/1.pdf"
-
#
-
# == Usage with mounted engines
-
#
-
# If you are using a mounted engine and you need to use a polymorphic_url
-
# pointing at the engine's routes, pass in the engine's route proxy as the first
-
# argument to the method. For example:
-
#
-
# polymorphic_url([blog, @post]) # calls blog.post_path(@post)
-
# form_for([blog, @post]) # => "/blog/posts/1"
-
#
-
1
module PolymorphicRoutes
-
1
include ActionController::ModelNaming
-
-
# Constructs a call to a named RESTful route for the given record and returns the
-
# resulting URL string. For example:
-
#
-
# # calls post_url(post)
-
# polymorphic_url(post) # => "http://example.com/posts/1"
-
# polymorphic_url([blog, post]) # => "http://example.com/blogs/1/posts/1"
-
# polymorphic_url([:admin, blog, post]) # => "http://example.com/admin/blogs/1/posts/1"
-
# polymorphic_url([user, :blog, post]) # => "http://example.com/users/1/blog/posts/1"
-
# polymorphic_url(Comment) # => "http://example.com/comments"
-
#
-
# ==== Options
-
#
-
# * <tt>:action</tt> - Specifies the action prefix for the named route:
-
# <tt>:new</tt> or <tt>:edit</tt>. Default is no prefix.
-
# * <tt>:routing_type</tt> - Allowed values are <tt>:path</tt> or <tt>:url</tt>.
-
# Default is <tt>:url</tt>.
-
#
-
# Also includes all the options from <tt>url_for</tt>. These include such
-
# things as <tt>:anchor</tt> or <tt>:trailing_slash</tt>. Example usage
-
# is given below:
-
#
-
# polymorphic_url([blog, post], anchor: 'my_anchor')
-
# # => "http://example.com/blogs/1/posts/1#my_anchor"
-
# polymorphic_url([blog, post], anchor: 'my_anchor', script_name: "/my_app")
-
# # => "http://example.com/my_app/blogs/1/posts/1#my_anchor"
-
#
-
# For all of these options, see the documentation for <tt>url_for</tt>.
-
#
-
# ==== Functionality
-
#
-
# # an Article record
-
# polymorphic_url(record) # same as article_url(record)
-
#
-
# # a Comment record
-
# polymorphic_url(record) # same as comment_url(record)
-
#
-
# # it recognizes new records and maps to the collection
-
# record = Comment.new
-
# polymorphic_url(record) # same as comments_url()
-
#
-
# # the class of a record will also map to the collection
-
# polymorphic_url(Comment) # same as comments_url()
-
#
-
1
def polymorphic_url(record_or_hash_or_array, options = {})
-
if record_or_hash_or_array.kind_of?(Array)
-
record_or_hash_or_array = record_or_hash_or_array.compact
-
if record_or_hash_or_array.first.is_a?(ActionDispatch::Routing::RoutesProxy)
-
proxy = record_or_hash_or_array.shift
-
end
-
record_or_hash_or_array = record_or_hash_or_array[0] if record_or_hash_or_array.size == 1
-
end
-
-
record = extract_record(record_or_hash_or_array)
-
record = convert_to_model(record)
-
-
args = Array === record_or_hash_or_array ?
-
record_or_hash_or_array.dup :
-
[ record_or_hash_or_array ]
-
-
inflection = if options[:action] && options[:action].to_s == "new"
-
args.pop
-
:singular
-
elsif (record.respond_to?(:persisted?) && !record.persisted?)
-
args.pop
-
:plural
-
elsif record.is_a?(Class)
-
args.pop
-
:plural
-
else
-
:singular
-
end
-
-
args.delete_if {|arg| arg.is_a?(Symbol) || arg.is_a?(String)}
-
named_route = build_named_route_call(record_or_hash_or_array, inflection, options)
-
-
url_options = options.except(:action, :routing_type)
-
unless url_options.empty?
-
args.last.kind_of?(Hash) ? args.last.merge!(url_options) : args << url_options
-
end
-
-
args.collect! { |a| convert_to_model(a) }
-
-
(proxy || self).send(named_route, *args)
-
end
-
-
# Returns the path component of a URL for the given record. It uses
-
# <tt>polymorphic_url</tt> with <tt>routing_type: :path</tt>.
-
1
def polymorphic_path(record_or_hash_or_array, options = {})
-
polymorphic_url(record_or_hash_or_array, options.merge(:routing_type => :path))
-
end
-
-
1
%w(edit new).each do |action|
-
2
module_eval <<-EOT, __FILE__, __LINE__ + 1
-
def #{action}_polymorphic_url(record_or_hash, options = {}) # def edit_polymorphic_url(record_or_hash, options = {})
-
polymorphic_url( # polymorphic_url(
-
record_or_hash, # record_or_hash,
-
options.merge(:action => "#{action}")) # options.merge(:action => "edit"))
-
end # end
-
#
-
def #{action}_polymorphic_path(record_or_hash, options = {}) # def edit_polymorphic_path(record_or_hash, options = {})
-
polymorphic_url( # polymorphic_url(
-
record_or_hash, # record_or_hash,
-
options.merge(:action => "#{action}", :routing_type => :path)) # options.merge(:action => "edit", :routing_type => :path))
-
end # end
-
EOT
-
end
-
-
1
private
-
1
def action_prefix(options)
-
options[:action] ? "#{options[:action]}_" : ''
-
end
-
-
1
def routing_type(options)
-
options[:routing_type] || :url
-
end
-
-
1
def build_named_route_call(records, inflection, options = {})
-
if records.is_a?(Array)
-
record = records.pop
-
route = records.map do |parent|
-
if parent.is_a?(Symbol) || parent.is_a?(String)
-
parent
-
else
-
model_name_from_record_or_class(parent).singular_route_key
-
end
-
end
-
else
-
record = extract_record(records)
-
route = []
-
end
-
-
if record.is_a?(Symbol) || record.is_a?(String)
-
route << record
-
elsif record
-
if inflection == :singular
-
route << model_name_from_record_or_class(record).singular_route_key
-
else
-
route << model_name_from_record_or_class(record).route_key
-
end
-
else
-
raise ArgumentError, "Nil location provided. Can't build URI."
-
end
-
-
route << routing_type(options)
-
-
action_prefix(options) + route.join("_")
-
end
-
-
1
def extract_record(record_or_hash_or_array)
-
case record_or_hash_or_array
-
when Array; record_or_hash_or_array.last
-
when Hash; record_or_hash_or_array[:id]
-
else record_or_hash_or_array
-
end
-
end
-
end
-
end
-
end
-
-
1
require 'action_dispatch/http/request'
-
1
require 'active_support/core_ext/uri'
-
1
require 'active_support/core_ext/array/extract_options'
-
1
require 'rack/utils'
-
1
require 'action_controller/metal/exceptions'
-
-
1
module ActionDispatch
-
1
module Routing
-
1
class Redirect # :nodoc:
-
1
attr_reader :status, :block
-
-
1
def initialize(status, block)
-
@status = status
-
@block = block
-
end
-
-
1
def call(env)
-
req = Request.new(env)
-
-
# If any of the path parameters has an invalid encoding then
-
# raise since it's likely to trigger errors further on.
-
req.symbolized_path_parameters.each do |key, value|
-
unless value.valid_encoding?
-
raise ActionController::BadRequest, "Invalid parameter: #{key} => #{value}"
-
end
-
end
-
-
uri = URI.parse(path(req.symbolized_path_parameters, req))
-
-
unless uri.host
-
if relative_path?(uri.path)
-
uri.path = "#{req.script_name}/#{uri.path}"
-
elsif uri.path.empty?
-
uri.path = req.script_name.empty? ? "/" : req.script_name
-
end
-
end
-
-
uri.scheme ||= req.scheme
-
uri.host ||= req.host
-
uri.port ||= req.port unless req.standard_port?
-
-
body = %(<html><body>You are being <a href="#{ERB::Util.h(uri.to_s)}">redirected</a>.</body></html>)
-
-
headers = {
-
'Location' => uri.to_s,
-
'Content-Type' => 'text/html',
-
'Content-Length' => body.length.to_s
-
}
-
-
[ status, headers, [body] ]
-
end
-
-
1
def path(params, request)
-
block.call params, request
-
end
-
-
1
def inspect
-
"redirect(#{status})"
-
end
-
-
1
private
-
1
def relative_path?(path)
-
path && !path.empty? && path[0] != '/'
-
end
-
-
1
def escape(params)
-
Hash[params.map{ |k,v| [k, Rack::Utils.escape(v)] }]
-
end
-
-
1
def escape_fragment(params)
-
Hash[params.map{ |k,v| [k, Journey::Router::Utils.escape_fragment(v)] }]
-
end
-
-
1
def escape_path(params)
-
Hash[params.map{ |k,v| [k, Journey::Router::Utils.escape_path(v)] }]
-
end
-
end
-
-
1
class PathRedirect < Redirect
-
1
URL_PARTS = /\A([^?]+)?(\?[^#]+)?(#.+)?\z/
-
-
1
def path(params, request)
-
if block.match(URL_PARTS)
-
path = interpolation_required?($1, params) ? $1 % escape_path(params) : $1
-
query = interpolation_required?($2, params) ? $2 % escape(params) : $2
-
fragment = interpolation_required?($3, params) ? $3 % escape_fragment(params) : $3
-
-
"#{path}#{query}#{fragment}"
-
else
-
interpolation_required?(block, params) ? block % escape(params) : block
-
end
-
end
-
-
1
def inspect
-
"redirect(#{status}, #{block})"
-
end
-
-
1
private
-
1
def interpolation_required?(string, params)
-
!params.empty? && string && string.match(/%\{\w*\}/)
-
end
-
end
-
-
1
class OptionRedirect < Redirect # :nodoc:
-
1
alias :options :block
-
-
1
def path(params, request)
-
url_options = {
-
:protocol => request.protocol,
-
:host => request.host,
-
:port => request.optional_port,
-
:path => request.path,
-
:params => request.query_parameters
-
}.merge! options
-
-
if !params.empty? && url_options[:path].match(/%\{\w*\}/)
-
url_options[:path] = (url_options[:path] % escape_path(params))
-
end
-
-
unless options[:host] || options[:domain]
-
if relative_path?(url_options[:path])
-
url_options[:path] = "/#{url_options[:path]}"
-
url_options[:script_name] = request.script_name
-
elsif url_options[:path].empty?
-
url_options[:path] = request.script_name.empty? ? "/" : ""
-
url_options[:script_name] = request.script_name
-
end
-
end
-
-
ActionDispatch::Http::URL.url_for url_options
-
end
-
-
1
def inspect
-
"redirect(#{status}, #{options.map{ |k,v| "#{k}: #{v}" }.join(', ')})"
-
end
-
end
-
-
1
module Redirection
-
-
# Redirect any path to another path:
-
#
-
# get "/stories" => redirect("/posts")
-
#
-
# You can also use interpolation in the supplied redirect argument:
-
#
-
# get 'docs/:article', to: redirect('/wiki/%{article}')
-
#
-
# Note that if you return a path without a leading slash then the url is prefixed with the
-
# current SCRIPT_NAME environment variable. This is typically '/' but may be different in
-
# a mounted engine or where the application is deployed to a subdirectory of a website.
-
#
-
# Alternatively you can use one of the other syntaxes:
-
#
-
# The block version of redirect allows for the easy encapsulation of any logic associated with
-
# the redirect in question. Either the params and request are supplied as arguments, or just
-
# params, depending of how many arguments your block accepts. A string is required as a
-
# return value.
-
#
-
# get 'jokes/:number', to: redirect { |params, request|
-
# path = (params[:number].to_i.even? ? "wheres-the-beef" : "i-love-lamp")
-
# "http://#{request.host_with_port}/#{path}"
-
# }
-
#
-
# Note that the +do end+ syntax for the redirect block wouldn't work, as Ruby would pass
-
# the block to +get+ instead of +redirect+. Use <tt>{ ... }</tt> instead.
-
#
-
# The options version of redirect allows you to supply only the parts of the url which need
-
# to change, it also supports interpolation of the path similar to the first example.
-
#
-
# get 'stores/:name', to: redirect(subdomain: 'stores', path: '/%{name}')
-
# get 'stores/:name(*all)', to: redirect(subdomain: 'stores', path: '/%{name}%{all}')
-
#
-
# Finally, an object which responds to call can be supplied to redirect, allowing you to reuse
-
# common redirect routes. The call method must accept two arguments, params and request, and return
-
# a string.
-
#
-
# get 'accounts/:name' => redirect(SubdomainRedirector.new('api'))
-
#
-
1
def redirect(*args, &block)
-
options = args.extract_options!
-
status = options.delete(:status) || 301
-
path = args.shift
-
-
return OptionRedirect.new(status, options) if options.any?
-
return PathRedirect.new(status, path) if String === path
-
-
block = path if path.respond_to? :call
-
raise ArgumentError, "redirection argument not supported" unless block
-
Redirect.new status, block
-
end
-
end
-
end
-
end
-
1
require 'action_dispatch/journey'
-
1
require 'forwardable'
-
1
require 'thread_safe'
-
1
require 'active_support/concern'
-
1
require 'active_support/core_ext/object/to_query'
-
1
require 'active_support/core_ext/hash/slice'
-
1
require 'active_support/core_ext/module/remove_method'
-
1
require 'active_support/core_ext/array/extract_options'
-
1
require 'action_controller/metal/exceptions'
-
1
require 'action_dispatch/http/request'
-
-
1
module ActionDispatch
-
1
module Routing
-
1
class RouteSet #:nodoc:
-
# Since the router holds references to many parts of the system
-
# like engines, controllers and the application itself, inspecting
-
# the route set can actually be really slow, therefore we default
-
# alias inspect to to_s.
-
1
alias inspect to_s
-
-
1
PARAMETERS_KEY = 'action_dispatch.request.path_parameters'
-
-
1
class Dispatcher #:nodoc:
-
1
def initialize(options={})
-
58
@defaults = options[:defaults]
-
58
@glob_param = options.delete(:glob)
-
58
@controller_class_names = ThreadSafe::Cache.new
-
end
-
-
1
def call(env)
-
params = env[PARAMETERS_KEY]
-
-
# If any of the path parameters has an invalid encoding then
-
# raise since it's likely to trigger errors further on.
-
params.each do |key, value|
-
next unless value.respond_to?(:valid_encoding?)
-
-
unless value.valid_encoding?
-
raise ActionController::BadRequest, "Invalid parameter: #{key} => #{value}"
-
end
-
end
-
-
prepare_params!(params)
-
-
# Just raise undefined constant errors if a controller was specified as default.
-
unless controller = controller(params, @defaults.key?(:controller))
-
return [404, {'X-Cascade' => 'pass'}, []]
-
end
-
-
dispatch(controller, params[:action], env)
-
end
-
-
1
def prepare_params!(params)
-
normalize_controller!(params)
-
merge_default_action!(params)
-
split_glob_param!(params) if @glob_param
-
end
-
-
# If this is a default_controller (i.e. a controller specified by the user)
-
# we should raise an error in case it's not found, because it usually means
-
# a user error. However, if the controller was retrieved through a dynamic
-
# segment, as in :controller(/:action), we should simply return nil and
-
# delegate the control back to Rack cascade. Besides, if this is not a default
-
# controller, it means we should respect the @scope[:module] parameter.
-
1
def controller(params, default_controller=true)
-
if params && params.key?(:controller)
-
controller_param = params[:controller]
-
controller_reference(controller_param)
-
end
-
rescue NameError => e
-
raise ActionController::RoutingError, e.message, e.backtrace if default_controller
-
end
-
-
1
private
-
-
1
def controller_reference(controller_param)
-
const_name = @controller_class_names[controller_param] ||= "#{controller_param.camelize}Controller"
-
ActiveSupport::Dependencies.constantize(const_name)
-
end
-
-
1
def dispatch(controller, action, env)
-
controller.action(action).call(env)
-
end
-
-
1
def normalize_controller!(params)
-
params[:controller] = params[:controller].underscore if params.key?(:controller)
-
end
-
-
1
def merge_default_action!(params)
-
params[:action] ||= 'index'
-
end
-
-
1
def split_glob_param!(params)
-
params[@glob_param] = params[@glob_param].split('/').map { |v| URI.parser.unescape(v) }
-
end
-
end
-
-
# A NamedRouteCollection instance is a collection of named routes, and also
-
# maintains an anonymous module that can be used to install helpers for the
-
# named routes.
-
1
class NamedRouteCollection #:nodoc:
-
1
include Enumerable
-
1
attr_reader :routes, :helpers, :module
-
-
1
def initialize
-
1
@routes = {}
-
1
@helpers = []
-
1
@module = Module.new
-
end
-
-
1
def helper_names
-
1
@helpers.map(&:to_s)
-
end
-
-
1
def clear!
-
1
@helpers.each do |helper|
-
@module.remove_possible_method helper
-
end
-
-
1
@routes.clear
-
1
@helpers.clear
-
end
-
-
1
def add(name, route)
-
38
routes[name.to_sym] = route
-
38
define_named_route_methods(name, route)
-
end
-
-
1
def get(name)
-
38
routes[name.to_sym]
-
end
-
-
1
alias []= add
-
1
alias [] get
-
1
alias clear clear!
-
-
1
def each
-
routes.each { |name, route| yield name, route }
-
self
-
end
-
-
1
def names
-
routes.keys
-
end
-
-
1
def length
-
routes.length
-
end
-
-
1
class UrlHelper # :nodoc:
-
1
def self.create(route, options)
-
76
if optimize_helper?(route)
-
76
OptimizedUrlHelper.new(route, options)
-
else
-
new route, options
-
end
-
end
-
-
1
def self.optimize_helper?(route)
-
76
!route.glob? && route.requirements.except(:controller, :action).empty?
-
end
-
-
1
class OptimizedUrlHelper < UrlHelper # :nodoc:
-
1
attr_reader :arg_size
-
-
1
def initialize(route, options)
-
76
super
-
76
@klass = Journey::Router::Utils
-
76
@required_parts = @route.required_parts
-
76
@arg_size = @required_parts.size
-
76
@optimized_path = @route.optimized_path
-
end
-
-
1
def call(t, args)
-
3
if args.size == arg_size && !args.last.is_a?(Hash) && optimize_routes_generation?(t)
-
3
options = @options.dup
-
3
options.merge!(t.url_options) if t.respond_to?(:url_options)
-
3
options[:path] = optimized_helper(args)
-
3
ActionDispatch::Http::URL.url_for(options)
-
else
-
super
-
end
-
end
-
-
1
private
-
-
1
def optimized_helper(args)
-
3
params = Hash[parameterize_args(args)]
-
3
missing_keys = missing_keys(params)
-
-
3
unless missing_keys.empty?
-
raise_generation_error(params, missing_keys)
-
end
-
-
15
@optimized_path.map{ |segment| replace_segment(params, segment) }.join
-
end
-
-
1
def replace_segment(params, segment)
-
12
Symbol === segment ? @klass.escape_segment(params[segment]) : segment
-
end
-
-
1
def optimize_routes_generation?(t)
-
3
t.send(:optimize_routes_generation?)
-
end
-
-
1
def parameterize_args(args)
-
3
@required_parts.zip(args.map(&:to_param))
-
end
-
-
1
def missing_keys(args)
-
6
args.select{ |part, arg| arg.nil? || arg.empty? }.keys
-
end
-
-
1
def raise_generation_error(args, missing_keys)
-
constraints = Hash[@route.requirements.merge(args).sort]
-
message = "No route matches #{constraints.inspect}"
-
message << " missing required keys: #{missing_keys.sort.inspect}"
-
-
raise ActionController::UrlGenerationError, message
-
end
-
end
-
-
1
def initialize(route, options)
-
76
@options = options
-
76
@segment_keys = route.segment_keys.uniq
-
76
@route = route
-
end
-
-
1
def call(t, args)
-
t.url_for(handle_positional_args(t, args, @options, @segment_keys))
-
end
-
-
1
def handle_positional_args(t, args, options, keys)
-
inner_options = args.extract_options!
-
result = options.dup
-
-
if args.size > 0
-
if args.size < keys.size - 1 # take format into account
-
keys -= t.url_options.keys if t.respond_to?(:url_options)
-
keys -= options.keys
-
end
-
keys -= inner_options.keys
-
result.merge!(Hash[keys.zip(args)])
-
end
-
-
result.merge!(inner_options)
-
end
-
end
-
-
1
private
-
# Create a url helper allowing ordered parameters to be associated
-
# with corresponding dynamic segments, so you can do:
-
#
-
# foo_url(bar, baz, bang)
-
#
-
# Instead of:
-
#
-
# foo_url(bar: bar, baz: baz, bang: bang)
-
#
-
# Also allow options hash, so you can do:
-
#
-
# foo_url(bar, baz, bang, sort_by: 'baz')
-
#
-
1
def define_url_helper(route, name, options)
-
76
helper = UrlHelper.create(route, options.dup)
-
-
76
@module.remove_possible_method name
-
76
@module.module_eval do
-
76
define_method(name) do |*args|
-
3
helper.call self, args
-
end
-
end
-
-
76
helpers << name
-
end
-
-
1
def define_named_route_methods(name, route)
-
38
define_url_helper route, :"#{name}_path",
-
route.defaults.merge(:use_route => name, :only_path => true)
-
38
define_url_helper route, :"#{name}_url",
-
route.defaults.merge(:use_route => name, :only_path => false)
-
end
-
end
-
-
1
attr_accessor :formatter, :set, :named_routes, :default_scope, :router
-
1
attr_accessor :disable_clear_and_finalize, :resources_path_names
-
1
attr_accessor :default_url_options, :request_class
-
-
1
alias :routes :set
-
-
1
def self.default_resources_path_names
-
1
{ :new => 'new', :edit => 'edit' }
-
end
-
-
1
def initialize(request_class = ActionDispatch::Request)
-
1
self.named_routes = NamedRouteCollection.new
-
1
self.resources_path_names = self.class.default_resources_path_names.dup
-
1
self.default_url_options = {}
-
1
self.request_class = request_class
-
-
1
@append = []
-
1
@prepend = []
-
1
@disable_clear_and_finalize = false
-
1
@finalized = false
-
-
1
@set = Journey::Routes.new
-
1
@router = Journey::Router.new(@set, {
-
:parameters_key => PARAMETERS_KEY,
-
:request_class => request_class})
-
1
@formatter = Journey::Formatter.new @set
-
end
-
-
1
def draw(&block)
-
1
clear! unless @disable_clear_and_finalize
-
1
eval_block(block)
-
1
finalize! unless @disable_clear_and_finalize
-
nil
-
end
-
-
1
def append(&block)
-
@append << block
-
end
-
-
1
def prepend(&block)
-
1
@prepend << block
-
end
-
-
1
def eval_block(block)
-
2
if block.arity == 1
-
raise "You are using the old router DSL which has been removed in Rails 3.1. " <<
-
"Please check how to update your routes file at: http://www.engineyard.com/blog/2010/the-lowdown-on-routes-in-rails-3/"
-
end
-
2
mapper = Mapper.new(self)
-
2
if default_scope
-
mapper.with_default_scope(default_scope, &block)
-
else
-
2
mapper.instance_exec(&block)
-
end
-
end
-
-
1
def finalize!
-
1
return if @finalized
-
1
@append.each { |blk| eval_block(blk) }
-
1
@finalized = true
-
end
-
-
1
def clear!
-
1
@finalized = false
-
1
named_routes.clear
-
1
set.clear
-
1
formatter.clear
-
2
@prepend.each { |blk| eval_block(blk) }
-
end
-
-
1
module MountedHelpers #:nodoc:
-
1
extend ActiveSupport::Concern
-
1
include UrlFor
-
end
-
-
# Contains all the mounted helpers across different
-
# engines and the `main_app` helper for the application.
-
# You can include this in your classes if you want to
-
# access routes for other engines.
-
1
def mounted_helpers
-
3
MountedHelpers
-
end
-
-
1
def define_mounted_helper(name)
-
1
return if MountedHelpers.method_defined?(name)
-
-
1
routes = self
-
1
MountedHelpers.class_eval do
-
1
define_method "_#{name}" do
-
RoutesProxy.new(routes, _routes_context)
-
end
-
end
-
-
1
MountedHelpers.class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
-
def #{name}
-
@_#{name} ||= _#{name}
-
end
-
RUBY
-
end
-
-
1
def url_helpers
-
@url_helpers ||= begin
-
1
routes = self
-
-
1
Module.new do
-
1
extend ActiveSupport::Concern
-
1
include UrlFor
-
-
# Define url_for in the singleton level so one can do:
-
# Rails.application.routes.url_helpers.url_for(args)
-
1
@_routes = routes
-
1
class << self
-
1
delegate :url_for, :optimize_routes_generation?, :to => '@_routes'
-
end
-
-
# Make named_routes available in the module singleton
-
# as well, so one can do:
-
# Rails.application.routes.url_helpers.posts_path
-
1
extend routes.named_routes.module
-
-
# Any class that includes this module will get all
-
# named routes...
-
1
include routes.named_routes.module
-
-
# plus a singleton class method called _routes ...
-
1
included do
-
6
singleton_class.send(:redefine_method, :_routes) { routes }
-
end
-
-
# And an instance method _routes. Note that
-
# UrlFor (included in this module) add extra
-
# conveniences for working with @_routes.
-
17
define_method(:_routes) { @_routes || routes }
-
end
-
4
end
-
end
-
-
1
def empty?
-
routes.empty?
-
end
-
-
1
def add_route(app, conditions = {}, requirements = {}, defaults = {}, name = nil, anchor = true)
-
59
raise ArgumentError, "Invalid route name: '#{name}'" unless name.blank? || name.to_s.match(/^[_a-z]\w*$/i)
-
-
59
if name && named_routes[name]
-
raise ArgumentError, "Invalid route name, already in use: '#{name}' \n" \
-
"You may have defined two routes with the same name using the `:as` option, or " \
-
"you may be overriding a route already defined by a resource with the same naming. " \
-
"For the latter, you can restrict the routes created with `resources` as explained here: \n" \
-
"http://guides.rubyonrails.org/routing.html#restricting-the-routes-created"
-
end
-
-
59
path = build_path(conditions.delete(:path_info), requirements, SEPARATORS, anchor)
-
156
conditions = build_conditions(conditions, path.names.map { |x| x.to_sym })
-
-
59
route = @set.add_route(app, path, conditions, defaults, name)
-
59
named_routes[name] = route if name
-
59
route
-
end
-
-
1
def build_path(path, requirements, separators, anchor)
-
59
strexp = Journey::Router::Strexp.new(
-
path,
-
requirements,
-
SEPARATORS,
-
anchor)
-
-
59
pattern = Journey::Path::Pattern.new(strexp)
-
-
59
builder = Journey::GTG::Builder.new pattern.spec
-
-
# Get all the symbol nodes followed by literals that are not the
-
# dummy node.
-
59
symbols = pattern.spec.grep(Journey::Nodes::Symbol).find_all { |n|
-
97
builder.followpos(n).first.literal?
-
}
-
-
# Get all the symbol nodes preceded by literals.
-
59
symbols.concat pattern.spec.find_all(&:literal?).map { |n|
-
100
builder.followpos(n).first
-
}.find_all(&:symbol?)
-
-
59
symbols.each { |x|
-
x.regexp = /(?:#{Regexp.union(x.regexp, '-')})+/
-
}
-
-
59
pattern
-
end
-
1
private :build_path
-
-
1
def build_conditions(current_conditions, path_values)
-
59
conditions = current_conditions.dup
-
-
# Rack-Mount requires that :request_method be a regular expression.
-
# :request_method represents the HTTP verb that matches this route.
-
#
-
# Here we munge values before they get sent on to rack-mount.
-
59
verbs = conditions[:request_method] || []
-
59
unless verbs.empty?
-
58
conditions[:request_method] = %r[^#{verbs.join('|')}$]
-
end
-
-
59
conditions.keep_if do |k, _|
-
117
k == :action || k == :controller || k == :required_defaults ||
-
@request_class.public_method_defined?(k) || path_values.include?(k)
-
end
-
end
-
1
private :build_conditions
-
-
1
class Generator #:nodoc:
-
1
PARAMETERIZE = lambda do |name, value|
-
if name == :controller
-
value
-
elsif value.is_a?(Array)
-
value.map { |v| v.to_param }.join('/')
-
elsif param = value.to_param
-
param
-
end
-
end
-
-
1
attr_reader :options, :recall, :set, :named_route
-
-
1
def initialize(options, recall, set)
-
8
@named_route = options.delete(:use_route)
-
8
@options = options.dup
-
8
@recall = recall.dup
-
8
@set = set
-
-
8
normalize_recall!
-
8
normalize_options!
-
8
normalize_controller_action_id!
-
8
use_relative_controller!
-
8
normalize_controller!
-
8
normalize_action!
-
end
-
-
1
def controller
-
16
@options[:controller]
-
end
-
-
1
def current_controller
-
8
@recall[:controller]
-
end
-
-
1
def use_recall_for(key)
-
16
if @recall[key] && (!@options.key?(key) || @options[key] == @recall[key])
-
8
if !named_route_exists? || segment_keys.include?(key)
-
8
@options[key] = @recall.delete(key)
-
end
-
end
-
end
-
-
# Set 'index' as default action for recall
-
1
def normalize_recall!
-
8
@recall[:action] ||= 'index'
-
end
-
-
1
def normalize_options!
-
# If an explicit :controller was given, always make :action explicit
-
# too, so that action expiry works as expected for things like
-
#
-
# generate({controller: 'content'}, {controller: 'content', action: 'show'})
-
#
-
# (the above is from the unit tests). In the above case, because the
-
# controller was explicitly given, but no action, the action is implied to
-
# be "index", not the recalled action of "show".
-
-
8
if options[:controller]
-
4
options[:action] ||= 'index'
-
4
options[:controller] = options[:controller].to_s
-
end
-
-
8
if options.key?(:action)
-
8
options[:action] = (options[:action] || 'index').to_s
-
end
-
end
-
-
# This pulls :controller, :action, and :id out of the recall.
-
# The recall key is only used if there is no key in the options
-
# or if the key in the options is identical. If any of
-
# :controller, :action or :id is not found, don't pull any
-
# more keys from the recall.
-
1
def normalize_controller_action_id!
-
8
use_recall_for(:controller) or return
-
4
use_recall_for(:action) or return
-
4
use_recall_for(:id)
-
end
-
-
# if the current controller is "foo/bar/baz" and controller: "baz/bat"
-
# is specified, the controller becomes "foo/baz/bat"
-
1
def use_relative_controller!
-
8
if !named_route && different_controller? && !controller.start_with?("/")
-
old_parts = current_controller.split('/')
-
size = controller.count("/") + 1
-
parts = old_parts[0...-size] << controller
-
@options[:controller] = parts.join("/")
-
end
-
end
-
-
# Remove leading slashes from controllers
-
1
def normalize_controller!
-
8
@options[:controller] = controller.sub(%r{^/}, '') if controller
-
end
-
-
# Move 'index' action from options to recall
-
1
def normalize_action!
-
8
if @options[:action] == 'index'
-
@recall[:action] = @options.delete(:action)
-
end
-
end
-
-
# Generates a path from routes, returns [path, params].
-
# If no route is generated the formatter will raise ActionController::UrlGenerationError
-
1
def generate
-
8
@set.formatter.generate(:path_info, named_route, options, recall, PARAMETERIZE)
-
end
-
-
1
def different_controller?
-
8
return false unless current_controller
-
controller.to_param != current_controller.to_param
-
end
-
-
1
private
-
1
def named_route_exists?
-
8
named_route && set.named_routes[named_route]
-
end
-
-
1
def segment_keys
-
set.named_routes[named_route].segment_keys
-
end
-
end
-
-
# Generate the path indicated by the arguments, and return an array of
-
# the keys that were not used to generate it.
-
1
def extra_keys(options, recall={})
-
4
generate_extras(options, recall).last
-
end
-
-
1
def generate_extras(options, recall={})
-
4
path, params = generate(options, recall)
-
4
return path, params.keys
-
end
-
-
1
def generate(options, recall = {})
-
8
Generator.new(options, recall, self).generate
-
end
-
-
1
RESERVED_OPTIONS = [:host, :protocol, :port, :subdomain, :domain, :tld_length,
-
:trailing_slash, :anchor, :params, :only_path, :script_name,
-
:original_script_name]
-
-
1
def mounted?
-
2
false
-
end
-
-
1
def optimize_routes_generation?
-
2
!mounted? && default_url_options.empty?
-
end
-
-
1
def _generate_prefix(options = {})
-
nil
-
end
-
-
# The +options+ argument must be +nil+ or a hash whose keys are *symbols*.
-
1
def url_for(options)
-
4
options = default_url_options.merge(options || {})
-
-
4
user, password = extract_authentication(options)
-
4
recall = options.delete(:_recall)
-
-
4
original_script_name = options.delete(:original_script_name).presence
-
4
script_name = options.delete(:script_name).presence || _generate_prefix(options)
-
-
4
if script_name && original_script_name
-
script_name = original_script_name + script_name
-
end
-
-
4
path_options = options.except(*RESERVED_OPTIONS)
-
4
path_options = yield(path_options) if block_given?
-
-
4
path, params = generate(path_options, recall || {})
-
4
params.merge!(options[:params] || {})
-
-
4
ActionDispatch::Http::URL.url_for(options.merge!({
-
:path => path,
-
:script_name => script_name,
-
:params => params,
-
:user => user,
-
:password => password
-
}))
-
end
-
-
1
def call(env)
-
@router.call(env)
-
end
-
-
1
def recognize_path(path, environment = {})
-
method = (environment[:method] || "GET").to_s.upcase
-
path = Journey::Router::Utils.normalize_path(path) unless path =~ %r{://}
-
extras = environment[:extras] || {}
-
-
begin
-
env = Rack::MockRequest.env_for(path, {:method => method})
-
rescue URI::InvalidURIError => e
-
raise ActionController::RoutingError, e.message
-
end
-
-
req = @request_class.new(env)
-
@router.recognize(req) do |route, _matches, params|
-
params.merge!(extras)
-
params.each do |key, value|
-
if value.is_a?(String)
-
value = value.dup.force_encoding(Encoding::BINARY)
-
params[key] = URI.parser.unescape(value)
-
end
-
end
-
old_params = env[::ActionDispatch::Routing::RouteSet::PARAMETERS_KEY]
-
env[::ActionDispatch::Routing::RouteSet::PARAMETERS_KEY] = (old_params || {}).merge(params)
-
dispatcher = route.app
-
while dispatcher.is_a?(Mapper::Constraints) && dispatcher.matches?(env) do
-
dispatcher = dispatcher.app
-
end
-
-
if dispatcher.is_a?(Dispatcher)
-
if dispatcher.controller(params, false)
-
dispatcher.prepare_params!(params)
-
return params
-
else
-
raise ActionController::RoutingError, "A route matches #{path.inspect}, but references missing controller: #{params[:controller].camelize}Controller"
-
end
-
end
-
end
-
-
raise ActionController::RoutingError, "No route matches #{path.inspect}"
-
end
-
-
1
private
-
-
1
def extract_authentication(options)
-
4
if options[:user] && options[:password]
-
[options.delete(:user), options.delete(:password)]
-
else
-
nil
-
end
-
end
-
-
end
-
end
-
end
-
1
module ActionDispatch
-
1
module Routing
-
# In <tt>config/routes.rb</tt> you define URL-to-controller mappings, but the reverse
-
# is also possible: an URL can be generated from one of your routing definitions.
-
# URL generation functionality is centralized in this module.
-
#
-
# See ActionDispatch::Routing for general information about routing and routes.rb.
-
#
-
# <b>Tip:</b> If you need to generate URLs from your models or some other place,
-
# then ActionController::UrlFor is what you're looking for. Read on for
-
# an introduction. In general, this module should not be included on its own,
-
# as it is usually included by url_helpers (as in Rails.application.routes.url_helpers).
-
#
-
# == URL generation from parameters
-
#
-
# As you may know, some functions, such as ActionController::Base#url_for
-
# and ActionView::Helpers::UrlHelper#link_to, can generate URLs given a set
-
# of parameters. For example, you've probably had the chance to write code
-
# like this in one of your views:
-
#
-
# <%= link_to('Click here', controller: 'users',
-
# action: 'new', message: 'Welcome!') %>
-
# # => <a href="/users/new?message=Welcome%21">Click here</a>
-
#
-
# link_to, and all other functions that require URL generation functionality,
-
# actually use ActionController::UrlFor under the hood. And in particular,
-
# they use the ActionController::UrlFor#url_for method. One can generate
-
# the same path as the above example by using the following code:
-
#
-
# include UrlFor
-
# url_for(controller: 'users',
-
# action: 'new',
-
# message: 'Welcome!',
-
# only_path: true)
-
# # => "/users/new?message=Welcome%21"
-
#
-
# Notice the <tt>only_path: true</tt> part. This is because UrlFor has no
-
# information about the website hostname that your Rails app is serving. So if you
-
# want to include the hostname as well, then you must also pass the <tt>:host</tt>
-
# argument:
-
#
-
# include UrlFor
-
# url_for(controller: 'users',
-
# action: 'new',
-
# message: 'Welcome!',
-
# host: 'www.example.com')
-
# # => "http://www.example.com/users/new?message=Welcome%21"
-
#
-
# By default, all controllers and views have access to a special version of url_for,
-
# that already knows what the current hostname is. So if you use url_for in your
-
# controllers or your views, then you don't need to explicitly pass the <tt>:host</tt>
-
# argument.
-
#
-
# For convenience reasons, mailers provide a shortcut for ActionController::UrlFor#url_for.
-
# So within mailers, you only have to type 'url_for' instead of 'ActionController::UrlFor#url_for'
-
# in full. However, mailers don't have hostname information, and that's why you'll still
-
# have to specify the <tt>:host</tt> argument when generating URLs in mailers.
-
#
-
#
-
# == URL generation for named routes
-
#
-
# UrlFor also allows one to access methods that have been auto-generated from
-
# named routes. For example, suppose that you have a 'users' resource in your
-
# <tt>config/routes.rb</tt>:
-
#
-
# resources :users
-
#
-
# This generates, among other things, the method <tt>users_path</tt>. By default,
-
# this method is accessible from your controllers, views and mailers. If you need
-
# to access this auto-generated method from other places (such as a model), then
-
# you can do that by including Rails.application.routes.url_helpers in your class:
-
#
-
# class User < ActiveRecord::Base
-
# include Rails.application.routes.url_helpers
-
#
-
# def base_uri
-
# user_path(self)
-
# end
-
# end
-
#
-
# User.find(1).base_uri # => "/users/1"
-
#
-
1
module UrlFor
-
1
extend ActiveSupport::Concern
-
1
include PolymorphicRoutes
-
-
1
included do
-
6
unless method_defined?(:default_url_options)
-
# Including in a class uses an inheritable hash. Modules get a plain hash.
-
5
if respond_to?(:class_attribute)
-
4
class_attribute :default_url_options
-
else
-
1
mattr_writer :default_url_options
-
end
-
-
5
self.default_url_options = {}
-
end
-
-
6
include(*_url_for_modules) if respond_to?(:_url_for_modules)
-
end
-
-
1
def initialize(*)
-
6
@_routes = nil
-
6
super
-
end
-
-
# Hook overridden in controller to add request information
-
# with `default_url_options`. Application logic should not
-
# go into url_options.
-
1
def url_options
-
6
default_url_options
-
end
-
-
# Generate a url based on the options provided, default_url_options and the
-
# routes defined in routes.rb. The following options are supported:
-
#
-
# * <tt>:only_path</tt> - If true, the relative url is returned. Defaults to +false+.
-
# * <tt>:protocol</tt> - The protocol to connect to. Defaults to 'http'.
-
# * <tt>:host</tt> - Specifies the host the link should be targeted at.
-
# If <tt>:only_path</tt> is false, this option must be
-
# provided either explicitly, or via +default_url_options+.
-
# * <tt>:subdomain</tt> - Specifies the subdomain of the link, using the +tld_length+
-
# to split the subdomain from the host.
-
# If false, removes all subdomains from the host part of the link.
-
# * <tt>:domain</tt> - Specifies the domain of the link, using the +tld_length+
-
# to split the domain from the host.
-
# * <tt>:tld_length</tt> - Number of labels the TLD id composed of, only used if
-
# <tt>:subdomain</tt> or <tt>:domain</tt> are supplied. Defaults to
-
# <tt>ActionDispatch::Http::URL.tld_length</tt>, which in turn defaults to 1.
-
# * <tt>:port</tt> - Optionally specify the port to connect to.
-
# * <tt>:anchor</tt> - An anchor name to be appended to the path.
-
# * <tt>:trailing_slash</tt> - If true, adds a trailing slash, as in "/archive/2009/"
-
# * <tt>:script_name</tt> - Specifies application path relative to domain root. If provided, prepends application path.
-
#
-
# Any other key (<tt>:controller</tt>, <tt>:action</tt>, etc.) given to
-
# +url_for+ is forwarded to the Routes module.
-
#
-
# url_for controller: 'tasks', action: 'testing', host: 'somehost.org', port: '8080'
-
# # => 'http://somehost.org:8080/tasks/testing'
-
# url_for controller: 'tasks', action: 'testing', host: 'somehost.org', anchor: 'ok', only_path: true
-
# # => '/tasks/testing#ok'
-
# url_for controller: 'tasks', action: 'testing', trailing_slash: true
-
# # => 'http://somehost.org/tasks/testing/'
-
# url_for controller: 'tasks', action: 'testing', host: 'somehost.org', number: '33'
-
# # => 'http://somehost.org/tasks/testing?number=33'
-
# url_for controller: 'tasks', action: 'testing', host: 'somehost.org', script_name: "/myapp"
-
# # => 'http://somehost.org/myapp/tasks/testing'
-
# url_for controller: 'tasks', action: 'testing', host: 'somehost.org', script_name: "/myapp", only_path: true
-
# # => '/myapp/tasks/testing'
-
1
def url_for(options = nil)
-
case options
-
when nil
-
_routes.url_for(url_options.symbolize_keys)
-
when Hash
-
_routes.url_for(options.symbolize_keys.reverse_merge!(url_options))
-
when String
-
options
-
when Array
-
polymorphic_url(options, options.extract_options!)
-
else
-
polymorphic_url(options)
-
end
-
end
-
-
1
protected
-
-
1
def optimize_routes_generation?
-
3
return @_optimized_routes if defined?(@_optimized_routes)
-
2
@_optimized_routes = _routes.optimize_routes_generation? && default_url_options.empty?
-
end
-
-
1
def _with_routes(routes)
-
old_routes, @_routes = @_routes, routes
-
yield
-
ensure
-
@_routes = old_routes
-
end
-
-
1
def _routes_context
-
self
-
end
-
end
-
end
-
end
-
1
module ActionDispatch
-
1
module Assertions
-
1
autoload :DomAssertions, 'action_dispatch/testing/assertions/dom'
-
1
autoload :ResponseAssertions, 'action_dispatch/testing/assertions/response'
-
1
autoload :RoutingAssertions, 'action_dispatch/testing/assertions/routing'
-
1
autoload :SelectorAssertions, 'action_dispatch/testing/assertions/selector'
-
1
autoload :TagAssertions, 'action_dispatch/testing/assertions/tag'
-
-
1
extend ActiveSupport::Concern
-
-
1
include DomAssertions
-
1
include ResponseAssertions
-
1
include RoutingAssertions
-
1
include SelectorAssertions
-
1
include TagAssertions
-
end
-
end
-
-
1
require 'action_view/vendor/html-scanner'
-
-
1
module ActionDispatch
-
1
module Assertions
-
1
module DomAssertions
-
# \Test two HTML strings for equivalency (e.g., identical up to reordering of attributes)
-
#
-
# # assert that the referenced method generates the appropriate HTML string
-
# assert_dom_equal '<a href="http://www.example.com">Apples</a>', link_to("Apples", "http://www.example.com")
-
1
def assert_dom_equal(expected, actual, message = nil)
-
expected_dom = HTML::Document.new(expected).root
-
actual_dom = HTML::Document.new(actual).root
-
assert_equal expected_dom, actual_dom, message
-
end
-
-
# The negated form of +assert_dom_equivalent+.
-
#
-
# # assert that the referenced method does not generate the specified HTML string
-
# assert_dom_not_equal '<a href="http://www.example.com">Apples</a>', link_to("Oranges", "http://www.example.com")
-
1
def assert_dom_not_equal(expected, actual, message = nil)
-
expected_dom = HTML::Document.new(expected).root
-
actual_dom = HTML::Document.new(actual).root
-
assert_not_equal expected_dom, actual_dom, message
-
end
-
end
-
end
-
end
-
-
1
module ActionDispatch
-
1
module Assertions
-
# A small suite of assertions that test responses from \Rails applications.
-
1
module ResponseAssertions
-
# Asserts that the response is one of the following types:
-
#
-
# * <tt>:success</tt> - Status code was in the 200-299 range
-
# * <tt>:redirect</tt> - Status code was in the 300-399 range
-
# * <tt>:missing</tt> - Status code was 404
-
# * <tt>:error</tt> - Status code was in the 500-599 range
-
#
-
# You can also pass an explicit status number like <tt>assert_response(501)</tt>
-
# or its symbolic equivalent <tt>assert_response(:not_implemented)</tt>.
-
# See Rack::Utils::SYMBOL_TO_STATUS_CODE for a full list.
-
#
-
# # assert that the response was a redirection
-
# assert_response :redirect
-
#
-
# # assert that the response code was status code 401 (unauthorized)
-
# assert_response 401
-
1
def assert_response(type, message = nil)
-
1
message ||= "Expected response to be a <#{type}>, but was <#{@response.response_code}>"
-
-
1
if Symbol === type
-
1
if [:success, :missing, :redirect, :error].include?(type)
-
1
assert @response.send("#{type}?"), message
-
else
-
code = Rack::Utils::SYMBOL_TO_STATUS_CODE[type]
-
if code.nil?
-
raise ArgumentError, "Invalid response type :#{type}"
-
end
-
assert_equal code, @response.response_code, message
-
end
-
else
-
assert_equal type, @response.response_code, message
-
end
-
end
-
-
# Assert that the redirection options passed in match those of the redirect called in the latest action.
-
# This match can be partial, such that <tt>assert_redirected_to(controller: "weblog")</tt> will also
-
# match the redirection of <tt>redirect_to(controller: "weblog", action: "show")</tt> and so on.
-
#
-
# # assert that the redirection was to the "index" action on the WeblogController
-
# assert_redirected_to controller: "weblog", action: "index"
-
#
-
# # assert that the redirection was to the named route login_url
-
# assert_redirected_to login_url
-
#
-
# # assert that the redirection was to the url for @customer
-
# assert_redirected_to @customer
-
#
-
# # asserts that the redirection matches the regular expression
-
# assert_redirected_to %r(\Ahttp://example.org)
-
1
def assert_redirected_to(options = {}, message=nil)
-
1
assert_response(:redirect, message)
-
1
return true if options === @response.location
-
-
1
redirect_is = normalize_argument_to_redirection(@response.location)
-
1
redirect_expected = normalize_argument_to_redirection(options)
-
-
1
message ||= "Expected response to be a redirect to <#{redirect_expected}> but was a redirect to <#{redirect_is}>"
-
1
assert_operator redirect_expected, :===, redirect_is, message
-
end
-
-
1
private
-
# Proxy to to_param if the object will respond to it.
-
1
def parameterize(value)
-
value.respond_to?(:to_param) ? value.to_param : value
-
end
-
-
1
def normalize_argument_to_redirection(fragment)
-
2
if Regexp === fragment
-
fragment
-
else
-
2
handle = @controller || Class.new(ActionController::Metal) do
-
include ActionController::Redirecting
-
def initialize(request)
-
@_request = request
-
end
-
end.new(@request)
-
2
handle._compute_redirect_to_location(fragment)
-
end
-
end
-
end
-
end
-
end
-
1
require 'uri'
-
1
require 'active_support/core_ext/hash/indifferent_access'
-
1
require 'active_support/core_ext/string/access'
-
1
require 'action_controller/metal/exceptions'
-
-
1
module ActionDispatch
-
1
module Assertions
-
# Suite of assertions to test routes generated by \Rails and the handling of requests made to them.
-
1
module RoutingAssertions
-
# Asserts that the routing of the given +path+ was handled correctly and that the parsed options (given in the +expected_options+ hash)
-
# match +path+. Basically, it asserts that \Rails recognizes the route given by +expected_options+.
-
#
-
# Pass a hash in the second argument (+path+) to specify the request method. This is useful for routes
-
# requiring a specific HTTP method. The hash should contain a :path with the incoming request path
-
# and a :method containing the required HTTP verb.
-
#
-
# # assert that POSTing to /items will call the create action on ItemsController
-
# assert_recognizes({controller: 'items', action: 'create'}, {path: 'items', method: :post})
-
#
-
# You can also pass in +extras+ with a hash containing URL parameters that would normally be in the query string. This can be used
-
# to assert that values in the query string string will end up in the params hash correctly. To test query strings you must use the
-
# extras argument, appending the query string on the path directly will not work. For example:
-
#
-
# # assert that a path of '/items/list/1?view=print' returns the correct options
-
# assert_recognizes({controller: 'items', action: 'list', id: '1', view: 'print'}, 'items/list/1', { view: "print" })
-
#
-
# The +message+ parameter allows you to pass in an error message that is displayed upon failure.
-
#
-
# # Check the default route (i.e., the index action)
-
# assert_recognizes({controller: 'items', action: 'index'}, 'items')
-
#
-
# # Test a specific action
-
# assert_recognizes({controller: 'items', action: 'list'}, 'items/list')
-
#
-
# # Test an action with a parameter
-
# assert_recognizes({controller: 'items', action: 'destroy', id: '1'}, 'items/destroy/1')
-
#
-
# # Test a custom route
-
# assert_recognizes({controller: 'items', action: 'show', id: '1'}, 'view/item1')
-
1
def assert_recognizes(expected_options, path, extras={}, msg=nil)
-
request = recognized_request_for(path, extras)
-
-
expected_options = expected_options.clone
-
-
expected_options.stringify_keys!
-
-
msg = message(msg, "") {
-
sprintf("The recognized options <%s> did not match <%s>, difference:",
-
request.path_parameters, expected_options)
-
}
-
-
assert_equal(expected_options, request.path_parameters, msg)
-
end
-
-
# Asserts that the provided options can be used to generate the provided path. This is the inverse of +assert_recognizes+.
-
# The +extras+ parameter is used to tell the request the names and values of additional request parameters that would be in
-
# a query string. The +message+ parameter allows you to specify a custom error message for assertion failures.
-
#
-
# The +defaults+ parameter is unused.
-
#
-
# # Asserts that the default action is generated for a route with no action
-
# assert_generates "/items", controller: "items", action: "index"
-
#
-
# # Tests that the list action is properly routed
-
# assert_generates "/items/list", controller: "items", action: "list"
-
#
-
# # Tests the generation of a route with a parameter
-
# assert_generates "/items/list/1", { controller: "items", action: "list", id: "1" }
-
#
-
# # Asserts that the generated route gives us our custom route
-
# assert_generates "changesets/12", { controller: 'scm', action: 'show_diff', revision: "12" }
-
1
def assert_generates(expected_path, options, defaults={}, extras = {}, message=nil)
-
if expected_path =~ %r{://}
-
fail_on(URI::InvalidURIError) do
-
uri = URI.parse(expected_path)
-
expected_path = uri.path.to_s.empty? ? "/" : uri.path
-
end
-
else
-
expected_path = "/#{expected_path}" unless expected_path.first == '/'
-
end
-
# Load routes.rb if it hasn't been loaded.
-
-
generated_path, extra_keys = @routes.generate_extras(options, defaults)
-
found_extras = options.reject { |k, _| ! extra_keys.include? k }
-
-
msg = message || sprintf("found extras <%s>, not <%s>", found_extras, extras)
-
assert_equal(extras, found_extras, msg)
-
-
msg = message || sprintf("The generated path <%s> did not match <%s>", generated_path,
-
expected_path)
-
assert_equal(expected_path, generated_path, msg)
-
end
-
-
# Asserts that path and options match both ways; in other words, it verifies that <tt>path</tt> generates
-
# <tt>options</tt> and then that <tt>options</tt> generates <tt>path</tt>. This essentially combines +assert_recognizes+
-
# and +assert_generates+ into one step.
-
#
-
# The +extras+ hash allows you to specify options that would normally be provided as a query string to the action. The
-
# +message+ parameter allows you to specify a custom error message to display upon failure.
-
#
-
# # Assert a basic route: a controller with the default action (index)
-
# assert_routing '/home', controller: 'home', action: 'index'
-
#
-
# # Test a route generated with a specific controller, action, and parameter (id)
-
# assert_routing '/entries/show/23', controller: 'entries', action: 'show', id: 23
-
#
-
# # Assert a basic route (controller + default action), with an error message if it fails
-
# assert_routing '/store', { controller: 'store', action: 'index' }, {}, {}, 'Route for store index not generated properly'
-
#
-
# # Tests a route, providing a defaults hash
-
# assert_routing 'controller/action/9', {id: "9", item: "square"}, {controller: "controller", action: "action"}, {}, {item: "square"}
-
#
-
# # Tests a route with a HTTP method
-
# assert_routing({ method: 'put', path: '/product/321' }, { controller: "product", action: "update", id: "321" })
-
1
def assert_routing(path, options, defaults={}, extras={}, message=nil)
-
assert_recognizes(options, path, extras, message)
-
-
controller, default_controller = options[:controller], defaults[:controller]
-
if controller && controller.include?(?/) && default_controller && default_controller.include?(?/)
-
options[:controller] = "/#{controller}"
-
end
-
-
generate_options = options.dup.delete_if{ |k, _| defaults.key?(k) }
-
assert_generates(path.is_a?(Hash) ? path[:path] : path, generate_options, defaults, extras, message)
-
end
-
-
# A helper to make it easier to test different route configurations.
-
# This method temporarily replaces @routes
-
# with a new RouteSet instance.
-
#
-
# The new instance is yielded to the passed block. Typically the block
-
# will create some routes using <tt>set.draw { match ... }</tt>:
-
#
-
# with_routing do |set|
-
# set.draw do
-
# resources :users
-
# end
-
# assert_equal "/users", users_path
-
# end
-
#
-
1
def with_routing
-
old_routes, @routes = @routes, ActionDispatch::Routing::RouteSet.new
-
if defined?(@controller) && @controller
-
old_controller, @controller = @controller, @controller.clone
-
_routes = @routes
-
-
# Unfortunately, there is currently an abstraction leak between AC::Base
-
# and AV::Base which requires having the URL helpers in both AC and AV.
-
# To do this safely at runtime for tests, we need to bump up the helper serial
-
# to that the old AV subclass isn't cached.
-
#
-
# TODO: Make this unnecessary
-
@controller.singleton_class.send(:include, _routes.url_helpers)
-
@controller.view_context_class = Class.new(@controller.view_context_class) do
-
include _routes.url_helpers
-
end
-
end
-
yield @routes
-
ensure
-
@routes = old_routes
-
if defined?(@controller) && @controller
-
@controller = old_controller
-
end
-
end
-
-
# ROUTES TODO: These assertions should really work in an integration context
-
1
def method_missing(selector, *args, &block)
-
if defined?(@controller) && @controller && @routes && @routes.named_routes.helpers.include?(selector)
-
@controller.send(selector, *args, &block)
-
else
-
super
-
end
-
end
-
-
1
private
-
# Recognizes the route for a given path.
-
1
def recognized_request_for(path, extras = {})
-
if path.is_a?(Hash)
-
method = path[:method]
-
path = path[:path]
-
else
-
method = :get
-
end
-
-
# Assume given controller
-
request = ActionController::TestRequest.new
-
-
if path =~ %r{://}
-
fail_on(URI::InvalidURIError) do
-
uri = URI.parse(path)
-
request.env["rack.url_scheme"] = uri.scheme || "http"
-
request.host = uri.host if uri.host
-
request.port = uri.port if uri.port
-
request.path = uri.path.to_s.empty? ? "/" : uri.path
-
end
-
else
-
path = "/#{path}" unless path.first == "/"
-
request.path = path
-
end
-
-
request.request_method = method if method
-
-
params = fail_on(ActionController::RoutingError) do
-
@routes.recognize_path(path, { :method => method, :extras => extras })
-
end
-
request.path_parameters = params.with_indifferent_access
-
-
request
-
end
-
-
1
def fail_on(exception_class)
-
yield
-
rescue exception_class => e
-
raise Minitest::Assertion, e.message
-
end
-
end
-
end
-
end
-
1
require 'action_view/vendor/html-scanner'
-
1
require 'active_support/core_ext/object/inclusion'
-
-
#--
-
# Copyright (c) 2006 Assaf Arkin (http://labnotes.org)
-
# Under MIT and/or CC By license.
-
#++
-
-
1
module ActionDispatch
-
1
module Assertions
-
1
NO_STRIP = %w{pre script style textarea}
-
-
# Adds the +assert_select+ method for use in Rails functional
-
# test cases, which can be used to make assertions on the response HTML of a controller
-
# action. You can also call +assert_select+ within another +assert_select+ to
-
# make assertions on elements selected by the enclosing assertion.
-
#
-
# Use +css_select+ to select elements without making an assertions, either
-
# from the response HTML or elements selected by the enclosing assertion.
-
#
-
# In addition to HTML responses, you can make the following assertions:
-
#
-
# * +assert_select_encoded+ - Assertions on HTML encoded inside XML, for example for dealing with feed item descriptions.
-
# * +assert_select_email+ - Assertions on the HTML body of an e-mail.
-
#
-
# Also see HTML::Selector to learn how to use selectors.
-
1
module SelectorAssertions
-
# Select and return all matching elements.
-
#
-
# If called with a single argument, uses that argument as a selector
-
# to match all elements of the current page. Returns an empty array
-
# if no match is found.
-
#
-
# If called with two arguments, uses the first argument as the base
-
# element and the second argument as the selector. Attempts to match the
-
# base element and any of its children. Returns an empty array if no
-
# match is found.
-
#
-
# The selector may be a CSS selector expression (String), an expression
-
# with substitution values (Array) or an HTML::Selector object.
-
#
-
# # Selects all div tags
-
# divs = css_select("div")
-
#
-
# # Selects all paragraph tags and does something interesting
-
# pars = css_select("p")
-
# pars.each do |par|
-
# # Do something fun with paragraphs here...
-
# end
-
#
-
# # Selects all list items in unordered lists
-
# items = css_select("ul>li")
-
#
-
# # Selects all form tags and then all inputs inside the form
-
# forms = css_select("form")
-
# forms.each do |form|
-
# inputs = css_select(form, "input")
-
# ...
-
# end
-
1
def css_select(*args)
-
# See assert_select to understand what's going on here.
-
arg = args.shift
-
-
if arg.is_a?(HTML::Node)
-
root = arg
-
arg = args.shift
-
elsif arg == nil
-
raise ArgumentError, "First argument is either selector or element to select, but nil found. Perhaps you called assert_select with an element that does not exist?"
-
elsif defined?(@selected) && @selected
-
matches = []
-
-
@selected.each do |selected|
-
subset = css_select(selected, HTML::Selector.new(arg.dup, args.dup))
-
subset.each do |match|
-
matches << match unless matches.any? { |m| m.equal?(match) }
-
end
-
end
-
-
return matches
-
else
-
root = response_from_page
-
end
-
-
case arg
-
when String
-
selector = HTML::Selector.new(arg, args)
-
when Array
-
selector = HTML::Selector.new(*arg)
-
when HTML::Selector
-
selector = arg
-
else raise ArgumentError, "Expecting a selector as the first argument"
-
end
-
-
selector.select(root)
-
end
-
-
# An assertion that selects elements and makes one or more equality tests.
-
#
-
# If the first argument is an element, selects all matching elements
-
# starting from (and including) that element and all its children in
-
# depth-first order.
-
#
-
# If no element if specified, calling +assert_select+ selects from the
-
# response HTML unless +assert_select+ is called from within an +assert_select+ block.
-
#
-
# When called with a block +assert_select+ passes an array of selected elements
-
# to the block. Calling +assert_select+ from the block, with no element specified,
-
# runs the assertion on the complete set of elements selected by the enclosing assertion.
-
# Alternatively the array may be iterated through so that +assert_select+ can be called
-
# separately for each element.
-
#
-
#
-
# ==== Example
-
# If the response contains two ordered lists, each with four list elements then:
-
# assert_select "ol" do |elements|
-
# elements.each do |element|
-
# assert_select element, "li", 4
-
# end
-
# end
-
#
-
# will pass, as will:
-
# assert_select "ol" do
-
# assert_select "li", 8
-
# end
-
#
-
# The selector may be a CSS selector expression (String), an expression
-
# with substitution values, or an HTML::Selector object.
-
#
-
# === Equality Tests
-
#
-
# The equality test may be one of the following:
-
# * <tt>true</tt> - Assertion is true if at least one element selected.
-
# * <tt>false</tt> - Assertion is true if no element selected.
-
# * <tt>String/Regexp</tt> - Assertion is true if the text value of at least
-
# one element matches the string or regular expression.
-
# * <tt>Integer</tt> - Assertion is true if exactly that number of
-
# elements are selected.
-
# * <tt>Range</tt> - Assertion is true if the number of selected
-
# elements fit the range.
-
# If no equality test specified, the assertion is true if at least one
-
# element selected.
-
#
-
# To perform more than one equality tests, use a hash with the following keys:
-
# * <tt>:text</tt> - Narrow the selection to elements that have this text
-
# value (string or regexp).
-
# * <tt>:html</tt> - Narrow the selection to elements that have this HTML
-
# content (string or regexp).
-
# * <tt>:count</tt> - Assertion is true if the number of selected elements
-
# is equal to this value.
-
# * <tt>:minimum</tt> - Assertion is true if the number of selected
-
# elements is at least this value.
-
# * <tt>:maximum</tt> - Assertion is true if the number of selected
-
# elements is at most this value.
-
#
-
# If the method is called with a block, once all equality tests are
-
# evaluated the block is called with an array of all matched elements.
-
#
-
# # At least one form element
-
# assert_select "form"
-
#
-
# # Form element includes four input fields
-
# assert_select "form input", 4
-
#
-
# # Page title is "Welcome"
-
# assert_select "title", "Welcome"
-
#
-
# # Page title is "Welcome" and there is only one title element
-
# assert_select "title", {count: 1, text: "Welcome"},
-
# "Wrong title or more than one title element"
-
#
-
# # Page contains no forms
-
# assert_select "form", false, "This page must contain no forms"
-
#
-
# # Test the content and style
-
# assert_select "body div.header ul.menu"
-
#
-
# # Use substitution values
-
# assert_select "ol>li#?", /item-\d+/
-
#
-
# # All input fields in the form have a name
-
# assert_select "form input" do
-
# assert_select "[name=?]", /.+/ # Not empty
-
# end
-
1
def assert_select(*args, &block)
-
# Start with optional element followed by mandatory selector.
-
arg = args.shift
-
@selected ||= nil
-
-
if arg.is_a?(HTML::Node)
-
# First argument is a node (tag or text, but also HTML root),
-
# so we know what we're selecting from.
-
root = arg
-
arg = args.shift
-
elsif arg == nil
-
# This usually happens when passing a node/element that
-
# happens to be nil.
-
raise ArgumentError, "First argument is either selector or element to select, but nil found. Perhaps you called assert_select with an element that does not exist?"
-
elsif @selected
-
root = HTML::Node.new(nil)
-
root.children.concat @selected
-
else
-
# Otherwise just operate on the response document.
-
root = response_from_page
-
end
-
-
# First or second argument is the selector: string and we pass
-
# all remaining arguments. Array and we pass the argument. Also
-
# accepts selector itself.
-
case arg
-
when String
-
selector = HTML::Selector.new(arg, args)
-
when Array
-
selector = HTML::Selector.new(*arg)
-
when HTML::Selector
-
selector = arg
-
else raise ArgumentError, "Expecting a selector as the first argument"
-
end
-
-
# Next argument is used for equality tests.
-
equals = {}
-
case arg = args.shift
-
when Hash
-
equals = arg
-
when String, Regexp
-
equals[:text] = arg
-
when Integer
-
equals[:count] = arg
-
when Range
-
equals[:minimum] = arg.begin
-
equals[:maximum] = arg.end
-
when FalseClass
-
equals[:count] = 0
-
when NilClass, TrueClass
-
equals[:minimum] = 1
-
else raise ArgumentError, "I don't understand what you're trying to match"
-
end
-
-
# By default we're looking for at least one match.
-
if equals[:count]
-
equals[:minimum] = equals[:maximum] = equals[:count]
-
else
-
equals[:minimum] = 1 unless equals[:minimum]
-
end
-
-
# Last argument is the message we use if the assertion fails.
-
message = args.shift
-
#- message = "No match made with selector #{selector.inspect}" unless message
-
if args.shift
-
raise ArgumentError, "Not expecting that last argument, you either have too many arguments, or they're the wrong type"
-
end
-
-
matches = selector.select(root)
-
# If text/html, narrow down to those elements that match it.
-
content_mismatch = nil
-
if match_with = equals[:text]
-
matches.delete_if do |match|
-
text = ""
-
stack = match.children.reverse
-
while node = stack.pop
-
if node.tag?
-
stack.concat node.children.reverse
-
else
-
content = node.content
-
text << content
-
end
-
end
-
text.strip! unless NO_STRIP.include?(match.name)
-
text.sub!(/\A\n/, '') if match.name == "textarea"
-
unless match_with.is_a?(Regexp) ? (text =~ match_with) : (text == match_with.to_s)
-
content_mismatch ||= sprintf("<%s> expected but was\n<%s>", match_with, text)
-
true
-
end
-
end
-
elsif match_with = equals[:html]
-
matches.delete_if do |match|
-
html = match.children.map(&:to_s).join
-
html.strip! unless NO_STRIP.include?(match.name)
-
unless match_with.is_a?(Regexp) ? (html =~ match_with) : (html == match_with.to_s)
-
content_mismatch ||= sprintf("<%s> expected but was\n<%s>", match_with, html)
-
true
-
end
-
end
-
end
-
# Expecting foo found bar element only if found zero, not if
-
# found one but expecting two.
-
message ||= content_mismatch if matches.empty?
-
# Test minimum/maximum occurrence.
-
min, max, count = equals[:minimum], equals[:maximum], equals[:count]
-
-
# FIXME: minitest provides messaging when we use assert_operator,
-
# so is this custom message really needed?
-
message = message || %(Expected #{count_description(min, max, count)} matching "#{selector.to_s}", found #{matches.size})
-
if count
-
assert_equal count, matches.size, message
-
else
-
assert_operator matches.size, :>=, min, message if min
-
assert_operator matches.size, :<=, max, message if max
-
end
-
-
# If a block is given call that block. Set @selected to allow
-
# nested assert_select, which can be nested several levels deep.
-
if block_given? && !matches.empty?
-
begin
-
in_scope, @selected = @selected, matches
-
yield matches
-
ensure
-
@selected = in_scope
-
end
-
end
-
-
# Returns all matches elements.
-
matches
-
end
-
-
1
def count_description(min, max, count) #:nodoc:
-
pluralize = lambda {|word, quantity| word << (quantity == 1 ? '' : 's')}
-
-
if min && max && (max != min)
-
"between #{min} and #{max} elements"
-
elsif min && max && max == min && count
-
"exactly #{count} #{pluralize['element', min]}"
-
elsif min && !(min == 1 && max == 1)
-
"at least #{min} #{pluralize['element', min]}"
-
elsif max
-
"at most #{max} #{pluralize['element', max]}"
-
end
-
end
-
-
# Extracts the content of an element, treats it as encoded HTML and runs
-
# nested assertion on it.
-
#
-
# You typically call this method within another assertion to operate on
-
# all currently selected elements. You can also pass an element or array
-
# of elements.
-
#
-
# The content of each element is un-encoded, and wrapped in the root
-
# element +encoded+. It then calls the block with all un-encoded elements.
-
#
-
# # Selects all bold tags from within the title of an Atom feed's entries (perhaps to nab a section name prefix)
-
# assert_select "feed[xmlns='http://www.w3.org/2005/Atom']" do
-
# # Select each entry item and then the title item
-
# assert_select "entry>title" do
-
# # Run assertions on the encoded title elements
-
# assert_select_encoded do
-
# assert_select "b"
-
# end
-
# end
-
# end
-
#
-
#
-
# # Selects all paragraph tags from within the description of an RSS feed
-
# assert_select "rss[version=2.0]" do
-
# # Select description element of each feed item.
-
# assert_select "channel>item>description" do
-
# # Run assertions on the encoded elements.
-
# assert_select_encoded do
-
# assert_select "p"
-
# end
-
# end
-
# end
-
1
def assert_select_encoded(element = nil, &block)
-
case element
-
when Array
-
elements = element
-
when HTML::Node
-
elements = [element]
-
when nil
-
unless elements = @selected
-
raise ArgumentError, "First argument is optional, but must be called from a nested assert_select"
-
end
-
else
-
raise ArgumentError, "Argument is optional, and may be node or array of nodes"
-
end
-
-
fix_content = lambda do |node|
-
# Gets around a bug in the Rails 1.1 HTML parser.
-
node.content.gsub(/<!\[CDATA\[(.*)(\]\]>)?/m) { Rack::Utils.escapeHTML($1) }
-
end
-
-
selected = elements.map do |elem|
-
text = elem.children.select{ |c| not c.tag? }.map{ |c| fix_content[c] }.join
-
root = HTML::Document.new(CGI.unescapeHTML("<encoded>#{text}</encoded>")).root
-
css_select(root, "encoded:root", &block)[0]
-
end
-
-
begin
-
old_selected, @selected = @selected, selected
-
assert_select ":root", &block
-
ensure
-
@selected = old_selected
-
end
-
end
-
-
# Extracts the body of an email and runs nested assertions on it.
-
#
-
# You must enable deliveries for this assertion to work, use:
-
# ActionMailer::Base.perform_deliveries = true
-
#
-
# assert_select_email do
-
# assert_select "h1", "Email alert"
-
# end
-
#
-
# assert_select_email do
-
# items = assert_select "ol>li"
-
# items.each do
-
# # Work with items here...
-
# end
-
# end
-
1
def assert_select_email(&block)
-
deliveries = ActionMailer::Base.deliveries
-
assert !deliveries.empty?, "No e-mail in delivery list"
-
-
deliveries.each do |delivery|
-
(delivery.parts.empty? ? [delivery] : delivery.parts).each do |part|
-
if part["Content-Type"].to_s =~ /^text\/html\W/
-
root = HTML::Document.new(part.body.to_s).root
-
assert_select root, ":root", &block
-
end
-
end
-
end
-
end
-
-
1
protected
-
# +assert_select+ and +css_select+ call this to obtain the content in the HTML page.
-
1
def response_from_page
-
html_document.root
-
end
-
end
-
end
-
end
-
1
require 'action_view/vendor/html-scanner'
-
-
1
module ActionDispatch
-
1
module Assertions
-
# Pair of assertions to testing elements in the HTML output of the response.
-
1
module TagAssertions
-
# Asserts that there is a tag/node/element in the body of the response
-
# that meets all of the given conditions. The +conditions+ parameter must
-
# be a hash of any of the following keys (all are optional):
-
#
-
# * <tt>:tag</tt>: the node type must match the corresponding value
-
# * <tt>:attributes</tt>: a hash. The node's attributes must match the
-
# corresponding values in the hash.
-
# * <tt>:parent</tt>: a hash. The node's parent must match the
-
# corresponding hash.
-
# * <tt>:child</tt>: a hash. At least one of the node's immediate children
-
# must meet the criteria described by the hash.
-
# * <tt>:ancestor</tt>: a hash. At least one of the node's ancestors must
-
# meet the criteria described by the hash.
-
# * <tt>:descendant</tt>: a hash. At least one of the node's descendants
-
# must meet the criteria described by the hash.
-
# * <tt>:sibling</tt>: a hash. At least one of the node's siblings must
-
# meet the criteria described by the hash.
-
# * <tt>:after</tt>: a hash. The node must be after any sibling meeting
-
# the criteria described by the hash, and at least one sibling must match.
-
# * <tt>:before</tt>: a hash. The node must be before any sibling meeting
-
# the criteria described by the hash, and at least one sibling must match.
-
# * <tt>:children</tt>: a hash, for counting children of a node. Accepts
-
# the keys:
-
# * <tt>:count</tt>: either a number or a range which must equal (or
-
# include) the number of children that match.
-
# * <tt>:less_than</tt>: the number of matching children must be less
-
# than this number.
-
# * <tt>:greater_than</tt>: the number of matching children must be
-
# greater than this number.
-
# * <tt>:only</tt>: another hash consisting of the keys to use
-
# to match on the children, and only matching children will be
-
# counted.
-
# * <tt>:content</tt>: the textual content of the node must match the
-
# given value. This will not match HTML tags in the body of a
-
# tag--only text.
-
#
-
# Conditions are matched using the following algorithm:
-
#
-
# * if the condition is a string, it must be a substring of the value.
-
# * if the condition is a regexp, it must match the value.
-
# * if the condition is a number, the value must match number.to_s.
-
# * if the condition is +true+, the value must not be +nil+.
-
# * if the condition is +false+ or +nil+, the value must be +nil+.
-
#
-
# # Assert that there is a "span" tag
-
# assert_tag tag: "span"
-
#
-
# # Assert that there is a "span" tag with id="x"
-
# assert_tag tag: "span", attributes: { id: "x" }
-
#
-
# # Assert that there is a "span" tag using the short-hand
-
# assert_tag :span
-
#
-
# # Assert that there is a "span" tag with id="x" using the short-hand
-
# assert_tag :span, attributes: { id: "x" }
-
#
-
# # Assert that there is a "span" inside of a "div"
-
# assert_tag tag: "span", parent: { tag: "div" }
-
#
-
# # Assert that there is a "span" somewhere inside a table
-
# assert_tag tag: "span", ancestor: { tag: "table" }
-
#
-
# # Assert that there is a "span" with at least one "em" child
-
# assert_tag tag: "span", child: { tag: "em" }
-
#
-
# # Assert that there is a "span" containing a (possibly nested)
-
# # "strong" tag.
-
# assert_tag tag: "span", descendant: { tag: "strong" }
-
#
-
# # Assert that there is a "span" containing between 2 and 4 "em" tags
-
# # as immediate children
-
# assert_tag tag: "span",
-
# children: { count: 2..4, only: { tag: "em" } }
-
#
-
# # Get funky: assert that there is a "div", with an "ul" ancestor
-
# # and an "li" parent (with "class" = "enum"), and containing a
-
# # "span" descendant that contains text matching /hello world/
-
# assert_tag tag: "div",
-
# ancestor: { tag: "ul" },
-
# parent: { tag: "li",
-
# attributes: { class: "enum" } },
-
# descendant: { tag: "span",
-
# child: /hello world/ }
-
#
-
# <b>Please note</b>: +assert_tag+ and +assert_no_tag+ only work
-
# with well-formed XHTML. They recognize a few tags as implicitly self-closing
-
# (like br and hr and such) but will not work correctly with tags
-
# that allow optional closing tags (p, li, td). <em>You must explicitly
-
# close all of your tags to use these assertions.</em>
-
1
def assert_tag(*opts)
-
opts = opts.size > 1 ? opts.last.merge({ :tag => opts.first.to_s }) : opts.first
-
tag = find_tag(opts)
-
assert tag, "expected tag, but no tag found matching #{opts.inspect} in:\n#{@response.body.inspect}"
-
end
-
-
# Identical to +assert_tag+, but asserts that a matching tag does _not_
-
# exist. (See +assert_tag+ for a full discussion of the syntax.)
-
#
-
# # Assert that there is not a "div" containing a "p"
-
# assert_no_tag tag: "div", descendant: { tag: "p" }
-
#
-
# # Assert that an unordered list is empty
-
# assert_no_tag tag: "ul", descendant: { tag: "li" }
-
#
-
# # Assert that there is not a "p" tag with between 1 to 3 "img" tags
-
# # as immediate children
-
# assert_no_tag tag: "p",
-
# children: { count: 1..3, only: { tag: "img" } }
-
1
def assert_no_tag(*opts)
-
opts = opts.size > 1 ? opts.last.merge({ :tag => opts.first.to_s }) : opts.first
-
tag = find_tag(opts)
-
assert !tag, "expected no tag, but found tag matching #{opts.inspect} in:\n#{@response.body.inspect}"
-
end
-
-
1
def find_tag(conditions)
-
html_document.find(conditions)
-
end
-
-
1
def find_all_tag(conditions)
-
html_document.find_all(conditions)
-
end
-
-
1
def html_document
-
xml = @response.content_type =~ /xml$/
-
@html_document ||= HTML::Document.new(@response.body, false, xml)
-
end
-
end
-
end
-
end
-
1
require 'stringio'
-
1
require 'uri'
-
1
require 'active_support/core_ext/kernel/singleton_class'
-
1
require 'active_support/core_ext/object/try'
-
1
require 'rack/test'
-
1
require 'minitest'
-
-
1
module ActionDispatch
-
1
module Integration #:nodoc:
-
1
module RequestHelpers
-
# Performs a GET request with the given parameters.
-
#
-
# - +path+: The URI (as a String) on which you want to perform a GET
-
# request.
-
# - +parameters+: The HTTP parameters that you want to pass. This may
-
# be +nil+,
-
# a Hash, or a String that is appropriately encoded
-
# (<tt>application/x-www-form-urlencoded</tt> or
-
# <tt>multipart/form-data</tt>).
-
# - +headers_or_env+: Additional headers to pass, as a Hash. The headers will be
-
# merged into the Rack env hash.
-
#
-
# This method returns a Response object, which one can use to
-
# inspect the details of the response. Furthermore, if this method was
-
# called from an ActionDispatch::IntegrationTest object, then that
-
# object's <tt>@response</tt> instance variable will point to the same
-
# response object.
-
#
-
# You can also perform POST, PATCH, PUT, DELETE, and HEAD requests with
-
# +#post+, +#patch+, +#put+, +#delete+, and +#head+.
-
1
def get(path, parameters = nil, headers_or_env = nil)
-
process :get, path, parameters, headers_or_env
-
end
-
-
# Performs a POST request with the given parameters. See +#get+ for more
-
# details.
-
1
def post(path, parameters = nil, headers_or_env = nil)
-
process :post, path, parameters, headers_or_env
-
end
-
-
# Performs a PATCH request with the given parameters. See +#get+ for more
-
# details.
-
1
def patch(path, parameters = nil, headers_or_env = nil)
-
process :patch, path, parameters, headers_or_env
-
end
-
-
# Performs a PUT request with the given parameters. See +#get+ for more
-
# details.
-
1
def put(path, parameters = nil, headers_or_env = nil)
-
process :put, path, parameters, headers_or_env
-
end
-
-
# Performs a DELETE request with the given parameters. See +#get+ for
-
# more details.
-
1
def delete(path, parameters = nil, headers_or_env = nil)
-
process :delete, path, parameters, headers_or_env
-
end
-
-
# Performs a HEAD request with the given parameters. See +#get+ for more
-
# details.
-
1
def head(path, parameters = nil, headers_or_env = nil)
-
process :head, path, parameters, headers_or_env
-
end
-
-
# Performs an XMLHttpRequest request with the given parameters, mirroring
-
# a request from the Prototype library.
-
#
-
# The request_method is +:get+, +:post+, +:patch+, +:put+, +:delete+ or
-
# +:head+; the parameters are +nil+, a hash, or a url-encoded or multipart
-
# string; the headers are a hash.
-
1
def xml_http_request(request_method, path, parameters = nil, headers_or_env = nil)
-
headers_or_env ||= {}
-
headers_or_env['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
-
headers_or_env['HTTP_ACCEPT'] ||= [Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ')
-
process(request_method, path, parameters, headers_or_env)
-
end
-
1
alias xhr :xml_http_request
-
-
# Follow a single redirect response. If the last response was not a
-
# redirect, an exception will be raised. Otherwise, the redirect is
-
# performed on the location header.
-
1
def follow_redirect!
-
raise "not a redirect! #{status} #{status_message}" unless redirect?
-
get(response.location)
-
status
-
end
-
-
# Performs a request using the specified method, following any subsequent
-
# redirect. Note that the redirects are followed until the response is
-
# not a redirect--this means you may run into an infinite loop if your
-
# redirect loops back to itself.
-
1
def request_via_redirect(http_method, path, parameters = nil, headers_or_env = nil)
-
process(http_method, path, parameters, headers_or_env)
-
follow_redirect! while redirect?
-
status
-
end
-
-
# Performs a GET request, following any subsequent redirect.
-
# See +request_via_redirect+ for more information.
-
1
def get_via_redirect(path, parameters = nil, headers_or_env = nil)
-
request_via_redirect(:get, path, parameters, headers_or_env)
-
end
-
-
# Performs a POST request, following any subsequent redirect.
-
# See +request_via_redirect+ for more information.
-
1
def post_via_redirect(path, parameters = nil, headers_or_env = nil)
-
request_via_redirect(:post, path, parameters, headers_or_env)
-
end
-
-
# Performs a PATCH request, following any subsequent redirect.
-
# See +request_via_redirect+ for more information.
-
1
def patch_via_redirect(path, parameters = nil, headers_or_env = nil)
-
request_via_redirect(:patch, path, parameters, headers_or_env)
-
end
-
-
# Performs a PUT request, following any subsequent redirect.
-
# See +request_via_redirect+ for more information.
-
1
def put_via_redirect(path, parameters = nil, headers_or_env = nil)
-
request_via_redirect(:put, path, parameters, headers_or_env)
-
end
-
-
# Performs a DELETE request, following any subsequent redirect.
-
# See +request_via_redirect+ for more information.
-
1
def delete_via_redirect(path, parameters = nil, headers_or_env = nil)
-
request_via_redirect(:delete, path, parameters, headers_or_env)
-
end
-
end
-
-
# An instance of this class represents a set of requests and responses
-
# performed sequentially by a test process. Because you can instantiate
-
# multiple sessions and run them side-by-side, you can also mimic (to some
-
# limited extent) multiple simultaneous users interacting with your system.
-
#
-
# Typically, you will instantiate a new session using
-
# IntegrationTest#open_session, rather than instantiating
-
# Integration::Session directly.
-
1
class Session
-
1
DEFAULT_HOST = "www.example.com"
-
-
1
include Minitest::Assertions
-
1
include TestProcess, RequestHelpers, Assertions
-
-
1
%w( status status_message headers body redirect? ).each do |method|
-
5
delegate method, :to => :response, :allow_nil => true
-
end
-
-
1
%w( path ).each do |method|
-
1
delegate method, :to => :request, :allow_nil => true
-
end
-
-
# The hostname used in the last request.
-
1
def host
-
@host || DEFAULT_HOST
-
end
-
1
attr_writer :host
-
-
# The remote_addr used in the last request.
-
1
attr_accessor :remote_addr
-
-
# The Accept header to send.
-
1
attr_accessor :accept
-
-
# A map of the cookies returned by the last response, and which will be
-
# sent with the next request.
-
1
def cookies
-
_mock_session.cookie_jar
-
end
-
-
# A reference to the controller instance used by the last request.
-
1
attr_reader :controller
-
-
# A reference to the request instance used by the last request.
-
1
attr_reader :request
-
-
# A reference to the response instance used by the last request.
-
1
attr_reader :response
-
-
# A running counter of the number of requests processed.
-
1
attr_accessor :request_count
-
-
1
include ActionDispatch::Routing::UrlFor
-
-
# Create and initialize a new Session instance.
-
1
def initialize(app)
-
super()
-
@app = app
-
-
# If the app is a Rails app, make url_helpers available on the session
-
# This makes app.url_for and app.foo_path available in the console
-
if app.respond_to?(:routes)
-
singleton_class.class_eval do
-
include app.routes.url_helpers if app.routes.respond_to?(:url_helpers)
-
include app.routes.mounted_helpers if app.routes.respond_to?(:mounted_helpers)
-
end
-
end
-
-
reset!
-
end
-
-
1
def url_options
-
@url_options ||= default_url_options.dup.tap do |url_options|
-
url_options.reverse_merge!(controller.url_options) if controller
-
-
if @app.respond_to?(:routes) && @app.routes.respond_to?(:default_url_options)
-
url_options.reverse_merge!(@app.routes.default_url_options)
-
end
-
-
url_options.reverse_merge!(:host => host, :protocol => https? ? "https" : "http")
-
end
-
end
-
-
# Resets the instance. This can be used to reset the state information
-
# in an existing session instance, so it can be used from a clean-slate
-
# condition.
-
#
-
# session.reset!
-
1
def reset!
-
@https = false
-
@controller = @request = @response = nil
-
@_mock_session = nil
-
@request_count = 0
-
@url_options = nil
-
-
self.host = DEFAULT_HOST
-
self.remote_addr = "127.0.0.1"
-
self.accept = "text/xml,application/xml,application/xhtml+xml," +
-
"text/html;q=0.9,text/plain;q=0.8,image/png," +
-
"*/*;q=0.5"
-
-
unless defined? @named_routes_configured
-
# the helpers are made protected by default--we make them public for
-
# easier access during testing and troubleshooting.
-
@named_routes_configured = true
-
end
-
end
-
-
# Specify whether or not the session should mimic a secure HTTPS request.
-
#
-
# session.https!
-
# session.https!(false)
-
1
def https!(flag = true)
-
@https = flag
-
end
-
-
# Returns +true+ if the session is mimicking a secure HTTPS request.
-
#
-
# if session.https?
-
# ...
-
# end
-
1
def https?
-
@https
-
end
-
-
# Set the host name to use in the next request.
-
#
-
# session.host! "www.example.com"
-
1
alias :host! :host=
-
-
1
private
-
1
def _mock_session
-
@_mock_session ||= Rack::MockSession.new(@app, host)
-
end
-
-
# Performs the actual request.
-
1
def process(method, path, parameters = nil, headers_or_env = nil)
-
if path =~ %r{://}
-
location = URI.parse(path)
-
https! URI::HTTPS === location if location.scheme
-
host! "#{location.host}:#{location.port}" if location.host
-
path = location.query ? "#{location.path}?#{location.query}" : location.path
-
end
-
-
unless ActionController::Base < ActionController::Testing
-
ActionController::Base.class_eval do
-
include ActionController::Testing
-
end
-
end
-
-
hostname, port = host.split(':')
-
-
env = {
-
:method => method,
-
:params => parameters,
-
-
"SERVER_NAME" => hostname,
-
"SERVER_PORT" => port || (https? ? "443" : "80"),
-
"HTTPS" => https? ? "on" : "off",
-
"rack.url_scheme" => https? ? "https" : "http",
-
-
"REQUEST_URI" => path,
-
"HTTP_HOST" => host,
-
"REMOTE_ADDR" => remote_addr,
-
"CONTENT_TYPE" => "application/x-www-form-urlencoded",
-
"HTTP_ACCEPT" => accept
-
}
-
# this modifies the passed env directly
-
Http::Headers.new(env).merge!(headers_or_env || {})
-
-
session = Rack::Test::Session.new(_mock_session)
-
-
# NOTE: rack-test v0.5 doesn't build a default uri correctly
-
# Make sure requested path is always a full uri
-
uri = URI.parse('/')
-
uri.scheme ||= env['rack.url_scheme']
-
uri.host ||= env['SERVER_NAME']
-
uri.port ||= env['SERVER_PORT'].try(:to_i)
-
uri += path
-
-
session.request(uri.to_s, env)
-
-
@request_count += 1
-
@request = ActionDispatch::Request.new(session.last_request.env)
-
response = _mock_session.last_response
-
@response = ActionDispatch::TestResponse.new(response.status, response.headers, response.body)
-
@html_document = nil
-
@url_options = nil
-
-
@controller = session.last_request.env['action_controller.instance']
-
-
return response.status
-
end
-
end
-
-
1
module Runner
-
1
include ActionDispatch::Assertions
-
-
1
def app
-
@app ||= nil
-
end
-
-
# Reset the current session. This is useful for testing multiple sessions
-
# in a single test case.
-
1
def reset!
-
@integration_session = Integration::Session.new(app)
-
end
-
-
%w(get post patch put head delete cookies assigns
-
1
xml_http_request xhr get_via_redirect post_via_redirect).each do |method|
-
12
define_method(method) do |*args|
-
reset! unless integration_session
-
# reset the html_document variable, but only for new get/post calls
-
@html_document = nil unless method == 'cookies' || method == 'assigns'
-
integration_session.__send__(method, *args).tap do
-
copy_session_variables!
-
end
-
end
-
end
-
-
# Open a new session instance. If a block is given, the new session is
-
# yielded to the block before being returned.
-
#
-
# session = open_session do |sess|
-
# sess.extend(CustomAssertions)
-
# end
-
#
-
# By default, a single session is automatically created for you, but you
-
# can use this method to open multiple sessions that ought to be tested
-
# simultaneously.
-
1
def open_session(app = nil)
-
dup.tap do |session|
-
yield session if block_given?
-
end
-
end
-
-
# Copy the instance variables from the current session instance into the
-
# test instance.
-
1
def copy_session_variables! #:nodoc:
-
return unless integration_session
-
%w(controller response request).each do |var|
-
instance_variable_set("@#{var}", @integration_session.__send__(var))
-
end
-
end
-
-
1
def default_url_options
-
reset! unless integration_session
-
integration_session.default_url_options
-
end
-
-
1
def default_url_options=(options)
-
reset! unless integration_session
-
integration_session.default_url_options = options
-
end
-
-
1
def respond_to?(method, include_private = false)
-
integration_session.respond_to?(method, include_private) || super
-
end
-
-
# Delegate unhandled messages to the current session instance.
-
1
def method_missing(sym, *args, &block)
-
reset! unless integration_session
-
if integration_session.respond_to?(sym)
-
integration_session.__send__(sym, *args, &block).tap do
-
copy_session_variables!
-
end
-
else
-
super
-
end
-
end
-
-
1
private
-
1
def integration_session
-
@integration_session ||= nil
-
end
-
end
-
end
-
-
# An integration test spans multiple controllers and actions,
-
# tying them all together to ensure they work together as expected. It tests
-
# more completely than either unit or functional tests do, exercising the
-
# entire stack, from the dispatcher to the database.
-
#
-
# At its simplest, you simply extend <tt>IntegrationTest</tt> and write your tests
-
# using the get/post methods:
-
#
-
# require "test_helper"
-
#
-
# class ExampleTest < ActionDispatch::IntegrationTest
-
# fixtures :people
-
#
-
# def test_login
-
# # get the login page
-
# get "/login"
-
# assert_equal 200, status
-
#
-
# # post the login and follow through to the home page
-
# post "/login", username: people(:jamis).username,
-
# password: people(:jamis).password
-
# follow_redirect!
-
# assert_equal 200, status
-
# assert_equal "/home", path
-
# end
-
# end
-
#
-
# However, you can also have multiple session instances open per test, and
-
# even extend those instances with assertions and methods to create a very
-
# powerful testing DSL that is specific for your application. You can even
-
# reference any named routes you happen to have defined.
-
#
-
# require "test_helper"
-
#
-
# class AdvancedTest < ActionDispatch::IntegrationTest
-
# fixtures :people, :rooms
-
#
-
# def test_login_and_speak
-
# jamis, david = login(:jamis), login(:david)
-
# room = rooms(:office)
-
#
-
# jamis.enter(room)
-
# jamis.speak(room, "anybody home?")
-
#
-
# david.enter(room)
-
# david.speak(room, "hello!")
-
# end
-
#
-
# private
-
#
-
# module CustomAssertions
-
# def enter(room)
-
# # reference a named route, for maximum internal consistency!
-
# get(room_url(id: room.id))
-
# assert(...)
-
# ...
-
# end
-
#
-
# def speak(room, message)
-
# xml_http_request "/say/#{room.id}", message: message
-
# assert(...)
-
# ...
-
# end
-
# end
-
#
-
# def login(who)
-
# open_session do |sess|
-
# sess.extend(CustomAssertions)
-
# who = people(who)
-
# sess.post "/login", username: who.username,
-
# password: who.password
-
# assert(...)
-
# end
-
# end
-
# end
-
1
class IntegrationTest < ActiveSupport::TestCase
-
1
include Integration::Runner
-
1
include ActionController::TemplateAssertions
-
1
include ActionDispatch::Routing::UrlFor
-
-
1
@@app = nil
-
-
1
def self.app
-
@@app || ActionDispatch.test_app
-
end
-
-
1
def self.app=(app)
-
@@app = app
-
end
-
-
1
def app
-
super || self.class.app
-
end
-
-
1
def url_options
-
reset! unless integration_session
-
integration_session.url_options
-
end
-
end
-
end
-
1
require 'action_dispatch/middleware/cookies'
-
1
require 'action_dispatch/middleware/flash'
-
1
require 'active_support/core_ext/hash/indifferent_access'
-
-
1
module ActionDispatch
-
1
module TestProcess
-
1
def assigns(key = nil)
-
1
assigns = {}.with_indifferent_access
-
3
@controller.view_assigns.each { |k, v| assigns.regular_writer(k, v) }
-
1
key.nil? ? assigns : assigns[key]
-
end
-
-
1
def session
-
@request.session
-
end
-
-
1
def flash
-
@request.flash
-
end
-
-
1
def cookies
-
@request.cookie_jar
-
end
-
-
1
def redirect_to_url
-
@response.redirect_url
-
end
-
-
# Shortcut for <tt>Rack::Test::UploadedFile.new(File.join(ActionController::TestCase.fixture_path, path), type)</tt>:
-
#
-
# post :change_avatar, avatar: fixture_file_upload('files/spongebob.png', 'image/png')
-
#
-
# To upload binary files on Windows, pass <tt>:binary</tt> as the last parameter.
-
# This will not affect other platforms:
-
#
-
# post :change_avatar, avatar: fixture_file_upload('files/spongebob.png', 'image/png', :binary)
-
1
def fixture_file_upload(path, mime_type = nil, binary = false)
-
if self.class.respond_to?(:fixture_path) && self.class.fixture_path
-
path = File.join(self.class.fixture_path, path)
-
end
-
Rack::Test::UploadedFile.new(path, mime_type, binary)
-
end
-
end
-
end
-
1
require 'active_support/core_ext/hash/indifferent_access'
-
1
require 'rack/utils'
-
-
1
module ActionDispatch
-
1
class TestRequest < Request
-
1
DEFAULT_ENV = Rack::MockRequest.env_for('/',
-
'HTTP_HOST' => 'test.host',
-
'REMOTE_ADDR' => '0.0.0.0',
-
'HTTP_USER_AGENT' => 'Rails Testing'
-
)
-
-
1
def self.new(env = {})
-
4
super
-
end
-
-
1
def initialize(env = {})
-
4
env = Rails.application.env_config.merge(env) if defined?(Rails.application) && Rails.application
-
4
super(default_env.merge(env))
-
end
-
-
1
def request_method=(method)
-
@env['REQUEST_METHOD'] = method.to_s.upcase
-
end
-
-
1
def host=(host)
-
@env['HTTP_HOST'] = host
-
end
-
-
1
def port=(number)
-
@env['SERVER_PORT'] = number.to_i
-
end
-
-
1
def request_uri=(uri)
-
@env['REQUEST_URI'] = uri
-
end
-
-
1
def path=(path)
-
@env['PATH_INFO'] = path
-
end
-
-
1
def action=(action_name)
-
path_parameters["action"] = action_name.to_s
-
end
-
-
1
def if_modified_since=(last_modified)
-
@env['HTTP_IF_MODIFIED_SINCE'] = last_modified
-
end
-
-
1
def if_none_match=(etag)
-
@env['HTTP_IF_NONE_MATCH'] = etag
-
end
-
-
1
def remote_addr=(addr)
-
@env['REMOTE_ADDR'] = addr
-
end
-
-
1
def user_agent=(user_agent)
-
@env['HTTP_USER_AGENT'] = user_agent
-
end
-
-
1
def accept=(mime_types)
-
@env.delete('action_dispatch.request.accepts')
-
@env['HTTP_ACCEPT'] = Array(mime_types).collect { |mime_type| mime_type.to_s }.join(",")
-
end
-
-
1
alias :rack_cookies :cookies
-
-
1
def cookies
-
8
@cookies ||= {}.with_indifferent_access
-
end
-
-
1
private
-
-
1
def default_env
-
DEFAULT_ENV
-
end
-
end
-
end
-
1
module ActionDispatch
-
# Integration test methods such as ActionDispatch::Integration::Session#get
-
# and ActionDispatch::Integration::Session#post return objects of class
-
# TestResponse, which represent the HTTP response results of the requested
-
# controller actions.
-
#
-
# See Response for more information on controller response objects.
-
1
class TestResponse < Response
-
1
def self.from_response(response)
-
new.tap do |resp|
-
resp.status = response.status
-
resp.headers = response.headers
-
resp.body = response.body
-
end
-
end
-
-
# Was the response successful?
-
1
alias_method :success?, :successful?
-
-
# Was the URL not found?
-
1
alias_method :missing?, :not_found?
-
-
# Were we redirected?
-
1
alias_method :redirect?, :redirection?
-
-
# Was there a server-side error?
-
1
alias_method :error?, :server_error?
-
end
-
end
-
#--
-
# Copyright (c) 2004-2014 David Heinemeier Hansson
-
#
-
# Permission is hereby granted, free of charge, to any person obtaining
-
# a copy of this software and associated documentation files (the
-
# "Software"), to deal in the Software without restriction, including
-
# without limitation the rights to use, copy, modify, merge, publish,
-
# distribute, sublicense, and/or sell copies of the Software, and to
-
# permit persons to whom the Software is furnished to do so, subject to
-
# the following conditions:
-
#
-
# The above copyright notice and this permission notice shall be
-
# included in all copies or substantial portions of the Software.
-
#
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
#++
-
-
1
require 'action_pack/version'
-
1
module ActionPack
-
# Returns the version of the currently loaded ActionPack as a <tt>Gem::Version</tt>
-
1
def self.gem_version
-
Gem::Version.new VERSION::STRING
-
end
-
-
1
module VERSION
-
1
MAJOR = 4
-
1
MINOR = 1
-
1
TINY = 6
-
1
PRE = nil
-
-
1
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
-
end
-
end
-
1
require_relative 'gem_version'
-
-
1
module ActionPack
-
# Returns the version of the currently loaded ActionPack as a <tt>Gem::Version</tt>
-
1
def self.version
-
gem_version
-
end
-
end
-
#--
-
# Copyright (c) 2004-2014 David Heinemeier Hansson
-
#
-
# Permission is hereby granted, free of charge, to any person obtaining
-
# a copy of this software and associated documentation files (the
-
# "Software"), to deal in the Software without restriction, including
-
# without limitation the rights to use, copy, modify, merge, publish,
-
# distribute, sublicense, and/or sell copies of the Software, and to
-
# permit persons to whom the Software is furnished to do so, subject to
-
# the following conditions:
-
#
-
# The above copyright notice and this permission notice shall be
-
# included in all copies or substantial portions of the Software.
-
#
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
#++
-
-
1
require 'active_support'
-
1
require 'active_support/rails'
-
1
require 'action_view/version'
-
-
1
module ActionView
-
1
extend ActiveSupport::Autoload
-
-
1
ENCODING_FLAG = '#.*coding[:=]\s*(\S+)[ \t]*'
-
-
1
eager_autoload do
-
1
autoload :Base
-
1
autoload :Context
-
1
autoload :CompiledTemplates, "action_view/context"
-
1
autoload :Digestor
-
1
autoload :Helpers
-
1
autoload :LookupContext
-
1
autoload :Layouts
-
1
autoload :PathSet
-
1
autoload :RecordIdentifier
-
1
autoload :Rendering
-
1
autoload :RoutingUrlFor
-
1
autoload :Template
-
1
autoload :ViewPaths
-
-
1
autoload_under "renderer" do
-
1
autoload :Renderer
-
1
autoload :AbstractRenderer
-
1
autoload :PartialRenderer
-
1
autoload :TemplateRenderer
-
1
autoload :StreamingTemplateRenderer
-
end
-
-
1
autoload_at "action_view/template/resolver" do
-
1
autoload :Resolver
-
1
autoload :PathResolver
-
1
autoload :OptimizedFileSystemResolver
-
1
autoload :FallbackFileSystemResolver
-
end
-
-
1
autoload_at "action_view/buffers" do
-
1
autoload :OutputBuffer
-
1
autoload :StreamingBuffer
-
end
-
-
1
autoload_at "action_view/flows" do
-
1
autoload :OutputFlow
-
1
autoload :StreamingFlow
-
end
-
-
1
autoload_at "action_view/template/error" do
-
1
autoload :MissingTemplate
-
1
autoload :ActionViewError
-
1
autoload :EncodingError
-
1
autoload :MissingRequestError
-
1
autoload :TemplateError
-
1
autoload :WrongEncodingError
-
end
-
end
-
-
1
autoload :TestCase
-
-
1
def self.eager_load!
-
super
-
ActionView::Helpers.eager_load!
-
ActionView::Template.eager_load!
-
HTML.eager_load!
-
end
-
end
-
-
1
require 'active_support/core_ext/string/output_safety'
-
-
1
ActiveSupport.on_load(:i18n) do
-
1
I18n.load_path << "#{File.dirname(__FILE__)}/action_view/locale/en.yml"
-
end
-
1
require 'active_support/core_ext/module/attr_internal'
-
1
require 'active_support/core_ext/module/attribute_accessors'
-
1
require 'active_support/ordered_options'
-
1
require 'action_view/log_subscriber'
-
1
require 'action_view/helpers'
-
1
require 'action_view/context'
-
1
require 'action_view/template'
-
1
require 'action_view/lookup_context'
-
-
1
module ActionView #:nodoc:
-
# = Action View Base
-
#
-
# Action View templates can be written in several ways. If the template file has a <tt>.erb</tt> extension then it uses a mixture of ERB
-
# (included in Ruby) and HTML. If the template file has a <tt>.builder</tt> extension then Jim Weirich's Builder::XmlMarkup library is used.
-
#
-
# == ERB
-
#
-
# You trigger ERB by using embeddings such as <% %>, <% -%>, and <%= %>. The <%= %> tag set is used when you want output. Consider the
-
# following loop for names:
-
#
-
# <b>Names of all the people</b>
-
# <% @people.each do |person| %>
-
# Name: <%= person.name %><br/>
-
# <% end %>
-
#
-
# The loop is setup in regular embedding tags <% %> and the name is written using the output embedding tag <%= %>. Note that this
-
# is not just a usage suggestion. Regular output functions like print or puts won't work with ERB templates. So this would be wrong:
-
#
-
# <%# WRONG %>
-
# Hi, Mr. <% puts "Frodo" %>
-
#
-
# If you absolutely must write from within a function use +concat+.
-
#
-
# <%- and -%> suppress leading and trailing whitespace, including the trailing newline, and can be used interchangeably with <% and %>.
-
#
-
# === Using sub templates
-
#
-
# Using sub templates allows you to sidestep tedious replication and extract common display structures in shared templates. The
-
# classic example is the use of a header and footer (even though the Action Pack-way would be to use Layouts):
-
#
-
# <%= render "shared/header" %>
-
# Something really specific and terrific
-
# <%= render "shared/footer" %>
-
#
-
# As you see, we use the output embeddings for the render methods. The render call itself will just return a string holding the
-
# result of the rendering. The output embedding writes it to the current template.
-
#
-
# But you don't have to restrict yourself to static includes. Templates can share variables amongst themselves by using instance
-
# variables defined using the regular embedding tags. Like this:
-
#
-
# <% @page_title = "A Wonderful Hello" %>
-
# <%= render "shared/header" %>
-
#
-
# Now the header can pick up on the <tt>@page_title</tt> variable and use it for outputting a title tag:
-
#
-
# <title><%= @page_title %></title>
-
#
-
# === Passing local variables to sub templates
-
#
-
# You can pass local variables to sub templates by using a hash with the variable names as keys and the objects as values:
-
#
-
# <%= render "shared/header", { headline: "Welcome", person: person } %>
-
#
-
# These can now be accessed in <tt>shared/header</tt> with:
-
#
-
# Headline: <%= headline %>
-
# First name: <%= person.first_name %>
-
#
-
# If you need to find out whether a certain local variable has been assigned a value in a particular render call,
-
# you need to use the following pattern:
-
#
-
# <% if local_assigns.has_key? :headline %>
-
# Headline: <%= headline %>
-
# <% end %>
-
#
-
# Testing using <tt>defined? headline</tt> will not work. This is an implementation restriction.
-
#
-
# === Template caching
-
#
-
# By default, Rails will compile each template to a method in order to render it. When you alter a template,
-
# Rails will check the file's modification time and recompile it in development mode.
-
#
-
# == Builder
-
#
-
# Builder templates are a more programmatic alternative to ERB. They are especially useful for generating XML content. An XmlMarkup object
-
# named +xml+ is automatically made available to templates with a <tt>.builder</tt> extension.
-
#
-
# Here are some basic examples:
-
#
-
# xml.em("emphasized") # => <em>emphasized</em>
-
# xml.em { xml.b("emph & bold") } # => <em><b>emph & bold</b></em>
-
# xml.a("A Link", "href" => "http://onestepback.org") # => <a href="http://onestepback.org">A Link</a>
-
# xml.target("name" => "compile", "option" => "fast") # => <target option="fast" name="compile"\>
-
# # NOTE: order of attributes is not specified.
-
#
-
# Any method with a block will be treated as an XML markup tag with nested markup in the block. For example, the following:
-
#
-
# xml.div do
-
# xml.h1(@person.name)
-
# xml.p(@person.bio)
-
# end
-
#
-
# would produce something like:
-
#
-
# <div>
-
# <h1>David Heinemeier Hansson</h1>
-
# <p>A product of Danish Design during the Winter of '79...</p>
-
# </div>
-
#
-
# A full-length RSS example actually used on Basecamp:
-
#
-
# xml.rss("version" => "2.0", "xmlns:dc" => "http://purl.org/dc/elements/1.1/") do
-
# xml.channel do
-
# xml.title(@feed_title)
-
# xml.link(@url)
-
# xml.description "Basecamp: Recent items"
-
# xml.language "en-us"
-
# xml.ttl "40"
-
#
-
# @recent_items.each do |item|
-
# xml.item do
-
# xml.title(item_title(item))
-
# xml.description(item_description(item)) if item_description(item)
-
# xml.pubDate(item_pubDate(item))
-
# xml.guid(@person.firm.account.url + @recent_items.url(item))
-
# xml.link(@person.firm.account.url + @recent_items.url(item))
-
#
-
# xml.tag!("dc:creator", item.author_name) if item_has_creator?(item)
-
# end
-
# end
-
# end
-
# end
-
#
-
# More builder documentation can be found at http://builder.rubyforge.org.
-
1
class Base
-
1
include Helpers, ::ERB::Util, Context
-
-
# Specify the proc used to decorate input tags that refer to attributes with errors.
-
1
cattr_accessor :field_error_proc
-
1
@@field_error_proc = Proc.new{ |html_tag, instance| "<div class=\"field_with_errors\">#{html_tag}</div>".html_safe }
-
-
# How to complete the streaming when an exception occurs.
-
# This is our best guess: first try to close the attribute, then the tag.
-
1
cattr_accessor :streaming_completion_on_exception
-
1
@@streaming_completion_on_exception = %("><script>window.location = "/500.html"</script></html>)
-
-
# Specify whether rendering within namespaced controllers should prefix
-
# the partial paths for ActiveModel objects with the namespace.
-
# (e.g., an Admin::PostsController would render @post using /admin/posts/_post.erb)
-
1
cattr_accessor :prefix_partial_path_with_controller_namespace
-
1
@@prefix_partial_path_with_controller_namespace = true
-
-
# Specify default_formats that can be rendered.
-
1
cattr_accessor :default_formats
-
-
# Specify whether an error should be raised for missing translations
-
1
cattr_accessor :raise_on_missing_translations
-
1
@@raise_on_missing_translations = false
-
-
1
class_attribute :_routes
-
1
class_attribute :logger
-
-
1
class << self
-
1
delegate :erb_trim_mode=, :to => 'ActionView::Template::Handlers::ERB'
-
-
1
def cache_template_loading
-
ActionView::Resolver.caching?
-
end
-
-
1
def cache_template_loading=(value)
-
ActionView::Resolver.caching = value
-
end
-
-
1
def xss_safe? #:nodoc:
-
true
-
end
-
end
-
-
1
attr_accessor :view_renderer
-
1
attr_internal :config, :assigns
-
-
1
delegate :lookup_context, :to => :view_renderer
-
1
delegate :formats, :formats=, :locale, :locale=, :view_paths, :view_paths=, :to => :lookup_context
-
-
1
def assign(new_assigns) # :nodoc:
-
6
@_assigns = new_assigns.each { |key, value| instance_variable_set("@#{key}", value) }
-
end
-
-
1
def initialize(context = nil, assigns = {}, controller = nil, formats = nil) #:nodoc:
-
2
@_config = ActiveSupport::InheritableOptions.new
-
-
2
if context.is_a?(ActionView::Renderer)
-
2
@view_renderer = context
-
else
-
lookup_context = context.is_a?(ActionView::LookupContext) ?
-
context : ActionView::LookupContext.new(context)
-
lookup_context.formats = formats if formats
-
lookup_context.prefixes = controller._prefixes if controller
-
@view_renderer = ActionView::Renderer.new(lookup_context)
-
end
-
-
2
assign(assigns)
-
2
assign_controller(controller)
-
2
_prepare_context
-
end
-
-
1
ActiveSupport.run_load_hooks(:action_view, self)
-
end
-
end
-
1
module ActionView
-
1
module CompiledTemplates #:nodoc:
-
# holds compiled template code
-
end
-
-
# = Action View Context
-
#
-
# Action View contexts are supplied to Action Controller to render a template.
-
# The default Action View context is ActionView::Base.
-
#
-
# In order to work with ActionController, a Context must just include this module.
-
# The initialization of the variables used by the context (@output_buffer, @view_flow,
-
# and @virtual_path) is responsibility of the object that includes this module
-
# (although you can call _prepare_context defined below).
-
1
module Context
-
1
include CompiledTemplates
-
1
attr_accessor :output_buffer, :view_flow
-
-
# Prepares the context by setting the appropriate instance variables.
-
# :api: plugin
-
1
def _prepare_context
-
2
@view_flow = OutputFlow.new
-
2
@output_buffer = nil
-
2
@virtual_path = nil
-
end
-
-
# Encapsulates the interaction with the view flow so it
-
# returns the correct buffer on +yield+. This is usually
-
# overwritten by helpers to add more behavior.
-
# :api: plugin
-
1
def _layout_for(name=nil)
-
name ||= :layout
-
view_flow.get(name).html_safe
-
end
-
end
-
end
-
1
require 'thread_safe'
-
-
1
module ActionView
-
1
class DependencyTracker # :nodoc:
-
1
@trackers = ThreadSafe::Cache.new
-
-
1
def self.find_dependencies(name, template)
-
tracker = @trackers[template.handler]
-
-
if tracker.present?
-
tracker.call(name, template)
-
else
-
[]
-
end
-
end
-
-
1
def self.register_tracker(extension, tracker)
-
2
handler = Template.handler_for_extension(extension)
-
2
@trackers[handler] = tracker
-
end
-
-
1
def self.remove_tracker(handler)
-
@trackers.delete(handler)
-
end
-
-
1
class ERBTracker # :nodoc:
-
1
EXPLICIT_DEPENDENCY = /# Template Dependency: (\S+)/
-
-
# A valid ruby identifier - suitable for class, method and specially variable names
-
1
IDENTIFIER = /
-
[[:alpha:]_] # at least one uppercase letter, lowercase letter or underscore
-
[[:word:]]* # followed by optional letters, numbers or underscores
-
/x
-
-
# Any kind of variable name. e.g. @instance, @@class, $global or local.
-
# Possibly following a method call chain
-
1
VARIABLE_OR_METHOD_CHAIN = /
-
(?:\$|@{1,2})? # optional global, instance or class variable indicator
-
(?:#{IDENTIFIER}\.)* # followed by an optional chain of zero-argument method calls
-
(?<dynamic>#{IDENTIFIER}) # and a final valid identifier, captured as DYNAMIC
-
/x
-
-
# A simple string literal. e.g. "School's out!"
-
1
STRING = /
-
(?<quote>['"]) # an opening quote
-
(?<static>.*?) # with anything inside, captured as STATIC
-
\k<quote> # and a matching closing quote
-
/x
-
-
# Part of any hash containing the :partial key
-
1
PARTIAL_HASH_KEY = /
-
(?:\bpartial:|:partial\s*=>) # partial key in either old or new style hash syntax
-
\s* # followed by optional spaces
-
/x
-
-
# Part of any hash containing the :layout key
-
1
LAYOUT_HASH_KEY = /
-
(?:\blayout:|:layout\s*=>) # layout key in either old or new style hash syntax
-
\s* # followed by optional spaces
-
/x
-
-
# Matches:
-
# partial: "comments/comment", collection: @all_comments => "comments/comment"
-
# (object: @single_comment, partial: "comments/comment") => "comments/comment"
-
#
-
# "comments/comments"
-
# 'comments/comments'
-
# ('comments/comments')
-
#
-
# (@topic) => "topics/topic"
-
# topics => "topics/topic"
-
# (message.topics) => "topics/topic"
-
1
RENDER_ARGUMENTS = /\A
-
(?:\s*\(?\s*) # optional opening paren surrounded by spaces
-
(?:.*?#{PARTIAL_HASH_KEY}|#{LAYOUT_HASH_KEY})? # optional hash, up to the partial or layout key declaration
-
(?:#{STRING}|#{VARIABLE_OR_METHOD_CHAIN}) # finally, the dependency name of interest
-
/xm
-
-
1
def self.call(name, template)
-
new(name, template).dependencies
-
end
-
-
1
def initialize(name, template)
-
@name, @template = name, template
-
end
-
-
1
def dependencies
-
render_dependencies + explicit_dependencies
-
end
-
-
1
attr_reader :name, :template
-
1
private :name, :template
-
-
-
1
private
-
1
def source
-
template.source
-
end
-
-
1
def directory
-
name.split("/")[0..-2].join("/")
-
end
-
-
1
def render_dependencies
-
render_dependencies = []
-
render_calls = source.split(/\brender\b/).drop(1)
-
-
render_calls.each do |arguments|
-
arguments.scan(RENDER_ARGUMENTS) do
-
add_dynamic_dependency(render_dependencies, Regexp.last_match[:dynamic])
-
add_static_dependency(render_dependencies, Regexp.last_match[:static])
-
end
-
end
-
-
render_dependencies.uniq
-
end
-
-
1
def add_dynamic_dependency(dependencies, dependency)
-
if dependency
-
dependencies << "#{dependency.pluralize}/#{dependency.singularize}"
-
end
-
end
-
-
1
def add_static_dependency(dependencies, dependency)
-
if dependency
-
if dependency.include?('/')
-
dependencies << dependency
-
else
-
dependencies << "#{directory}/#{dependency}"
-
end
-
end
-
end
-
-
1
def explicit_dependencies
-
source.scan(EXPLICIT_DEPENDENCY).flatten.uniq
-
end
-
end
-
-
1
register_tracker :erb, ERBTracker
-
end
-
end
-
1
require 'active_support/core_ext/string/output_safety'
-
-
1
module ActionView
-
1
class OutputFlow #:nodoc:
-
1
attr_reader :content
-
-
1
def initialize
-
2
@content = Hash.new { |h,k| h[k] = ActiveSupport::SafeBuffer.new }
-
end
-
-
# Called by _layout_for to read stored values.
-
1
def get(key)
-
@content[key]
-
end
-
-
# Called by each renderer object to set the layout contents.
-
1
def set(key, value)
-
2
@content[key] = value
-
end
-
-
# Called by content_for
-
1
def append(key, value)
-
@content[key] << value
-
end
-
1
alias_method :append!, :append
-
-
end
-
-
1
class StreamingFlow < OutputFlow #:nodoc:
-
1
def initialize(view, fiber)
-
@view = view
-
@parent = nil
-
@child = view.output_buffer
-
@content = view.view_flow.content
-
@fiber = fiber
-
@root = Fiber.current.object_id
-
end
-
-
# Try to get stored content. If the content
-
# is not available and we are inside the layout
-
# fiber, we set that we are waiting for the given
-
# key and yield.
-
1
def get(key)
-
return super if @content.key?(key)
-
-
if inside_fiber?
-
view = @view
-
-
begin
-
@waiting_for = key
-
view.output_buffer, @parent = @child, view.output_buffer
-
Fiber.yield
-
ensure
-
@waiting_for = nil
-
view.output_buffer, @child = @parent, view.output_buffer
-
end
-
end
-
-
super
-
end
-
-
# Appends the contents for the given key. This is called
-
# by provides and resumes back to the fiber if it is
-
# the key it is waiting for.
-
1
def append!(key, value)
-
super
-
@fiber.resume if @waiting_for == key
-
end
-
-
1
private
-
-
1
def inside_fiber?
-
Fiber.current.object_id != @root
-
end
-
end
-
end
-
1
module ActionView
-
# Returns the version of the currently loaded ActionView as a <tt>Gem::Version</tt>
-
1
def self.gem_version
-
Gem::Version.new VERSION::STRING
-
end
-
-
1
module VERSION
-
1
MAJOR = 4
-
1
MINOR = 1
-
1
TINY = 6
-
1
PRE = nil
-
-
1
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
-
end
-
end
-
1
require 'active_support/benchmarkable'
-
-
1
module ActionView #:nodoc:
-
1
module Helpers #:nodoc:
-
1
extend ActiveSupport::Autoload
-
-
1
autoload :ActiveModelHelper
-
1
autoload :AssetTagHelper
-
1
autoload :AssetUrlHelper
-
1
autoload :AtomFeedHelper
-
1
autoload :CacheHelper
-
1
autoload :CaptureHelper
-
1
autoload :ControllerHelper
-
1
autoload :CsrfHelper
-
1
autoload :DateHelper
-
1
autoload :DebugHelper
-
1
autoload :FormHelper
-
1
autoload :FormOptionsHelper
-
1
autoload :FormTagHelper
-
1
autoload :JavaScriptHelper, "action_view/helpers/javascript_helper"
-
1
autoload :NumberHelper
-
1
autoload :OutputSafetyHelper
-
1
autoload :RecordTagHelper
-
1
autoload :RenderingHelper
-
1
autoload :SanitizeHelper
-
1
autoload :TagHelper
-
1
autoload :TextHelper
-
1
autoload :TranslationHelper
-
1
autoload :UrlHelper
-
1
autoload :Tags
-
-
1
def self.eager_load!
-
super
-
Tags.eager_load!
-
end
-
-
1
extend ActiveSupport::Concern
-
-
1
include ActiveSupport::Benchmarkable
-
1
include ActiveModelHelper
-
1
include AssetTagHelper
-
1
include AssetUrlHelper
-
1
include AtomFeedHelper
-
1
include CacheHelper
-
1
include CaptureHelper
-
1
include ControllerHelper
-
1
include CsrfHelper
-
1
include DateHelper
-
1
include DebugHelper
-
1
include FormHelper
-
1
include FormOptionsHelper
-
1
include FormTagHelper
-
1
include JavaScriptHelper
-
1
include NumberHelper
-
1
include OutputSafetyHelper
-
1
include RecordTagHelper
-
1
include RenderingHelper
-
1
include SanitizeHelper
-
1
include TagHelper
-
1
include TextHelper
-
1
include TranslationHelper
-
1
include UrlHelper
-
end
-
end
-
1
require 'active_support/core_ext/module/attribute_accessors'
-
1
require 'active_support/core_ext/enumerable'
-
-
1
module ActionView
-
# = Active Model Helpers
-
1
module Helpers
-
1
module ActiveModelHelper
-
end
-
-
1
module ActiveModelInstanceTag
-
1
def object
-
@active_model_object ||= begin
-
object = super
-
object.respond_to?(:to_model) ? object.to_model : object
-
end
-
end
-
-
1
def content_tag(*)
-
error_wrapping(super)
-
end
-
-
1
def tag(type, options, *)
-
tag_generate_errors?(options) ? error_wrapping(super) : super
-
end
-
-
1
def error_wrapping(html_tag)
-
if object_has_errors?
-
Base.field_error_proc.call(html_tag, self)
-
else
-
html_tag
-
end
-
end
-
-
1
def error_message
-
object.errors[@method_name]
-
end
-
-
1
private
-
-
1
def object_has_errors?
-
object.respond_to?(:errors) && object.errors.respond_to?(:[]) && error_message.present?
-
end
-
-
1
def tag_generate_errors?(options)
-
options['type'] != 'hidden'
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/array/extract_options'
-
1
require 'active_support/core_ext/hash/keys'
-
1
require 'action_view/helpers/asset_url_helper'
-
1
require 'action_view/helpers/tag_helper'
-
-
1
module ActionView
-
# = Action View Asset Tag Helpers
-
1
module Helpers #:nodoc:
-
# This module provides methods for generating HTML that links views to assets such
-
# as images, javascripts, stylesheets, and feeds. These methods do not verify
-
# the assets exist before linking to them:
-
#
-
# image_tag("rails.png")
-
# # => <img alt="Rails" src="/assets/rails.png" />
-
# stylesheet_link_tag("application")
-
# # => <link href="/assets/application.css?body=1" media="screen" rel="stylesheet" />
-
1
module AssetTagHelper
-
1
extend ActiveSupport::Concern
-
-
1
include AssetUrlHelper
-
1
include TagHelper
-
-
# Returns an HTML script tag for each of the +sources+ provided.
-
#
-
# Sources may be paths to JavaScript files. Relative paths are assumed to be relative
-
# to <tt>assets/javascripts</tt>, full paths are assumed to be relative to the document
-
# root. Relative paths are idiomatic, use absolute paths only when needed.
-
#
-
# When passing paths, the ".js" extension is optional. If you do not want ".js"
-
# appended to the path <tt>extname: false</tt> can be set on the options.
-
#
-
# You can modify the HTML attributes of the script tag by passing a hash as the
-
# last argument.
-
#
-
# When the Asset Pipeline is enabled, you can pass the name of your manifest as
-
# source, and include other JavaScript or CoffeeScript files inside the manifest.
-
#
-
# javascript_include_tag "xmlhr"
-
# # => <script src="/assets/xmlhr.js?1284139606"></script>
-
#
-
# javascript_include_tag "template.jst", extname: false
-
# # => <script src="/assets/template.jst?1284139606"></script>
-
#
-
# javascript_include_tag "xmlhr.js"
-
# # => <script src="/assets/xmlhr.js?1284139606"></script>
-
#
-
# javascript_include_tag "common.javascript", "/elsewhere/cools"
-
# # => <script src="/assets/common.javascript?1284139606"></script>
-
# # <script src="/elsewhere/cools.js?1423139606"></script>
-
#
-
# javascript_include_tag "http://www.example.com/xmlhr"
-
# # => <script src="http://www.example.com/xmlhr"></script>
-
#
-
# javascript_include_tag "http://www.example.com/xmlhr.js"
-
# # => <script src="http://www.example.com/xmlhr.js"></script>
-
1
def javascript_include_tag(*sources)
-
options = sources.extract_options!.stringify_keys
-
path_options = options.extract!('protocol', 'extname').symbolize_keys
-
sources.uniq.map { |source|
-
tag_options = {
-
"src" => path_to_javascript(source, path_options)
-
}.merge!(options)
-
content_tag(:script, "", tag_options)
-
}.join("\n").html_safe
-
end
-
-
# Returns a stylesheet link tag for the sources specified as arguments. If
-
# you don't specify an extension, <tt>.css</tt> will be appended automatically.
-
# You can modify the link attributes by passing a hash as the last argument.
-
# For historical reasons, the 'media' attribute will always be present and defaults
-
# to "screen", so you must explicitly set it to "all" for the stylesheet(s) to
-
# apply to all media types.
-
#
-
# stylesheet_link_tag "style"
-
# # => <link href="/assets/style.css" media="screen" rel="stylesheet" />
-
#
-
# stylesheet_link_tag "style.css"
-
# # => <link href="/assets/style.css" media="screen" rel="stylesheet" />
-
#
-
# stylesheet_link_tag "http://www.example.com/style.css"
-
# # => <link href="http://www.example.com/style.css" media="screen" rel="stylesheet" />
-
#
-
# stylesheet_link_tag "style", media: "all"
-
# # => <link href="/assets/style.css" media="all" rel="stylesheet" />
-
#
-
# stylesheet_link_tag "style", media: "print"
-
# # => <link href="/assets/style.css" media="print" rel="stylesheet" />
-
#
-
# stylesheet_link_tag "random.styles", "/css/stylish"
-
# # => <link href="/assets/random.styles" media="screen" rel="stylesheet" />
-
# # <link href="/css/stylish.css" media="screen" rel="stylesheet" />
-
1
def stylesheet_link_tag(*sources)
-
options = sources.extract_options!.stringify_keys
-
path_options = options.extract!('protocol').symbolize_keys
-
-
sources.uniq.map { |source|
-
tag_options = {
-
"rel" => "stylesheet",
-
"media" => "screen",
-
"href" => path_to_stylesheet(source, path_options)
-
}.merge!(options)
-
tag(:link, tag_options)
-
}.join("\n").html_safe
-
end
-
-
# Returns a link tag that browsers and feed readers can use to auto-detect
-
# an RSS or Atom feed. The +type+ can either be <tt>:rss</tt> (default) or
-
# <tt>:atom</tt>. Control the link options in url_for format using the
-
# +url_options+. You can modify the LINK tag itself in +tag_options+.
-
#
-
# ==== Options
-
#
-
# * <tt>:rel</tt> - Specify the relation of this link, defaults to "alternate"
-
# * <tt>:type</tt> - Override the auto-generated mime type
-
# * <tt>:title</tt> - Specify the title of the link, defaults to the +type+
-
#
-
# ==== Examples
-
#
-
# auto_discovery_link_tag
-
# # => <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.currenthost.com/controller/action" />
-
# auto_discovery_link_tag(:atom)
-
# # => <link rel="alternate" type="application/atom+xml" title="ATOM" href="http://www.currenthost.com/controller/action" />
-
# auto_discovery_link_tag(:rss, {action: "feed"})
-
# # => <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.currenthost.com/controller/feed" />
-
# auto_discovery_link_tag(:rss, {action: "feed"}, {title: "My RSS"})
-
# # => <link rel="alternate" type="application/rss+xml" title="My RSS" href="http://www.currenthost.com/controller/feed" />
-
# auto_discovery_link_tag(:rss, {controller: "news", action: "feed"})
-
# # => <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.currenthost.com/news/feed" />
-
# auto_discovery_link_tag(:rss, "http://www.example.com/feed.rss", {title: "Example RSS"})
-
# # => <link rel="alternate" type="application/rss+xml" title="Example RSS" href="http://www.example.com/feed" />
-
1
def auto_discovery_link_tag(type = :rss, url_options = {}, tag_options = {})
-
if !(type == :rss || type == :atom) && tag_options[:type].blank?
-
raise ArgumentError.new("You should pass :type tag_option key explicitly, because you have passed #{type} type other than :rss or :atom.")
-
end
-
-
tag(
-
"link",
-
"rel" => tag_options[:rel] || "alternate",
-
"type" => tag_options[:type] || Mime::Type.lookup_by_extension(type.to_s).to_s,
-
"title" => tag_options[:title] || type.to_s.upcase,
-
"href" => url_options.is_a?(Hash) ? url_for(url_options.merge(:only_path => false)) : url_options
-
)
-
end
-
-
# Returns a link loading a favicon file. You may specify a different file
-
# in the first argument. The helper accepts an additional options hash where
-
# you can override "rel" and "type".
-
#
-
# ==== Options
-
#
-
# * <tt>:rel</tt> - Specify the relation of this link, defaults to 'shortcut icon'
-
# * <tt>:type</tt> - Override the auto-generated mime type, defaults to 'image/vnd.microsoft.icon'
-
#
-
# ==== Examples
-
#
-
# favicon_link_tag 'myicon.ico'
-
# # => <link href="/assets/myicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon" />
-
#
-
# Mobile Safari looks for a different <link> tag, pointing to an image that
-
# will be used if you add the page to the home screen of an iPod Touch, iPhone, or iPad.
-
# The following call would generate such a tag:
-
#
-
# favicon_link_tag 'mb-icon.png', rel: 'apple-touch-icon', type: 'image/png'
-
# # => <link href="/assets/mb-icon.png" rel="apple-touch-icon" type="image/png" />
-
1
def favicon_link_tag(source='favicon.ico', options={})
-
tag('link', {
-
:rel => 'shortcut icon',
-
:type => 'image/vnd.microsoft.icon',
-
:href => path_to_image(source)
-
}.merge!(options.symbolize_keys))
-
end
-
-
# Returns an HTML image tag for the +source+. The +source+ can be a full
-
# path or a file.
-
#
-
# ==== Options
-
#
-
# You can add HTML attributes using the +options+. The +options+ supports
-
# two additional keys for convenience and conformance:
-
#
-
# * <tt>:alt</tt> - If no alt text is given, the file name part of the
-
# +source+ is used (capitalized and without the extension)
-
# * <tt>:size</tt> - Supplied as "{Width}x{Height}" or "{Number}", so "30x45" becomes
-
# width="30" and height="45", and "50" becomes width="50" and height="50".
-
# <tt>:size</tt> will be ignored if the value is not in the correct format.
-
#
-
# ==== Examples
-
#
-
# image_tag("icon")
-
# # => <img alt="Icon" src="/assets/icon" />
-
# image_tag("icon.png")
-
# # => <img alt="Icon" src="/assets/icon.png" />
-
# image_tag("icon.png", size: "16x10", alt: "Edit Entry")
-
# # => <img src="/assets/icon.png" width="16" height="10" alt="Edit Entry" />
-
# image_tag("/icons/icon.gif", size: "16")
-
# # => <img src="/icons/icon.gif" width="16" height="16" alt="Icon" />
-
# image_tag("/icons/icon.gif", height: '32', width: '32')
-
# # => <img alt="Icon" height="32" src="/icons/icon.gif" width="32" />
-
# image_tag("/icons/icon.gif", class: "menu_icon")
-
# # => <img alt="Icon" class="menu_icon" src="/icons/icon.gif" />
-
1
def image_tag(source, options={})
-
options = options.symbolize_keys
-
-
src = options[:src] = path_to_image(source)
-
-
unless src =~ /^(?:cid|data):/ || src.blank?
-
options[:alt] = options.fetch(:alt){ image_alt(src) }
-
end
-
-
options[:width], options[:height] = extract_dimensions(options.delete(:size)) if options[:size]
-
tag("img", options)
-
end
-
-
# Returns a string suitable for an html image tag alt attribute.
-
# The +src+ argument is meant to be an image file path.
-
# The method removes the basename of the file path and the digest,
-
# if any. It also removes hyphens and underscores from file names and
-
# replaces them with spaces, returning a space-separated, titleized
-
# string.
-
#
-
# ==== Examples
-
#
-
# image_alt('rails.png')
-
# # => Rails
-
#
-
# image_alt('hyphenated-file-name.png')
-
# # => Hyphenated file name
-
#
-
# image_alt('underscored_file_name.png')
-
# # => Underscored file name
-
1
def image_alt(src)
-
File.basename(src, '.*').sub(/-[[:xdigit:]]{32}\z/, '').tr('-_', ' ').capitalize
-
end
-
-
# Returns an html video tag for the +sources+. If +sources+ is a string,
-
# a single video tag will be returned. If +sources+ is an array, a video
-
# tag with nested source tags for each source will be returned. The
-
# +sources+ can be full paths or files that exists in your public videos
-
# directory.
-
#
-
# ==== Options
-
# You can add HTML attributes using the +options+. The +options+ supports
-
# two additional keys for convenience and conformance:
-
#
-
# * <tt>:poster</tt> - Set an image (like a screenshot) to be shown
-
# before the video loads. The path is calculated like the +src+ of +image_tag+.
-
# * <tt>:size</tt> - Supplied as "{Width}x{Height}" or "{Number}", so "30x45" becomes
-
# width="30" and height="45", and "50" becomes width="50" and height="50".
-
# <tt>:size</tt> will be ignored if the value is not in the correct format.
-
#
-
# ==== Examples
-
#
-
# video_tag("trailer")
-
# # => <video src="/videos/trailer" />
-
# video_tag("trailer.ogg")
-
# # => <video src="/videos/trailer.ogg" />
-
# video_tag("trailer.ogg", controls: true, autobuffer: true)
-
# # => <video autobuffer="autobuffer" controls="controls" src="/videos/trailer.ogg" />
-
# video_tag("trailer.m4v", size: "16x10", poster: "screenshot.png")
-
# # => <video src="/videos/trailer.m4v" width="16" height="10" poster="/assets/screenshot.png" />
-
# video_tag("/trailers/hd.avi", size: "16x16")
-
# # => <video src="/trailers/hd.avi" width="16" height="16" />
-
# video_tag("/trailers/hd.avi", size: "16")
-
# # => <video height="16" src="/trailers/hd.avi" width="16" />
-
# video_tag("/trailers/hd.avi", height: '32', width: '32')
-
# # => <video height="32" src="/trailers/hd.avi" width="32" />
-
# video_tag("trailer.ogg", "trailer.flv")
-
# # => <video><source src="/videos/trailer.ogg" /><source src="/videos/trailer.flv" /></video>
-
# video_tag(["trailer.ogg", "trailer.flv"])
-
# # => <video><source src="/videos/trailer.ogg" /><source src="/videos/trailer.flv" /></video>
-
# video_tag(["trailer.ogg", "trailer.flv"], size: "160x120")
-
# # => <video height="120" width="160"><source src="/videos/trailer.ogg" /><source src="/videos/trailer.flv" /></video>
-
1
def video_tag(*sources)
-
multiple_sources_tag('video', sources) do |options|
-
options[:poster] = path_to_image(options[:poster]) if options[:poster]
-
options[:width], options[:height] = extract_dimensions(options.delete(:size)) if options[:size]
-
end
-
end
-
-
# Returns an HTML audio tag for the +source+.
-
# The +source+ can be full path or file that exists in
-
# your public audios directory.
-
#
-
# audio_tag("sound")
-
# # => <audio src="/audios/sound" />
-
# audio_tag("sound.wav")
-
# # => <audio src="/audios/sound.wav" />
-
# audio_tag("sound.wav", autoplay: true, controls: true)
-
# # => <audio autoplay="autoplay" controls="controls" src="/audios/sound.wav" />
-
# audio_tag("sound.wav", "sound.mid")
-
# # => <audio><source src="/audios/sound.wav" /><source src="/audios/sound.mid" /></audio>
-
1
def audio_tag(*sources)
-
multiple_sources_tag('audio', sources)
-
end
-
-
1
private
-
1
def multiple_sources_tag(type, sources)
-
options = sources.extract_options!.symbolize_keys
-
sources.flatten!
-
-
yield options if block_given?
-
-
if sources.size > 1
-
content_tag(type, options) do
-
safe_join sources.map { |source| tag("source", :src => send("path_to_#{type}", source)) }
-
end
-
else
-
options[:src] = send("path_to_#{type}", sources.first)
-
content_tag(type, nil, options)
-
end
-
end
-
-
1
def extract_dimensions(size)
-
if size =~ %r{\A\d+x\d+\z}
-
size.split('x')
-
elsif size =~ %r{\A\d+\z}
-
[size, size]
-
end
-
end
-
end
-
end
-
end
-
1
require 'zlib'
-
-
1
module ActionView
-
# = Action View Asset URL Helpers
-
1
module Helpers
-
# This module provides methods for generating asset paths and
-
# urls.
-
#
-
# image_path("rails.png")
-
# # => "/assets/rails.png"
-
#
-
# image_url("rails.png")
-
# # => "http://www.example.com/assets/rails.png"
-
#
-
# === Using asset hosts
-
#
-
# By default, Rails links to these assets on the current host in the public
-
# folder, but you can direct Rails to link to assets from a dedicated asset
-
# server by setting <tt>ActionController::Base.asset_host</tt> in the application
-
# configuration, typically in <tt>config/environments/production.rb</tt>.
-
# For example, you'd define <tt>assets.example.com</tt> to be your asset
-
# host this way, inside the <tt>configure</tt> block of your environment-specific
-
# configuration files or <tt>config/application.rb</tt>:
-
#
-
# config.action_controller.asset_host = "assets.example.com"
-
#
-
# Helpers take that into account:
-
#
-
# image_tag("rails.png")
-
# # => <img alt="Rails" src="http://assets.example.com/assets/rails.png" />
-
# stylesheet_link_tag("application")
-
# # => <link href="http://assets.example.com/assets/application.css" media="screen" rel="stylesheet" />
-
#
-
# Browsers typically open at most two simultaneous connections to a single
-
# host, which means your assets often have to wait for other assets to finish
-
# downloading. You can alleviate this by using a <tt>%d</tt> wildcard in the
-
# +asset_host+. For example, "assets%d.example.com". If that wildcard is
-
# present Rails distributes asset requests among the corresponding four hosts
-
# "assets0.example.com", ..., "assets3.example.com". With this trick browsers
-
# will open eight simultaneous connections rather than two.
-
#
-
# image_tag("rails.png")
-
# # => <img alt="Rails" src="http://assets0.example.com/assets/rails.png" />
-
# stylesheet_link_tag("application")
-
# # => <link href="http://assets2.example.com/assets/application.css" media="screen" rel="stylesheet" />
-
#
-
# To do this, you can either setup four actual hosts, or you can use wildcard
-
# DNS to CNAME the wildcard to a single asset host. You can read more about
-
# setting up your DNS CNAME records from your ISP.
-
#
-
# Note: This is purely a browser performance optimization and is not meant
-
# for server load balancing. See http://www.die.net/musings/page_load_time/
-
# for background.
-
#
-
# Alternatively, you can exert more control over the asset host by setting
-
# +asset_host+ to a proc like this:
-
#
-
# ActionController::Base.asset_host = Proc.new { |source|
-
# "http://assets#{Digest::MD5.hexdigest(source).to_i(16) % 2 + 1}.example.com"
-
# }
-
# image_tag("rails.png")
-
# # => <img alt="Rails" src="http://assets1.example.com/assets/rails.png" />
-
# stylesheet_link_tag("application")
-
# # => <link href="http://assets2.example.com/assets/application.css" media="screen" rel="stylesheet" />
-
#
-
# The example above generates "http://assets1.example.com" and
-
# "http://assets2.example.com". This option is useful for example if
-
# you need fewer/more than four hosts, custom host names, etc.
-
#
-
# As you see the proc takes a +source+ parameter. That's a string with the
-
# absolute path of the asset, for example "/assets/rails.png".
-
#
-
# ActionController::Base.asset_host = Proc.new { |source|
-
# if source.ends_with?('.css')
-
# "http://stylesheets.example.com"
-
# else
-
# "http://assets.example.com"
-
# end
-
# }
-
# image_tag("rails.png")
-
# # => <img alt="Rails" src="http://assets.example.com/assets/rails.png" />
-
# stylesheet_link_tag("application")
-
# # => <link href="http://stylesheets.example.com/assets/application.css" media="screen" rel="stylesheet" />
-
#
-
# Alternatively you may ask for a second parameter +request+. That one is
-
# particularly useful for serving assets from an SSL-protected page. The
-
# example proc below disables asset hosting for HTTPS connections, while
-
# still sending assets for plain HTTP requests from asset hosts. If you don't
-
# have SSL certificates for each of the asset hosts this technique allows you
-
# to avoid warnings in the client about mixed media.
-
#
-
# config.action_controller.asset_host = Proc.new { |source, request|
-
# if request.ssl?
-
# "#{request.protocol}#{request.host_with_port}"
-
# else
-
# "#{request.protocol}assets.example.com"
-
# end
-
# }
-
#
-
# You can also implement a custom asset host object that responds to +call+
-
# and takes either one or two parameters just like the proc.
-
#
-
# config.action_controller.asset_host = AssetHostingWithMinimumSsl.new(
-
# "http://asset%d.example.com", "https://asset1.example.com"
-
# )
-
#
-
1
module AssetUrlHelper
-
1
URI_REGEXP = %r{^[-a-z]+://|^(?:cid|data):|^//}i
-
-
# Computes the path to asset in public directory. If :type
-
# options is set, a file extension will be appended and scoped
-
# to the corresponding public directory.
-
#
-
# All other asset *_path helpers delegate through this method.
-
#
-
# asset_path "application.js" # => /application.js
-
# asset_path "application", type: :javascript # => /javascripts/application.js
-
# asset_path "application", type: :stylesheet # => /stylesheets/application.css
-
# asset_path "http://www.example.com/js/xmlhr.js" # => http://www.example.com/js/xmlhr.js
-
1
def asset_path(source, options = {})
-
source = source.to_s
-
return "" unless source.present?
-
return source if source =~ URI_REGEXP
-
-
tail, source = source[/([\?#].+)$/], source.sub(/([\?#].+)$/, '')
-
-
if extname = compute_asset_extname(source, options)
-
source = "#{source}#{extname}"
-
end
-
-
if source[0] != ?/
-
source = compute_asset_path(source, options)
-
end
-
-
relative_url_root = defined?(config.relative_url_root) && config.relative_url_root
-
if relative_url_root
-
source = File.join(relative_url_root, source) unless source.starts_with?("#{relative_url_root}/")
-
end
-
-
if host = compute_asset_host(source, options)
-
source = File.join(host, source)
-
end
-
-
"#{source}#{tail}"
-
end
-
1
alias_method :path_to_asset, :asset_path # aliased to avoid conflicts with an asset_path named route
-
-
# Computes the full URL to an asset in the public directory. This
-
# will use +asset_path+ internally, so most of their behaviors
-
# will be the same.
-
1
def asset_url(source, options = {})
-
path_to_asset(source, options.merge(:protocol => :request))
-
end
-
1
alias_method :url_to_asset, :asset_url # aliased to avoid conflicts with an asset_url named route
-
-
1
ASSET_EXTENSIONS = {
-
javascript: '.js',
-
stylesheet: '.css'
-
}
-
-
# Compute extname to append to asset path. Returns nil if
-
# nothing should be added.
-
1
def compute_asset_extname(source, options = {})
-
return if options[:extname] == false
-
extname = options[:extname] || ASSET_EXTENSIONS[options[:type]]
-
extname if extname && File.extname(source) != extname
-
end
-
-
# Maps asset types to public directory.
-
1
ASSET_PUBLIC_DIRECTORIES = {
-
audio: '/audios',
-
font: '/fonts',
-
image: '/images',
-
javascript: '/javascripts',
-
stylesheet: '/stylesheets',
-
video: '/videos'
-
}
-
-
# Computes asset path to public directory. Plugins and
-
# extensions can override this method to point to custom assets
-
# or generate digested paths or query strings.
-
1
def compute_asset_path(source, options = {})
-
dir = ASSET_PUBLIC_DIRECTORIES[options[:type]] || ""
-
File.join(dir, source)
-
end
-
-
# Pick an asset host for this source. Returns +nil+ if no host is set,
-
# the host if no wildcard is set, the host interpolated with the
-
# numbers 0-3 if it contains <tt>%d</tt> (the number is the source hash mod 4),
-
# or the value returned from invoking call on an object responding to call
-
# (proc or otherwise).
-
1
def compute_asset_host(source = "", options = {})
-
request = self.request if respond_to?(:request)
-
host = config.asset_host if defined? config.asset_host
-
-
if host.respond_to?(:call)
-
arity = host.respond_to?(:arity) ? host.arity : host.method(:call).arity
-
args = [source]
-
args << request if request && (arity > 1 || arity < 0)
-
host = host.call(*args)
-
elsif host =~ /%d/
-
host = host % (Zlib.crc32(source) % 4)
-
end
-
-
host ||= request.base_url if request && options[:protocol] == :request
-
return unless host
-
-
if host =~ URI_REGEXP
-
host
-
else
-
protocol = options[:protocol] || config.default_asset_host_protocol || (request ? :request : :relative)
-
case protocol
-
when :relative
-
"//#{host}"
-
when :request
-
"#{request.protocol}#{host}"
-
else
-
"#{protocol}://#{host}"
-
end
-
end
-
end
-
-
# Computes the path to a javascript asset in the public javascripts directory.
-
# If the +source+ filename has no extension, .js will be appended (except for explicit URIs)
-
# Full paths from the document root will be passed through.
-
# Used internally by javascript_include_tag to build the script path.
-
#
-
# javascript_path "xmlhr" # => /javascripts/xmlhr.js
-
# javascript_path "dir/xmlhr.js" # => /javascripts/dir/xmlhr.js
-
# javascript_path "/dir/xmlhr" # => /dir/xmlhr.js
-
# javascript_path "http://www.example.com/js/xmlhr" # => http://www.example.com/js/xmlhr
-
# javascript_path "http://www.example.com/js/xmlhr.js" # => http://www.example.com/js/xmlhr.js
-
1
def javascript_path(source, options = {})
-
path_to_asset(source, {type: :javascript}.merge!(options))
-
end
-
1
alias_method :path_to_javascript, :javascript_path # aliased to avoid conflicts with a javascript_path named route
-
-
# Computes the full URL to a javascript asset in the public javascripts directory.
-
# This will use +javascript_path+ internally, so most of their behaviors will be the same.
-
1
def javascript_url(source, options = {})
-
url_to_asset(source, {type: :javascript}.merge!(options))
-
end
-
1
alias_method :url_to_javascript, :javascript_url # aliased to avoid conflicts with a javascript_url named route
-
-
# Computes the path to a stylesheet asset in the public stylesheets directory.
-
# If the +source+ filename has no extension, <tt>.css</tt> will be appended (except for explicit URIs).
-
# Full paths from the document root will be passed through.
-
# Used internally by +stylesheet_link_tag+ to build the stylesheet path.
-
#
-
# stylesheet_path "style" # => /stylesheets/style.css
-
# stylesheet_path "dir/style.css" # => /stylesheets/dir/style.css
-
# stylesheet_path "/dir/style.css" # => /dir/style.css
-
# stylesheet_path "http://www.example.com/css/style" # => http://www.example.com/css/style
-
# stylesheet_path "http://www.example.com/css/style.css" # => http://www.example.com/css/style.css
-
1
def stylesheet_path(source, options = {})
-
path_to_asset(source, {type: :stylesheet}.merge!(options))
-
end
-
1
alias_method :path_to_stylesheet, :stylesheet_path # aliased to avoid conflicts with a stylesheet_path named route
-
-
# Computes the full URL to a stylesheet asset in the public stylesheets directory.
-
# This will use +stylesheet_path+ internally, so most of their behaviors will be the same.
-
1
def stylesheet_url(source, options = {})
-
url_to_asset(source, {type: :stylesheet}.merge!(options))
-
end
-
1
alias_method :url_to_stylesheet, :stylesheet_url # aliased to avoid conflicts with a stylesheet_url named route
-
-
# Computes the path to an image asset.
-
# Full paths from the document root will be passed through.
-
# Used internally by +image_tag+ to build the image path:
-
#
-
# image_path("edit") # => "/assets/edit"
-
# image_path("edit.png") # => "/assets/edit.png"
-
# image_path("icons/edit.png") # => "/assets/icons/edit.png"
-
# image_path("/icons/edit.png") # => "/icons/edit.png"
-
# image_path("http://www.example.com/img/edit.png") # => "http://www.example.com/img/edit.png"
-
#
-
# If you have images as application resources this method may conflict with their named routes.
-
# The alias +path_to_image+ is provided to avoid that. Rails uses the alias internally, and
-
# plugin authors are encouraged to do so.
-
1
def image_path(source, options = {})
-
path_to_asset(source, {type: :image}.merge!(options))
-
end
-
1
alias_method :path_to_image, :image_path # aliased to avoid conflicts with an image_path named route
-
-
# Computes the full URL to an image asset.
-
# This will use +image_path+ internally, so most of their behaviors will be the same.
-
1
def image_url(source, options = {})
-
url_to_asset(source, {type: :image}.merge!(options))
-
end
-
1
alias_method :url_to_image, :image_url # aliased to avoid conflicts with an image_url named route
-
-
# Computes the path to a video asset in the public videos directory.
-
# Full paths from the document root will be passed through.
-
# Used internally by +video_tag+ to build the video path.
-
#
-
# video_path("hd") # => /videos/hd
-
# video_path("hd.avi") # => /videos/hd.avi
-
# video_path("trailers/hd.avi") # => /videos/trailers/hd.avi
-
# video_path("/trailers/hd.avi") # => /trailers/hd.avi
-
# video_path("http://www.example.com/vid/hd.avi") # => http://www.example.com/vid/hd.avi
-
1
def video_path(source, options = {})
-
path_to_asset(source, {type: :video}.merge!(options))
-
end
-
1
alias_method :path_to_video, :video_path # aliased to avoid conflicts with a video_path named route
-
-
# Computes the full URL to a video asset in the public videos directory.
-
# This will use +video_path+ internally, so most of their behaviors will be the same.
-
1
def video_url(source, options = {})
-
url_to_asset(source, {type: :video}.merge!(options))
-
end
-
1
alias_method :url_to_video, :video_url # aliased to avoid conflicts with an video_url named route
-
-
# Computes the path to an audio asset in the public audios directory.
-
# Full paths from the document root will be passed through.
-
# Used internally by +audio_tag+ to build the audio path.
-
#
-
# audio_path("horse") # => /audios/horse
-
# audio_path("horse.wav") # => /audios/horse.wav
-
# audio_path("sounds/horse.wav") # => /audios/sounds/horse.wav
-
# audio_path("/sounds/horse.wav") # => /sounds/horse.wav
-
# audio_path("http://www.example.com/sounds/horse.wav") # => http://www.example.com/sounds/horse.wav
-
1
def audio_path(source, options = {})
-
path_to_asset(source, {type: :audio}.merge!(options))
-
end
-
1
alias_method :path_to_audio, :audio_path # aliased to avoid conflicts with an audio_path named route
-
-
# Computes the full URL to an audio asset in the public audios directory.
-
# This will use +audio_path+ internally, so most of their behaviors will be the same.
-
1
def audio_url(source, options = {})
-
url_to_asset(source, {type: :audio}.merge!(options))
-
end
-
1
alias_method :url_to_audio, :audio_url # aliased to avoid conflicts with an audio_url named route
-
-
# Computes the path to a font asset.
-
# Full paths from the document root will be passed through.
-
#
-
# font_path("font") # => /assets/font
-
# font_path("font.ttf") # => /assets/font.ttf
-
# font_path("dir/font.ttf") # => /assets/dir/font.ttf
-
# font_path("/dir/font.ttf") # => /dir/font.ttf
-
# font_path("http://www.example.com/dir/font.ttf") # => http://www.example.com/dir/font.ttf
-
1
def font_path(source, options = {})
-
path_to_asset(source, {type: :font}.merge!(options))
-
end
-
1
alias_method :path_to_font, :font_path # aliased to avoid conflicts with an font_path named route
-
-
# Computes the full URL to a font asset.
-
# This will use +font_path+ internally, so most of their behaviors will be the same.
-
1
def font_url(source, options = {})
-
url_to_asset(source, {type: :font}.merge!(options))
-
end
-
1
alias_method :url_to_font, :font_url # aliased to avoid conflicts with an font_url named route
-
end
-
end
-
end
-
1
require 'set'
-
-
1
module ActionView
-
# = Action View Atom Feed Helpers
-
1
module Helpers
-
1
module AtomFeedHelper
-
# Adds easy defaults to writing Atom feeds with the Builder template engine (this does not work on ERB or any other
-
# template languages).
-
#
-
# Full usage example:
-
#
-
# config/routes.rb:
-
# Rails.application.routes.draw do
-
# resources :posts
-
# root to: "posts#index"
-
# end
-
#
-
# app/controllers/posts_controller.rb:
-
# class PostsController < ApplicationController::Base
-
# # GET /posts.html
-
# # GET /posts.atom
-
# def index
-
# @posts = Post.all
-
#
-
# respond_to do |format|
-
# format.html
-
# format.atom
-
# end
-
# end
-
# end
-
#
-
# app/views/posts/index.atom.builder:
-
# atom_feed do |feed|
-
# feed.title("My great blog!")
-
# feed.updated(@posts[0].created_at) if @posts.length > 0
-
#
-
# @posts.each do |post|
-
# feed.entry(post) do |entry|
-
# entry.title(post.title)
-
# entry.content(post.body, type: 'html')
-
#
-
# entry.author do |author|
-
# author.name("DHH")
-
# end
-
# end
-
# end
-
# end
-
#
-
# The options for atom_feed are:
-
#
-
# * <tt>:language</tt>: Defaults to "en-US".
-
# * <tt>:root_url</tt>: The HTML alternative that this feed is doubling for. Defaults to / on the current host.
-
# * <tt>:url</tt>: The URL for this feed. Defaults to the current URL.
-
# * <tt>:id</tt>: The id for this feed. Defaults to "tag:#{request.host},#{options[:schema_date]}:#{request.fullpath.split(".")[0]}"
-
# * <tt>:schema_date</tt>: The date at which the tag scheme for the feed was first used. A good default is the year you
-
# created the feed. See http://feedvalidator.org/docs/error/InvalidTAG.html for more information. If not specified,
-
# 2005 is used (as an "I don't care" value).
-
# * <tt>:instruct</tt>: Hash of XML processing instructions in the form {target => {attribute => value, }} or {target => [{attribute => value, }, ]}
-
#
-
# Other namespaces can be added to the root element:
-
#
-
# app/views/posts/index.atom.builder:
-
# atom_feed({'xmlns:app' => 'http://www.w3.org/2007/app',
-
# 'xmlns:openSearch' => 'http://a9.com/-/spec/opensearch/1.1/'}) do |feed|
-
# feed.title("My great blog!")
-
# feed.updated((@posts.first.created_at))
-
# feed.tag!('openSearch:totalResults', 10)
-
#
-
# @posts.each do |post|
-
# feed.entry(post) do |entry|
-
# entry.title(post.title)
-
# entry.content(post.body, type: 'html')
-
# entry.tag!('app:edited', Time.now)
-
#
-
# entry.author do |author|
-
# author.name("DHH")
-
# end
-
# end
-
# end
-
# end
-
#
-
# The Atom spec defines five elements (content rights title subtitle
-
# summary) which may directly contain xhtml content if type: 'xhtml'
-
# is specified as an attribute. If so, this helper will take care of
-
# the enclosing div and xhtml namespace declaration. Example usage:
-
#
-
# entry.summary type: 'xhtml' do |xhtml|
-
# xhtml.p pluralize(order.line_items.count, "line item")
-
# xhtml.p "Shipped to #{order.address}"
-
# xhtml.p "Paid by #{order.pay_type}"
-
# end
-
#
-
#
-
# <tt>atom_feed</tt> yields an +AtomFeedBuilder+ instance. Nested elements yield
-
# an +AtomBuilder+ instance.
-
1
def atom_feed(options = {}, &block)
-
if options[:schema_date]
-
options[:schema_date] = options[:schema_date].strftime("%Y-%m-%d") if options[:schema_date].respond_to?(:strftime)
-
else
-
options[:schema_date] = "2005" # The Atom spec copyright date
-
end
-
-
xml = options.delete(:xml) || eval("xml", block.binding)
-
xml.instruct!
-
if options[:instruct]
-
options[:instruct].each do |target,attrs|
-
if attrs.respond_to?(:keys)
-
xml.instruct!(target, attrs)
-
elsif attrs.respond_to?(:each)
-
attrs.each { |attr_group| xml.instruct!(target, attr_group) }
-
end
-
end
-
end
-
-
feed_opts = {"xml:lang" => options[:language] || "en-US", "xmlns" => 'http://www.w3.org/2005/Atom'}
-
feed_opts.merge!(options).reject!{|k,v| !k.to_s.match(/^xml/)}
-
-
xml.feed(feed_opts) do
-
xml.id(options[:id] || "tag:#{request.host},#{options[:schema_date]}:#{request.fullpath.split(".")[0]}")
-
xml.link(:rel => 'alternate', :type => 'text/html', :href => options[:root_url] || (request.protocol + request.host_with_port))
-
xml.link(:rel => 'self', :type => 'application/atom+xml', :href => options[:url] || request.url)
-
-
yield AtomFeedBuilder.new(xml, self, options)
-
end
-
end
-
-
1
class AtomBuilder #:nodoc:
-
1
XHTML_TAG_NAMES = %w(content rights title subtitle summary).to_set
-
-
1
def initialize(xml)
-
@xml = xml
-
end
-
-
1
private
-
# Delegate to xml builder, first wrapping the element in a xhtml
-
# namespaced div element if the method and arguments indicate
-
# that an xhtml_block? is desired.
-
1
def method_missing(method, *arguments, &block)
-
if xhtml_block?(method, arguments)
-
@xml.__send__(method, *arguments) do
-
@xml.div(:xmlns => 'http://www.w3.org/1999/xhtml') do |xhtml|
-
block.call(xhtml)
-
end
-
end
-
else
-
@xml.__send__(method, *arguments, &block)
-
end
-
end
-
-
# True if the method name matches one of the five elements defined
-
# in the Atom spec as potentially containing XHTML content and
-
# if type: 'xhtml' is, in fact, specified.
-
1
def xhtml_block?(method, arguments)
-
if XHTML_TAG_NAMES.include?(method.to_s)
-
last = arguments.last
-
last.is_a?(Hash) && last[:type].to_s == 'xhtml'
-
end
-
end
-
end
-
-
1
class AtomFeedBuilder < AtomBuilder #:nodoc:
-
1
def initialize(xml, view, feed_options = {})
-
@xml, @view, @feed_options = xml, view, feed_options
-
end
-
-
# Accepts a Date or Time object and inserts it in the proper format. If nil is passed, current time in UTC is used.
-
1
def updated(date_or_time = nil)
-
@xml.updated((date_or_time || Time.now.utc).xmlschema)
-
end
-
-
# Creates an entry tag for a specific record and prefills the id using class and id.
-
#
-
# Options:
-
#
-
# * <tt>:published</tt>: Time first published. Defaults to the created_at attribute on the record if one such exists.
-
# * <tt>:updated</tt>: Time of update. Defaults to the updated_at attribute on the record if one such exists.
-
# * <tt>:url</tt>: The URL for this entry. Defaults to the polymorphic_url for the record.
-
# * <tt>:id</tt>: The ID for this entry. Defaults to "tag:#{@view.request.host},#{@feed_options[:schema_date]}:#{record.class}/#{record.id}"
-
# * <tt>:type</tt>: The TYPE for this entry. Defaults to "text/html".
-
1
def entry(record, options = {})
-
@xml.entry do
-
@xml.id(options[:id] || "tag:#{@view.request.host},#{@feed_options[:schema_date]}:#{record.class}/#{record.id}")
-
-
if options[:published] || (record.respond_to?(:created_at) && record.created_at)
-
@xml.published((options[:published] || record.created_at).xmlschema)
-
end
-
-
if options[:updated] || (record.respond_to?(:updated_at) && record.updated_at)
-
@xml.updated((options[:updated] || record.updated_at).xmlschema)
-
end
-
-
type = options.fetch(:type, 'text/html')
-
-
@xml.link(:rel => 'alternate', :type => type, :href => options[:url] || @view.polymorphic_url(record))
-
-
yield AtomBuilder.new(@xml)
-
end
-
end
-
end
-
-
end
-
end
-
end
-
1
module ActionView
-
# = Action View Cache Helper
-
1
module Helpers
-
1
module CacheHelper
-
# This helper exposes a method for caching fragments of a view
-
# rather than an entire action or page. This technique is useful
-
# caching pieces like menus, lists of new topics, static HTML
-
# fragments, and so on. This method takes a block that contains
-
# the content you wish to cache.
-
#
-
# The best way to use this is by doing key-based cache expiration
-
# on top of a cache store like Memcached that'll automatically
-
# kick out old entries. For more on key-based expiration, see:
-
# http://37signals.com/svn/posts/3113-how-key-based-cache-expiration-works
-
#
-
# When using this method, you list the cache dependency as the name of the cache, like so:
-
#
-
# <% cache project do %>
-
# <b>All the topics on this project</b>
-
# <%= render project.topics %>
-
# <% end %>
-
#
-
# This approach will assume that when a new topic is added, you'll touch
-
# the project. The cache key generated from this call will be something like:
-
#
-
# views/projects/123-20120806214154/7a1156131a6928cb0026877f8b749ac9
-
# ^class ^id ^updated_at ^template tree digest
-
#
-
# The cache is thus automatically bumped whenever the project updated_at is touched.
-
#
-
# If your template cache depends on multiple sources (try to avoid this to keep things simple),
-
# you can name all these dependencies as part of an array:
-
#
-
# <% cache [ project, current_user ] do %>
-
# <b>All the topics on this project</b>
-
# <%= render project.topics %>
-
# <% end %>
-
#
-
# This will include both records as part of the cache key and updating either of them will
-
# expire the cache.
-
#
-
# ==== Template digest
-
#
-
# The template digest that's added to the cache key is computed by taking an md5 of the
-
# contents of the entire template file. This ensures that your caches will automatically
-
# expire when you change the template file.
-
#
-
# Note that the md5 is taken of the entire template file, not just what's within the
-
# cache do/end call. So it's possible that changing something outside of that call will
-
# still expire the cache.
-
#
-
# Additionally, the digestor will automatically look through your template file for
-
# explicit and implicit dependencies, and include those as part of the digest.
-
#
-
# The digestor can be bypassed by passing skip_digest: true as an option to the cache call:
-
#
-
# <% cache project, skip_digest: true do %>
-
# <b>All the topics on this project</b>
-
# <%= render project.topics %>
-
# <% end %>
-
#
-
# ==== Implicit dependencies
-
#
-
# Most template dependencies can be derived from calls to render in the template itself.
-
# Here are some examples of render calls that Cache Digests knows how to decode:
-
#
-
# render partial: "comments/comment", collection: commentable.comments
-
# render "comments/comments"
-
# render 'comments/comments'
-
# render('comments/comments')
-
#
-
# render "header" => render("comments/header")
-
#
-
# render(@topic) => render("topics/topic")
-
# render(topics) => render("topics/topic")
-
# render(message.topics) => render("topics/topic")
-
#
-
# It's not possible to derive all render calls like that, though. Here are a few examples of things that can't be derived:
-
#
-
# render group_of_attachments
-
# render @project.documents.where(published: true).order('created_at')
-
#
-
# You will have to rewrite those to the explicit form:
-
#
-
# render partial: 'attachments/attachment', collection: group_of_attachments
-
# render partial: 'documents/document', collection: @project.documents.where(published: true).order('created_at')
-
#
-
# === Explicit dependencies
-
#
-
# Some times you'll have template dependencies that can't be derived at all. This is typically
-
# the case when you have template rendering that happens in helpers. Here's an example:
-
#
-
# <%= render_sortable_todolists @project.todolists %>
-
#
-
# You'll need to use a special comment format to call those out:
-
#
-
# <%# Template Dependency: todolists/todolist %>
-
# <%= render_sortable_todolists @project.todolists %>
-
#
-
# The pattern used to match these is /# Template Dependency: ([^ ]+)/, so it's important that you type it out just so.
-
# You can only declare one template dependency per line.
-
#
-
# === External dependencies
-
#
-
# If you use a helper method, for example, inside of a cached block and you then update that helper,
-
# you'll have to bump the cache as well. It doesn't really matter how you do it, but the md5 of the template file
-
# must change. One recommendation is to simply be explicit in a comment, like:
-
#
-
# <%# Helper Dependency Updated: May 6, 2012 at 6pm %>
-
# <%= some_helper_method(person) %>
-
#
-
# Now all you'll have to do is change that timestamp when the helper method changes.
-
1
def cache(name = {}, options = nil, &block)
-
if controller.perform_caching
-
safe_concat(fragment_for(cache_fragment_name(name, options), options, &block))
-
else
-
yield
-
end
-
-
nil
-
end
-
-
# Cache fragments of a view if +condition+ is true
-
#
-
# <%= cache_if admin?, project do %>
-
# <b>All the topics on this project</b>
-
# <%= render project.topics %>
-
# <% end %>
-
1
def cache_if(condition, name = {}, options = nil, &block)
-
if condition
-
cache(name, options, &block)
-
else
-
yield
-
end
-
-
nil
-
end
-
-
# Cache fragments of a view unless +condition+ is true
-
#
-
# <%= cache_unless admin?, project do %>
-
# <b>All the topics on this project</b>
-
# <%= render project.topics %>
-
# <% end %>
-
1
def cache_unless(condition, name = {}, options = nil, &block)
-
cache_if !condition, name, options, &block
-
end
-
-
# This helper returns the name of a cache key for a given fragment cache
-
# call. By supplying skip_digest: true to cache, the digestion of cache
-
# fragments can be manually bypassed. This is useful when cache fragments
-
# cannot be manually expired unless you know the exact key which is the
-
# case when using memcached.
-
1
def cache_fragment_name(name = {}, options = nil)
-
skip_digest = options && options[:skip_digest]
-
-
if skip_digest
-
name
-
else
-
fragment_name_with_digest(name)
-
end
-
end
-
-
1
private
-
-
1
def fragment_name_with_digest(name) #:nodoc:
-
if @virtual_path
-
names = Array(name.is_a?(Hash) ? controller.url_for(name).split("://").last : name)
-
digest = Digestor.digest name: @virtual_path, finder: lookup_context, dependencies: view_cache_dependencies
-
-
[ *names, digest ]
-
else
-
name
-
end
-
end
-
-
# TODO: Create an object that has caching read/write on it
-
1
def fragment_for(name = {}, options = nil, &block) #:nodoc:
-
read_fragment_for(name, options) || write_fragment_for(name, options, &block)
-
end
-
-
1
def read_fragment_for(name, options) #:nodoc:
-
controller.read_fragment(name, options)
-
end
-
-
1
def write_fragment_for(name, options) #:nodoc:
-
# VIEW TODO: Make #capture usable outside of ERB
-
# This dance is needed because Builder can't use capture
-
pos = output_buffer.length
-
yield
-
output_safe = output_buffer.html_safe?
-
fragment = output_buffer.slice!(pos..-1)
-
if output_safe
-
self.output_buffer = output_buffer.class.new(output_buffer)
-
end
-
controller.write_fragment(name, fragment, options)
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/string/output_safety'
-
-
1
module ActionView
-
# = Action View Capture Helper
-
1
module Helpers
-
# CaptureHelper exposes methods to let you extract generated markup which
-
# can be used in other parts of a template or layout file.
-
#
-
# It provides a method to capture blocks into variables through capture and
-
# a way to capture a block of markup for use in a layout through content_for.
-
1
module CaptureHelper
-
# The capture method allows you to extract part of a template into a
-
# variable. You can then use this variable anywhere in your templates or layout.
-
#
-
# The capture method can be used in ERB templates...
-
#
-
# <% @greeting = capture do %>
-
# Welcome to my shiny new web page! The date and time is
-
# <%= Time.now %>
-
# <% end %>
-
#
-
# ...and Builder (RXML) templates.
-
#
-
# @timestamp = capture do
-
# "The current timestamp is #{Time.now}."
-
# end
-
#
-
# You can then use that variable anywhere else. For example:
-
#
-
# <html>
-
# <head><title><%= @greeting %></title></head>
-
# <body>
-
# <b><%= @greeting %></b>
-
# </body></html>
-
#
-
1
def capture(*args)
-
value = nil
-
buffer = with_output_buffer { value = yield(*args) }
-
if string = buffer.presence || value and string.is_a?(String)
-
ERB::Util.html_escape string
-
end
-
end
-
-
# Calling content_for stores a block of markup in an identifier for later use.
-
# In order to access this stored content in other templates, helper modules
-
# or the layout, you would pass the identifier as an argument to <tt>content_for</tt>.
-
#
-
# Note: <tt>yield</tt> can still be used to retrieve the stored content, but calling
-
# <tt>yield</tt> doesn't work in helper modules, while <tt>content_for</tt> does.
-
#
-
# <% content_for :not_authorized do %>
-
# alert('You are not authorized to do that!')
-
# <% end %>
-
#
-
# You can then use <tt>content_for :not_authorized</tt> anywhere in your templates.
-
#
-
# <%= content_for :not_authorized if current_user.nil? %>
-
#
-
# This is equivalent to:
-
#
-
# <%= yield :not_authorized if current_user.nil? %>
-
#
-
# <tt>content_for</tt>, however, can also be used in helper modules.
-
#
-
# module StorageHelper
-
# def stored_content
-
# content_for(:storage) || "Your storage is empty"
-
# end
-
# end
-
#
-
# This helper works just like normal helpers.
-
#
-
# <%= stored_content %>
-
#
-
# You can also use the <tt>yield</tt> syntax alongside an existing call to
-
# <tt>yield</tt> in a layout. For example:
-
#
-
# <%# This is the layout %>
-
# <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
-
# <head>
-
# <title>My Website</title>
-
# <%= yield :script %>
-
# </head>
-
# <body>
-
# <%= yield %>
-
# </body>
-
# </html>
-
#
-
# And now, we'll create a view that has a <tt>content_for</tt> call that
-
# creates the <tt>script</tt> identifier.
-
#
-
# <%# This is our view %>
-
# Please login!
-
#
-
# <% content_for :script do %>
-
# <script>alert('You are not authorized to view this page!')</script>
-
# <% end %>
-
#
-
# Then, in another view, you could to do something like this:
-
#
-
# <%= link_to 'Logout', action: 'logout', remote: true %>
-
#
-
# <% content_for :script do %>
-
# <%= javascript_include_tag :defaults %>
-
# <% end %>
-
#
-
# That will place +script+ tags for your default set of JavaScript files on the page;
-
# this technique is useful if you'll only be using these scripts in a few views.
-
#
-
# Note that content_for concatenates (default) the blocks it is given for a particular
-
# identifier in order. For example:
-
#
-
# <% content_for :navigation do %>
-
# <li><%= link_to 'Home', action: 'index' %></li>
-
# <% end %>
-
#
-
# And in other place:
-
#
-
# <% content_for :navigation do %>
-
# <li><%= link_to 'Login', action: 'login' %></li>
-
# <% end %>
-
#
-
# Then, in another template or layout, this code would render both links in order:
-
#
-
# <ul><%= content_for :navigation %></ul>
-
#
-
# If the flush parameter is true content_for replaces the blocks it is given. For example:
-
#
-
# <% content_for :navigation do %>
-
# <li><%= link_to 'Home', action: 'index' %></li>
-
# <% end %>
-
#
-
# <%# Add some other content, or use a different template: %>
-
#
-
# <% content_for :navigation, flush: true do %>
-
# <li><%= link_to 'Login', action: 'login' %></li>
-
# <% end %>
-
#
-
# Then, in another template or layout, this code would render only the last link:
-
#
-
# <ul><%= content_for :navigation %></ul>
-
#
-
# Lastly, simple content can be passed as a parameter:
-
#
-
# <% content_for :script, javascript_include_tag(:defaults) %>
-
#
-
# WARNING: content_for is ignored in caches. So you shouldn't use it for elements that will be fragment cached.
-
1
def content_for(name, content = nil, options = {}, &block)
-
if content || block_given?
-
if block_given?
-
options = content if content
-
content = capture(&block)
-
end
-
if content
-
options[:flush] ? @view_flow.set(name, content) : @view_flow.append(name, content)
-
end
-
nil
-
else
-
@view_flow.get(name).presence
-
end
-
end
-
-
# The same as +content_for+ but when used with streaming flushes
-
# straight back to the layout. In other words, if you want to
-
# concatenate several times to the same buffer when rendering a given
-
# template, you should use +content_for+, if not, use +provide+ to tell
-
# the layout to stop looking for more contents.
-
1
def provide(name, content = nil, &block)
-
content = capture(&block) if block_given?
-
result = @view_flow.append!(name, content) if content
-
result unless content
-
end
-
-
# content_for? checks whether any content has been captured yet using `content_for`.
-
# Useful to render parts of your layout differently based on what is in your views.
-
#
-
# <%# This is the layout %>
-
# <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
-
# <head>
-
# <title>My Website</title>
-
# <%= yield :script %>
-
# </head>
-
# <body class="<%= content_for?(:right_col) ? 'two-column' : 'one-column' %>">
-
# <%= yield %>
-
# <%= yield :right_col %>
-
# </body>
-
# </html>
-
1
def content_for?(name)
-
@view_flow.get(name).present?
-
end
-
-
# Use an alternate output buffer for the duration of the block.
-
# Defaults to a new empty string.
-
1
def with_output_buffer(buf = nil) #:nodoc:
-
unless buf
-
buf = ActionView::OutputBuffer.new
-
buf.force_encoding(output_buffer.encoding) if output_buffer
-
end
-
self.output_buffer, old_buffer = buf, output_buffer
-
yield
-
output_buffer
-
ensure
-
self.output_buffer = old_buffer
-
end
-
-
# Add the output buffer to the response body and start a new one.
-
1
def flush_output_buffer #:nodoc:
-
if output_buffer && !output_buffer.empty?
-
response.stream.write output_buffer
-
self.output_buffer = output_buffer.respond_to?(:clone_empty) ? output_buffer.clone_empty : output_buffer[0, 0]
-
nil
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/module/attr_internal'
-
-
1
module ActionView
-
1
module Helpers
-
# This module keeps all methods and behavior in ActionView
-
# that simply delegates to the controller.
-
1
module ControllerHelper #:nodoc:
-
1
attr_internal :controller, :request
-
-
1
delegate :request_forgery_protection_token, :params, :session, :cookies, :response, :headers,
-
:flash, :action_name, :controller_name, :controller_path, :to => :controller
-
-
1
def assign_controller(controller)
-
2
if @_controller = controller
-
2
@_request = controller.request if controller.respond_to?(:request)
-
2
@_config = controller.config.inheritable_copy if controller.respond_to?(:config)
-
end
-
end
-
-
1
def logger
-
controller.logger if controller.respond_to?(:logger)
-
end
-
end
-
end
-
end
-
1
module ActionView
-
# = Action View CSRF Helper
-
1
module Helpers
-
1
module CsrfHelper
-
# Returns meta tags "csrf-param" and "csrf-token" with the name of the cross-site
-
# request forgery protection parameter and token, respectively.
-
#
-
# <head>
-
# <%= csrf_meta_tags %>
-
# </head>
-
#
-
# These are used to generate the dynamic forms that implement non-remote links with
-
# <tt>:method</tt>.
-
#
-
# You don't need to use these tags for regular forms as they generate their own hidden fields.
-
#
-
# For AJAX requests other than GETs, extract the "csrf-token" from the meta-tag and send as the
-
# "X-CSRF-Token" HTTP header. If you are using jQuery with jquery-rails this happens automatically.
-
#
-
1
def csrf_meta_tags
-
if protect_against_forgery?
-
[
-
tag('meta', :name => 'csrf-param', :content => request_forgery_protection_token),
-
tag('meta', :name => 'csrf-token', :content => form_authenticity_token)
-
].join("\n").html_safe
-
end
-
end
-
-
# For backwards compatibility.
-
1
alias csrf_meta_tag csrf_meta_tags
-
end
-
end
-
end
-
1
require 'date'
-
1
require 'action_view/helpers/tag_helper'
-
1
require 'active_support/core_ext/array/extract_options'
-
1
require 'active_support/core_ext/date/conversions'
-
1
require 'active_support/core_ext/hash/slice'
-
1
require 'active_support/core_ext/object/with_options'
-
-
1
module ActionView
-
1
module Helpers
-
# = Action View Date Helpers
-
#
-
# The Date Helper primarily creates select/option tags for different kinds of dates and times or date and time
-
# elements. All of the select-type methods share a number of common options that are as follows:
-
#
-
# * <tt>:prefix</tt> - overwrites the default prefix of "date" used for the select names. So specifying "birthday"
-
# would give \birthday[month] instead of \date[month] if passed to the <tt>select_month</tt> method.
-
# * <tt>:include_blank</tt> - set to true if it should be possible to set an empty date.
-
# * <tt>:discard_type</tt> - set to true if you want to discard the type part of the select name. If set to true,
-
# the <tt>select_month</tt> method would use simply "date" (which can be overwritten using <tt>:prefix</tt>) instead
-
# of \date[month].
-
1
module DateHelper
-
# Reports the approximate distance in time between two Time, Date or DateTime objects or integers as seconds.
-
# Pass <tt>include_seconds: true</tt> if you want more detailed approximations when distance < 1 min, 29 secs.
-
# Distances are reported based on the following table:
-
#
-
# 0 <-> 29 secs # => less than a minute
-
# 30 secs <-> 1 min, 29 secs # => 1 minute
-
# 1 min, 30 secs <-> 44 mins, 29 secs # => [2..44] minutes
-
# 44 mins, 30 secs <-> 89 mins, 29 secs # => about 1 hour
-
# 89 mins, 30 secs <-> 23 hrs, 59 mins, 29 secs # => about [2..24] hours
-
# 23 hrs, 59 mins, 30 secs <-> 41 hrs, 59 mins, 29 secs # => 1 day
-
# 41 hrs, 59 mins, 30 secs <-> 29 days, 23 hrs, 59 mins, 29 secs # => [2..29] days
-
# 29 days, 23 hrs, 59 mins, 30 secs <-> 44 days, 23 hrs, 59 mins, 29 secs # => about 1 month
-
# 44 days, 23 hrs, 59 mins, 30 secs <-> 59 days, 23 hrs, 59 mins, 29 secs # => about 2 months
-
# 59 days, 23 hrs, 59 mins, 30 secs <-> 1 yr minus 1 sec # => [2..12] months
-
# 1 yr <-> 1 yr, 3 months # => about 1 year
-
# 1 yr, 3 months <-> 1 yr, 9 months # => over 1 year
-
# 1 yr, 9 months <-> 2 yr minus 1 sec # => almost 2 years
-
# 2 yrs <-> max time or date # => (same rules as 1 yr)
-
#
-
# With <tt>include_seconds: true</tt> and the difference < 1 minute 29 seconds:
-
# 0-4 secs # => less than 5 seconds
-
# 5-9 secs # => less than 10 seconds
-
# 10-19 secs # => less than 20 seconds
-
# 20-39 secs # => half a minute
-
# 40-59 secs # => less than a minute
-
# 60-89 secs # => 1 minute
-
#
-
# from_time = Time.now
-
# distance_of_time_in_words(from_time, from_time + 50.minutes) # => about 1 hour
-
# distance_of_time_in_words(from_time, 50.minutes.from_now) # => about 1 hour
-
# distance_of_time_in_words(from_time, from_time + 15.seconds) # => less than a minute
-
# distance_of_time_in_words(from_time, from_time + 15.seconds, include_seconds: true) # => less than 20 seconds
-
# distance_of_time_in_words(from_time, 3.years.from_now) # => about 3 years
-
# distance_of_time_in_words(from_time, from_time + 60.hours) # => 3 days
-
# distance_of_time_in_words(from_time, from_time + 45.seconds, include_seconds: true) # => less than a minute
-
# distance_of_time_in_words(from_time, from_time - 45.seconds, include_seconds: true) # => less than a minute
-
# distance_of_time_in_words(from_time, 76.seconds.from_now) # => 1 minute
-
# distance_of_time_in_words(from_time, from_time + 1.year + 3.days) # => about 1 year
-
# distance_of_time_in_words(from_time, from_time + 3.years + 6.months) # => over 3 years
-
# distance_of_time_in_words(from_time, from_time + 4.years + 9.days + 30.minutes + 5.seconds) # => about 4 years
-
#
-
# to_time = Time.now + 6.years + 19.days
-
# distance_of_time_in_words(from_time, to_time, include_seconds: true) # => about 6 years
-
# distance_of_time_in_words(to_time, from_time, include_seconds: true) # => about 6 years
-
# distance_of_time_in_words(Time.now, Time.now) # => less than a minute
-
1
def distance_of_time_in_words(from_time, to_time = 0, options = {})
-
options = {
-
scope: :'datetime.distance_in_words'
-
}.merge!(options)
-
-
from_time = from_time.to_time if from_time.respond_to?(:to_time)
-
to_time = to_time.to_time if to_time.respond_to?(:to_time)
-
from_time, to_time = to_time, from_time if from_time > to_time
-
distance_in_minutes = ((to_time - from_time)/60.0).round
-
distance_in_seconds = (to_time - from_time).round
-
-
I18n.with_options :locale => options[:locale], :scope => options[:scope] do |locale|
-
case distance_in_minutes
-
when 0..1
-
return distance_in_minutes == 0 ?
-
locale.t(:less_than_x_minutes, :count => 1) :
-
locale.t(:x_minutes, :count => distance_in_minutes) unless options[:include_seconds]
-
-
case distance_in_seconds
-
when 0..4 then locale.t :less_than_x_seconds, :count => 5
-
when 5..9 then locale.t :less_than_x_seconds, :count => 10
-
when 10..19 then locale.t :less_than_x_seconds, :count => 20
-
when 20..39 then locale.t :half_a_minute
-
when 40..59 then locale.t :less_than_x_minutes, :count => 1
-
else locale.t :x_minutes, :count => 1
-
end
-
-
when 2...45 then locale.t :x_minutes, :count => distance_in_minutes
-
when 45...90 then locale.t :about_x_hours, :count => 1
-
# 90 mins up to 24 hours
-
when 90...1440 then locale.t :about_x_hours, :count => (distance_in_minutes.to_f / 60.0).round
-
# 24 hours up to 42 hours
-
when 1440...2520 then locale.t :x_days, :count => 1
-
# 42 hours up to 30 days
-
when 2520...43200 then locale.t :x_days, :count => (distance_in_minutes.to_f / 1440.0).round
-
# 30 days up to 60 days
-
when 43200...86400 then locale.t :about_x_months, :count => (distance_in_minutes.to_f / 43200.0).round
-
# 60 days up to 365 days
-
when 86400...525600 then locale.t :x_months, :count => (distance_in_minutes.to_f / 43200.0).round
-
else
-
if from_time.acts_like?(:time) && to_time.acts_like?(:time)
-
fyear = from_time.year
-
fyear += 1 if from_time.month >= 3
-
tyear = to_time.year
-
tyear -= 1 if to_time.month < 3
-
leap_years = (fyear > tyear) ? 0 : (fyear..tyear).count{|x| Date.leap?(x)}
-
minute_offset_for_leap_year = leap_years * 1440
-
# Discount the leap year days when calculating year distance.
-
# e.g. if there are 20 leap year days between 2 dates having the same day
-
# and month then the based on 365 days calculation
-
# the distance in years will come out to over 80 years when in written
-
# English it would read better as about 80 years.
-
minutes_with_offset = distance_in_minutes - minute_offset_for_leap_year
-
else
-
minutes_with_offset = distance_in_minutes
-
end
-
remainder = (minutes_with_offset % 525600)
-
distance_in_years = (minutes_with_offset.div 525600)
-
if remainder < 131400
-
locale.t(:about_x_years, :count => distance_in_years)
-
elsif remainder < 394200
-
locale.t(:over_x_years, :count => distance_in_years)
-
else
-
locale.t(:almost_x_years, :count => distance_in_years + 1)
-
end
-
end
-
end
-
end
-
-
# Like <tt>distance_of_time_in_words</tt>, but where <tt>to_time</tt> is fixed to <tt>Time.now</tt>.
-
#
-
# time_ago_in_words(3.minutes.from_now) # => 3 minutes
-
# time_ago_in_words(3.minutes.ago) # => 3 minutes
-
# time_ago_in_words(Time.now - 15.hours) # => about 15 hours
-
# time_ago_in_words(Time.now) # => less than a minute
-
# time_ago_in_words(Time.now, include_seconds: true) # => less than 5 seconds
-
#
-
# from_time = Time.now - 3.days - 14.minutes - 25.seconds
-
# time_ago_in_words(from_time) # => 3 days
-
#
-
# from_time = (3.days + 14.minutes + 25.seconds).ago
-
# time_ago_in_words(from_time) # => 3 days
-
#
-
# Note that you cannot pass a <tt>Numeric</tt> value to <tt>time_ago_in_words</tt>.
-
#
-
1
def time_ago_in_words(from_time, include_seconds_or_options = {})
-
distance_of_time_in_words(from_time, Time.now, include_seconds_or_options)
-
end
-
-
1
alias_method :distance_of_time_in_words_to_now, :time_ago_in_words
-
-
# Returns a set of select tags (one for year, month, and day) pre-selected for accessing a specified date-based
-
# attribute (identified by +method+) on an object assigned to the template (identified by +object+).
-
#
-
# ==== Options
-
# * <tt>:use_month_numbers</tt> - Set to true if you want to use month numbers rather than month names (e.g.
-
# "2" instead of "February").
-
# * <tt>:use_two_digit_numbers</tt> - Set to true if you want to display two digit month and day numbers (e.g.
-
# "02" instead of "February" and "08" instead of "8").
-
# * <tt>:use_short_month</tt> - Set to true if you want to use abbreviated month names instead of full
-
# month names (e.g. "Feb" instead of "February").
-
# * <tt>:add_month_numbers</tt> - Set to true if you want to use both month numbers and month names (e.g.
-
# "2 - February" instead of "February").
-
# * <tt>:use_month_names</tt> - Set to an array with 12 month names if you want to customize month names.
-
# Note: You can also use Rails' i18n functionality for this.
-
# * <tt>:month_format_string</tt> - Set to a format string. The string gets passed keys +:number+ (integer)
-
# and +:name+ (string). A format string would be something like "%{name} (%<number>02d)" for example.
-
# See <tt>Kernel.sprintf</tt> for documentation on format sequences.
-
# * <tt>:date_separator</tt> - Specifies a string to separate the date fields. Default is "" (i.e. nothing).
-
# * <tt>:start_year</tt> - Set the start year for the year select. Default is <tt>Date.today.year - 5</tt>if
-
# you are creating new record. While editing existing record, <tt>:start_year</tt> defaults to
-
# the current selected year minus 5.
-
# * <tt>:end_year</tt> - Set the end year for the year select. Default is <tt>Date.today.year + 5</tt> if
-
# you are creating new record. While editing existing record, <tt>:end_year</tt> defaults to
-
# the current selected year plus 5.
-
# * <tt>:discard_day</tt> - Set to true if you don't want to show a day select. This includes the day
-
# as a hidden field instead of showing a select field. Also note that this implicitly sets the day to be the
-
# first of the given month in order to not create invalid dates like 31 February.
-
# * <tt>:discard_month</tt> - Set to true if you don't want to show a month select. This includes the month
-
# as a hidden field instead of showing a select field. Also note that this implicitly sets :discard_day to true.
-
# * <tt>:discard_year</tt> - Set to true if you don't want to show a year select. This includes the year
-
# as a hidden field instead of showing a select field.
-
# * <tt>:order</tt> - Set to an array containing <tt>:day</tt>, <tt>:month</tt> and <tt>:year</tt> to
-
# customize the order in which the select fields are shown. If you leave out any of the symbols, the respective
-
# select will not be shown (like when you set <tt>discard_xxx: true</tt>. Defaults to the order defined in
-
# the respective locale (e.g. [:year, :month, :day] in the en locale that ships with Rails).
-
# * <tt>:include_blank</tt> - Include a blank option in every select field so it's possible to set empty
-
# dates.
-
# * <tt>:default</tt> - Set a default date if the affected date isn't set or is nil.
-
# * <tt>:selected</tt> - Set a date that overrides the actual value.
-
# * <tt>:disabled</tt> - Set to true if you want show the select fields as disabled.
-
# * <tt>:prompt</tt> - Set to true (for a generic prompt), a prompt string or a hash of prompt strings
-
# for <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>, <tt>:hour</tt>, <tt>:minute</tt> and <tt>:second</tt>.
-
# Setting this option prepends a select option with a generic prompt (Day, Month, Year, Hour, Minute, Seconds)
-
# or the given prompt string.
-
# * <tt>:with_css_classes</tt> - Set to true if you want assign different styles for 'select' tags. This option
-
# automatically set classes 'year', 'month', 'day', 'hour', 'minute' and 'second' for your 'select' tags.
-
#
-
# If anything is passed in the +html_options+ hash it will be applied to every select tag in the set.
-
#
-
# NOTE: Discarded selects will default to 1. So if no month select is available, January will be assumed.
-
#
-
# # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute.
-
# date_select("article", "written_on")
-
#
-
# # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute,
-
# # with the year in the year drop down box starting at 1995.
-
# date_select("article", "written_on", start_year: 1995)
-
#
-
# # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute,
-
# # with the year in the year drop down box starting at 1995, numbers used for months instead of words,
-
# # and without a day select box.
-
# date_select("article", "written_on", start_year: 1995, use_month_numbers: true,
-
# discard_day: true, include_blank: true)
-
#
-
# # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute,
-
# # with two digit numbers used for months and days.
-
# date_select("article", "written_on", use_two_digit_numbers: true)
-
#
-
# # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute
-
# # with the fields ordered as day, month, year rather than month, day, year.
-
# date_select("article", "written_on", order: [:day, :month, :year])
-
#
-
# # Generates a date select that when POSTed is stored in the user variable, in the birthday attribute
-
# # lacking a year field.
-
# date_select("user", "birthday", order: [:month, :day])
-
#
-
# # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute
-
# # which is initially set to the date 3 days from the current date
-
# date_select("article", "written_on", default: 3.days.from_now)
-
#
-
# # Generates a date select that when POSTed is stored in the article variable, in the written_on attribute
-
# # which is set in the form with todays date, regardless of the value in the Active Record object.
-
# date_select("article", "written_on", selected: Date.today)
-
#
-
# # Generates a date select that when POSTed is stored in the credit_card variable, in the bill_due attribute
-
# # that will have a default day of 20.
-
# date_select("credit_card", "bill_due", default: { day: 20 })
-
#
-
# # Generates a date select with custom prompts.
-
# date_select("article", "written_on", prompt: { day: 'Select day', month: 'Select month', year: 'Select year' })
-
#
-
# The selects are prepared for multi-parameter assignment to an Active Record object.
-
#
-
# Note: If the day is not included as an option but the month is, the day will be set to the 1st to ensure that
-
# all month choices are valid.
-
1
def date_select(object_name, method, options = {}, html_options = {})
-
Tags::DateSelect.new(object_name, method, self, options, html_options).render
-
end
-
-
# Returns a set of select tags (one for hour, minute and optionally second) pre-selected for accessing a
-
# specified time-based attribute (identified by +method+) on an object assigned to the template (identified by
-
# +object+). You can include the seconds with <tt>:include_seconds</tt>. You can get hours in the AM/PM format
-
# with <tt>:ampm</tt> option.
-
#
-
# This method will also generate 3 input hidden tags, for the actual year, month and day unless the option
-
# <tt>:ignore_date</tt> is set to +true+. If you set the <tt>:ignore_date</tt> to +true+, you must have a
-
# +date_select+ on the same method within the form otherwise an exception will be raised.
-
#
-
# If anything is passed in the html_options hash it will be applied to every select tag in the set.
-
#
-
# # Creates a time select tag that, when POSTed, will be stored in the article variable in the sunrise attribute.
-
# time_select("article", "sunrise")
-
#
-
# # Creates a time select tag with a seconds field that, when POSTed, will be stored in the article variables in
-
# # the sunrise attribute.
-
# time_select("article", "start_time", include_seconds: true)
-
#
-
# # You can set the <tt>:minute_step</tt> to 15 which will give you: 00, 15, 30 and 45.
-
# time_select 'game', 'game_time', {minute_step: 15}
-
#
-
# # Creates a time select tag with a custom prompt. Use <tt>prompt: true</tt> for generic prompts.
-
# time_select("article", "written_on", prompt: {hour: 'Choose hour', minute: 'Choose minute', second: 'Choose seconds'})
-
# time_select("article", "written_on", prompt: {hour: true}) # generic prompt for hours
-
# time_select("article", "written_on", prompt: true) # generic prompts for all
-
#
-
# # You can set :ampm option to true which will show the hours as: 12 PM, 01 AM .. 11 PM.
-
# time_select 'game', 'game_time', {ampm: true}
-
#
-
# The selects are prepared for multi-parameter assignment to an Active Record object.
-
#
-
# Note: If the day is not included as an option but the month is, the day will be set to the 1st to ensure that
-
# all month choices are valid.
-
1
def time_select(object_name, method, options = {}, html_options = {})
-
Tags::TimeSelect.new(object_name, method, self, options, html_options).render
-
end
-
-
# Returns a set of select tags (one for year, month, day, hour, and minute) pre-selected for accessing a
-
# specified datetime-based attribute (identified by +method+) on an object assigned to the template (identified
-
# by +object+).
-
#
-
# If anything is passed in the html_options hash it will be applied to every select tag in the set.
-
#
-
# # Generates a datetime select that, when POSTed, will be stored in the article variable in the written_on
-
# # attribute.
-
# datetime_select("article", "written_on")
-
#
-
# # Generates a datetime select with a year select that starts at 1995 that, when POSTed, will be stored in the
-
# # article variable in the written_on attribute.
-
# datetime_select("article", "written_on", start_year: 1995)
-
#
-
# # Generates a datetime select with a default value of 3 days from the current time that, when POSTed, will
-
# # be stored in the trip variable in the departing attribute.
-
# datetime_select("trip", "departing", default: 3.days.from_now)
-
#
-
# # Generate a datetime select with hours in the AM/PM format
-
# datetime_select("article", "written_on", ampm: true)
-
#
-
# # Generates a datetime select that discards the type that, when POSTed, will be stored in the article variable
-
# # as the written_on attribute.
-
# datetime_select("article", "written_on", discard_type: true)
-
#
-
# # Generates a datetime select with a custom prompt. Use <tt>prompt: true</tt> for generic prompts.
-
# datetime_select("article", "written_on", prompt: {day: 'Choose day', month: 'Choose month', year: 'Choose year'})
-
# datetime_select("article", "written_on", prompt: {hour: true}) # generic prompt for hours
-
# datetime_select("article", "written_on", prompt: true) # generic prompts for all
-
#
-
# The selects are prepared for multi-parameter assignment to an Active Record object.
-
1
def datetime_select(object_name, method, options = {}, html_options = {})
-
Tags::DatetimeSelect.new(object_name, method, self, options, html_options).render
-
end
-
-
# Returns a set of html select-tags (one for year, month, day, hour, minute, and second) pre-selected with the
-
# +datetime+. It's also possible to explicitly set the order of the tags using the <tt>:order</tt> option with
-
# an array of symbols <tt>:year</tt>, <tt>:month</tt> and <tt>:day</tt> in the desired order. If you do not
-
# supply a Symbol, it will be appended onto the <tt>:order</tt> passed in. You can also add
-
# <tt>:date_separator</tt>, <tt>:datetime_separator</tt> and <tt>:time_separator</tt> keys to the +options+ to
-
# control visual display of the elements.
-
#
-
# If anything is passed in the html_options hash it will be applied to every select tag in the set.
-
#
-
# my_date_time = Time.now + 4.days
-
#
-
# # Generates a datetime select that defaults to the datetime in my_date_time (four days after today).
-
# select_datetime(my_date_time)
-
#
-
# # Generates a datetime select that defaults to today (no specified datetime)
-
# select_datetime()
-
#
-
# # Generates a datetime select that defaults to the datetime in my_date_time (four days after today)
-
# # with the fields ordered year, month, day rather than month, day, year.
-
# select_datetime(my_date_time, order: [:year, :month, :day])
-
#
-
# # Generates a datetime select that defaults to the datetime in my_date_time (four days after today)
-
# # with a '/' between each date field.
-
# select_datetime(my_date_time, date_separator: '/')
-
#
-
# # Generates a datetime select that defaults to the datetime in my_date_time (four days after today)
-
# # with a date fields separated by '/', time fields separated by '' and the date and time fields
-
# # separated by a comma (',').
-
# select_datetime(my_date_time, date_separator: '/', time_separator: '', datetime_separator: ',')
-
#
-
# # Generates a datetime select that discards the type of the field and defaults to the datetime in
-
# # my_date_time (four days after today)
-
# select_datetime(my_date_time, discard_type: true)
-
#
-
# # Generate a datetime field with hours in the AM/PM format
-
# select_datetime(my_date_time, ampm: true)
-
#
-
# # Generates a datetime select that defaults to the datetime in my_date_time (four days after today)
-
# # prefixed with 'payday' rather than 'date'
-
# select_datetime(my_date_time, prefix: 'payday')
-
#
-
# # Generates a datetime select with a custom prompt. Use <tt>prompt: true</tt> for generic prompts.
-
# select_datetime(my_date_time, prompt: {day: 'Choose day', month: 'Choose month', year: 'Choose year'})
-
# select_datetime(my_date_time, prompt: {hour: true}) # generic prompt for hours
-
# select_datetime(my_date_time, prompt: true) # generic prompts for all
-
1
def select_datetime(datetime = Time.current, options = {}, html_options = {})
-
DateTimeSelector.new(datetime, options, html_options).select_datetime
-
end
-
-
# Returns a set of html select-tags (one for year, month, and day) pre-selected with the +date+.
-
# It's possible to explicitly set the order of the tags using the <tt>:order</tt> option with an array of
-
# symbols <tt>:year</tt>, <tt>:month</tt> and <tt>:day</tt> in the desired order.
-
# If the array passed to the <tt>:order</tt> option does not contain all the three symbols, all tags will be hidden.
-
#
-
# If anything is passed in the html_options hash it will be applied to every select tag in the set.
-
#
-
# my_date = Time.now + 6.days
-
#
-
# # Generates a date select that defaults to the date in my_date (six days after today).
-
# select_date(my_date)
-
#
-
# # Generates a date select that defaults to today (no specified date).
-
# select_date()
-
#
-
# # Generates a date select that defaults to the date in my_date (six days after today)
-
# # with the fields ordered year, month, day rather than month, day, year.
-
# select_date(my_date, order: [:year, :month, :day])
-
#
-
# # Generates a date select that discards the type of the field and defaults to the date in
-
# # my_date (six days after today).
-
# select_date(my_date, discard_type: true)
-
#
-
# # Generates a date select that defaults to the date in my_date,
-
# # which has fields separated by '/'.
-
# select_date(my_date, date_separator: '/')
-
#
-
# # Generates a date select that defaults to the datetime in my_date (six days after today)
-
# # prefixed with 'payday' rather than 'date'.
-
# select_date(my_date, prefix: 'payday')
-
#
-
# # Generates a date select with a custom prompt. Use <tt>prompt: true</tt> for generic prompts.
-
# select_date(my_date, prompt: {day: 'Choose day', month: 'Choose month', year: 'Choose year'})
-
# select_date(my_date, prompt: {hour: true}) # generic prompt for hours
-
# select_date(my_date, prompt: true) # generic prompts for all
-
1
def select_date(date = Date.current, options = {}, html_options = {})
-
DateTimeSelector.new(date, options, html_options).select_date
-
end
-
-
# Returns a set of html select-tags (one for hour and minute).
-
# You can set <tt>:time_separator</tt> key to format the output, and
-
# the <tt>:include_seconds</tt> option to include an input for seconds.
-
#
-
# If anything is passed in the html_options hash it will be applied to every select tag in the set.
-
#
-
# my_time = Time.now + 5.days + 7.hours + 3.minutes + 14.seconds
-
#
-
# # Generates a time select that defaults to the time in my_time.
-
# select_time(my_time)
-
#
-
# # Generates a time select that defaults to the current time (no specified time).
-
# select_time()
-
#
-
# # Generates a time select that defaults to the time in my_time,
-
# # which has fields separated by ':'.
-
# select_time(my_time, time_separator: ':')
-
#
-
# # Generates a time select that defaults to the time in my_time,
-
# # that also includes an input for seconds.
-
# select_time(my_time, include_seconds: true)
-
#
-
# # Generates a time select that defaults to the time in my_time, that has fields
-
# # separated by ':' and includes an input for seconds.
-
# select_time(my_time, time_separator: ':', include_seconds: true)
-
#
-
# # Generate a time select field with hours in the AM/PM format
-
# select_time(my_time, ampm: true)
-
#
-
# # Generates a time select field with hours that range from 2 to 14
-
# select_time(my_time, start_hour: 2, end_hour: 14)
-
#
-
# # Generates a time select with a custom prompt. Use <tt>:prompt</tt> to true for generic prompts.
-
# select_time(my_time, prompt: {day: 'Choose day', month: 'Choose month', year: 'Choose year'})
-
# select_time(my_time, prompt: {hour: true}) # generic prompt for hours
-
# select_time(my_time, prompt: true) # generic prompts for all
-
1
def select_time(datetime = Time.current, options = {}, html_options = {})
-
DateTimeSelector.new(datetime, options, html_options).select_time
-
end
-
-
# Returns a select tag with options for each of the seconds 0 through 59 with the current second selected.
-
# The <tt>datetime</tt> can be either a +Time+ or +DateTime+ object or an integer.
-
# Override the field name using the <tt>:field_name</tt> option, 'second' by default.
-
#
-
# my_time = Time.now + 16.minutes
-
#
-
# # Generates a select field for seconds that defaults to the seconds for the time in my_time.
-
# select_second(my_time)
-
#
-
# # Generates a select field for seconds that defaults to the number given.
-
# select_second(33)
-
#
-
# # Generates a select field for seconds that defaults to the seconds for the time in my_time
-
# # that is named 'interval' rather than 'second'.
-
# select_second(my_time, field_name: 'interval')
-
#
-
# # Generates a select field for seconds with a custom prompt. Use <tt>prompt: true</tt> for a
-
# # generic prompt.
-
# select_second(14, prompt: 'Choose seconds')
-
1
def select_second(datetime, options = {}, html_options = {})
-
DateTimeSelector.new(datetime, options, html_options).select_second
-
end
-
-
# Returns a select tag with options for each of the minutes 0 through 59 with the current minute selected.
-
# Also can return a select tag with options by <tt>minute_step</tt> from 0 through 59 with the 00 minute
-
# selected. The <tt>datetime</tt> can be either a +Time+ or +DateTime+ object or an integer.
-
# Override the field name using the <tt>:field_name</tt> option, 'minute' by default.
-
#
-
# my_time = Time.now + 6.hours
-
#
-
# # Generates a select field for minutes that defaults to the minutes for the time in my_time.
-
# select_minute(my_time)
-
#
-
# # Generates a select field for minutes that defaults to the number given.
-
# select_minute(14)
-
#
-
# # Generates a select field for minutes that defaults to the minutes for the time in my_time
-
# # that is named 'moment' rather than 'minute'.
-
# select_minute(my_time, field_name: 'moment')
-
#
-
# # Generates a select field for minutes with a custom prompt. Use <tt>prompt: true</tt> for a
-
# # generic prompt.
-
# select_minute(14, prompt: 'Choose minutes')
-
1
def select_minute(datetime, options = {}, html_options = {})
-
DateTimeSelector.new(datetime, options, html_options).select_minute
-
end
-
-
# Returns a select tag with options for each of the hours 0 through 23 with the current hour selected.
-
# The <tt>datetime</tt> can be either a +Time+ or +DateTime+ object or an integer.
-
# Override the field name using the <tt>:field_name</tt> option, 'hour' by default.
-
#
-
# my_time = Time.now + 6.hours
-
#
-
# # Generates a select field for hours that defaults to the hour for the time in my_time.
-
# select_hour(my_time)
-
#
-
# # Generates a select field for hours that defaults to the number given.
-
# select_hour(13)
-
#
-
# # Generates a select field for hours that defaults to the hour for the time in my_time
-
# # that is named 'stride' rather than 'hour'.
-
# select_hour(my_time, field_name: 'stride')
-
#
-
# # Generates a select field for hours with a custom prompt. Use <tt>prompt: true</tt> for a
-
# # generic prompt.
-
# select_hour(13, prompt: 'Choose hour')
-
#
-
# # Generate a select field for hours in the AM/PM format
-
# select_hour(my_time, ampm: true)
-
#
-
# # Generates a select field that includes options for hours from 2 to 14.
-
# select_hour(my_time, start_hour: 2, end_hour: 14)
-
1
def select_hour(datetime, options = {}, html_options = {})
-
DateTimeSelector.new(datetime, options, html_options).select_hour
-
end
-
-
# Returns a select tag with options for each of the days 1 through 31 with the current day selected.
-
# The <tt>date</tt> can also be substituted for a day number.
-
# If you want to display days with a leading zero set the <tt>:use_two_digit_numbers</tt> key in +options+ to true.
-
# Override the field name using the <tt>:field_name</tt> option, 'day' by default.
-
#
-
# my_date = Time.now + 2.days
-
#
-
# # Generates a select field for days that defaults to the day for the date in my_date.
-
# select_day(my_date)
-
#
-
# # Generates a select field for days that defaults to the number given.
-
# select_day(5)
-
#
-
# # Generates a select field for days that defaults to the number given, but displays it with two digits.
-
# select_day(5, use_two_digit_numbers: true)
-
#
-
# # Generates a select field for days that defaults to the day for the date in my_date
-
# # that is named 'due' rather than 'day'.
-
# select_day(my_date, field_name: 'due')
-
#
-
# # Generates a select field for days with a custom prompt. Use <tt>prompt: true</tt> for a
-
# # generic prompt.
-
# select_day(5, prompt: 'Choose day')
-
1
def select_day(date, options = {}, html_options = {})
-
DateTimeSelector.new(date, options, html_options).select_day
-
end
-
-
# Returns a select tag with options for each of the months January through December with the current month
-
# selected. The month names are presented as keys (what's shown to the user) and the month numbers (1-12) are
-
# used as values (what's submitted to the server). It's also possible to use month numbers for the presentation
-
# instead of names -- set the <tt>:use_month_numbers</tt> key in +options+ to true for this to happen. If you
-
# want both numbers and names, set the <tt>:add_month_numbers</tt> key in +options+ to true. If you would prefer
-
# to show month names as abbreviations, set the <tt>:use_short_month</tt> key in +options+ to true. If you want
-
# to use your own month names, set the <tt>:use_month_names</tt> key in +options+ to an array of 12 month names.
-
# If you want to display months with a leading zero set the <tt>:use_two_digit_numbers</tt> key in +options+ to true.
-
# Override the field name using the <tt>:field_name</tt> option, 'month' by default.
-
#
-
# # Generates a select field for months that defaults to the current month that
-
# # will use keys like "January", "March".
-
# select_month(Date.today)
-
#
-
# # Generates a select field for months that defaults to the current month that
-
# # is named "start" rather than "month".
-
# select_month(Date.today, field_name: 'start')
-
#
-
# # Generates a select field for months that defaults to the current month that
-
# # will use keys like "1", "3".
-
# select_month(Date.today, use_month_numbers: true)
-
#
-
# # Generates a select field for months that defaults to the current month that
-
# # will use keys like "1 - January", "3 - March".
-
# select_month(Date.today, add_month_numbers: true)
-
#
-
# # Generates a select field for months that defaults to the current month that
-
# # will use keys like "Jan", "Mar".
-
# select_month(Date.today, use_short_month: true)
-
#
-
# # Generates a select field for months that defaults to the current month that
-
# # will use keys like "Januar", "Marts."
-
# select_month(Date.today, use_month_names: %w(Januar Februar Marts ...))
-
#
-
# # Generates a select field for months that defaults to the current month that
-
# # will use keys with two digit numbers like "01", "03".
-
# select_month(Date.today, use_two_digit_numbers: true)
-
#
-
# # Generates a select field for months with a custom prompt. Use <tt>prompt: true</tt> for a
-
# # generic prompt.
-
# select_month(14, prompt: 'Choose month')
-
1
def select_month(date, options = {}, html_options = {})
-
DateTimeSelector.new(date, options, html_options).select_month
-
end
-
-
# Returns a select tag with options for each of the five years on each side of the current, which is selected.
-
# The five year radius can be changed using the <tt>:start_year</tt> and <tt>:end_year</tt> keys in the
-
# +options+. Both ascending and descending year lists are supported by making <tt>:start_year</tt> less than or
-
# greater than <tt>:end_year</tt>. The <tt>date</tt> can also be substituted for a year given as a number.
-
# Override the field name using the <tt>:field_name</tt> option, 'year' by default.
-
#
-
# # Generates a select field for years that defaults to the current year that
-
# # has ascending year values.
-
# select_year(Date.today, start_year: 1992, end_year: 2007)
-
#
-
# # Generates a select field for years that defaults to the current year that
-
# # is named 'birth' rather than 'year'.
-
# select_year(Date.today, field_name: 'birth')
-
#
-
# # Generates a select field for years that defaults to the current year that
-
# # has descending year values.
-
# select_year(Date.today, start_year: 2005, end_year: 1900)
-
#
-
# # Generates a select field for years that defaults to the year 2006 that
-
# # has ascending year values.
-
# select_year(2006, start_year: 2000, end_year: 2010)
-
#
-
# # Generates a select field for years with a custom prompt. Use <tt>prompt: true</tt> for a
-
# # generic prompt.
-
# select_year(14, prompt: 'Choose year')
-
1
def select_year(date, options = {}, html_options = {})
-
DateTimeSelector.new(date, options, html_options).select_year
-
end
-
-
# Returns an html time tag for the given date or time.
-
#
-
# time_tag Date.today # =>
-
# <time datetime="2010-11-04">November 04, 2010</time>
-
# time_tag Time.now # =>
-
# <time datetime="2010-11-04T17:55:45+01:00">November 04, 2010 17:55</time>
-
# time_tag Date.yesterday, 'Yesterday' # =>
-
# <time datetime="2010-11-03">Yesterday</time>
-
# time_tag Date.today, pubdate: true # =>
-
# <time datetime="2010-11-04" pubdate="pubdate">November 04, 2010</time>
-
# time_tag Date.today, datetime: Date.today.strftime('%G-W%V') # =>
-
# <time datetime="2010-W44">November 04, 2010</time>
-
#
-
# <%= time_tag Time.now do %>
-
# <span>Right now</span>
-
# <% end %>
-
# # => <time datetime="2010-11-04T17:55:45+01:00"><span>Right now</span></time>
-
1
def time_tag(date_or_time, *args, &block)
-
options = args.extract_options!
-
format = options.delete(:format) || :long
-
content = args.first || I18n.l(date_or_time, :format => format)
-
datetime = date_or_time.acts_like?(:time) ? date_or_time.xmlschema : date_or_time.iso8601
-
-
content_tag(:time, content, options.reverse_merge(:datetime => datetime), &block)
-
end
-
end
-
-
1
class DateTimeSelector #:nodoc:
-
1
include ActionView::Helpers::TagHelper
-
-
1
DEFAULT_PREFIX = 'date'.freeze
-
1
POSITION = {
-
:year => 1, :month => 2, :day => 3, :hour => 4, :minute => 5, :second => 6
-
}.freeze
-
-
1
AMPM_TRANSLATION = Hash[
-
[[0, "12 AM"], [1, "01 AM"], [2, "02 AM"], [3, "03 AM"],
-
[4, "04 AM"], [5, "05 AM"], [6, "06 AM"], [7, "07 AM"],
-
[8, "08 AM"], [9, "09 AM"], [10, "10 AM"], [11, "11 AM"],
-
[12, "12 PM"], [13, "01 PM"], [14, "02 PM"], [15, "03 PM"],
-
[16, "04 PM"], [17, "05 PM"], [18, "06 PM"], [19, "07 PM"],
-
[20, "08 PM"], [21, "09 PM"], [22, "10 PM"], [23, "11 PM"]]
-
].freeze
-
-
1
def initialize(datetime, options = {}, html_options = {})
-
@options = options.dup
-
@html_options = html_options.dup
-
@datetime = datetime
-
@options[:datetime_separator] ||= ' — '
-
@options[:time_separator] ||= ' : '
-
end
-
-
1
def select_datetime
-
order = date_order.dup
-
order -= [:hour, :minute, :second]
-
@options[:discard_year] ||= true unless order.include?(:year)
-
@options[:discard_month] ||= true unless order.include?(:month)
-
@options[:discard_day] ||= true if @options[:discard_month] || !order.include?(:day)
-
@options[:discard_minute] ||= true if @options[:discard_hour]
-
@options[:discard_second] ||= true unless @options[:include_seconds] && !@options[:discard_minute]
-
-
set_day_if_discarded
-
-
if @options[:tag] && @options[:ignore_date]
-
select_time
-
else
-
[:day, :month, :year].each { |o| order.unshift(o) unless order.include?(o) }
-
order += [:hour, :minute, :second] unless @options[:discard_hour]
-
-
build_selects_from_types(order)
-
end
-
end
-
-
1
def select_date
-
order = date_order.dup
-
-
@options[:discard_hour] = true
-
@options[:discard_minute] = true
-
@options[:discard_second] = true
-
-
@options[:discard_year] ||= true unless order.include?(:year)
-
@options[:discard_month] ||= true unless order.include?(:month)
-
@options[:discard_day] ||= true if @options[:discard_month] || !order.include?(:day)
-
-
set_day_if_discarded
-
-
[:day, :month, :year].each { |o| order.unshift(o) unless order.include?(o) }
-
-
build_selects_from_types(order)
-
end
-
-
1
def select_time
-
order = []
-
-
@options[:discard_month] = true
-
@options[:discard_year] = true
-
@options[:discard_day] = true
-
@options[:discard_second] ||= true unless @options[:include_seconds]
-
-
order += [:year, :month, :day] unless @options[:ignore_date]
-
-
order += [:hour, :minute]
-
order << :second if @options[:include_seconds]
-
-
build_selects_from_types(order)
-
end
-
-
1
def select_second
-
if @options[:use_hidden] || @options[:discard_second]
-
build_hidden(:second, sec) if @options[:include_seconds]
-
else
-
build_options_and_select(:second, sec)
-
end
-
end
-
-
1
def select_minute
-
if @options[:use_hidden] || @options[:discard_minute]
-
build_hidden(:minute, min)
-
else
-
build_options_and_select(:minute, min, :step => @options[:minute_step])
-
end
-
end
-
-
1
def select_hour
-
if @options[:use_hidden] || @options[:discard_hour]
-
build_hidden(:hour, hour)
-
else
-
options = {}
-
options[:ampm] = @options[:ampm] || false
-
options[:start] = @options[:start_hour] || 0
-
options[:end] = @options[:end_hour] || 23
-
build_options_and_select(:hour, hour, options)
-
end
-
end
-
-
1
def select_day
-
if @options[:use_hidden] || @options[:discard_day]
-
build_hidden(:day, day || 1)
-
else
-
build_options_and_select(:day, day, :start => 1, :end => 31, :leading_zeros => false, :use_two_digit_numbers => @options[:use_two_digit_numbers])
-
end
-
end
-
-
1
def select_month
-
if @options[:use_hidden] || @options[:discard_month]
-
build_hidden(:month, month || 1)
-
else
-
month_options = []
-
1.upto(12) do |month_number|
-
options = { :value => month_number }
-
options[:selected] = "selected" if month == month_number
-
month_options << content_tag(:option, month_name(month_number), options) + "\n"
-
end
-
build_select(:month, month_options.join)
-
end
-
end
-
-
1
def select_year
-
if !@datetime || @datetime == 0
-
val = '1'
-
middle_year = Date.today.year
-
else
-
val = middle_year = year
-
end
-
-
if @options[:use_hidden] || @options[:discard_year]
-
build_hidden(:year, val)
-
else
-
options = {}
-
options[:start] = @options[:start_year] || middle_year - 5
-
options[:end] = @options[:end_year] || middle_year + 5
-
options[:step] = options[:start] < options[:end] ? 1 : -1
-
options[:leading_zeros] = false
-
options[:max_years_allowed] = @options[:max_years_allowed] || 1000
-
-
if (options[:end] - options[:start]).abs > options[:max_years_allowed]
-
raise ArgumentError, "There are too many years options to be built. Are you sure you haven't mistyped something? You can provide the :max_years_allowed parameter."
-
end
-
-
build_options_and_select(:year, val, options)
-
end
-
end
-
-
1
private
-
1
%w( sec min hour day month year ).each do |method|
-
6
define_method(method) do
-
@datetime.kind_of?(Numeric) ? @datetime : @datetime.send(method) if @datetime
-
end
-
end
-
-
# If the day is hidden, the day should be set to the 1st so all month and year choices are
-
# valid. Otherwise, February 31st or February 29th, 2011 can be selected, which are invalid.
-
1
def set_day_if_discarded
-
if @datetime && @options[:discard_day]
-
@datetime = @datetime.change(:day => 1)
-
end
-
end
-
-
# Returns translated month names, but also ensures that a custom month
-
# name array has a leading nil element.
-
1
def month_names
-
@month_names ||= begin
-
month_names = @options[:use_month_names] || translated_month_names
-
month_names.unshift(nil) if month_names.size < 13
-
month_names
-
end
-
end
-
-
# Returns translated month names.
-
# => [nil, "January", "February", "March",
-
# "April", "May", "June", "July",
-
# "August", "September", "October",
-
# "November", "December"]
-
#
-
# If <tt>:use_short_month</tt> option is set
-
# => [nil, "Jan", "Feb", "Mar", "Apr", "May", "Jun",
-
# "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
-
1
def translated_month_names
-
key = @options[:use_short_month] ? :'date.abbr_month_names' : :'date.month_names'
-
I18n.translate(key, :locale => @options[:locale])
-
end
-
-
# Looks up month names by number (1-based):
-
#
-
# month_name(1) # => "January"
-
#
-
# If the <tt>:use_month_numbers</tt> option is passed:
-
#
-
# month_name(1) # => 1
-
#
-
# If the <tt>:use_two_month_numbers</tt> option is passed:
-
#
-
# month_name(1) # => '01'
-
#
-
# If the <tt>:add_month_numbers</tt> option is passed:
-
#
-
# month_name(1) # => "1 - January"
-
#
-
# If the <tt>:month_format_string</tt> option is passed:
-
#
-
# month_name(1) # => "January (01)"
-
#
-
# depending on the format string.
-
1
def month_name(number)
-
if @options[:use_month_numbers]
-
number
-
elsif @options[:use_two_digit_numbers]
-
'%02d' % number
-
elsif @options[:add_month_numbers]
-
"#{number} - #{month_names[number]}"
-
elsif format_string = @options[:month_format_string]
-
format_string % {number: number, name: month_names[number]}
-
else
-
month_names[number]
-
end
-
end
-
-
1
def date_order
-
@date_order ||= @options[:order] || translated_date_order
-
end
-
-
1
def translated_date_order
-
date_order = I18n.translate(:'date.order', :locale => @options[:locale], :default => [])
-
date_order = date_order.map { |element| element.to_sym }
-
-
forbidden_elements = date_order - [:year, :month, :day]
-
if forbidden_elements.any?
-
raise StandardError,
-
"#{@options[:locale]}.date.order only accepts :year, :month and :day"
-
end
-
-
date_order
-
end
-
-
# Build full select tag from date type and options.
-
1
def build_options_and_select(type, selected, options = {})
-
build_select(type, build_options(selected, options))
-
end
-
-
# Build select option html from date value and options.
-
# build_options(15, start: 1, end: 31)
-
# => "<option value="1">1</option>
-
# <option value="2">2</option>
-
# <option value="3">3</option>..."
-
#
-
# If <tt>use_two_digit_numbers: true</tt> option is passed
-
# build_options(15, start: 1, end: 31, use_two_digit_numbers: true)
-
# => "<option value="1">01</option>
-
# <option value="2">02</option>
-
# <option value="3">03</option>..."
-
#
-
# If <tt>:step</tt> options is passed
-
# build_options(15, start: 1, end: 31, step: 2)
-
# => "<option value="1">1</option>
-
# <option value="3">3</option>
-
# <option value="5">5</option>..."
-
1
def build_options(selected, options = {})
-
options = {
-
leading_zeros: true, ampm: false, use_two_digit_numbers: false
-
}.merge!(options)
-
-
start = options.delete(:start) || 0
-
stop = options.delete(:end) || 59
-
step = options.delete(:step) || 1
-
leading_zeros = options.delete(:leading_zeros)
-
-
select_options = []
-
start.step(stop, step) do |i|
-
value = leading_zeros ? sprintf("%02d", i) : i
-
tag_options = { :value => value }
-
tag_options[:selected] = "selected" if selected == i
-
text = options[:use_two_digit_numbers] ? sprintf("%02d", i) : value
-
text = options[:ampm] ? AMPM_TRANSLATION[i] : text
-
select_options << content_tag(:option, text, tag_options)
-
end
-
-
(select_options.join("\n") + "\n").html_safe
-
end
-
-
# Builds select tag from date type and html select options.
-
# build_select(:month, "<option value="1">January</option>...")
-
# => "<select id="post_written_on_2i" name="post[written_on(2i)]">
-
# <option value="1">January</option>...
-
# </select>"
-
1
def build_select(type, select_options_as_html)
-
select_options = {
-
:id => input_id_from_type(type),
-
:name => input_name_from_type(type)
-
}.merge!(@html_options)
-
select_options[:disabled] = 'disabled' if @options[:disabled]
-
select_options[:class] = [select_options[:class], type].compact.join(' ') if @options[:with_css_classes]
-
-
select_html = "\n"
-
select_html << content_tag(:option, '', :value => '') + "\n" if @options[:include_blank]
-
select_html << prompt_option_tag(type, @options[:prompt]) + "\n" if @options[:prompt]
-
select_html << select_options_as_html
-
-
(content_tag(:select, select_html.html_safe, select_options) + "\n").html_safe
-
end
-
-
# Builds a prompt option tag with supplied options or from default options.
-
# prompt_option_tag(:month, prompt: 'Select month')
-
# => "<option value="">Select month</option>"
-
1
def prompt_option_tag(type, options)
-
prompt = case options
-
when Hash
-
default_options = {:year => false, :month => false, :day => false, :hour => false, :minute => false, :second => false}
-
default_options.merge!(options)[type.to_sym]
-
when String
-
options
-
else
-
I18n.translate(:"datetime.prompts.#{type}", :locale => @options[:locale])
-
end
-
-
prompt ? content_tag(:option, prompt, :value => '') : ''
-
end
-
-
# Builds hidden input tag for date part and value.
-
# build_hidden(:year, 2008)
-
# => "<input id="post_written_on_1i" name="post[written_on(1i)]" type="hidden" value="2008" />"
-
1
def build_hidden(type, value)
-
select_options = {
-
:type => "hidden",
-
:id => input_id_from_type(type),
-
:name => input_name_from_type(type),
-
:value => value
-
}.merge!(@html_options.slice(:disabled))
-
select_options[:disabled] = 'disabled' if @options[:disabled]
-
-
tag(:input, select_options) + "\n".html_safe
-
end
-
-
# Returns the name attribute for the input tag.
-
# => post[written_on(1i)]
-
1
def input_name_from_type(type)
-
prefix = @options[:prefix] || ActionView::Helpers::DateTimeSelector::DEFAULT_PREFIX
-
prefix += "[#{@options[:index]}]" if @options.has_key?(:index)
-
-
field_name = @options[:field_name] || type
-
if @options[:include_position]
-
field_name += "(#{ActionView::Helpers::DateTimeSelector::POSITION[type]}i)"
-
end
-
-
@options[:discard_type] ? prefix : "#{prefix}[#{field_name}]"
-
end
-
-
# Returns the id attribute for the input tag.
-
# => "post_written_on_1i"
-
1
def input_id_from_type(type)
-
id = input_name_from_type(type).gsub(/([\[\(])|(\]\[)/, '_').gsub(/[\]\)]/, '')
-
id = @options[:namespace] + '_' + id if @options[:namespace]
-
-
id
-
end
-
-
# Given an ordering of datetime components, create the selection HTML
-
# and join them with their appropriate separators.
-
1
def build_selects_from_types(order)
-
select = ''
-
first_visible = order.find { |type| !@options[:"discard_#{type}"] }
-
order.reverse.each do |type|
-
separator = separator(type) unless type == first_visible # don't add before first visible field
-
select.insert(0, separator.to_s + send("select_#{type}").to_s)
-
end
-
select.html_safe
-
end
-
-
# Returns the separator for a given datetime component.
-
1
def separator(type)
-
return "" if @options[:use_hidden]
-
-
case type
-
when :year, :month, :day
-
@options[:"discard_#{type}"] ? "" : @options[:date_separator]
-
when :hour
-
(@options[:discard_year] && @options[:discard_day]) ? "" : @options[:datetime_separator]
-
when :minute, :second
-
@options[:"discard_#{type}"] ? "" : @options[:time_separator]
-
end
-
end
-
end
-
-
1
class FormBuilder
-
# Wraps ActionView::Helpers::DateHelper#date_select for form builders:
-
#
-
# <%= form_for @person do |f| %>
-
# <%= f.date_select :birth_date %>
-
# <%= f.submit %>
-
# <% end %>
-
#
-
# Please refer to the documentation of the base helper for details.
-
1
def date_select(method, options = {}, html_options = {})
-
@template.date_select(@object_name, method, objectify_options(options), html_options)
-
end
-
-
# Wraps ActionView::Helpers::DateHelper#time_select for form builders:
-
#
-
# <%= form_for @race do |f| %>
-
# <%= f.time_select :average_lap %>
-
# <%= f.submit %>
-
# <% end %>
-
#
-
# Please refer to the documentation of the base helper for details.
-
1
def time_select(method, options = {}, html_options = {})
-
@template.time_select(@object_name, method, objectify_options(options), html_options)
-
end
-
-
# Wraps ActionView::Helpers::DateHelper#datetime_select for form builders:
-
#
-
# <%= form_for @person do |f| %>
-
# <%= f.datetime_select :last_request_at %>
-
# <%= f.submit %>
-
# <% end %>
-
#
-
# Please refer to the documentation of the base helper for details.
-
1
def datetime_select(method, options = {}, html_options = {})
-
@template.datetime_select(@object_name, method, objectify_options(options), html_options)
-
end
-
end
-
end
-
end
-
1
module ActionView
-
# = Action View Debug Helper
-
#
-
# Provides a set of methods for making it easier to debug Rails objects.
-
1
module Helpers
-
1
module DebugHelper
-
-
1
include TagHelper
-
-
# Returns a YAML representation of +object+ wrapped with <pre> and </pre>.
-
# If the object cannot be converted to YAML using +to_yaml+, +inspect+ will be called instead.
-
# Useful for inspecting an object at the time of rendering.
-
#
-
# @user = User.new({ username: 'testing', password: 'xyz', age: 42}) %>
-
# debug(@user)
-
# # =>
-
# <pre class='debug_dump'>--- !ruby/object:User
-
# attributes:
-
# updated_at:
-
# username: testing
-
#
-
# age: 42
-
# password: xyz
-
# created_at:
-
# attributes_cache: {}
-
#
-
# new_record: true
-
# </pre>
-
1
def debug(object)
-
Marshal::dump(object)
-
object = ERB::Util.html_escape(object.to_yaml).gsub(" ", " ").html_safe
-
content_tag(:pre, object, :class => "debug_dump")
-
rescue Exception # errors from Marshal or YAML
-
# Object couldn't be dumped, perhaps because of singleton methods -- this is the fallback
-
content_tag(:code, object.inspect, :class => "debug_dump")
-
end
-
end
-
end
-
end
-
1
require 'cgi'
-
1
require 'action_view/helpers/date_helper'
-
1
require 'action_view/helpers/tag_helper'
-
1
require 'action_view/helpers/form_tag_helper'
-
1
require 'action_view/helpers/active_model_helper'
-
1
require 'action_view/model_naming'
-
1
require 'active_support/core_ext/module/attribute_accessors'
-
1
require 'active_support/core_ext/hash/slice'
-
1
require 'active_support/core_ext/string/output_safety'
-
1
require 'active_support/core_ext/string/inflections'
-
-
1
module ActionView
-
# = Action View Form Helpers
-
1
module Helpers
-
# Form helpers are designed to make working with resources much easier
-
# compared to using vanilla HTML.
-
#
-
# Typically, a form designed to create or update a resource reflects the
-
# identity of the resource in several ways: (i) the url that the form is
-
# sent to (the form element's +action+ attribute) should result in a request
-
# being routed to the appropriate controller action (with the appropriate <tt>:id</tt>
-
# parameter in the case of an existing resource), (ii) input fields should
-
# be named in such a way that in the controller their values appear in the
-
# appropriate places within the +params+ hash, and (iii) for an existing record,
-
# when the form is initially displayed, input fields corresponding to attributes
-
# of the resource should show the current values of those attributes.
-
#
-
# In Rails, this is usually achieved by creating the form using +form_for+ and
-
# a number of related helper methods. +form_for+ generates an appropriate <tt>form</tt>
-
# tag and yields a form builder object that knows the model the form is about.
-
# Input fields are created by calling methods defined on the form builder, which
-
# means they are able to generate the appropriate names and default values
-
# corresponding to the model attributes, as well as convenient IDs, etc.
-
# Conventions in the generated field names allow controllers to receive form data
-
# nicely structured in +params+ with no effort on your side.
-
#
-
# For example, to create a new person you typically set up a new instance of
-
# +Person+ in the <tt>PeopleController#new</tt> action, <tt>@person</tt>, and
-
# in the view template pass that object to +form_for+:
-
#
-
# <%= form_for @person do |f| %>
-
# <%= f.label :first_name %>:
-
# <%= f.text_field :first_name %><br />
-
#
-
# <%= f.label :last_name %>:
-
# <%= f.text_field :last_name %><br />
-
#
-
# <%= f.submit %>
-
# <% end %>
-
#
-
# The HTML generated for this would be (modulus formatting):
-
#
-
# <form action="/people" class="new_person" id="new_person" method="post">
-
# <div style="display:none">
-
# <input name="authenticity_token" type="hidden" value="NrOp5bsjoLRuK8IW5+dQEYjKGUJDe7TQoZVvq95Wteg=" />
-
# </div>
-
# <label for="person_first_name">First name</label>:
-
# <input id="person_first_name" name="person[first_name]" type="text" /><br />
-
#
-
# <label for="person_last_name">Last name</label>:
-
# <input id="person_last_name" name="person[last_name]" type="text" /><br />
-
#
-
# <input name="commit" type="submit" value="Create Person" />
-
# </form>
-
#
-
# As you see, the HTML reflects knowledge about the resource in several spots,
-
# like the path the form should be submitted to, or the names of the input fields.
-
#
-
# In particular, thanks to the conventions followed in the generated field names, the
-
# controller gets a nested hash <tt>params[:person]</tt> with the person attributes
-
# set in the form. That hash is ready to be passed to <tt>Person.create</tt>:
-
#
-
# if @person = Person.create(params[:person])
-
# # success
-
# else
-
# # error handling
-
# end
-
#
-
# Interestingly, the exact same view code in the previous example can be used to edit
-
# a person. If <tt>@person</tt> is an existing record with name "John Smith" and ID 256,
-
# the code above as is would yield instead:
-
#
-
# <form action="/people/256" class="edit_person" id="edit_person_256" method="post">
-
# <div style="display:none">
-
# <input name="_method" type="hidden" value="patch" />
-
# <input name="authenticity_token" type="hidden" value="NrOp5bsjoLRuK8IW5+dQEYjKGUJDe7TQoZVvq95Wteg=" />
-
# </div>
-
# <label for="person_first_name">First name</label>:
-
# <input id="person_first_name" name="person[first_name]" type="text" value="John" /><br />
-
#
-
# <label for="person_last_name">Last name</label>:
-
# <input id="person_last_name" name="person[last_name]" type="text" value="Smith" /><br />
-
#
-
# <input name="commit" type="submit" value="Update Person" />
-
# </form>
-
#
-
# Note that the endpoint, default values, and submit button label are tailored for <tt>@person</tt>.
-
# That works that way because the involved helpers know whether the resource is a new record or not,
-
# and generate HTML accordingly.
-
#
-
# The controller would receive the form data again in <tt>params[:person]</tt>, ready to be
-
# passed to <tt>Person#update</tt>:
-
#
-
# if @person.update(params[:person])
-
# # success
-
# else
-
# # error handling
-
# end
-
#
-
# That's how you typically work with resources.
-
1
module FormHelper
-
1
extend ActiveSupport::Concern
-
-
1
include FormTagHelper
-
1
include UrlHelper
-
1
include ModelNaming
-
-
# Creates a form that allows the user to create or update the attributes
-
# of a specific model object.
-
#
-
# The method can be used in several slightly different ways, depending on
-
# how much you wish to rely on Rails to infer automatically from the model
-
# how the form should be constructed. For a generic model object, a form
-
# can be created by passing +form_for+ a string or symbol representing
-
# the object we are concerned with:
-
#
-
# <%= form_for :person do |f| %>
-
# First name: <%= f.text_field :first_name %><br />
-
# Last name : <%= f.text_field :last_name %><br />
-
# Biography : <%= f.text_area :biography %><br />
-
# Admin? : <%= f.check_box :admin %><br />
-
# <%= f.submit %>
-
# <% end %>
-
#
-
# The variable +f+ yielded to the block is a FormBuilder object that
-
# incorporates the knowledge about the model object represented by
-
# <tt>:person</tt> passed to +form_for+. Methods defined on the FormBuilder
-
# are used to generate fields bound to this model. Thus, for example,
-
#
-
# <%= f.text_field :first_name %>
-
#
-
# will get expanded to
-
#
-
# <%= text_field :person, :first_name %>
-
# which results in an html <tt><input></tt> tag whose +name+ attribute is
-
# <tt>person[first_name]</tt>. This means that when the form is submitted,
-
# the value entered by the user will be available in the controller as
-
# <tt>params[:person][:first_name]</tt>.
-
#
-
# For fields generated in this way using the FormBuilder,
-
# if <tt>:person</tt> also happens to be the name of an instance variable
-
# <tt>@person</tt>, the default value of the field shown when the form is
-
# initially displayed (e.g. in the situation where you are editing an
-
# existing record) will be the value of the corresponding attribute of
-
# <tt>@person</tt>.
-
#
-
# The rightmost argument to +form_for+ is an
-
# optional hash of options -
-
#
-
# * <tt>:url</tt> - The URL the form is to be submitted to. This may be
-
# represented in the same way as values passed to +url_for+ or +link_to+.
-
# So for example you may use a named route directly. When the model is
-
# represented by a string or symbol, as in the example above, if the
-
# <tt>:url</tt> option is not specified, by default the form will be
-
# sent back to the current url (We will describe below an alternative
-
# resource-oriented usage of +form_for+ in which the URL does not need
-
# to be specified explicitly).
-
# * <tt>:namespace</tt> - A namespace for your form to ensure uniqueness of
-
# id attributes on form elements. The namespace attribute will be prefixed
-
# with underscore on the generated HTML id.
-
# * <tt>:html</tt> - Optional HTML attributes for the form tag.
-
#
-
# Also note that +form_for+ doesn't create an exclusive scope. It's still
-
# possible to use both the stand-alone FormHelper methods and methods
-
# from FormTagHelper. For example:
-
#
-
# <%= form_for :person do |f| %>
-
# First name: <%= f.text_field :first_name %>
-
# Last name : <%= f.text_field :last_name %>
-
# Biography : <%= text_area :person, :biography %>
-
# Admin? : <%= check_box_tag "person[admin]", "1", @person.company.admin? %>
-
# <%= f.submit %>
-
# <% end %>
-
#
-
# This also works for the methods in FormOptionHelper and DateHelper that
-
# are designed to work with an object as base, like
-
# FormOptionHelper#collection_select and DateHelper#datetime_select.
-
#
-
# === #form_for with a model object
-
#
-
# In the examples above, the object to be created or edited was
-
# represented by a symbol passed to +form_for+, and we noted that
-
# a string can also be used equivalently. It is also possible, however,
-
# to pass a model object itself to +form_for+. For example, if <tt>@post</tt>
-
# is an existing record you wish to edit, you can create the form using
-
#
-
# <%= form_for @post do |f| %>
-
# ...
-
# <% end %>
-
#
-
# This behaves in almost the same way as outlined previously, with a
-
# couple of small exceptions. First, the prefix used to name the input
-
# elements within the form (hence the key that denotes them in the +params+
-
# hash) is actually derived from the object's _class_, e.g. <tt>params[:post]</tt>
-
# if the object's class is +Post+. However, this can be overwritten using
-
# the <tt>:as</tt> option, e.g. -
-
#
-
# <%= form_for(@person, as: :client) do |f| %>
-
# ...
-
# <% end %>
-
#
-
# would result in <tt>params[:client]</tt>.
-
#
-
# Secondly, the field values shown when the form is initially displayed
-
# are taken from the attributes of the object passed to +form_for+,
-
# regardless of whether the object is an instance
-
# variable. So, for example, if we had a _local_ variable +post+
-
# representing an existing record,
-
#
-
# <%= form_for post do |f| %>
-
# ...
-
# <% end %>
-
#
-
# would produce a form with fields whose initial state reflect the current
-
# values of the attributes of +post+.
-
#
-
# === Resource-oriented style
-
#
-
# In the examples just shown, although not indicated explicitly, we still
-
# need to use the <tt>:url</tt> option in order to specify where the
-
# form is going to be sent. However, further simplification is possible
-
# if the record passed to +form_for+ is a _resource_, i.e. it corresponds
-
# to a set of RESTful routes, e.g. defined using the +resources+ method
-
# in <tt>config/routes.rb</tt>. In this case Rails will simply infer the
-
# appropriate URL from the record itself. For example,
-
#
-
# <%= form_for @post do |f| %>
-
# ...
-
# <% end %>
-
#
-
# is then equivalent to something like:
-
#
-
# <%= form_for @post, as: :post, url: post_path(@post), method: :patch, html: { class: "edit_post", id: "edit_post_45" } do |f| %>
-
# ...
-
# <% end %>
-
#
-
# And for a new record
-
#
-
# <%= form_for(Post.new) do |f| %>
-
# ...
-
# <% end %>
-
#
-
# is equivalent to something like:
-
#
-
# <%= form_for @post, as: :post, url: posts_path, html: { class: "new_post", id: "new_post" } do |f| %>
-
# ...
-
# <% end %>
-
#
-
# However you can still overwrite individual conventions, such as:
-
#
-
# <%= form_for(@post, url: super_posts_path) do |f| %>
-
# ...
-
# <% end %>
-
#
-
# You can also set the answer format, like this:
-
#
-
# <%= form_for(@post, format: :json) do |f| %>
-
# ...
-
# <% end %>
-
#
-
# For namespaced routes, like +admin_post_url+:
-
#
-
# <%= form_for([:admin, @post]) do |f| %>
-
# ...
-
# <% end %>
-
#
-
# If your resource has associations defined, for example, you want to add comments
-
# to the document given that the routes are set correctly:
-
#
-
# <%= form_for([@document, @comment]) do |f| %>
-
# ...
-
# <% end %>
-
#
-
# Where <tt>@document = Document.find(params[:id])</tt> and
-
# <tt>@comment = Comment.new</tt>.
-
#
-
# === Setting the method
-
#
-
# You can force the form to use the full array of HTTP verbs by setting
-
#
-
# method: (:get|:post|:patch|:put|:delete)
-
#
-
# in the options hash. If the verb is not GET or POST, which are natively
-
# supported by HTML forms, the form will be set to POST and a hidden input
-
# called _method will carry the intended verb for the server to interpret.
-
#
-
# === Unobtrusive JavaScript
-
#
-
# Specifying:
-
#
-
# remote: true
-
#
-
# in the options hash creates a form that will allow the unobtrusive JavaScript drivers to modify its
-
# behavior. The expected default behavior is an XMLHttpRequest in the background instead of the regular
-
# POST arrangement, but ultimately the behavior is the choice of the JavaScript driver implementor.
-
# Even though it's using JavaScript to serialize the form elements, the form submission will work just like
-
# a regular submission as viewed by the receiving side (all elements available in <tt>params</tt>).
-
#
-
# Example:
-
#
-
# <%= form_for(@post, remote: true) do |f| %>
-
# ...
-
# <% end %>
-
#
-
# The HTML generated for this would be:
-
#
-
# <form action='http://www.example.com' method='post' data-remote='true'>
-
# <div style='display:none'>
-
# <input name='_method' type='hidden' value='patch' />
-
# </div>
-
# ...
-
# </form>
-
#
-
# === Setting HTML options
-
#
-
# You can set data attributes directly by passing in a data hash, but all other HTML options must be wrapped in
-
# the HTML key. Example:
-
#
-
# <%= form_for(@post, data: { behavior: "autosave" }, html: { name: "go" }) do |f| %>
-
# ...
-
# <% end %>
-
#
-
# The HTML generated for this would be:
-
#
-
# <form action='http://www.example.com' method='post' data-behavior='autosave' name='go'>
-
# <div style='display:none'>
-
# <input name='_method' type='hidden' value='patch' />
-
# </div>
-
# ...
-
# </form>
-
#
-
# === Removing hidden model id's
-
#
-
# The form_for method automatically includes the model id as a hidden field in the form.
-
# This is used to maintain the correlation between the form data and its associated model.
-
# Some ORM systems do not use IDs on nested models so in this case you want to be able
-
# to disable the hidden id.
-
#
-
# In the following example the Post model has many Comments stored within it in a NoSQL database,
-
# thus there is no primary key for comments.
-
#
-
# Example:
-
#
-
# <%= form_for(@post) do |f| %>
-
# <%= f.fields_for(:comments, include_id: false) do |cf| %>
-
# ...
-
# <% end %>
-
# <% end %>
-
#
-
# === Customized form builders
-
#
-
# You can also build forms using a customized FormBuilder class. Subclass
-
# FormBuilder and override or define some more helpers, then use your
-
# custom builder. For example, let's say you made a helper to
-
# automatically add labels to form inputs.
-
#
-
# <%= form_for @person, url: { action: "create" }, builder: LabellingFormBuilder do |f| %>
-
# <%= f.text_field :first_name %>
-
# <%= f.text_field :last_name %>
-
# <%= f.text_area :biography %>
-
# <%= f.check_box :admin %>
-
# <%= f.submit %>
-
# <% end %>
-
#
-
# In this case, if you use this:
-
#
-
# <%= render f %>
-
#
-
# The rendered template is <tt>people/_labelling_form</tt> and the local
-
# variable referencing the form builder is called
-
# <tt>labelling_form</tt>.
-
#
-
# The custom FormBuilder class is automatically merged with the options
-
# of a nested fields_for call, unless it's explicitly set.
-
#
-
# In many cases you will want to wrap the above in another helper, so you
-
# could do something like the following:
-
#
-
# def labelled_form_for(record_or_name_or_array, *args, &block)
-
# options = args.extract_options!
-
# form_for(record_or_name_or_array, *(args << options.merge(builder: LabellingFormBuilder)), &block)
-
# end
-
#
-
# If you don't need to attach a form to a model instance, then check out
-
# FormTagHelper#form_tag.
-
#
-
# === Form to external resources
-
#
-
# When you build forms to external resources sometimes you need to set an authenticity token or just render a form
-
# without it, for example when you submit data to a payment gateway number and types of fields could be limited.
-
#
-
# To set an authenticity token you need to pass an <tt>:authenticity_token</tt> parameter
-
#
-
# <%= form_for @invoice, url: external_url, authenticity_token: 'external_token' do |f|
-
# ...
-
# <% end %>
-
#
-
# If you don't want to an authenticity token field be rendered at all just pass <tt>false</tt>:
-
#
-
# <%= form_for @invoice, url: external_url, authenticity_token: false do |f|
-
# ...
-
# <% end %>
-
1
def form_for(record, options = {}, &block)
-
raise ArgumentError, "Missing block" unless block_given?
-
html_options = options[:html] ||= {}
-
-
case record
-
when String, Symbol
-
object_name = record
-
object = nil
-
else
-
object = record.is_a?(Array) ? record.last : record
-
raise ArgumentError, "First argument in form cannot contain nil or be empty" unless object
-
object_name = options[:as] || model_name_from_record_or_class(object).param_key
-
apply_form_for_options!(record, object, options)
-
end
-
-
html_options[:data] = options.delete(:data) if options.has_key?(:data)
-
html_options[:remote] = options.delete(:remote) if options.has_key?(:remote)
-
html_options[:method] = options.delete(:method) if options.has_key?(:method)
-
html_options[:authenticity_token] = options.delete(:authenticity_token)
-
-
builder = instantiate_builder(object_name, object, options)
-
output = capture(builder, &block)
-
html_options[:multipart] ||= builder.multipart?
-
-
form_tag(options[:url] || {}, html_options) { output }
-
end
-
-
1
def apply_form_for_options!(record, object, options) #:nodoc:
-
object = convert_to_model(object)
-
-
as = options[:as]
-
namespace = options[:namespace]
-
action, method = object.respond_to?(:persisted?) && object.persisted? ? [:edit, :patch] : [:new, :post]
-
options[:html].reverse_merge!(
-
class: as ? "#{action}_#{as}" : dom_class(object, action),
-
id: (as ? [namespace, action, as] : [namespace, dom_id(object, action)]).compact.join("_").presence,
-
method: method
-
)
-
-
options[:url] ||= polymorphic_path(record, format: options.delete(:format))
-
end
-
1
private :apply_form_for_options!
-
-
# Creates a scope around a specific model object like form_for, but
-
# doesn't create the form tags themselves. This makes fields_for suitable
-
# for specifying additional model objects in the same form.
-
#
-
# Although the usage and purpose of +field_for+ is similar to +form_for+'s,
-
# its method signature is slightly different. Like +form_for+, it yields
-
# a FormBuilder object associated with a particular model object to a block,
-
# and within the block allows methods to be called on the builder to
-
# generate fields associated with the model object. Fields may reflect
-
# a model object in two ways - how they are named (hence how submitted
-
# values appear within the +params+ hash in the controller) and what
-
# default values are shown when the form the fields appear in is first
-
# displayed. In order for both of these features to be specified independently,
-
# both an object name (represented by either a symbol or string) and the
-
# object itself can be passed to the method separately -
-
#
-
# <%= form_for @person do |person_form| %>
-
# First name: <%= person_form.text_field :first_name %>
-
# Last name : <%= person_form.text_field :last_name %>
-
#
-
# <%= fields_for :permission, @person.permission do |permission_fields| %>
-
# Admin? : <%= permission_fields.check_box :admin %>
-
# <% end %>
-
#
-
# <%= f.submit %>
-
# <% end %>
-
#
-
# In this case, the checkbox field will be represented by an HTML +input+
-
# tag with the +name+ attribute <tt>permission[admin]</tt>, and the submitted
-
# value will appear in the controller as <tt>params[:permission][:admin]</tt>.
-
# If <tt>@person.permission</tt> is an existing record with an attribute
-
# +admin+, the initial state of the checkbox when first displayed will
-
# reflect the value of <tt>@person.permission.admin</tt>.
-
#
-
# Often this can be simplified by passing just the name of the model
-
# object to +fields_for+ -
-
#
-
# <%= fields_for :permission do |permission_fields| %>
-
# Admin?: <%= permission_fields.check_box :admin %>
-
# <% end %>
-
#
-
# ...in which case, if <tt>:permission</tt> also happens to be the name of an
-
# instance variable <tt>@permission</tt>, the initial state of the input
-
# field will reflect the value of that variable's attribute <tt>@permission.admin</tt>.
-
#
-
# Alternatively, you can pass just the model object itself (if the first
-
# argument isn't a string or symbol +fields_for+ will realize that the
-
# name has been omitted) -
-
#
-
# <%= fields_for @person.permission do |permission_fields| %>
-
# Admin?: <%= permission_fields.check_box :admin %>
-
# <% end %>
-
#
-
# and +fields_for+ will derive the required name of the field from the
-
# _class_ of the model object, e.g. if <tt>@person.permission</tt>, is
-
# of class +Permission+, the field will still be named <tt>permission[admin]</tt>.
-
#
-
# Note: This also works for the methods in FormOptionHelper and
-
# DateHelper that are designed to work with an object as base, like
-
# FormOptionHelper#collection_select and DateHelper#datetime_select.
-
#
-
# === Nested Attributes Examples
-
#
-
# When the object belonging to the current scope has a nested attribute
-
# writer for a certain attribute, fields_for will yield a new scope
-
# for that attribute. This allows you to create forms that set or change
-
# the attributes of a parent object and its associations in one go.
-
#
-
# Nested attribute writers are normal setter methods named after an
-
# association. The most common way of defining these writers is either
-
# with +accepts_nested_attributes_for+ in a model definition or by
-
# defining a method with the proper name. For example: the attribute
-
# writer for the association <tt>:address</tt> is called
-
# <tt>address_attributes=</tt>.
-
#
-
# Whether a one-to-one or one-to-many style form builder will be yielded
-
# depends on whether the normal reader method returns a _single_ object
-
# or an _array_ of objects.
-
#
-
# ==== One-to-one
-
#
-
# Consider a Person class which returns a _single_ Address from the
-
# <tt>address</tt> reader method and responds to the
-
# <tt>address_attributes=</tt> writer method:
-
#
-
# class Person
-
# def address
-
# @address
-
# end
-
#
-
# def address_attributes=(attributes)
-
# # Process the attributes hash
-
# end
-
# end
-
#
-
# This model can now be used with a nested fields_for, like so:
-
#
-
# <%= form_for @person do |person_form| %>
-
# ...
-
# <%= person_form.fields_for :address do |address_fields| %>
-
# Street : <%= address_fields.text_field :street %>
-
# Zip code: <%= address_fields.text_field :zip_code %>
-
# <% end %>
-
# ...
-
# <% end %>
-
#
-
# When address is already an association on a Person you can use
-
# +accepts_nested_attributes_for+ to define the writer method for you:
-
#
-
# class Person < ActiveRecord::Base
-
# has_one :address
-
# accepts_nested_attributes_for :address
-
# end
-
#
-
# If you want to destroy the associated model through the form, you have
-
# to enable it first using the <tt>:allow_destroy</tt> option for
-
# +accepts_nested_attributes_for+:
-
#
-
# class Person < ActiveRecord::Base
-
# has_one :address
-
# accepts_nested_attributes_for :address, allow_destroy: true
-
# end
-
#
-
# Now, when you use a form element with the <tt>_destroy</tt> parameter,
-
# with a value that evaluates to +true+, you will destroy the associated
-
# model (eg. 1, '1', true, or 'true'):
-
#
-
# <%= form_for @person do |person_form| %>
-
# ...
-
# <%= person_form.fields_for :address do |address_fields| %>
-
# ...
-
# Delete: <%= address_fields.check_box :_destroy %>
-
# <% end %>
-
# ...
-
# <% end %>
-
#
-
# ==== One-to-many
-
#
-
# Consider a Person class which returns an _array_ of Project instances
-
# from the <tt>projects</tt> reader method and responds to the
-
# <tt>projects_attributes=</tt> writer method:
-
#
-
# class Person
-
# def projects
-
# [@project1, @project2]
-
# end
-
#
-
# def projects_attributes=(attributes)
-
# # Process the attributes hash
-
# end
-
# end
-
#
-
# Note that the <tt>projects_attributes=</tt> writer method is in fact
-
# required for fields_for to correctly identify <tt>:projects</tt> as a
-
# collection, and the correct indices to be set in the form markup.
-
#
-
# When projects is already an association on Person you can use
-
# +accepts_nested_attributes_for+ to define the writer method for you:
-
#
-
# class Person < ActiveRecord::Base
-
# has_many :projects
-
# accepts_nested_attributes_for :projects
-
# end
-
#
-
# This model can now be used with a nested fields_for. The block given to
-
# the nested fields_for call will be repeated for each instance in the
-
# collection:
-
#
-
# <%= form_for @person do |person_form| %>
-
# ...
-
# <%= person_form.fields_for :projects do |project_fields| %>
-
# <% if project_fields.object.active? %>
-
# Name: <%= project_fields.text_field :name %>
-
# <% end %>
-
# <% end %>
-
# ...
-
# <% end %>
-
#
-
# It's also possible to specify the instance to be used:
-
#
-
# <%= form_for @person do |person_form| %>
-
# ...
-
# <% @person.projects.each do |project| %>
-
# <% if project.active? %>
-
# <%= person_form.fields_for :projects, project do |project_fields| %>
-
# Name: <%= project_fields.text_field :name %>
-
# <% end %>
-
# <% end %>
-
# <% end %>
-
# ...
-
# <% end %>
-
#
-
# Or a collection to be used:
-
#
-
# <%= form_for @person do |person_form| %>
-
# ...
-
# <%= person_form.fields_for :projects, @active_projects do |project_fields| %>
-
# Name: <%= project_fields.text_field :name %>
-
# <% end %>
-
# ...
-
# <% end %>
-
#
-
# If you want to destroy any of the associated models through the
-
# form, you have to enable it first using the <tt>:allow_destroy</tt>
-
# option for +accepts_nested_attributes_for+:
-
#
-
# class Person < ActiveRecord::Base
-
# has_many :projects
-
# accepts_nested_attributes_for :projects, allow_destroy: true
-
# end
-
#
-
# This will allow you to specify which models to destroy in the
-
# attributes hash by adding a form element for the <tt>_destroy</tt>
-
# parameter with a value that evaluates to +true+
-
# (eg. 1, '1', true, or 'true'):
-
#
-
# <%= form_for @person do |person_form| %>
-
# ...
-
# <%= person_form.fields_for :projects do |project_fields| %>
-
# Delete: <%= project_fields.check_box :_destroy %>
-
# <% end %>
-
# ...
-
# <% end %>
-
#
-
# When a collection is used you might want to know the index of each
-
# object into the array. For this purpose, the <tt>index</tt> method
-
# is available in the FormBuilder object.
-
#
-
# <%= form_for @person do |person_form| %>
-
# ...
-
# <%= person_form.fields_for :projects do |project_fields| %>
-
# Project #<%= project_fields.index %>
-
# ...
-
# <% end %>
-
# ...
-
# <% end %>
-
#
-
# Note that fields_for will automatically generate a hidden field
-
# to store the ID of the record. There are circumstances where this
-
# hidden field is not needed and you can pass <tt>include_id: false</tt>
-
# to prevent fields_for from rendering it automatically.
-
1
def fields_for(record_name, record_object = nil, options = {}, &block)
-
builder = instantiate_builder(record_name, record_object, options)
-
capture(builder, &block)
-
end
-
-
# Returns a label tag tailored for labelling an input field for a specified attribute (identified by +method+) on an object
-
# assigned to the template (identified by +object+). The text of label will default to the attribute name unless a translation
-
# is found in the current I18n locale (through helpers.label.<modelname>.<attribute>) or you specify it explicitly.
-
# Additional options on the label tag can be passed as a hash with +options+. These options will be tagged
-
# onto the HTML as an HTML element attribute as in the example shown, except for the <tt>:value</tt> option, which is designed to
-
# target labels for radio_button tags (where the value is used in the ID of the input tag).
-
#
-
# ==== Examples
-
# label(:post, :title)
-
# # => <label for="post_title">Title</label>
-
#
-
# You can localize your labels based on model and attribute names.
-
# For example you can define the following in your locale (e.g. en.yml)
-
#
-
# helpers:
-
# label:
-
# post:
-
# body: "Write your entire text here"
-
#
-
# Which then will result in
-
#
-
# label(:post, :body)
-
# # => <label for="post_body">Write your entire text here</label>
-
#
-
# Localization can also be based purely on the translation of the attribute-name
-
# (if you are using ActiveRecord):
-
#
-
# activerecord:
-
# attributes:
-
# post:
-
# cost: "Total cost"
-
#
-
# label(:post, :cost)
-
# # => <label for="post_cost">Total cost</label>
-
#
-
# label(:post, :title, "A short title")
-
# # => <label for="post_title">A short title</label>
-
#
-
# label(:post, :title, "A short title", class: "title_label")
-
# # => <label for="post_title" class="title_label">A short title</label>
-
#
-
# label(:post, :privacy, "Public Post", value: "public")
-
# # => <label for="post_privacy_public">Public Post</label>
-
#
-
# label(:post, :terms) do
-
# 'Accept <a href="/terms">Terms</a>.'.html_safe
-
# end
-
1
def label(object_name, method, content_or_options = nil, options = nil, &block)
-
Tags::Label.new(object_name, method, self, content_or_options, options).render(&block)
-
end
-
-
# Returns an input tag of the "text" type tailored for accessing a specified attribute (identified by +method+) on an object
-
# assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
-
# hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
-
# shown.
-
#
-
# ==== Examples
-
# text_field(:post, :title, size: 20)
-
# # => <input type="text" id="post_title" name="post[title]" size="20" value="#{@post.title}" />
-
#
-
# text_field(:post, :title, class: "create_input")
-
# # => <input type="text" id="post_title" name="post[title]" value="#{@post.title}" class="create_input" />
-
#
-
# text_field(:session, :user, onchange: "if ($('#session_user').val() === 'admin') { alert('Your login cannot be admin!'); }")
-
# # => <input type="text" id="session_user" name="session[user]" value="#{@session.user}" onchange="if ($('#session_user').val() === 'admin') { alert('Your login cannot be admin!'); }"/>
-
#
-
# text_field(:snippet, :code, size: 20, class: 'code_input')
-
# # => <input type="text" id="snippet_code" name="snippet[code]" size="20" value="#{@snippet.code}" class="code_input" />
-
1
def text_field(object_name, method, options = {})
-
Tags::TextField.new(object_name, method, self, options).render
-
end
-
-
# Returns an input tag of the "password" type tailored for accessing a specified attribute (identified by +method+) on an object
-
# assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
-
# hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
-
# shown. For security reasons this field is blank by default; pass in a value via +options+ if this is not desired.
-
#
-
# ==== Examples
-
# password_field(:login, :pass, size: 20)
-
# # => <input type="password" id="login_pass" name="login[pass]" size="20" />
-
#
-
# password_field(:account, :secret, class: "form_input", value: @account.secret)
-
# # => <input type="password" id="account_secret" name="account[secret]" value="#{@account.secret}" class="form_input" />
-
#
-
# password_field(:user, :password, onchange: "if ($('#user_password').val().length > 30) { alert('Your password needs to be shorter!'); }")
-
# # => <input type="password" id="user_password" name="user[password]" onchange="if ($('#user_password').val().length > 30) { alert('Your password needs to be shorter!'); }"/>
-
#
-
# password_field(:account, :pin, size: 20, class: 'form_input')
-
# # => <input type="password" id="account_pin" name="account[pin]" size="20" class="form_input" />
-
1
def password_field(object_name, method, options = {})
-
Tags::PasswordField.new(object_name, method, self, options).render
-
end
-
-
# Returns a hidden input tag tailored for accessing a specified attribute (identified by +method+) on an object
-
# assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
-
# hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
-
# shown.
-
#
-
# ==== Examples
-
# hidden_field(:signup, :pass_confirm)
-
# # => <input type="hidden" id="signup_pass_confirm" name="signup[pass_confirm]" value="#{@signup.pass_confirm}" />
-
#
-
# hidden_field(:post, :tag_list)
-
# # => <input type="hidden" id="post_tag_list" name="post[tag_list]" value="#{@post.tag_list}" />
-
#
-
# hidden_field(:user, :token)
-
# # => <input type="hidden" id="user_token" name="user[token]" value="#{@user.token}" />
-
1
def hidden_field(object_name, method, options = {})
-
Tags::HiddenField.new(object_name, method, self, options).render
-
end
-
-
# Returns a file upload input tag tailored for accessing a specified attribute (identified by +method+) on an object
-
# assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
-
# hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
-
# shown.
-
#
-
# Using this method inside a +form_for+ block will set the enclosing form's encoding to <tt>multipart/form-data</tt>.
-
#
-
# ==== Options
-
# * Creates standard HTML attributes for the tag.
-
# * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
-
# * <tt>:multiple</tt> - If set to true, *in most updated browsers* the user will be allowed to select multiple files.
-
# * <tt>:accept</tt> - If set to one or multiple mime-types, the user will be suggested a filter when choosing a file. You still need to set up model validations.
-
#
-
# ==== Examples
-
# file_field(:user, :avatar)
-
# # => <input type="file" id="user_avatar" name="user[avatar]" />
-
#
-
# file_field(:post, :image, :multiple => true)
-
# # => <input type="file" id="post_image" name="post[image]" multiple="true" />
-
#
-
# file_field(:post, :attached, accept: 'text/html')
-
# # => <input accept="text/html" type="file" id="post_attached" name="post[attached]" />
-
#
-
# file_field(:post, :image, accept: 'image/png,image/gif,image/jpeg')
-
# # => <input type="file" id="post_image" name="post[image]" accept="image/png,image/gif,image/jpeg" />
-
#
-
# file_field(:attachment, :file, class: 'file_input')
-
# # => <input type="file" id="attachment_file" name="attachment[file]" class="file_input" />
-
1
def file_field(object_name, method, options = {})
-
Tags::FileField.new(object_name, method, self, options).render
-
end
-
-
# Returns a textarea opening and closing tag set tailored for accessing a specified attribute (identified by +method+)
-
# on an object assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
-
# hash with +options+.
-
#
-
# ==== Examples
-
# text_area(:post, :body, cols: 20, rows: 40)
-
# # => <textarea cols="20" rows="40" id="post_body" name="post[body]">
-
# # #{@post.body}
-
# # </textarea>
-
#
-
# text_area(:comment, :text, size: "20x30")
-
# # => <textarea cols="20" rows="30" id="comment_text" name="comment[text]">
-
# # #{@comment.text}
-
# # </textarea>
-
#
-
# text_area(:application, :notes, cols: 40, rows: 15, class: 'app_input')
-
# # => <textarea cols="40" rows="15" id="application_notes" name="application[notes]" class="app_input">
-
# # #{@application.notes}
-
# # </textarea>
-
#
-
# text_area(:entry, :body, size: "20x20", disabled: 'disabled')
-
# # => <textarea cols="20" rows="20" id="entry_body" name="entry[body]" disabled="disabled">
-
# # #{@entry.body}
-
# # </textarea>
-
1
def text_area(object_name, method, options = {})
-
Tags::TextArea.new(object_name, method, self, options).render
-
end
-
-
# Returns a checkbox tag tailored for accessing a specified attribute (identified by +method+) on an object
-
# assigned to the template (identified by +object+). This object must be an instance object (@object) and not a local object.
-
# It's intended that +method+ returns an integer and if that integer is above zero, then the checkbox is checked.
-
# Additional options on the input tag can be passed as a hash with +options+. The +checked_value+ defaults to 1
-
# while the default +unchecked_value+ is set to 0 which is convenient for boolean values.
-
#
-
# ==== Gotcha
-
#
-
# The HTML specification says unchecked check boxes are not successful, and
-
# thus web browsers do not send them. Unfortunately this introduces a gotcha:
-
# if an +Invoice+ model has a +paid+ flag, and in the form that edits a paid
-
# invoice the user unchecks its check box, no +paid+ parameter is sent. So,
-
# any mass-assignment idiom like
-
#
-
# @invoice.update(params[:invoice])
-
#
-
# wouldn't update the flag.
-
#
-
# To prevent this the helper generates an auxiliary hidden field before
-
# the very check box. The hidden field has the same name and its
-
# attributes mimic an unchecked check box.
-
#
-
# This way, the client either sends only the hidden field (representing
-
# the check box is unchecked), or both fields. Since the HTML specification
-
# says key/value pairs have to be sent in the same order they appear in the
-
# form, and parameters extraction gets the last occurrence of any repeated
-
# key in the query string, that works for ordinary forms.
-
#
-
# Unfortunately that workaround does not work when the check box goes
-
# within an array-like parameter, as in
-
#
-
# <%= fields_for "project[invoice_attributes][]", invoice, index: nil do |form| %>
-
# <%= form.check_box :paid %>
-
# ...
-
# <% end %>
-
#
-
# because parameter name repetition is precisely what Rails seeks to distinguish
-
# the elements of the array. For each item with a checked check box you
-
# get an extra ghost item with only that attribute, assigned to "0".
-
#
-
# In that case it is preferable to either use +check_box_tag+ or to use
-
# hashes instead of arrays.
-
#
-
# # Let's say that @post.validated? is 1:
-
# check_box("post", "validated")
-
# # => <input name="post[validated]" type="hidden" value="0" />
-
# # <input checked="checked" type="checkbox" id="post_validated" name="post[validated]" value="1" />
-
#
-
# # Let's say that @puppy.gooddog is "no":
-
# check_box("puppy", "gooddog", {}, "yes", "no")
-
# # => <input name="puppy[gooddog]" type="hidden" value="no" />
-
# # <input type="checkbox" id="puppy_gooddog" name="puppy[gooddog]" value="yes" />
-
#
-
# check_box("eula", "accepted", { class: 'eula_check' }, "yes", "no")
-
# # => <input name="eula[accepted]" type="hidden" value="no" />
-
# # <input type="checkbox" class="eula_check" id="eula_accepted" name="eula[accepted]" value="yes" />
-
1
def check_box(object_name, method, options = {}, checked_value = "1", unchecked_value = "0")
-
Tags::CheckBox.new(object_name, method, self, checked_value, unchecked_value, options).render
-
end
-
-
# Returns a radio button tag for accessing a specified attribute (identified by +method+) on an object
-
# assigned to the template (identified by +object+). If the current value of +method+ is +tag_value+ the
-
# radio button will be checked.
-
#
-
# To force the radio button to be checked pass <tt>checked: true</tt> in the
-
# +options+ hash. You may pass HTML options there as well.
-
#
-
# # Let's say that @post.category returns "rails":
-
# radio_button("post", "category", "rails")
-
# radio_button("post", "category", "java")
-
# # => <input type="radio" id="post_category_rails" name="post[category]" value="rails" checked="checked" />
-
# # <input type="radio" id="post_category_java" name="post[category]" value="java" />
-
#
-
# radio_button("user", "receive_newsletter", "yes")
-
# radio_button("user", "receive_newsletter", "no")
-
# # => <input type="radio" id="user_receive_newsletter_yes" name="user[receive_newsletter]" value="yes" />
-
# # <input type="radio" id="user_receive_newsletter_no" name="user[receive_newsletter]" value="no" checked="checked" />
-
1
def radio_button(object_name, method, tag_value, options = {})
-
Tags::RadioButton.new(object_name, method, self, tag_value, options).render
-
end
-
-
# Returns a text_field of type "color".
-
#
-
# color_field("car", "color")
-
# # => <input id="car_color" name="car[color]" type="color" value="#000000" />
-
1
def color_field(object_name, method, options = {})
-
Tags::ColorField.new(object_name, method, self, options).render
-
end
-
-
# Returns an input of type "search" for accessing a specified attribute (identified by +method+) on an object
-
# assigned to the template (identified by +object_name+). Inputs of type "search" may be styled differently by
-
# some browsers.
-
#
-
# search_field(:user, :name)
-
# # => <input id="user_name" name="user[name]" type="search" />
-
# search_field(:user, :name, autosave: false)
-
# # => <input autosave="false" id="user_name" name="user[name]" type="search" />
-
# search_field(:user, :name, results: 3)
-
# # => <input id="user_name" name="user[name]" results="3" type="search" />
-
# # Assume request.host returns "www.example.com"
-
# search_field(:user, :name, autosave: true)
-
# # => <input autosave="com.example.www" id="user_name" name="user[name]" results="10" type="search" />
-
# search_field(:user, :name, onsearch: true)
-
# # => <input id="user_name" incremental="true" name="user[name]" onsearch="true" type="search" />
-
# search_field(:user, :name, autosave: false, onsearch: true)
-
# # => <input autosave="false" id="user_name" incremental="true" name="user[name]" onsearch="true" type="search" />
-
# search_field(:user, :name, autosave: true, onsearch: true)
-
# # => <input autosave="com.example.www" id="user_name" incremental="true" name="user[name]" onsearch="true" results="10" type="search" />
-
1
def search_field(object_name, method, options = {})
-
Tags::SearchField.new(object_name, method, self, options).render
-
end
-
-
# Returns a text_field of type "tel".
-
#
-
# telephone_field("user", "phone")
-
# # => <input id="user_phone" name="user[phone]" type="tel" />
-
#
-
1
def telephone_field(object_name, method, options = {})
-
Tags::TelField.new(object_name, method, self, options).render
-
end
-
# aliases telephone_field
-
1
alias phone_field telephone_field
-
-
# Returns a text_field of type "date".
-
#
-
# date_field("user", "born_on")
-
# # => <input id="user_born_on" name="user[born_on]" type="date" />
-
#
-
# The default value is generated by trying to call "to_date"
-
# on the object's value, which makes it behave as expected for instances
-
# of DateTime and ActiveSupport::TimeWithZone. You can still override that
-
# by passing the "value" option explicitly, e.g.
-
#
-
# @user.born_on = Date.new(1984, 1, 27)
-
# date_field("user", "born_on", value: "1984-05-12")
-
# # => <input id="user_born_on" name="user[born_on]" type="date" value="1984-05-12" />
-
#
-
1
def date_field(object_name, method, options = {})
-
Tags::DateField.new(object_name, method, self, options).render
-
end
-
-
# Returns a text_field of type "time".
-
#
-
# The default value is generated by trying to call +strftime+ with "%T.%L"
-
# on the objects's value. It is still possible to override that
-
# by passing the "value" option.
-
#
-
# === Options
-
# * Accepts same options as time_field_tag
-
#
-
# === Example
-
# time_field("task", "started_at")
-
# # => <input id="task_started_at" name="task[started_at]" type="time" />
-
#
-
1
def time_field(object_name, method, options = {})
-
Tags::TimeField.new(object_name, method, self, options).render
-
end
-
-
# Returns a text_field of type "datetime".
-
#
-
# datetime_field("user", "born_on")
-
# # => <input id="user_born_on" name="user[born_on]" type="datetime" />
-
#
-
# The default value is generated by trying to call +strftime+ with "%Y-%m-%dT%T.%L%z"
-
# on the object's value, which makes it behave as expected for instances
-
# of DateTime and ActiveSupport::TimeWithZone.
-
#
-
# @user.born_on = Date.new(1984, 1, 12)
-
# datetime_field("user", "born_on")
-
# # => <input id="user_born_on" name="user[born_on]" type="datetime" value="1984-01-12T00:00:00.000+0000" />
-
#
-
1
def datetime_field(object_name, method, options = {})
-
Tags::DatetimeField.new(object_name, method, self, options).render
-
end
-
-
# Returns a text_field of type "datetime-local".
-
#
-
# datetime_local_field("user", "born_on")
-
# # => <input id="user_born_on" name="user[born_on]" type="datetime-local" />
-
#
-
# The default value is generated by trying to call +strftime+ with "%Y-%m-%dT%T"
-
# on the object's value, which makes it behave as expected for instances
-
# of DateTime and ActiveSupport::TimeWithZone.
-
#
-
# @user.born_on = Date.new(1984, 1, 12)
-
# datetime_local_field("user", "born_on")
-
# # => <input id="user_born_on" name="user[born_on]" type="datetime-local" value="1984-01-12T00:00:00" />
-
#
-
1
def datetime_local_field(object_name, method, options = {})
-
Tags::DatetimeLocalField.new(object_name, method, self, options).render
-
end
-
-
# Returns a text_field of type "month".
-
#
-
# month_field("user", "born_on")
-
# # => <input id="user_born_on" name="user[born_on]" type="month" />
-
#
-
# The default value is generated by trying to call +strftime+ with "%Y-%m"
-
# on the object's value, which makes it behave as expected for instances
-
# of DateTime and ActiveSupport::TimeWithZone.
-
#
-
# @user.born_on = Date.new(1984, 1, 27)
-
# month_field("user", "born_on")
-
# # => <input id="user_born_on" name="user[born_on]" type="date" value="1984-01" />
-
#
-
1
def month_field(object_name, method, options = {})
-
Tags::MonthField.new(object_name, method, self, options).render
-
end
-
-
# Returns a text_field of type "week".
-
#
-
# week_field("user", "born_on")
-
# # => <input id="user_born_on" name="user[born_on]" type="week" />
-
#
-
# The default value is generated by trying to call +strftime+ with "%Y-W%W"
-
# on the object's value, which makes it behave as expected for instances
-
# of DateTime and ActiveSupport::TimeWithZone.
-
#
-
# @user.born_on = Date.new(1984, 5, 12)
-
# week_field("user", "born_on")
-
# # => <input id="user_born_on" name="user[born_on]" type="date" value="1984-W19" />
-
#
-
1
def week_field(object_name, method, options = {})
-
Tags::WeekField.new(object_name, method, self, options).render
-
end
-
-
# Returns a text_field of type "url".
-
#
-
# url_field("user", "homepage")
-
# # => <input id="user_homepage" name="user[homepage]" type="url" />
-
#
-
1
def url_field(object_name, method, options = {})
-
Tags::UrlField.new(object_name, method, self, options).render
-
end
-
-
# Returns a text_field of type "email".
-
#
-
# email_field("user", "address")
-
# # => <input id="user_address" name="user[address]" type="email" />
-
#
-
1
def email_field(object_name, method, options = {})
-
Tags::EmailField.new(object_name, method, self, options).render
-
end
-
-
# Returns an input tag of type "number".
-
#
-
# ==== Options
-
# * Accepts same options as number_field_tag
-
1
def number_field(object_name, method, options = {})
-
Tags::NumberField.new(object_name, method, self, options).render
-
end
-
-
# Returns an input tag of type "range".
-
#
-
# ==== Options
-
# * Accepts same options as range_field_tag
-
1
def range_field(object_name, method, options = {})
-
Tags::RangeField.new(object_name, method, self, options).render
-
end
-
-
1
private
-
-
1
def instantiate_builder(record_name, record_object, options)
-
case record_name
-
when String, Symbol
-
object = record_object
-
object_name = record_name
-
else
-
object = record_name
-
object_name = model_name_from_record_or_class(object).param_key
-
end
-
-
builder = options[:builder] || default_form_builder
-
builder.new(object_name, object, self, options)
-
end
-
-
1
def default_form_builder
-
builder = ActionView::Base.default_form_builder
-
builder.respond_to?(:constantize) ? builder.constantize : builder
-
end
-
end
-
-
# A +FormBuilder+ object is associated with a particular model object and
-
# allows you to generate fields associated with the model object. The
-
# +FormBuilder+ object is yielded when using +form_for+ or +fields_for+.
-
# For example:
-
#
-
# <%= form_for @person do |person_form| %>
-
# Name: <%= person_form.text_field :name %>
-
# Admin: <%= person_form.check_box :admin %>
-
# <% end %>
-
#
-
# In the above block, the a +FormBuilder+ object is yielded as the
-
# +person_form+ variable. This allows you to generate the +text_field+
-
# and +check_box+ fields by specifying their eponymous methods, which
-
# modify the underlying template and associates the +@person+ model object
-
# with the form.
-
#
-
# The +FormBuilder+ object can be thought of as serving as a proxy for the
-
# methods in the +FormHelper+ module. This class, however, allows you to
-
# call methods with the model object you are building the form for.
-
#
-
# You can create your own custom FormBuilder templates by subclassing this
-
# class. For example:
-
#
-
# class MyFormBuilder < ActionView::Helpers::FormBuilder
-
# def div_radio_button(method, tag_value, options = {})
-
# @template.content_tag(:div,
-
# @template.radio_button(
-
# @object_name, method, tag_value, objectify_options(options)
-
# )
-
# )
-
# end
-
#
-
# The above code creates a new method +div_radio_button+ which wraps a div
-
# around the a new radio button. Note that when options are passed in, you
-
# must called +objectify_options+ in order for the model object to get
-
# correctly passed to the method. If +objectify_options+ is not called,
-
# then the newly created helper will not be linked back to the model.
-
#
-
# The +div_radio_button+ code from above can now be used as follows:
-
#
-
# <%= form_for @person, :builder => MyFormBuilder do |f| %>
-
# I am a child: <%= f.div_radio_button(:admin, "child") %>
-
# I am an adult: <%= f.div_radio_button(:admin, "adult") %>
-
# <% end -%>
-
#
-
# The standard set of helper methods for form building are located in the
-
# +field_helpers+ class attribute.
-
1
class FormBuilder
-
1
include ModelNaming
-
-
# The methods which wrap a form helper call.
-
1
class_attribute :field_helpers
-
1
self.field_helpers = [:fields_for, :label, :text_field, :password_field,
-
:hidden_field, :file_field, :text_area, :check_box,
-
:radio_button, :color_field, :search_field,
-
:telephone_field, :phone_field, :date_field,
-
:time_field, :datetime_field, :datetime_local_field,
-
:month_field, :week_field, :url_field, :email_field,
-
:number_field, :range_field]
-
-
1
attr_accessor :object_name, :object, :options
-
-
1
attr_reader :multipart, :index
-
1
alias :multipart? :multipart
-
-
1
def multipart=(multipart)
-
@multipart = multipart
-
-
if parent_builder = @options[:parent_builder]
-
parent_builder.multipart = multipart
-
end
-
end
-
-
1
def self._to_partial_path
-
@_to_partial_path ||= name.demodulize.underscore.sub!(/_builder$/, '')
-
end
-
-
1
def to_partial_path
-
self.class._to_partial_path
-
end
-
-
1
def to_model
-
self
-
end
-
-
1
def initialize(object_name, object, template, options)
-
@nested_child_index = {}
-
@object_name, @object, @template, @options = object_name, object, template, options
-
@default_options = @options ? @options.slice(:index, :namespace) : {}
-
if @object_name.to_s.match(/\[\]$/)
-
if object ||= @template.instance_variable_get("@#{Regexp.last_match.pre_match}") and object.respond_to?(:to_param)
-
@auto_index = object.to_param
-
else
-
raise ArgumentError, "object[] naming but object param and @object var don't exist or don't respond to to_param: #{object.inspect}"
-
end
-
end
-
@multipart = nil
-
@index = options[:index] || options[:child_index]
-
end
-
-
1
(field_helpers - [:label, :check_box, :radio_button, :fields_for, :hidden_field, :file_field]).each do |selector|
-
17
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
-
def #{selector}(method, options = {}) # def text_field(method, options = {})
-
@template.send( # @template.send(
-
#{selector.inspect}, # "text_field",
-
@object_name, # @object_name,
-
method, # method,
-
objectify_options(options)) # objectify_options(options))
-
end # end
-
RUBY_EVAL
-
end
-
-
# Creates a scope around a specific model object like form_for, but
-
# doesn't create the form tags themselves. This makes fields_for suitable
-
# for specifying additional model objects in the same form.
-
#
-
# Although the usage and purpose of +field_for+ is similar to +form_for+'s,
-
# its method signature is slightly different. Like +form_for+, it yields
-
# a FormBuilder object associated with a particular model object to a block,
-
# and within the block allows methods to be called on the builder to
-
# generate fields associated with the model object. Fields may reflect
-
# a model object in two ways - how they are named (hence how submitted
-
# values appear within the +params+ hash in the controller) and what
-
# default values are shown when the form the fields appear in is first
-
# displayed. In order for both of these features to be specified independently,
-
# both an object name (represented by either a symbol or string) and the
-
# object itself can be passed to the method separately -
-
#
-
# <%= form_for @person do |person_form| %>
-
# First name: <%= person_form.text_field :first_name %>
-
# Last name : <%= person_form.text_field :last_name %>
-
#
-
# <%= fields_for :permission, @person.permission do |permission_fields| %>
-
# Admin? : <%= permission_fields.check_box :admin %>
-
# <% end %>
-
#
-
# <%= person_form.submit %>
-
# <% end %>
-
#
-
# In this case, the checkbox field will be represented by an HTML +input+
-
# tag with the +name+ attribute <tt>permission[admin]</tt>, and the submitted
-
# value will appear in the controller as <tt>params[:permission][:admin]</tt>.
-
# If <tt>@person.permission</tt> is an existing record with an attribute
-
# +admin+, the initial state of the checkbox when first displayed will
-
# reflect the value of <tt>@person.permission.admin</tt>.
-
#
-
# Often this can be simplified by passing just the name of the model
-
# object to +fields_for+ -
-
#
-
# <%= fields_for :permission do |permission_fields| %>
-
# Admin?: <%= permission_fields.check_box :admin %>
-
# <% end %>
-
#
-
# ...in which case, if <tt>:permission</tt> also happens to be the name of an
-
# instance variable <tt>@permission</tt>, the initial state of the input
-
# field will reflect the value of that variable's attribute <tt>@permission.admin</tt>.
-
#
-
# Alternatively, you can pass just the model object itself (if the first
-
# argument isn't a string or symbol +fields_for+ will realize that the
-
# name has been omitted) -
-
#
-
# <%= fields_for @person.permission do |permission_fields| %>
-
# Admin?: <%= permission_fields.check_box :admin %>
-
# <% end %>
-
#
-
# and +fields_for+ will derive the required name of the field from the
-
# _class_ of the model object, e.g. if <tt>@person.permission</tt>, is
-
# of class +Permission+, the field will still be named <tt>permission[admin]</tt>.
-
#
-
# Note: This also works for the methods in FormOptionHelper and
-
# DateHelper that are designed to work with an object as base, like
-
# FormOptionHelper#collection_select and DateHelper#datetime_select.
-
#
-
# === Nested Attributes Examples
-
#
-
# When the object belonging to the current scope has a nested attribute
-
# writer for a certain attribute, fields_for will yield a new scope
-
# for that attribute. This allows you to create forms that set or change
-
# the attributes of a parent object and its associations in one go.
-
#
-
# Nested attribute writers are normal setter methods named after an
-
# association. The most common way of defining these writers is either
-
# with +accepts_nested_attributes_for+ in a model definition or by
-
# defining a method with the proper name. For example: the attribute
-
# writer for the association <tt>:address</tt> is called
-
# <tt>address_attributes=</tt>.
-
#
-
# Whether a one-to-one or one-to-many style form builder will be yielded
-
# depends on whether the normal reader method returns a _single_ object
-
# or an _array_ of objects.
-
#
-
# ==== One-to-one
-
#
-
# Consider a Person class which returns a _single_ Address from the
-
# <tt>address</tt> reader method and responds to the
-
# <tt>address_attributes=</tt> writer method:
-
#
-
# class Person
-
# def address
-
# @address
-
# end
-
#
-
# def address_attributes=(attributes)
-
# # Process the attributes hash
-
# end
-
# end
-
#
-
# This model can now be used with a nested fields_for, like so:
-
#
-
# <%= form_for @person do |person_form| %>
-
# ...
-
# <%= person_form.fields_for :address do |address_fields| %>
-
# Street : <%= address_fields.text_field :street %>
-
# Zip code: <%= address_fields.text_field :zip_code %>
-
# <% end %>
-
# ...
-
# <% end %>
-
#
-
# When address is already an association on a Person you can use
-
# +accepts_nested_attributes_for+ to define the writer method for you:
-
#
-
# class Person < ActiveRecord::Base
-
# has_one :address
-
# accepts_nested_attributes_for :address
-
# end
-
#
-
# If you want to destroy the associated model through the form, you have
-
# to enable it first using the <tt>:allow_destroy</tt> option for
-
# +accepts_nested_attributes_for+:
-
#
-
# class Person < ActiveRecord::Base
-
# has_one :address
-
# accepts_nested_attributes_for :address, allow_destroy: true
-
# end
-
#
-
# Now, when you use a form element with the <tt>_destroy</tt> parameter,
-
# with a value that evaluates to +true+, you will destroy the associated
-
# model (eg. 1, '1', true, or 'true'):
-
#
-
# <%= form_for @person do |person_form| %>
-
# ...
-
# <%= person_form.fields_for :address do |address_fields| %>
-
# ...
-
# Delete: <%= address_fields.check_box :_destroy %>
-
# <% end %>
-
# ...
-
# <% end %>
-
#
-
# ==== One-to-many
-
#
-
# Consider a Person class which returns an _array_ of Project instances
-
# from the <tt>projects</tt> reader method and responds to the
-
# <tt>projects_attributes=</tt> writer method:
-
#
-
# class Person
-
# def projects
-
# [@project1, @project2]
-
# end
-
#
-
# def projects_attributes=(attributes)
-
# # Process the attributes hash
-
# end
-
# end
-
#
-
# Note that the <tt>projects_attributes=</tt> writer method is in fact
-
# required for fields_for to correctly identify <tt>:projects</tt> as a
-
# collection, and the correct indices to be set in the form markup.
-
#
-
# When projects is already an association on Person you can use
-
# +accepts_nested_attributes_for+ to define the writer method for you:
-
#
-
# class Person < ActiveRecord::Base
-
# has_many :projects
-
# accepts_nested_attributes_for :projects
-
# end
-
#
-
# This model can now be used with a nested fields_for. The block given to
-
# the nested fields_for call will be repeated for each instance in the
-
# collection:
-
#
-
# <%= form_for @person do |person_form| %>
-
# ...
-
# <%= person_form.fields_for :projects do |project_fields| %>
-
# <% if project_fields.object.active? %>
-
# Name: <%= project_fields.text_field :name %>
-
# <% end %>
-
# <% end %>
-
# ...
-
# <% end %>
-
#
-
# It's also possible to specify the instance to be used:
-
#
-
# <%= form_for @person do |person_form| %>
-
# ...
-
# <% @person.projects.each do |project| %>
-
# <% if project.active? %>
-
# <%= person_form.fields_for :projects, project do |project_fields| %>
-
# Name: <%= project_fields.text_field :name %>
-
# <% end %>
-
# <% end %>
-
# <% end %>
-
# ...
-
# <% end %>
-
#
-
# Or a collection to be used:
-
#
-
# <%= form_for @person do |person_form| %>
-
# ...
-
# <%= person_form.fields_for :projects, @active_projects do |project_fields| %>
-
# Name: <%= project_fields.text_field :name %>
-
# <% end %>
-
# ...
-
# <% end %>
-
#
-
# If you want to destroy any of the associated models through the
-
# form, you have to enable it first using the <tt>:allow_destroy</tt>
-
# option for +accepts_nested_attributes_for+:
-
#
-
# class Person < ActiveRecord::Base
-
# has_many :projects
-
# accepts_nested_attributes_for :projects, allow_destroy: true
-
# end
-
#
-
# This will allow you to specify which models to destroy in the
-
# attributes hash by adding a form element for the <tt>_destroy</tt>
-
# parameter with a value that evaluates to +true+
-
# (eg. 1, '1', true, or 'true'):
-
#
-
# <%= form_for @person do |person_form| %>
-
# ...
-
# <%= person_form.fields_for :projects do |project_fields| %>
-
# Delete: <%= project_fields.check_box :_destroy %>
-
# <% end %>
-
# ...
-
# <% end %>
-
#
-
# When a collection is used you might want to know the index of each
-
# object into the array. For this purpose, the <tt>index</tt> method
-
# is available in the FormBuilder object.
-
#
-
# <%= form_for @person do |person_form| %>
-
# ...
-
# <%= person_form.fields_for :projects do |project_fields| %>
-
# Project #<%= project_fields.index %>
-
# ...
-
# <% end %>
-
# ...
-
# <% end %>
-
#
-
# Note that fields_for will automatically generate a hidden field
-
# to store the ID of the record. There are circumstances where this
-
# hidden field is not needed and you can pass <tt>include_id: false</tt>
-
# to prevent fields_for from rendering it automatically.
-
1
def fields_for(record_name, record_object = nil, fields_options = {}, &block)
-
fields_options, record_object = record_object, nil if record_object.is_a?(Hash) && record_object.extractable_options?
-
fields_options[:builder] ||= options[:builder]
-
fields_options[:namespace] = options[:namespace]
-
fields_options[:parent_builder] = self
-
-
case record_name
-
when String, Symbol
-
if nested_attributes_association?(record_name)
-
return fields_for_with_nested_attributes(record_name, record_object, fields_options, block)
-
end
-
else
-
record_object = record_name.is_a?(Array) ? record_name.last : record_name
-
record_name = model_name_from_record_or_class(record_object).param_key
-
end
-
-
index = if options.has_key?(:index)
-
options[:index]
-
elsif defined?(@auto_index)
-
self.object_name = @object_name.to_s.sub(/\[\]$/,"")
-
@auto_index
-
end
-
-
record_name = index ? "#{object_name}[#{index}][#{record_name}]" : "#{object_name}[#{record_name}]"
-
fields_options[:child_index] = index
-
-
@template.fields_for(record_name, record_object, fields_options, &block)
-
end
-
-
# Returns a label tag tailored for labelling an input field for a specified attribute (identified by +method+) on an object
-
# assigned to the template (identified by +object+). The text of label will default to the attribute name unless a translation
-
# is found in the current I18n locale (through helpers.label.<modelname>.<attribute>) or you specify it explicitly.
-
# Additional options on the label tag can be passed as a hash with +options+. These options will be tagged
-
# onto the HTML as an HTML element attribute as in the example shown, except for the <tt>:value</tt> option, which is designed to
-
# target labels for radio_button tags (where the value is used in the ID of the input tag).
-
#
-
# ==== Examples
-
# label(:post, :title)
-
# # => <label for="post_title">Title</label>
-
#
-
# You can localize your labels based on model and attribute names.
-
# For example you can define the following in your locale (e.g. en.yml)
-
#
-
# helpers:
-
# label:
-
# post:
-
# body: "Write your entire text here"
-
#
-
# Which then will result in
-
#
-
# label(:post, :body)
-
# # => <label for="post_body">Write your entire text here</label>
-
#
-
# Localization can also be based purely on the translation of the attribute-name
-
# (if you are using ActiveRecord):
-
#
-
# activerecord:
-
# attributes:
-
# post:
-
# cost: "Total cost"
-
#
-
# label(:post, :cost)
-
# # => <label for="post_cost">Total cost</label>
-
#
-
# label(:post, :title, "A short title")
-
# # => <label for="post_title">A short title</label>
-
#
-
# label(:post, :title, "A short title", class: "title_label")
-
# # => <label for="post_title" class="title_label">A short title</label>
-
#
-
# label(:post, :privacy, "Public Post", value: "public")
-
# # => <label for="post_privacy_public">Public Post</label>
-
#
-
# label(:post, :terms) do
-
# 'Accept <a href="/terms">Terms</a>.'.html_safe
-
# end
-
1
def label(method, text = nil, options = {}, &block)
-
@template.label(@object_name, method, text, objectify_options(options), &block)
-
end
-
-
# Returns a checkbox tag tailored for accessing a specified attribute (identified by +method+) on an object
-
# assigned to the template (identified by +object+). This object must be an instance object (@object) and not a local object.
-
# It's intended that +method+ returns an integer and if that integer is above zero, then the checkbox is checked.
-
# Additional options on the input tag can be passed as a hash with +options+. The +checked_value+ defaults to 1
-
# while the default +unchecked_value+ is set to 0 which is convenient for boolean values.
-
#
-
# ==== Gotcha
-
#
-
# The HTML specification says unchecked check boxes are not successful, and
-
# thus web browsers do not send them. Unfortunately this introduces a gotcha:
-
# if an +Invoice+ model has a +paid+ flag, and in the form that edits a paid
-
# invoice the user unchecks its check box, no +paid+ parameter is sent. So,
-
# any mass-assignment idiom like
-
#
-
# @invoice.update(params[:invoice])
-
#
-
# wouldn't update the flag.
-
#
-
# To prevent this the helper generates an auxiliary hidden field before
-
# the very check box. The hidden field has the same name and its
-
# attributes mimic an unchecked check box.
-
#
-
# This way, the client either sends only the hidden field (representing
-
# the check box is unchecked), or both fields. Since the HTML specification
-
# says key/value pairs have to be sent in the same order they appear in the
-
# form, and parameters extraction gets the last occurrence of any repeated
-
# key in the query string, that works for ordinary forms.
-
#
-
# Unfortunately that workaround does not work when the check box goes
-
# within an array-like parameter, as in
-
#
-
# <%= fields_for "project[invoice_attributes][]", invoice, index: nil do |form| %>
-
# <%= form.check_box :paid %>
-
# ...
-
# <% end %>
-
#
-
# because parameter name repetition is precisely what Rails seeks to distinguish
-
# the elements of the array. For each item with a checked check box you
-
# get an extra ghost item with only that attribute, assigned to "0".
-
#
-
# In that case it is preferable to either use +check_box_tag+ or to use
-
# hashes instead of arrays.
-
#
-
# # Let's say that @post.validated? is 1:
-
# check_box("post", "validated")
-
# # => <input name="post[validated]" type="hidden" value="0" />
-
# # <input checked="checked" type="checkbox" id="post_validated" name="post[validated]" value="1" />
-
#
-
# # Let's say that @puppy.gooddog is "no":
-
# check_box("puppy", "gooddog", {}, "yes", "no")
-
# # => <input name="puppy[gooddog]" type="hidden" value="no" />
-
# # <input type="checkbox" id="puppy_gooddog" name="puppy[gooddog]" value="yes" />
-
#
-
# check_box("eula", "accepted", { class: 'eula_check' }, "yes", "no")
-
# # => <input name="eula[accepted]" type="hidden" value="no" />
-
# # <input type="checkbox" class="eula_check" id="eula_accepted" name="eula[accepted]" value="yes" />
-
1
def check_box(method, options = {}, checked_value = "1", unchecked_value = "0")
-
@template.check_box(@object_name, method, objectify_options(options), checked_value, unchecked_value)
-
end
-
-
# Returns a radio button tag for accessing a specified attribute (identified by +method+) on an object
-
# assigned to the template (identified by +object+). If the current value of +method+ is +tag_value+ the
-
# radio button will be checked.
-
#
-
# To force the radio button to be checked pass <tt>checked: true</tt> in the
-
# +options+ hash. You may pass HTML options there as well.
-
#
-
# # Let's say that @post.category returns "rails":
-
# radio_button("post", "category", "rails")
-
# radio_button("post", "category", "java")
-
# # => <input type="radio" id="post_category_rails" name="post[category]" value="rails" checked="checked" />
-
# # <input type="radio" id="post_category_java" name="post[category]" value="java" />
-
#
-
# radio_button("user", "receive_newsletter", "yes")
-
# radio_button("user", "receive_newsletter", "no")
-
# # => <input type="radio" id="user_receive_newsletter_yes" name="user[receive_newsletter]" value="yes" />
-
# # <input type="radio" id="user_receive_newsletter_no" name="user[receive_newsletter]" value="no" checked="checked" />
-
1
def radio_button(method, tag_value, options = {})
-
@template.radio_button(@object_name, method, tag_value, objectify_options(options))
-
end
-
-
# Returns a hidden input tag tailored for accessing a specified attribute (identified by +method+) on an object
-
# assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
-
# hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
-
# shown.
-
#
-
# ==== Examples
-
# hidden_field(:signup, :pass_confirm)
-
# # => <input type="hidden" id="signup_pass_confirm" name="signup[pass_confirm]" value="#{@signup.pass_confirm}" />
-
#
-
# hidden_field(:post, :tag_list)
-
# # => <input type="hidden" id="post_tag_list" name="post[tag_list]" value="#{@post.tag_list}" />
-
#
-
# hidden_field(:user, :token)
-
# # => <input type="hidden" id="user_token" name="user[token]" value="#{@user.token}" />
-
#
-
1
def hidden_field(method, options = {})
-
@emitted_hidden_id = true if method == :id
-
@template.hidden_field(@object_name, method, objectify_options(options))
-
end
-
-
# Returns a file upload input tag tailored for accessing a specified attribute (identified by +method+) on an object
-
# assigned to the template (identified by +object+). Additional options on the input tag can be passed as a
-
# hash with +options+. These options will be tagged onto the HTML as an HTML element attribute as in the example
-
# shown.
-
#
-
# Using this method inside a +form_for+ block will set the enclosing form's encoding to <tt>multipart/form-data</tt>.
-
#
-
# ==== Options
-
# * Creates standard HTML attributes for the tag.
-
# * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
-
# * <tt>:multiple</tt> - If set to true, *in most updated browsers* the user will be allowed to select multiple files.
-
# * <tt>:accept</tt> - If set to one or multiple mime-types, the user will be suggested a filter when choosing a file. You still need to set up model validations.
-
#
-
# ==== Examples
-
# file_field(:user, :avatar)
-
# # => <input type="file" id="user_avatar" name="user[avatar]" />
-
#
-
# file_field(:post, :image, :multiple => true)
-
# # => <input type="file" id="post_image" name="post[image]" multiple="true" />
-
#
-
# file_field(:post, :attached, accept: 'text/html')
-
# # => <input accept="text/html" type="file" id="post_attached" name="post[attached]" />
-
#
-
# file_field(:post, :image, accept: 'image/png,image/gif,image/jpeg')
-
# # => <input type="file" id="post_image" name="post[image]" accept="image/png,image/gif,image/jpeg" />
-
#
-
# file_field(:attachment, :file, class: 'file_input')
-
# # => <input type="file" id="attachment_file" name="attachment[file]" class="file_input" />
-
1
def file_field(method, options = {})
-
self.multipart = true
-
@template.file_field(@object_name, method, objectify_options(options))
-
end
-
-
# Add the submit button for the given form. When no value is given, it checks
-
# if the object is a new resource or not to create the proper label:
-
#
-
# <%= form_for @post do |f| %>
-
# <%= f.submit %>
-
# <% end %>
-
#
-
# In the example above, if @post is a new record, it will use "Create Post" as
-
# submit button label, otherwise, it uses "Update Post".
-
#
-
# Those labels can be customized using I18n, under the helpers.submit key and accept
-
# the %{model} as translation interpolation:
-
#
-
# en:
-
# helpers:
-
# submit:
-
# create: "Create a %{model}"
-
# update: "Confirm changes to %{model}"
-
#
-
# It also searches for a key specific for the given object:
-
#
-
# en:
-
# helpers:
-
# submit:
-
# post:
-
# create: "Add %{model}"
-
#
-
1
def submit(value=nil, options={})
-
value, options = nil, value if value.is_a?(Hash)
-
value ||= submit_default_value
-
@template.submit_tag(value, options)
-
end
-
-
# Add the submit button for the given form. When no value is given, it checks
-
# if the object is a new resource or not to create the proper label:
-
#
-
# <%= form_for @post do |f| %>
-
# <%= f.button %>
-
# <% end %>
-
#
-
# In the example above, if @post is a new record, it will use "Create Post" as
-
# button label, otherwise, it uses "Update Post".
-
#
-
# Those labels can be customized using I18n, under the helpers.submit key
-
# (the same as submit helper) and accept the %{model} as translation interpolation:
-
#
-
# en:
-
# helpers:
-
# submit:
-
# create: "Create a %{model}"
-
# update: "Confirm changes to %{model}"
-
#
-
# It also searches for a key specific for the given object:
-
#
-
# en:
-
# helpers:
-
# submit:
-
# post:
-
# create: "Add %{model}"
-
#
-
# ==== Examples
-
# button("Create a post")
-
# # => <button name='button' type='submit'>Create post</button>
-
#
-
# button do
-
# content_tag(:strong, 'Ask me!')
-
# end
-
# # => <button name='button' type='submit'>
-
# # <strong>Ask me!</strong>
-
# # </button>
-
#
-
1
def button(value = nil, options = {}, &block)
-
value, options = nil, value if value.is_a?(Hash)
-
value ||= submit_default_value
-
@template.button_tag(value, options, &block)
-
end
-
-
1
def emitted_hidden_id?
-
@emitted_hidden_id ||= nil
-
end
-
-
1
private
-
1
def objectify_options(options)
-
@default_options.merge(options.merge(object: @object))
-
end
-
-
1
def submit_default_value
-
object = convert_to_model(@object)
-
key = object ? (object.persisted? ? :update : :create) : :submit
-
-
model = if object.class.respond_to?(:model_name)
-
object.class.model_name.human
-
else
-
@object_name.to_s.humanize
-
end
-
-
defaults = []
-
defaults << :"helpers.submit.#{object_name}.#{key}"
-
defaults << :"helpers.submit.#{key}"
-
defaults << "#{key.to_s.humanize} #{model}"
-
-
I18n.t(defaults.shift, model: model, default: defaults)
-
end
-
-
1
def nested_attributes_association?(association_name)
-
@object.respond_to?("#{association_name}_attributes=")
-
end
-
-
1
def fields_for_with_nested_attributes(association_name, association, options, block)
-
name = "#{object_name}[#{association_name}_attributes]"
-
association = convert_to_model(association)
-
-
if association.respond_to?(:persisted?)
-
association = [association] if @object.send(association_name).respond_to?(:to_ary)
-
elsif !association.respond_to?(:to_ary)
-
association = @object.send(association_name)
-
end
-
-
if association.respond_to?(:to_ary)
-
explicit_child_index = options[:child_index]
-
output = ActiveSupport::SafeBuffer.new
-
association.each do |child|
-
options[:child_index] = nested_child_index(name) unless explicit_child_index
-
output << fields_for_nested_model("#{name}[#{options[:child_index]}]", child, options, block)
-
end
-
output
-
elsif association
-
fields_for_nested_model(name, association, options, block)
-
end
-
end
-
-
1
def fields_for_nested_model(name, object, fields_options, block)
-
object = convert_to_model(object)
-
emit_hidden_id = object.persisted? && fields_options.fetch(:include_id) {
-
options.fetch(:include_id, true)
-
}
-
-
@template.fields_for(name, object, fields_options) do |f|
-
output = @template.capture(f, &block)
-
output.concat f.hidden_field(:id) if output && emit_hidden_id && !f.emitted_hidden_id?
-
output
-
end
-
end
-
-
1
def nested_child_index(name)
-
@nested_child_index[name] ||= -1
-
@nested_child_index[name] += 1
-
end
-
end
-
end
-
-
1
ActiveSupport.on_load(:action_view) do
-
3
cattr_accessor(:default_form_builder) { ::ActionView::Helpers::FormBuilder }
-
end
-
end
-
1
require 'cgi'
-
1
require 'erb'
-
1
require 'action_view/helpers/form_helper'
-
1
require 'active_support/core_ext/string/output_safety'
-
1
require 'active_support/core_ext/array/extract_options'
-
1
require 'active_support/core_ext/array/wrap'
-
-
1
module ActionView
-
# = Action View Form Option Helpers
-
1
module Helpers
-
# Provides a number of methods for turning different kinds of containers into a set of option tags.
-
#
-
# The <tt>collection_select</tt>, <tt>select</tt> and <tt>time_zone_select</tt> methods take an <tt>options</tt> parameter, a hash:
-
#
-
# * <tt>:include_blank</tt> - set to true or a prompt string if the first option element of the select element is a blank. Useful if there is not a default value required for the select element.
-
#
-
# select("post", "category", Post::CATEGORIES, {include_blank: true})
-
#
-
# could become:
-
#
-
# <select name="post[category]">
-
# <option></option>
-
# <option>joke</option>
-
# <option>poem</option>
-
# </select>
-
#
-
# Another common case is a select tag for a <tt>belongs_to</tt>-associated object.
-
#
-
# Example with <tt>@post.person_id => 2</tt>:
-
#
-
# select("post", "person_id", Person.all.collect {|p| [ p.name, p.id ] }, {include_blank: 'None'})
-
#
-
# could become:
-
#
-
# <select name="post[person_id]">
-
# <option value="">None</option>
-
# <option value="1">David</option>
-
# <option value="2" selected="selected">Sam</option>
-
# <option value="3">Tobias</option>
-
# </select>
-
#
-
# * <tt>:prompt</tt> - set to true or a prompt string. When the select element doesn't have a value yet, this prepends an option with a generic prompt -- "Please select" -- or the given prompt string.
-
#
-
# select("post", "person_id", Person.all.collect {|p| [ p.name, p.id ] }, {prompt: 'Select Person'})
-
#
-
# could become:
-
#
-
# <select name="post[person_id]">
-
# <option value="">Select Person</option>
-
# <option value="1">David</option>
-
# <option value="2">Sam</option>
-
# <option value="3">Tobias</option>
-
# </select>
-
#
-
# * <tt>:index</tt> - like the other form helpers, +select+ can accept an <tt>:index</tt> option to manually set the ID used in the resulting output. Unlike other helpers, +select+ expects this
-
# option to be in the +html_options+ parameter.
-
#
-
# select("album[]", "genre", %w[rap rock country], {}, { index: nil })
-
#
-
# becomes:
-
#
-
# <select name="album[][genre]" id="album__genre">
-
# <option value="rap">rap</option>
-
# <option value="rock">rock</option>
-
# <option value="country">country</option>
-
# </select>
-
#
-
# * <tt>:disabled</tt> - can be a single value or an array of values that will be disabled options in the final output.
-
#
-
# select("post", "category", Post::CATEGORIES, {disabled: 'restricted'})
-
#
-
# could become:
-
#
-
# <select name="post[category]">
-
# <option></option>
-
# <option>joke</option>
-
# <option>poem</option>
-
# <option disabled="disabled">restricted</option>
-
# </select>
-
#
-
# When used with the <tt>collection_select</tt> helper, <tt>:disabled</tt> can also be a Proc that identifies those options that should be disabled.
-
#
-
# collection_select(:post, :category_id, Category.all, :id, :name, {disabled: lambda{|category| category.archived? }})
-
#
-
# If the categories "2008 stuff" and "Christmas" return true when the method <tt>archived?</tt> is called, this would return:
-
# <select name="post[category_id]">
-
# <option value="1" disabled="disabled">2008 stuff</option>
-
# <option value="2" disabled="disabled">Christmas</option>
-
# <option value="3">Jokes</option>
-
# <option value="4">Poems</option>
-
# </select>
-
#
-
1
module FormOptionsHelper
-
# ERB::Util can mask some helpers like textilize. Make sure to include them.
-
1
include TextHelper
-
-
# Create a select tag and a series of contained option tags for the provided object and method.
-
# The option currently held by the object will be selected, provided that the object is available.
-
#
-
# There are two possible formats for the +choices+ parameter, corresponding to other helpers' output:
-
#
-
# * A flat collection (see +options_for_select+).
-
#
-
# * A nested collection (see +grouped_options_for_select+).
-
#
-
# For example:
-
#
-
# select("post", "person_id", Person.all.collect {|p| [ p.name, p.id ] }, { include_blank: true })
-
#
-
# would become:
-
#
-
# <select name="post[person_id]">
-
# <option value=""></option>
-
# <option value="1" selected="selected">David</option>
-
# <option value="2">Sam</option>
-
# <option value="3">Tobias</option>
-
# </select>
-
#
-
# assuming the associated person has ID 1.
-
#
-
# This can be used to provide a default set of options in the standard way: before rendering the create form, a
-
# new model instance is assigned the default options and bound to @model_name. Usually this model is not saved
-
# to the database. Instead, a second model object is created when the create request is received.
-
# This allows the user to submit a form page more than once with the expected results of creating multiple records.
-
# In addition, this allows a single partial to be used to generate form inputs for both edit and create forms.
-
#
-
# By default, <tt>post.person_id</tt> is the selected option. Specify <tt>selected: value</tt> to use a different selection
-
# or <tt>selected: nil</tt> to leave all options unselected. Similarly, you can specify values to be disabled in the option
-
# tags by specifying the <tt>:disabled</tt> option. This can either be a single value or an array of values to be disabled.
-
#
-
# A block can be passed to +select+ to customize how the options tags will be rendered. This
-
# is useful when the options tag has complex attributes.
-
#
-
# select(report, "campaign_ids") do
-
# available_campaigns.each do |c|
-
# content_tag(:option, c.name, value: c.id, data: { tags: c.tags.to_json })
-
# end
-
# end
-
#
-
# ==== Gotcha
-
#
-
# The HTML specification says when +multiple+ parameter passed to select and all options got deselected
-
# web browsers do not send any value to server. Unfortunately this introduces a gotcha:
-
# if an +User+ model has many +roles+ and have +role_ids+ accessor, and in the form that edits roles of the user
-
# the user deselects all roles from +role_ids+ multiple select box, no +role_ids+ parameter is sent. So,
-
# any mass-assignment idiom like
-
#
-
# @user.update(params[:user])
-
#
-
# wouldn't update roles.
-
#
-
# To prevent this the helper generates an auxiliary hidden field before
-
# every multiple select. The hidden field has the same name as multiple select and blank value.
-
#
-
# <b>Note:</b> The client either sends only the hidden field (representing
-
# the deselected multiple select box), or both fields. This means that the resulting array
-
# always contains a blank string.
-
#
-
# In case if you don't want the helper to generate this hidden field you can specify
-
# <tt>include_hidden: false</tt> option.
-
#
-
1
def select(object, method, choices = nil, options = {}, html_options = {}, &block)
-
Tags::Select.new(object, method, self, choices, options, html_options, &block).render
-
end
-
-
# Returns <tt><select></tt> and <tt><option></tt> tags for the collection of existing return values of
-
# +method+ for +object+'s class. The value returned from calling +method+ on the instance +object+ will
-
# be selected. If calling +method+ returns +nil+, no selection is made without including <tt>:prompt</tt>
-
# or <tt>:include_blank</tt> in the +options+ hash.
-
#
-
# The <tt>:value_method</tt> and <tt>:text_method</tt> parameters are methods to be called on each member
-
# of +collection+. The return values are used as the +value+ attribute and contents of each
-
# <tt><option></tt> tag, respectively. They can also be any object that responds to +call+, such
-
# as a +proc+, that will be called for each member of the +collection+ to
-
# retrieve the value/text.
-
#
-
# Example object structure for use with this method:
-
#
-
# class Post < ActiveRecord::Base
-
# belongs_to :author
-
# end
-
#
-
# class Author < ActiveRecord::Base
-
# has_many :posts
-
# def name_with_initial
-
# "#{first_name.first}. #{last_name}"
-
# end
-
# end
-
#
-
# Sample usage (selecting the associated Author for an instance of Post, <tt>@post</tt>):
-
#
-
# collection_select(:post, :author_id, Author.all, :id, :name_with_initial, prompt: true)
-
#
-
# If <tt>@post.author_id</tt> is already <tt>1</tt>, this would return:
-
# <select name="post[author_id]">
-
# <option value="">Please select</option>
-
# <option value="1" selected="selected">D. Heinemeier Hansson</option>
-
# <option value="2">D. Thomas</option>
-
# <option value="3">M. Clark</option>
-
# </select>
-
1
def collection_select(object, method, collection, value_method, text_method, options = {}, html_options = {})
-
Tags::CollectionSelect.new(object, method, self, collection, value_method, text_method, options, html_options).render
-
end
-
-
# Returns <tt><select></tt>, <tt><optgroup></tt> and <tt><option></tt> tags for the collection of existing return values of
-
# +method+ for +object+'s class. The value returned from calling +method+ on the instance +object+ will
-
# be selected. If calling +method+ returns +nil+, no selection is made without including <tt>:prompt</tt>
-
# or <tt>:include_blank</tt> in the +options+ hash.
-
#
-
# Parameters:
-
# * +object+ - The instance of the class to be used for the select tag
-
# * +method+ - The attribute of +object+ corresponding to the select tag
-
# * +collection+ - An array of objects representing the <tt><optgroup></tt> tags.
-
# * +group_method+ - The name of a method which, when called on a member of +collection+, returns an
-
# array of child objects representing the <tt><option></tt> tags.
-
# * +group_label_method+ - The name of a method which, when called on a member of +collection+, returns a
-
# string to be used as the +label+ attribute for its <tt><optgroup></tt> tag.
-
# * +option_key_method+ - The name of a method which, when called on a child object of a member of
-
# +collection+, returns a value to be used as the +value+ attribute for its <tt><option></tt> tag.
-
# * +option_value_method+ - The name of a method which, when called on a child object of a member of
-
# +collection+, returns a value to be used as the contents of its <tt><option></tt> tag.
-
#
-
# Example object structure for use with this method:
-
#
-
# class Continent < ActiveRecord::Base
-
# has_many :countries
-
# # attribs: id, name
-
# end
-
#
-
# class Country < ActiveRecord::Base
-
# belongs_to :continent
-
# # attribs: id, name, continent_id
-
# end
-
#
-
# class City < ActiveRecord::Base
-
# belongs_to :country
-
# # attribs: id, name, country_id
-
# end
-
#
-
# Sample usage:
-
#
-
# grouped_collection_select(:city, :country_id, @continents, :countries, :name, :id, :name)
-
#
-
# Possible output:
-
#
-
# <select name="city[country_id]">
-
# <optgroup label="Africa">
-
# <option value="1">South Africa</option>
-
# <option value="3">Somalia</option>
-
# </optgroup>
-
# <optgroup label="Europe">
-
# <option value="7" selected="selected">Denmark</option>
-
# <option value="2">Ireland</option>
-
# </optgroup>
-
# </select>
-
#
-
1
def grouped_collection_select(object, method, collection, group_method, group_label_method, option_key_method, option_value_method, options = {}, html_options = {})
-
Tags::GroupedCollectionSelect.new(object, method, self, collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options).render
-
end
-
-
# Returns select and option tags for the given object and method, using
-
# #time_zone_options_for_select to generate the list of option tags.
-
#
-
# In addition to the <tt>:include_blank</tt> option documented above,
-
# this method also supports a <tt>:model</tt> option, which defaults
-
# to ActiveSupport::TimeZone. This may be used by users to specify a
-
# different time zone model object. (See +time_zone_options_for_select+
-
# for more information.)
-
#
-
# You can also supply an array of ActiveSupport::TimeZone objects
-
# as +priority_zones+, so that they will be listed above the rest of the
-
# (long) list. (You can use ActiveSupport::TimeZone.us_zones as a convenience
-
# for obtaining a list of the US time zones, or a Regexp to select the zones
-
# of your choice)
-
#
-
# Finally, this method supports a <tt>:default</tt> option, which selects
-
# a default ActiveSupport::TimeZone if the object's time zone is +nil+.
-
#
-
# time_zone_select( "user", "time_zone", nil, include_blank: true)
-
#
-
# time_zone_select( "user", "time_zone", nil, default: "Pacific Time (US & Canada)" )
-
#
-
# time_zone_select( "user", 'time_zone', ActiveSupport::TimeZone.us_zones, default: "Pacific Time (US & Canada)")
-
#
-
# time_zone_select( "user", 'time_zone', [ ActiveSupport::TimeZone['Alaska'], ActiveSupport::TimeZone['Hawaii'] ])
-
#
-
# time_zone_select( "user", 'time_zone', /Australia/)
-
#
-
# time_zone_select( "user", "time_zone", ActiveSupport::TimeZone.all.sort, model: ActiveSupport::TimeZone)
-
1
def time_zone_select(object, method, priority_zones = nil, options = {}, html_options = {})
-
Tags::TimeZoneSelect.new(object, method, self, priority_zones, options, html_options).render
-
end
-
-
# Accepts a container (hash, array, enumerable, your type) and returns a string of option tags. Given a container
-
# where the elements respond to first and last (such as a two-element array), the "lasts" serve as option values and
-
# the "firsts" as option text. Hashes are turned into this form automatically, so the keys become "firsts" and values
-
# become lasts. If +selected+ is specified, the matching "last" or element will get the selected option-tag. +selected+
-
# may also be an array of values to be selected when using a multiple select.
-
#
-
# options_for_select([["Dollar", "$"], ["Kroner", "DKK"]])
-
# # => <option value="$">Dollar</option>
-
# # => <option value="DKK">Kroner</option>
-
#
-
# options_for_select([ "VISA", "MasterCard" ], "MasterCard")
-
# # => <option>VISA</option>
-
# # => <option selected="selected">MasterCard</option>
-
#
-
# options_for_select({ "Basic" => "$20", "Plus" => "$40" }, "$40")
-
# # => <option value="$20">Basic</option>
-
# # => <option value="$40" selected="selected">Plus</option>
-
#
-
# options_for_select([ "VISA", "MasterCard", "Discover" ], ["VISA", "Discover"])
-
# # => <option selected="selected">VISA</option>
-
# # => <option>MasterCard</option>
-
# # => <option selected="selected">Discover</option>
-
#
-
# You can optionally provide html attributes as the last element of the array.
-
#
-
# options_for_select([ "Denmark", ["USA", {class: 'bold'}], "Sweden" ], ["USA", "Sweden"])
-
# # => <option value="Denmark">Denmark</option>
-
# # => <option value="USA" class="bold" selected="selected">USA</option>
-
# # => <option value="Sweden" selected="selected">Sweden</option>
-
#
-
# options_for_select([["Dollar", "$", {class: "bold"}], ["Kroner", "DKK", {onclick: "alert('HI');"}]])
-
# # => <option value="$" class="bold">Dollar</option>
-
# # => <option value="DKK" onclick="alert('HI');">Kroner</option>
-
#
-
# If you wish to specify disabled option tags, set +selected+ to be a hash, with <tt>:disabled</tt> being either a value
-
# or array of values to be disabled. In this case, you can use <tt>:selected</tt> to specify selected option tags.
-
#
-
# options_for_select(["Free", "Basic", "Advanced", "Super Platinum"], disabled: "Super Platinum")
-
# # => <option value="Free">Free</option>
-
# # => <option value="Basic">Basic</option>
-
# # => <option value="Advanced">Advanced</option>
-
# # => <option value="Super Platinum" disabled="disabled">Super Platinum</option>
-
#
-
# options_for_select(["Free", "Basic", "Advanced", "Super Platinum"], disabled: ["Advanced", "Super Platinum"])
-
# # => <option value="Free">Free</option>
-
# # => <option value="Basic">Basic</option>
-
# # => <option value="Advanced" disabled="disabled">Advanced</option>
-
# # => <option value="Super Platinum" disabled="disabled">Super Platinum</option>
-
#
-
# options_for_select(["Free", "Basic", "Advanced", "Super Platinum"], selected: "Free", disabled: "Super Platinum")
-
# # => <option value="Free" selected="selected">Free</option>
-
# # => <option value="Basic">Basic</option>
-
# # => <option value="Advanced">Advanced</option>
-
# # => <option value="Super Platinum" disabled="disabled">Super Platinum</option>
-
#
-
# NOTE: Only the option tags are returned, you have to wrap this call in a regular HTML select tag.
-
1
def options_for_select(container, selected = nil)
-
return container if String === container
-
-
selected, disabled = extract_selected_and_disabled(selected).map do |r|
-
Array(r).map { |item| item.to_s }
-
end
-
-
container.map do |element|
-
html_attributes = option_html_attributes(element)
-
text, value = option_text_and_value(element).map { |item| item.to_s }
-
-
html_attributes[:selected] ||= option_value_selected?(value, selected)
-
html_attributes[:disabled] ||= disabled && option_value_selected?(value, disabled)
-
html_attributes[:value] = value
-
-
content_tag_string(:option, text, html_attributes)
-
end.join("\n").html_safe
-
end
-
-
# Returns a string of option tags that have been compiled by iterating over the +collection+ and assigning
-
# the result of a call to the +value_method+ as the option value and the +text_method+ as the option text.
-
#
-
# options_from_collection_for_select(@people, 'id', 'name')
-
# # => <option value="#{person.id}">#{person.name}</option>
-
#
-
# This is more often than not used inside a #select_tag like this example:
-
#
-
# select_tag 'person', options_from_collection_for_select(@people, 'id', 'name')
-
#
-
# If +selected+ is specified as a value or array of values, the element(s) returning a match on +value_method+
-
# will be selected option tag(s).
-
#
-
# If +selected+ is specified as a Proc, those members of the collection that return true for the anonymous
-
# function are the selected values.
-
#
-
# +selected+ can also be a hash, specifying both <tt>:selected</tt> and/or <tt>:disabled</tt> values as required.
-
#
-
# Be sure to specify the same class as the +value_method+ when specifying selected or disabled options.
-
# Failure to do this will produce undesired results. Example:
-
# options_from_collection_for_select(@people, 'id', 'name', '1')
-
# Will not select a person with the id of 1 because 1 (an Integer) is not the same as '1' (a string)
-
# options_from_collection_for_select(@people, 'id', 'name', 1)
-
# should produce the desired results.
-
1
def options_from_collection_for_select(collection, value_method, text_method, selected = nil)
-
options = collection.map do |element|
-
[value_for_collection(element, text_method), value_for_collection(element, value_method), option_html_attributes(element)]
-
end
-
selected, disabled = extract_selected_and_disabled(selected)
-
select_deselect = {
-
selected: extract_values_from_collection(collection, value_method, selected),
-
disabled: extract_values_from_collection(collection, value_method, disabled)
-
}
-
-
options_for_select(options, select_deselect)
-
end
-
-
# Returns a string of <tt><option></tt> tags, like <tt>options_from_collection_for_select</tt>, but
-
# groups them by <tt><optgroup></tt> tags based on the object relationships of the arguments.
-
#
-
# Parameters:
-
# * +collection+ - An array of objects representing the <tt><optgroup></tt> tags.
-
# * +group_method+ - The name of a method which, when called on a member of +collection+, returns an
-
# array of child objects representing the <tt><option></tt> tags.
-
# * group_label_method+ - The name of a method which, when called on a member of +collection+, returns a
-
# string to be used as the +label+ attribute for its <tt><optgroup></tt> tag.
-
# * +option_key_method+ - The name of a method which, when called on a child object of a member of
-
# +collection+, returns a value to be used as the +value+ attribute for its <tt><option></tt> tag.
-
# * +option_value_method+ - The name of a method which, when called on a child object of a member of
-
# +collection+, returns a value to be used as the contents of its <tt><option></tt> tag.
-
# * +selected_key+ - A value equal to the +value+ attribute for one of the <tt><option></tt> tags,
-
# which will have the +selected+ attribute set. Corresponds to the return value of one of the calls
-
# to +option_key_method+. If +nil+, no selection is made. Can also be a hash if disabled values are
-
# to be specified.
-
#
-
# Example object structure for use with this method:
-
#
-
# class Continent < ActiveRecord::Base
-
# has_many :countries
-
# # attribs: id, name
-
# end
-
#
-
# class Country < ActiveRecord::Base
-
# belongs_to :continent
-
# # attribs: id, name, continent_id
-
# end
-
#
-
# Sample usage:
-
# option_groups_from_collection_for_select(@continents, :countries, :name, :id, :name, 3)
-
#
-
# Possible output:
-
# <optgroup label="Africa">
-
# <option value="1">Egypt</option>
-
# <option value="4">Rwanda</option>
-
# ...
-
# </optgroup>
-
# <optgroup label="Asia">
-
# <option value="3" selected="selected">China</option>
-
# <option value="12">India</option>
-
# <option value="5">Japan</option>
-
# ...
-
# </optgroup>
-
#
-
# <b>Note:</b> Only the <tt><optgroup></tt> and <tt><option></tt> tags are returned, so you still have to
-
# wrap the output in an appropriate <tt><select></tt> tag.
-
1
def option_groups_from_collection_for_select(collection, group_method, group_label_method, option_key_method, option_value_method, selected_key = nil)
-
collection.map do |group|
-
option_tags = options_from_collection_for_select(
-
group.send(group_method), option_key_method, option_value_method, selected_key)
-
-
content_tag(:optgroup, option_tags, label: group.send(group_label_method))
-
end.join.html_safe
-
end
-
-
# Returns a string of <tt><option></tt> tags, like <tt>options_for_select</tt>, but
-
# wraps them with <tt><optgroup></tt> tags:
-
#
-
# grouped_options = [
-
# ['North America',
-
# [['United States','US'],'Canada']],
-
# ['Europe',
-
# ['Denmark','Germany','France']]
-
# ]
-
# grouped_options_for_select(grouped_options)
-
#
-
# grouped_options = {
-
# 'North America' => [['United States','US'], 'Canada'],
-
# 'Europe' => ['Denmark','Germany','France']
-
# }
-
# grouped_options_for_select(grouped_options)
-
#
-
# Possible output:
-
# <optgroup label="North America">
-
# <option value="US">United States</option>
-
# <option value="Canada">Canada</option>
-
# </optgroup>
-
# <optgroup label="Europe">
-
# <option value="Denmark">Denmark</option>
-
# <option value="Germany">Germany</option>
-
# <option value="France">France</option>
-
# </optgroup>
-
#
-
# Parameters:
-
# * +grouped_options+ - Accepts a nested array or hash of strings. The first value serves as the
-
# <tt><optgroup></tt> label while the second value must be an array of options. The second value can be a
-
# nested array of text-value pairs. See <tt>options_for_select</tt> for more info.
-
# Ex. ["North America",[["United States","US"],["Canada","CA"]]]
-
# * +selected_key+ - A value equal to the +value+ attribute for one of the <tt><option></tt> tags,
-
# which will have the +selected+ attribute set. Note: It is possible for this value to match multiple options
-
# as you might have the same option in multiple groups. Each will then get <tt>selected="selected"</tt>.
-
#
-
# Options:
-
# * <tt>:prompt</tt> - set to true or a prompt string. When the select element doesn't have a value yet, this
-
# prepends an option with a generic prompt - "Please select" - or the given prompt string.
-
# * <tt>:divider</tt> - the divider for the options groups.
-
#
-
# grouped_options = [
-
# [['United States','US'], 'Canada'],
-
# ['Denmark','Germany','France']
-
# ]
-
# grouped_options_for_select(grouped_options, nil, divider: '---------')
-
#
-
# Possible output:
-
# <optgroup label="---------">
-
# <option value="US">United States</option>
-
# <option value="Canada">Canada</option>
-
# </optgroup>
-
# <optgroup label="---------">
-
# <option value="Denmark">Denmark</option>
-
# <option value="Germany">Germany</option>
-
# <option value="France">France</option>
-
# </optgroup>
-
#
-
# <b>Note:</b> Only the <tt><optgroup></tt> and <tt><option></tt> tags are returned, so you still have to
-
# wrap the output in an appropriate <tt><select></tt> tag.
-
1
def grouped_options_for_select(grouped_options, selected_key = nil, options = {})
-
prompt = options[:prompt]
-
divider = options[:divider]
-
-
body = "".html_safe
-
-
if prompt
-
body.safe_concat content_tag(:option, prompt_text(prompt), value: "")
-
end
-
-
grouped_options.each do |container|
-
html_attributes = option_html_attributes(container)
-
-
if divider
-
label = divider
-
else
-
label, container = container
-
end
-
-
html_attributes = { label: label }.merge!(html_attributes)
-
body.safe_concat content_tag(:optgroup, options_for_select(container, selected_key), html_attributes)
-
end
-
-
body
-
end
-
-
# Returns a string of option tags for pretty much any time zone in the
-
# world. Supply a ActiveSupport::TimeZone name as +selected+ to have it
-
# marked as the selected option tag. You can also supply an array of
-
# ActiveSupport::TimeZone objects as +priority_zones+, so that they will
-
# be listed above the rest of the (long) list. (You can use
-
# ActiveSupport::TimeZone.us_zones as a convenience for obtaining a list
-
# of the US time zones, or a Regexp to select the zones of your choice)
-
#
-
# The +selected+ parameter must be either +nil+, or a string that names
-
# a ActiveSupport::TimeZone.
-
#
-
# By default, +model+ is the ActiveSupport::TimeZone constant (which can
-
# be obtained in Active Record as a value object). The only requirement
-
# is that the +model+ parameter be an object that responds to +all+, and
-
# returns an array of objects that represent time zones.
-
#
-
# NOTE: Only the option tags are returned, you have to wrap this call in
-
# a regular HTML select tag.
-
1
def time_zone_options_for_select(selected = nil, priority_zones = nil, model = ::ActiveSupport::TimeZone)
-
zone_options = "".html_safe
-
-
zones = model.all
-
convert_zones = lambda { |list| list.map { |z| [ z.to_s, z.name ] } }
-
-
if priority_zones
-
if priority_zones.is_a?(Regexp)
-
priority_zones = zones.select { |z| z =~ priority_zones }
-
end
-
-
zone_options.safe_concat options_for_select(convert_zones[priority_zones], selected)
-
zone_options.safe_concat content_tag(:option, '-------------', value: '', disabled: true)
-
zone_options.safe_concat "\n"
-
-
zones = zones - priority_zones
-
end
-
-
zone_options.safe_concat options_for_select(convert_zones[zones], selected)
-
end
-
-
# Returns radio button tags for the collection of existing return values
-
# of +method+ for +object+'s class. The value returned from calling
-
# +method+ on the instance +object+ will be selected. If calling +method+
-
# returns +nil+, no selection is made.
-
#
-
# The <tt>:value_method</tt> and <tt>:text_method</tt> parameters are
-
# methods to be called on each member of +collection+. The return values
-
# are used as the +value+ attribute and contents of each radio button tag,
-
# respectively. They can also be any object that responds to +call+, such
-
# as a +proc+, that will be called for each member of the +collection+ to
-
# retrieve the value/text.
-
#
-
# Example object structure for use with this method:
-
# class Post < ActiveRecord::Base
-
# belongs_to :author
-
# end
-
# class Author < ActiveRecord::Base
-
# has_many :posts
-
# def name_with_initial
-
# "#{first_name.first}. #{last_name}"
-
# end
-
# end
-
#
-
# Sample usage (selecting the associated Author for an instance of Post, <tt>@post</tt>):
-
# collection_radio_buttons(:post, :author_id, Author.all, :id, :name_with_initial)
-
#
-
# If <tt>@post.author_id</tt> is already <tt>1</tt>, this would return:
-
# <input id="post_author_id_1" name="post[author_id]" type="radio" value="1" checked="checked" />
-
# <label for="post_author_id_1">D. Heinemeier Hansson</label>
-
# <input id="post_author_id_2" name="post[author_id]" type="radio" value="2" />
-
# <label for="post_author_id_2">D. Thomas</label>
-
# <input id="post_author_id_3" name="post[author_id]" type="radio" value="3" />
-
# <label for="post_author_id_3">M. Clark</label>
-
#
-
# It is also possible to customize the way the elements will be shown by
-
# giving a block to the method:
-
# collection_radio_buttons(:post, :author_id, Author.all, :id, :name_with_initial) do |b|
-
# b.label { b.radio_button }
-
# end
-
#
-
# The argument passed to the block is a special kind of builder for this
-
# collection, which has the ability to generate the label and radio button
-
# for the current item in the collection, with proper text and value.
-
# Using it, you can change the label and radio button display order or
-
# even use the label as wrapper, as in the example above.
-
#
-
# The builder methods <tt>label</tt> and <tt>radio_button</tt> also accept
-
# extra html options:
-
# collection_radio_buttons(:post, :author_id, Author.all, :id, :name_with_initial) do |b|
-
# b.label(class: "radio_button") { b.radio_button(class: "radio_button") }
-
# end
-
#
-
# There are also three special methods available: <tt>object</tt>, <tt>text</tt> and
-
# <tt>value</tt>, which are the current item being rendered, its text and value methods,
-
# respectively. You can use them like this:
-
# collection_radio_buttons(:post, :author_id, Author.all, :id, :name_with_initial) do |b|
-
# b.label(:"data-value" => b.value) { b.radio_button + b.text }
-
# end
-
1
def collection_radio_buttons(object, method, collection, value_method, text_method, options = {}, html_options = {}, &block)
-
Tags::CollectionRadioButtons.new(object, method, self, collection, value_method, text_method, options, html_options).render(&block)
-
end
-
-
# Returns check box tags for the collection of existing return values of
-
# +method+ for +object+'s class. The value returned from calling +method+
-
# on the instance +object+ will be selected. If calling +method+ returns
-
# +nil+, no selection is made.
-
#
-
# The <tt>:value_method</tt> and <tt>:text_method</tt> parameters are
-
# methods to be called on each member of +collection+. The return values
-
# are used as the +value+ attribute and contents of each check box tag,
-
# respectively. They can also be any object that responds to +call+, such
-
# as a +proc+, that will be called for each member of the +collection+ to
-
# retrieve the value/text.
-
#
-
# Example object structure for use with this method:
-
# class Post < ActiveRecord::Base
-
# has_and_belongs_to_many :authors
-
# end
-
# class Author < ActiveRecord::Base
-
# has_and_belongs_to_many :posts
-
# def name_with_initial
-
# "#{first_name.first}. #{last_name}"
-
# end
-
# end
-
#
-
# Sample usage (selecting the associated Author for an instance of Post, <tt>@post</tt>):
-
# collection_check_boxes(:post, :author_ids, Author.all, :id, :name_with_initial)
-
#
-
# If <tt>@post.author_ids</tt> is already <tt>[1]</tt>, this would return:
-
# <input id="post_author_ids_1" name="post[author_ids][]" type="checkbox" value="1" checked="checked" />
-
# <label for="post_author_ids_1">D. Heinemeier Hansson</label>
-
# <input id="post_author_ids_2" name="post[author_ids][]" type="checkbox" value="2" />
-
# <label for="post_author_ids_2">D. Thomas</label>
-
# <input id="post_author_ids_3" name="post[author_ids][]" type="checkbox" value="3" />
-
# <label for="post_author_ids_3">M. Clark</label>
-
# <input name="post[author_ids][]" type="hidden" value="" />
-
#
-
# It is also possible to customize the way the elements will be shown by
-
# giving a block to the method:
-
# collection_check_boxes(:post, :author_ids, Author.all, :id, :name_with_initial) do |b|
-
# b.label { b.check_box }
-
# end
-
#
-
# The argument passed to the block is a special kind of builder for this
-
# collection, which has the ability to generate the label and check box
-
# for the current item in the collection, with proper text and value.
-
# Using it, you can change the label and check box display order or even
-
# use the label as wrapper, as in the example above.
-
#
-
# The builder methods <tt>label</tt> and <tt>check_box</tt> also accept
-
# extra html options:
-
# collection_check_boxes(:post, :author_ids, Author.all, :id, :name_with_initial) do |b|
-
# b.label(class: "check_box") { b.check_box(class: "check_box") }
-
# end
-
#
-
# There are also three special methods available: <tt>object</tt>, <tt>text</tt> and
-
# <tt>value</tt>, which are the current item being rendered, its text and value methods,
-
# respectively. You can use them like this:
-
# collection_check_boxes(:post, :author_ids, Author.all, :id, :name_with_initial) do |b|
-
# b.label(:"data-value" => b.value) { b.check_box + b.text }
-
# end
-
1
def collection_check_boxes(object, method, collection, value_method, text_method, options = {}, html_options = {}, &block)
-
Tags::CollectionCheckBoxes.new(object, method, self, collection, value_method, text_method, options, html_options).render(&block)
-
end
-
-
1
private
-
1
def option_html_attributes(element)
-
if Array === element
-
element.select { |e| Hash === e }.reduce({}, :merge!)
-
else
-
{}
-
end
-
end
-
-
1
def option_text_and_value(option)
-
# Options are [text, value] pairs or strings used for both.
-
if !option.is_a?(String) && option.respond_to?(:first) && option.respond_to?(:last)
-
option = option.reject { |e| Hash === e } if Array === option
-
[option.first, option.last]
-
else
-
[option, option]
-
end
-
end
-
-
1
def option_value_selected?(value, selected)
-
Array(selected).include? value
-
end
-
-
1
def extract_selected_and_disabled(selected)
-
if selected.is_a?(Proc)
-
[selected, nil]
-
else
-
selected = Array.wrap(selected)
-
options = selected.extract_options!.symbolize_keys
-
selected_items = options.fetch(:selected, selected)
-
[selected_items, options[:disabled]]
-
end
-
end
-
-
1
def extract_values_from_collection(collection, value_method, selected)
-
if selected.is_a?(Proc)
-
collection.map do |element|
-
element.send(value_method) if selected.call(element)
-
end.compact
-
else
-
selected
-
end
-
end
-
-
1
def value_for_collection(item, value)
-
value.respond_to?(:call) ? value.call(item) : item.send(value)
-
end
-
-
1
def prompt_text(prompt)
-
prompt.kind_of?(String) ? prompt : I18n.translate('helpers.select.prompt', default: 'Please select')
-
end
-
end
-
-
1
class FormBuilder
-
# Wraps ActionView::Helpers::FormOptionsHelper#select for form builders:
-
#
-
# <%= form_for @post do |f| %>
-
# <%= f.select :person_id, Person.all.collect { |p| [ p.name, p.id ] }, include_blank: true %>
-
# <%= f.submit %>
-
# <% end %>
-
#
-
# Please refer to the documentation of the base helper for details.
-
1
def select(method, choices = nil, options = {}, html_options = {}, &block)
-
@template.select(@object_name, method, choices, objectify_options(options), @default_options.merge(html_options), &block)
-
end
-
-
# Wraps ActionView::Helpers::FormOptionsHelper#collection_select for form builders:
-
#
-
# <%= form_for @post do |f| %>
-
# <%= f.collection_select :person_id, Author.all, :id, :name_with_initial, prompt: true %>
-
# <%= f.submit %>
-
# <% end %>
-
#
-
# Please refer to the documentation of the base helper for details.
-
1
def collection_select(method, collection, value_method, text_method, options = {}, html_options = {})
-
@template.collection_select(@object_name, method, collection, value_method, text_method, objectify_options(options), @default_options.merge(html_options))
-
end
-
-
# Wraps ActionView::Helpers::FormOptionsHelper#grouped_collection_select for form builders:
-
#
-
# <%= form_for @city do |f| %>
-
# <%= f.grouped_collection_select :country_id, @continents, :countries, :name, :id, :name %>
-
# <%= f.submit %>
-
# <% end %>
-
#
-
# Please refer to the documentation of the base helper for details.
-
1
def grouped_collection_select(method, collection, group_method, group_label_method, option_key_method, option_value_method, options = {}, html_options = {})
-
@template.grouped_collection_select(@object_name, method, collection, group_method, group_label_method, option_key_method, option_value_method, objectify_options(options), @default_options.merge(html_options))
-
end
-
-
# Wraps ActionView::Helpers::FormOptionsHelper#time_zone_select for form builders:
-
#
-
# <%= form_for @user do |f| %>
-
# <%= f.time_zone_select :time_zone, nil, include_blank: true %>
-
# <%= f.submit %>
-
# <% end %>
-
#
-
# Please refer to the documentation of the base helper for details.
-
1
def time_zone_select(method, priority_zones = nil, options = {}, html_options = {})
-
@template.time_zone_select(@object_name, method, priority_zones, objectify_options(options), @default_options.merge(html_options))
-
end
-
-
# Wraps ActionView::Helpers::FormOptionsHelper#collection_check_boxes for form builders:
-
#
-
# <%= form_for @post do |f| %>
-
# <%= f.collection_check_boxes :author_ids, Author.all, :id, :name_with_initial %>
-
# <%= f.submit %>
-
# <% end %>
-
#
-
# Please refer to the documentation of the base helper for details.
-
1
def collection_check_boxes(method, collection, value_method, text_method, options = {}, html_options = {}, &block)
-
@template.collection_check_boxes(@object_name, method, collection, value_method, text_method, objectify_options(options), @default_options.merge(html_options), &block)
-
end
-
-
# Wraps ActionView::Helpers::FormOptionsHelper#collection_radio_buttons for form builders:
-
#
-
# <%= form_for @post do |f| %>
-
# <%= f.collection_radio_buttons :author_id, Author.all, :id, :name_with_initial %>
-
# <%= f.submit %>
-
# <% end %>
-
#
-
# Please refer to the documentation of the base helper for details.
-
1
def collection_radio_buttons(method, collection, value_method, text_method, options = {}, html_options = {}, &block)
-
@template.collection_radio_buttons(@object_name, method, collection, value_method, text_method, objectify_options(options), @default_options.merge(html_options), &block)
-
end
-
end
-
end
-
end
-
1
require 'cgi'
-
1
require 'action_view/helpers/tag_helper'
-
1
require 'active_support/core_ext/string/output_safety'
-
1
require 'active_support/core_ext/module/attribute_accessors'
-
-
1
module ActionView
-
# = Action View Form Tag Helpers
-
1
module Helpers
-
# Provides a number of methods for creating form tags that don't rely on an Active Record object assigned to the template like
-
# FormHelper does. Instead, you provide the names and values manually.
-
#
-
# NOTE: The HTML options <tt>disabled</tt>, <tt>readonly</tt>, and <tt>multiple</tt> can all be treated as booleans. So specifying
-
# <tt>disabled: true</tt> will give <tt>disabled="disabled"</tt>.
-
1
module FormTagHelper
-
1
extend ActiveSupport::Concern
-
-
1
include UrlHelper
-
1
include TextHelper
-
-
1
mattr_accessor :embed_authenticity_token_in_remote_forms
-
1
self.embed_authenticity_token_in_remote_forms = false
-
-
# Starts a form tag that points the action to an url configured with <tt>url_for_options</tt> just like
-
# ActionController::Base#url_for. The method for the form defaults to POST.
-
#
-
# ==== Options
-
# * <tt>:multipart</tt> - If set to true, the enctype is set to "multipart/form-data".
-
# * <tt>:method</tt> - The method to use when submitting the form, usually either "get" or "post".
-
# If "patch", "put", "delete", or another verb is used, a hidden input with name <tt>_method</tt>
-
# is added to simulate the verb over post.
-
# * <tt>:authenticity_token</tt> - Authenticity token to use in the form. Use only if you need to
-
# pass custom authenticity token string, or to not add authenticity_token field at all
-
# (by passing <tt>false</tt>). Remote forms may omit the embedded authenticity token
-
# by setting <tt>config.action_view.embed_authenticity_token_in_remote_forms = false</tt>.
-
# This is helpful when you're fragment-caching the form. Remote forms get the
-
# authenticity token from the <tt>meta</tt> tag, so embedding is unnecessary unless you
-
# support browsers without JavaScript.
-
# * A list of parameters to feed to the URL the form will be posted to.
-
# * <tt>:remote</tt> - If set to true, will allow the Unobtrusive JavaScript drivers to control the
-
# submit behavior. By default this behavior is an ajax submit.
-
# * <tt>:enforce_utf8</tt> - If set to false, a hidden input with name utf8 is not output.
-
#
-
# ==== Examples
-
# form_tag('/posts')
-
# # => <form action="/posts" method="post">
-
#
-
# form_tag('/posts/1', method: :put)
-
# # => <form action="/posts/1" method="post"> ... <input name="_method" type="hidden" value="put" /> ...
-
#
-
# form_tag('/upload', multipart: true)
-
# # => <form action="/upload" method="post" enctype="multipart/form-data">
-
#
-
# <%= form_tag('/posts') do -%>
-
# <div><%= submit_tag 'Save' %></div>
-
# <% end -%>
-
# # => <form action="/posts" method="post"><div><input type="submit" name="commit" value="Save" /></div></form>
-
#
-
# <%= form_tag('/posts', remote: true) %>
-
# # => <form action="/posts" method="post" data-remote="true">
-
#
-
# form_tag('http://far.away.com/form', authenticity_token: false)
-
# # form without authenticity token
-
#
-
# form_tag('http://far.away.com/form', authenticity_token: "cf50faa3fe97702ca1ae")
-
# # form with custom authenticity token
-
#
-
1
def form_tag(url_for_options = {}, options = {}, &block)
-
html_options = html_options_for_form(url_for_options, options)
-
if block_given?
-
form_tag_in_block(html_options, &block)
-
else
-
form_tag_html(html_options)
-
end
-
end
-
-
# Creates a dropdown selection box, or if the <tt>:multiple</tt> option is set to true, a multiple
-
# choice selection box.
-
#
-
# Helpers::FormOptions can be used to create common select boxes such as countries, time zones, or
-
# associated records. <tt>option_tags</tt> is a string containing the option tags for the select box.
-
#
-
# ==== Options
-
# * <tt>:multiple</tt> - If set to true the selection will allow multiple choices.
-
# * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
-
# * <tt>:include_blank</tt> - If set to true, an empty option will be created.
-
# * <tt>:prompt</tt> - Create a prompt option with blank value and the text asking user to select something
-
# * Any other key creates standard HTML attributes for the tag.
-
#
-
# ==== Examples
-
# select_tag "people", options_from_collection_for_select(@people, "id", "name")
-
# # <select id="people" name="people"><option value="1">David</option></select>
-
#
-
# select_tag "people", "<option>David</option>".html_safe
-
# # => <select id="people" name="people"><option>David</option></select>
-
#
-
# select_tag "count", "<option>1</option><option>2</option><option>3</option><option>4</option>".html_safe
-
# # => <select id="count" name="count"><option>1</option><option>2</option>
-
# # <option>3</option><option>4</option></select>
-
#
-
# select_tag "colors", "<option>Red</option><option>Green</option><option>Blue</option>".html_safe, multiple: true
-
# # => <select id="colors" multiple="multiple" name="colors[]"><option>Red</option>
-
# # <option>Green</option><option>Blue</option></select>
-
#
-
# select_tag "locations", "<option>Home</option><option selected='selected'>Work</option><option>Out</option>".html_safe
-
# # => <select id="locations" name="locations"><option>Home</option><option selected='selected'>Work</option>
-
# # <option>Out</option></select>
-
#
-
# select_tag "access", "<option>Read</option><option>Write</option>".html_safe, multiple: true, class: 'form_input'
-
# # => <select class="form_input" id="access" multiple="multiple" name="access[]"><option>Read</option>
-
# # <option>Write</option></select>
-
#
-
# select_tag "people", options_from_collection_for_select(@people, "id", "name"), include_blank: true
-
# # => <select id="people" name="people"><option value=""></option><option value="1">David</option></select>
-
#
-
# select_tag "people", options_from_collection_for_select(@people, "id", "name"), prompt: "Select something"
-
# # => <select id="people" name="people"><option value="">Select something</option><option value="1">David</option></select>
-
#
-
# select_tag "destination", "<option>NYC</option><option>Paris</option><option>Rome</option>".html_safe, disabled: true
-
# # => <select disabled="disabled" id="destination" name="destination"><option>NYC</option>
-
# # <option>Paris</option><option>Rome</option></select>
-
#
-
# select_tag "credit_card", options_for_select([ "VISA", "MasterCard" ], "MasterCard")
-
# # => <select id="credit_card" name="credit_card"><option>VISA</option>
-
# # <option selected="selected">MasterCard</option></select>
-
1
def select_tag(name, option_tags = nil, options = {})
-
option_tags ||= ""
-
html_name = (options[:multiple] == true && !name.to_s.ends_with?("[]")) ? "#{name}[]" : name
-
-
if options.delete(:include_blank)
-
option_tags = content_tag(:option, '', :value => '').safe_concat(option_tags)
-
end
-
-
if prompt = options.delete(:prompt)
-
option_tags = content_tag(:option, prompt, :value => '').safe_concat(option_tags)
-
end
-
-
content_tag :select, option_tags, { "name" => html_name, "id" => sanitize_to_id(name) }.update(options.stringify_keys)
-
end
-
-
# Creates a standard text field; use these text fields to input smaller chunks of text like a username
-
# or a search query.
-
#
-
# ==== Options
-
# * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
-
# * <tt>:size</tt> - The number of visible characters that will fit in the input.
-
# * <tt>:maxlength</tt> - The maximum number of characters that the browser will allow the user to enter.
-
# * <tt>:placeholder</tt> - The text contained in the field by default which is removed when the field receives focus.
-
# * Any other key creates standard HTML attributes for the tag.
-
#
-
# ==== Examples
-
# text_field_tag 'name'
-
# # => <input id="name" name="name" type="text" />
-
#
-
# text_field_tag 'query', 'Enter your search query here'
-
# # => <input id="query" name="query" type="text" value="Enter your search query here" />
-
#
-
# text_field_tag 'search', nil, placeholder: 'Enter search term...'
-
# # => <input id="search" name="search" placeholder="Enter search term..." type="text" />
-
#
-
# text_field_tag 'request', nil, class: 'special_input'
-
# # => <input class="special_input" id="request" name="request" type="text" />
-
#
-
# text_field_tag 'address', '', size: 75
-
# # => <input id="address" name="address" size="75" type="text" value="" />
-
#
-
# text_field_tag 'zip', nil, maxlength: 5
-
# # => <input id="zip" maxlength="5" name="zip" type="text" />
-
#
-
# text_field_tag 'payment_amount', '$0.00', disabled: true
-
# # => <input disabled="disabled" id="payment_amount" name="payment_amount" type="text" value="$0.00" />
-
#
-
# text_field_tag 'ip', '0.0.0.0', maxlength: 15, size: 20, class: "ip-input"
-
# # => <input class="ip-input" id="ip" maxlength="15" name="ip" size="20" type="text" value="0.0.0.0" />
-
1
def text_field_tag(name, value = nil, options = {})
-
tag :input, { "type" => "text", "name" => name, "id" => sanitize_to_id(name), "value" => value }.update(options.stringify_keys)
-
end
-
-
# Creates a label element. Accepts a block.
-
#
-
# ==== Options
-
# * Creates standard HTML attributes for the tag.
-
#
-
# ==== Examples
-
# label_tag 'name'
-
# # => <label for="name">Name</label>
-
#
-
# label_tag 'name', 'Your name'
-
# # => <label for="name">Your name</label>
-
#
-
# label_tag 'name', nil, class: 'small_label'
-
# # => <label for="name" class="small_label">Name</label>
-
1
def label_tag(name = nil, content_or_options = nil, options = nil, &block)
-
if block_given? && content_or_options.is_a?(Hash)
-
options = content_or_options = content_or_options.stringify_keys
-
else
-
options ||= {}
-
options = options.stringify_keys
-
end
-
options["for"] = sanitize_to_id(name) unless name.blank? || options.has_key?("for")
-
content_tag :label, content_or_options || name.to_s.humanize, options, &block
-
end
-
-
# Creates a hidden form input field used to transmit data that would be lost due to HTTP's statelessness or
-
# data that should be hidden from the user.
-
#
-
# ==== Options
-
# * Creates standard HTML attributes for the tag.
-
#
-
# ==== Examples
-
# hidden_field_tag 'tags_list'
-
# # => <input id="tags_list" name="tags_list" type="hidden" />
-
#
-
# hidden_field_tag 'token', 'VUBJKB23UIVI1UU1VOBVI@'
-
# # => <input id="token" name="token" type="hidden" value="VUBJKB23UIVI1UU1VOBVI@" />
-
#
-
# hidden_field_tag 'collected_input', '', onchange: "alert('Input collected!')"
-
# # => <input id="collected_input" name="collected_input" onchange="alert('Input collected!')"
-
# # type="hidden" value="" />
-
1
def hidden_field_tag(name, value = nil, options = {})
-
text_field_tag(name, value, options.stringify_keys.update("type" => "hidden"))
-
end
-
-
# Creates a file upload field. If you are using file uploads then you will also need
-
# to set the multipart option for the form tag:
-
#
-
# <%= form_tag '/upload', multipart: true do %>
-
# <label for="file">File to Upload</label> <%= file_field_tag "file" %>
-
# <%= submit_tag %>
-
# <% end %>
-
#
-
# The specified URL will then be passed a File object containing the selected file, or if the field
-
# was left blank, a StringIO object.
-
#
-
# ==== Options
-
# * Creates standard HTML attributes for the tag.
-
# * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
-
# * <tt>:multiple</tt> - If set to true, *in most updated browsers* the user will be allowed to select multiple files.
-
# * <tt>:accept</tt> - If set to one or multiple mime-types, the user will be suggested a filter when choosing a file. You still need to set up model validations.
-
#
-
# ==== Examples
-
# file_field_tag 'attachment'
-
# # => <input id="attachment" name="attachment" type="file" />
-
#
-
# file_field_tag 'avatar', class: 'profile_input'
-
# # => <input class="profile_input" id="avatar" name="avatar" type="file" />
-
#
-
# file_field_tag 'picture', disabled: true
-
# # => <input disabled="disabled" id="picture" name="picture" type="file" />
-
#
-
# file_field_tag 'resume', value: '~/resume.doc'
-
# # => <input id="resume" name="resume" type="file" value="~/resume.doc" />
-
#
-
# file_field_tag 'user_pic', accept: 'image/png,image/gif,image/jpeg'
-
# # => <input accept="image/png,image/gif,image/jpeg" id="user_pic" name="user_pic" type="file" />
-
#
-
# file_field_tag 'file', accept: 'text/html', class: 'upload', value: 'index.html'
-
# # => <input accept="text/html" class="upload" id="file" name="file" type="file" value="index.html" />
-
1
def file_field_tag(name, options = {})
-
text_field_tag(name, nil, options.update("type" => "file"))
-
end
-
-
# Creates a password field, a masked text field that will hide the users input behind a mask character.
-
#
-
# ==== Options
-
# * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
-
# * <tt>:size</tt> - The number of visible characters that will fit in the input.
-
# * <tt>:maxlength</tt> - The maximum number of characters that the browser will allow the user to enter.
-
# * Any other key creates standard HTML attributes for the tag.
-
#
-
# ==== Examples
-
# password_field_tag 'pass'
-
# # => <input id="pass" name="pass" type="password" />
-
#
-
# password_field_tag 'secret', 'Your secret here'
-
# # => <input id="secret" name="secret" type="password" value="Your secret here" />
-
#
-
# password_field_tag 'masked', nil, class: 'masked_input_field'
-
# # => <input class="masked_input_field" id="masked" name="masked" type="password" />
-
#
-
# password_field_tag 'token', '', size: 15
-
# # => <input id="token" name="token" size="15" type="password" value="" />
-
#
-
# password_field_tag 'key', nil, maxlength: 16
-
# # => <input id="key" maxlength="16" name="key" type="password" />
-
#
-
# password_field_tag 'confirm_pass', nil, disabled: true
-
# # => <input disabled="disabled" id="confirm_pass" name="confirm_pass" type="password" />
-
#
-
# password_field_tag 'pin', '1234', maxlength: 4, size: 6, class: "pin_input"
-
# # => <input class="pin_input" id="pin" maxlength="4" name="pin" size="6" type="password" value="1234" />
-
1
def password_field_tag(name = "password", value = nil, options = {})
-
text_field_tag(name, value, options.update("type" => "password"))
-
end
-
-
# Creates a text input area; use a textarea for longer text inputs such as blog posts or descriptions.
-
#
-
# ==== Options
-
# * <tt>:size</tt> - A string specifying the dimensions (columns by rows) of the textarea (e.g., "25x10").
-
# * <tt>:rows</tt> - Specify the number of rows in the textarea
-
# * <tt>:cols</tt> - Specify the number of columns in the textarea
-
# * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
-
# * <tt>:escape</tt> - By default, the contents of the text input are HTML escaped.
-
# If you need unescaped contents, set this to false.
-
# * Any other key creates standard HTML attributes for the tag.
-
#
-
# ==== Examples
-
# text_area_tag 'post'
-
# # => <textarea id="post" name="post"></textarea>
-
#
-
# text_area_tag 'bio', @user.bio
-
# # => <textarea id="bio" name="bio">This is my biography.</textarea>
-
#
-
# text_area_tag 'body', nil, rows: 10, cols: 25
-
# # => <textarea cols="25" id="body" name="body" rows="10"></textarea>
-
#
-
# text_area_tag 'body', nil, size: "25x10"
-
# # => <textarea name="body" id="body" cols="25" rows="10"></textarea>
-
#
-
# text_area_tag 'description', "Description goes here.", disabled: true
-
# # => <textarea disabled="disabled" id="description" name="description">Description goes here.</textarea>
-
#
-
# text_area_tag 'comment', nil, class: 'comment_input'
-
# # => <textarea class="comment_input" id="comment" name="comment"></textarea>
-
1
def text_area_tag(name, content = nil, options = {})
-
options = options.stringify_keys
-
-
if size = options.delete("size")
-
options["cols"], options["rows"] = size.split("x") if size.respond_to?(:split)
-
end
-
-
escape = options.delete("escape") { true }
-
content = ERB::Util.html_escape(content) if escape
-
-
content_tag :textarea, content.to_s.html_safe, { "name" => name, "id" => sanitize_to_id(name) }.update(options)
-
end
-
-
# Creates a check box form input tag.
-
#
-
# ==== Options
-
# * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
-
# * Any other key creates standard HTML options for the tag.
-
#
-
# ==== Examples
-
# check_box_tag 'accept'
-
# # => <input id="accept" name="accept" type="checkbox" value="1" />
-
#
-
# check_box_tag 'rock', 'rock music'
-
# # => <input id="rock" name="rock" type="checkbox" value="rock music" />
-
#
-
# check_box_tag 'receive_email', 'yes', true
-
# # => <input checked="checked" id="receive_email" name="receive_email" type="checkbox" value="yes" />
-
#
-
# check_box_tag 'tos', 'yes', false, class: 'accept_tos'
-
# # => <input class="accept_tos" id="tos" name="tos" type="checkbox" value="yes" />
-
#
-
# check_box_tag 'eula', 'accepted', false, disabled: true
-
# # => <input disabled="disabled" id="eula" name="eula" type="checkbox" value="accepted" />
-
1
def check_box_tag(name, value = "1", checked = false, options = {})
-
html_options = { "type" => "checkbox", "name" => name, "id" => sanitize_to_id(name), "value" => value }.update(options.stringify_keys)
-
html_options["checked"] = "checked" if checked
-
tag :input, html_options
-
end
-
-
# Creates a radio button; use groups of radio buttons named the same to allow users to
-
# select from a group of options.
-
#
-
# ==== Options
-
# * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
-
# * Any other key creates standard HTML options for the tag.
-
#
-
# ==== Examples
-
# radio_button_tag 'gender', 'male'
-
# # => <input id="gender_male" name="gender" type="radio" value="male" />
-
#
-
# radio_button_tag 'receive_updates', 'no', true
-
# # => <input checked="checked" id="receive_updates_no" name="receive_updates" type="radio" value="no" />
-
#
-
# radio_button_tag 'time_slot', "3:00 p.m.", false, disabled: true
-
# # => <input disabled="disabled" id="time_slot_300_pm" name="time_slot" type="radio" value="3:00 p.m." />
-
#
-
# radio_button_tag 'color', "green", true, class: "color_input"
-
# # => <input checked="checked" class="color_input" id="color_green" name="color" type="radio" value="green" />
-
1
def radio_button_tag(name, value, checked = false, options = {})
-
html_options = { "type" => "radio", "name" => name, "id" => "#{sanitize_to_id(name)}_#{sanitize_to_id(value)}", "value" => value }.update(options.stringify_keys)
-
html_options["checked"] = "checked" if checked
-
tag :input, html_options
-
end
-
-
# Creates a submit button with the text <tt>value</tt> as the caption.
-
#
-
# ==== Options
-
# * <tt>:data</tt> - This option can be used to add custom data attributes.
-
# * <tt>:disabled</tt> - If true, the user will not be able to use this input.
-
# * Any other key creates standard HTML options for the tag.
-
#
-
# ==== Data attributes
-
#
-
# * <tt>confirm: 'question?'</tt> - If present the unobtrusive JavaScript
-
# drivers will provide a prompt with the question specified. If the user accepts,
-
# the form is processed normally, otherwise no action is taken.
-
# * <tt>:disable_with</tt> - Value of this parameter will be used as the value for a
-
# disabled version of the submit button when the form is submitted. This feature is
-
# provided by the unobtrusive JavaScript driver.
-
#
-
# ==== Examples
-
# submit_tag
-
# # => <input name="commit" type="submit" value="Save changes" />
-
#
-
# submit_tag "Edit this article"
-
# # => <input name="commit" type="submit" value="Edit this article" />
-
#
-
# submit_tag "Save edits", disabled: true
-
# # => <input disabled="disabled" name="commit" type="submit" value="Save edits" />
-
#
-
# submit_tag "Complete sale", data: { disable_with: "Please wait..." }
-
# # => <input name="commit" data-disable-with="Please wait..." type="submit" value="Complete sale" />
-
#
-
# submit_tag nil, class: "form_submit"
-
# # => <input class="form_submit" name="commit" type="submit" />
-
#
-
# submit_tag "Edit", class: "edit_button"
-
# # => <input class="edit_button" name="commit" type="submit" value="Edit" />
-
#
-
# submit_tag "Save", data: { confirm: "Are you sure?" }
-
# # => <input name='commit' type='submit' value='Save' data-confirm="Are you sure?" />
-
#
-
1
def submit_tag(value = "Save changes", options = {})
-
options = options.stringify_keys
-
-
tag :input, { "type" => "submit", "name" => "commit", "value" => value }.update(options)
-
end
-
-
# Creates a button element that defines a <tt>submit</tt> button,
-
# <tt>reset</tt>button or a generic button which can be used in
-
# JavaScript, for example. You can use the button tag as a regular
-
# submit tag but it isn't supported in legacy browsers. However,
-
# the button tag allows richer labels such as images and emphasis,
-
# so this helper will also accept a block.
-
#
-
# ==== Options
-
# * <tt>:data</tt> - This option can be used to add custom data attributes.
-
# * <tt>:disabled</tt> - If true, the user will not be able to
-
# use this input.
-
# * Any other key creates standard HTML options for the tag.
-
#
-
# ==== Data attributes
-
#
-
# * <tt>confirm: 'question?'</tt> - If present, the
-
# unobtrusive JavaScript drivers will provide a prompt with
-
# the question specified. If the user accepts, the form is
-
# processed normally, otherwise no action is taken.
-
# * <tt>:disable_with</tt> - Value of this parameter will be
-
# used as the value for a disabled version of the submit
-
# button when the form is submitted. This feature is provided
-
# by the unobtrusive JavaScript driver.
-
#
-
# ==== Examples
-
# button_tag
-
# # => <button name="button" type="submit">Button</button>
-
#
-
# button_tag(type: 'button') do
-
# content_tag(:strong, 'Ask me!')
-
# end
-
# # => <button name="button" type="button">
-
# # <strong>Ask me!</strong>
-
# # </button>
-
#
-
# button_tag "Checkout", data: { disable_with: "Please wait..." }
-
# # => <button data-disable-with="Please wait..." name="button" type="submit">Checkout</button>
-
#
-
1
def button_tag(content_or_options = nil, options = nil, &block)
-
if content_or_options.is_a? Hash
-
options = content_or_options
-
else
-
options ||= {}
-
end
-
-
options = { 'name' => 'button', 'type' => 'submit' }.merge!(options.stringify_keys)
-
-
if block_given?
-
content_tag :button, options, &block
-
else
-
content_tag :button, content_or_options || 'Button', options
-
end
-
end
-
-
# Displays an image which when clicked will submit the form.
-
#
-
# <tt>source</tt> is passed to AssetTagHelper#path_to_image
-
#
-
# ==== Options
-
# * <tt>:data</tt> - This option can be used to add custom data attributes.
-
# * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
-
# * Any other key creates standard HTML options for the tag.
-
#
-
# ==== Data attributes
-
#
-
# * <tt>confirm: 'question?'</tt> - This will add a JavaScript confirm
-
# prompt with the question specified. If the user accepts, the form is
-
# processed normally, otherwise no action is taken.
-
#
-
# ==== Examples
-
# image_submit_tag("login.png")
-
# # => <input alt="Login" src="/images/login.png" type="image" />
-
#
-
# image_submit_tag("purchase.png", disabled: true)
-
# # => <input alt="Purchase" disabled="disabled" src="/images/purchase.png" type="image" />
-
#
-
# image_submit_tag("search.png", class: 'search_button', alt: 'Find')
-
# # => <input alt="Find" class="search_button" src="/images/search.png" type="image" />
-
#
-
# image_submit_tag("agree.png", disabled: true, class: "agree_disagree_button")
-
# # => <input alt="Agree" class="agree_disagree_button" disabled="disabled" src="/images/agree.png" type="image" />
-
#
-
# image_submit_tag("save.png", data: { confirm: "Are you sure?" })
-
# # => <input alt="Save" src="/images/save.png" data-confirm="Are you sure?" type="image" />
-
1
def image_submit_tag(source, options = {})
-
options = options.stringify_keys
-
tag :input, { "alt" => image_alt(source), "type" => "image", "src" => path_to_image(source) }.update(options)
-
end
-
-
# Creates a field set for grouping HTML form elements.
-
#
-
# <tt>legend</tt> will become the fieldset's title (optional as per W3C).
-
# <tt>options</tt> accept the same values as tag.
-
#
-
# ==== Examples
-
# <%= field_set_tag do %>
-
# <p><%= text_field_tag 'name' %></p>
-
# <% end %>
-
# # => <fieldset><p><input id="name" name="name" type="text" /></p></fieldset>
-
#
-
# <%= field_set_tag 'Your details' do %>
-
# <p><%= text_field_tag 'name' %></p>
-
# <% end %>
-
# # => <fieldset><legend>Your details</legend><p><input id="name" name="name" type="text" /></p></fieldset>
-
#
-
# <%= field_set_tag nil, class: 'format' do %>
-
# <p><%= text_field_tag 'name' %></p>
-
# <% end %>
-
# # => <fieldset class="format"><p><input id="name" name="name" type="text" /></p></fieldset>
-
1
def field_set_tag(legend = nil, options = nil, &block)
-
output = tag(:fieldset, options, true)
-
output.safe_concat(content_tag(:legend, legend)) unless legend.blank?
-
output.concat(capture(&block)) if block_given?
-
output.safe_concat("</fieldset>")
-
end
-
-
# Creates a text field of type "color".
-
#
-
# ==== Options
-
# * Accepts the same options as text_field_tag.
-
1
def color_field_tag(name, value = nil, options = {})
-
text_field_tag(name, value, options.stringify_keys.update("type" => "color"))
-
end
-
-
# Creates a text field of type "search".
-
#
-
# ==== Options
-
# * Accepts the same options as text_field_tag.
-
1
def search_field_tag(name, value = nil, options = {})
-
text_field_tag(name, value, options.stringify_keys.update("type" => "search"))
-
end
-
-
# Creates a text field of type "tel".
-
#
-
# ==== Options
-
# * Accepts the same options as text_field_tag.
-
1
def telephone_field_tag(name, value = nil, options = {})
-
text_field_tag(name, value, options.stringify_keys.update("type" => "tel"))
-
end
-
1
alias phone_field_tag telephone_field_tag
-
-
# Creates a text field of type "date".
-
#
-
# ==== Options
-
# * Accepts the same options as text_field_tag.
-
1
def date_field_tag(name, value = nil, options = {})
-
text_field_tag(name, value, options.stringify_keys.update("type" => "date"))
-
end
-
-
# Creates a text field of type "time".
-
#
-
# === Options
-
# * <tt>:min</tt> - The minimum acceptable value.
-
# * <tt>:max</tt> - The maximum acceptable value.
-
# * <tt>:step</tt> - The acceptable value granularity.
-
# * Otherwise accepts the same options as text_field_tag.
-
1
def time_field_tag(name, value = nil, options = {})
-
text_field_tag(name, value, options.stringify_keys.update("type" => "time"))
-
end
-
-
# Creates a text field of type "datetime".
-
#
-
# === Options
-
# * <tt>:min</tt> - The minimum acceptable value.
-
# * <tt>:max</tt> - The maximum acceptable value.
-
# * <tt>:step</tt> - The acceptable value granularity.
-
# * Otherwise accepts the same options as text_field_tag.
-
1
def datetime_field_tag(name, value = nil, options = {})
-
text_field_tag(name, value, options.stringify_keys.update("type" => "datetime"))
-
end
-
-
# Creates a text field of type "datetime-local".
-
#
-
# === Options
-
# * <tt>:min</tt> - The minimum acceptable value.
-
# * <tt>:max</tt> - The maximum acceptable value.
-
# * <tt>:step</tt> - The acceptable value granularity.
-
# * Otherwise accepts the same options as text_field_tag.
-
1
def datetime_local_field_tag(name, value = nil, options = {})
-
text_field_tag(name, value, options.stringify_keys.update("type" => "datetime-local"))
-
end
-
-
# Creates a text field of type "month".
-
#
-
# === Options
-
# * <tt>:min</tt> - The minimum acceptable value.
-
# * <tt>:max</tt> - The maximum acceptable value.
-
# * <tt>:step</tt> - The acceptable value granularity.
-
# * Otherwise accepts the same options as text_field_tag.
-
1
def month_field_tag(name, value = nil, options = {})
-
text_field_tag(name, value, options.stringify_keys.update("type" => "month"))
-
end
-
-
# Creates a text field of type "week".
-
#
-
# === Options
-
# * <tt>:min</tt> - The minimum acceptable value.
-
# * <tt>:max</tt> - The maximum acceptable value.
-
# * <tt>:step</tt> - The acceptable value granularity.
-
# * Otherwise accepts the same options as text_field_tag.
-
1
def week_field_tag(name, value = nil, options = {})
-
text_field_tag(name, value, options.stringify_keys.update("type" => "week"))
-
end
-
-
# Creates a text field of type "url".
-
#
-
# ==== Options
-
# * Accepts the same options as text_field_tag.
-
1
def url_field_tag(name, value = nil, options = {})
-
text_field_tag(name, value, options.stringify_keys.update("type" => "url"))
-
end
-
-
# Creates a text field of type "email".
-
#
-
# ==== Options
-
# * Accepts the same options as text_field_tag.
-
1
def email_field_tag(name, value = nil, options = {})
-
text_field_tag(name, value, options.stringify_keys.update("type" => "email"))
-
end
-
-
# Creates a number field.
-
#
-
# ==== Options
-
# * <tt>:min</tt> - The minimum acceptable value.
-
# * <tt>:max</tt> - The maximum acceptable value.
-
# * <tt>:in</tt> - A range specifying the <tt>:min</tt> and
-
# <tt>:max</tt> values.
-
# * <tt>:step</tt> - The acceptable value granularity.
-
# * Otherwise accepts the same options as text_field_tag.
-
#
-
# ==== Examples
-
# number_field_tag 'quantity', nil, in: 1...10
-
# # => <input id="quantity" name="quantity" min="1" max="9" type="number" />
-
1
def number_field_tag(name, value = nil, options = {})
-
options = options.stringify_keys
-
options["type"] ||= "number"
-
if range = options.delete("in") || options.delete("within")
-
options.update("min" => range.min, "max" => range.max)
-
end
-
text_field_tag(name, value, options)
-
end
-
-
# Creates a range form element.
-
#
-
# ==== Options
-
# * Accepts the same options as number_field_tag.
-
1
def range_field_tag(name, value = nil, options = {})
-
number_field_tag(name, value, options.stringify_keys.update("type" => "range"))
-
end
-
-
# Creates the hidden UTF8 enforcer tag. Override this method in a helper
-
# to customize the tag.
-
1
def utf8_enforcer_tag
-
tag(:input, :type => "hidden", :name => "utf8", :value => "✓".html_safe)
-
end
-
-
1
private
-
1
def html_options_for_form(url_for_options, options)
-
options.stringify_keys.tap do |html_options|
-
html_options["enctype"] = "multipart/form-data" if html_options.delete("multipart")
-
# The following URL is unescaped, this is just a hash of options, and it is the
-
# responsibility of the caller to escape all the values.
-
html_options["action"] = url_for(url_for_options)
-
html_options["accept-charset"] = "UTF-8"
-
-
html_options["data-remote"] = true if html_options.delete("remote")
-
-
if html_options["data-remote"] &&
-
!embed_authenticity_token_in_remote_forms &&
-
html_options["authenticity_token"].blank?
-
# The authenticity token is taken from the meta tag in this case
-
html_options["authenticity_token"] = false
-
elsif html_options["authenticity_token"] == true
-
# Include the default authenticity_token, which is only generated when its set to nil,
-
# but we needed the true value to override the default of no authenticity_token on data-remote.
-
html_options["authenticity_token"] = nil
-
end
-
end
-
end
-
-
1
def extra_tags_for_form(html_options)
-
authenticity_token = html_options.delete("authenticity_token")
-
method = html_options.delete("method").to_s
-
-
method_tag = case method
-
when /^get$/i # must be case-insensitive, but can't use downcase as might be nil
-
html_options["method"] = "get"
-
''
-
when /^post$/i, "", nil
-
html_options["method"] = "post"
-
token_tag(authenticity_token)
-
else
-
html_options["method"] = "post"
-
method_tag(method) + token_tag(authenticity_token)
-
end
-
-
enforce_utf8 = html_options.delete("enforce_utf8") { true }
-
tags = (enforce_utf8 ? utf8_enforcer_tag : ''.html_safe) << method_tag
-
content_tag(:div, tags, :style => 'display:none')
-
end
-
-
1
def form_tag_html(html_options)
-
extra_tags = extra_tags_for_form(html_options)
-
tag(:form, html_options, true) + extra_tags
-
end
-
-
1
def form_tag_in_block(html_options, &block)
-
content = capture(&block)
-
output = form_tag_html(html_options)
-
output << content
-
output.safe_concat("</form>")
-
end
-
-
# see http://www.w3.org/TR/html4/types.html#type-name
-
1
def sanitize_to_id(name)
-
name.to_s.delete(']').gsub(/[^-a-zA-Z0-9:.]/, "_")
-
end
-
end
-
end
-
end
-
1
require 'action_view/helpers/tag_helper'
-
-
1
module ActionView
-
1
module Helpers
-
1
module JavaScriptHelper
-
1
JS_ESCAPE_MAP = {
-
'\\' => '\\\\',
-
'</' => '<\/',
-
"\r\n" => '\n',
-
"\n" => '\n',
-
"\r" => '\n',
-
'"' => '\\"',
-
"'" => "\\'"
-
}
-
-
1
JS_ESCAPE_MAP["\342\200\250".force_encoding(Encoding::UTF_8).encode!] = '
'
-
1
JS_ESCAPE_MAP["\342\200\251".force_encoding(Encoding::UTF_8).encode!] = '
'
-
-
# Escapes carriage returns and single and double quotes for JavaScript segments.
-
#
-
# Also available through the alias j(). This is particularly helpful in JavaScript
-
# responses, like:
-
#
-
# $('some_element').replaceWith('<%=j render 'some/element_template' %>');
-
1
def escape_javascript(javascript)
-
if javascript
-
result = javascript.gsub(/(\\|<\/|\r\n|\342\200\250|\342\200\251|[\n\r"'])/u) {|match| JS_ESCAPE_MAP[match] }
-
javascript.html_safe? ? result.html_safe : result
-
else
-
''
-
end
-
end
-
-
1
alias_method :j, :escape_javascript
-
-
# Returns a JavaScript tag with the +content+ inside. Example:
-
# javascript_tag "alert('All is good')"
-
#
-
# Returns:
-
# <script>
-
# //<![CDATA[
-
# alert('All is good')
-
# //]]>
-
# </script>
-
#
-
# +html_options+ may be a hash of attributes for the <tt>\<script></tt>
-
# tag.
-
#
-
# javascript_tag "alert('All is good')", defer: 'defer'
-
# # => <script defer="defer">alert('All is good')</script>
-
#
-
# Instead of passing the content as an argument, you can also use a block
-
# in which case, you pass your +html_options+ as the first parameter.
-
#
-
# <%= javascript_tag defer: 'defer' do -%>
-
# alert('All is good')
-
# <% end -%>
-
1
def javascript_tag(content_or_options_with_block = nil, html_options = {}, &block)
-
content =
-
if block_given?
-
html_options = content_or_options_with_block if content_or_options_with_block.is_a?(Hash)
-
capture(&block)
-
else
-
content_or_options_with_block
-
end
-
-
content_tag(:script, javascript_cdata_section(content), html_options)
-
end
-
-
1
def javascript_cdata_section(content) #:nodoc:
-
"\n//#{cdata_section("\n#{content}\n//")}\n".html_safe
-
end
-
end
-
end
-
end
-
# encoding: utf-8
-
-
1
require 'active_support/core_ext/hash/keys'
-
1
require 'active_support/core_ext/string/output_safety'
-
1
require 'active_support/number_helper'
-
-
1
module ActionView
-
# = Action View Number Helpers
-
1
module Helpers #:nodoc:
-
-
# Provides methods for converting numbers into formatted strings.
-
# Methods are provided for phone numbers, currency, percentage,
-
# precision, positional notation, file size and pretty printing.
-
#
-
# Most methods expect a +number+ argument, and will return it
-
# unchanged if can't be converted into a valid number.
-
1
module NumberHelper
-
-
# Raised when argument +number+ param given to the helpers is invalid and
-
# the option :raise is set to +true+.
-
1
class InvalidNumberError < StandardError
-
1
attr_accessor :number
-
1
def initialize(number)
-
@number = number
-
end
-
end
-
-
# Formats a +number+ into a US phone number (e.g., (555)
-
# 123-9876). You can customize the format in the +options+ hash.
-
#
-
# ==== Options
-
#
-
# * <tt>:area_code</tt> - Adds parentheses around the area code.
-
# * <tt>:delimiter</tt> - Specifies the delimiter to use
-
# (defaults to "-").
-
# * <tt>:extension</tt> - Specifies an extension to add to the
-
# end of the generated number.
-
# * <tt>:country_code</tt> - Sets the country code for the phone
-
# number.
-
# * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
-
# the argument is invalid.
-
#
-
# ==== Examples
-
#
-
# number_to_phone(5551234) # => 555-1234
-
# number_to_phone("5551234") # => 555-1234
-
# number_to_phone(1235551234) # => 123-555-1234
-
# number_to_phone(1235551234, area_code: true) # => (123) 555-1234
-
# number_to_phone(1235551234, delimiter: " ") # => 123 555 1234
-
# number_to_phone(1235551234, area_code: true, extension: 555) # => (123) 555-1234 x 555
-
# number_to_phone(1235551234, country_code: 1) # => +1-123-555-1234
-
# number_to_phone("123a456") # => 123a456
-
# number_to_phone("1234a567", raise: true) # => InvalidNumberError
-
#
-
# number_to_phone(1235551234, country_code: 1, extension: 1343, delimiter: ".")
-
# # => +1.123.555.1234 x 1343
-
1
def number_to_phone(number, options = {})
-
return unless number
-
options = options.symbolize_keys
-
-
parse_float(number, true) if options.delete(:raise)
-
ERB::Util.html_escape(ActiveSupport::NumberHelper.number_to_phone(number, options))
-
end
-
-
# Formats a +number+ into a currency string (e.g., $13.65). You
-
# can customize the format in the +options+ hash.
-
#
-
# ==== Options
-
#
-
# * <tt>:locale</tt> - Sets the locale to be used for formatting
-
# (defaults to current locale).
-
# * <tt>:precision</tt> - Sets the level of precision (defaults
-
# to 2).
-
# * <tt>:unit</tt> - Sets the denomination of the currency
-
# (defaults to "$").
-
# * <tt>:separator</tt> - Sets the separator between the units
-
# (defaults to ".").
-
# * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
-
# to ",").
-
# * <tt>:format</tt> - Sets the format for non-negative numbers
-
# (defaults to "%u%n"). Fields are <tt>%u</tt> for the
-
# currency, and <tt>%n</tt> for the number.
-
# * <tt>:negative_format</tt> - Sets the format for negative
-
# numbers (defaults to prepending an hyphen to the formatted
-
# number given by <tt>:format</tt>). Accepts the same fields
-
# than <tt>:format</tt>, except <tt>%n</tt> is here the
-
# absolute value of the number.
-
# * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
-
# the argument is invalid.
-
#
-
# ==== Examples
-
#
-
# number_to_currency(1234567890.50) # => $1,234,567,890.50
-
# number_to_currency(1234567890.506) # => $1,234,567,890.51
-
# number_to_currency(1234567890.506, precision: 3) # => $1,234,567,890.506
-
# number_to_currency(1234567890.506, locale: :fr) # => 1 234 567 890,51 €
-
# number_to_currency("123a456") # => $123a456
-
#
-
# number_to_currency("123a456", raise: true) # => InvalidNumberError
-
#
-
# number_to_currency(-1234567890.50, negative_format: "(%u%n)")
-
# # => ($1,234,567,890.50)
-
# number_to_currency(1234567890.50, unit: "R$", separator: ",", delimiter: "")
-
# # => R$1234567890,50
-
# number_to_currency(1234567890.50, unit: "R$", separator: ",", delimiter: "", format: "%n %u")
-
# # => 1234567890,50 R$
-
1
def number_to_currency(number, options = {})
-
delegate_number_helper_method(:number_to_currency, number, options)
-
end
-
-
# Formats a +number+ as a percentage string (e.g., 65%). You can
-
# customize the format in the +options+ hash.
-
#
-
# ==== Options
-
#
-
# * <tt>:locale</tt> - Sets the locale to be used for formatting
-
# (defaults to current locale).
-
# * <tt>:precision</tt> - Sets the precision of the number
-
# (defaults to 3).
-
# * <tt>:significant</tt> - If +true+, precision will be the #
-
# of significant_digits. If +false+, the # of fractional
-
# digits (defaults to +false+).
-
# * <tt>:separator</tt> - Sets the separator between the
-
# fractional and integer digits (defaults to ".").
-
# * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
-
# to "").
-
# * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
-
# insignificant zeros after the decimal separator (defaults to
-
# +false+).
-
# * <tt>:format</tt> - Specifies the format of the percentage
-
# string The number field is <tt>%n</tt> (defaults to "%n%").
-
# * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
-
# the argument is invalid.
-
#
-
# ==== Examples
-
#
-
# number_to_percentage(100) # => 100.000%
-
# number_to_percentage("98") # => 98.000%
-
# number_to_percentage(100, precision: 0) # => 100%
-
# number_to_percentage(1000, delimiter: '.', separator: ',') # => 1.000,000%
-
# number_to_percentage(302.24398923423, precision: 5) # => 302.24399%
-
# number_to_percentage(1000, locale: :fr) # => 1 000,000%
-
# number_to_percentage("98a") # => 98a%
-
# number_to_percentage(100, format: "%n %") # => 100 %
-
#
-
# number_to_percentage("98a", raise: true) # => InvalidNumberError
-
1
def number_to_percentage(number, options = {})
-
delegate_number_helper_method(:number_to_percentage, number, options)
-
end
-
-
# Formats a +number+ with grouped thousands using +delimiter+
-
# (e.g., 12,324). You can customize the format in the +options+
-
# hash.
-
#
-
# ==== Options
-
#
-
# * <tt>:locale</tt> - Sets the locale to be used for formatting
-
# (defaults to current locale).
-
# * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
-
# to ",").
-
# * <tt>:separator</tt> - Sets the separator between the
-
# fractional and integer digits (defaults to ".").
-
# * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
-
# the argument is invalid.
-
#
-
# ==== Examples
-
#
-
# number_with_delimiter(12345678) # => 12,345,678
-
# number_with_delimiter("123456") # => 123,456
-
# number_with_delimiter(12345678.05) # => 12,345,678.05
-
# number_with_delimiter(12345678, delimiter: ".") # => 12.345.678
-
# number_with_delimiter(12345678, delimiter: ",") # => 12,345,678
-
# number_with_delimiter(12345678.05, separator: " ") # => 12,345,678 05
-
# number_with_delimiter(12345678.05, locale: :fr) # => 12 345 678,05
-
# number_with_delimiter("112a") # => 112a
-
# number_with_delimiter(98765432.98, delimiter: " ", separator: ",")
-
# # => 98 765 432,98
-
#
-
# number_with_delimiter("112a", raise: true) # => raise InvalidNumberError
-
1
def number_with_delimiter(number, options = {})
-
delegate_number_helper_method(:number_to_delimited, number, options)
-
end
-
-
# Formats a +number+ with the specified level of
-
# <tt>:precision</tt> (e.g., 112.32 has a precision of 2 if
-
# +:significant+ is +false+, and 5 if +:significant+ is +true+).
-
# You can customize the format in the +options+ hash.
-
#
-
# ==== Options
-
#
-
# * <tt>:locale</tt> - Sets the locale to be used for formatting
-
# (defaults to current locale).
-
# * <tt>:precision</tt> - Sets the precision of the number
-
# (defaults to 3).
-
# * <tt>:significant</tt> - If +true+, precision will be the #
-
# of significant_digits. If +false+, the # of fractional
-
# digits (defaults to +false+).
-
# * <tt>:separator</tt> - Sets the separator between the
-
# fractional and integer digits (defaults to ".").
-
# * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
-
# to "").
-
# * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
-
# insignificant zeros after the decimal separator (defaults to
-
# +false+).
-
# * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
-
# the argument is invalid.
-
#
-
# ==== Examples
-
#
-
# number_with_precision(111.2345) # => 111.235
-
# number_with_precision(111.2345, precision: 2) # => 111.23
-
# number_with_precision(13, precision: 5) # => 13.00000
-
# number_with_precision(389.32314, precision: 0) # => 389
-
# number_with_precision(111.2345, significant: true) # => 111
-
# number_with_precision(111.2345, precision: 1, significant: true) # => 100
-
# number_with_precision(13, precision: 5, significant: true) # => 13.000
-
# number_with_precision(111.234, locale: :fr) # => 111,234
-
#
-
# number_with_precision(13, precision: 5, significant: true, strip_insignificant_zeros: true)
-
# # => 13
-
#
-
# number_with_precision(389.32314, precision: 4, significant: true) # => 389.3
-
# number_with_precision(1111.2345, precision: 2, separator: ',', delimiter: '.')
-
# # => 1.111,23
-
1
def number_with_precision(number, options = {})
-
delegate_number_helper_method(:number_to_rounded, number, options)
-
end
-
-
# Formats the bytes in +number+ into a more understandable
-
# representation (e.g., giving it 1500 yields 1.5 KB). This
-
# method is useful for reporting file sizes to users. You can
-
# customize the format in the +options+ hash.
-
#
-
# See <tt>number_to_human</tt> if you want to pretty-print a
-
# generic number.
-
#
-
# ==== Options
-
#
-
# * <tt>:locale</tt> - Sets the locale to be used for formatting
-
# (defaults to current locale).
-
# * <tt>:precision</tt> - Sets the precision of the number
-
# (defaults to 3).
-
# * <tt>:significant</tt> - If +true+, precision will be the #
-
# of significant_digits. If +false+, the # of fractional
-
# digits (defaults to +true+)
-
# * <tt>:separator</tt> - Sets the separator between the
-
# fractional and integer digits (defaults to ".").
-
# * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
-
# to "").
-
# * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
-
# insignificant zeros after the decimal separator (defaults to
-
# +true+)
-
# * <tt>:prefix</tt> - If +:si+ formats the number using the SI
-
# prefix (defaults to :binary)
-
# * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
-
# the argument is invalid.
-
#
-
# ==== Examples
-
#
-
# number_to_human_size(123) # => 123 Bytes
-
# number_to_human_size(1234) # => 1.21 KB
-
# number_to_human_size(12345) # => 12.1 KB
-
# number_to_human_size(1234567) # => 1.18 MB
-
# number_to_human_size(1234567890) # => 1.15 GB
-
# number_to_human_size(1234567890123) # => 1.12 TB
-
# number_to_human_size(1234567, precision: 2) # => 1.2 MB
-
# number_to_human_size(483989, precision: 2) # => 470 KB
-
# number_to_human_size(1234567, precision: 2, separator: ',') # => 1,2 MB
-
#
-
# Non-significant zeros after the fractional separator are
-
# stripped out by default (set
-
# <tt>:strip_insignificant_zeros</tt> to +false+ to change
-
# that):
-
#
-
# number_to_human_size(1234567890123, precision: 5) # => "1.1229 TB"
-
# number_to_human_size(524288000, precision: 5) # => "500 MB"
-
1
def number_to_human_size(number, options = {})
-
delegate_number_helper_method(:number_to_human_size, number, options)
-
end
-
-
# Pretty prints (formats and approximates) a number in a way it
-
# is more readable by humans (eg.: 1200000000 becomes "1.2
-
# Billion"). This is useful for numbers that can get very large
-
# (and too hard to read).
-
#
-
# See <tt>number_to_human_size</tt> if you want to print a file
-
# size.
-
#
-
# You can also define you own unit-quantifier names if you want
-
# to use other decimal units (eg.: 1500 becomes "1.5
-
# kilometers", 0.150 becomes "150 milliliters", etc). You may
-
# define a wide range of unit quantifiers, even fractional ones
-
# (centi, deci, mili, etc).
-
#
-
# ==== Options
-
#
-
# * <tt>:locale</tt> - Sets the locale to be used for formatting
-
# (defaults to current locale).
-
# * <tt>:precision</tt> - Sets the precision of the number
-
# (defaults to 3).
-
# * <tt>:significant</tt> - If +true+, precision will be the #
-
# of significant_digits. If +false+, the # of fractional
-
# digits (defaults to +true+)
-
# * <tt>:separator</tt> - Sets the separator between the
-
# fractional and integer digits (defaults to ".").
-
# * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
-
# to "").
-
# * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
-
# insignificant zeros after the decimal separator (defaults to
-
# +true+)
-
# * <tt>:units</tt> - A Hash of unit quantifier names. Or a
-
# string containing an i18n scope where to find this hash. It
-
# might have the following keys:
-
# * *integers*: <tt>:unit</tt>, <tt>:ten</tt>,
-
# *<tt>:hundred</tt>, <tt>:thousand</tt>, <tt>:million</tt>,
-
# *<tt>:billion</tt>, <tt>:trillion</tt>,
-
# *<tt>:quadrillion</tt>
-
# * *fractionals*: <tt>:deci</tt>, <tt>:centi</tt>,
-
# *<tt>:mili</tt>, <tt>:micro</tt>, <tt>:nano</tt>,
-
# *<tt>:pico</tt>, <tt>:femto</tt>
-
# * <tt>:format</tt> - Sets the format of the output string
-
# (defaults to "%n %u"). The field types are:
-
# * %u - The quantifier (ex.: 'thousand')
-
# * %n - The number
-
# * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
-
# the argument is invalid.
-
#
-
# ==== Examples
-
#
-
# number_to_human(123) # => "123"
-
# number_to_human(1234) # => "1.23 Thousand"
-
# number_to_human(12345) # => "12.3 Thousand"
-
# number_to_human(1234567) # => "1.23 Million"
-
# number_to_human(1234567890) # => "1.23 Billion"
-
# number_to_human(1234567890123) # => "1.23 Trillion"
-
# number_to_human(1234567890123456) # => "1.23 Quadrillion"
-
# number_to_human(1234567890123456789) # => "1230 Quadrillion"
-
# number_to_human(489939, precision: 2) # => "490 Thousand"
-
# number_to_human(489939, precision: 4) # => "489.9 Thousand"
-
# number_to_human(1234567, precision: 4,
-
# significant: false) # => "1.2346 Million"
-
# number_to_human(1234567, precision: 1,
-
# separator: ',',
-
# significant: false) # => "1,2 Million"
-
#
-
# Non-significant zeros after the decimal separator are stripped
-
# out by default (set <tt>:strip_insignificant_zeros</tt> to
-
# +false+ to change that):
-
# number_to_human(12345012345, significant_digits: 6) # => "12.345 Billion"
-
# number_to_human(500000000, precision: 5) # => "500 Million"
-
#
-
# ==== Custom Unit Quantifiers
-
#
-
# You can also use your own custom unit quantifiers:
-
# number_to_human(500000, units: {unit: "ml", thousand: "lt"}) # => "500 lt"
-
#
-
# If in your I18n locale you have:
-
# distance:
-
# centi:
-
# one: "centimeter"
-
# other: "centimeters"
-
# unit:
-
# one: "meter"
-
# other: "meters"
-
# thousand:
-
# one: "kilometer"
-
# other: "kilometers"
-
# billion: "gazillion-distance"
-
#
-
# Then you could do:
-
#
-
# number_to_human(543934, units: :distance) # => "544 kilometers"
-
# number_to_human(54393498, units: :distance) # => "54400 kilometers"
-
# number_to_human(54393498000, units: :distance) # => "54.4 gazillion-distance"
-
# number_to_human(343, units: :distance, precision: 1) # => "300 meters"
-
# number_to_human(1, units: :distance) # => "1 meter"
-
# number_to_human(0.34, units: :distance) # => "34 centimeters"
-
#
-
1
def number_to_human(number, options = {})
-
delegate_number_helper_method(:number_to_human, number, options)
-
end
-
-
1
private
-
-
1
def delegate_number_helper_method(method, number, options)
-
return unless number
-
options = escape_unsafe_options(options.symbolize_keys)
-
-
wrap_with_output_safety_handling(number, options.delete(:raise)) {
-
ActiveSupport::NumberHelper.public_send(method, number, options)
-
}
-
end
-
-
1
def escape_unsafe_options(options)
-
options[:format] = ERB::Util.html_escape(options[:format]) if options[:format]
-
options[:negative_format] = ERB::Util.html_escape(options[:negative_format]) if options[:negative_format]
-
options[:separator] = ERB::Util.html_escape(options[:separator]) if options[:separator]
-
options[:delimiter] = ERB::Util.html_escape(options[:delimiter]) if options[:delimiter]
-
options[:unit] = ERB::Util.html_escape(options[:unit]) if options[:unit] && !options[:unit].html_safe?
-
options[:units] = escape_units(options[:units]) if options[:units] && Hash === options[:units]
-
options
-
end
-
-
1
def escape_units(units)
-
Hash[units.map do |k, v|
-
[k, ERB::Util.html_escape(v)]
-
end]
-
end
-
-
1
def wrap_with_output_safety_handling(number, raise_on_invalid, &block)
-
valid_float = valid_float?(number)
-
raise InvalidNumberError, number if raise_on_invalid && !valid_float
-
-
formatted_number = yield
-
-
if valid_float || number.html_safe?
-
formatted_number.html_safe
-
else
-
formatted_number
-
end
-
end
-
-
1
def valid_float?(number)
-
!parse_float(number, false).nil?
-
end
-
-
1
def parse_float(number, raise_error)
-
Float(number)
-
rescue ArgumentError, TypeError
-
raise InvalidNumberError, number if raise_error
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/string/output_safety'
-
-
1
module ActionView #:nodoc:
-
# = Action View Raw Output Helper
-
1
module Helpers #:nodoc:
-
1
module OutputSafetyHelper
-
# This method outputs without escaping a string. Since escaping tags is
-
# now default, this can be used when you don't want Rails to automatically
-
# escape tags. This is not recommended if the data is coming from the user's
-
# input.
-
#
-
# For example:
-
#
-
# raw @user.name
-
# # => 'Jimmy <alert>Tables</alert>'
-
1
def raw(stringish)
-
stringish.to_s.html_safe
-
end
-
-
# This method returns a html safe string similar to what <tt>Array#join</tt>
-
# would return. All items in the array, including the supplied separator, are
-
# html escaped unless they are html safe, and the returned string is marked
-
# as html safe.
-
#
-
# safe_join(["<p>foo</p>".html_safe, "<p>bar</p>"], "<br />")
-
# # => "<p>foo</p><br /><p>bar</p>"
-
#
-
# safe_join(["<p>foo</p>".html_safe, "<p>bar</p>".html_safe], "<br />".html_safe)
-
# # => "<p>foo</p><br /><p>bar</p>"
-
#
-
1
def safe_join(array, sep=$,)
-
sep = ERB::Util.html_escape(sep)
-
-
array.map { |i| ERB::Util.html_escape(i) }.join(sep).html_safe
-
end
-
end
-
end
-
end
-
1
require 'action_view/record_identifier'
-
-
1
module ActionView
-
# = Action View Record Tag Helpers
-
1
module Helpers
-
1
module RecordTagHelper
-
1
include ActionView::RecordIdentifier
-
-
# Produces a wrapper DIV element with id and class parameters that
-
# relate to the specified Active Record object. Usage example:
-
#
-
# <%= div_for(@person, class: "foo") do %>
-
# <%= @person.name %>
-
# <% end %>
-
#
-
# produces:
-
#
-
# <div id="person_123" class="person foo"> Joe Bloggs </div>
-
#
-
# You can also pass an array of Active Record objects, which will then
-
# get iterated over and yield each record as an argument for the block.
-
# For example:
-
#
-
# <%= div_for(@people, class: "foo") do |person| %>
-
# <%= person.name %>
-
# <% end %>
-
#
-
# produces:
-
#
-
# <div id="person_123" class="person foo"> Joe Bloggs </div>
-
# <div id="person_124" class="person foo"> Jane Bloggs </div>
-
#
-
1
def div_for(record, *args, &block)
-
content_tag_for(:div, record, *args, &block)
-
end
-
-
# content_tag_for creates an HTML element with id and class parameters
-
# that relate to the specified Active Record object. For example:
-
#
-
# <%= content_tag_for(:tr, @person) do %>
-
# <td><%= @person.first_name %></td>
-
# <td><%= @person.last_name %></td>
-
# <% end %>
-
#
-
# would produce the following HTML (assuming @person is an instance of
-
# a Person object, with an id value of 123):
-
#
-
# <tr id="person_123" class="person">....</tr>
-
#
-
# If you require the HTML id attribute to have a prefix, you can specify it:
-
#
-
# <%= content_tag_for(:tr, @person, :foo) do %> ...
-
#
-
# produces:
-
#
-
# <tr id="foo_person_123" class="person">...
-
#
-
# You can also pass an array of objects which this method will loop through
-
# and yield the current object to the supplied block, reducing the need for
-
# having to iterate through the object (using <tt>each</tt>) beforehand.
-
# For example (assuming @people is an array of Person objects):
-
#
-
# <%= content_tag_for(:tr, @people) do |person| %>
-
# <td><%= person.first_name %></td>
-
# <td><%= person.last_name %></td>
-
# <% end %>
-
#
-
# produces:
-
#
-
# <tr id="person_123" class="person">...</tr>
-
# <tr id="person_124" class="person">...</tr>
-
#
-
# content_tag_for also accepts a hash of options, which will be converted to
-
# additional HTML attributes. If you specify a <tt>:class</tt> value, it will be combined
-
# with the default class name for your object. For example:
-
#
-
# <%= content_tag_for(:li, @person, class: "bar") %>...
-
#
-
# produces:
-
#
-
# <li id="person_123" class="person bar">...
-
#
-
1
def content_tag_for(tag_name, single_or_multiple_records, prefix = nil, options = nil, &block)
-
options, prefix = prefix, nil if prefix.is_a?(Hash)
-
-
Array(single_or_multiple_records).map do |single_record|
-
content_tag_for_single_record(tag_name, single_record, prefix, options, &block)
-
end.join("\n").html_safe
-
end
-
-
1
private
-
-
# Called by <tt>content_tag_for</tt> internally to render a content tag
-
# for each record.
-
1
def content_tag_for_single_record(tag_name, record, prefix, options, &block)
-
options = options ? options.dup : {}
-
options[:class] = [ dom_class(record, prefix), options[:class] ].compact
-
options[:id] = dom_id(record, prefix)
-
-
if block_given?
-
content_tag(tag_name, capture(record, &block), options)
-
else
-
content_tag(tag_name, "", options)
-
end
-
end
-
end
-
end
-
end
-
1
module ActionView
-
1
module Helpers
-
# = Action View Rendering
-
#
-
# Implements methods that allow rendering from a view context.
-
# In order to use this module, all you need is to implement
-
# view_renderer that returns an ActionView::Renderer object.
-
1
module RenderingHelper
-
# Returns the result of a render that's dictated by the options hash. The primary options are:
-
#
-
# * <tt>:partial</tt> - See <tt>ActionView::PartialRenderer</tt>.
-
# * <tt>:file</tt> - Renders an explicit template file (this used to be the old default), add :locals to pass in those.
-
# * <tt>:inline</tt> - Renders an inline template similar to how it's done in the controller.
-
# * <tt>:text</tt> - Renders the text passed in out.
-
# * <tt>:plain</tt> - Renders the text passed in out. Setting the content
-
# type as <tt>text/plain</tt>.
-
# * <tt>:html</tt> - Renders the html safe string passed in out, otherwise
-
# performs html escape on the string first. Setting the content type as
-
# <tt>text/html</tt>.
-
# * <tt>:body</tt> - Renders the text passed in, and inherits the content
-
# type of <tt>text/html</tt> from <tt>ActionDispatch::Response</tt>
-
# object.
-
#
-
# If no options hash is passed or :update specified, the default is to render a partial and use the second parameter
-
# as the locals hash.
-
1
def render(options = {}, locals = {}, &block)
-
case options
-
when Hash
-
if block_given?
-
view_renderer.render_partial(self, options.merge(:partial => options[:layout]), &block)
-
else
-
view_renderer.render(self, options)
-
end
-
else
-
view_renderer.render_partial(self, :partial => options, :locals => locals)
-
end
-
end
-
-
# Overwrites _layout_for in the context object so it supports the case a block is
-
# passed to a partial. Returns the contents that are yielded to a layout, given a
-
# name or a block.
-
#
-
# You can think of a layout as a method that is called with a block. If the user calls
-
# <tt>yield :some_name</tt>, the block, by default, returns <tt>content_for(:some_name)</tt>.
-
# If the user calls simply +yield+, the default block returns <tt>content_for(:layout)</tt>.
-
#
-
# The user can override this default by passing a block to the layout:
-
#
-
# # The template
-
# <%= render layout: "my_layout" do %>
-
# Content
-
# <% end %>
-
#
-
# # The layout
-
# <html>
-
# <%= yield %>
-
# </html>
-
#
-
# In this case, instead of the default block, which would return <tt>content_for(:layout)</tt>,
-
# this method returns the block that was passed in to <tt>render :layout</tt>, and the response
-
# would be
-
#
-
# <html>
-
# Content
-
# </html>
-
#
-
# Finally, the block can take block arguments, which can be passed in by +yield+:
-
#
-
# # The template
-
# <%= render layout: "my_layout" do |customer| %>
-
# Hello <%= customer.name %>
-
# <% end %>
-
#
-
# # The layout
-
# <html>
-
# <%= yield Struct.new(:name).new("David") %>
-
# </html>
-
#
-
# In this case, the layout would receive the block passed into <tt>render :layout</tt>,
-
# and the struct specified would be passed into the block as an argument. The result
-
# would be
-
#
-
# <html>
-
# Hello David
-
# </html>
-
#
-
1
def _layout_for(*args, &block)
-
name = args.first
-
-
if block && !name.is_a?(Symbol)
-
capture(*args, &block)
-
else
-
super
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/object/try'
-
1
require 'action_view/vendor/html-scanner'
-
-
1
module ActionView
-
# = Action View Sanitize Helpers
-
1
module Helpers
-
# The SanitizeHelper module provides a set of methods for scrubbing text of undesired HTML elements.
-
# These helper methods extend Action View making them callable within your template files.
-
1
module SanitizeHelper
-
1
extend ActiveSupport::Concern
-
# This +sanitize+ helper will html encode all tags and strip all attributes that
-
# aren't specifically allowed.
-
#
-
# It also strips href/src tags with invalid protocols, like javascript: especially.
-
# It does its best to counter any tricks that hackers may use, like throwing in
-
# unicode/ascii/hex values to get past the javascript: filters. Check out
-
# the extensive test suite.
-
#
-
# <%= sanitize @article.body %>
-
#
-
# You can add or remove tags/attributes if you want to customize it a bit.
-
# See ActionView::Base for full docs on the available options. You can add
-
# tags/attributes for single uses of +sanitize+ by passing either the
-
# <tt>:attributes</tt> or <tt>:tags</tt> options:
-
#
-
# Normal Use
-
#
-
# <%= sanitize @article.body %>
-
#
-
# Custom Use (only the mentioned tags and attributes are allowed, nothing else)
-
#
-
# <%= sanitize @article.body, tags: %w(table tr td), attributes: %w(id class style) %>
-
#
-
# Add table tags to the default allowed tags
-
#
-
# class Application < Rails::Application
-
# config.action_view.sanitized_allowed_tags = 'table', 'tr', 'td'
-
# end
-
#
-
# Remove tags to the default allowed tags
-
#
-
# class Application < Rails::Application
-
# config.after_initialize do
-
# ActionView::Base.sanitized_allowed_tags.delete 'div'
-
# end
-
# end
-
#
-
# Change allowed default attributes
-
#
-
# class Application < Rails::Application
-
# config.action_view.sanitized_allowed_attributes = ['id', 'class', 'style']
-
# end
-
#
-
# Please note that sanitizing user-provided text does not guarantee that the
-
# resulting markup is valid (conforming to a document type) or even well-formed.
-
# The output may still contain e.g. unescaped '<', '>', '&' characters and
-
# confuse browsers.
-
#
-
1
def sanitize(html, options = {})
-
self.class.white_list_sanitizer.sanitize(html, options).try(:html_safe)
-
end
-
-
# Sanitizes a block of CSS code. Used by +sanitize+ when it comes across a style attribute.
-
1
def sanitize_css(style)
-
self.class.white_list_sanitizer.sanitize_css(style)
-
end
-
-
# Strips all HTML tags from the +html+, including comments. This uses the
-
# html-scanner tokenizer and so its HTML parsing ability is limited by
-
# that of html-scanner.
-
#
-
# strip_tags("Strip <i>these</i> tags!")
-
# # => Strip these tags!
-
#
-
# strip_tags("<b>Bold</b> no more! <a href='more.html'>See more here</a>...")
-
# # => Bold no more! See more here...
-
#
-
# strip_tags("<div id='top-bar'>Welcome to my website!</div>")
-
# # => Welcome to my website!
-
1
def strip_tags(html)
-
self.class.full_sanitizer.sanitize(html)
-
end
-
-
# Strips all link tags from +text+ leaving just the link text.
-
#
-
# strip_links('<a href="http://www.rubyonrails.org">Ruby on Rails</a>')
-
# # => Ruby on Rails
-
#
-
# strip_links('Please e-mail me at <a href="mailto:me@email.com">me@email.com</a>.')
-
# # => Please e-mail me at me@email.com.
-
#
-
# strip_links('Blog: <a href="http://www.myblog.com/" class="nav" target=\"_blank\">Visit</a>.')
-
# # => Blog: Visit.
-
1
def strip_links(html)
-
self.class.link_sanitizer.sanitize(html)
-
end
-
-
1
module ClassMethods #:nodoc:
-
1
attr_writer :full_sanitizer, :link_sanitizer, :white_list_sanitizer
-
-
1
def sanitized_protocol_separator
-
white_list_sanitizer.protocol_separator
-
end
-
-
1
def sanitized_uri_attributes
-
white_list_sanitizer.uri_attributes
-
end
-
-
1
def sanitized_bad_tags
-
white_list_sanitizer.bad_tags
-
end
-
-
1
def sanitized_allowed_tags
-
white_list_sanitizer.allowed_tags
-
end
-
-
1
def sanitized_allowed_attributes
-
white_list_sanitizer.allowed_attributes
-
end
-
-
1
def sanitized_allowed_css_properties
-
white_list_sanitizer.allowed_css_properties
-
end
-
-
1
def sanitized_allowed_css_keywords
-
white_list_sanitizer.allowed_css_keywords
-
end
-
-
1
def sanitized_shorthand_css_properties
-
white_list_sanitizer.shorthand_css_properties
-
end
-
-
1
def sanitized_allowed_protocols
-
white_list_sanitizer.allowed_protocols
-
end
-
-
1
def sanitized_protocol_separator=(value)
-
white_list_sanitizer.protocol_separator = value
-
end
-
-
# Gets the HTML::FullSanitizer instance used by +strip_tags+. Replace with
-
# any object that responds to +sanitize+.
-
#
-
# class Application < Rails::Application
-
# config.action_view.full_sanitizer = MySpecialSanitizer.new
-
# end
-
#
-
1
def full_sanitizer
-
@full_sanitizer ||= HTML::FullSanitizer.new
-
end
-
-
# Gets the HTML::LinkSanitizer instance used by +strip_links+. Replace with
-
# any object that responds to +sanitize+.
-
#
-
# class Application < Rails::Application
-
# config.action_view.link_sanitizer = MySpecialSanitizer.new
-
# end
-
#
-
1
def link_sanitizer
-
@link_sanitizer ||= HTML::LinkSanitizer.new
-
end
-
-
# Gets the HTML::WhiteListSanitizer instance used by sanitize and +sanitize_css+.
-
# Replace with any object that responds to +sanitize+.
-
#
-
# class Application < Rails::Application
-
# config.action_view.white_list_sanitizer = MySpecialSanitizer.new
-
# end
-
#
-
1
def white_list_sanitizer
-
@white_list_sanitizer ||= HTML::WhiteListSanitizer.new
-
end
-
-
# Adds valid HTML attributes that the +sanitize+ helper checks for URIs.
-
#
-
# class Application < Rails::Application
-
# config.action_view.sanitized_uri_attributes = 'lowsrc', 'target'
-
# end
-
#
-
1
def sanitized_uri_attributes=(attributes)
-
HTML::WhiteListSanitizer.uri_attributes.merge(attributes)
-
end
-
-
# Adds to the Set of 'bad' tags for the +sanitize+ helper.
-
#
-
# class Application < Rails::Application
-
# config.action_view.sanitized_bad_tags = 'embed', 'object'
-
# end
-
#
-
1
def sanitized_bad_tags=(attributes)
-
HTML::WhiteListSanitizer.bad_tags.merge(attributes)
-
end
-
-
# Adds to the Set of allowed tags for the +sanitize+ helper.
-
#
-
# class Application < Rails::Application
-
# config.action_view.sanitized_allowed_tags = 'table', 'tr', 'td'
-
# end
-
#
-
1
def sanitized_allowed_tags=(attributes)
-
HTML::WhiteListSanitizer.allowed_tags.merge(attributes)
-
end
-
-
# Adds to the Set of allowed HTML attributes for the +sanitize+ helper.
-
#
-
# class Application < Rails::Application
-
# config.action_view.sanitized_allowed_attributes = ['onclick', 'longdesc']
-
# end
-
#
-
1
def sanitized_allowed_attributes=(attributes)
-
HTML::WhiteListSanitizer.allowed_attributes.merge(attributes)
-
end
-
-
# Adds to the Set of allowed CSS properties for the #sanitize and +sanitize_css+ helpers.
-
#
-
# class Application < Rails::Application
-
# config.action_view.sanitized_allowed_css_properties = 'expression'
-
# end
-
#
-
1
def sanitized_allowed_css_properties=(attributes)
-
HTML::WhiteListSanitizer.allowed_css_properties.merge(attributes)
-
end
-
-
# Adds to the Set of allowed CSS keywords for the +sanitize+ and +sanitize_css+ helpers.
-
#
-
# class Application < Rails::Application
-
# config.action_view.sanitized_allowed_css_keywords = 'expression'
-
# end
-
#
-
1
def sanitized_allowed_css_keywords=(attributes)
-
HTML::WhiteListSanitizer.allowed_css_keywords.merge(attributes)
-
end
-
-
# Adds to the Set of allowed shorthand CSS properties for the +sanitize+ and +sanitize_css+ helpers.
-
#
-
# class Application < Rails::Application
-
# config.action_view.sanitized_shorthand_css_properties = 'expression'
-
# end
-
#
-
1
def sanitized_shorthand_css_properties=(attributes)
-
HTML::WhiteListSanitizer.shorthand_css_properties.merge(attributes)
-
end
-
-
# Adds to the Set of allowed protocols for the +sanitize+ helper.
-
#
-
# class Application < Rails::Application
-
# config.action_view.sanitized_allowed_protocols = 'ssh', 'feed'
-
# end
-
#
-
1
def sanitized_allowed_protocols=(attributes)
-
HTML::WhiteListSanitizer.allowed_protocols.merge(attributes)
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/string/output_safety'
-
1
require 'set'
-
-
1
module ActionView
-
# = Action View Tag Helpers
-
1
module Helpers #:nodoc:
-
# Provides methods to generate HTML tags programmatically when you can't use
-
# a Builder. By default, they output XHTML compliant tags.
-
1
module TagHelper
-
1
extend ActiveSupport::Concern
-
1
include CaptureHelper
-
-
1
BOOLEAN_ATTRIBUTES = %w(disabled readonly multiple checked autobuffer
-
autoplay controls loop selected hidden scoped async
-
defer reversed ismap seamless muted required
-
autofocus novalidate formnovalidate open pubdate
-
itemscope allowfullscreen default inert sortable
-
truespeed typemustmatch).to_set
-
-
31
BOOLEAN_ATTRIBUTES.merge(BOOLEAN_ATTRIBUTES.map {|attribute| attribute.to_sym })
-
-
1
PRE_CONTENT_STRINGS = {
-
:textarea => "\n"
-
}
-
-
# Returns an empty HTML tag of type +name+ which by default is XHTML
-
# compliant. Set +open+ to true to create an open tag compatible
-
# with HTML 4.0 and below. Add HTML attributes by passing an attributes
-
# hash to +options+. Set +escape+ to false to disable attribute value
-
# escaping.
-
#
-
# ==== Options
-
# You can use symbols or strings for the attribute names.
-
#
-
# Use +true+ with boolean attributes that can render with no value, like
-
# +disabled+ and +readonly+.
-
#
-
# HTML5 <tt>data-*</tt> attributes can be set with a single +data+ key
-
# pointing to a hash of sub-attributes.
-
#
-
# To play nicely with JavaScript conventions sub-attributes are dasherized.
-
# For example, a key +user_id+ would render as <tt>data-user-id</tt> and
-
# thus accessed as <tt>dataset.userId</tt>.
-
#
-
# Values are encoded to JSON, with the exception of strings and symbols.
-
# This may come in handy when using jQuery's HTML5-aware <tt>.data()</tt>
-
# from 1.4.3.
-
#
-
# ==== Examples
-
# tag("br")
-
# # => <br />
-
#
-
# tag("br", nil, true)
-
# # => <br>
-
#
-
# tag("input", type: 'text', disabled: true)
-
# # => <input type="text" disabled="disabled" />
-
#
-
# tag("img", src: "open & shut.png")
-
# # => <img src="open & shut.png" />
-
#
-
# tag("img", {src: "open & shut.png"}, false, false)
-
# # => <img src="open & shut.png" />
-
#
-
# tag("div", data: {name: 'Stephen', city_state: %w(Chicago IL)})
-
# # => <div data-name="Stephen" data-city-state="["Chicago","IL"]" />
-
1
def tag(name, options = nil, open = false, escape = true)
-
"<#{name}#{tag_options(options, escape) if options}#{open ? ">" : " />"}".html_safe
-
end
-
-
# Returns an HTML block tag of type +name+ surrounding the +content+. Add
-
# HTML attributes by passing an attributes hash to +options+.
-
# Instead of passing the content as an argument, you can also use a block
-
# in which case, you pass your +options+ as the second parameter.
-
# Set escape to false to disable attribute value escaping.
-
#
-
# ==== Options
-
# The +options+ hash is used with attributes with no value like (<tt>disabled</tt> and
-
# <tt>readonly</tt>), which you can give a value of true in the +options+ hash. You can use
-
# symbols or strings for the attribute names.
-
#
-
# ==== Examples
-
# content_tag(:p, "Hello world!")
-
# # => <p>Hello world!</p>
-
# content_tag(:div, content_tag(:p, "Hello world!"), class: "strong")
-
# # => <div class="strong"><p>Hello world!</p></div>
-
# content_tag("select", options, multiple: true)
-
# # => <select multiple="multiple">...options...</select>
-
#
-
# <%= content_tag :div, class: "strong" do -%>
-
# Hello world!
-
# <% end -%>
-
# # => <div class="strong">Hello world!</div>
-
1
def content_tag(name, content_or_options_with_block = nil, options = nil, escape = true, &block)
-
if block_given?
-
options = content_or_options_with_block if content_or_options_with_block.is_a?(Hash)
-
content_tag_string(name, capture(&block), options, escape)
-
else
-
content_tag_string(name, content_or_options_with_block, options, escape)
-
end
-
end
-
-
# Returns a CDATA section with the given +content+. CDATA sections
-
# are used to escape blocks of text containing characters which would
-
# otherwise be recognized as markup. CDATA sections begin with the string
-
# <tt><![CDATA[</tt> and end with (and may not contain) the string <tt>]]></tt>.
-
#
-
# cdata_section("<hello world>")
-
# # => <![CDATA[<hello world>]]>
-
#
-
# cdata_section(File.read("hello_world.txt"))
-
# # => <![CDATA[<hello from a text file]]>
-
#
-
# cdata_section("hello]]>world")
-
# # => <![CDATA[hello]]]]><![CDATA[>world]]>
-
1
def cdata_section(content)
-
splitted = content.to_s.gsub(']]>', ']]]]><![CDATA[>')
-
"<![CDATA[#{splitted}]]>".html_safe
-
end
-
-
# Returns an escaped version of +html+ without affecting existing escaped entities.
-
#
-
# escape_once("1 < 2 & 3")
-
# # => "1 < 2 & 3"
-
#
-
# escape_once("<< Accept & Checkout")
-
# # => "<< Accept & Checkout"
-
1
def escape_once(html)
-
ERB::Util.html_escape_once(html)
-
end
-
-
1
private
-
-
1
def content_tag_string(name, content, options, escape = true)
-
tag_options = tag_options(options, escape) if options
-
content = ERB::Util.h(content) if escape
-
"<#{name}#{tag_options}>#{PRE_CONTENT_STRINGS[name.to_sym]}#{content}</#{name}>".html_safe
-
end
-
-
1
def tag_options(options, escape = true)
-
return if options.blank?
-
attrs = []
-
options.each_pair do |key, value|
-
if key.to_s == 'data' && value.is_a?(Hash)
-
value.each_pair do |k, v|
-
attrs << data_tag_option(k, v, escape)
-
end
-
elsif BOOLEAN_ATTRIBUTES.include?(key)
-
attrs << boolean_tag_option(key) if value
-
elsif !value.nil?
-
attrs << tag_option(key, value, escape)
-
end
-
end
-
" #{attrs.sort! * ' '}" unless attrs.empty?
-
end
-
-
1
def data_tag_option(key, value, escape)
-
key = "data-#{key.to_s.dasherize}"
-
unless value.is_a?(String) || value.is_a?(Symbol) || value.is_a?(BigDecimal)
-
value = value.to_json
-
end
-
tag_option(key, value, escape)
-
end
-
-
1
def boolean_tag_option(key)
-
%(#{key}="#{key}")
-
end
-
-
1
def tag_option(key, value, escape)
-
value = value.join(" ") if value.is_a?(Array)
-
value = ERB::Util.h(value) if escape
-
%(#{key}="#{value}")
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/string/filters'
-
1
require 'active_support/core_ext/array/extract_options'
-
-
1
module ActionView
-
# = Action View Text Helpers
-
1
module Helpers #:nodoc:
-
# The TextHelper module provides a set of methods for filtering, formatting
-
# and transforming strings, which can reduce the amount of inline Ruby code in
-
# your views. These helper methods extend Action View making them callable
-
# within your template files.
-
#
-
# ==== Sanitization
-
#
-
# Most text helpers by default sanitize the given content, but do not escape it.
-
# This means HTML tags will appear in the page but all malicious code will be removed.
-
# Let's look at some examples using the +simple_format+ method:
-
#
-
# simple_format('<a href="http://example.com/">Example</a>')
-
# # => "<p><a href=\"http://example.com/\">Example</a></p>"
-
#
-
# simple_format('<a href="javascript:alert(\'no!\')">Example</a>')
-
# # => "<p><a>Example</a></p>"
-
#
-
# If you want to escape all content, you should invoke the +h+ method before
-
# calling the text helper.
-
#
-
# simple_format h('<a href="http://example.com/">Example</a>')
-
# # => "<p><a href=\"http://example.com/\">Example</a></p>"
-
1
module TextHelper
-
1
extend ActiveSupport::Concern
-
-
1
include SanitizeHelper
-
1
include TagHelper
-
1
include OutputSafetyHelper
-
-
# The preferred method of outputting text in your views is to use the
-
# <%= "text" %> eRuby syntax. The regular _puts_ and _print_ methods
-
# do not operate as expected in an eRuby code block. If you absolutely must
-
# output text within a non-output code block (i.e., <% %>), you can use the concat method.
-
#
-
# <%
-
# concat "hello"
-
# # is the equivalent of <%= "hello" %>
-
#
-
# if logged_in
-
# concat "Logged in!"
-
# else
-
# concat link_to('login', action: :login)
-
# end
-
# # will either display "Logged in!" or a login link
-
# %>
-
1
def concat(string)
-
output_buffer << string
-
end
-
-
1
def safe_concat(string)
-
output_buffer.respond_to?(:safe_concat) ? output_buffer.safe_concat(string) : concat(string)
-
end
-
-
# Truncates a given +text+ after a given <tt>:length</tt> if +text+ is longer than <tt>:length</tt>
-
# (defaults to 30). The last characters will be replaced with the <tt>:omission</tt> (defaults to "...")
-
# for a total length not exceeding <tt>:length</tt>.
-
#
-
# Pass a <tt>:separator</tt> to truncate +text+ at a natural break.
-
#
-
# Pass a block if you want to show extra content when the text is truncated.
-
#
-
# The result is marked as HTML-safe, but it is escaped by default, unless <tt>:escape</tt> is
-
# +false+. Care should be taken if +text+ contains HTML tags or entities, because truncation
-
# may produce invalid HTML (such as unbalanced or incomplete tags).
-
#
-
# truncate("Once upon a time in a world far far away")
-
# # => "Once upon a time in a world..."
-
#
-
# truncate("Once upon a time in a world far far away", length: 17)
-
# # => "Once upon a ti..."
-
#
-
# truncate("Once upon a time in a world far far away", length: 17, separator: ' ')
-
# # => "Once upon a..."
-
#
-
# truncate("And they found that many people were sleeping better.", length: 25, omission: '... (continued)')
-
# # => "And they f... (continued)"
-
#
-
# truncate("<p>Once upon a time in a world far far away</p>")
-
# # => "<p>Once upon a time in a wo..."
-
#
-
# truncate("<p>Once upon a time in a world far far away</p>", escape: false)
-
# # => "<p>Once upon a time in a wo..."
-
#
-
# truncate("Once upon a time in a world far far away") { link_to "Continue", "#" }
-
# # => "Once upon a time in a wo...<a href="#">Continue</a>"
-
1
def truncate(text, options = {}, &block)
-
if text
-
length = options.fetch(:length, 30)
-
-
content = text.truncate(length, options)
-
content = options[:escape] == false ? content.html_safe : ERB::Util.html_escape(content)
-
content << capture(&block) if block_given? && text.length > length
-
content
-
end
-
end
-
-
# Highlights one or more +phrases+ everywhere in +text+ by inserting it into
-
# a <tt>:highlighter</tt> string. The highlighter can be specialized by passing <tt>:highlighter</tt>
-
# as a single-quoted string with <tt>\1</tt> where the phrase is to be inserted (defaults to
-
# '<mark>\1</mark>')
-
#
-
# highlight('You searched for: rails', 'rails')
-
# # => You searched for: <mark>rails</mark>
-
#
-
# highlight('You searched for: ruby, rails, dhh', 'actionpack')
-
# # => You searched for: ruby, rails, dhh
-
#
-
# highlight('You searched for: rails', ['for', 'rails'], highlighter: '<em>\1</em>')
-
# # => You searched <em>for</em>: <em>rails</em>
-
#
-
# highlight('You searched for: rails', 'rails', highlighter: '<a href="search?q=\1">\1</a>')
-
# # => You searched for: <a href="search?q=rails">rails</a>
-
1
def highlight(text, phrases, options = {})
-
text = sanitize(text) if options.fetch(:sanitize, true)
-
-
if text.blank? || phrases.blank?
-
text
-
else
-
highlighter = options.fetch(:highlighter, '<mark>\1</mark>')
-
match = Array(phrases).map { |p| Regexp.escape(p) }.join('|')
-
text.gsub(/(#{match})(?![^<]*?>)/i, highlighter)
-
end.html_safe
-
end
-
-
# Extracts an excerpt from +text+ that matches the first instance of +phrase+.
-
# The <tt>:radius</tt> option expands the excerpt on each side of the first occurrence of +phrase+ by the number of characters
-
# defined in <tt>:radius</tt> (which defaults to 100). If the excerpt radius overflows the beginning or end of the +text+,
-
# then the <tt>:omission</tt> option (which defaults to "...") will be prepended/appended accordingly. Use the
-
# <tt>:separator</tt> option to choose the delimitation. The resulting string will be stripped in any case. If the +phrase+
-
# isn't found, nil is returned.
-
#
-
# excerpt('This is an example', 'an', radius: 5)
-
# # => ...s is an exam...
-
#
-
# excerpt('This is an example', 'is', radius: 5)
-
# # => This is a...
-
#
-
# excerpt('This is an example', 'is')
-
# # => This is an example
-
#
-
# excerpt('This next thing is an example', 'ex', radius: 2)
-
# # => ...next...
-
#
-
# excerpt('This is also an example', 'an', radius: 8, omission: '<chop> ')
-
# # => <chop> is also an example
-
#
-
# excerpt('This is a very beautiful morning', 'very', separator: ' ', radius: 1)
-
# # => ...a very beautiful...
-
1
def excerpt(text, phrase, options = {})
-
return unless text && phrase
-
-
separator = options[:separator] || ''
-
phrase = Regexp.escape(phrase)
-
regex = /#{phrase}/i
-
-
return unless matches = text.match(regex)
-
phrase = matches[0]
-
-
unless separator.empty?
-
text.split(separator).each do |value|
-
if value.match(regex)
-
regex = phrase = value
-
break
-
end
-
end
-
end
-
-
first_part, second_part = text.split(regex, 2)
-
-
prefix, first_part = cut_excerpt_part(:first, first_part, separator, options)
-
postfix, second_part = cut_excerpt_part(:second, second_part, separator, options)
-
-
affix = [first_part, separator, phrase, separator, second_part].join.strip
-
[prefix, affix, postfix].join
-
end
-
-
# Attempts to pluralize the +singular+ word unless +count+ is 1. If
-
# +plural+ is supplied, it will use that when count is > 1, otherwise
-
# it will use the Inflector to determine the plural form.
-
#
-
# pluralize(1, 'person')
-
# # => 1 person
-
#
-
# pluralize(2, 'person')
-
# # => 2 people
-
#
-
# pluralize(3, 'person', 'users')
-
# # => 3 users
-
#
-
# pluralize(0, 'person')
-
# # => 0 people
-
1
def pluralize(count, singular, plural = nil)
-
word = if (count == 1 || count =~ /^1(\.0+)?$/)
-
singular
-
else
-
plural || singular.pluralize
-
end
-
-
"#{count || 0} #{word}"
-
end
-
-
# Wraps the +text+ into lines no longer than +line_width+ width. This method
-
# breaks on the first whitespace character that does not exceed +line_width+
-
# (which is 80 by default).
-
#
-
# word_wrap('Once upon a time')
-
# # => Once upon a time
-
#
-
# word_wrap('Once upon a time, in a kingdom called Far Far Away, a king fell ill, and finding a successor to the throne turned out to be more trouble than anyone could have imagined...')
-
# # => Once upon a time, in a kingdom called Far Far Away, a king fell ill, and finding\na successor to the throne turned out to be more trouble than anyone could have\nimagined...
-
#
-
# word_wrap('Once upon a time', line_width: 8)
-
# # => Once\nupon a\ntime
-
#
-
# word_wrap('Once upon a time', line_width: 1)
-
# # => Once\nupon\na\ntime
-
1
def word_wrap(text, options = {})
-
line_width = options.fetch(:line_width, 80)
-
-
text.split("\n").collect! do |line|
-
line.length > line_width ? line.gsub(/(.{1,#{line_width}})(\s+|$)/, "\\1\n").strip : line
-
end * "\n"
-
end
-
-
# Returns +text+ transformed into HTML using simple formatting rules.
-
# Two or more consecutive newlines(<tt>\n\n</tt>) are considered as a
-
# paragraph and wrapped in <tt><p></tt> tags. One newline (<tt>\n</tt>) is
-
# considered as a linebreak and a <tt><br /></tt> tag is appended. This
-
# method does not remove the newlines from the +text+.
-
#
-
# You can pass any HTML attributes into <tt>html_options</tt>. These
-
# will be added to all created paragraphs.
-
#
-
# ==== Options
-
# * <tt>:sanitize</tt> - If +false+, does not sanitize +text+.
-
# * <tt>:wrapper_tag</tt> - String representing the wrapper tag, defaults to <tt>"p"</tt>
-
#
-
# ==== Examples
-
# my_text = "Here is some basic text...\n...with a line break."
-
#
-
# simple_format(my_text)
-
# # => "<p>Here is some basic text...\n<br />...with a line break.</p>"
-
#
-
# simple_format(my_text, {}, wrapper_tag: "div")
-
# # => "<div>Here is some basic text...\n<br />...with a line break.</div>"
-
#
-
# more_text = "We want to put a paragraph...\n\n...right there."
-
#
-
# simple_format(more_text)
-
# # => "<p>We want to put a paragraph...</p>\n\n<p>...right there.</p>"
-
#
-
# simple_format("Look ma! A class!", class: 'description')
-
# # => "<p class='description'>Look ma! A class!</p>"
-
#
-
# simple_format("<blink>Unblinkable.</blink>")
-
# # => "<p>Unblinkable.</p>"
-
#
-
# simple_format("<blink>Blinkable!</blink> It's true.", {}, sanitize: false)
-
# # => "<p><blink>Blinkable!</blink> It's true.</p>"
-
1
def simple_format(text, html_options = {}, options = {})
-
wrapper_tag = options.fetch(:wrapper_tag, :p)
-
-
text = sanitize(text) if options.fetch(:sanitize, true)
-
paragraphs = split_paragraphs(text)
-
-
if paragraphs.empty?
-
content_tag(wrapper_tag, nil, html_options)
-
else
-
paragraphs.map! { |paragraph|
-
content_tag(wrapper_tag, raw(paragraph), html_options)
-
}.join("\n\n").html_safe
-
end
-
end
-
-
# Creates a Cycle object whose _to_s_ method cycles through elements of an
-
# array every time it is called. This can be used for example, to alternate
-
# classes for table rows. You can use named cycles to allow nesting in loops.
-
# Passing a Hash as the last parameter with a <tt>:name</tt> key will create a
-
# named cycle. The default name for a cycle without a +:name+ key is
-
# <tt>"default"</tt>. You can manually reset a cycle by calling reset_cycle
-
# and passing the name of the cycle. The current cycle string can be obtained
-
# anytime using the current_cycle method.
-
#
-
# # Alternate CSS classes for even and odd numbers...
-
# @items = [1,2,3,4]
-
# <table>
-
# <% @items.each do |item| %>
-
# <tr class="<%= cycle("odd", "even") -%>">
-
# <td>item</td>
-
# </tr>
-
# <% end %>
-
# </table>
-
#
-
#
-
# # Cycle CSS classes for rows, and text colors for values within each row
-
# @items = x = [{first: 'Robert', middle: 'Daniel', last: 'James'},
-
# {first: 'Emily', middle: 'Shannon', maiden: 'Pike', last: 'Hicks'},
-
# {first: 'June', middle: 'Dae', last: 'Jones'}]
-
# <% @items.each do |item| %>
-
# <tr class="<%= cycle("odd", "even", name: "row_class") -%>">
-
# <td>
-
# <% item.values.each do |value| %>
-
# <%# Create a named cycle "colors" %>
-
# <span style="color:<%= cycle("red", "green", "blue", name: "colors") -%>">
-
# <%= value %>
-
# </span>
-
# <% end %>
-
# <% reset_cycle("colors") %>
-
# </td>
-
# </tr>
-
# <% end %>
-
1
def cycle(first_value, *values)
-
options = values.extract_options!
-
name = options.fetch(:name, 'default')
-
-
values.unshift(*first_value)
-
-
cycle = get_cycle(name)
-
unless cycle && cycle.values == values
-
cycle = set_cycle(name, Cycle.new(*values))
-
end
-
cycle.to_s
-
end
-
-
# Returns the current cycle string after a cycle has been started. Useful
-
# for complex table highlighting or any other design need which requires
-
# the current cycle string in more than one place.
-
#
-
# # Alternate background colors
-
# @items = [1,2,3,4]
-
# <% @items.each do |item| %>
-
# <div style="background-color:<%= cycle("red","white","blue") %>">
-
# <span style="background-color:<%= current_cycle %>"><%= item %></span>
-
# </div>
-
# <% end %>
-
1
def current_cycle(name = "default")
-
cycle = get_cycle(name)
-
cycle.current_value if cycle
-
end
-
-
# Resets a cycle so that it starts from the first element the next time
-
# it is called. Pass in +name+ to reset a named cycle.
-
#
-
# # Alternate CSS classes for even and odd numbers...
-
# @items = [[1,2,3,4], [5,6,3], [3,4,5,6,7,4]]
-
# <table>
-
# <% @items.each do |item| %>
-
# <tr class="<%= cycle("even", "odd") -%>">
-
# <% item.each do |value| %>
-
# <span style="color:<%= cycle("#333", "#666", "#999", name: "colors") -%>">
-
# <%= value %>
-
# </span>
-
# <% end %>
-
#
-
# <% reset_cycle("colors") %>
-
# </tr>
-
# <% end %>
-
# </table>
-
1
def reset_cycle(name = "default")
-
cycle = get_cycle(name)
-
cycle.reset if cycle
-
end
-
-
1
class Cycle #:nodoc:
-
1
attr_reader :values
-
-
1
def initialize(first_value, *values)
-
@values = values.unshift(first_value)
-
reset
-
end
-
-
1
def reset
-
@index = 0
-
end
-
-
1
def current_value
-
@values[previous_index].to_s
-
end
-
-
1
def to_s
-
value = @values[@index].to_s
-
@index = next_index
-
return value
-
end
-
-
1
private
-
-
1
def next_index
-
step_index(1)
-
end
-
-
1
def previous_index
-
step_index(-1)
-
end
-
-
1
def step_index(n)
-
(@index + n) % @values.size
-
end
-
end
-
-
1
private
-
# The cycle helpers need to store the cycles in a place that is
-
# guaranteed to be reset every time a page is rendered, so it
-
# uses an instance variable of ActionView::Base.
-
1
def get_cycle(name)
-
@_cycles = Hash.new unless defined?(@_cycles)
-
return @_cycles[name]
-
end
-
-
1
def set_cycle(name, cycle_object)
-
@_cycles = Hash.new unless defined?(@_cycles)
-
@_cycles[name] = cycle_object
-
end
-
-
1
def split_paragraphs(text)
-
return [] if text.blank?
-
-
text.to_str.gsub(/\r\n?/, "\n").split(/\n\n+/).map! do |t|
-
t.gsub!(/([^\n]\n)(?=[^\n])/, '\1<br />') || t
-
end
-
end
-
-
1
def cut_excerpt_part(part_position, part, separator, options)
-
return "", "" unless part
-
-
radius = options.fetch(:radius, 100)
-
omission = options.fetch(:omission, "...")
-
-
part = part.split(separator)
-
part.delete("")
-
affix = part.size > radius ? omission : ""
-
-
part = if part_position == :first
-
drop_index = [part.length - radius, 0].max
-
part.drop(drop_index)
-
else
-
part.first(radius)
-
end
-
-
return affix, part.join(separator)
-
end
-
end
-
end
-
end
-
1
require 'action_view/helpers/tag_helper'
-
1
require 'i18n/exceptions'
-
-
1
module ActionView
-
# = Action View Translation Helpers
-
1
module Helpers
-
1
module TranslationHelper
-
# Delegates to <tt>I18n#translate</tt> but also performs three additional functions.
-
#
-
# First, it will ensure that any thrown +MissingTranslation+ messages will be turned
-
# into inline spans that:
-
#
-
# * have a "translation-missing" class set,
-
# * contain the missing key as a title attribute and
-
# * a titleized version of the last key segment as a text.
-
#
-
# E.g. the value returned for a missing translation key :"blog.post.title" will be
-
# <span class="translation_missing" title="translation missing: en.blog.post.title">Title</span>.
-
# This way your views will display rather reasonable strings but it will still
-
# be easy to spot missing translations.
-
#
-
# Second, it'll scope the key by the current partial if the key starts
-
# with a period. So if you call <tt>translate(".foo")</tt> from the
-
# <tt>people/index.html.erb</tt> template, you'll actually be calling
-
# <tt>I18n.translate("people.index.foo")</tt>. This makes it less repetitive
-
# to translate many keys within the same partials and gives you a simple framework
-
# for scoping them consistently. If you don't prepend the key with a period,
-
# nothing is converted.
-
#
-
# Third, it'll mark the translation as safe HTML if the key has the suffix
-
# "_html" or the last element of the key is the word "html". For example,
-
# calling translate("footer_html") or translate("footer.html") will return
-
# a safe HTML string that won't be escaped by other HTML helper methods. This
-
# naming convention helps to identify translations that include HTML tags so that
-
# you know what kind of output to expect when you call translate in a template.
-
1
def translate(key, options = {})
-
options[:default] = wrap_translate_defaults(options[:default]) if options[:default]
-
-
# If the user has specified rescue_format then pass it all through, otherwise use
-
# raise and do the work ourselves
-
options[:raise] ||= ActionView::Base.raise_on_missing_translations
-
-
raise_error = options[:raise] || options.key?(:rescue_format)
-
unless raise_error
-
options[:raise] = true
-
end
-
-
if html_safe_translation_key?(key)
-
html_safe_options = options.dup
-
options.except(*I18n::RESERVED_KEYS).each do |name, value|
-
unless name == :count && value.is_a?(Numeric)
-
html_safe_options[name] = ERB::Util.html_escape(value.to_s)
-
end
-
end
-
translation = I18n.translate(scope_key_by_partial(key), html_safe_options)
-
-
translation.respond_to?(:html_safe) ? translation.html_safe : translation
-
else
-
I18n.translate(scope_key_by_partial(key), options)
-
end
-
rescue I18n::MissingTranslationData => e
-
raise e if raise_error
-
-
keys = I18n.normalize_keys(e.locale, e.key, e.options[:scope])
-
content_tag('span', keys.last.to_s.titleize, :class => 'translation_missing', :title => "translation missing: #{keys.join('.')}")
-
end
-
1
alias :t :translate
-
-
# Delegates to <tt>I18n.localize</tt> with no additional functionality.
-
#
-
# See http://rubydoc.info/github/svenfuchs/i18n/master/I18n/Backend/Base:localize
-
# for more information.
-
1
def localize(*args)
-
I18n.localize(*args)
-
end
-
1
alias :l :localize
-
-
1
private
-
1
def scope_key_by_partial(key)
-
if key.to_s.first == "."
-
if @virtual_path
-
@virtual_path.gsub(%r{/_?}, ".") + key.to_s
-
else
-
raise "Cannot use t(#{key.inspect}) shortcut because path is not available"
-
end
-
else
-
key
-
end
-
end
-
-
1
def html_safe_translation_key?(key)
-
key.to_s =~ /(\b|_|\.)html$/
-
end
-
-
1
def wrap_translate_defaults(defaults)
-
new_defaults = []
-
defaults = Array(defaults)
-
while key = defaults.shift
-
if key.is_a?(Symbol)
-
new_defaults << lambda { |_, options| translate key, options.merge(:default => defaults) }
-
break
-
else
-
new_defaults << key
-
end
-
end
-
-
new_defaults
-
end
-
end
-
end
-
end
-
1
require 'action_view/helpers/javascript_helper'
-
1
require 'active_support/core_ext/array/access'
-
1
require 'active_support/core_ext/hash/keys'
-
1
require 'active_support/core_ext/string/output_safety'
-
-
1
module ActionView
-
# = Action View URL Helpers
-
1
module Helpers #:nodoc:
-
# Provides a set of methods for making links and getting URLs that
-
# depend on the routing subsystem (see ActionDispatch::Routing).
-
# This allows you to use the same format for links in views
-
# and controllers.
-
1
module UrlHelper
-
# This helper may be included in any class that includes the
-
# URL helpers of a routes (routes.url_helpers). Some methods
-
# provided here will only work in the context of a request
-
# (link_to_unless_current, for instance), which must be provided
-
# as a method called #request on the context.
-
1
BUTTON_TAG_METHOD_VERBS = %w{patch put delete}
-
1
extend ActiveSupport::Concern
-
-
1
include TagHelper
-
-
1
module ClassMethods
-
1
def _url_for_modules
-
1
ActionView::RoutingUrlFor
-
end
-
end
-
-
# Basic implementation of url_for to allow use helpers without routes existence
-
1
def url_for(options = nil) # :nodoc:
-
case options
-
when String
-
options
-
when :back
-
_back_url
-
else
-
raise ArgumentError, "arguments passed to url_for can't be handled. Please require " +
-
"routes or provide your own implementation"
-
end
-
end
-
-
1
def _back_url # :nodoc:
-
referrer = controller.respond_to?(:request) && controller.request.env["HTTP_REFERER"]
-
referrer || 'javascript:history.back()'
-
end
-
1
protected :_back_url
-
-
# Creates a link tag of the given +name+ using a URL created by the set of +options+.
-
# See the valid options in the documentation for +url_for+. It's also possible to
-
# pass a String instead of an options hash, which generates a link tag that uses the
-
# value of the String as the href for the link. Using a <tt>:back</tt> Symbol instead
-
# of an options hash will generate a link to the referrer (a JavaScript back link
-
# will be used in place of a referrer if none exists). If +nil+ is passed as the name
-
# the value of the link itself will become the name.
-
#
-
# ==== Signatures
-
#
-
# link_to(body, url, html_options = {})
-
# # url is a String; you can use URL helpers like
-
# # posts_path
-
#
-
# link_to(body, url_options = {}, html_options = {})
-
# # url_options, except :method, is passed to url_for
-
#
-
# link_to(options = {}, html_options = {}) do
-
# # name
-
# end
-
#
-
# link_to(url, html_options = {}) do
-
# # name
-
# end
-
#
-
# ==== Options
-
# * <tt>:data</tt> - This option can be used to add custom data attributes.
-
# * <tt>method: symbol of HTTP verb</tt> - This modifier will dynamically
-
# create an HTML form and immediately submit the form for processing using
-
# the HTTP verb specified. Useful for having links perform a POST operation
-
# in dangerous actions like deleting a record (which search bots can follow
-
# while spidering your site). Supported verbs are <tt>:post</tt>, <tt>:delete</tt>, <tt>:patch</tt>, and <tt>:put</tt>.
-
# Note that if the user has JavaScript disabled, the request will fall back
-
# to using GET. If <tt>href: '#'</tt> is used and the user has JavaScript
-
# disabled clicking the link will have no effect. If you are relying on the
-
# POST behavior, you should check for it in your controller's action by using
-
# the request object's methods for <tt>post?</tt>, <tt>delete?</tt>, <tt>patch?</tt>, or <tt>put?</tt>.
-
# * <tt>remote: true</tt> - This will allow the unobtrusive JavaScript
-
# driver to make an Ajax request to the URL in question instead of following
-
# the link. The drivers each provide mechanisms for listening for the
-
# completion of the Ajax request and performing JavaScript operations once
-
# they're complete
-
#
-
# ==== Data attributes
-
#
-
# * <tt>confirm: 'question?'</tt> - This will allow the unobtrusive JavaScript
-
# driver to prompt with the question specified (in this case, the
-
# resulting text would be <tt>question?</tt>. If the user accepts, the
-
# link is processed normally, otherwise no action is taken.
-
# * <tt>:disable_with</tt> - Value of this parameter will be
-
# used as the value for a disabled version of the submit
-
# button when the form is submitted. This feature is provided
-
# by the unobtrusive JavaScript driver.
-
#
-
# ==== Examples
-
# Because it relies on +url_for+, +link_to+ supports both older-style controller/action/id arguments
-
# and newer RESTful routes. Current Rails style favors RESTful routes whenever possible, so base
-
# your application on resources and use
-
#
-
# link_to "Profile", profile_path(@profile)
-
# # => <a href="/profiles/1">Profile</a>
-
#
-
# or the even pithier
-
#
-
# link_to "Profile", @profile
-
# # => <a href="/profiles/1">Profile</a>
-
#
-
# in place of the older more verbose, non-resource-oriented
-
#
-
# link_to "Profile", controller: "profiles", action: "show", id: @profile
-
# # => <a href="/profiles/show/1">Profile</a>
-
#
-
# Similarly,
-
#
-
# link_to "Profiles", profiles_path
-
# # => <a href="/profiles">Profiles</a>
-
#
-
# is better than
-
#
-
# link_to "Profiles", controller: "profiles"
-
# # => <a href="/profiles">Profiles</a>
-
#
-
# You can use a block as well if your link target is hard to fit into the name parameter. ERB example:
-
#
-
# <%= link_to(@profile) do %>
-
# <strong><%= @profile.name %></strong> -- <span>Check it out!</span>
-
# <% end %>
-
# # => <a href="/profiles/1">
-
# <strong>David</strong> -- <span>Check it out!</span>
-
# </a>
-
#
-
# Classes and ids for CSS are easy to produce:
-
#
-
# link_to "Articles", articles_path, id: "news", class: "article"
-
# # => <a href="/articles" class="article" id="news">Articles</a>
-
#
-
# Be careful when using the older argument style, as an extra literal hash is needed:
-
#
-
# link_to "Articles", { controller: "articles" }, id: "news", class: "article"
-
# # => <a href="/articles" class="article" id="news">Articles</a>
-
#
-
# Leaving the hash off gives the wrong link:
-
#
-
# link_to "WRONG!", controller: "articles", id: "news", class: "article"
-
# # => <a href="/articles/index/news?class=article">WRONG!</a>
-
#
-
# +link_to+ can also produce links with anchors or query strings:
-
#
-
# link_to "Comment wall", profile_path(@profile, anchor: "wall")
-
# # => <a href="/profiles/1#wall">Comment wall</a>
-
#
-
# link_to "Ruby on Rails search", controller: "searches", query: "ruby on rails"
-
# # => <a href="/searches?query=ruby+on+rails">Ruby on Rails search</a>
-
#
-
# link_to "Nonsense search", searches_path(foo: "bar", baz: "quux")
-
# # => <a href="/searches?foo=bar&baz=quux">Nonsense search</a>
-
#
-
# The only option specific to +link_to+ (<tt>:method</tt>) is used as follows:
-
#
-
# link_to("Destroy", "http://www.example.com", method: :delete)
-
# # => <a href='http://www.example.com' rel="nofollow" data-method="delete">Destroy</a>
-
#
-
# You can also use custom data attributes using the <tt>:data</tt> option:
-
#
-
# link_to "Visit Other Site", "http://www.rubyonrails.org/", data: { confirm: "Are you sure?" }
-
# # => <a href="http://www.rubyonrails.org/" data-confirm="Are you sure?">Visit Other Site</a>
-
1
def link_to(name = nil, options = nil, html_options = nil, &block)
-
html_options, options, name = options, name, block if block_given?
-
options ||= {}
-
-
html_options = convert_options_to_data_attributes(options, html_options)
-
-
url = url_for(options)
-
html_options['href'] ||= url
-
-
content_tag(:a, name || url, html_options, &block)
-
end
-
-
# Generates a form containing a single button that submits to the URL created
-
# by the set of +options+. This is the safest method to ensure links that
-
# cause changes to your data are not triggered by search bots or accelerators.
-
# If the HTML button does not work with your layout, you can also consider
-
# using the +link_to+ method with the <tt>:method</tt> modifier as described in
-
# the +link_to+ documentation.
-
#
-
# By default, the generated form element has a class name of <tt>button_to</tt>
-
# to allow styling of the form itself and its children. This can be changed
-
# using the <tt>:form_class</tt> modifier within +html_options+. You can control
-
# the form submission and input element behavior using +html_options+.
-
# This method accepts the <tt>:method</tt> modifier described in the +link_to+ documentation.
-
# If no <tt>:method</tt> modifier is given, it will default to performing a POST operation.
-
# You can also disable the button by passing <tt>disabled: true</tt> in +html_options+.
-
# If you are using RESTful routes, you can pass the <tt>:method</tt>
-
# to change the HTTP verb used to submit the form.
-
#
-
# ==== Options
-
# The +options+ hash accepts the same options as +url_for+.
-
#
-
# There are a few special +html_options+:
-
# * <tt>:method</tt> - Symbol of HTTP verb. Supported verbs are <tt>:post</tt>, <tt>:get</tt>,
-
# <tt>:delete</tt>, <tt>:patch</tt>, and <tt>:put</tt>. By default it will be <tt>:post</tt>.
-
# * <tt>:disabled</tt> - If set to true, it will generate a disabled button.
-
# * <tt>:data</tt> - This option can be used to add custom data attributes.
-
# * <tt>:remote</tt> - If set to true, will allow the Unobtrusive JavaScript drivers to control the
-
# submit behavior. By default this behavior is an ajax submit.
-
# * <tt>:form</tt> - This hash will be form attributes
-
# * <tt>:form_class</tt> - This controls the class of the form within which the submit button will
-
# be placed
-
# * <tt>:params</tt> - Hash of parameters to be rendered as hidden fields within the form.
-
#
-
# ==== Data attributes
-
#
-
# * <tt>:confirm</tt> - This will use the unobtrusive JavaScript driver to
-
# prompt with the question specified. If the user accepts, the link is
-
# processed normally, otherwise no action is taken.
-
# * <tt>:disable_with</tt> - Value of this parameter will be
-
# used as the value for a disabled version of the submit
-
# button when the form is submitted. This feature is provided
-
# by the unobtrusive JavaScript driver.
-
#
-
# ==== Examples
-
# <%= button_to "New", action: "new" %>
-
# # => "<form method="post" action="/controller/new" class="button_to">
-
# # <div><input value="New" type="submit" /></div>
-
# # </form>"
-
#
-
# <%= button_to "New", new_articles_path %>
-
# # => "<form method="post" action="/articles/new" class="button_to">
-
# # <div><input value="New" type="submit" /></div>
-
# # </form>"
-
#
-
# <%= button_to [:make_happy, @user] do %>
-
# Make happy <strong><%= @user.name %></strong>
-
# <% end %>
-
# # => "<form method="post" action="/users/1/make_happy" class="button_to">
-
# # <div>
-
# # <button type="submit">
-
# # Make happy <strong><%= @user.name %></strong>
-
# # </button>
-
# # </div>
-
# # </form>"
-
#
-
# <%= button_to "New", { action: "new" }, form_class: "new-thing" %>
-
# # => "<form method="post" action="/controller/new" class="new-thing">
-
# # <div><input value="New" type="submit" /></div>
-
# # </form>"
-
#
-
#
-
# <%= button_to "Create", { action: "create" }, remote: true, form: { "data-type" => "json" } %>
-
# # => "<form method="post" action="/images/create" class="button_to" data-remote="true" data-type="json">
-
# # <div>
-
# # <input value="Create" type="submit" />
-
# # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6"/>
-
# # </div>
-
# # </form>"
-
#
-
#
-
# <%= button_to "Delete Image", { action: "delete", id: @image.id },
-
# method: :delete, data: { confirm: "Are you sure?" } %>
-
# # => "<form method="post" action="/images/delete/1" class="button_to">
-
# # <div>
-
# # <input type="hidden" name="_method" value="delete" />
-
# # <input data-confirm='Are you sure?' value="Delete Image" type="submit" />
-
# # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6"/>
-
# # </div>
-
# # </form>"
-
#
-
#
-
# <%= button_to('Destroy', 'http://www.example.com',
-
# method: "delete", remote: true, data: { confirm: 'Are you sure?', disable_with: 'loading...' }) %>
-
# # => "<form class='button_to' method='post' action='http://www.example.com' data-remote='true'>
-
# # <div>
-
# # <input name='_method' value='delete' type='hidden' />
-
# # <input value='Destroy' type='submit' data-disable-with='loading...' data-confirm='Are you sure?' />
-
# # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6"/>
-
# # </div>
-
# # </form>"
-
# #
-
1
def button_to(name = nil, options = nil, html_options = nil, &block)
-
html_options, options = options, name if block_given?
-
options ||= {}
-
html_options ||= {}
-
-
html_options = html_options.stringify_keys
-
convert_boolean_attributes!(html_options, %w(disabled))
-
-
url = options.is_a?(String) ? options : url_for(options)
-
remote = html_options.delete('remote')
-
params = html_options.delete('params')
-
-
method = html_options.delete('method').to_s
-
method_tag = BUTTON_TAG_METHOD_VERBS.include?(method) ? method_tag(method) : ''.html_safe
-
-
form_method = method == 'get' ? 'get' : 'post'
-
form_options = html_options.delete('form') || {}
-
form_options[:class] ||= html_options.delete('form_class') || 'button_to'
-
form_options.merge!(method: form_method, action: url)
-
form_options.merge!("data-remote" => "true") if remote
-
-
request_token_tag = form_method == 'post' ? token_tag : ''
-
-
html_options = convert_options_to_data_attributes(options, html_options)
-
html_options['type'] = 'submit'
-
-
button = if block_given?
-
content_tag('button', html_options, &block)
-
else
-
html_options['value'] = name || url
-
tag('input', html_options)
-
end
-
-
inner_tags = method_tag.safe_concat(button).safe_concat(request_token_tag)
-
if params
-
params.each do |param_name, value|
-
inner_tags.safe_concat tag(:input, type: "hidden", name: param_name, value: value.to_param)
-
end
-
end
-
content_tag('form', content_tag('div', inner_tags), form_options)
-
end
-
-
# Creates a link tag of the given +name+ using a URL created by the set of
-
# +options+ unless the current request URI is the same as the links, in
-
# which case only the name is returned (or the given block is yielded, if
-
# one exists). You can give +link_to_unless_current+ a block which will
-
# specialize the default behavior (e.g., show a "Start Here" link rather
-
# than the link's text).
-
#
-
# ==== Examples
-
# Let's say you have a navigation menu...
-
#
-
# <ul id="navbar">
-
# <li><%= link_to_unless_current("Home", { action: "index" }) %></li>
-
# <li><%= link_to_unless_current("About Us", { action: "about" }) %></li>
-
# </ul>
-
#
-
# If in the "about" action, it will render...
-
#
-
# <ul id="navbar">
-
# <li><a href="/controller/index">Home</a></li>
-
# <li>About Us</li>
-
# </ul>
-
#
-
# ...but if in the "index" action, it will render:
-
#
-
# <ul id="navbar">
-
# <li>Home</li>
-
# <li><a href="/controller/about">About Us</a></li>
-
# </ul>
-
#
-
# The implicit block given to +link_to_unless_current+ is evaluated if the current
-
# action is the action given. So, if we had a comments page and wanted to render a
-
# "Go Back" link instead of a link to the comments page, we could do something like this...
-
#
-
# <%=
-
# link_to_unless_current("Comment", { controller: "comments", action: "new" }) do
-
# link_to("Go back", { controller: "posts", action: "index" })
-
# end
-
# %>
-
1
def link_to_unless_current(name, options = {}, html_options = {}, &block)
-
link_to_unless current_page?(options), name, options, html_options, &block
-
end
-
-
# Creates a link tag of the given +name+ using a URL created by the set of
-
# +options+ unless +condition+ is true, in which case only the name is
-
# returned. To specialize the default behavior (i.e., show a login link rather
-
# than just the plaintext link text), you can pass a block that
-
# accepts the name or the full argument list for +link_to_unless+.
-
#
-
# ==== Examples
-
# <%= link_to_unless(@current_user.nil?, "Reply", { action: "reply" }) %>
-
# # If the user is logged in...
-
# # => <a href="/controller/reply/">Reply</a>
-
#
-
# <%=
-
# link_to_unless(@current_user.nil?, "Reply", { action: "reply" }) do |name|
-
# link_to(name, { controller: "accounts", action: "signup" })
-
# end
-
# %>
-
# # If the user is logged in...
-
# # => <a href="/controller/reply/">Reply</a>
-
# # If not...
-
# # => <a href="/accounts/signup">Reply</a>
-
1
def link_to_unless(condition, name, options = {}, html_options = {}, &block)
-
if condition
-
if block_given?
-
block.arity <= 1 ? capture(name, &block) : capture(name, options, html_options, &block)
-
else
-
ERB::Util.html_escape(name)
-
end
-
else
-
link_to(name, options, html_options)
-
end
-
end
-
-
# Creates a link tag of the given +name+ using a URL created by the set of
-
# +options+ if +condition+ is true, otherwise only the name is
-
# returned. To specialize the default behavior, you can pass a block that
-
# accepts the name or the full argument list for +link_to_unless+ (see the examples
-
# in +link_to_unless+).
-
#
-
# ==== Examples
-
# <%= link_to_if(@current_user.nil?, "Login", { controller: "sessions", action: "new" }) %>
-
# # If the user isn't logged in...
-
# # => <a href="/sessions/new/">Login</a>
-
#
-
# <%=
-
# link_to_if(@current_user.nil?, "Login", { controller: "sessions", action: "new" }) do
-
# link_to(@current_user.login, { controller: "accounts", action: "show", id: @current_user })
-
# end
-
# %>
-
# # If the user isn't logged in...
-
# # => <a href="/sessions/new/">Login</a>
-
# # If they are logged in...
-
# # => <a href="/accounts/show/3">my_username</a>
-
1
def link_to_if(condition, name, options = {}, html_options = {}, &block)
-
link_to_unless !condition, name, options, html_options, &block
-
end
-
-
# Creates a mailto link tag to the specified +email_address+, which is
-
# also used as the name of the link unless +name+ is specified. Additional
-
# HTML attributes for the link can be passed in +html_options+.
-
#
-
# +mail_to+ has several methods for customizing the email itself by
-
# passing special keys to +html_options+.
-
#
-
# ==== Options
-
# * <tt>:subject</tt> - Preset the subject line of the email.
-
# * <tt>:body</tt> - Preset the body of the email.
-
# * <tt>:cc</tt> - Carbon Copy additional recipients on the email.
-
# * <tt>:bcc</tt> - Blind Carbon Copy additional recipients on the email.
-
#
-
# ==== Obfuscation
-
# Prior to Rails 4.0, +mail_to+ provided options for encoding the address
-
# in order to hinder email harvesters. To take advantage of these options,
-
# install the +actionview-encoded_mail_to+ gem.
-
#
-
# ==== Examples
-
# mail_to "me@domain.com"
-
# # => <a href="mailto:me@domain.com">me@domain.com</a>
-
#
-
# mail_to "me@domain.com", "My email"
-
# # => <a href="mailto:me@domain.com">My email</a>
-
#
-
# mail_to "me@domain.com", "My email", cc: "ccaddress@domain.com",
-
# subject: "This is an example email"
-
# # => <a href="mailto:me@domain.com?cc=ccaddress@domain.com&subject=This%20is%20an%20example%20email">My email</a>
-
#
-
# You can use a block as well if your link target is hard to fit into the name parameter. ERB example:
-
#
-
# <%= mail_to "me@domain.com" do %>
-
# <strong>Email me:</strong> <span>me@domain.com</span>
-
# <% end %>
-
# # => <a href="mailto:me@domain.com">
-
# <strong>Email me:</strong> <span>me@domain.com</span>
-
# </a>
-
1
def mail_to(email_address, name = nil, html_options = {}, &block)
-
email_address = ERB::Util.html_escape(email_address)
-
-
html_options, name = name, nil if block_given?
-
html_options = (html_options || {}).stringify_keys
-
-
extras = %w{ cc bcc body subject }.map! { |item|
-
option = html_options.delete(item) || next
-
"#{item}=#{Rack::Utils.escape_path(option)}"
-
}.compact
-
extras = extras.empty? ? '' : '?' + ERB::Util.html_escape(extras.join('&'))
-
-
html_options["href"] = "mailto:#{email_address}#{extras}".html_safe
-
-
content_tag(:a, name || email_address.html_safe, html_options, &block)
-
end
-
-
# True if the current request URI was generated by the given +options+.
-
#
-
# ==== Examples
-
# Let's say we're in the <tt>http://www.example.com/shop/checkout?order=desc</tt> action.
-
#
-
# current_page?(action: 'process')
-
# # => false
-
#
-
# current_page?(controller: 'shop', action: 'checkout')
-
# # => true
-
#
-
# current_page?(controller: 'shop', action: 'checkout', order: 'asc')
-
# # => false
-
#
-
# current_page?(action: 'checkout')
-
# # => true
-
#
-
# current_page?(controller: 'library', action: 'checkout')
-
# # => false
-
#
-
# current_page?('http://www.example.com/shop/checkout')
-
# # => true
-
#
-
# current_page?('/shop/checkout')
-
# # => true
-
#
-
# Let's say we're in the <tt>http://www.example.com/shop/checkout?order=desc&page=1</tt> action.
-
#
-
# current_page?(action: 'process')
-
# # => false
-
#
-
# current_page?(controller: 'shop', action: 'checkout')
-
# # => true
-
#
-
# current_page?(controller: 'shop', action: 'checkout', order: 'desc', page: '1')
-
# # => true
-
#
-
# current_page?(controller: 'shop', action: 'checkout', order: 'desc', page: '2')
-
# # => false
-
#
-
# current_page?(controller: 'shop', action: 'checkout', order: 'desc')
-
# # => false
-
#
-
# current_page?(action: 'checkout')
-
# # => true
-
#
-
# current_page?(controller: 'library', action: 'checkout')
-
# # => false
-
#
-
# Let's say we're in the <tt>http://www.example.com/products</tt> action with method POST in case of invalid product.
-
#
-
# current_page?(controller: 'product', action: 'index')
-
# # => false
-
#
-
1
def current_page?(options)
-
unless request
-
raise "You cannot use helpers that need to determine the current " \
-
"page unless your view context provides a Request object " \
-
"in a #request method"
-
end
-
-
return false unless request.get? || request.head?
-
-
url_string = URI.parser.unescape(url_for(options)).force_encoding(Encoding::BINARY)
-
-
# We ignore any extra parameters in the request_uri if the
-
# submitted url doesn't have any either. This lets the function
-
# work with things like ?order=asc
-
request_uri = url_string.index("?") ? request.fullpath : request.path
-
request_uri = URI.parser.unescape(request_uri).force_encoding(Encoding::BINARY)
-
-
if url_string =~ /^\w+:\/\//
-
url_string == "#{request.protocol}#{request.host_with_port}#{request_uri}"
-
else
-
url_string == request_uri
-
end
-
end
-
-
1
private
-
1
def convert_options_to_data_attributes(options, html_options)
-
if html_options
-
html_options = html_options.stringify_keys
-
html_options['data-remote'] = 'true' if link_to_remote_options?(options) || link_to_remote_options?(html_options)
-
-
method = html_options.delete('method')
-
-
add_method_to_attributes!(html_options, method) if method
-
-
html_options
-
else
-
link_to_remote_options?(options) ? {'data-remote' => 'true'} : {}
-
end
-
end
-
-
1
def link_to_remote_options?(options)
-
if options.is_a?(Hash)
-
options.delete('remote') || options.delete(:remote)
-
end
-
end
-
-
1
def add_method_to_attributes!(html_options, method)
-
if method && method.to_s.downcase != "get" && html_options["rel"] !~ /nofollow/
-
html_options["rel"] = "#{html_options["rel"]} nofollow".lstrip
-
end
-
html_options["data-method"] = method
-
end
-
-
# Processes the +html_options+ hash, converting the boolean
-
# attributes from true/false form into the form required by
-
# HTML/XHTML. (An attribute is considered to be boolean if
-
# its name is listed in the given +bool_attrs+ array.)
-
#
-
# More specifically, for each boolean attribute in +html_options+
-
# given as:
-
#
-
# "attr" => bool_value
-
#
-
# if the associated +bool_value+ evaluates to true, it is
-
# replaced with the attribute's name; otherwise the attribute is
-
# removed from the +html_options+ hash. (See the XHTML 1.0 spec,
-
# section 4.5 "Attribute Minimization" for more:
-
# http://www.w3.org/TR/xhtml1/#h-4.5)
-
#
-
# Returns the updated +html_options+ hash, which is also modified
-
# in place.
-
#
-
# Example:
-
#
-
# convert_boolean_attributes!( html_options,
-
# %w( checked disabled readonly ) )
-
1
def convert_boolean_attributes!(html_options, bool_attrs)
-
bool_attrs.each { |x| html_options[x] = x if html_options.delete(x) }
-
html_options
-
end
-
-
1
def token_tag(token=nil)
-
if token != false && protect_against_forgery?
-
token ||= form_authenticity_token
-
tag(:input, type: "hidden", name: request_forgery_protection_token.to_s, value: token)
-
else
-
''
-
end
-
end
-
-
1
def method_tag(method)
-
tag('input', type: 'hidden', name: '_method', value: method.to_s)
-
end
-
end
-
end
-
end
-
1
require "action_view/rendering"
-
1
require "active_support/core_ext/module/remove_method"
-
-
1
module ActionView
-
# Layouts reverse the common pattern of including shared headers and footers in many templates to isolate changes in
-
# repeated setups. The inclusion pattern has pages that look like this:
-
#
-
# <%= render "shared/header" %>
-
# Hello World
-
# <%= render "shared/footer" %>
-
#
-
# This approach is a decent way of keeping common structures isolated from the changing content, but it's verbose
-
# and if you ever want to change the structure of these two includes, you'll have to change all the templates.
-
#
-
# With layouts, you can flip it around and have the common structure know where to insert changing content. This means
-
# that the header and footer are only mentioned in one place, like this:
-
#
-
# // The header part of this layout
-
# <%= yield %>
-
# // The footer part of this layout
-
#
-
# And then you have content pages that look like this:
-
#
-
# hello world
-
#
-
# At rendering time, the content page is computed and then inserted in the layout, like this:
-
#
-
# // The header part of this layout
-
# hello world
-
# // The footer part of this layout
-
#
-
# == Accessing shared variables
-
#
-
# Layouts have access to variables specified in the content pages and vice versa. This allows you to have layouts with
-
# references that won't materialize before rendering time:
-
#
-
# <h1><%= @page_title %></h1>
-
# <%= yield %>
-
#
-
# ...and content pages that fulfill these references _at_ rendering time:
-
#
-
# <% @page_title = "Welcome" %>
-
# Off-world colonies offers you a chance to start a new life
-
#
-
# The result after rendering is:
-
#
-
# <h1>Welcome</h1>
-
# Off-world colonies offers you a chance to start a new life
-
#
-
# == Layout assignment
-
#
-
# You can either specify a layout declaratively (using the #layout class method) or give
-
# it the same name as your controller, and place it in <tt>app/views/layouts</tt>.
-
# If a subclass does not have a layout specified, it inherits its layout using normal Ruby inheritance.
-
#
-
# For instance, if you have PostsController and a template named <tt>app/views/layouts/posts.html.erb</tt>,
-
# that template will be used for all actions in PostsController and controllers inheriting
-
# from PostsController.
-
#
-
# If you use a module, for instance Weblog::PostsController, you will need a template named
-
# <tt>app/views/layouts/weblog/posts.html.erb</tt>.
-
#
-
# Since all your controllers inherit from ApplicationController, they will use
-
# <tt>app/views/layouts/application.html.erb</tt> if no other layout is specified
-
# or provided.
-
#
-
# == Inheritance Examples
-
#
-
# class BankController < ActionController::Base
-
# # bank.html.erb exists
-
#
-
# class ExchangeController < BankController
-
# # exchange.html.erb exists
-
#
-
# class CurrencyController < BankController
-
#
-
# class InformationController < BankController
-
# layout "information"
-
#
-
# class TellerController < InformationController
-
# # teller.html.erb exists
-
#
-
# class EmployeeController < InformationController
-
# # employee.html.erb exists
-
# layout nil
-
#
-
# class VaultController < BankController
-
# layout :access_level_layout
-
#
-
# class TillController < BankController
-
# layout false
-
#
-
# In these examples, we have three implicit lookup scenarios:
-
# * The BankController uses the "bank" layout.
-
# * The ExchangeController uses the "exchange" layout.
-
# * The CurrencyController inherits the layout from BankController.
-
#
-
# However, when a layout is explicitly set, the explicitly set layout wins:
-
# * The InformationController uses the "information" layout, explicitly set.
-
# * The TellerController also uses the "information" layout, because the parent explicitly set it.
-
# * The EmployeeController uses the "employee" layout, because it set the layout to nil, resetting the parent configuration.
-
# * The VaultController chooses a layout dynamically by calling the <tt>access_level_layout</tt> method.
-
# * The TillController does not use a layout at all.
-
#
-
# == Types of layouts
-
#
-
# Layouts are basically just regular templates, but the name of this template needs not be specified statically. Sometimes
-
# you want to alternate layouts depending on runtime information, such as whether someone is logged in or not. This can
-
# be done either by specifying a method reference as a symbol or using an inline method (as a proc).
-
#
-
# The method reference is the preferred approach to variable layouts and is used like this:
-
#
-
# class WeblogController < ActionController::Base
-
# layout :writers_and_readers
-
#
-
# def index
-
# # fetching posts
-
# end
-
#
-
# private
-
# def writers_and_readers
-
# logged_in? ? "writer_layout" : "reader_layout"
-
# end
-
# end
-
#
-
# Now when a new request for the index action is processed, the layout will vary depending on whether the person accessing
-
# is logged in or not.
-
#
-
# If you want to use an inline method, such as a proc, do something like this:
-
#
-
# class WeblogController < ActionController::Base
-
# layout proc { |controller| controller.logged_in? ? "writer_layout" : "reader_layout" }
-
# end
-
#
-
# If an argument isn't given to the proc, it's evaluated in the context of
-
# the current controller anyway.
-
#
-
# class WeblogController < ActionController::Base
-
# layout proc { logged_in? ? "writer_layout" : "reader_layout" }
-
# end
-
#
-
# Of course, the most common way of specifying a layout is still just as a plain template name:
-
#
-
# class WeblogController < ActionController::Base
-
# layout "weblog_standard"
-
# end
-
#
-
# The template will be looked always in <tt>app/views/layouts/</tt> folder. But you can point
-
# <tt>layouts</tt> folder direct also. <tt>layout "layouts/demo"</tt> is the same as <tt>layout "demo"</tt>.
-
#
-
# Setting the layout to nil forces it to be looked up in the filesystem and fallbacks to the parent behavior if none exists.
-
# Setting it to nil is useful to re-enable template lookup overriding a previous configuration set in the parent:
-
#
-
# class ApplicationController < ActionController::Base
-
# layout "application"
-
# end
-
#
-
# class PostsController < ApplicationController
-
# # Will use "application" layout
-
# end
-
#
-
# class CommentsController < ApplicationController
-
# # Will search for "comments" layout and fallback "application" layout
-
# layout nil
-
# end
-
#
-
# == Conditional layouts
-
#
-
# If you have a layout that by default is applied to all the actions of a controller, you still have the option of rendering
-
# a given action or set of actions without a layout, or restricting a layout to only a single action or a set of actions. The
-
# <tt>:only</tt> and <tt>:except</tt> options can be passed to the layout call. For example:
-
#
-
# class WeblogController < ActionController::Base
-
# layout "weblog_standard", except: :rss
-
#
-
# # ...
-
#
-
# end
-
#
-
# This will assign "weblog_standard" as the WeblogController's layout for all actions except for the +rss+ action, which will
-
# be rendered directly, without wrapping a layout around the rendered view.
-
#
-
# Both the <tt>:only</tt> and <tt>:except</tt> condition can accept an arbitrary number of method references, so
-
# #<tt>except: [ :rss, :text_only ]</tt> is valid, as is <tt>except: :rss</tt>.
-
#
-
# == Using a different layout in the action render call
-
#
-
# If most of your actions use the same layout, it makes perfect sense to define a controller-wide layout as described above.
-
# Sometimes you'll have exceptions where one action wants to use a different layout than the rest of the controller.
-
# You can do this by passing a <tt>:layout</tt> option to the <tt>render</tt> call. For example:
-
#
-
# class WeblogController < ActionController::Base
-
# layout "weblog_standard"
-
#
-
# def help
-
# render action: "help", layout: "help"
-
# end
-
# end
-
#
-
# This will override the controller-wide "weblog_standard" layout, and will render the help action with the "help" layout instead.
-
1
module Layouts
-
1
extend ActiveSupport::Concern
-
-
1
include ActionView::Rendering
-
-
1
included do
-
2
class_attribute :_layout, :_layout_conditions, :instance_accessor => false
-
2
self._layout = nil
-
2
self._layout_conditions = {}
-
2
_write_layout_method
-
end
-
-
1
delegate :_layout_conditions, to: :class
-
-
1
module ClassMethods
-
1
def inherited(klass) # :nodoc:
-
3
super
-
3
klass._write_layout_method
-
end
-
-
# This module is mixed in if layout conditions are provided. This means
-
# that if no layout conditions are used, this method is not used
-
1
module LayoutConditions # :nodoc:
-
1
private
-
-
# Determines whether the current action has a layout definition by
-
# checking the action name against the :only and :except conditions
-
# set by the <tt>layout</tt> method.
-
#
-
# ==== Returns
-
# * <tt> Boolean</tt> - True if the action has a layout definition, false otherwise.
-
1
def _conditional_layout?
-
return unless super
-
-
conditions = _layout_conditions
-
-
if only = conditions[:only]
-
only.include?(action_name)
-
elsif except = conditions[:except]
-
!except.include?(action_name)
-
else
-
true
-
end
-
end
-
end
-
-
# Specify the layout to use for this class.
-
#
-
# If the specified layout is a:
-
# String:: the String is the template name
-
# Symbol:: call the method specified by the symbol, which will return the template name
-
# false:: There is no layout
-
# true:: raise an ArgumentError
-
# nil:: Force default layout behavior with inheritance
-
#
-
# ==== Parameters
-
# * <tt>layout</tt> - The layout to use.
-
#
-
# ==== Options (conditions)
-
# * :only - A list of actions to apply this layout to.
-
# * :except - Apply this layout to all actions but this one.
-
1
def layout(layout, conditions = {})
-
include LayoutConditions unless conditions.empty?
-
-
conditions.each {|k, v| conditions[k] = Array(v).map {|a| a.to_s} }
-
self._layout_conditions = conditions
-
-
self._layout = layout
-
_write_layout_method
-
end
-
-
# Creates a _layout method to be called by _default_layout .
-
#
-
# If a layout is not explicitly mentioned then look for a layout with the controller's name.
-
# if nothing is found then try same procedure to find super class's layout.
-
1
def _write_layout_method # :nodoc:
-
5
remove_possible_method(:_layout)
-
-
5
prefixes = _implied_layout_name =~ /\blayouts/ ? [] : ["layouts"]
-
5
default_behavior = "lookup_context.find_all('#{_implied_layout_name}', #{prefixes.inspect}).first || super"
-
5
name_clause = if name
-
5
default_behavior
-
else
-
<<-RUBY
-
super
-
RUBY
-
end
-
-
5
layout_definition = case _layout
-
when String
-
_layout.inspect
-
when Symbol
-
<<-RUBY
-
#{_layout}.tap do |layout|
-
return #{default_behavior} if layout.nil?
-
unless layout.is_a?(String) || !layout
-
raise ArgumentError, "Your layout method :#{_layout} returned \#{layout}. It " \
-
"should have returned a String, false, or nil"
-
end
-
end
-
RUBY
-
when Proc
-
define_method :_layout_from_proc, &_layout
-
protected :_layout_from_proc
-
<<-RUBY
-
result = _layout_from_proc(#{_layout.arity == 0 ? '' : 'self'})
-
return #{default_behavior} if result.nil?
-
result
-
RUBY
-
when false
-
nil
-
when true
-
raise ArgumentError, "Layouts must be specified as a String, Symbol, Proc, false, or nil"
-
when nil
-
5
name_clause
-
end
-
-
5
self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
-
def _layout
-
if _conditional_layout?
-
#{layout_definition}
-
else
-
#{name_clause}
-
end
-
end
-
private :_layout
-
RUBY
-
end
-
-
1
private
-
-
# If no layout is supplied, look for a template named the return
-
# value of this method.
-
#
-
# ==== Returns
-
# * <tt>String</tt> - A template name
-
1
def _implied_layout_name # :nodoc:
-
10
controller_path
-
end
-
end
-
-
1
def _normalize_options(options) # :nodoc:
-
2
super
-
-
2
if _include_layout?(options)
-
4
layout = options.delete(:layout) { :default }
-
2
options[:layout] = _layout_for_option(layout)
-
end
-
end
-
-
1
attr_internal_writer :action_has_layout
-
-
1
def initialize(*) # :nodoc:
-
4
@_action_has_layout = true
-
4
super
-
end
-
-
# Controls whether an action should be rendered using a layout.
-
# If you want to disable any <tt>layout</tt> settings for the
-
# current action so that it is rendered without a layout then
-
# either override this method in your controller to return false
-
# for that action or set the <tt>action_has_layout</tt> attribute
-
# to false before rendering.
-
1
def action_has_layout?
-
2
@_action_has_layout
-
end
-
-
1
private
-
-
1
def _conditional_layout?
-
4
true
-
end
-
-
# This will be overwritten by _write_layout_method
-
1
def _layout; end
-
-
# Determine the layout for a given name, taking into account the name type.
-
#
-
# ==== Parameters
-
# * <tt>name</tt> - The name of the template
-
1
def _layout_for_option(name)
-
2
case name
-
when String then _normalize_layout(name)
-
when Proc then name
-
when true then Proc.new { _default_layout(true) }
-
4
when :default then Proc.new { _default_layout(false) }
-
when false, nil then nil
-
else
-
raise ArgumentError,
-
"String, Proc, :default, true, or false, expected for `layout'; you passed #{name.inspect}"
-
end
-
end
-
-
1
def _normalize_layout(value)
-
2
value.is_a?(String) && value !~ /\blayouts/ ? "layouts/#{value}" : value
-
end
-
-
# Returns the default layout for this controller.
-
# Optionally raises an exception if the layout could not be found.
-
#
-
# ==== Parameters
-
# * <tt>require_layout</tt> - If set to true and layout is not found,
-
# an ArgumentError exception is raised (defaults to false)
-
#
-
# ==== Returns
-
# * <tt>template</tt> - The template object for the default layout (or nil)
-
1
def _default_layout(require_layout = false)
-
2
begin
-
2
value = _layout if action_has_layout?
-
rescue NameError => e
-
raise e, "Could not render layout: #{e.message}"
-
end
-
-
2
if require_layout && action_has_layout? && !value
-
raise ArgumentError,
-
"There was no default layout for #{self.class} in #{view_paths.inspect}"
-
end
-
-
2
_normalize_layout(value)
-
end
-
-
1
def _include_layout?(options)
-
2
(options.keys & [:body, :text, :plain, :html, :inline, :partial]).empty? || options.key?(:layout)
-
end
-
end
-
end
-
1
require 'active_support/log_subscriber'
-
-
1
module ActionView
-
# = Action View Log Subscriber
-
#
-
# Provides functionality so that Rails can output logs from Action View.
-
1
class LogSubscriber < ActiveSupport::LogSubscriber
-
1
VIEWS_PATTERN = /^app\/views\//
-
-
1
def initialize
-
1
@root = nil
-
1
super
-
end
-
-
1
def render_template(event)
-
1
return unless logger.info?
-
1
message = " Rendered #{from_rails_root(event.payload[:identifier])}"
-
1
message << " within #{from_rails_root(event.payload[:layout])}" if event.payload[:layout]
-
1
message << " (#{event.duration.round(1)}ms)"
-
1
info(message)
-
end
-
1
alias :render_partial :render_template
-
1
alias :render_collection :render_template
-
-
1
def logger
-
5
ActionView::Base.logger
-
end
-
-
1
protected
-
-
1
EMPTY = ''
-
1
def from_rails_root(string)
-
2
string = string.sub(rails_root, EMPTY)
-
2
string.sub!(VIEWS_PATTERN, EMPTY)
-
2
string
-
end
-
-
1
def rails_root
-
2
@root ||= "#{Rails.root}/"
-
end
-
end
-
end
-
-
1
ActionView::LogSubscriber.attach_to :action_view
-
1
require 'thread_safe'
-
1
require 'active_support/core_ext/module/remove_method'
-
1
require 'active_support/core_ext/module/attribute_accessors'
-
1
require 'action_view/template/resolver'
-
-
1
module ActionView
-
# = Action View Lookup Context
-
#
-
# LookupContext is the object responsible to hold all information required to lookup
-
# templates, i.e. view paths and details. The LookupContext is also responsible to
-
# generate a key, given to view paths, used in the resolver cache lookup. Since
-
# this key is generated just once during the request, it speeds up all cache accesses.
-
1
class LookupContext #:nodoc:
-
1
attr_accessor :prefixes, :rendered_format
-
-
1
mattr_accessor :fallbacks
-
1
@@fallbacks = FallbackFileSystemResolver.instances
-
-
1
mattr_accessor :registered_details
-
1
self.registered_details = []
-
-
1
def self.register_detail(name, options = {}, &block)
-
4
self.registered_details << name
-
14
initialize = registered_details.map { |n| "@details[:#{n}] = details[:#{n}] || default_#{n}" }
-
-
4
Accessors.send :define_method, :"default_#{name}", &block
-
4
Accessors.module_eval <<-METHOD, __FILE__, __LINE__ + 1
-
def #{name}
-
@details.fetch(:#{name}, [])
-
end
-
-
def #{name}=(value)
-
value = value.present? ? Array(value) : default_#{name}
-
_set_detail(:#{name}, value) if value != @details[:#{name}]
-
end
-
-
remove_possible_method :initialize_details
-
def initialize_details(details)
-
#{initialize.join("\n")}
-
end
-
METHOD
-
end
-
-
# Holds accessors for the registered details.
-
1
module Accessors #:nodoc:
-
end
-
-
1
register_detail(:locale) do
-
4
locales = [I18n.locale]
-
4
locales.concat(I18n.fallbacks[I18n.locale]) if I18n.respond_to? :fallbacks
-
4
locales << I18n.default_locale
-
4
locales.uniq!
-
4
locales
-
end
-
13
register_detail(:formats) { ActionView::Base.default_formats || [:html, :text, :js, :css, :xml, :json] }
-
5
register_detail(:variants) { [] }
-
5
register_detail(:handlers){ Template::Handlers.extensions }
-
-
1
class DetailsKey #:nodoc:
-
1
alias :eql? :equal?
-
1
alias :object_hash :hash
-
-
1
attr_reader :hash
-
1
@details_keys = ThreadSafe::Cache.new
-
-
1
def self.get(details)
-
2
if details[:formats]
-
2
details = details.dup
-
2
syms = Set.new Mime::SET.symbols
-
2
details[:formats] = details[:formats].select { |v|
-
2
syms.include? v
-
}
-
end
-
2
@details_keys[details] ||= new
-
end
-
-
1
def self.clear
-
@details_keys.clear
-
end
-
-
1
def initialize
-
1
@hash = object_hash
-
end
-
end
-
-
# Add caching behavior on top of Details.
-
1
module DetailsCache
-
1
attr_accessor :cache
-
-
# Calculate the details key. Remove the handlers from calculation to improve performance
-
# since the user cannot modify it explicitly.
-
1
def details_key #:nodoc:
-
6
@details_key ||= DetailsKey.get(@details) if @cache
-
end
-
-
# Temporary skip passing the details_key forward.
-
1
def disable_cache
-
old_value, @cache = @cache, false
-
yield
-
ensure
-
@cache = old_value
-
end
-
-
1
protected
-
-
1
def _set_detail(key, value)
-
4
@details = @details.dup if @details_key
-
4
@details_key = nil
-
4
@details[key] = value
-
end
-
end
-
-
# Helpers related to template lookup using the lookup context information.
-
1
module ViewPaths
-
1
attr_reader :view_paths, :html_fallback_for_js
-
-
# Whenever setting view paths, makes a copy so we can manipulate then in
-
# instance objects as we wish.
-
1
def view_paths=(paths)
-
4
@view_paths = ActionView::PathSet.new(Array(paths))
-
end
-
-
1
def find(name, prefixes = [], partial = false, keys = [], options = {})
-
2
@view_paths.find(*args_for_lookup(name, prefixes, partial, keys, options))
-
end
-
1
alias :find_template :find
-
-
1
def find_all(name, prefixes = [], partial = false, keys = [], options = {})
-
4
@view_paths.find_all(*args_for_lookup(name, prefixes, partial, keys, options))
-
end
-
-
1
def exists?(name, prefixes = [], partial = false, keys = [], options = {})
-
@view_paths.exists?(*args_for_lookup(name, prefixes, partial, keys, options))
-
end
-
1
alias :template_exists? :exists?
-
-
# Add fallbacks to the view paths. Useful in cases you are rendering a :file.
-
1
def with_fallbacks
-
added_resolvers = 0
-
self.class.fallbacks.each do |resolver|
-
next if view_paths.include?(resolver)
-
view_paths.push(resolver)
-
added_resolvers += 1
-
end
-
yield
-
ensure
-
added_resolvers.times { view_paths.pop }
-
end
-
-
1
protected
-
-
1
def args_for_lookup(name, prefixes, partial, keys, details_options) #:nodoc:
-
6
name, prefixes = normalize_name(name, prefixes)
-
6
details, details_key = detail_args_for(details_options)
-
6
[name, prefixes, partial || false, details, details_key, keys]
-
end
-
-
# Compute details hash and key according to user options (e.g. passed from #render).
-
1
def detail_args_for(options)
-
6
return @details, details_key if options.empty? # most common path.
-
user_details = @details.merge(options)
-
-
if @cache
-
details_key = DetailsKey.get(user_details)
-
else
-
details_key = nil
-
end
-
-
[user_details, details_key]
-
end
-
-
# Support legacy foo.erb names even though we now ignore .erb
-
# as well as incorrectly putting part of the path in the template
-
# name instead of the prefix.
-
1
def normalize_name(name, prefixes) #:nodoc:
-
6
prefixes = prefixes.presence
-
6
parts = name.to_s.split('/')
-
6
parts.shift if parts.first.empty?
-
6
name = parts.pop
-
-
6
return name, prefixes || [""] if parts.empty?
-
-
parts = parts.join('/')
-
prefixes = prefixes ? prefixes.map { |p| "#{p}/#{parts}" } : [parts]
-
-
return name, prefixes
-
end
-
end
-
-
1
include Accessors
-
1
include DetailsCache
-
1
include ViewPaths
-
-
1
def initialize(view_paths, details = {}, prefixes = [])
-
4
@details, @details_key = {}, nil
-
4
@skip_default_locale = false
-
4
@cache = true
-
4
@prefixes = prefixes
-
4
@rendered_format = nil
-
-
4
self.view_paths = view_paths
-
4
initialize_details(details)
-
end
-
-
# Override formats= to expand ["*/*"] values and automatically
-
# add :html as fallback to :js.
-
1
def formats=(values)
-
16
if values
-
8
values.concat(default_formats) if values.delete "*/*"
-
8
if values == [:js]
-
values << :html
-
@html_fallback_for_js = true
-
end
-
end
-
16
super(values)
-
end
-
-
# Do not use the default locale on template lookup.
-
1
def skip_default_locale!
-
@skip_default_locale = true
-
self.locale = nil
-
end
-
-
# Override locale to return a symbol instead of array.
-
1
def locale
-
@details[:locale].first
-
end
-
-
# Overload locale= to also set the I18n.locale. If the current I18n.config object responds
-
# to original_config, it means that it's has a copy of the original I18n configuration and it's
-
# acting as proxy, which we need to skip.
-
1
def locale=(value)
-
if value
-
config = I18n.config.respond_to?(:original_config) ? I18n.config.original_config : I18n.config
-
config.locale = value
-
end
-
-
super(@skip_default_locale ? I18n.locale : default_locale)
-
end
-
-
# A method which only uses the first format in the formats array for layout lookup.
-
1
def with_layout_format
-
2
if formats.size == 1
-
2
yield
-
else
-
old_formats = formats
-
_set_detail(:formats, formats[0,1])
-
-
begin
-
yield
-
ensure
-
_set_detail(:formats, old_formats)
-
end
-
end
-
end
-
end
-
end
-
1
module ActionView
-
1
module ModelNaming
-
# Converts the given object to an ActiveModel compliant one.
-
1
def convert_to_model(object)
-
object.respond_to?(:to_model) ? object.to_model : object
-
end
-
-
1
def model_name_from_record_or_class(record_or_class)
-
(record_or_class.is_a?(Class) ? record_or_class : convert_to_model(record_or_class).class).model_name
-
end
-
end
-
end
-
1
module ActionView #:nodoc:
-
# = Action View PathSet
-
#
-
# This class is used to store and access paths in Action View. A number of
-
# operations are defined so that you can search among the paths in this
-
# set and also perform operations on other +PathSet+ objects.
-
#
-
# A +LookupContext+ will use a +PathSet+ to store the paths in its context.
-
1
class PathSet #:nodoc:
-
1
include Enumerable
-
-
1
attr_reader :paths
-
-
1
delegate :[], :include?, :pop, :size, :each, to: :paths
-
-
1
def initialize(paths = [])
-
20
@paths = typecast paths
-
end
-
-
1
def initialize_copy(other)
-
@paths = other.paths.dup
-
self
-
end
-
-
1
def to_ary
-
10
paths.dup
-
end
-
-
1
def compact
-
PathSet.new paths.compact
-
end
-
-
1
def +(array)
-
PathSet.new(paths + array)
-
end
-
-
1
%w(<< concat push insert unshift).each do |method|
-
5
class_eval <<-METHOD, __FILE__, __LINE__ + 1
-
def #{method}(*args)
-
paths.#{method}(*typecast(args))
-
end
-
METHOD
-
end
-
-
1
def find(*args)
-
2
find_all(*args).first || raise(MissingTemplate.new(self, *args))
-
end
-
-
1
def find_all(path, prefixes = [], *args)
-
12
prefixes = [prefixes] if String === prefixes
-
12
prefixes.each do |prefix|
-
12
paths.each do |resolver|
-
12
templates = resolver.find_all(path, prefix, *args)
-
12
return templates unless templates.empty?
-
end
-
end
-
4
[]
-
end
-
-
1
def exists?(path, prefixes, *args)
-
find_all(path, prefixes, *args).any?
-
end
-
-
1
private
-
-
1
def typecast(paths)
-
24
paths.map do |path|
-
18
case path
-
when Pathname, String
-
2
OptimizedFileSystemResolver.new path.to_s
-
else
-
16
path
-
end
-
end
-
end
-
end
-
end
-
1
require "action_view"
-
1
require "rails"
-
-
1
module ActionView
-
# = Action View Railtie
-
1
class Railtie < Rails::Railtie # :nodoc:
-
1
config.action_view = ActiveSupport::OrderedOptions.new
-
1
config.action_view.embed_authenticity_token_in_remote_forms = false
-
-
1
config.eager_load_namespaces << ActionView
-
-
1
initializer "action_view.embed_authenticity_token_in_remote_forms" do |app|
-
1
ActiveSupport.on_load(:action_view) do
-
1
ActionView::Helpers::FormTagHelper.embed_authenticity_token_in_remote_forms =
-
app.config.action_view.delete(:embed_authenticity_token_in_remote_forms)
-
end
-
end
-
-
1
initializer "action_view.logger" do
-
2
ActiveSupport.on_load(:action_view) { self.logger ||= Rails.logger }
-
end
-
-
1
initializer "action_view.set_configs" do |app|
-
1
ActiveSupport.on_load(:action_view) do
-
1
app.config.action_view.each do |k,v|
-
send "#{k}=", v
-
end
-
end
-
end
-
-
1
initializer "action_view.caching" do |app|
-
1
ActiveSupport.on_load(:action_view) do
-
1
if app.config.action_view.cache_template_loading.nil?
-
1
ActionView::Resolver.caching = app.config.cache_classes
-
end
-
end
-
end
-
-
1
initializer "action_view.setup_action_pack" do |app|
-
1
ActiveSupport.on_load(:action_controller) do
-
1
ActionView::RoutingUrlFor.send(:include, ActionDispatch::Routing::UrlFor)
-
end
-
end
-
-
1
rake_tasks do
-
load "action_view/tasks/dependencies.rake"
-
end
-
end
-
end
-
1
require 'active_support/core_ext/module'
-
1
require 'action_view/model_naming'
-
-
1
module ActionView
-
# The record identifier encapsulates a number of naming conventions for dealing with records, like Active Records or
-
# pretty much any other model type that has an id. These patterns are then used to try elevate the view actions to
-
# a higher logical level.
-
#
-
# # routes
-
# resources :posts
-
#
-
# # view
-
# <%= div_for(post) do %> <div id="post_45" class="post">
-
# <%= post.body %> What a wonderful world!
-
# <% end %> </div>
-
#
-
# # controller
-
# def update
-
# post = Post.find(params[:id])
-
# post.update(params[:post])
-
#
-
# redirect_to(post) # Calls polymorphic_url(post) which in turn calls post_url(post)
-
# end
-
#
-
# As the example above shows, you can stop caring to a large extent what the actual id of the post is.
-
# You just know that one is being assigned and that the subsequent calls in redirect_to expect that
-
# same naming convention and allows you to write less code if you follow it.
-
1
module RecordIdentifier
-
1
extend self
-
1
extend ModelNaming
-
-
1
include ModelNaming
-
-
1
JOIN = '_'.freeze
-
1
NEW = 'new'.freeze
-
-
# The DOM class convention is to use the singular form of an object or class.
-
#
-
# dom_class(post) # => "post"
-
# dom_class(Person) # => "person"
-
#
-
# If you need to address multiple instances of the same class in the same view, you can prefix the dom_class:
-
#
-
# dom_class(post, :edit) # => "edit_post"
-
# dom_class(Person, :edit) # => "edit_person"
-
1
def dom_class(record_or_class, prefix = nil)
-
singular = model_name_from_record_or_class(record_or_class).param_key
-
prefix ? "#{prefix}#{JOIN}#{singular}" : singular
-
end
-
-
# The DOM id convention is to use the singular form of an object or class with the id following an underscore.
-
# If no id is found, prefix with "new_" instead.
-
#
-
# dom_id(Post.find(45)) # => "post_45"
-
# dom_id(Post.new) # => "new_post"
-
#
-
# If you need to address multiple instances of the same class in the same view, you can prefix the dom_id:
-
#
-
# dom_id(Post.find(45), :edit) # => "edit_post_45"
-
# dom_id(Post.new, :custom) # => "custom_post"
-
1
def dom_id(record, prefix = nil)
-
if record_id = record_key_for_dom_id(record)
-
"#{dom_class(record, prefix)}#{JOIN}#{record_id}"
-
else
-
dom_class(record, prefix || NEW)
-
end
-
end
-
-
1
protected
-
-
# Returns a string representation of the key attribute(s) that is suitable for use in an HTML DOM id.
-
# This can be overwritten to customize the default generated string representation if desired.
-
# If you need to read back a key from a dom_id in order to query for the underlying database record,
-
# you should write a helper like 'person_record_from_dom_id' that will extract the key either based
-
# on the default implementation (which just joins all key attributes with '_') or on your own
-
# overwritten version of the method. By default, this implementation passes the key string through a
-
# method that replaces all characters that are invalid inside DOM ids, with valid ones. You need to
-
# make sure yourself that your dom ids are valid, in case you overwrite this method.
-
1
def record_key_for_dom_id(record)
-
key = convert_to_model(record).to_key
-
key ? key.join('_') : key
-
end
-
end
-
end
-
1
module ActionView
-
# This class defines the interface for a renderer. Each class that
-
# subclasses +AbstractRenderer+ is used by the base +Renderer+ class to
-
# render a specific type of object.
-
#
-
# The base +Renderer+ class uses its +render+ method to delegate to the
-
# renderers. These currently consist of
-
#
-
# PartialRenderer - Used for rendering partials
-
# TemplateRenderer - Used for rendering other types of templates
-
# StreamingTemplateRenderer - Used for streaming
-
#
-
# Whenever the +render+ method is called on the base +Renderer+ class, a new
-
# renderer object of the correct type is created, and the +render+ method on
-
# that new object is called in turn. This abstracts the setup and rendering
-
# into a separate classes for partials and templates.
-
1
class AbstractRenderer #:nodoc:
-
1
delegate :find_template, :template_exists?, :with_fallbacks, :with_layout_format, :formats, :to => :@lookup_context
-
-
1
def initialize(lookup_context)
-
2
@lookup_context = lookup_context
-
end
-
-
1
def render
-
raise NotImplementedError
-
end
-
-
1
protected
-
-
1
def extract_details(options)
-
2
@lookup_context.registered_details.each_with_object({}) do |key, details|
-
8
next unless value = options[key]
-
details[key] = Array(value)
-
end
-
end
-
-
1
def instrument(name, options={})
-
4
ActiveSupport::Notifications.instrument("render_#{name}.action_view", options){ yield }
-
end
-
-
1
def prepend_formats(formats)
-
2
formats = Array(formats)
-
2
return if formats.empty? || @lookup_context.html_fallback_for_js
-
2
@lookup_context.formats = formats | @lookup_context.formats
-
end
-
end
-
end
-
1
module ActionView
-
# This is the main entry point for rendering. It basically delegates
-
# to other objects like TemplateRenderer and PartialRenderer which
-
# actually renders the template.
-
#
-
# The Renderer will parse the options from the +render+ or +render_body+
-
# method and render a partial or a template based on the options. The
-
# +TemplateRenderer+ and +PartialRenderer+ objects are wrappers which do all
-
# the setup and logic necessary to render a view and a new object is created
-
# each time +render+ is called.
-
1
class Renderer
-
1
attr_accessor :lookup_context
-
-
1
def initialize(lookup_context)
-
2
@lookup_context = lookup_context
-
end
-
-
# Main render entry point shared by AV and AC.
-
1
def render(context, options)
-
2
if options.key?(:partial)
-
render_partial(context, options)
-
else
-
2
render_template(context, options)
-
end
-
end
-
-
# Render but returns a valid Rack body. If fibers are defined, we return
-
# a streaming body that renders the template piece by piece.
-
#
-
# Note that partials are not supported to be rendered with streaming,
-
# so in such cases, we just wrap them in an array.
-
1
def render_body(context, options)
-
if options.key?(:partial)
-
[render_partial(context, options)]
-
else
-
StreamingTemplateRenderer.new(@lookup_context).render(context, options)
-
end
-
end
-
-
# Direct accessor to template rendering.
-
1
def render_template(context, options) #:nodoc:
-
2
TemplateRenderer.new(@lookup_context).render(context, options)
-
end
-
-
# Direct access to partial rendering.
-
1
def render_partial(context, options, &block) #:nodoc:
-
PartialRenderer.new(@lookup_context).render(context, options, block)
-
end
-
end
-
end
-
1
require 'active_support/core_ext/object/try'
-
-
1
module ActionView
-
1
class TemplateRenderer < AbstractRenderer #:nodoc:
-
1
def render(context, options)
-
2
@view = context
-
2
@details = extract_details(options)
-
2
template = determine_template(options)
-
2
context = @lookup_context
-
-
2
prepend_formats(template.formats)
-
-
2
unless context.rendered_format
-
2
context.rendered_format = template.formats.first || formats.first
-
end
-
-
2
render_template(template, options[:layout], options[:locals])
-
end
-
-
# Determine the template to be rendered using the given options.
-
1
def determine_template(options) #:nodoc:
-
2
keys = options.fetch(:locals, {}).keys
-
-
2
if options.key?(:body)
-
Template::Text.new(options[:body])
-
2
elsif options.key?(:text)
-
Template::Text.new(options[:text], formats.first)
-
2
elsif options.key?(:plain)
-
Template::Text.new(options[:plain])
-
2
elsif options.key?(:html)
-
Template::HTML.new(options[:html], formats.first)
-
2
elsif options.key?(:file)
-
with_fallbacks { find_template(options[:file], nil, false, keys, @details) }
-
2
elsif options.key?(:inline)
-
handler = Template.handler_for_extension(options[:type] || "erb")
-
Template.new(options[:inline], "inline template", handler, :locals => keys)
-
2
elsif options.key?(:template)
-
2
if options[:template].respond_to?(:render)
-
options[:template]
-
else
-
2
find_template(options[:template], options[:prefixes], false, keys, @details)
-
end
-
else
-
raise ArgumentError, "You invoked render but did not give any of :partial, :template, :inline, :file, :plain, :text or :body option."
-
end
-
end
-
-
# Renders the given template. A string representing the layout can be
-
# supplied as well.
-
1
def render_template(template, layout_name = nil, locals = nil) #:nodoc:
-
2
view, locals = @view, locals || {}
-
-
2
render_with_layout(layout_name, locals) do |layout|
-
2
instrument(:template, :identifier => template.identifier, :layout => layout.try(:virtual_path)) do
-
2
template.render(view, locals) { |*name| view._layout_for(*name) }
-
end
-
end
-
end
-
-
1
def render_with_layout(path, locals) #:nodoc:
-
2
layout = path && find_layout(path, locals.keys)
-
2
content = yield(layout)
-
-
2
if layout
-
2
view = @view
-
2
view.view_flow.set(:layout, content)
-
2
layout.render(view, locals){ |*name| view._layout_for(*name) }
-
else
-
content
-
end
-
end
-
-
# This is the method which actually finds the layout using details in the lookup
-
# context object. If no layout is found, it checks if at least a layout with
-
# the given name exists across all details before raising the error.
-
1
def find_layout(layout, keys)
-
4
with_layout_format { resolve_layout(layout, keys) }
-
end
-
-
1
def resolve_layout(layout, keys)
-
4
case layout
-
when String
-
begin
-
if layout =~ /^\//
-
with_fallbacks { find_template(layout, nil, false, keys, @details) }
-
else
-
find_template(layout, nil, false, keys, @details)
-
end
-
rescue ActionView::MissingTemplate
-
all_details = @details.merge(:formats => @lookup_context.default_formats)
-
raise unless template_exists?(layout, nil, false, keys, all_details)
-
end
-
when Proc
-
2
resolve_layout(layout.call, keys)
-
when FalseClass
-
nil
-
else
-
2
layout
-
end
-
end
-
end
-
end
-
1
require "action_view/view_paths"
-
-
1
module ActionView
-
# This is a class to fix I18n global state. Whenever you provide I18n.locale during a request,
-
# it will trigger the lookup_context and consequently expire the cache.
-
1
class I18nProxy < ::I18n::Config #:nodoc:
-
1
attr_reader :original_config, :lookup_context
-
-
1
def initialize(original_config, lookup_context)
-
4
original_config = original_config.original_config if original_config.respond_to?(:original_config)
-
4
@original_config, @lookup_context = original_config, lookup_context
-
end
-
-
1
def locale
-
@original_config.locale
-
end
-
-
1
def locale=(value)
-
@lookup_context.locale = value
-
end
-
end
-
-
1
module Rendering
-
1
extend ActiveSupport::Concern
-
1
include ActionView::ViewPaths
-
-
# Overwrite process to setup I18n proxy.
-
1
def process(*) #:nodoc:
-
4
old_config, I18n.config = I18n.config, I18nProxy.new(I18n.config, lookup_context)
-
4
super
-
ensure
-
4
I18n.config = old_config
-
end
-
-
1
module ClassMethods
-
1
def view_context_class
-
@view_context_class ||= begin
-
1
routes = respond_to?(:_routes) && _routes
-
1
helpers = respond_to?(:_helpers) && _helpers
-
-
1
Class.new(ActionView::Base) do
-
1
if routes
-
1
include routes.url_helpers
-
1
include routes.mounted_helpers
-
end
-
-
1
if helpers
-
1
include helpers
-
end
-
end
-
2
end
-
end
-
end
-
-
1
attr_internal_writer :view_context_class
-
-
1
def view_context_class
-
2
@_view_context_class ||= self.class.view_context_class
-
end
-
-
# An instance of a view class. The default view class is ActionView::Base
-
#
-
# The view class must have the following methods:
-
# View.new[lookup_context, assigns, controller]
-
# Create a new ActionView instance for a controller
-
# View#render[options]
-
# Returns String with the rendered template
-
#
-
# Override this method in a module to change the default behavior.
-
1
def view_context
-
2
view_context_class.new(view_renderer, view_assigns, self)
-
end
-
-
# Returns an object that is able to render templates.
-
# :api: private
-
1
def view_renderer
-
4
@_view_renderer ||= ActionView::Renderer.new(lookup_context)
-
end
-
-
1
def render_to_body(options = {})
-
2
_process_options(options)
-
2
_render_template(options)
-
end
-
-
1
def rendered_format
-
4
Mime[lookup_context.rendered_format]
-
end
-
-
1
private
-
-
# Find and render a template based on the options given.
-
# :api: private
-
1
def _render_template(options) #:nodoc:
-
2
variant = options[:variant]
-
-
2
lookup_context.rendered_format = nil if options[:formats]
-
2
lookup_context.variants = variant if variant
-
-
2
view_renderer.render(view_context, options)
-
end
-
-
# Assign the rendered format to lookup context.
-
1
def _process_format(format, options = {}) #:nodoc:
-
2
super
-
2
lookup_context.formats = [format.to_sym]
-
2
lookup_context.rendered_format = lookup_context.formats.first
-
end
-
-
# Normalize args by converting render "foo" to render :action => "foo" and
-
# render "foo/bar" to render :file => "foo/bar".
-
# :api: private
-
1
def _normalize_args(action=nil, options={})
-
2
options = super(action, options)
-
2
case action
-
when NilClass
-
when Hash
-
options = action
-
when String, Symbol
-
action = action.to_s
-
key = action.include?(?/) ? :file : :action
-
options[key] = action
-
else
-
options[:partial] = action
-
end
-
-
2
options
-
end
-
-
# Normalize options.
-
# :api: private
-
1
def _normalize_options(options)
-
2
options = super(options)
-
2
if options[:partial] == true
-
options[:partial] = action_name
-
end
-
-
2
if (options.keys & [:partial, :file, :template]).empty?
-
2
options[:prefixes] ||= _prefixes
-
end
-
-
2
options[:template] ||= (options[:action] || action_name).to_s
-
2
options
-
end
-
end
-
end
-
1
module ActionView
-
1
module RoutingUrlFor
-
-
# Returns the URL for the set of +options+ provided. This takes the
-
# same options as +url_for+ in Action Controller (see the
-
# documentation for <tt>ActionController::Base#url_for</tt>). Note that by default
-
# <tt>:only_path</tt> is <tt>true</tt> so you'll get the relative "/controller/action"
-
# instead of the fully qualified URL like "http://example.com/controller/action".
-
#
-
# ==== Options
-
# * <tt>:anchor</tt> - Specifies the anchor name to be appended to the path.
-
# * <tt>:only_path</tt> - If true, returns the relative URL (omitting the protocol, host name, and port) (<tt>true</tt> by default unless <tt>:host</tt> is specified).
-
# * <tt>:trailing_slash</tt> - If true, adds a trailing slash, as in "/archive/2005/". Note that this
-
# is currently not recommended since it breaks caching.
-
# * <tt>:host</tt> - Overrides the default (current) host if provided.
-
# * <tt>:protocol</tt> - Overrides the default (current) protocol if provided.
-
# * <tt>:user</tt> - Inline HTTP authentication (only plucked out if <tt>:password</tt> is also present).
-
# * <tt>:password</tt> - Inline HTTP authentication (only plucked out if <tt>:user</tt> is also present).
-
#
-
# ==== Relying on named routes
-
#
-
# Passing a record (like an Active Record) instead of a hash as the options parameter will
-
# trigger the named route for that record. The lookup will happen on the name of the class. So passing a
-
# Workshop object will attempt to use the +workshop_path+ route. If you have a nested route, such as
-
# +admin_workshop_path+ you'll have to call that explicitly (it's impossible for +url_for+ to guess that route).
-
#
-
# ==== Implicit Controller Namespacing
-
#
-
# Controllers passed in using the +:controller+ option will retain their namespace unless it is an absolute one.
-
#
-
# ==== Examples
-
# <%= url_for(action: 'index') %>
-
# # => /blog/
-
#
-
# <%= url_for(action: 'find', controller: 'books') %>
-
# # => /books/find
-
#
-
# <%= url_for(action: 'login', controller: 'members', only_path: false, protocol: 'https') %>
-
# # => https://www.example.com/members/login/
-
#
-
# <%= url_for(action: 'play', anchor: 'player') %>
-
# # => /messages/play/#player
-
#
-
# <%= url_for(action: 'jump', anchor: 'tax&ship') %>
-
# # => /testing/jump/#tax&ship
-
#
-
# <%= url_for(Workshop.new) %>
-
# # relies on Workshop answering a persisted? call (and in this case returning false)
-
# # => /workshops
-
#
-
# <%= url_for(@workshop) %>
-
# # calls @workshop.to_param which by default returns the id
-
# # => /workshops/5
-
#
-
# # to_param can be re-defined in a model to provide different URL names:
-
# # => /workshops/1-workshop-name
-
#
-
# <%= url_for("http://www.example.com") %>
-
# # => http://www.example.com
-
#
-
# <%= url_for(:back) %>
-
# # if request.env["HTTP_REFERER"] is set to "http://www.example.com"
-
# # => http://www.example.com
-
#
-
# <%= url_for(:back) %>
-
# # if request.env["HTTP_REFERER"] is not set or is blank
-
# # => javascript:history.back()
-
#
-
# <%= url_for(action: 'index', controller: 'users') %>
-
# # Assuming an "admin" namespace
-
# # => /admin/users
-
#
-
# <%= url_for(action: 'index', controller: '/users') %>
-
# # Specify absolute path with beginning slash
-
# # => /users
-
1
def url_for(options = nil)
-
case options
-
when String
-
options
-
when nil, Hash
-
options ||= {}
-
options = { :only_path => options[:host].nil? }.merge!(options.symbolize_keys)
-
super
-
when :back
-
_back_url
-
when Array
-
polymorphic_path(options, options.extract_options!)
-
else
-
polymorphic_path(options)
-
end
-
end
-
-
1
def url_options #:nodoc:
-
return super unless controller.respond_to?(:url_options)
-
controller.url_options
-
end
-
-
1
def _routes_context #:nodoc:
-
controller
-
end
-
1
protected :_routes_context
-
-
1
def optimize_routes_generation? #:nodoc:
-
controller.respond_to?(:optimize_routes_generation?, true) ?
-
controller.optimize_routes_generation? : super
-
end
-
1
protected :optimize_routes_generation?
-
end
-
end
-
1
require 'active_support/core_ext/object/try'
-
1
require 'active_support/core_ext/kernel/singleton_class'
-
1
require 'thread'
-
-
1
module ActionView
-
# = Action View Template
-
1
class Template
-
1
extend ActiveSupport::Autoload
-
-
# === Encodings in ActionView::Template
-
#
-
# ActionView::Template is one of a few sources of potential
-
# encoding issues in Rails. This is because the source for
-
# templates are usually read from disk, and Ruby (like most
-
# encoding-aware programming languages) assumes that the
-
# String retrieved through File IO is encoded in the
-
# <tt>default_external</tt> encoding. In Rails, the default
-
# <tt>default_external</tt> encoding is UTF-8.
-
#
-
# As a result, if a user saves their template as ISO-8859-1
-
# (for instance, using a non-Unicode-aware text editor),
-
# and uses characters outside of the ASCII range, their
-
# users will see diamonds with question marks in them in
-
# the browser.
-
#
-
# For the rest of this documentation, when we say "UTF-8",
-
# we mean "UTF-8 or whatever the default_internal encoding
-
# is set to". By default, it will be UTF-8.
-
#
-
# To mitigate this problem, we use a few strategies:
-
# 1. If the source is not valid UTF-8, we raise an exception
-
# when the template is compiled to alert the user
-
# to the problem.
-
# 2. The user can specify the encoding using Ruby-style
-
# encoding comments in any template engine. If such
-
# a comment is supplied, Rails will apply that encoding
-
# to the resulting compiled source returned by the
-
# template handler.
-
# 3. In all cases, we transcode the resulting String to
-
# the UTF-8.
-
#
-
# This means that other parts of Rails can always assume
-
# that templates are encoded in UTF-8, even if the original
-
# source of the template was not UTF-8.
-
#
-
# From a user's perspective, the easiest thing to do is
-
# to save your templates as UTF-8. If you do this, you
-
# do not need to do anything else for things to "just work".
-
#
-
# === Instructions for template handlers
-
#
-
# The easiest thing for you to do is to simply ignore
-
# encodings. Rails will hand you the template source
-
# as the default_internal (generally UTF-8), raising
-
# an exception for the user before sending the template
-
# to you if it could not determine the original encoding.
-
#
-
# For the greatest simplicity, you can support only
-
# UTF-8 as the <tt>default_internal</tt>. This means
-
# that from the perspective of your handler, the
-
# entire pipeline is just UTF-8.
-
#
-
# === Advanced: Handlers with alternate metadata sources
-
#
-
# If you want to provide an alternate mechanism for
-
# specifying encodings (like ERB does via <%# encoding: ... %>),
-
# you may indicate that you will handle encodings yourself
-
# by implementing <tt>self.handles_encoding?</tt>
-
# on your handler.
-
#
-
# If you do, Rails will not try to encode the String
-
# into the default_internal, passing you the unaltered
-
# bytes tagged with the assumed encoding (from
-
# default_external).
-
#
-
# In this case, make sure you return a String from
-
# your handler encoded in the default_internal. Since
-
# you are handling out-of-band metadata, you are
-
# also responsible for alerting the user to any
-
# problems with converting the user's data to
-
# the <tt>default_internal</tt>.
-
#
-
# To do so, simply raise +WrongEncodingError+ as follows:
-
#
-
# raise WrongEncodingError.new(
-
# problematic_string,
-
# expected_encoding
-
# )
-
-
1
eager_autoload do
-
1
autoload :Error
-
1
autoload :Handlers
-
1
autoload :HTML
-
1
autoload :Text
-
1
autoload :Types
-
end
-
-
1
extend Template::Handlers
-
-
1
attr_accessor :locals, :formats, :variants, :virtual_path
-
-
1
attr_reader :source, :identifier, :handler, :original_encoding, :updated_at
-
-
# This finalizer is needed (and exactly with a proc inside another proc)
-
# otherwise templates leak in development.
-
1
Finalizer = proc do |method_name, mod|
-
4
proc do
-
mod.module_eval do
-
remove_possible_method method_name
-
end
-
end
-
end
-
-
1
def initialize(source, identifier, handler, details)
-
6
format = details[:format] || (handler.default_format if handler.respond_to?(:default_format))
-
-
6
@source = source
-
6
@identifier = identifier
-
6
@handler = handler
-
6
@compiled = false
-
6
@original_encoding = nil
-
6
@locals = details[:locals] || []
-
6
@virtual_path = details[:virtual_path]
-
6
@updated_at = details[:updated_at] || Time.now
-
12
@formats = Array(format).map { |f| f.respond_to?(:ref) ? f.ref : f }
-
6
@variants = [details[:variant]]
-
6
@compile_mutex = Mutex.new
-
end
-
-
# Returns if the underlying handler supports streaming. If so,
-
# a streaming buffer *may* be passed when it start rendering.
-
1
def supports_streaming?
-
handler.respond_to?(:supports_streaming?) && handler.supports_streaming?
-
end
-
-
# Render a template. If the template was not compiled yet, it is done
-
# exactly before rendering.
-
#
-
# This method is instrumented as "!render_template.action_view". Notice that
-
# we use a bang in this instrumentation because you don't want to
-
# consume this in production. This is only slow if it's being listened to.
-
1
def render(view, locals, buffer=nil, &block)
-
4
instrument("!render_template") do
-
4
compile!(view)
-
4
view.send(method_name, locals, buffer, &block)
-
end
-
rescue => e
-
handle_render_error(view, e)
-
end
-
-
1
def type
-
@type ||= Types[@formats.first] if @formats.first
-
end
-
-
# Receives a view object and return a template similar to self by using @virtual_path.
-
#
-
# This method is useful if you have a template object but it does not contain its source
-
# anymore since it was already compiled. In such cases, all you need to do is to call
-
# refresh passing in the view object.
-
#
-
# Notice this method raises an error if the template to be refreshed does not have a
-
# virtual path set (true just for inline templates).
-
1
def refresh(view)
-
raise "A template needs to have a virtual path in order to be refreshed" unless @virtual_path
-
lookup = view.lookup_context
-
pieces = @virtual_path.split("/")
-
name = pieces.pop
-
partial = !!name.sub!(/^_/, "")
-
lookup.disable_cache do
-
lookup.find_template(name, [ pieces.join('/') ], partial, @locals)
-
end
-
end
-
-
1
def inspect
-
4
@inspect ||= defined?(Rails.root) ? identifier.sub("#{Rails.root}/", '') : identifier
-
end
-
-
# This method is responsible for properly setting the encoding of the
-
# source. Until this point, we assume that the source is BINARY data.
-
# If no additional information is supplied, we assume the encoding is
-
# the same as <tt>Encoding.default_external</tt>.
-
#
-
# The user can also specify the encoding via a comment on the first
-
# line of the template (# encoding: NAME-OF-ENCODING). This will work
-
# with any template engine, as we process out the encoding comment
-
# before passing the source on to the template engine, leaving a
-
# blank line in its stead.
-
1
def encode!
-
4
return unless source.encoding == Encoding::BINARY
-
-
# Look for # encoding: *. If we find one, we'll encode the
-
# String in that encoding, otherwise, we'll use the
-
# default external encoding.
-
if source.sub!(/\A#{ENCODING_FLAG}/, '')
-
encoding = magic_encoding = $1
-
else
-
encoding = Encoding.default_external
-
end
-
-
# Tag the source with the default external encoding
-
# or the encoding specified in the file
-
source.force_encoding(encoding)
-
-
# If the user didn't specify an encoding, and the handler
-
# handles encodings, we simply pass the String as is to
-
# the handler (with the default_external tag)
-
if !magic_encoding && @handler.respond_to?(:handles_encoding?) && @handler.handles_encoding?
-
source
-
# Otherwise, if the String is valid in the encoding,
-
# encode immediately to default_internal. This means
-
# that if a handler doesn't handle encodings, it will
-
# always get Strings in the default_internal
-
elsif source.valid_encoding?
-
source.encode!
-
# Otherwise, since the String is invalid in the encoding
-
# specified, raise an exception
-
else
-
raise WrongEncodingError.new(source, encoding)
-
end
-
end
-
-
1
protected
-
-
# Compile a template. This method ensures a template is compiled
-
# just once and removes the source after it is compiled.
-
1
def compile!(view) #:nodoc:
-
4
return if @compiled
-
-
# Templates can be used concurrently in threaded environments
-
# so compilation and any instance variable modification must
-
# be synchronized
-
4
@compile_mutex.synchronize do
-
# Any thread holding this lock will be compiling the template needed
-
# by the threads waiting. So re-check the @compiled flag to avoid
-
# re-compilation
-
4
return if @compiled
-
-
4
if view.is_a?(ActionView::CompiledTemplates)
-
4
mod = ActionView::CompiledTemplates
-
else
-
mod = view.singleton_class
-
end
-
-
4
instrument("!compile_template") do
-
4
compile(view, mod)
-
end
-
-
# Just discard the source if we have a virtual path. This
-
# means we can get the template back.
-
4
@source = nil if @virtual_path
-
4
@compiled = true
-
end
-
end
-
-
# Among other things, this method is responsible for properly setting
-
# the encoding of the compiled template.
-
#
-
# If the template engine handles encodings, we send the encoded
-
# String to the engine without further processing. This allows
-
# the template engine to support additional mechanisms for
-
# specifying the encoding. For instance, ERB supports <%# encoding: %>
-
#
-
# Otherwise, after we figure out the correct encoding, we then
-
# encode the source into <tt>Encoding.default_internal</tt>.
-
# In general, this means that templates will be UTF-8 inside of Rails,
-
# regardless of the original source encoding.
-
1
def compile(view, mod) #:nodoc:
-
4
encode!
-
4
method_name = self.method_name
-
4
code = @handler.call(self)
-
-
# Make sure that the resulting String to be eval'd is in the
-
# encoding of the code
-
4
source = <<-end_src
-
def #{method_name}(local_assigns, output_buffer)
-
_old_virtual_path, @virtual_path = @virtual_path, #{@virtual_path.inspect};_old_output_buffer = @output_buffer;#{locals_code};#{code}
-
ensure
-
@virtual_path, @output_buffer = _old_virtual_path, _old_output_buffer
-
end
-
end_src
-
-
# Make sure the source is in the encoding of the returned code
-
4
source.force_encoding(code.encoding)
-
-
# In case we get back a String from a handler that is not in
-
# BINARY or the default_internal, encode it to the default_internal
-
4
source.encode!
-
-
# Now, validate that the source we got back from the template
-
# handler is valid in the default_internal. This is for handlers
-
# that handle encoding but screw up
-
4
unless source.valid_encoding?
-
raise WrongEncodingError.new(@source, Encoding.default_internal)
-
end
-
-
4
begin
-
4
mod.module_eval(source, identifier, 0)
-
4
ObjectSpace.define_finalizer(self, Finalizer[method_name, mod])
-
rescue => e # errors from template code
-
if logger = (view && view.logger)
-
logger.debug "ERROR: compiling #{method_name} RAISED #{e}"
-
logger.debug "Function body: #{source}"
-
logger.debug "Backtrace: #{e.backtrace.join("\n")}"
-
end
-
-
raise ActionView::Template::Error.new(self, e)
-
end
-
end
-
-
1
def handle_render_error(view, e) #:nodoc:
-
if e.is_a?(Template::Error)
-
e.sub_template_of(self)
-
raise e
-
else
-
template = self
-
unless template.source
-
template = refresh(view)
-
template.encode!
-
end
-
raise Template::Error.new(template, e)
-
end
-
end
-
-
1
def locals_code #:nodoc:
-
# Double assign to suppress the dreaded 'assigned but unused variable' warning
-
4
@locals.map { |key| "#{key} = #{key} = local_assigns[:#{key}];" }.join
-
end
-
-
1
def method_name #:nodoc:
-
8
@method_name ||= "_#{identifier_method_name}__#{@identifier.hash}_#{__id__}".gsub('-', "_")
-
end
-
-
1
def identifier_method_name #:nodoc:
-
4
inspect.gsub(/[^a-z_]/, '_')
-
end
-
-
1
def instrument(action, &block)
-
8
payload = { virtual_path: @virtual_path, identifier: @identifier }
-
8
ActiveSupport::Notifications.instrument("#{action}.action_view", payload, &block)
-
end
-
end
-
end
-
1
module ActionView #:nodoc:
-
# = Action View Template Handlers
-
1
class Template
-
1
module Handlers #:nodoc:
-
1
autoload :ERB, 'action_view/template/handlers/erb'
-
1
autoload :Builder, 'action_view/template/handlers/builder'
-
1
autoload :Raw, 'action_view/template/handlers/raw'
-
-
1
def self.extended(base)
-
1
base.register_default_template_handler :erb, ERB.new
-
1
base.register_template_handler :builder, Builder.new
-
1
base.register_template_handler :raw, Raw.new
-
1
base.register_template_handler :ruby, :source.to_proc
-
end
-
-
1
@@template_handlers = {}
-
1
@@default_template_handlers = nil
-
-
1
def self.extensions
-
4
@@template_extensions ||= @@template_handlers.keys
-
end
-
-
# Register an object that knows how to handle template files with the given
-
# extensions. This can be used to implement new template types.
-
# The handler must respond to `:call`, which will be passed the template
-
# and should return the rendered template as a String.
-
1
def register_template_handler(*extensions, handler)
-
6
raise(ArgumentError, "Extension is required") if extensions.empty?
-
6
extensions.each do |extension|
-
6
@@template_handlers[extension.to_sym] = handler
-
end
-
6
@@template_extensions = nil
-
end
-
-
1
def template_handler_extensions
-
@@template_handlers.keys.map {|key| key.to_s }.sort
-
end
-
-
1
def registered_template_handler(extension)
-
4
extension && @@template_handlers[extension.to_sym]
-
end
-
-
1
def register_default_template_handler(extension, klass)
-
1
register_template_handler(extension, klass)
-
1
@@default_template_handlers = klass
-
end
-
-
1
def handler_for_extension(extension)
-
4
registered_template_handler(extension) || @@default_template_handlers
-
end
-
end
-
end
-
end
-
1
module ActionView
-
1
module Template::Handlers
-
1
class Builder
-
# Default format used by Builder.
-
1
class_attribute :default_format
-
1
self.default_format = :xml
-
-
1
def call(template)
-
require_engine
-
"xml = ::Builder::XmlMarkup.new(:indent => 2);" +
-
"self.output_buffer = xml.target!;" +
-
template.source +
-
";xml.target!;"
-
end
-
-
1
protected
-
-
1
def require_engine
-
@required ||= begin
-
require "builder"
-
true
-
end
-
end
-
end
-
end
-
end
-
1
require 'erubis'
-
-
1
module ActionView
-
1
class Template
-
1
module Handlers
-
1
class Erubis < ::Erubis::Eruby
-
1
def add_preamble(src)
-
@newline_pending = 0
-
src << "@output_buffer = output_buffer || ActionView::OutputBuffer.new;"
-
end
-
-
1
def add_text(src, text)
-
return if text.empty?
-
-
if text == "\n"
-
@newline_pending += 1
-
else
-
src << "@output_buffer.safe_append='"
-
src << "\n" * @newline_pending if @newline_pending > 0
-
src << escape_text(text)
-
src << "'.freeze;"
-
-
@newline_pending = 0
-
end
-
end
-
-
# Erubis toggles <%= and <%== behavior when escaping is enabled.
-
# We override to always treat <%== as escaped.
-
1
def add_expr(src, code, indicator)
-
case indicator
-
when '=='
-
add_expr_escaped(src, code)
-
else
-
super
-
end
-
end
-
-
1
BLOCK_EXPR = /\s+(do|\{)(\s*\|[^|]*\|)?\s*\Z/
-
-
1
def add_expr_literal(src, code)
-
flush_newline_if_pending(src)
-
if code =~ BLOCK_EXPR
-
src << '@output_buffer.append= ' << code
-
else
-
src << '@output_buffer.append=(' << code << ');'
-
end
-
end
-
-
1
def add_expr_escaped(src, code)
-
flush_newline_if_pending(src)
-
if code =~ BLOCK_EXPR
-
src << "@output_buffer.safe_append= " << code
-
else
-
src << "@output_buffer.safe_append=(" << code << ");"
-
end
-
end
-
-
1
def add_stmt(src, code)
-
flush_newline_if_pending(src)
-
super
-
end
-
-
1
def add_postamble(src)
-
flush_newline_if_pending(src)
-
src << '@output_buffer.to_s'
-
end
-
-
1
def flush_newline_if_pending(src)
-
if @newline_pending > 0
-
src << "@output_buffer.safe_append='#{"\n" * @newline_pending}'.freeze;"
-
@newline_pending = 0
-
end
-
end
-
end
-
-
1
class ERB
-
# Specify trim mode for the ERB compiler. Defaults to '-'.
-
# See ERB documentation for suitable values.
-
1
class_attribute :erb_trim_mode
-
1
self.erb_trim_mode = '-'
-
-
# Default implementation used.
-
1
class_attribute :erb_implementation
-
1
self.erb_implementation = Erubis
-
-
# Do not escape templates of these mime types.
-
1
class_attribute :escape_whitelist
-
1
self.escape_whitelist = ["text/plain"]
-
-
1
ENCODING_TAG = Regexp.new("\\A(<%#{ENCODING_FLAG}-?%>)[ \\t]*")
-
-
1
def self.call(template)
-
new.call(template)
-
end
-
-
1
def supports_streaming?
-
true
-
end
-
-
1
def handles_encoding?
-
true
-
end
-
-
1
def call(template)
-
# First, convert to BINARY, so in case the encoding is
-
# wrong, we can still find an encoding tag
-
# (<%# encoding %>) inside the String using a regular
-
# expression
-
template_source = template.source.dup.force_encoding(Encoding::ASCII_8BIT)
-
-
erb = template_source.gsub(ENCODING_TAG, '')
-
encoding = $2
-
-
erb.force_encoding valid_encoding(template.source.dup, encoding)
-
-
# Always make sure we return a String in the default_internal
-
erb.encode!
-
-
self.class.erb_implementation.new(
-
erb,
-
:escape => (self.class.escape_whitelist.include? template.type),
-
:trim => (self.class.erb_trim_mode == "-")
-
).src
-
end
-
-
1
private
-
-
1
def valid_encoding(string, encoding)
-
# If a magic encoding comment was found, tag the
-
# String with this encoding. This is for a case
-
# where the original String was assumed to be,
-
# for instance, UTF-8, but a magic comment
-
# proved otherwise
-
string.force_encoding(encoding) if encoding
-
-
# If the String is valid, return the encoding we found
-
return string.encoding if string.valid_encoding?
-
-
# Otherwise, raise an exception
-
raise WrongEncodingError.new(string, string.encoding)
-
end
-
end
-
end
-
end
-
end
-
1
module ActionView
-
1
module Template::Handlers
-
1
class Raw
-
1
def call(template)
-
escaped = template.source.gsub(':', '\:')
-
-
'%q:' + escaped + ':;'
-
end
-
end
-
end
-
end
-
1
require "pathname"
-
1
require "active_support/core_ext/class"
-
1
require "active_support/core_ext/module/attribute_accessors"
-
1
require "action_view/template"
-
1
require "thread"
-
1
require "thread_safe"
-
-
1
module ActionView
-
# = Action View Resolver
-
1
class Resolver
-
# Keeps all information about view path and builds virtual path.
-
1
class Path
-
1
attr_reader :name, :prefix, :partial, :virtual
-
1
alias_method :partial?, :partial
-
-
1
def self.build(name, prefix, partial)
-
3
virtual = ""
-
3
virtual << "#{prefix}/" unless prefix.empty?
-
3
virtual << (partial ? "_#{name}" : name)
-
3
new name, prefix, partial, virtual
-
end
-
-
1
def initialize(name, prefix, partial, virtual)
-
3
@name = name
-
3
@prefix = prefix
-
3
@partial = partial
-
3
@virtual = virtual
-
end
-
-
1
def to_str
-
3
@virtual
-
end
-
1
alias :to_s :to_str
-
end
-
-
# Threadsafe template cache
-
1
class Cache #:nodoc:
-
1
class SmallCache < ThreadSafe::Cache
-
1
def initialize(options = {})
-
14
super(options.merge(:initial_capacity => 2))
-
end
-
end
-
-
# preallocate all the default blocks for performance/memory consumption reasons
-
4
PARTIAL_BLOCK = lambda {|cache, partial| cache[partial] = SmallCache.new}
-
4
PREFIX_BLOCK = lambda {|cache, prefix| cache[prefix] = SmallCache.new(&PARTIAL_BLOCK)}
-
4
NAME_BLOCK = lambda {|cache, name| cache[name] = SmallCache.new(&PREFIX_BLOCK)}
-
2
KEY_BLOCK = lambda {|cache, key| cache[key] = SmallCache.new(&NAME_BLOCK)}
-
-
# usually a majority of template look ups return nothing, use this canonical preallocated array to save memory
-
1
NO_TEMPLATES = [].freeze
-
-
1
def initialize
-
4
@data = SmallCache.new(&KEY_BLOCK)
-
end
-
-
# Cache the templates returned by the block
-
1
def cache(key, name, prefix, partial, locals)
-
6
if Resolver.caching?
-
6
@data[key][name][prefix][partial][locals] ||= canonical_no_templates(yield)
-
else
-
fresh_templates = yield
-
cached_templates = @data[key][name][prefix][partial][locals]
-
-
if templates_have_changed?(cached_templates, fresh_templates)
-
@data[key][name][prefix][partial][locals] = canonical_no_templates(fresh_templates)
-
else
-
cached_templates || NO_TEMPLATES
-
end
-
end
-
end
-
-
1
def clear
-
@data.clear
-
end
-
-
1
private
-
-
1
def canonical_no_templates(templates)
-
3
templates.empty? ? NO_TEMPLATES : templates
-
end
-
-
1
def templates_have_changed?(cached_templates, fresh_templates)
-
# if either the old or new template list is empty, we don't need to (and can't)
-
# compare modification times, and instead just check whether the lists are different
-
if cached_templates.blank? || fresh_templates.blank?
-
return fresh_templates.blank? != cached_templates.blank?
-
end
-
-
cached_templates_max_updated_at = cached_templates.map(&:updated_at).max
-
-
# if a template has changed, it will be now be newer than all the cached templates
-
fresh_templates.any? { |t| t.updated_at > cached_templates_max_updated_at }
-
end
-
end
-
-
1
cattr_accessor :caching
-
1
self.caching = true
-
-
1
class << self
-
1
alias :caching? :caching
-
end
-
-
1
def initialize
-
4
@cache = Cache.new
-
end
-
-
1
def clear_cache
-
@cache.clear
-
end
-
-
# Normalizes the arguments and passes it on to find_templates.
-
1
def find_all(name, prefix=nil, partial=false, details={}, key=nil, locals=[])
-
6
cached(key, [name, prefix, partial], details, locals) do
-
3
find_templates(name, prefix, partial, details)
-
end
-
end
-
-
1
private
-
-
1
delegate :caching?, to: :class
-
-
# This is what child classes implement. No defaults are needed
-
# because Resolver guarantees that the arguments are present and
-
# normalized.
-
1
def find_templates(name, prefix, partial, details)
-
raise NotImplementedError, "Subclasses must implement a find_templates(name, prefix, partial, details) method"
-
end
-
-
# Helpers that builds a path. Useful for building virtual paths.
-
1
def build_path(name, prefix, partial)
-
Path.build(name, prefix, partial)
-
end
-
-
# Handles templates caching. If a key is given and caching is on
-
# always check the cache before hitting the resolver. Otherwise,
-
# it always hits the resolver but if the key is present, check if the
-
# resolver is fresher before returning it.
-
1
def cached(key, path_info, details, locals) #:nodoc:
-
6
name, prefix, partial = path_info
-
6
locals = locals.map { |x| x.to_s }.sort!
-
-
6
if key
-
6
@cache.cache(key, name, prefix, partial, locals) do
-
3
decorate(yield, path_info, details, locals)
-
end
-
else
-
decorate(yield, path_info, details, locals)
-
end
-
end
-
-
# Ensures all the resolver information is set in the template.
-
1
def decorate(templates, path_info, details, locals) #:nodoc:
-
3
cached = nil
-
3
templates.each do |t|
-
2
t.locals = locals
-
2
t.formats = details[:formats] || [:html] if t.formats.empty?
-
2
t.variants = details[:variants] || [] if t.variants.empty?
-
2
t.virtual_path ||= (cached ||= build_path(*path_info))
-
end
-
end
-
end
-
-
# An abstract class that implements a Resolver with path semantics.
-
1
class PathResolver < Resolver #:nodoc:
-
1
EXTENSIONS = { :locale => ".", :formats => ".", :variants => "+", :handlers => "." }
-
1
DEFAULT_PATTERN = ":prefix/:action{.:locale,}{.:formats,}{+:variants,}{.:handlers,}"
-
-
1
def initialize(pattern=nil)
-
4
@pattern = pattern || DEFAULT_PATTERN
-
4
super()
-
end
-
-
1
private
-
-
1
def find_templates(name, prefix, partial, details)
-
3
path = Path.build(name, prefix, partial)
-
3
query(path, details, details[:formats])
-
end
-
-
1
def query(path, details, formats)
-
3
query = build_query(path, details)
-
-
3
template_paths = find_template_paths query
-
-
3
template_paths.map { |template|
-
2
handler, format, variant = extract_handler_and_format_and_variant(template, formats)
-
2
contents = File.binread(template)
-
-
2
Template.new(contents, File.expand_path(template), handler,
-
:virtual_path => path.virtual,
-
:format => format,
-
:variant => variant,
-
:updated_at => mtime(template)
-
)
-
}
-
end
-
-
1
if RUBY_VERSION >= '2.2.0'
-
def find_template_paths(query)
-
Dir[query].reject { |filename|
-
File.directory?(filename) ||
-
# deals with case-insensitive file systems.
-
!File.fnmatch(query, filename, File::FNM_EXTGLOB)
-
}
-
end
-
else
-
1
def find_template_paths(query)
-
# deals with case-insensitive file systems.
-
5
sanitizer = Hash.new { |h,dir| h[dir] = Dir["#{dir}/*"] }
-
-
3
Dir[query].reject { |filename|
-
File.directory?(filename) ||
-
2
!sanitizer[File.dirname(filename)].include?(filename)
-
}
-
end
-
end
-
-
# Helper for building query glob string based on resolver's pattern.
-
1
def build_query(path, details)
-
query = @pattern.dup
-
-
prefix = path.prefix.empty? ? "" : "#{escape_entry(path.prefix)}\\1"
-
query.gsub!(/\:prefix(\/)?/, prefix)
-
-
partial = escape_entry(path.partial? ? "_#{path.name}" : path.name)
-
query.gsub!(/\:action/, partial)
-
-
details.each do |ext, variants|
-
query.gsub!(/\:#{ext}/, "{#{variants.compact.uniq.join(',')}}")
-
end
-
-
File.expand_path(query, @path)
-
end
-
-
1
def escape_entry(entry)
-
3
entry.gsub(/[*?{}\[\]]/, '\\\\\\&')
-
end
-
-
# Returns the file mtime from the filesystem.
-
1
def mtime(p)
-
2
File.mtime(p)
-
end
-
-
# Extract handler and formats from path. If a format cannot be a found neither
-
# from the path, or the handler, we should return the array of formats given
-
# to the resolver.
-
1
def extract_handler_and_format_and_variant(path, default_formats)
-
2
pieces = File.basename(path).split(".")
-
2
pieces.shift
-
-
2
extension = pieces.pop
-
2
unless extension
-
message = "The file #{path} did not specify a template handler. The default is currently ERB, " \
-
"but will change to RAW in the future."
-
ActiveSupport::Deprecation.warn message
-
end
-
-
2
handler = Template.handler_for_extension(extension)
-
2
format, variant = pieces.last.split(EXTENSIONS[:variants], 2) if pieces.last
-
2
format &&= Template::Types[format]
-
-
2
[handler, format, variant]
-
end
-
end
-
-
# A resolver that loads files from the filesystem. It allows setting your own
-
# resolving pattern. Such pattern can be a glob string supported by some variables.
-
#
-
# ==== Examples
-
#
-
# Default pattern, loads views the same way as previous versions of rails, eg. when you're
-
# looking for `users/new` it will produce query glob: `users/new{.{en},}{.{html,js},}{.{erb,haml},}`
-
#
-
# FileSystemResolver.new("/path/to/views", ":prefix/:action{.:locale,}{.:formats,}{.:handlers,}")
-
#
-
# This one allows you to keep files with different formats in separate subdirectories,
-
# eg. `users/new.html` will be loaded from `users/html/new.erb` or `users/new.html.erb`,
-
# `users/new.js` from `users/js/new.erb` or `users/new.js.erb`, etc.
-
#
-
# FileSystemResolver.new("/path/to/views", ":prefix/{:formats/,}:action{.:locale,}{.:formats,}{.:handlers,}")
-
#
-
# If you don't specify a pattern then the default will be used.
-
#
-
# In order to use any of the customized resolvers above in a Rails application, you just need
-
# to configure ActionController::Base.view_paths in an initializer, for example:
-
#
-
# ActionController::Base.view_paths = FileSystemResolver.new(
-
# Rails.root.join("app/views"),
-
# ":prefix{/:locale}/:action{.:formats,}{.:handlers,}"
-
# )
-
#
-
# ==== Pattern format and variables
-
#
-
# Pattern has to be a valid glob string, and it allows you to use the
-
# following variables:
-
#
-
# * <tt>:prefix</tt> - usually the controller path
-
# * <tt>:action</tt> - name of the action
-
# * <tt>:locale</tt> - possible locale versions
-
# * <tt>:formats</tt> - possible request formats (for example html, json, xml...)
-
# * <tt>:handlers</tt> - possible handlers (for example erb, haml, builder...)
-
#
-
1
class FileSystemResolver < PathResolver
-
1
def initialize(path, pattern=nil)
-
4
raise ArgumentError, "path already is a Resolver class" if path.is_a?(Resolver)
-
4
super(pattern)
-
4
@path = File.expand_path(path)
-
end
-
-
1
def to_s
-
@path.to_s
-
end
-
1
alias :to_path :to_s
-
-
1
def eql?(resolver)
-
self.class.equal?(resolver.class) && to_path == resolver.to_path
-
end
-
1
alias :== :eql?
-
end
-
-
# An Optimized resolver for Rails' most common case.
-
1
class OptimizedFileSystemResolver < FileSystemResolver #:nodoc:
-
1
def build_query(path, details)
-
3
query = escape_entry(File.join(@path, path))
-
-
3
exts = EXTENSIONS.map do |ext, prefix|
-
36
"{#{details[ext].compact.uniq.map { |e| "#{prefix}#{e}," }.join}}"
-
end.join
-
-
3
query + exts
-
end
-
end
-
-
# The same as FileSystemResolver but does not allow templates to store
-
# a virtual path since it is invalid for such resolvers.
-
1
class FallbackFileSystemResolver < FileSystemResolver #:nodoc:
-
1
def self.instances
-
1
[new(""), new("/")]
-
end
-
-
1
def decorate(*)
-
super.each { |t| t.virtual_path = nil }
-
end
-
end
-
end
-
1
require 'set'
-
1
require 'active_support/core_ext/module/attribute_accessors'
-
-
1
module ActionView
-
1
class Template
-
1
class Types
-
1
class Type
-
1
cattr_accessor :types
-
1
self.types = Set.new
-
-
1
def self.register(*t)
-
7
types.merge(t.map { |type| type.to_s })
-
end
-
-
1
register :html, :text, :js, :css, :xml, :json
-
-
1
def self.[](type)
-
return type if type.is_a?(self)
-
-
if type.is_a?(Symbol) || types.member?(type.to_s)
-
new(type)
-
end
-
end
-
-
1
attr_reader :symbol
-
-
1
def initialize(symbol)
-
@symbol = symbol.to_sym
-
end
-
-
1
delegate :to_s, :to_sym, :to => :symbol
-
1
alias to_str to_s
-
-
1
def ref
-
to_sym || to_s
-
end
-
-
1
def ==(type)
-
return false if type.blank?
-
symbol.to_sym == type.to_sym
-
end
-
end
-
-
1
cattr_accessor :type_klass
-
-
1
def self.delegate_to(klass)
-
2
self.type_klass = klass
-
end
-
-
1
delegate_to Type
-
-
1
def self.[](type)
-
2
type_klass[type]
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/module/remove_method'
-
1
require 'action_controller'
-
1
require 'action_controller/test_case'
-
1
require 'action_view'
-
-
1
module ActionView
-
# = Action View Test Case
-
1
class TestCase < ActiveSupport::TestCase
-
1
class TestController < ActionController::Base
-
1
include ActionDispatch::TestProcess
-
-
1
attr_accessor :request, :response, :params
-
-
1
class << self
-
1
attr_writer :controller_path
-
end
-
-
1
def controller_path=(path)
-
self.class.controller_path=(path)
-
end
-
-
1
def initialize
-
super
-
self.class.controller_path = ""
-
@request = ActionController::TestRequest.new
-
@response = ActionController::TestResponse.new
-
-
@request.env.delete('PATH_INFO')
-
@params = {}
-
end
-
end
-
-
1
module Behavior
-
1
extend ActiveSupport::Concern
-
-
1
include ActionDispatch::Assertions, ActionDispatch::TestProcess
-
1
include ActionController::TemplateAssertions
-
1
include ActionView::Context
-
-
1
include ActionDispatch::Routing::PolymorphicRoutes
-
-
1
include AbstractController::Helpers
-
1
include ActionView::Helpers
-
1
include ActionView::RecordIdentifier
-
1
include ActionView::RoutingUrlFor
-
-
1
include ActiveSupport::Testing::ConstantLookup
-
-
1
delegate :lookup_context, :to => :controller
-
1
attr_accessor :controller, :output_buffer, :rendered
-
-
1
module ClassMethods
-
1
def tests(helper_class)
-
case helper_class
-
when String, Symbol
-
self.helper_class = "#{helper_class.to_s.underscore}_helper".camelize.safe_constantize
-
when Module
-
self.helper_class = helper_class
-
end
-
end
-
-
1
def determine_default_helper_class(name)
-
determine_constant_from_test_name(name) do |constant|
-
Module === constant && !(Class === constant)
-
end
-
end
-
-
1
def helper_method(*methods)
-
# Almost a duplicate from ActionController::Helpers
-
methods.flatten.each do |method|
-
_helpers.module_eval <<-end_eval
-
def #{method}(*args, &block) # def current_user(*args, &block)
-
_test_case.send(%(#{method}), *args, &block) # _test_case.send(%(current_user), *args, &block)
-
end # end
-
end_eval
-
end
-
end
-
-
1
attr_writer :helper_class
-
-
1
def helper_class
-
@helper_class ||= determine_default_helper_class(name)
-
end
-
-
1
def new(*)
-
include_helper_modules!
-
super
-
end
-
-
1
private
-
-
1
def include_helper_modules!
-
helper(helper_class) if helper_class
-
include _helpers
-
end
-
-
end
-
-
1
def setup_with_controller
-
@controller = ActionView::TestCase::TestController.new
-
@request = @controller.request
-
@output_buffer = ActiveSupport::SafeBuffer.new
-
@rendered = ''
-
-
make_test_case_available_to_view!
-
say_no_to_protect_against_forgery!
-
end
-
-
1
def config
-
@controller.config if @controller.respond_to?(:config)
-
end
-
-
1
def render(options = {}, local_assigns = {}, &block)
-
view.assign(view_assigns)
-
@rendered << output = view.render(options, local_assigns, &block)
-
output
-
end
-
-
1
def rendered_views
-
@_rendered_views ||= RenderedViewsCollection.new
-
end
-
-
1
class RenderedViewsCollection
-
1
def initialize
-
@rendered_views ||= Hash.new { |hash, key| hash[key] = [] }
-
end
-
-
1
def add(view, locals)
-
@rendered_views[view] ||= []
-
@rendered_views[view] << locals
-
end
-
-
1
def locals_for(view)
-
@rendered_views[view]
-
end
-
-
1
def rendered_views
-
@rendered_views.keys
-
end
-
-
1
def view_rendered?(view, expected_locals)
-
locals_for(view).any? do |actual_locals|
-
expected_locals.all? {|key, value| value == actual_locals[key] }
-
end
-
end
-
end
-
-
1
included do
-
1
setup :setup_with_controller
-
end
-
-
1
private
-
-
# Support the selector assertions
-
#
-
# Need to experiment if this priority is the best one: rendered => output_buffer
-
1
def response_from_page
-
HTML::Document.new(@rendered.blank? ? @output_buffer : @rendered).root
-
end
-
-
1
def say_no_to_protect_against_forgery!
-
_helpers.module_eval do
-
remove_possible_method :protect_against_forgery?
-
def protect_against_forgery?
-
false
-
end
-
end
-
end
-
-
1
def make_test_case_available_to_view!
-
test_case_instance = self
-
_helpers.module_eval do
-
unless private_method_defined?(:_test_case)
-
define_method(:_test_case) { test_case_instance }
-
private :_test_case
-
end
-
end
-
end
-
-
1
module Locals
-
1
attr_accessor :rendered_views
-
-
1
def render(options = {}, local_assigns = {})
-
case options
-
when Hash
-
if block_given?
-
rendered_views.add options[:layout], options[:locals]
-
elsif options.key?(:partial)
-
rendered_views.add options[:partial], options[:locals]
-
end
-
else
-
rendered_views.add options, local_assigns
-
end
-
-
super
-
end
-
end
-
-
# The instance of ActionView::Base that is used by +render+.
-
1
def view
-
@view ||= begin
-
view = @controller.view_context
-
view.singleton_class.send :include, _helpers
-
view.extend(Locals)
-
view.rendered_views = self.rendered_views
-
view.output_buffer = self.output_buffer
-
view
-
end
-
end
-
-
1
alias_method :_view, :view
-
-
1
INTERNAL_IVARS = [
-
:@NAME,
-
:@failures,
-
:@assertions,
-
:@__io__,
-
:@_assertion_wrapped,
-
:@_assertions,
-
:@_result,
-
:@_routes,
-
:@controller,
-
:@_layouts,
-
:@_files,
-
:@_rendered_views,
-
:@method_name,
-
:@output_buffer,
-
:@_partials,
-
:@passed,
-
:@rendered,
-
:@request,
-
:@routes,
-
:@tagged_logger,
-
:@_templates,
-
:@options,
-
:@test_passed,
-
:@view,
-
:@view_context_class
-
]
-
-
1
def _user_defined_ivars
-
instance_variables - INTERNAL_IVARS
-
end
-
-
# Returns a Hash of instance variables and their values, as defined by
-
# the user in the test case, which are then assigned to the view being
-
# rendered. This is generally intended for internal use and extension
-
# frameworks.
-
1
def view_assigns
-
Hash[_user_defined_ivars.map do |ivar|
-
[ivar[1..-1].to_sym, instance_variable_get(ivar)]
-
end]
-
end
-
-
1
def _routes
-
@controller._routes if @controller.respond_to?(:_routes)
-
end
-
-
1
def method_missing(selector, *args)
-
if @controller.respond_to?(:_routes) &&
-
( @controller._routes.named_routes.helpers.include?(selector) ||
-
@controller._routes.mounted_helpers.method_defined?(selector) )
-
@controller.__send__(selector, *args)
-
else
-
super
-
end
-
end
-
end
-
-
1
include Behavior
-
end
-
end
-
1
require 'action_view/template/resolver'
-
-
1
module ActionView #:nodoc:
-
# Use FixtureResolver in your tests to simulate the presence of files on the
-
# file system. This is used internally by Rails' own test suite, and is
-
# useful for testing extensions that have no way of knowing what the file
-
# system will look like at runtime.
-
1
class FixtureResolver < PathResolver
-
1
attr_reader :hash
-
-
1
def initialize(hash = {}, pattern=nil)
-
super(pattern)
-
@hash = hash
-
end
-
-
1
def to_s
-
@hash.keys.join(', ')
-
end
-
-
1
private
-
-
1
def query(path, exts, formats)
-
query = ""
-
EXTENSIONS.each_key do |ext|
-
query << '(' << exts[ext].map {|e| e && Regexp.escape(".#{e}") }.join('|') << '|)'
-
end
-
query = /^(#{Regexp.escape(path)})#{query}$/
-
-
templates = []
-
@hash.each do |_path, array|
-
source, updated_at = array
-
next unless _path =~ query
-
handler, format, variant = extract_handler_and_format_and_variant(_path, formats)
-
templates << Template.new(source, _path, handler,
-
:virtual_path => path.virtual,
-
:format => format,
-
:variant => variant,
-
:updated_at => updated_at
-
)
-
end
-
-
templates.sort_by {|t| -t.identifier.match(/^#{query}$/).captures.reject(&:blank?).size }
-
end
-
end
-
-
1
class NullResolver < PathResolver
-
1
def query(path, exts, formats)
-
handler, format, variant = extract_handler_and_format_and_variant(path, formats)
-
[ActionView::Template.new("Template generated by Null Resolver", path, handler, :virtual_path => path, :format => format, :variant => variant)]
-
end
-
end
-
-
end
-
-
1
$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/html-scanner"
-
-
1
module HTML
-
1
extend ActiveSupport::Autoload
-
-
1
eager_autoload do
-
1
autoload :CDATA, 'html/node'
-
1
autoload :Document, 'html/document'
-
1
autoload :FullSanitizer, 'html/sanitizer'
-
1
autoload :LinkSanitizer, 'html/sanitizer'
-
1
autoload :Node, 'html/node'
-
1
autoload :Sanitizer, 'html/sanitizer'
-
1
autoload :Selector, 'html/selector'
-
1
autoload :Tag, 'html/node'
-
1
autoload :Text, 'html/node'
-
1
autoload :Tokenizer, 'html/tokenizer'
-
1
autoload :Version, 'html/version'
-
1
autoload :WhiteListSanitizer, 'html/sanitizer'
-
end
-
end
-
1
require_relative 'gem_version'
-
-
1
module ActionView
-
# Returns the version of the currently loaded ActionView as a <tt>Gem::Version</tt>
-
1
def self.version
-
gem_version
-
end
-
end
-
1
require 'action_view/base'
-
-
1
module ActionView
-
1
module ViewPaths
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
2
class_attribute :_view_paths
-
2
self._view_paths = ActionView::PathSet.new
-
2
self._view_paths.freeze
-
end
-
-
1
delegate :template_exists?, :view_paths, :formats, :formats=,
-
:locale, :locale=, :to => :lookup_context
-
-
1
module ClassMethods
-
1
def parent_prefixes
-
@parent_prefixes ||= begin
-
1
parent_controller = superclass
-
1
prefixes = []
-
-
1
until parent_controller.abstract?
-
1
prefixes << parent_controller.controller_path
-
1
parent_controller = parent_controller.superclass
-
end
-
-
1
prefixes
-
4
end
-
end
-
end
-
-
# The prefixes used in render "foo" shortcuts.
-
1
def _prefixes
-
@_prefixes ||= begin
-
4
parent_prefixes = self.class.parent_prefixes
-
4
parent_prefixes.dup.unshift(controller_path)
-
6
end
-
end
-
-
# LookupContext is the object responsible to hold all information required to lookup
-
# templates, i.e. view paths and details. Check ActionView::LookupContext for more
-
# information.
-
1
def lookup_context
-
@_lookup_context ||=
-
32
ActionView::LookupContext.new(self.class._view_paths, details_for_lookup, _prefixes)
-
end
-
-
1
def details_for_lookup
-
4
{ }
-
end
-
-
1
def append_view_path(path)
-
lookup_context.view_paths.push(*path)
-
end
-
-
1
def prepend_view_path(path)
-
lookup_context.view_paths.unshift(*path)
-
end
-
-
1
module ClassMethods
-
# Append a path to the list of view paths for this controller.
-
#
-
# ==== Parameters
-
# * <tt>path</tt> - If a String is provided, it gets converted into
-
# the default view path. You may also provide a custom view path
-
# (see ActionView::PathSet for more information)
-
1
def append_view_path(path)
-
self._view_paths = view_paths + Array(path)
-
end
-
-
# Prepend a path to the list of view paths for this controller.
-
#
-
# ==== Parameters
-
# * <tt>path</tt> - If a String is provided, it gets converted into
-
# the default view path. You may also provide a custom view path
-
# (see ActionView::PathSet for more information)
-
1
def prepend_view_path(path)
-
2
self._view_paths = ActionView::PathSet.new(Array(path) + view_paths)
-
end
-
-
# A list of all of the default view paths for this controller.
-
1
def view_paths
-
6
_view_paths
-
end
-
-
# Set the view paths.
-
#
-
# ==== Parameters
-
# * <tt>paths</tt> - If a PathSet is provided, use that;
-
# otherwise, process the parameter into a PathSet.
-
1
def view_paths=(paths)
-
8
self._view_paths = ActionView::PathSet.new(Array(paths))
-
end
-
end
-
end
-
end
-
#--
-
# Copyright (c) 2004-2014 David Heinemeier Hansson
-
#
-
# Permission is hereby granted, free of charge, to any person obtaining
-
# a copy of this software and associated documentation files (the
-
# "Software"), to deal in the Software without restriction, including
-
# without limitation the rights to use, copy, modify, merge, publish,
-
# distribute, sublicense, and/or sell copies of the Software, and to
-
# permit persons to whom the Software is furnished to do so, subject to
-
# the following conditions:
-
#
-
# The above copyright notice and this permission notice shall be
-
# included in all copies or substantial portions of the Software.
-
#
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
#++
-
-
1
require 'active_support'
-
1
require 'active_support/rails'
-
1
require 'active_model/version'
-
-
1
module ActiveModel
-
1
extend ActiveSupport::Autoload
-
-
1
autoload :AttributeMethods
-
1
autoload :BlockValidator, 'active_model/validator'
-
1
autoload :Callbacks
-
1
autoload :Conversion
-
1
autoload :Dirty
-
1
autoload :EachValidator, 'active_model/validator'
-
1
autoload :ForbiddenAttributesProtection
-
1
autoload :Lint
-
1
autoload :Model
-
1
autoload :Name, 'active_model/naming'
-
1
autoload :Naming
-
1
autoload :SecurePassword
-
1
autoload :Serialization
-
1
autoload :TestCase
-
1
autoload :Translation
-
1
autoload :Validations
-
1
autoload :Validator
-
-
1
eager_autoload do
-
1
autoload :Errors
-
1
autoload :StrictValidationFailed, 'active_model/errors'
-
end
-
-
1
module Serializers
-
1
extend ActiveSupport::Autoload
-
-
1
eager_autoload do
-
1
autoload :JSON
-
1
autoload :Xml
-
end
-
end
-
-
1
def self.eager_load!
-
super
-
ActiveModel::Serializers.eager_load!
-
end
-
end
-
-
1
ActiveSupport.on_load(:i18n) do
-
1
I18n.load_path << File.dirname(__FILE__) + '/active_model/locale/en.yml'
-
end
-
1
require 'thread_safe'
-
1
require 'mutex_m'
-
-
1
module ActiveModel
-
# Raised when an attribute is not defined.
-
#
-
# class User < ActiveRecord::Base
-
# has_many :pets
-
# end
-
#
-
# user = User.first
-
# user.pets.select(:id).first.user_id
-
# # => ActiveModel::MissingAttributeError: missing attribute: user_id
-
1
class MissingAttributeError < NoMethodError
-
end
-
-
# == Active \Model \Attribute \Methods
-
#
-
# Provides a way to add prefixes and suffixes to your methods as
-
# well as handling the creation of <tt>ActiveRecord::Base</tt>-like
-
# class methods such as +table_name+.
-
#
-
# The requirements to implement <tt>ActiveModel::AttributeMethods</tt> are to:
-
#
-
# * <tt>include ActiveModel::AttributeMethods</tt> in your class.
-
# * Call each of its method you want to add, such as +attribute_method_suffix+
-
# or +attribute_method_prefix+.
-
# * Call +define_attribute_methods+ after the other methods are called.
-
# * Define the various generic +_attribute+ methods that you have declared.
-
# * Define an +attributes+ method which returns a hash with each
-
# attribute name in your model as hash key and the attribute value as hash value.
-
# Hash keys must be strings.
-
#
-
# A minimal implementation could be:
-
#
-
# class Person
-
# include ActiveModel::AttributeMethods
-
#
-
# attribute_method_affix prefix: 'reset_', suffix: '_to_default!'
-
# attribute_method_suffix '_contrived?'
-
# attribute_method_prefix 'clear_'
-
# define_attribute_methods :name
-
#
-
# attr_accessor :name
-
#
-
# def attributes
-
# { 'name' => @name }
-
# end
-
#
-
# private
-
#
-
# def attribute_contrived?(attr)
-
# true
-
# end
-
#
-
# def clear_attribute(attr)
-
# send("#{attr}=", nil)
-
# end
-
#
-
# def reset_attribute_to_default!(attr)
-
# send("#{attr}=", 'Default Name')
-
# end
-
# end
-
1
module AttributeMethods
-
1
extend ActiveSupport::Concern
-
-
1
NAME_COMPILABLE_REGEXP = /\A[a-zA-Z_]\w*[!?=]?\z/
-
1
CALL_COMPILABLE_REGEXP = /\A[a-zA-Z_]\w*[!?]?\z/
-
-
1
included do
-
1
class_attribute :attribute_aliases, :attribute_method_matchers, instance_writer: false
-
1
self.attribute_aliases = {}
-
1
self.attribute_method_matchers = [ClassMethods::AttributeMethodMatcher.new]
-
end
-
-
1
module ClassMethods
-
# Declares a method available for all attributes with the given prefix.
-
# Uses +method_missing+ and <tt>respond_to?</tt> to rewrite the method.
-
#
-
# #{prefix}#{attr}(*args, &block)
-
#
-
# to
-
#
-
# #{prefix}attribute(#{attr}, *args, &block)
-
#
-
# An instance method <tt>#{prefix}attribute</tt> must exist and accept
-
# at least the +attr+ argument.
-
#
-
# class Person
-
# include ActiveModel::AttributeMethods
-
#
-
# attr_accessor :name
-
# attribute_method_prefix 'clear_'
-
# define_attribute_methods :name
-
#
-
# private
-
#
-
# def clear_attribute(attr)
-
# send("#{attr}=", nil)
-
# end
-
# end
-
#
-
# person = Person.new
-
# person.name = 'Bob'
-
# person.name # => "Bob"
-
# person.clear_name
-
# person.name # => nil
-
1
def attribute_method_prefix(*prefixes)
-
self.attribute_method_matchers += prefixes.map! { |prefix| AttributeMethodMatcher.new prefix: prefix }
-
undefine_attribute_methods
-
end
-
-
# Declares a method available for all attributes with the given suffix.
-
# Uses +method_missing+ and <tt>respond_to?</tt> to rewrite the method.
-
#
-
# #{attr}#{suffix}(*args, &block)
-
#
-
# to
-
#
-
# attribute#{suffix}(#{attr}, *args, &block)
-
#
-
# An <tt>attribute#{suffix}</tt> instance method must exist and accept at
-
# least the +attr+ argument.
-
#
-
# class Person
-
# include ActiveModel::AttributeMethods
-
#
-
# attr_accessor :name
-
# attribute_method_suffix '_short?'
-
# define_attribute_methods :name
-
#
-
# private
-
#
-
# def attribute_short?(attr)
-
# send(attr).length < 5
-
# end
-
# end
-
#
-
# person = Person.new
-
# person.name = 'Bob'
-
# person.name # => "Bob"
-
# person.name_short? # => true
-
1
def attribute_method_suffix(*suffixes)
-
11
self.attribute_method_matchers += suffixes.map! { |suffix| AttributeMethodMatcher.new suffix: suffix }
-
4
undefine_attribute_methods
-
end
-
-
# Declares a method available for all attributes with the given prefix
-
# and suffix. Uses +method_missing+ and <tt>respond_to?</tt> to rewrite
-
# the method.
-
#
-
# #{prefix}#{attr}#{suffix}(*args, &block)
-
#
-
# to
-
#
-
# #{prefix}attribute#{suffix}(#{attr}, *args, &block)
-
#
-
# An <tt>#{prefix}attribute#{suffix}</tt> instance method must exist and
-
# accept at least the +attr+ argument.
-
#
-
# class Person
-
# include ActiveModel::AttributeMethods
-
#
-
# attr_accessor :name
-
# attribute_method_affix prefix: 'reset_', suffix: '_to_default!'
-
# define_attribute_methods :name
-
#
-
# private
-
#
-
# def reset_attribute_to_default!(attr)
-
# send("#{attr}=", 'Default Name')
-
# end
-
# end
-
#
-
# person = Person.new
-
# person.name # => 'Gem'
-
# person.reset_name_to_default!
-
# person.name # => 'Default Name'
-
1
def attribute_method_affix(*affixes)
-
2
self.attribute_method_matchers += affixes.map! { |affix| AttributeMethodMatcher.new prefix: affix[:prefix], suffix: affix[:suffix] }
-
1
undefine_attribute_methods
-
end
-
-
# Allows you to make aliases for attributes.
-
#
-
# class Person
-
# include ActiveModel::AttributeMethods
-
#
-
# attr_accessor :name
-
# attribute_method_suffix '_short?'
-
# define_attribute_methods :name
-
#
-
# alias_attribute :nickname, :name
-
#
-
# private
-
#
-
# def attribute_short?(attr)
-
# send(attr).length < 5
-
# end
-
# end
-
#
-
# person = Person.new
-
# person.name = 'Bob'
-
# person.name # => "Bob"
-
# person.nickname # => "Bob"
-
# person.name_short? # => true
-
# person.nickname_short? # => true
-
1
def alias_attribute(new_name, old_name)
-
self.attribute_aliases = attribute_aliases.merge(new_name.to_s => old_name.to_s)
-
attribute_method_matchers.each do |matcher|
-
matcher_new = matcher.method_name(new_name).to_s
-
matcher_old = matcher.method_name(old_name).to_s
-
define_proxy_call false, self, matcher_new, matcher_old
-
end
-
end
-
-
# Is +new_name+ an alias?
-
1
def attribute_alias?(new_name)
-
attribute_aliases.key? new_name.to_s
-
end
-
-
# Returns the original name for the alias +name+
-
1
def attribute_alias(name)
-
attribute_aliases[name.to_s]
-
end
-
-
# Declares the attributes that should be prefixed and suffixed by
-
# ActiveModel::AttributeMethods.
-
#
-
# To use, pass attribute names (as strings or symbols), be sure to declare
-
# +define_attribute_methods+ after you define any prefix, suffix or affix
-
# methods, or they will not hook in.
-
#
-
# class Person
-
# include ActiveModel::AttributeMethods
-
#
-
# attr_accessor :name, :age, :address
-
# attribute_method_prefix 'clear_'
-
#
-
# # Call to define_attribute_methods must appear after the
-
# # attribute_method_prefix, attribute_method_suffix or
-
# # attribute_method_affix declares.
-
# define_attribute_methods :name, :age, :address
-
#
-
# private
-
#
-
# def clear_attribute(attr)
-
# send("#{attr}=", nil)
-
# end
-
# end
-
1
def define_attribute_methods(*attr_names)
-
25
attr_names.flatten.each { |attr_name| define_attribute_method(attr_name) }
-
end
-
-
# Declares an attribute that should be prefixed and suffixed by
-
# ActiveModel::AttributeMethods.
-
#
-
# To use, pass an attribute name (as string or symbol), be sure to declare
-
# +define_attribute_method+ after you define any prefix, suffix or affix
-
# method, or they will not hook in.
-
#
-
# class Person
-
# include ActiveModel::AttributeMethods
-
#
-
# attr_accessor :name
-
# attribute_method_suffix '_short?'
-
#
-
# # Call to define_attribute_method must appear after the
-
# # attribute_method_prefix, attribute_method_suffix or
-
# # attribute_method_affix declares.
-
# define_attribute_method :name
-
#
-
# private
-
#
-
# def attribute_short?(attr)
-
# send(attr).length < 5
-
# end
-
# end
-
#
-
# person = Person.new
-
# person.name = 'Bob'
-
# person.name # => "Bob"
-
# person.name_short? # => true
-
1
def define_attribute_method(attr_name)
-
21
attribute_method_matchers.each do |matcher|
-
189
method_name = matcher.method_name(attr_name)
-
-
189
unless instance_method_already_implemented?(method_name)
-
189
generate_method = "define_method_#{matcher.method_missing_target}"
-
-
189
if respond_to?(generate_method, true)
-
42
send(generate_method, attr_name)
-
else
-
147
define_proxy_call true, generated_attribute_methods, method_name, matcher.method_missing_target, attr_name.to_s
-
end
-
end
-
end
-
21
attribute_method_matchers_cache.clear
-
end
-
-
# Removes all the previously dynamically defined methods from the class.
-
#
-
# class Person
-
# include ActiveModel::AttributeMethods
-
#
-
# attr_accessor :name
-
# attribute_method_suffix '_short?'
-
# define_attribute_method :name
-
#
-
# private
-
#
-
# def attribute_short?(attr)
-
# send(attr).length < 5
-
# end
-
# end
-
#
-
# person = Person.new
-
# person.name = 'Bob'
-
# person.name_short? # => true
-
#
-
# Person.undefine_attribute_methods
-
#
-
# person.name_short? # => NoMethodError
-
1
def undefine_attribute_methods
-
generated_attribute_methods.module_eval do
-
instance_methods.each { |m| undef_method(m) }
-
end
-
attribute_method_matchers_cache.clear
-
end
-
-
1
def generated_attribute_methods #:nodoc:
-
@generated_attribute_methods ||= Module.new {
-
extend Mutex_m
-
390
}.tap { |mod| include mod }
-
end
-
-
1
protected
-
1
def instance_method_already_implemented?(method_name) #:nodoc:
-
189
generated_attribute_methods.method_defined?(method_name)
-
end
-
-
1
private
-
# The methods +method_missing+ and +respond_to?+ of this module are
-
# invoked often in a typical rails, both of which invoke the method
-
# +match_attribute_method?+. The latter method iterates through an
-
# array doing regular expression matches, which results in a lot of
-
# object creations. Most of the time it returns a +nil+ match. As the
-
# match result is always the same given a +method_name+, this cache is
-
# used to alleviate the GC, which ultimately also speeds up the app
-
# significantly (in our case our test suite finishes 10% faster with
-
# this cache).
-
1
def attribute_method_matchers_cache #:nodoc:
-
33
@attribute_method_matchers_cache ||= ThreadSafe::Cache.new(initial_capacity: 4)
-
end
-
-
1
def attribute_method_matcher(method_name) #:nodoc:
-
12
attribute_method_matchers_cache.compute_if_absent(method_name) do
-
# Must try to match prefixes/suffixes first, or else the matcher with no prefix/suffix
-
# will match every time.
-
6
matchers = attribute_method_matchers.partition(&:plain?).reverse.flatten(1)
-
6
match = nil
-
60
matchers.detect { |method| match = method.match(method_name) }
-
6
match
-
end
-
end
-
-
# Define a method `name` in `mod` that dispatches to `send`
-
# using the given `extra` args. This fallbacks `define_method`
-
# and `send` if the given names cannot be compiled.
-
1
def define_proxy_call(include_private, mod, name, send, *extra) #:nodoc:
-
147
defn = if name =~ NAME_COMPILABLE_REGEXP
-
147
"def #{name}(*args)"
-
else
-
"define_method(:'#{name}') do |*args|"
-
end
-
-
147
extra = (extra.map!(&:inspect) << "*args").join(", ")
-
-
147
target = if send =~ CALL_COMPILABLE_REGEXP
-
147
"#{"self." unless include_private}#{send}(#{extra})"
-
else
-
"send(:'#{send}', #{extra})"
-
end
-
-
147
mod.module_eval <<-RUBY, __FILE__, __LINE__ + 1
-
#{defn}
-
#{target}
-
end
-
RUBY
-
end
-
-
1
class AttributeMethodMatcher #:nodoc:
-
1
attr_reader :prefix, :suffix, :method_missing_target
-
-
1
AttributeMethodMatch = Struct.new(:target, :attr_name, :method_name)
-
-
1
def initialize(options = {})
-
9
@prefix, @suffix = options.fetch(:prefix, ''), options.fetch(:suffix, '')
-
9
@regex = /^(?:#{Regexp.escape(@prefix)})(.*)(?:#{Regexp.escape(@suffix)})$/
-
9
@method_missing_target = "#{@prefix}attribute#{@suffix}"
-
9
@method_name = "#{prefix}%s#{suffix}"
-
end
-
-
1
def match(method_name)
-
54
if @regex =~ method_name
-
6
AttributeMethodMatch.new(method_missing_target, $1, method_name)
-
end
-
end
-
-
1
def method_name(attr_name)
-
189
@method_name % attr_name
-
end
-
-
1
def plain?
-
54
prefix.empty? && suffix.empty?
-
end
-
end
-
end
-
-
# Allows access to the object attributes, which are held in the hash
-
# returned by <tt>attributes</tt>, as though they were first-class
-
# methods. So a +Person+ class with a +name+ attribute can for example use
-
# <tt>Person#name</tt> and <tt>Person#name=</tt> and never directly use
-
# the attributes hash -- except for multiple assigns with
-
# <tt>ActiveRecord::Base#attributes=</tt>.
-
#
-
# It's also possible to instantiate related objects, so a <tt>Client</tt>
-
# class belonging to the +clients+ table with a +master_id+ foreign key
-
# can instantiate master through <tt>Client#master</tt>.
-
1
def method_missing(method, *args, &block)
-
if respond_to_without_attributes?(method, true)
-
super
-
else
-
match = match_attribute_method?(method.to_s)
-
match ? attribute_missing(match, *args, &block) : super
-
end
-
end
-
-
# +attribute_missing+ is like +method_missing+, but for attributes. When
-
# +method_missing+ is called we check to see if there is a matching
-
# attribute method. If so, we tell +attribute_missing+ to dispatch the
-
# attribute. This method can be overloaded to customize the behavior.
-
1
def attribute_missing(match, *args, &block)
-
__send__(match.target, match.attr_name, *args, &block)
-
end
-
-
# A +Person+ instance with a +name+ attribute can ask
-
# <tt>person.respond_to?(:name)</tt>, <tt>person.respond_to?(:name=)</tt>,
-
# and <tt>person.respond_to?(:name?)</tt> which will all return +true+.
-
1
alias :respond_to_without_attributes? :respond_to?
-
1
def respond_to?(method, include_private_methods = false)
-
36
if super
-
24
true
-
12
elsif !include_private_methods && super(method, true)
-
# If we're here then we haven't found among non-private methods
-
# but found among all methods. Which means that the given method is private.
-
false
-
else
-
12
!match_attribute_method?(method.to_s).nil?
-
end
-
end
-
-
1
protected
-
1
def attribute_method?(attr_name) #:nodoc:
-
respond_to_without_attributes?(:attributes) && attributes.include?(attr_name)
-
end
-
-
1
private
-
# Returns a struct representing the matching attribute method.
-
# The struct's attributes are prefix, base and suffix.
-
1
def match_attribute_method?(method_name)
-
12
match = self.class.send(:attribute_method_matcher, method_name)
-
12
match if match && attribute_method?(match.attr_name)
-
end
-
-
1
def missing_attribute(attr_name, stack)
-
raise ActiveModel::MissingAttributeError, "missing attribute: #{attr_name}", stack
-
end
-
end
-
end
-
1
require 'active_support/core_ext/array/extract_options'
-
-
1
module ActiveModel
-
# == Active \Model \Callbacks
-
#
-
# Provides an interface for any class to have Active Record like callbacks.
-
#
-
# Like the Active Record methods, the callback chain is aborted as soon as
-
# one of the methods in the chain returns +false+.
-
#
-
# First, extend ActiveModel::Callbacks from the class you are creating:
-
#
-
# class MyModel
-
# extend ActiveModel::Callbacks
-
# end
-
#
-
# Then define a list of methods that you want callbacks attached to:
-
#
-
# define_model_callbacks :create, :update
-
#
-
# This will provide all three standard callbacks (before, around and after)
-
# for both the <tt>:create</tt> and <tt>:update</tt> methods. To implement,
-
# you need to wrap the methods you want callbacks on in a block so that the
-
# callbacks get a chance to fire:
-
#
-
# def create
-
# run_callbacks :create do
-
# # Your create action methods here
-
# end
-
# end
-
#
-
# Then in your class, you can use the +before_create+, +after_create+ and
-
# +around_create+ methods, just as you would in an Active Record model.
-
#
-
# before_create :action_before_create
-
#
-
# def action_before_create
-
# # Your code here
-
# end
-
#
-
# When defining an around callback remember to yield to the block, otherwise
-
# it won't be executed:
-
#
-
# around_create :log_status
-
#
-
# def log_status
-
# puts 'going to call the block...'
-
# yield
-
# puts 'block successfully called.'
-
# end
-
#
-
# You can choose not to have all three callbacks by passing a hash to the
-
# +define_model_callbacks+ method.
-
#
-
# define_model_callbacks :create, only: [:after, :before]
-
#
-
# Would only create the +after_create+ and +before_create+ callback methods in
-
# your class.
-
1
module Callbacks
-
1
def self.extended(base) #:nodoc:
-
1
base.class_eval do
-
1
include ActiveSupport::Callbacks
-
end
-
end
-
-
# define_model_callbacks accepts the same options +define_callbacks+ does,
-
# in case you want to overwrite a default. Besides that, it also accepts an
-
# <tt>:only</tt> option, where you can choose if you want all types (before,
-
# around or after) or just some.
-
#
-
# define_model_callbacks :initializer, only: :after
-
#
-
# Note, the <tt>only: <type></tt> hash will apply to all callbacks defined
-
# on that method call. To get around this you can call the define_model_callbacks
-
# method as many times as you need.
-
#
-
# define_model_callbacks :create, only: :after
-
# define_model_callbacks :update, only: :before
-
# define_model_callbacks :destroy, only: :around
-
#
-
# Would create +after_create+, +before_update+ and +around_destroy+ methods
-
# only.
-
#
-
# You can pass in a class to before_<type>, after_<type> and around_<type>,
-
# in which case the callback will call that class's <action>_<type> method
-
# passing the object that the callback is being called on.
-
#
-
# class MyModel
-
# extend ActiveModel::Callbacks
-
# define_model_callbacks :create
-
#
-
# before_create AnotherClass
-
# end
-
#
-
# class AnotherClass
-
# def self.before_create( obj )
-
# # obj is the MyModel instance that the callback is being called on
-
# end
-
# end
-
1
def define_model_callbacks(*callbacks)
-
2
options = callbacks.extract_options!
-
2
options = {
-
10
terminator: ->(_,result) { result == false },
-
skip_after_callbacks_if_terminated: true,
-
scope: [:kind, :name],
-
only: [:before, :around, :after]
-
}.merge!(options)
-
-
2
types = Array(options.delete(:only))
-
-
2
callbacks.each do |callback|
-
7
define_callbacks(callback, options)
-
-
7
types.each do |type|
-
15
send("_define_#{type}_model_callback", self, callback)
-
end
-
end
-
end
-
-
1
private
-
-
1
def _define_before_model_callback(klass, callback) #:nodoc:
-
4
klass.define_singleton_method("before_#{callback}") do |*args, &block|
-
14
set_callback(:"#{callback}", :before, *args, &block)
-
end
-
end
-
-
1
def _define_around_model_callback(klass, callback) #:nodoc:
-
4
klass.define_singleton_method("around_#{callback}") do |*args, &block|
-
set_callback(:"#{callback}", :around, *args, &block)
-
end
-
end
-
-
1
def _define_after_model_callback(klass, callback) #:nodoc:
-
7
klass.define_singleton_method("after_#{callback}") do |*args, &block|
-
22
options = args.extract_options!
-
22
options[:prepend] = true
-
22
conditional = ActiveSupport::Callbacks::Conditionals::Value.new { |v|
-
24
v != false
-
}
-
22
options[:if] = Array(options[:if]) << conditional
-
22
set_callback(:"#{callback}", :after, *(args << options), &block)
-
end
-
end
-
end
-
end
-
1
module ActiveModel
-
# == Active \Model \Conversion
-
#
-
# Handles default conversions: to_model, to_key, to_param, and to_partial_path.
-
#
-
# Let's take for example this non-persisted object.
-
#
-
# class ContactMessage
-
# include ActiveModel::Conversion
-
#
-
# # ContactMessage are never persisted in the DB
-
# def persisted?
-
# false
-
# end
-
# end
-
#
-
# cm = ContactMessage.new
-
# cm.to_model == cm # => true
-
# cm.to_key # => nil
-
# cm.to_param # => nil
-
# cm.to_partial_path # => "contact_messages/contact_message"
-
1
module Conversion
-
1
extend ActiveSupport::Concern
-
-
# If your object is already designed to implement all of the Active Model
-
# you can use the default <tt>:to_model</tt> implementation, which simply
-
# returns +self+.
-
#
-
# class Person
-
# include ActiveModel::Conversion
-
# end
-
#
-
# person = Person.new
-
# person.to_model == person # => true
-
#
-
# If your model does not act like an Active Model object, then you should
-
# define <tt>:to_model</tt> yourself returning a proxy object that wraps
-
# your object with Active Model compliant methods.
-
1
def to_model
-
self
-
end
-
-
# Returns an Enumerable of all key attributes if any is set, regardless if
-
# the object is persisted or not. Returns +nil+ if there are no key attributes.
-
#
-
# class Person < ActiveRecord::Base
-
# end
-
#
-
# person = Person.create
-
# person.to_key # => [1]
-
1
def to_key
-
key = respond_to?(:id) && id
-
key ? [key] : nil
-
end
-
-
# Returns a +string+ representing the object's key suitable for use in URLs,
-
# or +nil+ if <tt>persisted?</tt> is +false+.
-
#
-
# class Person < ActiveRecord::Base
-
# end
-
#
-
# person = Person.create
-
# person.to_param # => "1"
-
1
def to_param
-
(persisted? && key = to_key) ? key.join('-') : nil
-
end
-
-
# Returns a +string+ identifying the path associated with the object.
-
# ActionPack uses this to find a suitable partial to represent the object.
-
#
-
# class Person
-
# include ActiveModel::Conversion
-
# end
-
#
-
# person = Person.new
-
# person.to_partial_path # => "people/person"
-
1
def to_partial_path
-
self.class._to_partial_path
-
end
-
-
1
module ClassMethods #:nodoc:
-
# Provide a class level cache for #to_partial_path. This is an
-
# internal method and should not be accessed directly.
-
1
def _to_partial_path #:nodoc:
-
@_to_partial_path ||= begin
-
element = ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(self))
-
collection = ActiveSupport::Inflector.tableize(self)
-
"#{collection}/#{element}".freeze
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/hash_with_indifferent_access'
-
1
require 'active_support/core_ext/object/duplicable'
-
-
1
module ActiveModel
-
# == Active \Model \Dirty
-
#
-
# Provides a way to track changes in your object in the same way as
-
# Active Record does.
-
#
-
# The requirements for implementing ActiveModel::Dirty are:
-
#
-
# * <tt>include ActiveModel::Dirty</tt> in your object.
-
# * Call <tt>define_attribute_methods</tt> passing each method you want to
-
# track.
-
# * Call <tt>attr_name_will_change!</tt> before each change to the tracked
-
# attribute.
-
# * Call <tt>changes_applied</tt> after the changes are persisted.
-
# * Call <tt>reset_changes</tt> when you want to reset the changes
-
# information.
-
#
-
# A minimal implementation could be:
-
#
-
# class Person
-
# include ActiveModel::Dirty
-
#
-
# define_attribute_methods :name
-
#
-
# def name
-
# @name
-
# end
-
#
-
# def name=(val)
-
# name_will_change! unless val == @name
-
# @name = val
-
# end
-
#
-
# def save
-
# # do persistence work
-
# changes_applied
-
# end
-
#
-
# def reload!
-
# reset_changes
-
# end
-
# end
-
#
-
# A newly instantiated object is unchanged:
-
#
-
# person = Person.find_by(name: 'Uncle Bob')
-
# person.changed? # => false
-
#
-
# Change the name:
-
#
-
# person.name = 'Bob'
-
# person.changed? # => true
-
# person.name_changed? # => true
-
# person.name_changed?(from: "Uncle Bob", to: "Bob") # => true
-
# person.name_was # => "Uncle Bob"
-
# person.name_change # => ["Uncle Bob", "Bob"]
-
# person.name = 'Bill'
-
# person.name_change # => ["Uncle Bob", "Bill"]
-
#
-
# Save the changes:
-
#
-
# person.save
-
# person.changed? # => false
-
# person.name_changed? # => false
-
#
-
# Reset the changes:
-
#
-
# person.previous_changes # => {"name" => ["Uncle Bob", "Bill"]}
-
# person.reload!
-
# person.previous_changes # => {}
-
#
-
# Assigning the same value leaves the attribute unchanged:
-
#
-
# person.name = 'Bill'
-
# person.name_changed? # => false
-
# person.name_change # => nil
-
#
-
# Which attributes have changed?
-
#
-
# person.name = 'Bob'
-
# person.changed # => ["name"]
-
# person.changes # => {"name" => ["Bill", "Bob"]}
-
#
-
# If an attribute is modified in-place then make use of <tt>[attribute_name]_will_change!</tt>
-
# to mark that the attribute is changing. Otherwise ActiveModel can't track
-
# changes to in-place attributes.
-
#
-
# person.name_will_change!
-
# person.name_change # => ["Bill", "Bill"]
-
# person.name << 'y'
-
# person.name_change # => ["Bill", "Billy"]
-
1
module Dirty
-
1
extend ActiveSupport::Concern
-
1
include ActiveModel::AttributeMethods
-
-
1
included do
-
1
attribute_method_suffix '_changed?', '_change', '_will_change!', '_was'
-
1
attribute_method_affix prefix: 'reset_', suffix: '!'
-
end
-
-
# Returns +true+ if any attribute have unsaved changes, +false+ otherwise.
-
#
-
# person.changed? # => false
-
# person.name = 'bob'
-
# person.changed? # => true
-
1
def changed?
-
2
changed_attributes.present?
-
end
-
-
# Returns an array with the name of the attributes with unsaved changes.
-
#
-
# person.changed # => []
-
# person.name = 'bob'
-
# person.changed # => ["name"]
-
1
def changed
-
20
changed_attributes.keys
-
end
-
-
# Returns a hash of changed attributes indicating their original
-
# and new values like <tt>attr => [original value, new value]</tt>.
-
#
-
# person.changes # => {}
-
# person.name = 'bob'
-
# person.changes # => { "name" => ["bill", "bob"] }
-
1
def changes
-
38
ActiveSupport::HashWithIndifferentAccess[changed.map { |attr| [attr, attribute_change(attr)] }]
-
end
-
-
# Returns a hash of attributes that were changed before the model was saved.
-
#
-
# person.name # => "bob"
-
# person.name = 'robert'
-
# person.save
-
# person.previous_changes # => {"name" => ["bob", "robert"]}
-
1
def previous_changes
-
@previously_changed ||= ActiveSupport::HashWithIndifferentAccess.new
-
end
-
-
# Returns a hash of the attributes with unsaved changes indicating their original
-
# values like <tt>attr => original value</tt>.
-
#
-
# person.name # => "bob"
-
# person.name = 'robert'
-
# person.changed_attributes # => {"name" => "bob"}
-
1
def changed_attributes
-
152
@changed_attributes ||= ActiveSupport::HashWithIndifferentAccess.new
-
end
-
-
# Handle <tt>*_changed?</tt> for +method_missing+.
-
1
def attribute_changed?(attr, options = {}) #:nodoc:
-
66
result = changed_attributes.include?(attr)
-
66
result &&= options[:to] == __send__(attr) if options.key?(:to)
-
66
result &&= options[:from] == changed_attributes[attr] if options.key?(:from)
-
66
result
-
end
-
-
# Handle <tt>*_was</tt> for +method_missing+.
-
1
def attribute_was(attr) # :nodoc:
-
attribute_changed?(attr) ? changed_attributes[attr] : __send__(attr)
-
end
-
-
1
private
-
-
# Removes current changes and makes them accessible through +previous_changes+.
-
1
def changes_applied
-
8
@previously_changed = changes
-
8
@changed_attributes = ActiveSupport::HashWithIndifferentAccess.new
-
end
-
-
# Removes all dirty data: current changes and previous changes
-
1
def reset_changes
-
@previously_changed = ActiveSupport::HashWithIndifferentAccess.new
-
@changed_attributes = ActiveSupport::HashWithIndifferentAccess.new
-
end
-
-
# Handle <tt>*_change</tt> for +method_missing+.
-
1
def attribute_change(attr)
-
30
[changed_attributes[attr], __send__(attr)] if attribute_changed?(attr)
-
end
-
-
# Handle <tt>*_will_change!</tt> for +method_missing+.
-
1
def attribute_will_change!(attr)
-
return if attribute_changed?(attr)
-
-
begin
-
value = __send__(attr)
-
value = value.duplicable? ? value.clone : value
-
rescue TypeError, NoMethodError
-
end
-
-
changed_attributes[attr] = value
-
end
-
-
# Handle <tt>reset_*!</tt> for +method_missing+.
-
1
def reset_attribute!(attr)
-
if attribute_changed?(attr)
-
__send__("#{attr}=", changed_attributes[attr])
-
changed_attributes.delete(attr)
-
end
-
end
-
end
-
end
-
# -*- coding: utf-8 -*-
-
-
1
require 'active_support/core_ext/array/conversions'
-
1
require 'active_support/core_ext/string/inflections'
-
-
1
module ActiveModel
-
# == Active \Model \Errors
-
#
-
# Provides a modified +Hash+ that you can include in your object
-
# for handling error messages and interacting with Action View helpers.
-
#
-
# A minimal implementation could be:
-
#
-
# class Person
-
# # Required dependency for ActiveModel::Errors
-
# extend ActiveModel::Naming
-
#
-
# def initialize
-
# @errors = ActiveModel::Errors.new(self)
-
# end
-
#
-
# attr_accessor :name
-
# attr_reader :errors
-
#
-
# def validate!
-
# errors.add(:name, "cannot be nil") if name == nil
-
# end
-
#
-
# # The following methods are needed to be minimally implemented
-
#
-
# def read_attribute_for_validation(attr)
-
# send(attr)
-
# end
-
#
-
# def Person.human_attribute_name(attr, options = {})
-
# attr
-
# end
-
#
-
# def Person.lookup_ancestors
-
# [self]
-
# end
-
# end
-
#
-
# The last three methods are required in your object for Errors to be
-
# able to generate error messages correctly and also handle multiple
-
# languages. Of course, if you extend your object with ActiveModel::Translation
-
# you will not need to implement the last two. Likewise, using
-
# ActiveModel::Validations will handle the validation related methods
-
# for you.
-
#
-
# The above allows you to do:
-
#
-
# person = Person.new
-
# person.validate! # => ["cannot be nil"]
-
# person.errors.full_messages # => ["name cannot be nil"]
-
# # etc..
-
1
class Errors
-
1
include Enumerable
-
-
1
CALLBACKS_OPTIONS = [:if, :unless, :on, :allow_nil, :allow_blank, :strict]
-
-
1
attr_reader :messages
-
-
# Pass in the instance of the object that is using the errors object.
-
#
-
# class Person
-
# def initialize
-
# @errors = ActiveModel::Errors.new(self)
-
# end
-
# end
-
1
def initialize(base)
-
6
@base = base
-
6
@messages = {}
-
end
-
-
1
def initialize_dup(other) # :nodoc:
-
@messages = other.messages.dup
-
super
-
end
-
-
# Clear the error messages.
-
#
-
# person.errors.full_messages # => ["name cannot be nil"]
-
# person.errors.clear
-
# person.errors.full_messages # => []
-
1
def clear
-
8
messages.clear
-
end
-
-
# Returns +true+ if the error messages include an error for the given key
-
# +attribute+, +false+ otherwise.
-
#
-
# person.errors.messages # => {:name=>["cannot be nil"]}
-
# person.errors.include?(:name) # => true
-
# person.errors.include?(:age) # => false
-
1
def include?(attribute)
-
messages[attribute].present?
-
end
-
# aliases include?
-
1
alias :has_key? :include?
-
-
# Get messages for +key+.
-
#
-
# person.errors.messages # => {:name=>["cannot be nil"]}
-
# person.errors.get(:name) # => ["cannot be nil"]
-
# person.errors.get(:age) # => nil
-
1
def get(key)
-
messages[key]
-
end
-
-
# Set messages for +key+ to +value+.
-
#
-
# person.errors.get(:name) # => ["cannot be nil"]
-
# person.errors.set(:name, ["can't be nil"])
-
# person.errors.get(:name) # => ["can't be nil"]
-
1
def set(key, value)
-
messages[key] = value
-
end
-
-
# Delete messages for +key+. Returns the deleted messages.
-
#
-
# person.errors.get(:name) # => ["cannot be nil"]
-
# person.errors.delete(:name) # => ["cannot be nil"]
-
# person.errors.get(:name) # => nil
-
1
def delete(key)
-
messages.delete(key)
-
end
-
-
# When passed a symbol or a name of a method, returns an array of errors
-
# for the method.
-
#
-
# person.errors[:name] # => ["cannot be nil"]
-
# person.errors['name'] # => ["cannot be nil"]
-
1
def [](attribute)
-
get(attribute.to_sym) || set(attribute.to_sym, [])
-
end
-
-
# Adds to the supplied attribute the supplied error message.
-
#
-
# person.errors[:name] = "must be set"
-
# person.errors[:name] # => ['must be set']
-
1
def []=(attribute, error)
-
self[attribute] << error
-
end
-
-
# Iterates through each error key, value pair in the error messages hash.
-
# Yields the attribute and the error for that attribute. If the attribute
-
# has more than one error message, yields once for each error message.
-
#
-
# person.errors.add(:name, "can't be blank")
-
# person.errors.each do |attribute, error|
-
# # Will yield :name and "can't be blank"
-
# end
-
#
-
# person.errors.add(:name, "must be specified")
-
# person.errors.each do |attribute, error|
-
# # Will yield :name and "can't be blank"
-
# # then yield :name and "must be specified"
-
# end
-
1
def each
-
16
messages.each_key do |attribute|
-
self[attribute].each { |error| yield attribute, error }
-
end
-
end
-
-
# Returns the number of error messages.
-
#
-
# person.errors.add(:name, "can't be blank")
-
# person.errors.size # => 1
-
# person.errors.add(:name, "must be specified")
-
# person.errors.size # => 2
-
1
def size
-
values.flatten.size
-
end
-
-
# Returns all message values.
-
#
-
# person.errors.messages # => {:name=>["cannot be nil", "must be specified"]}
-
# person.errors.values # => [["cannot be nil", "must be specified"]]
-
1
def values
-
messages.values
-
end
-
-
# Returns all message keys.
-
#
-
# person.errors.messages # => {:name=>["cannot be nil", "must be specified"]}
-
# person.errors.keys # => [:name]
-
1
def keys
-
messages.keys
-
end
-
-
# Returns an array of error messages, with the attribute name included.
-
#
-
# person.errors.add(:name, "can't be blank")
-
# person.errors.add(:name, "must be specified")
-
# person.errors.to_a # => ["name can't be blank", "name must be specified"]
-
1
def to_a
-
full_messages
-
end
-
-
# Returns the number of error messages.
-
#
-
# person.errors.add(:name, "can't be blank")
-
# person.errors.count # => 1
-
# person.errors.add(:name, "must be specified")
-
# person.errors.count # => 2
-
1
def count
-
to_a.size
-
end
-
-
# Returns +true+ if no errors are found, +false+ otherwise.
-
# If the error message is a string it can be empty.
-
#
-
# person.errors.full_messages # => ["name cannot be nil"]
-
# person.errors.empty? # => false
-
1
def empty?
-
16
all? { |k, v| v && v.empty? && !v.is_a?(String) }
-
end
-
# aliases empty?
-
1
alias_method :blank?, :empty?
-
-
# Returns an xml formatted representation of the Errors hash.
-
#
-
# person.errors.add(:name, "can't be blank")
-
# person.errors.add(:name, "must be specified")
-
# person.errors.to_xml
-
# # =>
-
# # <?xml version=\"1.0\" encoding=\"UTF-8\"?>
-
# # <errors>
-
# # <error>name can't be blank</error>
-
# # <error>name must be specified</error>
-
# # </errors>
-
1
def to_xml(options={})
-
to_a.to_xml({ root: "errors", skip_types: true }.merge!(options))
-
end
-
-
# Returns a Hash that can be used as the JSON representation for this
-
# object. You can pass the <tt>:full_messages</tt> option. This determines
-
# if the json object should contain full messages or not (false by default).
-
#
-
# person.errors.as_json # => {:name=>["cannot be nil"]}
-
# person.errors.as_json(full_messages: true) # => {:name=>["name cannot be nil"]}
-
1
def as_json(options=nil)
-
to_hash(options && options[:full_messages])
-
end
-
-
# Returns a Hash of attributes with their error messages. If +full_messages+
-
# is +true+, it will contain full messages (see +full_message+).
-
#
-
# person.errors.to_hash # => {:name=>["cannot be nil"]}
-
# person.errors.to_hash(true) # => {:name=>["name cannot be nil"]}
-
1
def to_hash(full_messages = false)
-
if full_messages
-
messages = {}
-
self.messages.each do |attribute, array|
-
messages[attribute] = array.map { |message| full_message(attribute, message) }
-
end
-
messages
-
else
-
self.messages.dup
-
end
-
end
-
-
# Adds +message+ to the error messages on +attribute+. More than one error
-
# can be added to the same +attribute+. If no +message+ is supplied,
-
# <tt>:invalid</tt> is assumed.
-
#
-
# person.errors.add(:name)
-
# # => ["is invalid"]
-
# person.errors.add(:name, 'must be implemented')
-
# # => ["is invalid", "must be implemented"]
-
#
-
# person.errors.messages
-
# # => {:name=>["must be implemented", "is invalid"]}
-
#
-
# If +message+ is a symbol, it will be translated using the appropriate
-
# scope (see +generate_message+).
-
#
-
# If +message+ is a proc, it will be called, allowing for things like
-
# <tt>Time.now</tt> to be used within an error.
-
#
-
# If the <tt>:strict</tt> option is set to +true+, it will raise
-
# ActiveModel::StrictValidationFailed instead of adding the error.
-
# <tt>:strict</tt> option can also be set to any other exception.
-
#
-
# person.errors.add(:name, nil, strict: true)
-
# # => ActiveModel::StrictValidationFailed: name is invalid
-
# person.errors.add(:name, nil, strict: NameIsInvalid)
-
# # => NameIsInvalid: name is invalid
-
#
-
# person.errors.messages # => {}
-
1
def add(attribute, message = :invalid, options = {})
-
message = normalize_message(attribute, message, options)
-
if exception = options[:strict]
-
exception = ActiveModel::StrictValidationFailed if exception == true
-
raise exception, full_message(attribute, message)
-
end
-
-
self[attribute] << message
-
end
-
-
# Will add an error message to each of the attributes in +attributes+
-
# that is empty.
-
#
-
# person.errors.add_on_empty(:name)
-
# person.errors.messages
-
# # => {:name=>["can't be empty"]}
-
1
def add_on_empty(attributes, options = {})
-
Array(attributes).each do |attribute|
-
value = @base.send(:read_attribute_for_validation, attribute)
-
is_empty = value.respond_to?(:empty?) ? value.empty? : false
-
add(attribute, :empty, options) if value.nil? || is_empty
-
end
-
end
-
-
# Will add an error message to each of the attributes in +attributes+ that
-
# is blank (using Object#blank?).
-
#
-
# person.errors.add_on_blank(:name)
-
# person.errors.messages
-
# # => {:name=>["can't be blank"]}
-
1
def add_on_blank(attributes, options = {})
-
Array(attributes).each do |attribute|
-
value = @base.send(:read_attribute_for_validation, attribute)
-
add(attribute, :blank, options) if value.blank?
-
end
-
end
-
-
# Returns +true+ if an error on the attribute with the given message is
-
# present, +false+ otherwise. +message+ is treated the same as for +add+.
-
#
-
# person.errors.add :name, :blank
-
# person.errors.added? :name, :blank # => true
-
1
def added?(attribute, message = :invalid, options = {})
-
message = normalize_message(attribute, message, options)
-
self[attribute].include? message
-
end
-
-
# Returns all the full error messages in an array.
-
#
-
# class Person
-
# validates_presence_of :name, :address, :email
-
# validates_length_of :name, in: 5..30
-
# end
-
#
-
# person = Person.create(address: '123 First St.')
-
# person.errors.full_messages
-
# # => ["Name is too short (minimum is 5 characters)", "Name can't be blank", "Email can't be blank"]
-
1
def full_messages
-
map { |attribute, message| full_message(attribute, message) }
-
end
-
-
# Returns all the full error messages for a given attribute in an array.
-
#
-
# class Person
-
# validates_presence_of :name, :email
-
# validates_length_of :name, in: 5..30
-
# end
-
#
-
# person = Person.create()
-
# person.errors.full_messages_for(:name)
-
# # => ["Name is too short (minimum is 5 characters)", "Name can't be blank"]
-
1
def full_messages_for(attribute)
-
(get(attribute) || []).map { |message| full_message(attribute, message) }
-
end
-
-
# Returns a full message for a given attribute.
-
#
-
# person.errors.full_message(:name, 'is invalid') # => "Name is invalid"
-
1
def full_message(attribute, message)
-
return message if attribute == :base
-
attr_name = attribute.to_s.tr('.', '_').humanize
-
attr_name = @base.class.human_attribute_name(attribute, default: attr_name)
-
I18n.t(:"errors.format", {
-
default: "%{attribute} %{message}",
-
attribute: attr_name,
-
message: message
-
})
-
end
-
-
# Translates an error message in its default scope
-
# (<tt>activemodel.errors.messages</tt>).
-
#
-
# Error messages are first looked up in <tt>models.MODEL.attributes.ATTRIBUTE.MESSAGE</tt>,
-
# if it's not there, it's looked up in <tt>models.MODEL.MESSAGE</tt> and if
-
# that is not there also, it returns the translation of the default message
-
# (e.g. <tt>activemodel.errors.messages.MESSAGE</tt>). The translated model
-
# name, translated attribute name and the value are available for
-
# interpolation.
-
#
-
# When using inheritance in your models, it will check all the inherited
-
# models too, but only if the model itself hasn't been found. Say you have
-
# <tt>class Admin < User; end</tt> and you wanted the translation for
-
# the <tt>:blank</tt> error message for the <tt>title</tt> attribute,
-
# it looks for these translations:
-
#
-
# * <tt>activemodel.errors.models.admin.attributes.title.blank</tt>
-
# * <tt>activemodel.errors.models.admin.blank</tt>
-
# * <tt>activemodel.errors.models.user.attributes.title.blank</tt>
-
# * <tt>activemodel.errors.models.user.blank</tt>
-
# * any default you provided through the +options+ hash (in the <tt>activemodel.errors</tt> scope)
-
# * <tt>activemodel.errors.messages.blank</tt>
-
# * <tt>errors.attributes.title.blank</tt>
-
# * <tt>errors.messages.blank</tt>
-
1
def generate_message(attribute, type = :invalid, options = {})
-
type = options.delete(:message) if options[:message].is_a?(Symbol)
-
-
if @base.class.respond_to?(:i18n_scope)
-
defaults = @base.class.lookup_ancestors.map do |klass|
-
[ :"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.i18n_key}.attributes.#{attribute}.#{type}",
-
:"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.i18n_key}.#{type}" ]
-
end
-
else
-
defaults = []
-
end
-
-
defaults << options.delete(:message)
-
defaults << :"#{@base.class.i18n_scope}.errors.messages.#{type}" if @base.class.respond_to?(:i18n_scope)
-
defaults << :"errors.attributes.#{attribute}.#{type}"
-
defaults << :"errors.messages.#{type}"
-
-
defaults.compact!
-
defaults.flatten!
-
-
key = defaults.shift
-
value = (attribute != :base ? @base.send(:read_attribute_for_validation, attribute) : nil)
-
-
options = {
-
default: defaults,
-
model: @base.class.model_name.human,
-
attribute: @base.class.human_attribute_name(attribute),
-
value: value
-
}.merge!(options)
-
-
I18n.translate(key, options)
-
end
-
-
1
private
-
1
def normalize_message(attribute, message, options)
-
case message
-
when Symbol
-
generate_message(attribute, message, options.except(*CALLBACKS_OPTIONS))
-
when Proc
-
message.call
-
else
-
message
-
end
-
end
-
end
-
-
# Raised when a validation cannot be corrected by end users and are considered
-
# exceptional.
-
#
-
# class Person
-
# include ActiveModel::Validations
-
#
-
# attr_accessor :name
-
#
-
# validates_presence_of :name, strict: true
-
# end
-
#
-
# person = Person.new
-
# person.name = nil
-
# person.valid?
-
# # => ActiveModel::StrictValidationFailed: Name can't be blank
-
1
class StrictValidationFailed < StandardError
-
end
-
end
-
1
module ActiveModel
-
# Raised when forbidden attributes are used for mass assignment.
-
#
-
# class Person < ActiveRecord::Base
-
# end
-
#
-
# params = ActionController::Parameters.new(name: 'Bob')
-
# Person.new(params)
-
# # => ActiveModel::ForbiddenAttributesError
-
#
-
# params.permit!
-
# Person.new(params)
-
# # => #<Person id: nil, name: "Bob">
-
1
class ForbiddenAttributesError < StandardError
-
end
-
-
1
module ForbiddenAttributesProtection # :nodoc:
-
1
protected
-
1
def sanitize_for_mass_assignment(attributes)
-
6
if attributes.respond_to?(:permitted?) && !attributes.permitted?
-
raise ActiveModel::ForbiddenAttributesError
-
else
-
6
attributes
-
end
-
end
-
1
alias :sanitize_forbidden_attributes :sanitize_for_mass_assignment
-
end
-
end
-
1
module ActiveModel
-
# Returns the version of the currently loaded ActiveModel as a <tt>Gem::Version</tt>
-
1
def self.gem_version
-
Gem::Version.new VERSION::STRING
-
end
-
-
1
module VERSION
-
1
MAJOR = 4
-
1
MINOR = 1
-
1
TINY = 6
-
1
PRE = nil
-
-
1
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
-
end
-
end
-
1
require 'active_support/core_ext/hash/except'
-
1
require 'active_support/core_ext/module/introspection'
-
-
1
module ActiveModel
-
1
class Name
-
1
include Comparable
-
-
1
attr_reader :singular, :plural, :element, :collection,
-
:singular_route_key, :route_key, :param_key, :i18n_key,
-
:name
-
-
1
alias_method :cache_key, :collection
-
-
##
-
# :method: ==
-
#
-
# :call-seq:
-
# ==(other)
-
#
-
# Equivalent to <tt>String#==</tt>. Returns +true+ if the class name and
-
# +other+ are equal, otherwise +false+.
-
#
-
# class BlogPost
-
# extend ActiveModel::Naming
-
# end
-
#
-
# BlogPost.model_name == 'BlogPost' # => true
-
# BlogPost.model_name == 'Blog Post' # => false
-
-
##
-
# :method: ===
-
#
-
# :call-seq:
-
# ===(other)
-
#
-
# Equivalent to <tt>#==</tt>.
-
#
-
# class BlogPost
-
# extend ActiveModel::Naming
-
# end
-
#
-
# BlogPost.model_name === 'BlogPost' # => true
-
# BlogPost.model_name === 'Blog Post' # => false
-
-
##
-
# :method: <=>
-
#
-
# :call-seq:
-
# ==(other)
-
#
-
# Equivalent to <tt>String#<=></tt>.
-
#
-
# class BlogPost
-
# extend ActiveModel::Naming
-
# end
-
#
-
# BlogPost.model_name <=> 'BlogPost' # => 0
-
# BlogPost.model_name <=> 'Blog' # => 1
-
# BlogPost.model_name <=> 'BlogPosts' # => -1
-
-
##
-
# :method: =~
-
#
-
# :call-seq:
-
# =~(regexp)
-
#
-
# Equivalent to <tt>String#=~</tt>. Match the class name against the given
-
# regexp. Returns the position where the match starts or +nil+ if there is
-
# no match.
-
#
-
# class BlogPost
-
# extend ActiveModel::Naming
-
# end
-
#
-
# BlogPost.model_name =~ /Post/ # => 4
-
# BlogPost.model_name =~ /\d/ # => nil
-
-
##
-
# :method: !~
-
#
-
# :call-seq:
-
# !~(regexp)
-
#
-
# Equivalent to <tt>String#!~</tt>. Match the class name against the given
-
# regexp. Returns +true+ if there is no match, otherwise +false+.
-
#
-
# class BlogPost
-
# extend ActiveModel::Naming
-
# end
-
#
-
# BlogPost.model_name !~ /Post/ # => false
-
# BlogPost.model_name !~ /\d/ # => true
-
-
##
-
# :method: eql?
-
#
-
# :call-seq:
-
# eql?(other)
-
#
-
# Equivalent to <tt>String#eql?</tt>. Returns +true+ if the class name and
-
# +other+ have the same length and content, otherwise +false+.
-
#
-
# class BlogPost
-
# extend ActiveModel::Naming
-
# end
-
#
-
# BlogPost.model_name.eql?('BlogPost') # => true
-
# BlogPost.model_name.eql?('Blog Post') # => false
-
-
##
-
# :method: to_s
-
#
-
# :call-seq:
-
# to_s()
-
#
-
# Returns the class name.
-
#
-
# class BlogPost
-
# extend ActiveModel::Naming
-
# end
-
#
-
# BlogPost.model_name.to_s # => "BlogPost"
-
-
##
-
# :method: to_str
-
#
-
# :call-seq:
-
# to_str()
-
#
-
# Equivalent to +to_s+.
-
1
delegate :==, :===, :<=>, :=~, :"!~", :eql?, :to_s,
-
:to_str, to: :name
-
-
# Returns a new ActiveModel::Name instance. By default, the +namespace+
-
# and +name+ option will take the namespace and name of the given class
-
# respectively.
-
#
-
# module Foo
-
# class Bar
-
# end
-
# end
-
#
-
# ActiveModel::Name.new(Foo::Bar).to_s
-
# # => "Foo::Bar"
-
1
def initialize(klass, namespace = nil, name = nil)
-
@name = name || klass.name
-
-
raise ArgumentError, "Class name cannot be blank. You need to supply a name argument when anonymous class given" if @name.blank?
-
-
@unnamespaced = @name.sub(/^#{namespace.name}::/, '') if namespace
-
@klass = klass
-
@singular = _singularize(@name)
-
@plural = ActiveSupport::Inflector.pluralize(@singular)
-
@element = ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(@name))
-
@human = ActiveSupport::Inflector.humanize(@element)
-
@collection = ActiveSupport::Inflector.tableize(@name)
-
@param_key = (namespace ? _singularize(@unnamespaced) : @singular)
-
@i18n_key = @name.underscore.to_sym
-
-
@route_key = (namespace ? ActiveSupport::Inflector.pluralize(@param_key) : @plural.dup)
-
@singular_route_key = ActiveSupport::Inflector.singularize(@route_key)
-
@route_key << "_index" if @plural == @singular
-
end
-
-
# Transform the model name into a more humane format, using I18n. By default,
-
# it will underscore then humanize the class name.
-
#
-
# class BlogPost
-
# extend ActiveModel::Naming
-
# end
-
#
-
# BlogPost.model_name.human # => "Blog post"
-
#
-
# Specify +options+ with additional translating options.
-
1
def human(options={})
-
return @human unless @klass.respond_to?(:lookup_ancestors) &&
-
@klass.respond_to?(:i18n_scope)
-
-
defaults = @klass.lookup_ancestors.map do |klass|
-
klass.model_name.i18n_key
-
end
-
-
defaults << options[:default] if options[:default]
-
defaults << @human
-
-
options = { scope: [@klass.i18n_scope, :models], count: 1, default: defaults }.merge!(options.except(:default))
-
I18n.translate(defaults.shift, options)
-
end
-
-
1
private
-
-
1
def _singularize(string, replacement='_')
-
ActiveSupport::Inflector.underscore(string).tr('/', replacement)
-
end
-
end
-
-
# == Active \Model \Naming
-
#
-
# Creates a +model_name+ method on your object.
-
#
-
# To implement, just extend ActiveModel::Naming in your object:
-
#
-
# class BookCover
-
# extend ActiveModel::Naming
-
# end
-
#
-
# BookCover.model_name # => "BookCover"
-
# BookCover.model_name.human # => "Book cover"
-
#
-
# BookCover.model_name.i18n_key # => :book_cover
-
# BookModule::BookCover.model_name.i18n_key # => :"book_module/book_cover"
-
#
-
# Providing the functionality that ActiveModel::Naming provides in your object
-
# is required to pass the Active Model Lint test. So either extending the
-
# provided method below, or rolling your own is required.
-
1
module Naming
-
# Returns an ActiveModel::Name object for module. It can be
-
# used to retrieve all kinds of naming-related information
-
# (See ActiveModel::Name for more information).
-
#
-
# class Person < ActiveModel::Model
-
# end
-
#
-
# Person.model_name # => Person
-
# Person.model_name.class # => ActiveModel::Name
-
# Person.model_name.singular # => "person"
-
# Person.model_name.plural # => "people"
-
1
def model_name
-
@_model_name ||= begin
-
namespace = self.parents.detect do |n|
-
n.respond_to?(:use_relative_model_naming?) && n.use_relative_model_naming?
-
end
-
ActiveModel::Name.new(self, namespace)
-
end
-
end
-
-
# Returns the plural class name of a record or class.
-
#
-
# ActiveModel::Naming.plural(post) # => "posts"
-
# ActiveModel::Naming.plural(Highrise::Person) # => "highrise_people"
-
1
def self.plural(record_or_class)
-
model_name_from_record_or_class(record_or_class).plural
-
end
-
-
# Returns the singular class name of a record or class.
-
#
-
# ActiveModel::Naming.singular(post) # => "post"
-
# ActiveModel::Naming.singular(Highrise::Person) # => "highrise_person"
-
1
def self.singular(record_or_class)
-
model_name_from_record_or_class(record_or_class).singular
-
end
-
-
# Identifies whether the class name of a record or class is uncountable.
-
#
-
# ActiveModel::Naming.uncountable?(Sheep) # => true
-
# ActiveModel::Naming.uncountable?(Post) # => false
-
1
def self.uncountable?(record_or_class)
-
plural(record_or_class) == singular(record_or_class)
-
end
-
-
# Returns string to use while generating route names. It differs for
-
# namespaced models regarding whether it's inside isolated engine.
-
#
-
# # For isolated engine:
-
# ActiveModel::Naming.singular_route_key(Blog::Post) # => "post"
-
#
-
# # For shared engine:
-
# ActiveModel::Naming.singular_route_key(Blog::Post) # => "blog_post"
-
1
def self.singular_route_key(record_or_class)
-
model_name_from_record_or_class(record_or_class).singular_route_key
-
end
-
-
# Returns string to use while generating route names. It differs for
-
# namespaced models regarding whether it's inside isolated engine.
-
#
-
# # For isolated engine:
-
# ActiveModel::Naming.route_key(Blog::Post) # => "posts"
-
#
-
# # For shared engine:
-
# ActiveModel::Naming.route_key(Blog::Post) # => "blog_posts"
-
#
-
# The route key also considers if the noun is uncountable and, in
-
# such cases, automatically appends _index.
-
1
def self.route_key(record_or_class)
-
model_name_from_record_or_class(record_or_class).route_key
-
end
-
-
# Returns string to use for params names. It differs for
-
# namespaced models regarding whether it's inside isolated engine.
-
#
-
# # For isolated engine:
-
# ActiveModel::Naming.param_key(Blog::Post) # => "post"
-
#
-
# # For shared engine:
-
# ActiveModel::Naming.param_key(Blog::Post) # => "blog_post"
-
1
def self.param_key(record_or_class)
-
model_name_from_record_or_class(record_or_class).param_key
-
end
-
-
1
def self.model_name_from_record_or_class(record_or_class) #:nodoc:
-
if record_or_class.respond_to?(:model_name)
-
record_or_class.model_name
-
elsif record_or_class.respond_to?(:to_model)
-
record_or_class.to_model.class.model_name
-
else
-
record_or_class.class.model_name
-
end
-
end
-
1
private_class_method :model_name_from_record_or_class
-
end
-
end
-
1
require "active_model"
-
1
require "rails"
-
-
1
module ActiveModel
-
1
class Railtie < Rails::Railtie # :nodoc:
-
1
config.eager_load_namespaces << ActiveModel
-
-
1
initializer "active_model.secure_password" do
-
1
ActiveModel::SecurePassword.min_cost = Rails.env.test?
-
end
-
end
-
end
-
1
module ActiveModel
-
1
module SecurePassword
-
1
extend ActiveSupport::Concern
-
-
1
class << self
-
1
attr_accessor :min_cost # :nodoc:
-
end
-
1
self.min_cost = false
-
-
1
module ClassMethods
-
# Adds methods to set and authenticate against a BCrypt password.
-
# This mechanism requires you to have a +password_digest+ attribute.
-
#
-
# Validations for presence of password on create, confirmation of password
-
# (using a +password_confirmation+ attribute) are automatically added. If
-
# you wish to turn off validations, pass <tt>validations: false</tt> as an
-
# argument. You can add more validations by hand if need be.
-
#
-
# If you don't need the confirmation validation, just don't set any
-
# value to the password_confirmation attribute and the validation
-
# will not be triggered.
-
#
-
# You need to add bcrypt (~> 3.1.7) to Gemfile to use #has_secure_password:
-
#
-
# gem 'bcrypt', '~> 3.1.7'
-
#
-
# Example using Active Record (which automatically includes ActiveModel::SecurePassword):
-
#
-
# # Schema: User(name:string, password_digest:string)
-
# class User < ActiveRecord::Base
-
# has_secure_password
-
# end
-
#
-
# user = User.new(name: 'david', password: '', password_confirmation: 'nomatch')
-
# user.save # => false, password required
-
# user.password = 'mUc3m00RsqyRe'
-
# user.save # => false, confirmation doesn't match
-
# user.password_confirmation = 'mUc3m00RsqyRe'
-
# user.save # => true
-
# user.authenticate('notright') # => false
-
# user.authenticate('mUc3m00RsqyRe') # => user
-
# User.find_by(name: 'david').try(:authenticate, 'notright') # => false
-
# User.find_by(name: 'david').try(:authenticate, 'mUc3m00RsqyRe') # => user
-
1
def has_secure_password(options = {})
-
# Load bcrypt gem only when has_secure_password is used.
-
# This is to avoid ActiveModel (and by extension the entire framework)
-
# being dependent on a binary library.
-
begin
-
require 'bcrypt'
-
rescue LoadError
-
$stderr.puts "You don't have bcrypt installed in your application. Please add it to your Gemfile and run bundle install"
-
raise
-
end
-
-
attr_reader :password
-
-
include InstanceMethodsOnActivation
-
-
if options.fetch(:validations, true)
-
# This ensures the model has a password by checking whether the password_digest
-
# is present, so that this works with both new and existing records. However,
-
# when there is an error, the message is added to the password attribute instead
-
# so that the error message will make sense to the end-user.
-
validate do |record|
-
record.errors.add(:password, :blank) unless record.password_digest.present?
-
end
-
-
validates_confirmation_of :password, if: ->{ password.present? }
-
end
-
-
if respond_to?(:attributes_protected_by_default)
-
def self.attributes_protected_by_default #:nodoc:
-
super + ['password_digest']
-
end
-
end
-
end
-
end
-
-
1
module InstanceMethodsOnActivation
-
# Returns +self+ if the password is correct, otherwise +false+.
-
#
-
# class User < ActiveRecord::Base
-
# has_secure_password validations: false
-
# end
-
#
-
# user = User.new(name: 'david', password: 'mUc3m00RsqyRe')
-
# user.save
-
# user.authenticate('notright') # => false
-
# user.authenticate('mUc3m00RsqyRe') # => user
-
1
def authenticate(unencrypted_password)
-
BCrypt::Password.new(password_digest) == unencrypted_password && self
-
end
-
-
# Encrypts the password into the +password_digest+ attribute, only if the
-
# new password is not blank.
-
#
-
# class User < ActiveRecord::Base
-
# has_secure_password validations: false
-
# end
-
#
-
# user = User.new
-
# user.password = nil
-
# user.password_digest # => nil
-
# user.password = 'mUc3m00RsqyRe'
-
# user.password_digest # => "$2a$10$4LEA7r4YmNHtvlAvHhsYAeZmk/xeUVtMTYqwIvYY76EW5GUqDiP4."
-
1
def password=(unencrypted_password)
-
if unencrypted_password.nil?
-
self.password_digest = nil
-
elsif unencrypted_password.present?
-
@password = unencrypted_password
-
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : BCrypt::Engine.cost
-
self.password_digest = BCrypt::Password.create(unencrypted_password, cost: cost)
-
end
-
end
-
-
1
def password_confirmation=(unencrypted_password)
-
@password_confirmation = unencrypted_password
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/hash/except'
-
1
require 'active_support/core_ext/hash/slice'
-
-
1
module ActiveModel
-
# == Active \Model \Serialization
-
#
-
# Provides a basic serialization to a serializable_hash for your object.
-
#
-
# A minimal implementation could be:
-
#
-
# class Person
-
# include ActiveModel::Serialization
-
#
-
# attr_accessor :name
-
#
-
# def attributes
-
# {'name' => nil}
-
# end
-
# end
-
#
-
# Which would provide you with:
-
#
-
# person = Person.new
-
# person.serializable_hash # => {"name"=>nil}
-
# person.name = "Bob"
-
# person.serializable_hash # => {"name"=>"Bob"}
-
#
-
# You need to declare an attributes hash which contains the attributes you
-
# want to serialize. Attributes must be strings, not symbols. When called,
-
# serializable hash will use instance methods that match the name of the
-
# attributes hash's keys. In order to override this behavior, take a look at
-
# the private method +read_attribute_for_serialization+.
-
#
-
# Most of the time though, you will want to include the JSON or XML
-
# serializations. Both of these modules automatically include the
-
# <tt>ActiveModel::Serialization</tt> module, so there is no need to
-
# explicitly include it.
-
#
-
# A minimal implementation including XML and JSON would be:
-
#
-
# class Person
-
# include ActiveModel::Serializers::JSON
-
# include ActiveModel::Serializers::Xml
-
#
-
# attr_accessor :name
-
#
-
# def attributes
-
# {'name' => nil}
-
# end
-
# end
-
#
-
# Which would provide you with:
-
#
-
# person = Person.new
-
# person.serializable_hash # => {"name"=>nil}
-
# person.as_json # => {"name"=>nil}
-
# person.to_json # => "{\"name\":null}"
-
# person.to_xml # => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<serial-person...
-
#
-
# person.name = "Bob"
-
# person.serializable_hash # => {"name"=>"Bob"}
-
# person.as_json # => {"name"=>"Bob"}
-
# person.to_json # => "{\"name\":\"Bob\"}"
-
# person.to_xml # => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<serial-person...
-
#
-
# Valid options are <tt>:only</tt>, <tt>:except</tt>, <tt>:methods</tt> and
-
# <tt>:include</tt>. The following are all valid examples:
-
#
-
# person.serializable_hash(only: 'name')
-
# person.serializable_hash(include: :address)
-
# person.serializable_hash(include: { address: { only: 'city' }})
-
1
module Serialization
-
# Returns a serialized hash of your object.
-
#
-
# class Person
-
# include ActiveModel::Serialization
-
#
-
# attr_accessor :name, :age
-
#
-
# def attributes
-
# {'name' => nil, 'age' => nil}
-
# end
-
#
-
# def capitalized_name
-
# name.capitalize
-
# end
-
# end
-
#
-
# person = Person.new
-
# person.name = 'bob'
-
# person.age = 22
-
# person.serializable_hash # => {"name"=>"bob", "age"=>22}
-
# person.serializable_hash(only: :name) # => {"name"=>"bob"}
-
# person.serializable_hash(except: :name) # => {"age"=>22}
-
# person.serializable_hash(methods: :capitalized_name)
-
# # => {"name"=>"bob", "age"=>22, "capitalized_name"=>"Bob"}
-
1
def serializable_hash(options = nil)
-
options ||= {}
-
-
attribute_names = attributes.keys
-
if only = options[:only]
-
attribute_names &= Array(only).map(&:to_s)
-
elsif except = options[:except]
-
attribute_names -= Array(except).map(&:to_s)
-
end
-
-
hash = {}
-
attribute_names.each { |n| hash[n] = read_attribute_for_serialization(n) }
-
-
Array(options[:methods]).each { |m| hash[m.to_s] = send(m) if respond_to?(m) }
-
-
serializable_add_includes(options) do |association, records, opts|
-
hash[association.to_s] = if records.respond_to?(:to_ary)
-
records.to_ary.map { |a| a.serializable_hash(opts) }
-
else
-
records.serializable_hash(opts)
-
end
-
end
-
-
hash
-
end
-
-
1
private
-
-
# Hook method defining how an attribute value should be retrieved for
-
# serialization. By default this is assumed to be an instance named after
-
# the attribute. Override this method in subclasses should you need to
-
# retrieve the value for a given attribute differently:
-
#
-
# class MyClass
-
# include ActiveModel::Serialization
-
#
-
# def initialize(data = {})
-
# @data = data
-
# end
-
#
-
# def read_attribute_for_serialization(key)
-
# @data[key]
-
# end
-
# end
-
1
alias :read_attribute_for_serialization :send
-
-
# Add associations specified via the <tt>:include</tt> option.
-
#
-
# Expects a block that takes as arguments:
-
# +association+ - name of the association
-
# +records+ - the association record(s) to be serialized
-
# +opts+ - options for the association records
-
1
def serializable_add_includes(options = {}) #:nodoc:
-
return unless includes = options[:include]
-
-
unless includes.is_a?(Hash)
-
includes = Hash[Array(includes).map { |n| n.is_a?(Hash) ? n.to_a.first : [n, {}] }]
-
end
-
-
includes.each do |association, opts|
-
if records = send(association)
-
yield association, records, opts
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/json'
-
-
1
module ActiveModel
-
1
module Serializers
-
# == Active \Model \JSON \Serializer
-
1
module JSON
-
1
extend ActiveSupport::Concern
-
1
include ActiveModel::Serialization
-
-
1
included do
-
1
extend ActiveModel::Naming
-
-
1
class_attribute :include_root_in_json
-
1
self.include_root_in_json = false
-
end
-
-
# Returns a hash representing the model. Some configuration can be
-
# passed through +options+.
-
#
-
# The option <tt>include_root_in_json</tt> controls the top-level behavior
-
# of +as_json+. If +true+, +as_json+ will emit a single root node named
-
# after the object's type. The default value for <tt>include_root_in_json</tt>
-
# option is +false+.
-
#
-
# user = User.find(1)
-
# user.as_json
-
# # => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
-
# # "created_at" => "2006/08/01", "awesome" => true}
-
#
-
# ActiveRecord::Base.include_root_in_json = true
-
#
-
# user.as_json
-
# # => { "user" => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
-
# # "created_at" => "2006/08/01", "awesome" => true } }
-
#
-
# This behavior can also be achieved by setting the <tt>:root</tt> option
-
# to +true+ as in:
-
#
-
# user = User.find(1)
-
# user.as_json(root: true)
-
# # => { "user" => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
-
# # "created_at" => "2006/08/01", "awesome" => true } }
-
#
-
# Without any +options+, the returned Hash will include all the model's
-
# attributes.
-
#
-
# user = User.find(1)
-
# user.as_json
-
# # => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
-
# # "created_at" => "2006/08/01", "awesome" => true}
-
#
-
# The <tt>:only</tt> and <tt>:except</tt> options can be used to limit
-
# the attributes included, and work similar to the +attributes+ method.
-
#
-
# user.as_json(only: [:id, :name])
-
# # => { "id" => 1, "name" => "Konata Izumi" }
-
#
-
# user.as_json(except: [:id, :created_at, :age])
-
# # => { "name" => "Konata Izumi", "awesome" => true }
-
#
-
# To include the result of some method calls on the model use <tt>:methods</tt>:
-
#
-
# user.as_json(methods: :permalink)
-
# # => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
-
# # "created_at" => "2006/08/01", "awesome" => true,
-
# # "permalink" => "1-konata-izumi" }
-
#
-
# To include associations use <tt>:include</tt>:
-
#
-
# user.as_json(include: :posts)
-
# # => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
-
# # "created_at" => "2006/08/01", "awesome" => true,
-
# # "posts" => [ { "id" => 1, "author_id" => 1, "title" => "Welcome to the weblog" },
-
# # { "id" => 2, "author_id" => 1, "title" => "So I was thinking" } ] }
-
#
-
# Second level and higher order associations work as well:
-
#
-
# user.as_json(include: { posts: {
-
# include: { comments: {
-
# only: :body } },
-
# only: :title } })
-
# # => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
-
# # "created_at" => "2006/08/01", "awesome" => true,
-
# # "posts" => [ { "comments" => [ { "body" => "1st post!" }, { "body" => "Second!" } ],
-
# # "title" => "Welcome to the weblog" },
-
# # { "comments" => [ { "body" => "Don't think too hard" } ],
-
# # "title" => "So I was thinking" } ] }
-
1
def as_json(options = nil)
-
root = if options && options.key?(:root)
-
options[:root]
-
else
-
include_root_in_json
-
end
-
-
if root
-
root = self.class.model_name.element if root == true
-
{ root => serializable_hash(options) }
-
else
-
serializable_hash(options)
-
end
-
end
-
-
# Sets the model +attributes+ from a JSON string. Returns +self+.
-
#
-
# class Person
-
# include ActiveModel::Serializers::JSON
-
#
-
# attr_accessor :name, :age, :awesome
-
#
-
# def attributes=(hash)
-
# hash.each do |key, value|
-
# send("#{key}=", value)
-
# end
-
# end
-
#
-
# def attributes
-
# instance_values
-
# end
-
# end
-
#
-
# json = { name: 'bob', age: 22, awesome:true }.to_json
-
# person = Person.new
-
# person.from_json(json) # => #<Person:0x007fec5e7a0088 @age=22, @awesome=true, @name="bob">
-
# person.name # => "bob"
-
# person.age # => 22
-
# person.awesome # => true
-
#
-
# The default value for +include_root+ is +false+. You can change it to
-
# +true+ if the given JSON string includes a single root node.
-
#
-
# json = { person: { name: 'bob', age: 22, awesome:true } }.to_json
-
# person = Person.new
-
# person.from_json(json) # => #<Person:0x007fec5e7a0088 @age=22, @awesome=true, @name="bob">
-
# person.name # => "bob"
-
# person.age # => 22
-
# person.awesome # => true
-
1
def from_json(json, include_root=include_root_in_json)
-
hash = ActiveSupport::JSON.decode(json)
-
hash = hash.values.first if include_root
-
self.attributes = hash
-
self
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/module/attribute_accessors'
-
1
require 'active_support/core_ext/array/conversions'
-
1
require 'active_support/core_ext/hash/conversions'
-
1
require 'active_support/core_ext/hash/slice'
-
1
require 'active_support/core_ext/time/acts_like'
-
-
1
module ActiveModel
-
1
module Serializers
-
# == Active Model XML Serializer
-
1
module Xml
-
1
extend ActiveSupport::Concern
-
1
include ActiveModel::Serialization
-
-
1
included do
-
1
extend ActiveModel::Naming
-
end
-
-
1
class Serializer #:nodoc:
-
1
class Attribute #:nodoc:
-
1
attr_reader :name, :value, :type
-
-
1
def initialize(name, serializable, value)
-
@name, @serializable = name, serializable
-
-
if value.acts_like?(:time) && value.respond_to?(:in_time_zone)
-
value = value.in_time_zone
-
end
-
-
@value = value
-
@type = compute_type
-
end
-
-
1
def decorations
-
decorations = {}
-
decorations[:encoding] = 'base64' if type == :binary
-
decorations[:type] = (type == :string) ? nil : type
-
decorations[:nil] = true if value.nil?
-
decorations
-
end
-
-
1
protected
-
-
1
def compute_type
-
return if value.nil?
-
type = ActiveSupport::XmlMini::TYPE_NAMES[value.class.name]
-
type ||= :string if value.respond_to?(:to_str)
-
type ||= :yaml
-
type
-
end
-
end
-
-
1
class MethodAttribute < Attribute #:nodoc:
-
end
-
-
1
attr_reader :options
-
-
1
def initialize(serializable, options = nil)
-
@serializable = serializable
-
@options = options ? options.dup : {}
-
end
-
-
1
def serializable_hash
-
@serializable.serializable_hash(@options.except(:include))
-
end
-
-
1
def serializable_collection
-
methods = Array(options[:methods]).map(&:to_s)
-
serializable_hash.map do |name, value|
-
name = name.to_s
-
if methods.include?(name)
-
self.class::MethodAttribute.new(name, @serializable, value)
-
else
-
self.class::Attribute.new(name, @serializable, value)
-
end
-
end
-
end
-
-
1
def serialize
-
require 'builder' unless defined? ::Builder
-
-
options[:indent] ||= 2
-
options[:builder] ||= ::Builder::XmlMarkup.new(indent: options[:indent])
-
-
@builder = options[:builder]
-
@builder.instruct! unless options[:skip_instruct]
-
-
root = (options[:root] || @serializable.class.model_name.element).to_s
-
root = ActiveSupport::XmlMini.rename_key(root, options)
-
-
args = [root]
-
args << { xmlns: options[:namespace] } if options[:namespace]
-
args << { type: options[:type] } if options[:type] && !options[:skip_types]
-
-
@builder.tag!(*args) do
-
add_attributes_and_methods
-
add_includes
-
add_extra_behavior
-
add_procs
-
yield @builder if block_given?
-
end
-
end
-
-
1
private
-
-
1
def add_extra_behavior
-
end
-
-
1
def add_attributes_and_methods
-
serializable_collection.each do |attribute|
-
key = ActiveSupport::XmlMini.rename_key(attribute.name, options)
-
ActiveSupport::XmlMini.to_tag(key, attribute.value,
-
options.merge(attribute.decorations))
-
end
-
end
-
-
1
def add_includes
-
@serializable.send(:serializable_add_includes, options) do |association, records, opts|
-
add_associations(association, records, opts)
-
end
-
end
-
-
# TODO: This can likely be cleaned up to simple use ActiveSupport::XmlMini.to_tag as well.
-
1
def add_associations(association, records, opts)
-
merged_options = opts.merge(options.slice(:builder, :indent))
-
merged_options[:skip_instruct] = true
-
-
[:skip_types, :dasherize, :camelize].each do |key|
-
merged_options[key] = options[key] if merged_options[key].nil? && !options[key].nil?
-
end
-
-
if records.respond_to?(:to_ary)
-
records = records.to_ary
-
-
tag = ActiveSupport::XmlMini.rename_key(association.to_s, options)
-
type = options[:skip_types] ? { } : { type: "array" }
-
association_name = association.to_s.singularize
-
merged_options[:root] = association_name
-
-
if records.empty?
-
@builder.tag!(tag, type)
-
else
-
@builder.tag!(tag, type) do
-
records.each do |record|
-
if options[:skip_types]
-
record_type = {}
-
else
-
record_class = (record.class.to_s.underscore == association_name) ? nil : record.class.name
-
record_type = { type: record_class }
-
end
-
-
record.to_xml merged_options.merge(record_type)
-
end
-
end
-
end
-
else
-
merged_options[:root] = association.to_s
-
-
unless records.class.to_s.underscore == association.to_s
-
merged_options[:type] = records.class.name
-
end
-
-
records.to_xml merged_options
-
end
-
end
-
-
1
def add_procs
-
if procs = options.delete(:procs)
-
Array(procs).each do |proc|
-
if proc.arity == 1
-
proc.call(options)
-
else
-
proc.call(options, @serializable)
-
end
-
end
-
end
-
end
-
end
-
-
# Returns XML representing the model. Configuration can be
-
# passed through +options+.
-
#
-
# Without any +options+, the returned XML string will include all the
-
# model's attributes.
-
#
-
# user = User.find(1)
-
# user.to_xml
-
#
-
# <?xml version="1.0" encoding="UTF-8"?>
-
# <user>
-
# <id type="integer">1</id>
-
# <name>David</name>
-
# <age type="integer">16</age>
-
# <created-at type="dateTime">2011-01-30T22:29:23Z</created-at>
-
# </user>
-
#
-
# The <tt>:only</tt> and <tt>:except</tt> options can be used to limit the
-
# attributes included, and work similar to the +attributes+ method.
-
#
-
# To include the result of some method calls on the model use <tt>:methods</tt>.
-
#
-
# To include associations use <tt>:include</tt>.
-
#
-
# For further documentation, see <tt>ActiveRecord::Serialization#to_xml</tt>
-
1
def to_xml(options = {}, &block)
-
Serializer.new(self, options).serialize(&block)
-
end
-
-
# Sets the model +attributes+ from an XML string. Returns +self+.
-
#
-
# class Person
-
# include ActiveModel::Serializers::Xml
-
#
-
# attr_accessor :name, :age, :awesome
-
#
-
# def attributes=(hash)
-
# hash.each do |key, value|
-
# instance_variable_set("@#{key}", value)
-
# end
-
# end
-
#
-
# def attributes
-
# instance_values
-
# end
-
# end
-
#
-
# xml = { name: 'bob', age: 22, awesome:true }.to_xml
-
# person = Person.new
-
# person.from_xml(xml) # => #<Person:0x007fec5e3b3c40 @age=22, @awesome=true, @name="bob">
-
# person.name # => "bob"
-
# person.age # => 22
-
# person.awesome # => true
-
1
def from_xml(xml)
-
self.attributes = Hash.from_xml(xml).values.first
-
self
-
end
-
end
-
end
-
end
-
1
module ActiveModel
-
-
# == Active \Model \Translation
-
#
-
# Provides integration between your object and the Rails internationalization
-
# (i18n) framework.
-
#
-
# A minimal implementation could be:
-
#
-
# class TranslatedPerson
-
# extend ActiveModel::Translation
-
# end
-
#
-
# TranslatedPerson.human_attribute_name('my_attribute')
-
# # => "My attribute"
-
#
-
# This also provides the required class methods for hooking into the
-
# Rails internationalization API, including being able to define a
-
# class based +i18n_scope+ and +lookup_ancestors+ to find translations in
-
# parent classes.
-
1
module Translation
-
1
include ActiveModel::Naming
-
-
# Returns the +i18n_scope+ for the class. Overwrite if you want custom lookup.
-
1
def i18n_scope
-
:activemodel
-
end
-
-
# When localizing a string, it goes through the lookup returned by this
-
# method, which is used in ActiveModel::Name#human,
-
# ActiveModel::Errors#full_messages and
-
# ActiveModel::Translation#human_attribute_name.
-
1
def lookup_ancestors
-
self.ancestors.select { |x| x.respond_to?(:model_name) }
-
end
-
-
# Transforms attribute names into a more human format, such as "First name"
-
# instead of "first_name".
-
#
-
# Person.human_attribute_name("first_name") # => "First name"
-
#
-
# Specify +options+ with additional translating options.
-
1
def human_attribute_name(attribute, options = {})
-
options = { count: 1 }.merge!(options)
-
parts = attribute.to_s.split(".")
-
attribute = parts.pop
-
namespace = parts.join("/") unless parts.empty?
-
attributes_scope = "#{self.i18n_scope}.attributes"
-
-
if namespace
-
defaults = lookup_ancestors.map do |klass|
-
:"#{attributes_scope}.#{klass.model_name.i18n_key}/#{namespace}.#{attribute}"
-
end
-
defaults << :"#{attributes_scope}.#{namespace}.#{attribute}"
-
else
-
defaults = lookup_ancestors.map do |klass|
-
:"#{attributes_scope}.#{klass.model_name.i18n_key}.#{attribute}"
-
end
-
end
-
-
defaults << :"attributes.#{attribute}"
-
defaults << options.delete(:default) if options[:default]
-
defaults << attribute.humanize
-
-
options[:default] = defaults
-
I18n.translate(defaults.shift, options)
-
end
-
end
-
end
-
1
require 'active_support/core_ext/array/extract_options'
-
1
require 'active_support/core_ext/hash/keys'
-
1
require 'active_support/core_ext/hash/except'
-
-
1
module ActiveModel
-
-
# == Active \Model \Validations
-
#
-
# Provides a full validation framework to your objects.
-
#
-
# A minimal implementation could be:
-
#
-
# class Person
-
# include ActiveModel::Validations
-
#
-
# attr_accessor :first_name, :last_name
-
#
-
# validates_each :first_name, :last_name do |record, attr, value|
-
# record.errors.add attr, 'starts with z.' if value.to_s[0] == ?z
-
# end
-
# end
-
#
-
# Which provides you with the full standard validation stack that you
-
# know from Active Record:
-
#
-
# person = Person.new
-
# person.valid? # => true
-
# person.invalid? # => false
-
#
-
# person.first_name = 'zoolander'
-
# person.valid? # => false
-
# person.invalid? # => true
-
# person.errors.messages # => {first_name:["starts with z."]}
-
#
-
# Note that <tt>ActiveModel::Validations</tt> automatically adds an +errors+
-
# method to your instances initialized with a new <tt>ActiveModel::Errors</tt>
-
# object, so there is no need for you to do this manually.
-
1
module Validations
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
extend ActiveModel::Callbacks
-
1
extend ActiveModel::Translation
-
-
1
extend HelperMethods
-
1
include HelperMethods
-
-
1
attr_accessor :validation_context
-
1
define_callbacks :validate, scope: :name
-
-
1
class_attribute :_validators
-
4
self._validators = Hash.new { |h,k| h[k] = [] }
-
end
-
-
1
module ClassMethods
-
# Validates each attribute against a block.
-
#
-
# class Person
-
# include ActiveModel::Validations
-
#
-
# attr_accessor :first_name, :last_name
-
#
-
# validates_each :first_name, :last_name, allow_blank: true do |record, attr, value|
-
# record.errors.add attr, 'starts with z.' if value.to_s[0] == ?z
-
# end
-
# end
-
#
-
# Options:
-
# * <tt>:on</tt> - Specifies the contexts where this validation is active.
-
# You can pass a symbol or an array of symbols.
-
# (e.g. <tt>on: :create</tt> or <tt>on: :custom_validation_context</tt> or
-
# <tt>on: [:create, :custom_validation_context]</tt>)
-
# * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+.
-
# * <tt>:allow_blank</tt> - Skip validation if attribute is blank.
-
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine
-
# if the validation should occur (e.g. <tt>if: :allow_validation</tt>,
-
# or <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>). The method,
-
# proc or string should return or evaluate to a +true+ or +false+ value.
-
# * <tt>:unless</tt> - Specifies a method, proc or string to call to
-
# determine if the validation should not occur (e.g. <tt>unless: :skip_validation</tt>,
-
# or <tt>unless: Proc.new { |user| user.signup_step <= 2 }</tt>). The
-
# method, proc or string should return or evaluate to a +true+ or +false+
-
# value.
-
1
def validates_each(*attr_names, &block)
-
validates_with BlockValidator, _merge_attributes(attr_names), &block
-
end
-
-
# Adds a validation method or block to the class. This is useful when
-
# overriding the +validate+ instance method becomes too unwieldy and
-
# you're looking for more descriptive declaration of your validations.
-
#
-
# This can be done with a symbol pointing to a method:
-
#
-
# class Comment
-
# include ActiveModel::Validations
-
#
-
# validate :must_be_friends
-
#
-
# def must_be_friends
-
# errors.add(:base, 'Must be friends to leave a comment') unless commenter.friend_of?(commentee)
-
# end
-
# end
-
#
-
# With a block which is passed with the current record to be validated:
-
#
-
# class Comment
-
# include ActiveModel::Validations
-
#
-
# validate do |comment|
-
# comment.must_be_friends
-
# end
-
#
-
# def must_be_friends
-
# errors.add(:base, 'Must be friends to leave a comment') unless commenter.friend_of?(commentee)
-
# end
-
# end
-
#
-
# Or with a block where self points to the current record to be validated:
-
#
-
# class Comment
-
# include ActiveModel::Validations
-
#
-
# validate do
-
# errors.add(:base, 'Must be friends to leave a comment') unless commenter.friend_of?(commentee)
-
# end
-
# end
-
#
-
# Options:
-
# * <tt>:on</tt> - Specifies the contexts where this validation is active.
-
# You can pass a symbol or an array of symbols.
-
# (e.g. <tt>on: :create</tt> or <tt>on: :custom_validation_context</tt> or
-
# <tt>on: [:create, :custom_validation_context]</tt>)
-
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine
-
# if the validation should occur (e.g. <tt>if: :allow_validation</tt>,
-
# or <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>). The method,
-
# proc or string should return or evaluate to a +true+ or +false+ value.
-
# * <tt>:unless</tt> - Specifies a method, proc or string to call to
-
# determine if the validation should not occur (e.g. <tt>unless: :skip_validation</tt>,
-
# or <tt>unless: Proc.new { |user| user.signup_step <= 2 }</tt>). The
-
# method, proc or string should return or evaluate to a +true+ or +false+
-
# value.
-
1
def validate(*args, &block)
-
14
options = args.extract_options!
-
14
if options.key?(:on)
-
options = options.dup
-
options[:if] = Array(options[:if])
-
options[:if].unshift lambda { |o|
-
Array(options[:on]).include?(o.validation_context)
-
}
-
end
-
14
args << options
-
14
set_callback(:validate, *args, &block)
-
end
-
-
# List all validators that are being used to validate the model using
-
# +validates_with+ method.
-
#
-
# class Person
-
# include ActiveModel::Validations
-
#
-
# validates_with MyValidator
-
# validates_with OtherValidator, on: :create
-
# validates_with StrictValidator, strict: true
-
# end
-
#
-
# Person.validators
-
# # => [
-
# # #<MyValidator:0x007fbff403e808 @options={}>,
-
# # #<OtherValidator:0x007fbff403d930 @options={on: :create}>,
-
# # #<StrictValidator:0x007fbff3204a30 @options={strict:true}>
-
# # ]
-
1
def validators
-
_validators.values.flatten.uniq
-
end
-
-
# Clears all of the validators and validations.
-
#
-
# Note that this will clear anything that is being used to validate
-
# the model for both the +validates_with+ and +validate+ methods.
-
# It clears the validators that are created with an invocation of
-
# +validates_with+ and the callbacks that are set by an invocation
-
# of +validate+.
-
#
-
# class Person
-
# include ActiveModel::Validations
-
#
-
# validates_with MyValidator
-
# validates_with OtherValidator, on: :create
-
# validates_with StrictValidator, strict: true
-
# validate :cannot_be_robot
-
#
-
# def cannot_be_robot
-
# errors.add(:base, 'A person cannot be a robot') if person_is_robot
-
# end
-
# end
-
#
-
# Person.validators
-
# # => [
-
# # #<MyValidator:0x007fbff403e808 @options={}>,
-
# # #<OtherValidator:0x007fbff403d930 @options={on: :create}>,
-
# # #<StrictValidator:0x007fbff3204a30 @options={strict:true}>
-
# # ]
-
#
-
# If one runs <tt>Person.clear_validators!</tt> and then checks to see what
-
# validators this class has, you would obtain:
-
#
-
# Person.validators # => []
-
#
-
# Also, the callback set by <tt>validate :cannot_be_robot</tt> will be erased
-
# so that:
-
#
-
# Person._validate_callbacks.empty? # => true
-
#
-
1
def clear_validators!
-
reset_callbacks(:validate)
-
_validators.clear
-
end
-
-
# List all validators that are being used to validate a specific attribute.
-
#
-
# class Person
-
# include ActiveModel::Validations
-
#
-
# attr_accessor :name , :age
-
#
-
# validates_presence_of :name
-
# validates_inclusion_of :age, in: 0..99
-
# end
-
#
-
# Person.validators_on(:name)
-
# # => [
-
# # #<ActiveModel::Validations::PresenceValidator:0x007fe604914e60 @attributes=[:name], @options={}>,
-
# # ]
-
1
def validators_on(*attributes)
-
attributes.flat_map do |attribute|
-
_validators[attribute.to_sym]
-
end
-
end
-
-
# Returns +true+ if +attribute+ is an attribute method, +false+ otherwise.
-
#
-
# class Person
-
# include ActiveModel::Validations
-
#
-
# attr_accessor :name
-
# end
-
#
-
# User.attribute_method?(:name) # => true
-
# User.attribute_method?(:age) # => false
-
1
def attribute_method?(attribute)
-
method_defined?(attribute)
-
end
-
-
# Copy validators on inheritance.
-
1
def inherited(base) #:nodoc:
-
5
dup = _validators.dup
-
5
base._validators = dup.each { |k, v| dup[k] = v.dup }
-
5
super
-
end
-
end
-
-
# Clean the +Errors+ object if instance is duped.
-
1
def initialize_dup(other) #:nodoc:
-
@errors = nil
-
super
-
end
-
-
# Returns the +Errors+ object that holds all information about attribute
-
# error messages.
-
#
-
# class Person
-
# include ActiveModel::Validations
-
#
-
# attr_accessor :name
-
# validates_presence_of :name
-
# end
-
#
-
# person = Person.new
-
# person.valid? # => false
-
# person.errors # => #<ActiveModel::Errors:0x007fe603816640 @messages={name:["can't be blank"]}>
-
1
def errors
-
24
@errors ||= Errors.new(self)
-
end
-
-
# Runs all the specified validations and returns +true+ if no errors were
-
# added otherwise +false+.
-
#
-
# class Person
-
# include ActiveModel::Validations
-
#
-
# attr_accessor :name
-
# validates_presence_of :name
-
# end
-
#
-
# person = Person.new
-
# person.name = ''
-
# person.valid? # => false
-
# person.name = 'david'
-
# person.valid? # => true
-
#
-
# Context can optionally be supplied to define which callbacks to test
-
# against (the context is defined on the validations using <tt>:on</tt>).
-
#
-
# class Person
-
# include ActiveModel::Validations
-
#
-
# attr_accessor :name
-
# validates_presence_of :name, on: :new
-
# end
-
#
-
# person = Person.new
-
# person.valid? # => true
-
# person.valid?(:new) # => false
-
1
def valid?(context = nil)
-
8
current_context, self.validation_context = validation_context, context
-
8
errors.clear
-
8
run_validations!
-
ensure
-
8
self.validation_context = current_context
-
end
-
-
# Performs the opposite of <tt>valid?</tt>. Returns +true+ if errors were
-
# added, +false+ otherwise.
-
#
-
# class Person
-
# include ActiveModel::Validations
-
#
-
# attr_accessor :name
-
# validates_presence_of :name
-
# end
-
#
-
# person = Person.new
-
# person.name = ''
-
# person.invalid? # => true
-
# person.name = 'david'
-
# person.invalid? # => false
-
#
-
# Context can optionally be supplied to define which callbacks to test
-
# against (the context is defined on the validations using <tt>:on</tt>).
-
#
-
# class Person
-
# include ActiveModel::Validations
-
#
-
# attr_accessor :name
-
# validates_presence_of :name, on: :new
-
# end
-
#
-
# person = Person.new
-
# person.invalid? # => false
-
# person.invalid?(:new) # => true
-
1
def invalid?(context = nil)
-
!valid?(context)
-
end
-
-
# Hook method defining how an attribute value should be retrieved. By default
-
# this is assumed to be an instance named after the attribute. Override this
-
# method in subclasses should you need to retrieve the value for a given
-
# attribute differently:
-
#
-
# class MyClass
-
# include ActiveModel::Validations
-
#
-
# def initialize(data = {})
-
# @data = data
-
# end
-
#
-
# def read_attribute_for_validation(key)
-
# @data[key]
-
# end
-
# end
-
1
alias :read_attribute_for_validation :send
-
-
1
protected
-
-
1
def run_validations! #:nodoc:
-
8
run_callbacks :validate
-
8
errors.empty?
-
end
-
end
-
end
-
-
14
Dir[File.dirname(__FILE__) + "/validations/*.rb"].each { |file| require file }
-
1
module ActiveModel
-
1
module Validations
-
# == Active Model Absence Validator
-
1
class AbsenceValidator < EachValidator #:nodoc:
-
1
def validate_each(record, attr_name, value)
-
record.errors.add(attr_name, :present, options) if value.present?
-
end
-
end
-
-
1
module HelperMethods
-
# Validates that the specified attributes are blank (as defined by
-
# Object#blank?). Happens by default on save.
-
#
-
# class Person < ActiveRecord::Base
-
# validates_absence_of :first_name
-
# end
-
#
-
# The first_name attribute must be in the object and it must be blank.
-
#
-
# Configuration options:
-
# * <tt>:message</tt> - A custom error message (default is: "must be blank").
-
#
-
# There is also a list of default options supported by every validator:
-
# +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+.
-
# See <tt>ActiveModel::Validation#validates</tt> for more information
-
1
def validates_absence_of(*attr_names)
-
validates_with AbsenceValidator, _merge_attributes(attr_names)
-
end
-
end
-
end
-
end
-
1
module ActiveModel
-
-
1
module Validations
-
1
class AcceptanceValidator < EachValidator # :nodoc:
-
1
def initialize(options)
-
super({ allow_nil: true, accept: "1" }.merge!(options))
-
setup!(options[:class])
-
end
-
-
1
def validate_each(record, attribute, value)
-
unless value == options[:accept]
-
record.errors.add(attribute, :accepted, options.except(:accept, :allow_nil))
-
end
-
end
-
-
1
private
-
1
def setup!(klass)
-
attr_readers = attributes.reject { |name| klass.attribute_method?(name) }
-
attr_writers = attributes.reject { |name| klass.attribute_method?("#{name}=") }
-
klass.send(:attr_reader, *attr_readers)
-
klass.send(:attr_writer, *attr_writers)
-
end
-
end
-
-
1
module HelperMethods
-
# Encapsulates the pattern of wanting to validate the acceptance of a
-
# terms of service check box (or similar agreement).
-
#
-
# class Person < ActiveRecord::Base
-
# validates_acceptance_of :terms_of_service
-
# validates_acceptance_of :eula, message: 'must be abided'
-
# end
-
#
-
# If the database column does not exist, the +terms_of_service+ attribute
-
# is entirely virtual. This check is performed only if +terms_of_service+
-
# is not +nil+ and by default on save.
-
#
-
# Configuration options:
-
# * <tt>:message</tt> - A custom error message (default is: "must be
-
# accepted").
-
# * <tt>:accept</tt> - Specifies value that is considered accepted.
-
# The default value is a string "1", which makes it easy to relate to
-
# an HTML checkbox. This should be set to +true+ if you are validating
-
# a database column, since the attribute is typecast from "1" to +true+
-
# before validation.
-
#
-
# There is also a list of default options supported by every validator:
-
# +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+.
-
# See <tt>ActiveModel::Validation#validates</tt> for more information.
-
1
def validates_acceptance_of(*attr_names)
-
validates_with AcceptanceValidator, _merge_attributes(attr_names)
-
end
-
end
-
end
-
end
-
1
module ActiveModel
-
1
module Validations
-
# == Active \Model \Validation \Callbacks
-
#
-
# Provides an interface for any class to have +before_validation+ and
-
# +after_validation+ callbacks.
-
#
-
# First, include ActiveModel::Validations::Callbacks from the class you are
-
# creating:
-
#
-
# class MyModel
-
# include ActiveModel::Validations::Callbacks
-
#
-
# before_validation :do_stuff_before_validation
-
# after_validation :do_stuff_after_validation
-
# end
-
#
-
# Like other <tt>before_*</tt> callbacks if +before_validation+ returns
-
# +false+ then <tt>valid?</tt> will not be called.
-
1
module Callbacks
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
include ActiveSupport::Callbacks
-
1
define_callbacks :validation,
-
terminator: ->(_,result) { result == false },
-
skip_after_callbacks_if_terminated: true,
-
scope: [:kind, :name]
-
end
-
-
1
module ClassMethods
-
# Defines a callback that will get called right before validation
-
# happens.
-
#
-
# class Person
-
# include ActiveModel::Validations
-
# include ActiveModel::Validations::Callbacks
-
#
-
# attr_accessor :name
-
#
-
# validates_length_of :name, maximum: 6
-
#
-
# before_validation :remove_whitespaces
-
#
-
# private
-
#
-
# def remove_whitespaces
-
# name.strip!
-
# end
-
# end
-
#
-
# person = Person.new
-
# person.name = ' bob '
-
# person.valid? # => true
-
# person.name # => "bob"
-
1
def before_validation(*args, &block)
-
options = args.last
-
if options.is_a?(Hash) && options[:on]
-
options[:if] = Array(options[:if])
-
options[:on] = Array(options[:on])
-
options[:if].unshift lambda { |o|
-
options[:on].include? o.validation_context
-
}
-
end
-
set_callback(:validation, :before, *args, &block)
-
end
-
-
# Defines a callback that will get called right after validation
-
# happens.
-
#
-
# class Person
-
# include ActiveModel::Validations
-
# include ActiveModel::Validations::Callbacks
-
#
-
# attr_accessor :name, :status
-
#
-
# validates_presence_of :name
-
#
-
# after_validation :set_status
-
#
-
# private
-
#
-
# def set_status
-
# self.status = errors.empty?
-
# end
-
# end
-
#
-
# person = Person.new
-
# person.name = ''
-
# person.valid? # => false
-
# person.status # => false
-
# person.name = 'bob'
-
# person.valid? # => true
-
# person.status # => true
-
1
def after_validation(*args, &block)
-
options = args.extract_options!
-
options[:prepend] = true
-
options[:if] = Array(options[:if])
-
if options[:on]
-
options[:on] = Array(options[:on])
-
options[:if].unshift("#{options[:on]}.include? self.validation_context")
-
end
-
set_callback(:validation, :after, *(args << options), &block)
-
end
-
end
-
-
1
protected
-
-
# Overwrite run validations to include callbacks.
-
1
def run_validations! #:nodoc:
-
16
run_callbacks(:validation) { super }
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/range'
-
-
1
module ActiveModel
-
1
module Validations
-
1
module Clusivity #:nodoc:
-
1
ERROR_MESSAGE = "An object with the method #include? or a proc, lambda or symbol is required, " \
-
"and must be supplied as the :in (or :within) option of the configuration hash"
-
-
1
def check_validity!
-
unless delimiter.respond_to?(:include?) || delimiter.respond_to?(:call) || delimiter.respond_to?(:to_sym)
-
raise ArgumentError, ERROR_MESSAGE
-
end
-
end
-
-
1
private
-
-
1
def include?(record, value)
-
members = if delimiter.respond_to?(:call)
-
delimiter.call(record)
-
elsif delimiter.respond_to?(:to_sym)
-
record.send(delimiter)
-
else
-
delimiter
-
end
-
-
members.send(inclusion_method(members), value)
-
end
-
-
1
def delimiter
-
@delimiter ||= options[:in] || options[:within]
-
end
-
-
# In Ruby 1.9 <tt>Range#include?</tt> on non-number-or-time-ish ranges checks all
-
# possible values in the range for equality, which is slower but more accurate.
-
# <tt>Range#cover?</tt> uses the previous logic of comparing a value with the range
-
# endpoints, which is fast but is only accurate on Numeric, Time, or DateTime ranges.
-
1
def inclusion_method(enumerable)
-
if enumerable.is_a? Range
-
case enumerable.first
-
when Numeric, Time, DateTime
-
:cover?
-
else
-
:include?
-
end
-
else
-
:include?
-
end
-
end
-
end
-
end
-
end
-
1
module ActiveModel
-
-
1
module Validations
-
1
class ConfirmationValidator < EachValidator # :nodoc:
-
1
def initialize(options)
-
super
-
setup!(options[:class])
-
end
-
-
1
def validate_each(record, attribute, value)
-
if (confirmed = record.send("#{attribute}_confirmation")) && (value != confirmed)
-
human_attribute_name = record.class.human_attribute_name(attribute)
-
record.errors.add(:"#{attribute}_confirmation", :confirmation, options.merge(attribute: human_attribute_name))
-
end
-
end
-
-
1
private
-
1
def setup!(klass)
-
klass.send(:attr_reader, *attributes.map do |attribute|
-
:"#{attribute}_confirmation" unless klass.method_defined?(:"#{attribute}_confirmation")
-
end.compact)
-
-
klass.send(:attr_writer, *attributes.map do |attribute|
-
:"#{attribute}_confirmation" unless klass.method_defined?(:"#{attribute}_confirmation=")
-
end.compact)
-
end
-
end
-
-
1
module HelperMethods
-
# Encapsulates the pattern of wanting to validate a password or email
-
# address field with a confirmation.
-
#
-
# Model:
-
# class Person < ActiveRecord::Base
-
# validates_confirmation_of :user_name, :password
-
# validates_confirmation_of :email_address,
-
# message: 'should match confirmation'
-
# end
-
#
-
# View:
-
# <%= password_field "person", "password" %>
-
# <%= password_field "person", "password_confirmation" %>
-
#
-
# The added +password_confirmation+ attribute is virtual; it exists only
-
# as an in-memory attribute for validating the password. To achieve this,
-
# the validation adds accessors to the model for the confirmation
-
# attribute.
-
#
-
# NOTE: This check is performed only if +password_confirmation+ is not
-
# +nil+. To require confirmation, make sure to add a presence check for
-
# the confirmation attribute:
-
#
-
# validates_presence_of :password_confirmation, if: :password_changed?
-
#
-
# Configuration options:
-
# * <tt>:message</tt> - A custom error message (default is: "doesn't match
-
# confirmation").
-
#
-
# There is also a list of default options supported by every validator:
-
# +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+.
-
# See <tt>ActiveModel::Validation#validates</tt> for more information
-
1
def validates_confirmation_of(*attr_names)
-
validates_with ConfirmationValidator, _merge_attributes(attr_names)
-
end
-
end
-
end
-
end
-
1
require "active_model/validations/clusivity"
-
-
1
module ActiveModel
-
-
1
module Validations
-
1
class ExclusionValidator < EachValidator # :nodoc:
-
1
include Clusivity
-
-
1
def validate_each(record, attribute, value)
-
if include?(record, value)
-
record.errors.add(attribute, :exclusion, options.except(:in, :within).merge!(value: value))
-
end
-
end
-
end
-
-
1
module HelperMethods
-
# Validates that the value of the specified attribute is not in a
-
# particular enumerable object.
-
#
-
# class Person < ActiveRecord::Base
-
# validates_exclusion_of :username, in: %w( admin superuser ), message: "You don't belong here"
-
# validates_exclusion_of :age, in: 30..60, message: 'This site is only for under 30 and over 60'
-
# validates_exclusion_of :format, in: %w( mov avi ), message: "extension %{value} is not allowed"
-
# validates_exclusion_of :password, in: ->(person) { [person.username, person.first_name] },
-
# message: 'should not be the same as your username or first name'
-
# validates_exclusion_of :karma, in: :reserved_karmas
-
# end
-
#
-
# Configuration options:
-
# * <tt>:in</tt> - An enumerable object of items that the value shouldn't
-
# be part of. This can be supplied as a proc, lambda or symbol which returns an
-
# enumerable. If the enumerable is a range the test is performed with
-
# * <tt>:within</tt> - A synonym(or alias) for <tt>:in</tt>
-
# <tt>Range#cover?</tt>, otherwise with <tt>include?</tt>.
-
# * <tt>:message</tt> - Specifies a custom error message (default is: "is
-
# reserved").
-
#
-
# There is also a list of default options supported by every validator:
-
# +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+.
-
# See <tt>ActiveModel::Validation#validates</tt> for more information
-
1
def validates_exclusion_of(*attr_names)
-
validates_with ExclusionValidator, _merge_attributes(attr_names)
-
end
-
end
-
end
-
end
-
1
module ActiveModel
-
-
1
module Validations
-
1
class FormatValidator < EachValidator # :nodoc:
-
1
def validate_each(record, attribute, value)
-
if options[:with]
-
regexp = option_call(record, :with)
-
record_error(record, attribute, :with, value) if value.to_s !~ regexp
-
elsif options[:without]
-
regexp = option_call(record, :without)
-
record_error(record, attribute, :without, value) if value.to_s =~ regexp
-
end
-
end
-
-
1
def check_validity!
-
unless options.include?(:with) ^ options.include?(:without) # ^ == xor, or "exclusive or"
-
raise ArgumentError, "Either :with or :without must be supplied (but not both)"
-
end
-
-
check_options_validity :with
-
check_options_validity :without
-
end
-
-
1
private
-
-
1
def option_call(record, name)
-
option = options[name]
-
option.respond_to?(:call) ? option.call(record) : option
-
end
-
-
1
def record_error(record, attribute, name, value)
-
record.errors.add(attribute, :invalid, options.except(name).merge!(value: value))
-
end
-
-
1
def check_options_validity(name)
-
if option = options[name]
-
if option.is_a?(Regexp)
-
if options[:multiline] != true && regexp_using_multiline_anchors?(option)
-
raise ArgumentError, "The provided regular expression is using multiline anchors (^ or $), " \
-
"which may present a security risk. Did you mean to use \\A and \\z, or forgot to add the " \
-
":multiline => true option?"
-
end
-
elsif !option.respond_to?(:call)
-
raise ArgumentError, "A regular expression or a proc or lambda must be supplied as :#{name}"
-
end
-
end
-
end
-
-
1
def regexp_using_multiline_anchors?(regexp)
-
source = regexp.source
-
source.start_with?("^") || (source.end_with?("$") && !source.end_with?("\\$"))
-
end
-
end
-
-
1
module HelperMethods
-
# Validates whether the value of the specified attribute is of the correct
-
# form, going by the regular expression provided.You can require that the
-
# attribute matches the regular expression:
-
#
-
# class Person < ActiveRecord::Base
-
# validates_format_of :email, with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, on: :create
-
# end
-
#
-
# Alternatively, you can require that the specified attribute does _not_
-
# match the regular expression:
-
#
-
# class Person < ActiveRecord::Base
-
# validates_format_of :email, without: /NOSPAM/
-
# end
-
#
-
# You can also provide a proc or lambda which will determine the regular
-
# expression that will be used to validate the attribute.
-
#
-
# class Person < ActiveRecord::Base
-
# # Admin can have number as a first letter in their screen name
-
# validates_format_of :screen_name,
-
# with: ->(person) { person.admin? ? /\A[a-z0-9][a-z0-9_\-]*\z/i : /\A[a-z][a-z0-9_\-]*\z/i }
-
# end
-
#
-
# Note: use <tt>\A</tt> and <tt>\Z</tt> to match the start and end of the
-
# string, <tt>^</tt> and <tt>$</tt> match the start/end of a line.
-
#
-
# Due to frequent misuse of <tt>^</tt> and <tt>$</tt>, you need to pass
-
# the <tt>multiline: true</tt> option in case you use any of these two
-
# anchors in the provided regular expression. In most cases, you should be
-
# using <tt>\A</tt> and <tt>\z</tt>.
-
#
-
# You must pass either <tt>:with</tt> or <tt>:without</tt> as an option.
-
# In addition, both must be a regular expression or a proc or lambda, or
-
# else an exception will be raised.
-
#
-
# Configuration options:
-
# * <tt>:message</tt> - A custom error message (default is: "is invalid").
-
# * <tt>:with</tt> - Regular expression that if the attribute matches will
-
# result in a successful validation. This can be provided as a proc or
-
# lambda returning regular expression which will be called at runtime.
-
# * <tt>:without</tt> - Regular expression that if the attribute does not
-
# match will result in a successful validation. This can be provided as
-
# a proc or lambda returning regular expression which will be called at
-
# runtime.
-
# * <tt>:multiline</tt> - Set to true if your regular expression contains
-
# anchors that match the beginning or end of lines as opposed to the
-
# beginning or end of the string. These anchors are <tt>^</tt> and <tt>$</tt>.
-
#
-
# There is also a list of default options supported by every validator:
-
# +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+.
-
# See <tt>ActiveModel::Validation#validates</tt> for more information
-
1
def validates_format_of(*attr_names)
-
validates_with FormatValidator, _merge_attributes(attr_names)
-
end
-
end
-
end
-
end
-
1
require "active_model/validations/clusivity"
-
-
1
module ActiveModel
-
-
1
module Validations
-
1
class InclusionValidator < EachValidator # :nodoc:
-
1
include Clusivity
-
-
1
def validate_each(record, attribute, value)
-
unless include?(record, value)
-
record.errors.add(attribute, :inclusion, options.except(:in, :within).merge!(value: value))
-
end
-
end
-
end
-
-
1
module HelperMethods
-
# Validates whether the value of the specified attribute is available in a
-
# particular enumerable object.
-
#
-
# class Person < ActiveRecord::Base
-
# validates_inclusion_of :gender, in: %w( m f )
-
# validates_inclusion_of :age, in: 0..99
-
# validates_inclusion_of :format, in: %w( jpg gif png ), message: "extension %{value} is not included in the list"
-
# validates_inclusion_of :states, in: ->(person) { STATES[person.country] }
-
# validates_inclusion_of :karma, in: :available_karmas
-
# end
-
#
-
# Configuration options:
-
# * <tt>:in</tt> - An enumerable object of available items. This can be
-
# supplied as a proc, lambda or symbol which returns an enumerable. If the
-
# enumerable is a numerical range the test is performed with <tt>Range#cover?</tt>,
-
# otherwise with <tt>include?</tt>. When using a proc or lambda the instance
-
# under validation is passed as an argument.
-
# * <tt>:within</tt> - A synonym(or alias) for <tt>:in</tt>
-
# * <tt>:message</tt> - Specifies a custom error message (default is: "is
-
# not included in the list").
-
#
-
# There is also a list of default options supported by every validator:
-
# +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+.
-
# See <tt>ActiveModel::Validation#validates</tt> for more information
-
1
def validates_inclusion_of(*attr_names)
-
validates_with InclusionValidator, _merge_attributes(attr_names)
-
end
-
end
-
end
-
end
-
1
module ActiveModel
-
-
# == Active \Model Length Validator
-
1
module Validations
-
1
class LengthValidator < EachValidator # :nodoc:
-
1
MESSAGES = { is: :wrong_length, minimum: :too_short, maximum: :too_long }.freeze
-
1
CHECKS = { is: :==, minimum: :>=, maximum: :<= }.freeze
-
-
1
RESERVED_OPTIONS = [:minimum, :maximum, :within, :is, :tokenizer, :too_short, :too_long]
-
-
1
def initialize(options)
-
if range = (options.delete(:in) || options.delete(:within))
-
raise ArgumentError, ":in and :within must be a Range" unless range.is_a?(Range)
-
options[:minimum], options[:maximum] = range.min, range.max
-
end
-
-
if options[:allow_blank] == false && options[:minimum].nil? && options[:is].nil?
-
options[:minimum] = 1
-
end
-
-
super
-
end
-
-
1
def check_validity!
-
keys = CHECKS.keys & options.keys
-
-
if keys.empty?
-
raise ArgumentError, 'Range unspecified. Specify the :in, :within, :maximum, :minimum, or :is option.'
-
end
-
-
keys.each do |key|
-
value = options[key]
-
-
unless (value.is_a?(Integer) && value >= 0) || value == Float::INFINITY
-
raise ArgumentError, ":#{key} must be a nonnegative Integer or Infinity"
-
end
-
end
-
end
-
-
1
def validate_each(record, attribute, value)
-
value = tokenize(value)
-
value_length = value.respond_to?(:length) ? value.length : value.to_s.length
-
errors_options = options.except(*RESERVED_OPTIONS)
-
-
CHECKS.each do |key, validity_check|
-
next unless check_value = options[key]
-
-
if !value.nil? || skip_nil_check?(key)
-
next if value_length.send(validity_check, check_value)
-
end
-
-
errors_options[:count] = check_value
-
-
default_message = options[MESSAGES[key]]
-
errors_options[:message] ||= default_message if default_message
-
-
record.errors.add(attribute, MESSAGES[key], errors_options)
-
end
-
end
-
-
1
private
-
-
1
def tokenize(value)
-
if options[:tokenizer] && value.kind_of?(String)
-
options[:tokenizer].call(value)
-
end || value
-
end
-
-
1
def skip_nil_check?(key)
-
key == :maximum && options[:allow_nil].nil? && options[:allow_blank].nil?
-
end
-
end
-
-
1
module HelperMethods
-
-
# Validates that the specified attribute matches the length restrictions
-
# supplied. Only one option can be used at a time:
-
#
-
# class Person < ActiveRecord::Base
-
# validates_length_of :first_name, maximum: 30
-
# validates_length_of :last_name, maximum: 30, message: "less than 30 if you don't mind"
-
# validates_length_of :fax, in: 7..32, allow_nil: true
-
# validates_length_of :phone, in: 7..32, allow_blank: true
-
# validates_length_of :user_name, within: 6..20, too_long: 'pick a shorter name', too_short: 'pick a longer name'
-
# validates_length_of :zip_code, minimum: 5, too_short: 'please enter at least 5 characters'
-
# validates_length_of :smurf_leader, is: 4, message: "papa is spelled with 4 characters... don't play me."
-
# validates_length_of :essay, minimum: 100, too_short: 'Your essay must be at least 100 words.',
-
# tokenizer: ->(str) { str.scan(/\w+/) }
-
# end
-
#
-
# Configuration options:
-
# * <tt>:minimum</tt> - The minimum size of the attribute.
-
# * <tt>:maximum</tt> - The maximum size of the attribute. Allows +nil+ by
-
# default if not used with :minimum.
-
# * <tt>:is</tt> - The exact size of the attribute.
-
# * <tt>:within</tt> - A range specifying the minimum and maximum size of
-
# the attribute.
-
# * <tt>:in</tt> - A synonym (or alias) for <tt>:within</tt>.
-
# * <tt>:allow_nil</tt> - Attribute may be +nil+; skip validation.
-
# * <tt>:allow_blank</tt> - Attribute may be blank; skip validation.
-
# * <tt>:too_long</tt> - The error message if the attribute goes over the
-
# maximum (default is: "is too long (maximum is %{count} characters)").
-
# * <tt>:too_short</tt> - The error message if the attribute goes under the
-
# minimum (default is: "is too short (min is %{count} characters)").
-
# * <tt>:wrong_length</tt> - The error message if using the <tt>:is</tt>
-
# method and the attribute is the wrong size (default is: "is the wrong
-
# length (should be %{count} characters)").
-
# * <tt>:message</tt> - The error message to use for a <tt>:minimum</tt>,
-
# <tt>:maximum</tt>, or <tt>:is</tt> violation. An alias of the appropriate
-
# <tt>too_long</tt>/<tt>too_short</tt>/<tt>wrong_length</tt> message.
-
# * <tt>:tokenizer</tt> - Specifies how to split up the attribute string.
-
# (e.g. <tt>tokenizer: ->(str) { str.scan(/\w+/) }</tt> to count words
-
# as in above example). Defaults to <tt>->(value) { value.split(//) }</tt>
-
# which counts individual characters.
-
#
-
# There is also a list of default options supported by every validator:
-
# +:if+, +:unless+, +:on+ and +:strict+.
-
# See <tt>ActiveModel::Validation#validates</tt> for more information
-
1
def validates_length_of(*attr_names)
-
validates_with LengthValidator, _merge_attributes(attr_names)
-
end
-
-
1
alias_method :validates_size_of, :validates_length_of
-
end
-
end
-
end
-
1
module ActiveModel
-
-
1
module Validations
-
1
class NumericalityValidator < EachValidator # :nodoc:
-
1
CHECKS = { greater_than: :>, greater_than_or_equal_to: :>=,
-
equal_to: :==, less_than: :<, less_than_or_equal_to: :<=,
-
odd: :odd?, even: :even?, other_than: :!= }.freeze
-
-
1
RESERVED_OPTIONS = CHECKS.keys + [:only_integer]
-
-
1
def check_validity!
-
keys = CHECKS.keys - [:odd, :even]
-
options.slice(*keys).each do |option, value|
-
unless value.is_a?(Numeric) || value.is_a?(Proc) || value.is_a?(Symbol)
-
raise ArgumentError, ":#{option} must be a number, a symbol or a proc"
-
end
-
end
-
end
-
-
1
def validate_each(record, attr_name, value)
-
before_type_cast = :"#{attr_name}_before_type_cast"
-
-
raw_value = record.send(before_type_cast) if record.respond_to?(before_type_cast)
-
raw_value ||= value
-
-
return if options[:allow_nil] && raw_value.nil?
-
-
unless value = parse_raw_value_as_a_number(raw_value)
-
record.errors.add(attr_name, :not_a_number, filtered_options(raw_value))
-
return
-
end
-
-
if options[:only_integer]
-
unless value = parse_raw_value_as_an_integer(raw_value)
-
record.errors.add(attr_name, :not_an_integer, filtered_options(raw_value))
-
return
-
end
-
end
-
-
options.slice(*CHECKS.keys).each do |option, option_value|
-
case option
-
when :odd, :even
-
unless value.to_i.send(CHECKS[option])
-
record.errors.add(attr_name, option, filtered_options(value))
-
end
-
else
-
case option_value
-
when Proc
-
option_value = option_value.call(record)
-
when Symbol
-
option_value = record.send(option_value)
-
end
-
-
unless value.send(CHECKS[option], option_value)
-
record.errors.add(attr_name, option, filtered_options(value).merge!(count: option_value))
-
end
-
end
-
end
-
end
-
-
1
protected
-
-
1
def parse_raw_value_as_a_number(raw_value)
-
Kernel.Float(raw_value) if raw_value !~ /\A0[xX]/
-
rescue ArgumentError, TypeError
-
nil
-
end
-
-
1
def parse_raw_value_as_an_integer(raw_value)
-
raw_value.to_i if raw_value.to_s =~ /\A[+-]?\d+\Z/
-
end
-
-
1
def filtered_options(value)
-
filtered = options.except(*RESERVED_OPTIONS)
-
filtered[:value] = value
-
filtered
-
end
-
end
-
-
1
module HelperMethods
-
# Validates whether the value of the specified attribute is numeric by
-
# trying to convert it to a float with Kernel.Float (if <tt>only_integer</tt>
-
# is +false+) or applying it to the regular expression <tt>/\A[\+\-]?\d+\Z/</tt>
-
# (if <tt>only_integer</tt> is set to +true+).
-
#
-
# class Person < ActiveRecord::Base
-
# validates_numericality_of :value, on: :create
-
# end
-
#
-
# Configuration options:
-
# * <tt>:message</tt> - A custom error message (default is: "is not a number").
-
# * <tt>:only_integer</tt> - Specifies whether the value has to be an
-
# integer, e.g. an integral value (default is +false+).
-
# * <tt>:allow_nil</tt> - Skip validation if attribute is +nil+ (default is
-
# +false+). Notice that for fixnum and float columns empty strings are
-
# converted to +nil+.
-
# * <tt>:greater_than</tt> - Specifies the value must be greater than the
-
# supplied value.
-
# * <tt>:greater_than_or_equal_to</tt> - Specifies the value must be
-
# greater than or equal the supplied value.
-
# * <tt>:equal_to</tt> - Specifies the value must be equal to the supplied
-
# value.
-
# * <tt>:less_than</tt> - Specifies the value must be less than the
-
# supplied value.
-
# * <tt>:less_than_or_equal_to</tt> - Specifies the value must be less
-
# than or equal the supplied value.
-
# * <tt>:other_than</tt> - Specifies the value must be other than the
-
# supplied value.
-
# * <tt>:odd</tt> - Specifies the value must be an odd number.
-
# * <tt>:even</tt> - Specifies the value must be an even number.
-
#
-
# There is also a list of default options supported by every validator:
-
# +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+ .
-
# See <tt>ActiveModel::Validation#validates</tt> for more information
-
#
-
# The following checks can also be supplied with a proc or a symbol which
-
# corresponds to a method:
-
#
-
# * <tt>:greater_than</tt>
-
# * <tt>:greater_than_or_equal_to</tt>
-
# * <tt>:equal_to</tt>
-
# * <tt>:less_than</tt>
-
# * <tt>:less_than_or_equal_to</tt>
-
#
-
# For example:
-
#
-
# class Person < ActiveRecord::Base
-
# validates_numericality_of :width, less_than: ->(person) { person.height }
-
# validates_numericality_of :width, greater_than: :minimum_weight
-
# end
-
1
def validates_numericality_of(*attr_names)
-
validates_with NumericalityValidator, _merge_attributes(attr_names)
-
end
-
end
-
end
-
end
-
-
1
module ActiveModel
-
-
1
module Validations
-
1
class PresenceValidator < EachValidator # :nodoc:
-
1
def validate_each(record, attr_name, value)
-
12
record.errors.add(attr_name, :blank, options) if value.blank?
-
end
-
end
-
-
1
module HelperMethods
-
# Validates that the specified attributes are not blank (as defined by
-
# Object#blank?). Happens by default on save.
-
#
-
# class Person < ActiveRecord::Base
-
# validates_presence_of :first_name
-
# end
-
#
-
# The first_name attribute must be in the object and it cannot be blank.
-
#
-
# If you want to validate the presence of a boolean field (where the real
-
# values are +true+ and +false+), you will want to use
-
# <tt>validates_inclusion_of :field_name, in: [true, false]</tt>.
-
#
-
# This is due to the way Object#blank? handles boolean values:
-
# <tt>false.blank? # => true</tt>.
-
#
-
# Configuration options:
-
# * <tt>:message</tt> - A custom error message (default is: "can't be blank").
-
#
-
# There is also a list of default options supported by every validator:
-
# +:if+, +:unless+, +:on+, +:allow_nil+, +:allow_blank+, and +:strict+.
-
# See <tt>ActiveModel::Validation#validates</tt> for more information
-
1
def validates_presence_of(*attr_names)
-
validates_with PresenceValidator, _merge_attributes(attr_names)
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/hash/slice'
-
-
1
module ActiveModel
-
1
module Validations
-
1
module ClassMethods
-
# This method is a shortcut to all default validators and any custom
-
# validator classes ending in 'Validator'. Note that Rails default
-
# validators can be overridden inside specific classes by creating
-
# custom validator classes in their place such as PresenceValidator.
-
#
-
# Examples of using the default rails validators:
-
#
-
# validates :terms, acceptance: true
-
# validates :password, confirmation: true
-
# validates :username, exclusion: { in: %w(admin superuser) }
-
# validates :email, format: { with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, on: :create }
-
# validates :age, inclusion: { in: 0..9 }
-
# validates :first_name, length: { maximum: 30 }
-
# validates :age, numericality: true
-
# validates :username, presence: true
-
# validates :username, uniqueness: true
-
#
-
# The power of the +validates+ method comes when using custom validators
-
# and default validators in one call for a given attribute.
-
#
-
# class EmailValidator < ActiveModel::EachValidator
-
# def validate_each(record, attribute, value)
-
# record.errors.add attribute, (options[:message] || "is not an email") unless
-
# value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
-
# end
-
# end
-
#
-
# class Person
-
# include ActiveModel::Validations
-
# attr_accessor :name, :email
-
#
-
# validates :name, presence: true, uniqueness: true, length: { maximum: 100 }
-
# validates :email, presence: true, email: true
-
# end
-
#
-
# Validator classes may also exist within the class being validated
-
# allowing custom modules of validators to be included as needed.
-
#
-
# class Film
-
# include ActiveModel::Validations
-
#
-
# class TitleValidator < ActiveModel::EachValidator
-
# def validate_each(record, attribute, value)
-
# record.errors.add attribute, "must start with 'the'" unless value =~ /\Athe/i
-
# end
-
# end
-
#
-
# validates :name, title: true
-
# end
-
#
-
# Additionally validator classes may be in another namespace and still
-
# used within any class.
-
#
-
# validates :name, :'film/title' => true
-
#
-
# The validators hash can also handle regular expressions, ranges, arrays
-
# and strings in shortcut form.
-
#
-
# validates :email, format: /@/
-
# validates :gender, inclusion: %w(male female)
-
# validates :password, length: 6..20
-
#
-
# When using shortcut form, ranges and arrays are passed to your
-
# validator's initializer as <tt>options[:in]</tt> while other types
-
# including regular expressions and strings are passed as <tt>options[:with]</tt>.
-
#
-
# There is also a list of options that could be used along with validators:
-
#
-
# * <tt>:on</tt> - Specifies when this validation is active. Runs in all
-
# validation contexts by default (+nil+), other options are <tt>:create</tt>
-
# and <tt>:update</tt>.
-
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine
-
# if the validation should occur (e.g. <tt>if: :allow_validation</tt>,
-
# or <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>). The method,
-
# proc or string should return or evaluate to a +true+ or +false+ value.
-
# * <tt>:unless</tt> - Specifies a method, proc or string to call to determine
-
# if the validation should not occur (e.g. <tt>unless: :skip_validation</tt>,
-
# or <tt>unless: Proc.new { |user| user.signup_step <= 2 }</tt>). The
-
# method, proc or string should return or evaluate to a +true+ or
-
# +false+ value.
-
# * <tt>:allow_nil</tt> - Skip validation if the attribute is +nil+.
-
# * <tt>:allow_blank</tt> - Skip validation if the attribute is blank.
-
# * <tt>:strict</tt> - If the <tt>:strict</tt> option is set to true
-
# will raise ActiveModel::StrictValidationFailed instead of adding the error.
-
# <tt>:strict</tt> option can also be set to any other exception.
-
#
-
# Example:
-
#
-
# validates :password, presence: true, confirmation: true, if: :password_required?
-
# validates :token, uniqueness: true, strict: TokenGenerationException
-
#
-
#
-
# Finally, the options +:if+, +:unless+, +:on+, +:allow_blank+, +:allow_nil+, +:strict+
-
# and +:message+ can be given to one specific validator, as a hash:
-
#
-
# validates :password, presence: { if: :password_required?, message: 'is forgotten.' }, confirmation: true
-
1
def validates(*attributes)
-
3
defaults = attributes.extract_options!.dup
-
3
validations = defaults.slice!(*_validates_default_keys)
-
-
3
raise ArgumentError, "You need to supply at least one attribute" if attributes.empty?
-
3
raise ArgumentError, "You need to supply at least one validation" if validations.empty?
-
-
3
defaults[:attributes] = attributes
-
-
3
validations.each do |key, options|
-
3
next unless options
-
3
key = "#{key.to_s.camelize}Validator"
-
-
3
begin
-
3
validator = key.include?('::') ? key.constantize : const_get(key)
-
rescue NameError
-
raise ArgumentError, "Unknown validator: '#{key}'"
-
end
-
-
3
validates_with(validator, defaults.merge(_parse_validates_options(options)))
-
end
-
end
-
-
# This method is used to define validations that cannot be corrected by end
-
# users and are considered exceptional. So each validator defined with bang
-
# or <tt>:strict</tt> option set to <tt>true</tt> will always raise
-
# <tt>ActiveModel::StrictValidationFailed</tt> instead of adding error
-
# when validation fails. See <tt>validates</tt> for more information about
-
# the validation itself.
-
#
-
# class Person
-
# include ActiveModel::Validations
-
#
-
# attr_accessor :name
-
# validates! :name, presence: true
-
# end
-
#
-
# person = Person.new
-
# person.name = ''
-
# person.valid?
-
# # => ActiveModel::StrictValidationFailed: Name can't be blank
-
1
def validates!(*attributes)
-
options = attributes.extract_options!
-
options[:strict] = true
-
validates(*(attributes << options))
-
end
-
-
1
protected
-
-
# When creating custom validators, it might be useful to be able to specify
-
# additional default keys. This can be done by overwriting this method.
-
1
def _validates_default_keys # :nodoc:
-
3
[:if, :unless, :on, :allow_blank, :allow_nil , :strict]
-
end
-
-
1
def _parse_validates_options(options) # :nodoc:
-
3
case options
-
when TrueClass
-
3
{}
-
when Hash
-
options
-
when Range, Array
-
{ in: options }
-
else
-
{ with: options }
-
end
-
end
-
end
-
end
-
end
-
1
module ActiveModel
-
1
module Validations
-
1
module HelperMethods
-
1
private
-
1
def _merge_attributes(attr_names)
-
options = attr_names.extract_options!.symbolize_keys
-
attr_names.flatten!
-
options[:attributes] = attr_names
-
options
-
end
-
end
-
-
1
class WithValidator < EachValidator # :nodoc:
-
1
def validate_each(record, attr, val)
-
method_name = options[:with]
-
-
if record.method(method_name).arity == 0
-
record.send method_name
-
else
-
record.send method_name, attr
-
end
-
end
-
end
-
-
1
module ClassMethods
-
# Passes the record off to the class or classes specified and allows them
-
# to add errors based on more complex conditions.
-
#
-
# class Person
-
# include ActiveModel::Validations
-
# validates_with MyValidator
-
# end
-
#
-
# class MyValidator < ActiveModel::Validator
-
# def validate(record)
-
# if some_complex_logic
-
# record.errors.add :base, 'This record is invalid'
-
# end
-
# end
-
#
-
# private
-
# def some_complex_logic
-
# # ...
-
# end
-
# end
-
#
-
# You may also pass it multiple classes, like so:
-
#
-
# class Person
-
# include ActiveModel::Validations
-
# validates_with MyValidator, MyOtherValidator, on: :create
-
# end
-
#
-
# Configuration options:
-
# * <tt>:on</tt> - Specifies when this validation is active
-
# (<tt>:create</tt> or <tt>:update</tt>.
-
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine
-
# if the validation should occur (e.g. <tt>if: :allow_validation</tt>,
-
# or <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>).
-
# The method, proc or string should return or evaluate to a +true+ or
-
# +false+ value.
-
# * <tt>:unless</tt> - Specifies a method, proc or string to call to
-
# determine if the validation should not occur
-
# (e.g. <tt>unless: :skip_validation</tt>, or
-
# <tt>unless: Proc.new { |user| user.signup_step <= 2 }</tt>).
-
# The method, proc or string should return or evaluate to a +true+ or
-
# +false+ value.
-
# * <tt>:strict</tt> - Specifies whether validation should be strict.
-
# See <tt>ActiveModel::Validation#validates!</tt> for more information.
-
#
-
# If you pass any additional configuration options, they will be passed
-
# to the class and available as +options+:
-
#
-
# class Person
-
# include ActiveModel::Validations
-
# validates_with MyValidator, my_custom_key: 'my custom value'
-
# end
-
#
-
# class MyValidator < ActiveModel::Validator
-
# def validate(record)
-
# options[:my_custom_key] # => "my custom value"
-
# end
-
# end
-
1
def validates_with(*args, &block)
-
3
options = args.extract_options!
-
3
options[:class] = self
-
-
3
args.each do |klass|
-
3
validator = klass.new(options, &block)
-
-
3
if validator.respond_to?(:attributes) && !validator.attributes.empty?
-
3
validator.attributes.each do |attribute|
-
3
_validators[attribute.to_sym] << validator
-
end
-
else
-
_validators[nil] << validator
-
end
-
-
3
validate(validator, options)
-
end
-
end
-
end
-
-
# Passes the record off to the class or classes specified and allows them
-
# to add errors based on more complex conditions.
-
#
-
# class Person
-
# include ActiveModel::Validations
-
#
-
# validate :instance_validations
-
#
-
# def instance_validations
-
# validates_with MyValidator
-
# end
-
# end
-
#
-
# Please consult the class method documentation for more information on
-
# creating your own validator.
-
#
-
# You may also pass it multiple classes, like so:
-
#
-
# class Person
-
# include ActiveModel::Validations
-
#
-
# validate :instance_validations, on: :create
-
#
-
# def instance_validations
-
# validates_with MyValidator, MyOtherValidator
-
# end
-
# end
-
#
-
# Standard configuration options (<tt>:on</tt>, <tt>:if</tt> and
-
# <tt>:unless</tt>), which are available on the class version of
-
# +validates_with+, should instead be placed on the +validates+ method
-
# as these are applied and tested in the callback.
-
#
-
# If you pass any additional configuration options, they will be passed
-
# to the class and available as +options+, please refer to the
-
# class version of this method for more information.
-
1
def validates_with(*args, &block)
-
options = args.extract_options!
-
options[:class] = self.class
-
-
args.each do |klass|
-
validator = klass.new(options, &block)
-
validator.validate(self)
-
end
-
end
-
end
-
end
-
1
require "active_support/core_ext/module/anonymous"
-
-
1
module ActiveModel
-
-
# == Active \Model \Validator
-
#
-
# A simple base class that can be used along with
-
# ActiveModel::Validations::ClassMethods.validates_with
-
#
-
# class Person
-
# include ActiveModel::Validations
-
# validates_with MyValidator
-
# end
-
#
-
# class MyValidator < ActiveModel::Validator
-
# def validate(record)
-
# if some_complex_logic
-
# record.errors[:base] = "This record is invalid"
-
# end
-
# end
-
#
-
# private
-
# def some_complex_logic
-
# # ...
-
# end
-
# end
-
#
-
# Any class that inherits from ActiveModel::Validator must implement a method
-
# called +validate+ which accepts a +record+.
-
#
-
# class Person
-
# include ActiveModel::Validations
-
# validates_with MyValidator
-
# end
-
#
-
# class MyValidator < ActiveModel::Validator
-
# def validate(record)
-
# record # => The person instance being validated
-
# options # => Any non-standard options passed to validates_with
-
# end
-
# end
-
#
-
# To cause a validation error, you must add to the +record+'s errors directly
-
# from within the validators message.
-
#
-
# class MyValidator < ActiveModel::Validator
-
# def validate(record)
-
# record.errors.add :base, "This is some custom error message"
-
# record.errors.add :first_name, "This is some complex validation"
-
# # etc...
-
# end
-
# end
-
#
-
# To add behavior to the initialize method, use the following signature:
-
#
-
# class MyValidator < ActiveModel::Validator
-
# def initialize(options)
-
# super
-
# @my_custom_field = options[:field_name] || :first_name
-
# end
-
# end
-
#
-
# Note that the validator is initialized only once for the whole application
-
# life cycle, and not on each validation run.
-
#
-
# The easiest way to add custom validators for validating individual attributes
-
# is with the convenient <tt>ActiveModel::EachValidator</tt>.
-
#
-
# class TitleValidator < ActiveModel::EachValidator
-
# def validate_each(record, attribute, value)
-
# record.errors.add attribute, 'must be Mr., Mrs., or Dr.' unless %w(Mr. Mrs. Dr.).include?(value)
-
# end
-
# end
-
#
-
# This can now be used in combination with the +validates+ method
-
# (see <tt>ActiveModel::Validations::ClassMethods.validates</tt> for more on this).
-
#
-
# class Person
-
# include ActiveModel::Validations
-
# attr_accessor :title
-
#
-
# validates :title, presence: true, title: true
-
# end
-
#
-
# It can be useful to access the class that is using that validator when there are prerequisites such
-
# as an +attr_accessor+ being present. This class is accessible via +options[:class]+ in the constructor.
-
# To setup your validator override the constructor.
-
#
-
# class MyValidator < ActiveModel::Validator
-
# def initialize(options={})
-
# super
-
# options[:class].send :attr_accessor, :custom_attribute
-
# end
-
# end
-
1
class Validator
-
1
attr_reader :options
-
-
# Returns the kind of the validator.
-
#
-
# PresenceValidator.kind # => :presence
-
# UniquenessValidator.kind # => :uniqueness
-
1
def self.kind
-
@kind ||= name.split('::').last.underscore.sub(/_validator$/, '').to_sym unless anonymous?
-
end
-
-
# Accepts options that will be made available through the +options+ reader.
-
1
def initialize(options = {})
-
3
@options = options.except(:class).freeze
-
3
deprecated_setup(options)
-
end
-
-
# Returns the kind for this validator.
-
#
-
# PresenceValidator.new.kind # => :presence
-
# UniquenessValidator.new.kind # => :uniqueness
-
1
def kind
-
self.class.kind
-
end
-
-
# Override this method in subclasses with validation logic, adding errors
-
# to the records +errors+ array where necessary.
-
1
def validate(record)
-
raise NotImplementedError, "Subclasses must implement a validate(record) method."
-
end
-
-
1
private
-
1
def deprecated_setup(options) # TODO: remove me in 4.2.
-
3
return unless respond_to?(:setup)
-
ActiveSupport::Deprecation.warn "The `Validator#setup` instance method is deprecated and will be removed on Rails 4.2. Do your setup in the constructor instead:
-
-
class MyValidator < ActiveModel::Validator
-
def initialize(options={})
-
super
-
options[:class].send :attr_accessor, :custom_attribute
-
end
-
end
-
"
-
setup(options[:class])
-
end
-
end
-
-
# +EachValidator+ is a validator which iterates through the attributes given
-
# in the options hash invoking the <tt>validate_each</tt> method passing in the
-
# record, attribute and value.
-
#
-
# All Active Model validations are built on top of this validator.
-
1
class EachValidator < Validator #:nodoc:
-
1
attr_reader :attributes
-
-
# Returns a new validator instance. All options will be available via the
-
# +options+ reader, however the <tt>:attributes</tt> option will be removed
-
# and instead be made available through the +attributes+ reader.
-
1
def initialize(options)
-
3
@attributes = Array(options.delete(:attributes))
-
3
raise ArgumentError, ":attributes cannot be blank" if @attributes.empty?
-
3
super
-
3
check_validity!
-
end
-
-
# Performs validation on the supplied record. By default this will call
-
# +validates_each+ to determine validity therefore subclasses should
-
# override +validates_each+ with validation logic.
-
1
def validate(record)
-
12
attributes.each do |attribute|
-
12
value = record.read_attribute_for_validation(attribute)
-
12
next if (value.nil? && options[:allow_nil]) || (value.blank? && options[:allow_blank])
-
12
validate_each(record, attribute, value)
-
end
-
end
-
-
# Override this method in subclasses with the validation logic, adding
-
# errors to the records +errors+ array where necessary.
-
1
def validate_each(record, attribute, value)
-
raise NotImplementedError, "Subclasses must implement a validate_each(record, attribute, value) method"
-
end
-
-
# Hook method that gets called by the initializer allowing verification
-
# that the arguments supplied are valid. You could for example raise an
-
# +ArgumentError+ when invalid options are supplied.
-
1
def check_validity!
-
end
-
end
-
-
# +BlockValidator+ is a special +EachValidator+ which receives a block on initialization
-
# and call this block for each attribute being validated. +validates_each+ uses this validator.
-
1
class BlockValidator < EachValidator #:nodoc:
-
1
def initialize(options, &block)
-
@block = block
-
super
-
end
-
-
1
private
-
-
1
def validate_each(record, attribute, value)
-
@block.call(record, attribute, value)
-
end
-
end
-
end
-
1
require_relative 'gem_version'
-
-
1
module ActiveModel
-
# Returns the version of the currently loaded ActiveModel as a <tt>Gem::Version</tt>
-
1
def self.version
-
gem_version
-
end
-
end
-
#--
-
# Copyright (c) 2004-2014 David Heinemeier Hansson
-
#
-
# Permission is hereby granted, free of charge, to any person obtaining
-
# a copy of this software and associated documentation files (the
-
# "Software"), to deal in the Software without restriction, including
-
# without limitation the rights to use, copy, modify, merge, publish,
-
# distribute, sublicense, and/or sell copies of the Software, and to
-
# permit persons to whom the Software is furnished to do so, subject to
-
# the following conditions:
-
#
-
# The above copyright notice and this permission notice shall be
-
# included in all copies or substantial portions of the Software.
-
#
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
#++
-
-
1
require 'active_support'
-
1
require 'active_support/rails'
-
1
require 'active_model'
-
1
require 'arel'
-
-
1
require 'active_record/version'
-
-
1
module ActiveRecord
-
1
extend ActiveSupport::Autoload
-
-
1
autoload :Base
-
1
autoload :Callbacks
-
1
autoload :Core
-
1
autoload :ConnectionHandling
-
1
autoload :CounterCache
-
1
autoload :DynamicMatchers
-
1
autoload :Enum
-
1
autoload :Explain
-
1
autoload :Inheritance
-
1
autoload :Integration
-
1
autoload :Migration
-
1
autoload :Migrator, 'active_record/migration'
-
1
autoload :ModelSchema
-
1
autoload :NestedAttributes
-
1
autoload :NoTouching
-
1
autoload :Persistence
-
1
autoload :QueryCache
-
1
autoload :Querying
-
1
autoload :ReadonlyAttributes
-
1
autoload :Reflection
-
1
autoload :RuntimeRegistry
-
1
autoload :Sanitization
-
1
autoload :Schema
-
1
autoload :SchemaDumper
-
1
autoload :SchemaMigration
-
1
autoload :Scoping
-
1
autoload :Serialization
-
1
autoload :StatementCache
-
1
autoload :Store
-
1
autoload :Timestamp
-
1
autoload :Transactions
-
1
autoload :Translation
-
1
autoload :Validations
-
-
1
eager_autoload do
-
1
autoload :ActiveRecordError, 'active_record/errors'
-
1
autoload :ConnectionNotEstablished, 'active_record/errors'
-
1
autoload :ConnectionAdapters, 'active_record/connection_adapters/abstract_adapter'
-
-
1
autoload :Aggregations
-
1
autoload :Associations
-
1
autoload :AttributeAssignment
-
1
autoload :AttributeMethods
-
1
autoload :AutosaveAssociation
-
-
1
autoload :Relation
-
1
autoload :AssociationRelation
-
1
autoload :NullRelation
-
-
1
autoload_under 'relation' do
-
1
autoload :QueryMethods
-
1
autoload :FinderMethods
-
1
autoload :Calculations
-
1
autoload :PredicateBuilder
-
1
autoload :SpawnMethods
-
1
autoload :Batches
-
1
autoload :Delegation
-
end
-
-
1
autoload :Result
-
end
-
-
1
module Coders
-
1
autoload :YAMLColumn, 'active_record/coders/yaml_column'
-
1
autoload :JSON, 'active_record/coders/json'
-
end
-
-
1
module AttributeMethods
-
1
extend ActiveSupport::Autoload
-
-
1
eager_autoload do
-
1
autoload :BeforeTypeCast
-
1
autoload :Dirty
-
1
autoload :PrimaryKey
-
1
autoload :Query
-
1
autoload :Read
-
1
autoload :TimeZoneConversion
-
1
autoload :Write
-
1
autoload :Serialization
-
end
-
end
-
-
1
module Locking
-
1
extend ActiveSupport::Autoload
-
-
1
eager_autoload do
-
1
autoload :Optimistic
-
1
autoload :Pessimistic
-
end
-
end
-
-
1
module ConnectionAdapters
-
1
extend ActiveSupport::Autoload
-
-
1
eager_autoload do
-
1
autoload :AbstractAdapter
-
1
autoload :ConnectionManagement, "active_record/connection_adapters/abstract/connection_pool"
-
end
-
end
-
-
1
module Scoping
-
1
extend ActiveSupport::Autoload
-
-
1
eager_autoload do
-
1
autoload :Named
-
1
autoload :Default
-
end
-
end
-
-
1
module Tasks
-
1
extend ActiveSupport::Autoload
-
-
1
autoload :DatabaseTasks
-
1
autoload :SQLiteDatabaseTasks, 'active_record/tasks/sqlite_database_tasks'
-
1
autoload :MySQLDatabaseTasks, 'active_record/tasks/mysql_database_tasks'
-
1
autoload :PostgreSQLDatabaseTasks,
-
'active_record/tasks/postgresql_database_tasks'
-
end
-
-
1
autoload :TestFixtures, 'active_record/fixtures'
-
-
1
def self.eager_load!
-
super
-
ActiveRecord::Locking.eager_load!
-
ActiveRecord::Scoping.eager_load!
-
ActiveRecord::Associations.eager_load!
-
ActiveRecord::AttributeMethods.eager_load!
-
ActiveRecord::ConnectionAdapters.eager_load!
-
end
-
end
-
-
1
ActiveSupport.on_load(:active_record) do
-
1
Arel::Table.engine = self
-
end
-
-
1
ActiveSupport.on_load(:i18n) do
-
1
I18n.load_path << File.dirname(__FILE__) + '/active_record/locale/en.yml'
-
end
-
1
module ActiveRecord
-
# = Active Record Aggregations
-
1
module Aggregations # :nodoc:
-
1
extend ActiveSupport::Concern
-
-
1
def clear_aggregation_cache #:nodoc:
-
@aggregation_cache.clear if persisted?
-
end
-
-
# Active Record implements aggregation through a macro-like class method called +composed_of+
-
# for representing attributes as value objects. It expresses relationships like "Account [is]
-
# composed of Money [among other things]" or "Person [is] composed of [an] address". Each call
-
# to the macro adds a description of how the value objects are created from the attributes of
-
# the entity object (when the entity is initialized either as a new object or from finding an
-
# existing object) and how it can be turned back into attributes (when the entity is saved to
-
# the database).
-
#
-
# class Customer < ActiveRecord::Base
-
# composed_of :balance, class_name: "Money", mapping: %w(balance amount)
-
# composed_of :address, mapping: [ %w(address_street street), %w(address_city city) ]
-
# end
-
#
-
# The customer class now has the following methods to manipulate the value objects:
-
# * <tt>Customer#balance, Customer#balance=(money)</tt>
-
# * <tt>Customer#address, Customer#address=(address)</tt>
-
#
-
# These methods will operate with value objects like the ones described below:
-
#
-
# class Money
-
# include Comparable
-
# attr_reader :amount, :currency
-
# EXCHANGE_RATES = { "USD_TO_DKK" => 6 }
-
#
-
# def initialize(amount, currency = "USD")
-
# @amount, @currency = amount, currency
-
# end
-
#
-
# def exchange_to(other_currency)
-
# exchanged_amount = (amount * EXCHANGE_RATES["#{currency}_TO_#{other_currency}"]).floor
-
# Money.new(exchanged_amount, other_currency)
-
# end
-
#
-
# def ==(other_money)
-
# amount == other_money.amount && currency == other_money.currency
-
# end
-
#
-
# def <=>(other_money)
-
# if currency == other_money.currency
-
# amount <=> other_money.amount
-
# else
-
# amount <=> other_money.exchange_to(currency).amount
-
# end
-
# end
-
# end
-
#
-
# class Address
-
# attr_reader :street, :city
-
# def initialize(street, city)
-
# @street, @city = street, city
-
# end
-
#
-
# def close_to?(other_address)
-
# city == other_address.city
-
# end
-
#
-
# def ==(other_address)
-
# city == other_address.city && street == other_address.street
-
# end
-
# end
-
#
-
# Now it's possible to access attributes from the database through the value objects instead. If
-
# you choose to name the composition the same as the attribute's name, it will be the only way to
-
# access that attribute. That's the case with our +balance+ attribute. You interact with the value
-
# objects just like you would with any other attribute:
-
#
-
# customer.balance = Money.new(20) # sets the Money value object and the attribute
-
# customer.balance # => Money value object
-
# customer.balance.exchange_to("DKK") # => Money.new(120, "DKK")
-
# customer.balance > Money.new(10) # => true
-
# customer.balance == Money.new(20) # => true
-
# customer.balance < Money.new(5) # => false
-
#
-
# Value objects can also be composed of multiple attributes, such as the case of Address. The order
-
# of the mappings will determine the order of the parameters.
-
#
-
# customer.address_street = "Hyancintvej"
-
# customer.address_city = "Copenhagen"
-
# customer.address # => Address.new("Hyancintvej", "Copenhagen")
-
#
-
# customer.address_street = "Vesterbrogade"
-
# customer.address # => Address.new("Hyancintvej", "Copenhagen")
-
# customer.clear_aggregation_cache
-
# customer.address # => Address.new("Vesterbrogade", "Copenhagen")
-
#
-
# customer.address = Address.new("May Street", "Chicago")
-
# customer.address_street # => "May Street"
-
# customer.address_city # => "Chicago"
-
#
-
# == Writing value objects
-
#
-
# Value objects are immutable and interchangeable objects that represent a given value, such as
-
# a Money object representing $5. Two Money objects both representing $5 should be equal (through
-
# methods such as <tt>==</tt> and <tt><=></tt> from Comparable if ranking makes sense). This is
-
# unlike entity objects where equality is determined by identity. An entity class such as Customer can
-
# easily have two different objects that both have an address on Hyancintvej. Entity identity is
-
# determined by object or relational unique identifiers (such as primary keys). Normal
-
# ActiveRecord::Base classes are entity objects.
-
#
-
# It's also important to treat the value objects as immutable. Don't allow the Money object to have
-
# its amount changed after creation. Create a new Money object with the new value instead. The
-
# Money#exchange_to method is an example of this. It returns a new value object instead of changing
-
# its own values. Active Record won't persist value objects that have been changed through means
-
# other than the writer method.
-
#
-
# The immutable requirement is enforced by Active Record by freezing any object assigned as a value
-
# object. Attempting to change it afterwards will result in a RuntimeError.
-
#
-
# Read more about value objects on http://c2.com/cgi/wiki?ValueObject and on the dangers of not
-
# keeping value objects immutable on http://c2.com/cgi/wiki?ValueObjectsShouldBeImmutable
-
#
-
# == Custom constructors and converters
-
#
-
# By default value objects are initialized by calling the <tt>new</tt> constructor of the value
-
# class passing each of the mapped attributes, in the order specified by the <tt>:mapping</tt>
-
# option, as arguments. If the value class doesn't support this convention then +composed_of+ allows
-
# a custom constructor to be specified.
-
#
-
# When a new value is assigned to the value object, the default assumption is that the new value
-
# is an instance of the value class. Specifying a custom converter allows the new value to be automatically
-
# converted to an instance of value class if necessary.
-
#
-
# For example, the NetworkResource model has +network_address+ and +cidr_range+ attributes that
-
# should be aggregated using the NetAddr::CIDR value class (http://netaddr.rubyforge.org). The constructor
-
# for the value class is called +create+ and it expects a CIDR address string as a parameter. New
-
# values can be assigned to the value object using either another NetAddr::CIDR object, a string
-
# or an array. The <tt>:constructor</tt> and <tt>:converter</tt> options can be used to meet
-
# these requirements:
-
#
-
# class NetworkResource < ActiveRecord::Base
-
# composed_of :cidr,
-
# class_name: 'NetAddr::CIDR',
-
# mapping: [ %w(network_address network), %w(cidr_range bits) ],
-
# allow_nil: true,
-
# constructor: Proc.new { |network_address, cidr_range| NetAddr::CIDR.create("#{network_address}/#{cidr_range}") },
-
# converter: Proc.new { |value| NetAddr::CIDR.create(value.is_a?(Array) ? value.join('/') : value) }
-
# end
-
#
-
# # This calls the :constructor
-
# network_resource = NetworkResource.new(network_address: '192.168.0.1', cidr_range: 24)
-
#
-
# # These assignments will both use the :converter
-
# network_resource.cidr = [ '192.168.2.1', 8 ]
-
# network_resource.cidr = '192.168.0.1/24'
-
#
-
# # This assignment won't use the :converter as the value is already an instance of the value class
-
# network_resource.cidr = NetAddr::CIDR.create('192.168.2.1/8')
-
#
-
# # Saving and then reloading will use the :constructor on reload
-
# network_resource.save
-
# network_resource.reload
-
#
-
# == Finding records by a value object
-
#
-
# Once a +composed_of+ relationship is specified for a model, records can be loaded from the database
-
# by specifying an instance of the value object in the conditions hash. The following example
-
# finds all customers with +balance_amount+ equal to 20 and +balance_currency+ equal to "USD":
-
#
-
# Customer.where(balance: Money.new(20, "USD"))
-
#
-
1
module ClassMethods
-
# Adds reader and writer methods for manipulating a value object:
-
# <tt>composed_of :address</tt> adds <tt>address</tt> and <tt>address=(new_address)</tt> methods.
-
#
-
# Options are:
-
# * <tt>:class_name</tt> - Specifies the class name of the association. Use it only if that name
-
# can't be inferred from the part id. So <tt>composed_of :address</tt> will by default be linked
-
# to the Address class, but if the real class name is CompanyAddress, you'll have to specify it
-
# with this option.
-
# * <tt>:mapping</tt> - Specifies the mapping of entity attributes to attributes of the value
-
# object. Each mapping is represented as an array where the first item is the name of the
-
# entity attribute and the second item is the name of the attribute in the value object. The
-
# order in which mappings are defined determines the order in which attributes are sent to the
-
# value class constructor.
-
# * <tt>:allow_nil</tt> - Specifies that the value object will not be instantiated when all mapped
-
# attributes are +nil+. Setting the value object to +nil+ has the effect of writing +nil+ to all
-
# mapped attributes.
-
# This defaults to +false+.
-
# * <tt>:constructor</tt> - A symbol specifying the name of the constructor method or a Proc that
-
# is called to initialize the value object. The constructor is passed all of the mapped attributes,
-
# in the order that they are defined in the <tt>:mapping option</tt>, as arguments and uses them
-
# to instantiate a <tt>:class_name</tt> object.
-
# The default is <tt>:new</tt>.
-
# * <tt>:converter</tt> - A symbol specifying the name of a class method of <tt>:class_name</tt>
-
# or a Proc that is called when a new value is assigned to the value object. The converter is
-
# passed the single value that is used in the assignment and is only called if the new value is
-
# not an instance of <tt>:class_name</tt>. If <tt>:allow_nil</tt> is set to true, the converter
-
# can return nil to skip the assignment.
-
#
-
# Option examples:
-
# composed_of :temperature, mapping: %w(reading celsius)
-
# composed_of :balance, class_name: "Money", mapping: %w(balance amount),
-
# converter: Proc.new { |balance| balance.to_money }
-
# composed_of :address, mapping: [ %w(address_street street), %w(address_city city) ]
-
# composed_of :gps_location
-
# composed_of :gps_location, allow_nil: true
-
# composed_of :ip_address,
-
# class_name: 'IPAddr',
-
# mapping: %w(ip to_i),
-
# constructor: Proc.new { |ip| IPAddr.new(ip, Socket::AF_INET) },
-
# converter: Proc.new { |ip| ip.is_a?(Integer) ? IPAddr.new(ip, Socket::AF_INET) : IPAddr.new(ip.to_s) }
-
#
-
1
def composed_of(part_id, options = {})
-
options.assert_valid_keys(:class_name, :mapping, :allow_nil, :constructor, :converter)
-
-
name = part_id.id2name
-
class_name = options[:class_name] || name.camelize
-
mapping = options[:mapping] || [ name, name ]
-
mapping = [ mapping ] unless mapping.first.is_a?(Array)
-
allow_nil = options[:allow_nil] || false
-
constructor = options[:constructor] || :new
-
converter = options[:converter]
-
-
reader_method(name, class_name, mapping, allow_nil, constructor)
-
writer_method(name, class_name, mapping, allow_nil, converter)
-
-
reflection = ActiveRecord::Reflection.create(:composed_of, part_id, nil, options, self)
-
Reflection.add_aggregate_reflection self, part_id, reflection
-
end
-
-
1
private
-
1
def reader_method(name, class_name, mapping, allow_nil, constructor)
-
define_method(name) do
-
if @aggregation_cache[name].nil? && (!allow_nil || mapping.any? {|pair| !read_attribute(pair.first).nil? })
-
attrs = mapping.collect {|pair| read_attribute(pair.first)}
-
object = constructor.respond_to?(:call) ?
-
constructor.call(*attrs) :
-
class_name.constantize.send(constructor, *attrs)
-
@aggregation_cache[name] = object
-
end
-
@aggregation_cache[name]
-
end
-
end
-
-
1
def writer_method(name, class_name, mapping, allow_nil, converter)
-
define_method("#{name}=") do |part|
-
klass = class_name.constantize
-
unless part.is_a?(klass) || converter.nil? || part.nil?
-
part = converter.respond_to?(:call) ? converter.call(part) : klass.send(converter, part)
-
end
-
-
if part.nil? && allow_nil
-
mapping.each { |pair| self[pair.first] = nil }
-
@aggregation_cache[name] = nil
-
else
-
mapping.each { |pair| self[pair.first] = part.send(pair.last) }
-
@aggregation_cache[name] = part.freeze
-
end
-
end
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
class AssociationRelation < Relation
-
1
def initialize(klass, table, association)
-
16
super(klass, table)
-
16
@association = association
-
end
-
-
1
def proxy_association
-
@association
-
end
-
-
1
def ==(other)
-
other == to_a
-
end
-
-
1
private
-
-
1
def exec_queries
-
6
super.each { |r| @association.set_inverse_instance r }
-
end
-
end
-
end
-
1
require 'active_support/core_ext/enumerable'
-
1
require 'active_support/core_ext/string/conversions'
-
1
require 'active_support/core_ext/module/remove_method'
-
1
require 'active_record/errors'
-
-
1
module ActiveRecord
-
1
class AssociationNotFoundError < ConfigurationError #:nodoc:
-
1
def initialize(record, association_name)
-
super("Association named '#{association_name}' was not found on #{record.class.name}; perhaps you misspelled it?")
-
end
-
end
-
-
1
class InverseOfAssociationNotFoundError < ActiveRecordError #:nodoc:
-
1
def initialize(reflection, associated_class = nil)
-
super("Could not find the inverse association for #{reflection.name} (#{reflection.options[:inverse_of].inspect} in #{associated_class.nil? ? reflection.class_name : associated_class.name})")
-
end
-
end
-
-
1
class HasManyThroughAssociationNotFoundError < ActiveRecordError #:nodoc:
-
1
def initialize(owner_class_name, reflection)
-
super("Could not find the association #{reflection.options[:through].inspect} in model #{owner_class_name}")
-
end
-
end
-
-
1
class HasManyThroughAssociationPolymorphicSourceError < ActiveRecordError #:nodoc:
-
1
def initialize(owner_class_name, reflection, source_reflection)
-
super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' on the polymorphic object '#{source_reflection.class_name}##{source_reflection.name}' without 'source_type'. Try adding 'source_type: \"#{reflection.name.to_s.classify}\"' to 'has_many :through' definition.")
-
end
-
end
-
-
1
class HasManyThroughAssociationPolymorphicThroughError < ActiveRecordError #:nodoc:
-
1
def initialize(owner_class_name, reflection)
-
super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' which goes through the polymorphic association '#{owner_class_name}##{reflection.through_reflection.name}'.")
-
end
-
end
-
-
1
class HasManyThroughAssociationPointlessSourceTypeError < ActiveRecordError #:nodoc:
-
1
def initialize(owner_class_name, reflection, source_reflection)
-
super("Cannot have a has_many :through association '#{owner_class_name}##{reflection.name}' with a :source_type option if the '#{reflection.through_reflection.class_name}##{source_reflection.name}' is not polymorphic. Try removing :source_type on your association.")
-
end
-
end
-
-
1
class HasOneThroughCantAssociateThroughCollection < ActiveRecordError #:nodoc:
-
1
def initialize(owner_class_name, reflection, through_reflection)
-
super("Cannot have a has_one :through association '#{owner_class_name}##{reflection.name}' where the :through association '#{owner_class_name}##{through_reflection.name}' is a collection. Specify a has_one or belongs_to association in the :through option instead.")
-
end
-
end
-
-
1
class HasManyThroughSourceAssociationNotFoundError < ActiveRecordError #:nodoc:
-
1
def initialize(reflection)
-
through_reflection = reflection.through_reflection
-
source_reflection_names = reflection.source_reflection_names
-
source_associations = reflection.through_reflection.klass._reflections.keys
-
super("Could not find the source association(s) #{source_reflection_names.collect{ |a| a.inspect }.to_sentence(:two_words_connector => ' or ', :last_word_connector => ', or ', :locale => :en)} in model #{through_reflection.klass}. Try 'has_many #{reflection.name.inspect}, :through => #{through_reflection.name.inspect}, :source => <name>'. Is it one of #{source_associations.to_sentence(:two_words_connector => ' or ', :last_word_connector => ', or ', :locale => :en)}?")
-
end
-
end
-
-
1
class HasManyThroughCantAssociateThroughHasOneOrManyReflection < ActiveRecordError #:nodoc:
-
1
def initialize(owner, reflection)
-
super("Cannot modify association '#{owner.class.name}##{reflection.name}' because the source reflection class '#{reflection.source_reflection.class_name}' is associated to '#{reflection.through_reflection.class_name}' via :#{reflection.source_reflection.macro}.")
-
end
-
end
-
-
1
class HasManyThroughCantAssociateNewRecords < ActiveRecordError #:nodoc:
-
1
def initialize(owner, reflection)
-
super("Cannot associate new records through '#{owner.class.name}##{reflection.name}' on '#{reflection.source_reflection.class_name rescue nil}##{reflection.source_reflection.name rescue nil}'. Both records must have an id in order to create the has_many :through record associating them.")
-
end
-
end
-
-
1
class HasManyThroughCantDissociateNewRecords < ActiveRecordError #:nodoc:
-
1
def initialize(owner, reflection)
-
super("Cannot dissociate new records through '#{owner.class.name}##{reflection.name}' on '#{reflection.source_reflection.class_name rescue nil}##{reflection.source_reflection.name rescue nil}'. Both records must have an id in order to delete the has_many :through record associating them.")
-
end
-
end
-
-
1
class HasManyThroughNestedAssociationsAreReadonly < ActiveRecordError #:nodoc:
-
1
def initialize(owner, reflection)
-
super("Cannot modify association '#{owner.class.name}##{reflection.name}' because it goes through more than one other association.")
-
end
-
end
-
-
1
class EagerLoadPolymorphicError < ActiveRecordError #:nodoc:
-
1
def initialize(reflection)
-
super("Cannot eagerly load the polymorphic association #{reflection.name.inspect}")
-
end
-
end
-
-
1
class ReadOnlyAssociation < ActiveRecordError #:nodoc:
-
1
def initialize(reflection)
-
super("Cannot add to a has_many :through association. Try adding to #{reflection.through_reflection.name.inspect}.")
-
end
-
end
-
-
# This error is raised when trying to destroy a parent instance in N:1 or 1:1 associations
-
# (has_many, has_one) when there is at least 1 child associated instance.
-
# ex: if @project.tasks.size > 0, DeleteRestrictionError will be raised when trying to destroy @project
-
1
class DeleteRestrictionError < ActiveRecordError #:nodoc:
-
1
def initialize(name)
-
super("Cannot delete record because of dependent #{name}")
-
end
-
end
-
-
# See ActiveRecord::Associations::ClassMethods for documentation.
-
1
module Associations # :nodoc:
-
1
extend ActiveSupport::Autoload
-
1
extend ActiveSupport::Concern
-
-
# These classes will be loaded when associations are created.
-
# So there is no need to eager load them.
-
1
autoload :Association, 'active_record/associations/association'
-
1
autoload :SingularAssociation, 'active_record/associations/singular_association'
-
1
autoload :CollectionAssociation, 'active_record/associations/collection_association'
-
1
autoload :CollectionProxy, 'active_record/associations/collection_proxy'
-
-
1
autoload :BelongsToAssociation, 'active_record/associations/belongs_to_association'
-
1
autoload :BelongsToPolymorphicAssociation, 'active_record/associations/belongs_to_polymorphic_association'
-
1
autoload :HasManyAssociation, 'active_record/associations/has_many_association'
-
1
autoload :HasManyThroughAssociation, 'active_record/associations/has_many_through_association'
-
1
autoload :HasOneAssociation, 'active_record/associations/has_one_association'
-
1
autoload :HasOneThroughAssociation, 'active_record/associations/has_one_through_association'
-
1
autoload :ThroughAssociation, 'active_record/associations/through_association'
-
-
1
module Builder #:nodoc:
-
1
autoload :Association, 'active_record/associations/builder/association'
-
1
autoload :SingularAssociation, 'active_record/associations/builder/singular_association'
-
1
autoload :CollectionAssociation, 'active_record/associations/builder/collection_association'
-
-
1
autoload :BelongsTo, 'active_record/associations/builder/belongs_to'
-
1
autoload :HasOne, 'active_record/associations/builder/has_one'
-
1
autoload :HasMany, 'active_record/associations/builder/has_many'
-
1
autoload :HasAndBelongsToMany, 'active_record/associations/builder/has_and_belongs_to_many'
-
end
-
-
1
eager_autoload do
-
1
autoload :Preloader, 'active_record/associations/preloader'
-
1
autoload :JoinDependency, 'active_record/associations/join_dependency'
-
1
autoload :AssociationScope, 'active_record/associations/association_scope'
-
1
autoload :AliasTracker, 'active_record/associations/alias_tracker'
-
end
-
-
# Clears out the association cache.
-
1
def clear_association_cache #:nodoc:
-
@association_cache.clear if persisted?
-
end
-
-
# :nodoc:
-
1
attr_reader :association_cache
-
-
# Returns the association instance for the given name, instantiating it if it doesn't already exist
-
1
def association(name) #:nodoc:
-
14
association = association_instance_get(name)
-
-
14
if association.nil?
-
12
raise AssociationNotFoundError.new(self, name) unless reflection = self.class._reflect_on_association(name)
-
12
association = reflection.association_class.new(self, reflection)
-
12
association_instance_set(name, association)
-
end
-
-
14
association
-
end
-
-
1
private
-
# Returns the specified association instance if it responds to :loaded?, nil otherwise.
-
1
def association_instance_get(name)
-
66
@association_cache[name]
-
end
-
-
# Set the specified association instance.
-
1
def association_instance_set(name, association)
-
12
@association_cache[name] = association
-
end
-
-
# \Associations are a set of macro-like class methods for tying objects together through
-
# foreign keys. They express relationships like "Project has one Project Manager"
-
# or "Project belongs to a Portfolio". Each macro adds a number of methods to the
-
# class which are specialized according to the collection or association symbol and the
-
# options hash. It works much the same way as Ruby's own <tt>attr*</tt>
-
# methods.
-
#
-
# class Project < ActiveRecord::Base
-
# belongs_to :portfolio
-
# has_one :project_manager
-
# has_many :milestones
-
# has_and_belongs_to_many :categories
-
# end
-
#
-
# The project class now has the following methods (and more) to ease the traversal and
-
# manipulation of its relationships:
-
# * <tt>Project#portfolio, Project#portfolio=(portfolio), Project#portfolio.nil?</tt>
-
# * <tt>Project#project_manager, Project#project_manager=(project_manager), Project#project_manager.nil?,</tt>
-
# * <tt>Project#milestones.empty?, Project#milestones.size, Project#milestones, Project#milestones<<(milestone),</tt>
-
# <tt>Project#milestones.delete(milestone), Project#milestones.destroy(milestone), Project#milestones.find(milestone_id),</tt>
-
# <tt>Project#milestones.build, Project#milestones.create</tt>
-
# * <tt>Project#categories.empty?, Project#categories.size, Project#categories, Project#categories<<(category1),</tt>
-
# <tt>Project#categories.delete(category1), Project#categories.destroy(category1)</tt>
-
#
-
# === A word of warning
-
#
-
# Don't create associations that have the same name as instance methods of
-
# <tt>ActiveRecord::Base</tt>. Since the association adds a method with that name to
-
# its model, it will override the inherited method and break things.
-
# For instance, +attributes+ and +connection+ would be bad choices for association names.
-
#
-
# == Auto-generated methods
-
#
-
# === Singular associations (one-to-one)
-
# | | belongs_to |
-
# generated methods | belongs_to | :polymorphic | has_one
-
# ----------------------------------+------------+--------------+---------
-
# other | X | X | X
-
# other=(other) | X | X | X
-
# build_other(attributes={}) | X | | X
-
# create_other(attributes={}) | X | | X
-
# create_other!(attributes={}) | X | | X
-
#
-
# ===Collection associations (one-to-many / many-to-many)
-
# | | | has_many
-
# generated methods | habtm | has_many | :through
-
# ----------------------------------+-------+----------+----------
-
# others | X | X | X
-
# others=(other,other,...) | X | X | X
-
# other_ids | X | X | X
-
# other_ids=(id,id,...) | X | X | X
-
# others<< | X | X | X
-
# others.push | X | X | X
-
# others.concat | X | X | X
-
# others.build(attributes={}) | X | X | X
-
# others.create(attributes={}) | X | X | X
-
# others.create!(attributes={}) | X | X | X
-
# others.size | X | X | X
-
# others.length | X | X | X
-
# others.count | X | X | X
-
# others.sum(*args) | X | X | X
-
# others.empty? | X | X | X
-
# others.clear | X | X | X
-
# others.delete(other,other,...) | X | X | X
-
# others.delete_all | X | X | X
-
# others.destroy(other,other,...) | X | X | X
-
# others.destroy_all | X | X | X
-
# others.find(*args) | X | X | X
-
# others.exists? | X | X | X
-
# others.distinct | X | X | X
-
# others.uniq | X | X | X
-
# others.reset | X | X | X
-
#
-
# === Overriding generated methods
-
#
-
# Association methods are generated in a module that is included into the model class,
-
# which allows you to easily override with your own methods and call the original
-
# generated method with +super+. For example:
-
#
-
# class Car < ActiveRecord::Base
-
# belongs_to :owner
-
# belongs_to :old_owner
-
# def owner=(new_owner)
-
# self.old_owner = self.owner
-
# super
-
# end
-
# end
-
#
-
# If your model class is <tt>Project</tt>, the module is
-
# named <tt>Project::GeneratedFeatureMethods</tt>. The GeneratedFeatureMethods module is
-
# included in the model class immediately after the (anonymous) generated attributes methods
-
# module, meaning an association will override the methods for an attribute with the same name.
-
#
-
# == Cardinality and associations
-
#
-
# Active Record associations can be used to describe one-to-one, one-to-many and many-to-many
-
# relationships between models. Each model uses an association to describe its role in
-
# the relation. The +belongs_to+ association is always used in the model that has
-
# the foreign key.
-
#
-
# === One-to-one
-
#
-
# Use +has_one+ in the base, and +belongs_to+ in the associated model.
-
#
-
# class Employee < ActiveRecord::Base
-
# has_one :office
-
# end
-
# class Office < ActiveRecord::Base
-
# belongs_to :employee # foreign key - employee_id
-
# end
-
#
-
# === One-to-many
-
#
-
# Use +has_many+ in the base, and +belongs_to+ in the associated model.
-
#
-
# class Manager < ActiveRecord::Base
-
# has_many :employees
-
# end
-
# class Employee < ActiveRecord::Base
-
# belongs_to :manager # foreign key - manager_id
-
# end
-
#
-
# === Many-to-many
-
#
-
# There are two ways to build a many-to-many relationship.
-
#
-
# The first way uses a +has_many+ association with the <tt>:through</tt> option and a join model, so
-
# there are two stages of associations.
-
#
-
# class Assignment < ActiveRecord::Base
-
# belongs_to :programmer # foreign key - programmer_id
-
# belongs_to :project # foreign key - project_id
-
# end
-
# class Programmer < ActiveRecord::Base
-
# has_many :assignments
-
# has_many :projects, through: :assignments
-
# end
-
# class Project < ActiveRecord::Base
-
# has_many :assignments
-
# has_many :programmers, through: :assignments
-
# end
-
#
-
# For the second way, use +has_and_belongs_to_many+ in both models. This requires a join table
-
# that has no corresponding model or primary key.
-
#
-
# class Programmer < ActiveRecord::Base
-
# has_and_belongs_to_many :projects # foreign keys in the join table
-
# end
-
# class Project < ActiveRecord::Base
-
# has_and_belongs_to_many :programmers # foreign keys in the join table
-
# end
-
#
-
# Choosing which way to build a many-to-many relationship is not always simple.
-
# If you need to work with the relationship model as its own entity,
-
# use <tt>has_many :through</tt>. Use +has_and_belongs_to_many+ when working with legacy schemas or when
-
# you never work directly with the relationship itself.
-
#
-
# == Is it a +belongs_to+ or +has_one+ association?
-
#
-
# Both express a 1-1 relationship. The difference is mostly where to place the foreign
-
# key, which goes on the table for the class declaring the +belongs_to+ relationship.
-
#
-
# class User < ActiveRecord::Base
-
# # I reference an account.
-
# belongs_to :account
-
# end
-
#
-
# class Account < ActiveRecord::Base
-
# # One user references me.
-
# has_one :user
-
# end
-
#
-
# The tables for these classes could look something like:
-
#
-
# CREATE TABLE users (
-
# id int(11) NOT NULL auto_increment,
-
# account_id int(11) default NULL,
-
# name varchar default NULL,
-
# PRIMARY KEY (id)
-
# )
-
#
-
# CREATE TABLE accounts (
-
# id int(11) NOT NULL auto_increment,
-
# name varchar default NULL,
-
# PRIMARY KEY (id)
-
# )
-
#
-
# == Unsaved objects and associations
-
#
-
# You can manipulate objects and associations before they are saved to the database, but
-
# there is some special behavior you should be aware of, mostly involving the saving of
-
# associated objects.
-
#
-
# You can set the <tt>:autosave</tt> option on a <tt>has_one</tt>, <tt>belongs_to</tt>,
-
# <tt>has_many</tt>, or <tt>has_and_belongs_to_many</tt> association. Setting it
-
# to +true+ will _always_ save the members, whereas setting it to +false+ will
-
# _never_ save the members. More details about <tt>:autosave</tt> option is available at
-
# AutosaveAssociation.
-
#
-
# === One-to-one associations
-
#
-
# * Assigning an object to a +has_one+ association automatically saves that object and
-
# the object being replaced (if there is one), in order to update their foreign
-
# keys - except if the parent object is unsaved (<tt>new_record? == true</tt>).
-
# * If either of these saves fail (due to one of the objects being invalid), an
-
# <tt>ActiveRecord::RecordNotSaved</tt> exception is raised and the assignment is
-
# cancelled.
-
# * If you wish to assign an object to a +has_one+ association without saving it,
-
# use the <tt>build_association</tt> method (documented below). The object being
-
# replaced will still be saved to update its foreign key.
-
# * Assigning an object to a +belongs_to+ association does not save the object, since
-
# the foreign key field belongs on the parent. It does not save the parent either.
-
#
-
# === Collections
-
#
-
# * Adding an object to a collection (+has_many+ or +has_and_belongs_to_many+) automatically
-
# saves that object, except if the parent object (the owner of the collection) is not yet
-
# stored in the database.
-
# * If saving any of the objects being added to a collection (via <tt>push</tt> or similar)
-
# fails, then <tt>push</tt> returns +false+.
-
# * If saving fails while replacing the collection (via <tt>association=</tt>), an
-
# <tt>ActiveRecord::RecordNotSaved</tt> exception is raised and the assignment is
-
# cancelled.
-
# * You can add an object to a collection without automatically saving it by using the
-
# <tt>collection.build</tt> method (documented below).
-
# * All unsaved (<tt>new_record? == true</tt>) members of the collection are automatically
-
# saved when the parent is saved.
-
#
-
# == Customizing the query
-
#
-
# \Associations are built from <tt>Relation</tt>s, and you can use the <tt>Relation</tt> syntax
-
# to customize them. For example, to add a condition:
-
#
-
# class Blog < ActiveRecord::Base
-
# has_many :published_posts, -> { where published: true }, class_name: 'Post'
-
# end
-
#
-
# Inside the <tt>-> { ... }</tt> block you can use all of the usual <tt>Relation</tt> methods.
-
#
-
# === Accessing the owner object
-
#
-
# Sometimes it is useful to have access to the owner object when building the query. The owner
-
# is passed as a parameter to the block. For example, the following association would find all
-
# events that occur on the user's birthday:
-
#
-
# class User < ActiveRecord::Base
-
# has_many :birthday_events, ->(user) { where starts_on: user.birthday }, class_name: 'Event'
-
# end
-
#
-
# == Association callbacks
-
#
-
# Similar to the normal callbacks that hook into the life cycle of an Active Record object,
-
# you can also define callbacks that get triggered when you add an object to or remove an
-
# object from an association collection.
-
#
-
# class Project
-
# has_and_belongs_to_many :developers, after_add: :evaluate_velocity
-
#
-
# def evaluate_velocity(developer)
-
# ...
-
# end
-
# end
-
#
-
# It's possible to stack callbacks by passing them as an array. Example:
-
#
-
# class Project
-
# has_and_belongs_to_many :developers,
-
# after_add: [:evaluate_velocity, Proc.new { |p, d| p.shipping_date = Time.now}]
-
# end
-
#
-
# Possible callbacks are: +before_add+, +after_add+, +before_remove+ and +after_remove+.
-
#
-
# Should any of the +before_add+ callbacks throw an exception, the object does not get
-
# added to the collection. Same with the +before_remove+ callbacks; if an exception is
-
# thrown the object doesn't get removed.
-
#
-
# == Association extensions
-
#
-
# The proxy objects that control the access to associations can be extended through anonymous
-
# modules. This is especially beneficial for adding new finders, creators, and other
-
# factory-type methods that are only used as part of this association.
-
#
-
# class Account < ActiveRecord::Base
-
# has_many :people do
-
# def find_or_create_by_name(name)
-
# first_name, last_name = name.split(" ", 2)
-
# find_or_create_by(first_name: first_name, last_name: last_name)
-
# end
-
# end
-
# end
-
#
-
# person = Account.first.people.find_or_create_by_name("David Heinemeier Hansson")
-
# person.first_name # => "David"
-
# person.last_name # => "Heinemeier Hansson"
-
#
-
# If you need to share the same extensions between many associations, you can use a named
-
# extension module.
-
#
-
# module FindOrCreateByNameExtension
-
# def find_or_create_by_name(name)
-
# first_name, last_name = name.split(" ", 2)
-
# find_or_create_by(first_name: first_name, last_name: last_name)
-
# end
-
# end
-
#
-
# class Account < ActiveRecord::Base
-
# has_many :people, -> { extending FindOrCreateByNameExtension }
-
# end
-
#
-
# class Company < ActiveRecord::Base
-
# has_many :people, -> { extending FindOrCreateByNameExtension }
-
# end
-
#
-
# Some extensions can only be made to work with knowledge of the association's internals.
-
# Extensions can access relevant state using the following methods (where +items+ is the
-
# name of the association):
-
#
-
# * <tt>record.association(:items).owner</tt> - Returns the object the association is part of.
-
# * <tt>record.association(:items).reflection</tt> - Returns the reflection object that describes the association.
-
# * <tt>record.association(:items).target</tt> - Returns the associated object for +belongs_to+ and +has_one+, or
-
# the collection of associated objects for +has_many+ and +has_and_belongs_to_many+.
-
#
-
# However, inside the actual extension code, you will not have access to the <tt>record</tt> as
-
# above. In this case, you can access <tt>proxy_association</tt>. For example,
-
# <tt>record.association(:items)</tt> and <tt>record.items.proxy_association</tt> will return
-
# the same object, allowing you to make calls like <tt>proxy_association.owner</tt> inside
-
# association extensions.
-
#
-
# == Association Join Models
-
#
-
# Has Many associations can be configured with the <tt>:through</tt> option to use an
-
# explicit join model to retrieve the data. This operates similarly to a
-
# +has_and_belongs_to_many+ association. The advantage is that you're able to add validations,
-
# callbacks, and extra attributes on the join model. Consider the following schema:
-
#
-
# class Author < ActiveRecord::Base
-
# has_many :authorships
-
# has_many :books, through: :authorships
-
# end
-
#
-
# class Authorship < ActiveRecord::Base
-
# belongs_to :author
-
# belongs_to :book
-
# end
-
#
-
# @author = Author.first
-
# @author.authorships.collect { |a| a.book } # selects all books that the author's authorships belong to
-
# @author.books # selects all books by using the Authorship join model
-
#
-
# You can also go through a +has_many+ association on the join model:
-
#
-
# class Firm < ActiveRecord::Base
-
# has_many :clients
-
# has_many :invoices, through: :clients
-
# end
-
#
-
# class Client < ActiveRecord::Base
-
# belongs_to :firm
-
# has_many :invoices
-
# end
-
#
-
# class Invoice < ActiveRecord::Base
-
# belongs_to :client
-
# end
-
#
-
# @firm = Firm.first
-
# @firm.clients.collect { |c| c.invoices }.flatten # select all invoices for all clients of the firm
-
# @firm.invoices # selects all invoices by going through the Client join model
-
#
-
# Similarly you can go through a +has_one+ association on the join model:
-
#
-
# class Group < ActiveRecord::Base
-
# has_many :users
-
# has_many :avatars, through: :users
-
# end
-
#
-
# class User < ActiveRecord::Base
-
# belongs_to :group
-
# has_one :avatar
-
# end
-
#
-
# class Avatar < ActiveRecord::Base
-
# belongs_to :user
-
# end
-
#
-
# @group = Group.first
-
# @group.users.collect { |u| u.avatar }.compact # select all avatars for all users in the group
-
# @group.avatars # selects all avatars by going through the User join model.
-
#
-
# An important caveat with going through +has_one+ or +has_many+ associations on the
-
# join model is that these associations are *read-only*. For example, the following
-
# would not work following the previous example:
-
#
-
# @group.avatars << Avatar.new # this would work if User belonged_to Avatar rather than the other way around
-
# @group.avatars.delete(@group.avatars.last) # so would this
-
#
-
# == Setting Inverses
-
#
-
# If you are using a +belongs_to+ on the join model, it is a good idea to set the
-
# <tt>:inverse_of</tt> option on the +belongs_to+, which will mean that the following example
-
# works correctly (where <tt>tags</tt> is a +has_many+ <tt>:through</tt> association):
-
#
-
# @post = Post.first
-
# @tag = @post.tags.build name: "ruby"
-
# @tag.save
-
#
-
# The last line ought to save the through record (a <tt>Taggable</tt>). This will only work if the
-
# <tt>:inverse_of</tt> is set:
-
#
-
# class Taggable < ActiveRecord::Base
-
# belongs_to :post
-
# belongs_to :tag, inverse_of: :taggings
-
# end
-
#
-
# If you do not set the <tt>:inverse_of</tt> record, the association will
-
# do its best to match itself up with the correct inverse. Automatic
-
# inverse detection only works on <tt>has_many</tt>, <tt>has_one</tt>, and
-
# <tt>belongs_to</tt> associations.
-
#
-
# Extra options on the associations, as defined in the
-
# <tt>AssociationReflection::INVALID_AUTOMATIC_INVERSE_OPTIONS</tt> constant, will
-
# also prevent the association's inverse from being found automatically.
-
#
-
# The automatic guessing of the inverse association uses a heuristic based
-
# on the name of the class, so it may not work for all associations,
-
# especially the ones with non-standard names.
-
#
-
# You can turn off the automatic detection of inverse associations by setting
-
# the <tt>:inverse_of</tt> option to <tt>false</tt> like so:
-
#
-
# class Taggable < ActiveRecord::Base
-
# belongs_to :tag, inverse_of: false
-
# end
-
#
-
# == Nested \Associations
-
#
-
# You can actually specify *any* association with the <tt>:through</tt> option, including an
-
# association which has a <tt>:through</tt> option itself. For example:
-
#
-
# class Author < ActiveRecord::Base
-
# has_many :posts
-
# has_many :comments, through: :posts
-
# has_many :commenters, through: :comments
-
# end
-
#
-
# class Post < ActiveRecord::Base
-
# has_many :comments
-
# end
-
#
-
# class Comment < ActiveRecord::Base
-
# belongs_to :commenter
-
# end
-
#
-
# @author = Author.first
-
# @author.commenters # => People who commented on posts written by the author
-
#
-
# An equivalent way of setting up this association this would be:
-
#
-
# class Author < ActiveRecord::Base
-
# has_many :posts
-
# has_many :commenters, through: :posts
-
# end
-
#
-
# class Post < ActiveRecord::Base
-
# has_many :comments
-
# has_many :commenters, through: :comments
-
# end
-
#
-
# class Comment < ActiveRecord::Base
-
# belongs_to :commenter
-
# end
-
#
-
# When using nested association, you will not be able to modify the association because there
-
# is not enough information to know what modification to make. For example, if you tried to
-
# add a <tt>Commenter</tt> in the example above, there would be no way to tell how to set up the
-
# intermediate <tt>Post</tt> and <tt>Comment</tt> objects.
-
#
-
# == Polymorphic \Associations
-
#
-
# Polymorphic associations on models are not restricted on what types of models they
-
# can be associated with. Rather, they specify an interface that a +has_many+ association
-
# must adhere to.
-
#
-
# class Asset < ActiveRecord::Base
-
# belongs_to :attachable, polymorphic: true
-
# end
-
#
-
# class Post < ActiveRecord::Base
-
# has_many :assets, as: :attachable # The :as option specifies the polymorphic interface to use.
-
# end
-
#
-
# @asset.attachable = @post
-
#
-
# This works by using a type column in addition to a foreign key to specify the associated
-
# record. In the Asset example, you'd need an +attachable_id+ integer column and an
-
# +attachable_type+ string column.
-
#
-
# Using polymorphic associations in combination with single table inheritance (STI) is
-
# a little tricky. In order for the associations to work as expected, ensure that you
-
# store the base model for the STI models in the type column of the polymorphic
-
# association. To continue with the asset example above, suppose there are guest posts
-
# and member posts that use the posts table for STI. In this case, there must be a +type+
-
# column in the posts table.
-
#
-
# Note: The <tt>attachable_type=</tt> method is being called when assigning an +attachable+.
-
# The +class_name+ of the +attachable+ is passed as a String.
-
#
-
# class Asset < ActiveRecord::Base
-
# belongs_to :attachable, polymorphic: true
-
#
-
# def attachable_type=(class_name)
-
# super(class_name.constantize.base_class.to_s)
-
# end
-
# end
-
#
-
# class Post < ActiveRecord::Base
-
# # because we store "Post" in attachable_type now dependent: :destroy will work
-
# has_many :assets, as: :attachable, dependent: :destroy
-
# end
-
#
-
# class GuestPost < Post
-
# end
-
#
-
# class MemberPost < Post
-
# end
-
#
-
# == Caching
-
#
-
# All of the methods are built on a simple caching principle that will keep the result
-
# of the last query around unless specifically instructed not to. The cache is even
-
# shared across methods to make it even cheaper to use the macro-added methods without
-
# worrying too much about performance at the first go.
-
#
-
# project.milestones # fetches milestones from the database
-
# project.milestones.size # uses the milestone cache
-
# project.milestones.empty? # uses the milestone cache
-
# project.milestones(true).size # fetches milestones from the database
-
# project.milestones # uses the milestone cache
-
#
-
# == Eager loading of associations
-
#
-
# Eager loading is a way to find objects of a certain class and a number of named associations.
-
# This is one of the easiest ways of to prevent the dreaded 1+N problem in which fetching 100
-
# posts that each need to display their author triggers 101 database queries. Through the
-
# use of eager loading, the 101 queries can be reduced to 2.
-
#
-
# class Post < ActiveRecord::Base
-
# belongs_to :author
-
# has_many :comments
-
# end
-
#
-
# Consider the following loop using the class above:
-
#
-
# Post.all.each do |post|
-
# puts "Post: " + post.title
-
# puts "Written by: " + post.author.name
-
# puts "Last comment on: " + post.comments.first.created_on
-
# end
-
#
-
# To iterate over these one hundred posts, we'll generate 201 database queries. Let's
-
# first just optimize it for retrieving the author:
-
#
-
# Post.includes(:author).each do |post|
-
#
-
# This references the name of the +belongs_to+ association that also used the <tt>:author</tt>
-
# symbol. After loading the posts, find will collect the +author_id+ from each one and load
-
# all the referenced authors with one query. Doing so will cut down the number of queries
-
# from 201 to 102.
-
#
-
# We can improve upon the situation further by referencing both associations in the finder with:
-
#
-
# Post.includes(:author, :comments).each do |post|
-
#
-
# This will load all comments with a single query. This reduces the total number of queries
-
# to 3. More generally the number of queries will be 1 plus the number of associations
-
# named (except if some of the associations are polymorphic +belongs_to+ - see below).
-
#
-
# To include a deep hierarchy of associations, use a hash:
-
#
-
# Post.includes(:author, {comments: {author: :gravatar}}).each do |post|
-
#
-
# That'll grab not only all the comments but all their authors and gravatar pictures.
-
# You can mix and match symbols, arrays and hashes in any combination to describe the
-
# associations you want to load.
-
#
-
# All of this power shouldn't fool you into thinking that you can pull out huge amounts
-
# of data with no performance penalty just because you've reduced the number of queries.
-
# The database still needs to send all the data to Active Record and it still needs to
-
# be processed. So it's no catch-all for performance problems, but it's a great way to
-
# cut down on the number of queries in a situation as the one described above.
-
#
-
# Since only one table is loaded at a time, conditions or orders cannot reference tables
-
# other than the main one. If this is the case Active Record falls back to the previously
-
# used LEFT OUTER JOIN based strategy. For example
-
#
-
# Post.includes([:author, :comments]).where(['comments.approved = ?', true])
-
#
-
# This will result in a single SQL query with joins along the lines of:
-
# <tt>LEFT OUTER JOIN comments ON comments.post_id = posts.id</tt> and
-
# <tt>LEFT OUTER JOIN authors ON authors.id = posts.author_id</tt>. Note that using conditions
-
# like this can have unintended consequences.
-
# In the above example posts with no approved comments are not returned at all, because
-
# the conditions apply to the SQL statement as a whole and not just to the association.
-
#
-
# If you want to load all posts (including posts with no approved comments) then write
-
# your own LEFT OUTER JOIN query using ON
-
#
-
# Post.joins('LEFT OUTER JOIN comments ON comments.post_id = posts.id AND comments.approved = true')
-
#
-
# You must disambiguate column references for this fallback to happen, for example
-
# <tt>order: "author.name DESC"</tt> will work but <tt>order: "name DESC"</tt> will not.
-
#
-
# If you do want eager load only some members of an association it is usually more natural
-
# to include an association which has conditions defined on it:
-
#
-
# class Post < ActiveRecord::Base
-
# has_many :approved_comments, -> { where approved: true }, class_name: 'Comment'
-
# end
-
#
-
# Post.includes(:approved_comments)
-
#
-
# This will load posts and eager load the +approved_comments+ association, which contains
-
# only those comments that have been approved.
-
#
-
# If you eager load an association with a specified <tt>:limit</tt> option, it will be ignored,
-
# returning all the associated objects:
-
#
-
# class Picture < ActiveRecord::Base
-
# has_many :most_recent_comments, -> { order('id DESC').limit(10) }, class_name: 'Comment'
-
# end
-
#
-
# Picture.includes(:most_recent_comments).first.most_recent_comments # => returns all associated comments.
-
#
-
# Eager loading is supported with polymorphic associations.
-
#
-
# class Address < ActiveRecord::Base
-
# belongs_to :addressable, polymorphic: true
-
# end
-
#
-
# A call that tries to eager load the addressable model
-
#
-
# Address.includes(:addressable)
-
#
-
# This will execute one query to load the addresses and load the addressables with one
-
# query per addressable type.
-
# For example if all the addressables are either of class Person or Company then a total
-
# of 3 queries will be executed. The list of addressable types to load is determined on
-
# the back of the addresses loaded. This is not supported if Active Record has to fallback
-
# to the previous implementation of eager loading and will raise <tt>ActiveRecord::EagerLoadPolymorphicError</tt>.
-
# The reason is that the parent model's type is a column value so its corresponding table
-
# name cannot be put in the +FROM+/+JOIN+ clauses of that query.
-
#
-
# == Table Aliasing
-
#
-
# Active Record uses table aliasing in the case that a table is referenced multiple times
-
# in a join. If a table is referenced only once, the standard table name is used. The
-
# second time, the table is aliased as <tt>#{reflection_name}_#{parent_table_name}</tt>.
-
# Indexes are appended for any more successive uses of the table name.
-
#
-
# Post.joins(:comments)
-
# # => SELECT ... FROM posts INNER JOIN comments ON ...
-
# Post.joins(:special_comments) # STI
-
# # => SELECT ... FROM posts INNER JOIN comments ON ... AND comments.type = 'SpecialComment'
-
# Post.joins(:comments, :special_comments) # special_comments is the reflection name, posts is the parent table name
-
# # => SELECT ... FROM posts INNER JOIN comments ON ... INNER JOIN comments special_comments_posts
-
#
-
# Acts as tree example:
-
#
-
# TreeMixin.joins(:children)
-
# # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ...
-
# TreeMixin.joins(children: :parent)
-
# # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ...
-
# INNER JOIN parents_mixins ...
-
# TreeMixin.joins(children: {parent: :children})
-
# # => SELECT ... FROM mixins INNER JOIN mixins childrens_mixins ...
-
# INNER JOIN parents_mixins ...
-
# INNER JOIN mixins childrens_mixins_2
-
#
-
# Has and Belongs to Many join tables use the same idea, but add a <tt>_join</tt> suffix:
-
#
-
# Post.joins(:categories)
-
# # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ...
-
# Post.joins(categories: :posts)
-
# # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ...
-
# INNER JOIN categories_posts posts_categories_join INNER JOIN posts posts_categories
-
# Post.joins(categories: {posts: :categories})
-
# # => SELECT ... FROM posts INNER JOIN categories_posts ... INNER JOIN categories ...
-
# INNER JOIN categories_posts posts_categories_join INNER JOIN posts posts_categories
-
# INNER JOIN categories_posts categories_posts_join INNER JOIN categories categories_posts_2
-
#
-
# If you wish to specify your own custom joins using <tt>joins</tt> method, those table
-
# names will take precedence over the eager associations:
-
#
-
# Post.joins(:comments).joins("inner join comments ...")
-
# # => SELECT ... FROM posts INNER JOIN comments_posts ON ... INNER JOIN comments ...
-
# Post.joins(:comments, :special_comments).joins("inner join comments ...")
-
# # => SELECT ... FROM posts INNER JOIN comments comments_posts ON ...
-
# INNER JOIN comments special_comments_posts ...
-
# INNER JOIN comments ...
-
#
-
# Table aliases are automatically truncated according to the maximum length of table identifiers
-
# according to the specific database.
-
#
-
# == Modules
-
#
-
# By default, associations will look for objects within the current module scope. Consider:
-
#
-
# module MyApplication
-
# module Business
-
# class Firm < ActiveRecord::Base
-
# has_many :clients
-
# end
-
#
-
# class Client < ActiveRecord::Base; end
-
# end
-
# end
-
#
-
# When <tt>Firm#clients</tt> is called, it will in turn call
-
# <tt>MyApplication::Business::Client.find_all_by_firm_id(firm.id)</tt>.
-
# If you want to associate with a class in another module scope, this can be done by
-
# specifying the complete class name.
-
#
-
# module MyApplication
-
# module Business
-
# class Firm < ActiveRecord::Base; end
-
# end
-
#
-
# module Billing
-
# class Account < ActiveRecord::Base
-
# belongs_to :firm, class_name: "MyApplication::Business::Firm"
-
# end
-
# end
-
# end
-
#
-
# == Bi-directional associations
-
#
-
# When you specify an association there is usually an association on the associated model
-
# that specifies the same relationship in reverse. For example, with the following models:
-
#
-
# class Dungeon < ActiveRecord::Base
-
# has_many :traps
-
# has_one :evil_wizard
-
# end
-
#
-
# class Trap < ActiveRecord::Base
-
# belongs_to :dungeon
-
# end
-
#
-
# class EvilWizard < ActiveRecord::Base
-
# belongs_to :dungeon
-
# end
-
#
-
# The +traps+ association on +Dungeon+ and the +dungeon+ association on +Trap+ are
-
# the inverse of each other and the inverse of the +dungeon+ association on +EvilWizard+
-
# is the +evil_wizard+ association on +Dungeon+ (and vice-versa). By default,
-
# Active Record doesn't know anything about these inverse relationships and so no object
-
# loading optimization is possible. For example:
-
#
-
# d = Dungeon.first
-
# t = d.traps.first
-
# d.level == t.dungeon.level # => true
-
# d.level = 10
-
# d.level == t.dungeon.level # => false
-
#
-
# The +Dungeon+ instances +d+ and <tt>t.dungeon</tt> in the above example refer to
-
# the same object data from the database, but are actually different in-memory copies
-
# of that data. Specifying the <tt>:inverse_of</tt> option on associations lets you tell
-
# Active Record about inverse relationships and it will optimise object loading. For
-
# example, if we changed our model definitions to:
-
#
-
# class Dungeon < ActiveRecord::Base
-
# has_many :traps, inverse_of: :dungeon
-
# has_one :evil_wizard, inverse_of: :dungeon
-
# end
-
#
-
# class Trap < ActiveRecord::Base
-
# belongs_to :dungeon, inverse_of: :traps
-
# end
-
#
-
# class EvilWizard < ActiveRecord::Base
-
# belongs_to :dungeon, inverse_of: :evil_wizard
-
# end
-
#
-
# Then, from our code snippet above, +d+ and <tt>t.dungeon</tt> are actually the same
-
# in-memory instance and our final <tt>d.level == t.dungeon.level</tt> will return +true+.
-
#
-
# There are limitations to <tt>:inverse_of</tt> support:
-
#
-
# * does not work with <tt>:through</tt> associations.
-
# * does not work with <tt>:polymorphic</tt> associations.
-
# * for +belongs_to+ associations +has_many+ inverse associations are ignored.
-
#
-
# == Deleting from associations
-
#
-
# === Dependent associations
-
#
-
# +has_many+, +has_one+ and +belongs_to+ associations support the <tt>:dependent</tt> option.
-
# This allows you to specify that associated records should be deleted when the owner is
-
# deleted.
-
#
-
# For example:
-
#
-
# class Author
-
# has_many :posts, dependent: :destroy
-
# end
-
# Author.find(1).destroy # => Will destroy all of the author's posts, too
-
#
-
# The <tt>:dependent</tt> option can have different values which specify how the deletion
-
# is done. For more information, see the documentation for this option on the different
-
# specific association types. When no option is given, the behavior is to do nothing
-
# with the associated records when destroying a record.
-
#
-
# Note that <tt>:dependent</tt> is implemented using Rails' callback
-
# system, which works by processing callbacks in order. Therefore, other
-
# callbacks declared either before or after the <tt>:dependent</tt> option
-
# can affect what it does.
-
#
-
# === Delete or destroy?
-
#
-
# +has_many+ and +has_and_belongs_to_many+ associations have the methods <tt>destroy</tt>,
-
# <tt>delete</tt>, <tt>destroy_all</tt> and <tt>delete_all</tt>.
-
#
-
# For +has_and_belongs_to_many+, <tt>delete</tt> and <tt>destroy</tt> are the same: they
-
# cause the records in the join table to be removed.
-
#
-
# For +has_many+, <tt>destroy</tt> and <tt>destroy_all</tt> will always call the <tt>destroy</tt> method of the
-
# record(s) being removed so that callbacks are run. However <tt>delete</tt> and <tt>delete_all</tt> will either
-
# do the deletion according to the strategy specified by the <tt>:dependent</tt> option, or
-
# if no <tt>:dependent</tt> option is given, then it will follow the default strategy.
-
# The default strategy is <tt>:nullify</tt> (set the foreign keys to <tt>nil</tt>), except for
-
# +has_many+ <tt>:through</tt>, where the default strategy is <tt>delete_all</tt> (delete
-
# the join records, without running their callbacks).
-
#
-
# There is also a <tt>clear</tt> method which is the same as <tt>delete_all</tt>, except that
-
# it returns the association rather than the records which have been deleted.
-
#
-
# === What gets deleted?
-
#
-
# There is a potential pitfall here: +has_and_belongs_to_many+ and +has_many+ <tt>:through</tt>
-
# associations have records in join tables, as well as the associated records. So when we
-
# call one of these deletion methods, what exactly should be deleted?
-
#
-
# The answer is that it is assumed that deletion on an association is about removing the
-
# <i>link</i> between the owner and the associated object(s), rather than necessarily the
-
# associated objects themselves. So with +has_and_belongs_to_many+ and +has_many+
-
# <tt>:through</tt>, the join records will be deleted, but the associated records won't.
-
#
-
# This makes sense if you think about it: if you were to call <tt>post.tags.delete(Tag.find_by(name: 'food'))</tt>
-
# you would want the 'food' tag to be unlinked from the post, rather than for the tag itself
-
# to be removed from the database.
-
#
-
# However, there are examples where this strategy doesn't make sense. For example, suppose
-
# a person has many projects, and each project has many tasks. If we deleted one of a person's
-
# tasks, we would probably not want the project to be deleted. In this scenario, the delete method
-
# won't actually work: it can only be used if the association on the join model is a
-
# +belongs_to+. In other situations you are expected to perform operations directly on
-
# either the associated records or the <tt>:through</tt> association.
-
#
-
# With a regular +has_many+ there is no distinction between the "associated records"
-
# and the "link", so there is only one choice for what gets deleted.
-
#
-
# With +has_and_belongs_to_many+ and +has_many+ <tt>:through</tt>, if you want to delete the
-
# associated records themselves, you can always do something along the lines of
-
# <tt>person.tasks.each(&:destroy)</tt>.
-
#
-
# == Type safety with <tt>ActiveRecord::AssociationTypeMismatch</tt>
-
#
-
# If you attempt to assign an object to an association that doesn't match the inferred
-
# or specified <tt>:class_name</tt>, you'll get an <tt>ActiveRecord::AssociationTypeMismatch</tt>.
-
#
-
# == Options
-
#
-
# All of the association macros can be specialized through options. This makes cases
-
# more complex than the simple and guessable ones possible.
-
1
module ClassMethods
-
# Specifies a one-to-many association. The following methods for retrieval and query of
-
# collections of associated objects will be added:
-
#
-
# [collection(force_reload = false)]
-
# Returns an array of all the associated objects.
-
# An empty array is returned if none are found.
-
# [collection<<(object, ...)]
-
# Adds one or more objects to the collection by setting their foreign keys to the collection's primary key.
-
# Note that this operation instantly fires update SQL without waiting for the save or update call on the
-
# parent object, unless the parent object is a new record.
-
# [collection.delete(object, ...)]
-
# Removes one or more objects from the collection by setting their foreign keys to +NULL+.
-
# Objects will be in addition destroyed if they're associated with <tt>dependent: :destroy</tt>,
-
# and deleted if they're associated with <tt>dependent: :delete_all</tt>.
-
#
-
# If the <tt>:through</tt> option is used, then the join records are deleted (rather than
-
# nullified) by default, but you can specify <tt>dependent: :destroy</tt> or
-
# <tt>dependent: :nullify</tt> to override this.
-
# [collection.destroy(object, ...)]
-
# Removes one or more objects from the collection by running <tt>destroy</tt> on
-
# each record, regardless of any dependent option, ensuring callbacks are run.
-
#
-
# If the <tt>:through</tt> option is used, then the join records are destroyed
-
# instead, not the objects themselves.
-
# [collection=objects]
-
# Replaces the collections content by deleting and adding objects as appropriate. If the <tt>:through</tt>
-
# option is true callbacks in the join models are triggered except destroy callbacks, since deletion is
-
# direct.
-
# [collection_singular_ids]
-
# Returns an array of the associated objects' ids
-
# [collection_singular_ids=ids]
-
# Replace the collection with the objects identified by the primary keys in +ids+. This
-
# method loads the models and calls <tt>collection=</tt>. See above.
-
# [collection.clear]
-
# Removes every object from the collection. This destroys the associated objects if they
-
# are associated with <tt>dependent: :destroy</tt>, deletes them directly from the
-
# database if <tt>dependent: :delete_all</tt>, otherwise sets their foreign keys to +NULL+.
-
# If the <tt>:through</tt> option is true no destroy callbacks are invoked on the join models.
-
# Join models are directly deleted.
-
# [collection.empty?]
-
# Returns +true+ if there are no associated objects.
-
# [collection.size]
-
# Returns the number of associated objects.
-
# [collection.find(...)]
-
# Finds an associated object according to the same rules as <tt>ActiveRecord::Base.find</tt>.
-
# [collection.exists?(...)]
-
# Checks whether an associated object with the given conditions exists.
-
# Uses the same rules as <tt>ActiveRecord::Base.exists?</tt>.
-
# [collection.build(attributes = {}, ...)]
-
# Returns one or more new objects of the collection type that have been instantiated
-
# with +attributes+ and linked to this object through a foreign key, but have not yet
-
# been saved.
-
# [collection.create(attributes = {})]
-
# Returns a new object of the collection type that has been instantiated
-
# with +attributes+, linked to this object through a foreign key, and that has already
-
# been saved (if it passed the validation). *Note*: This only works if the base model
-
# already exists in the DB, not if it is a new (unsaved) record!
-
# [collection.create!(attributes = {})]
-
# Does the same as <tt>collection.create</tt>, but raises <tt>ActiveRecord::RecordInvalid</tt>
-
# if the record is invalid.
-
#
-
# (*Note*: +collection+ is replaced with the symbol passed as the first argument, so
-
# <tt>has_many :clients</tt> would add among others <tt>clients.empty?</tt>.)
-
#
-
# === Example
-
#
-
# A <tt>Firm</tt> class declares <tt>has_many :clients</tt>, which will add:
-
# * <tt>Firm#clients</tt> (similar to <tt>Client.where(firm_id: id)</tt>)
-
# * <tt>Firm#clients<<</tt>
-
# * <tt>Firm#clients.delete</tt>
-
# * <tt>Firm#clients.destroy</tt>
-
# * <tt>Firm#clients=</tt>
-
# * <tt>Firm#client_ids</tt>
-
# * <tt>Firm#client_ids=</tt>
-
# * <tt>Firm#clients.clear</tt>
-
# * <tt>Firm#clients.empty?</tt> (similar to <tt>firm.clients.size == 0</tt>)
-
# * <tt>Firm#clients.size</tt> (similar to <tt>Client.count "firm_id = #{id}"</tt>)
-
# * <tt>Firm#clients.find</tt> (similar to <tt>Client.where(firm_id: id).find(id)</tt>)
-
# * <tt>Firm#clients.exists?(name: 'ACME')</tt> (similar to <tt>Client.exists?(name: 'ACME', firm_id: firm.id)</tt>)
-
# * <tt>Firm#clients.build</tt> (similar to <tt>Client.new("firm_id" => id)</tt>)
-
# * <tt>Firm#clients.create</tt> (similar to <tt>c = Client.new("firm_id" => id); c.save; c</tt>)
-
# * <tt>Firm#clients.create!</tt> (similar to <tt>c = Client.new("firm_id" => id); c.save!</tt>)
-
# The declaration can also include an options hash to specialize the behavior of the association.
-
#
-
# === Options
-
# [:class_name]
-
# Specify the class name of the association. Use it only if that name can't be inferred
-
# from the association name. So <tt>has_many :products</tt> will by default be linked
-
# to the Product class, but if the real class name is SpecialProduct, you'll have to
-
# specify it with this option.
-
# [:foreign_key]
-
# Specify the foreign key used for the association. By default this is guessed to be the name
-
# of this class in lower-case and "_id" suffixed. So a Person class that makes a +has_many+
-
# association will use "person_id" as the default <tt>:foreign_key</tt>.
-
# [:primary_key]
-
# Specify the method that returns the primary key used for the association. By default this is +id+.
-
# [:dependent]
-
# Controls what happens to the associated objects when
-
# their owner is destroyed. Note that these are implemented as
-
# callbacks, and Rails executes callbacks in order. Therefore, other
-
# similar callbacks may affect the <tt>:dependent</tt> behavior, and the
-
# <tt>:dependent</tt> behavior may affect other callbacks.
-
#
-
# * <tt>:destroy</tt> causes all the associated objects to also be destroyed.
-
# * <tt>:delete_all</tt> causes all the associated objects to be deleted directly from the database (so callbacks will not be executed).
-
# * <tt>:nullify</tt> causes the foreign keys to be set to +NULL+. Callbacks are not executed.
-
# * <tt>:restrict_with_exception</tt> causes an exception to be raised if there are any associated records.
-
# * <tt>:restrict_with_error</tt> causes an error to be added to the owner if there are any associated objects.
-
#
-
# If using with the <tt>:through</tt> option, the association on the join model must be
-
# a +belongs_to+, and the records which get deleted are the join records, rather than
-
# the associated records.
-
# [:counter_cache]
-
# This option can be used to configure a custom named <tt>:counter_cache.</tt> You only need this option,
-
# when you customized the name of your <tt>:counter_cache</tt> on the <tt>belongs_to</tt> association.
-
# [:as]
-
# Specifies a polymorphic interface (See <tt>belongs_to</tt>).
-
# [:through]
-
# Specifies an association through which to perform the query. This can be any other type
-
# of association, including other <tt>:through</tt> associations. Options for <tt>:class_name</tt>,
-
# <tt>:primary_key</tt> and <tt>:foreign_key</tt> are ignored, as the association uses the
-
# source reflection.
-
#
-
# If the association on the join model is a +belongs_to+, the collection can be modified
-
# and the records on the <tt>:through</tt> model will be automatically created and removed
-
# as appropriate. Otherwise, the collection is read-only, so you should manipulate the
-
# <tt>:through</tt> association directly.
-
#
-
# If you are going to modify the association (rather than just read from it), then it is
-
# a good idea to set the <tt>:inverse_of</tt> option on the source association on the
-
# join model. This allows associated records to be built which will automatically create
-
# the appropriate join model records when they are saved. (See the 'Association Join Models'
-
# section above.)
-
# [:source]
-
# Specifies the source association name used by <tt>has_many :through</tt> queries.
-
# Only use it if the name cannot be inferred from the association.
-
# <tt>has_many :subscribers, through: :subscriptions</tt> will look for either <tt>:subscribers</tt> or
-
# <tt>:subscriber</tt> on Subscription, unless a <tt>:source</tt> is given.
-
# [:source_type]
-
# Specifies type of the source association used by <tt>has_many :through</tt> queries where the source
-
# association is a polymorphic +belongs_to+.
-
# [:validate]
-
# If +false+, don't validate the associated objects when saving the parent object. true by default.
-
# [:autosave]
-
# If true, always save the associated objects or destroy them if marked for destruction,
-
# when saving the parent object. If false, never save or destroy the associated objects.
-
# By default, only save associated objects that are new records. This option is implemented as a
-
# +before_save+ callback. Because callbacks are run in the order they are defined, associated objects
-
# may need to be explicitly saved in any user-defined +before_save+ callbacks.
-
#
-
# Note that <tt>accepts_nested_attributes_for</tt> sets <tt>:autosave</tt> to <tt>true</tt>.
-
# [:inverse_of]
-
# Specifies the name of the <tt>belongs_to</tt> association on the associated object
-
# that is the inverse of this <tt>has_many</tt> association. Does not work in combination
-
# with <tt>:through</tt> or <tt>:as</tt> options.
-
# See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
-
#
-
# Option examples:
-
# has_many :comments, -> { order "posted_on" }
-
# has_many :comments, -> { includes :author }
-
# has_many :people, -> { where("deleted = 0").order("name") }, class_name: "Person"
-
# has_many :tracks, -> { order "position" }, dependent: :destroy
-
# has_many :comments, dependent: :nullify
-
# has_many :tags, as: :taggable
-
# has_many :reports, -> { readonly }
-
# has_many :subscribers, through: :subscriptions, source: :user
-
1
def has_many(name, scope = nil, options = {}, &extension)
-
11
reflection = Builder::HasMany.build(self, name, scope, options, &extension)
-
11
Reflection.add_reflection self, name, reflection
-
end
-
-
# Specifies a one-to-one association with another class. This method should only be used
-
# if the other class contains the foreign key. If the current class contains the foreign key,
-
# then you should use +belongs_to+ instead. See also ActiveRecord::Associations::ClassMethods's overview
-
# on when to use +has_one+ and when to use +belongs_to+.
-
#
-
# The following methods for retrieval and query of a single associated object will be added:
-
#
-
# [association(force_reload = false)]
-
# Returns the associated object. +nil+ is returned if none is found.
-
# [association=(associate)]
-
# Assigns the associate object, extracts the primary key, sets it as the foreign key,
-
# and saves the associate object. To avoid database inconsistencies, permanently deletes an existing
-
# associated object when assigning a new one, even if the new one isn't saved to database.
-
# [build_association(attributes = {})]
-
# Returns a new object of the associated type that has been instantiated
-
# with +attributes+ and linked to this object through a foreign key, but has not
-
# yet been saved.
-
# [create_association(attributes = {})]
-
# Returns a new object of the associated type that has been instantiated
-
# with +attributes+, linked to this object through a foreign key, and that
-
# has already been saved (if it passed the validation).
-
# [create_association!(attributes = {})]
-
# Does the same as <tt>create_association</tt>, but raises <tt>ActiveRecord::RecordInvalid</tt>
-
# if the record is invalid.
-
#
-
# (+association+ is replaced with the symbol passed as the first argument, so
-
# <tt>has_one :manager</tt> would add among others <tt>manager.nil?</tt>.)
-
#
-
# === Example
-
#
-
# An Account class declares <tt>has_one :beneficiary</tt>, which will add:
-
# * <tt>Account#beneficiary</tt> (similar to <tt>Beneficiary.where(account_id: id).first</tt>)
-
# * <tt>Account#beneficiary=(beneficiary)</tt> (similar to <tt>beneficiary.account_id = account.id; beneficiary.save</tt>)
-
# * <tt>Account#build_beneficiary</tt> (similar to <tt>Beneficiary.new("account_id" => id)</tt>)
-
# * <tt>Account#create_beneficiary</tt> (similar to <tt>b = Beneficiary.new("account_id" => id); b.save; b</tt>)
-
# * <tt>Account#create_beneficiary!</tt> (similar to <tt>b = Beneficiary.new("account_id" => id); b.save!; b</tt>)
-
#
-
# === Options
-
#
-
# The declaration can also include an options hash to specialize the behavior of the association.
-
#
-
# Options are:
-
# [:class_name]
-
# Specify the class name of the association. Use it only if that name can't be inferred
-
# from the association name. So <tt>has_one :manager</tt> will by default be linked to the Manager class, but
-
# if the real class name is Person, you'll have to specify it with this option.
-
# [:dependent]
-
# Controls what happens to the associated object when
-
# its owner is destroyed:
-
#
-
# * <tt>:destroy</tt> causes the associated object to also be destroyed
-
# * <tt>:delete</tt> causes the associated object to be deleted directly from the database (so callbacks will not execute)
-
# * <tt>:nullify</tt> causes the foreign key to be set to +NULL+. Callbacks are not executed.
-
# * <tt>:restrict_with_exception</tt> causes an exception to be raised if there is an associated record
-
# * <tt>:restrict_with_error</tt> causes an error to be added to the owner if there is an associated object
-
# [:foreign_key]
-
# Specify the foreign key used for the association. By default this is guessed to be the name
-
# of this class in lower-case and "_id" suffixed. So a Person class that makes a +has_one+ association
-
# will use "person_id" as the default <tt>:foreign_key</tt>.
-
# [:primary_key]
-
# Specify the method that returns the primary key used for the association. By default this is +id+.
-
# [:as]
-
# Specifies a polymorphic interface (See <tt>belongs_to</tt>).
-
# [:through]
-
# Specifies a Join Model through which to perform the query. Options for <tt>:class_name</tt>,
-
# <tt>:primary_key</tt>, and <tt>:foreign_key</tt> are ignored, as the association uses the
-
# source reflection. You can only use a <tt>:through</tt> query through a <tt>has_one</tt>
-
# or <tt>belongs_to</tt> association on the join model.
-
# [:source]
-
# Specifies the source association name used by <tt>has_one :through</tt> queries.
-
# Only use it if the name cannot be inferred from the association.
-
# <tt>has_one :favorite, through: :favorites</tt> will look for a
-
# <tt>:favorite</tt> on Favorite, unless a <tt>:source</tt> is given.
-
# [:source_type]
-
# Specifies type of the source association used by <tt>has_one :through</tt> queries where the source
-
# association is a polymorphic +belongs_to+.
-
# [:validate]
-
# If +false+, don't validate the associated object when saving the parent object. +false+ by default.
-
# [:autosave]
-
# If true, always save the associated object or destroy it if marked for destruction,
-
# when saving the parent object. If false, never save or destroy the associated object.
-
# By default, only save the associated object if it's a new record.
-
#
-
# Note that <tt>accepts_nested_attributes_for</tt> sets <tt>:autosave</tt> to <tt>true</tt>.
-
# [:inverse_of]
-
# Specifies the name of the <tt>belongs_to</tt> association on the associated object
-
# that is the inverse of this <tt>has_one</tt> association. Does not work in combination
-
# with <tt>:through</tt> or <tt>:as</tt> options.
-
# See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
-
#
-
# Option examples:
-
# has_one :credit_card, dependent: :destroy # destroys the associated credit card
-
# has_one :credit_card, dependent: :nullify # updates the associated records foreign
-
# # key value to NULL rather than destroying it
-
# has_one :last_comment, -> { order 'posted_on' }, class_name: "Comment"
-
# has_one :project_manager, -> { where role: 'project_manager' }, class_name: "Person"
-
# has_one :attachment, as: :attachable
-
# has_one :boss, readonly: :true
-
# has_one :club, through: :membership
-
# has_one :primary_address, -> { where primary: true }, through: :addressables, source: :addressable
-
1
def has_one(name, scope = nil, options = {})
-
reflection = Builder::HasOne.build(self, name, scope, options)
-
Reflection.add_reflection self, name, reflection
-
end
-
-
# Specifies a one-to-one association with another class. This method should only be used
-
# if this class contains the foreign key. If the other class contains the foreign key,
-
# then you should use +has_one+ instead. See also ActiveRecord::Associations::ClassMethods's overview
-
# on when to use +has_one+ and when to use +belongs_to+.
-
#
-
# Methods will be added for retrieval and query for a single associated object, for which
-
# this object holds an id:
-
#
-
# [association(force_reload = false)]
-
# Returns the associated object. +nil+ is returned if none is found.
-
# [association=(associate)]
-
# Assigns the associate object, extracts the primary key, and sets it as the foreign key.
-
# [build_association(attributes = {})]
-
# Returns a new object of the associated type that has been instantiated
-
# with +attributes+ and linked to this object through a foreign key, but has not yet been saved.
-
# [create_association(attributes = {})]
-
# Returns a new object of the associated type that has been instantiated
-
# with +attributes+, linked to this object through a foreign key, and that
-
# has already been saved (if it passed the validation).
-
# [create_association!(attributes = {})]
-
# Does the same as <tt>create_association</tt>, but raises <tt>ActiveRecord::RecordInvalid</tt>
-
# if the record is invalid.
-
#
-
# (+association+ is replaced with the symbol passed as the first argument, so
-
# <tt>belongs_to :author</tt> would add among others <tt>author.nil?</tt>.)
-
#
-
# === Example
-
#
-
# A Post class declares <tt>belongs_to :author</tt>, which will add:
-
# * <tt>Post#author</tt> (similar to <tt>Author.find(author_id)</tt>)
-
# * <tt>Post#author=(author)</tt> (similar to <tt>post.author_id = author.id</tt>)
-
# * <tt>Post#build_author</tt> (similar to <tt>post.author = Author.new</tt>)
-
# * <tt>Post#create_author</tt> (similar to <tt>post.author = Author.new; post.author.save; post.author</tt>)
-
# * <tt>Post#create_author!</tt> (similar to <tt>post.author = Author.new; post.author.save!; post.author</tt>)
-
# The declaration can also include an options hash to specialize the behavior of the association.
-
#
-
# === Options
-
#
-
# [:class_name]
-
# Specify the class name of the association. Use it only if that name can't be inferred
-
# from the association name. So <tt>belongs_to :author</tt> will by default be linked to the Author class, but
-
# if the real class name is Person, you'll have to specify it with this option.
-
# [:foreign_key]
-
# Specify the foreign key used for the association. By default this is guessed to be the name
-
# of the association with an "_id" suffix. So a class that defines a <tt>belongs_to :person</tt>
-
# association will use "person_id" as the default <tt>:foreign_key</tt>. Similarly,
-
# <tt>belongs_to :favorite_person, class_name: "Person"</tt> will use a foreign key
-
# of "favorite_person_id".
-
# [:foreign_type]
-
# Specify the column used to store the associated object's type, if this is a polymorphic
-
# association. By default this is guessed to be the name of the association with a "_type"
-
# suffix. So a class that defines a <tt>belongs_to :taggable, polymorphic: true</tt>
-
# association will use "taggable_type" as the default <tt>:foreign_type</tt>.
-
# [:primary_key]
-
# Specify the method that returns the primary key of associated object used for the association.
-
# By default this is id.
-
# [:dependent]
-
# If set to <tt>:destroy</tt>, the associated object is destroyed when this object is. If set to
-
# <tt>:delete</tt>, the associated object is deleted *without* calling its destroy method.
-
# This option should not be specified when <tt>belongs_to</tt> is used in conjunction with
-
# a <tt>has_many</tt> relationship on another class because of the potential to leave
-
# orphaned records behind.
-
# [:counter_cache]
-
# Caches the number of belonging objects on the associate class through the use of +increment_counter+
-
# and +decrement_counter+. The counter cache is incremented when an object of this
-
# class is created and decremented when it's destroyed. This requires that a column
-
# named <tt>#{table_name}_count</tt> (such as +comments_count+ for a belonging Comment class)
-
# is used on the associate class (such as a Post class) - that is the migration for
-
# <tt>#{table_name}_count</tt> is created on the associate class (such that <tt>Post.comments_count</tt> will
-
# return the count cached, see note below). You can also specify a custom counter
-
# cache column by providing a column name instead of a +true+/+false+ value to this
-
# option (e.g., <tt>counter_cache: :my_custom_counter</tt>.)
-
# Note: Specifying a counter cache will add it to that model's list of readonly attributes
-
# using +attr_readonly+.
-
# [:polymorphic]
-
# Specify this association is a polymorphic association by passing +true+.
-
# Note: If you've enabled the counter cache, then you may want to add the counter cache attribute
-
# to the +attr_readonly+ list in the associated classes (e.g. <tt>class Post; attr_readonly :comments_count; end</tt>).
-
# [:validate]
-
# If +false+, don't validate the associated objects when saving the parent object. +false+ by default.
-
# [:autosave]
-
# If true, always save the associated object or destroy it if marked for destruction, when
-
# saving the parent object.
-
# If false, never save or destroy the associated object.
-
# By default, only save the associated object if it's a new record.
-
#
-
# Note that <tt>accepts_nested_attributes_for</tt> sets <tt>:autosave</tt> to <tt>true</tt>.
-
# [:touch]
-
# If true, the associated object will be touched (the updated_at/on attributes set to now)
-
# when this record is either saved or destroyed. If you specify a symbol, that attribute
-
# will be updated with the current time in addition to the updated_at/on attribute.
-
# [:inverse_of]
-
# Specifies the name of the <tt>has_one</tt> or <tt>has_many</tt> association on the associated
-
# object that is the inverse of this <tt>belongs_to</tt> association. Does not work in
-
# combination with the <tt>:polymorphic</tt> options.
-
# See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
-
#
-
# Option examples:
-
# belongs_to :firm, foreign_key: "client_of"
-
# belongs_to :person, primary_key: "name", foreign_key: "person_name"
-
# belongs_to :author, class_name: "Person", foreign_key: "author_id"
-
# belongs_to :valid_coupon, ->(o) { where "discounts > ?", o.payments_count },
-
# class_name: "Coupon", foreign_key: "coupon_id"
-
# belongs_to :attachable, polymorphic: true
-
# belongs_to :project, readonly: true
-
# belongs_to :post, counter_cache: true
-
# belongs_to :company, touch: true
-
# belongs_to :company, touch: :employees_last_updated_at
-
1
def belongs_to(name, scope = nil, options = {})
-
3
reflection = Builder::BelongsTo.build(self, name, scope, options)
-
3
Reflection.add_reflection self, name, reflection
-
end
-
-
# Specifies a many-to-many relationship with another class. This associates two classes via an
-
# intermediate join table. Unless the join table is explicitly specified as an option, it is
-
# guessed using the lexical order of the class names. So a join between Developer and Project
-
# will give the default join table name of "developers_projects" because "D" precedes "P" alphabetically.
-
# Note that this precedence is calculated using the <tt><</tt> operator for String. This
-
# means that if the strings are of different lengths, and the strings are equal when compared
-
# up to the shortest length, then the longer string is considered of higher
-
# lexical precedence than the shorter one. For example, one would expect the tables "paper_boxes" and "papers"
-
# to generate a join table name of "papers_paper_boxes" because of the length of the name "paper_boxes",
-
# but it in fact generates a join table name of "paper_boxes_papers". Be aware of this caveat, and use the
-
# custom <tt>:join_table</tt> option if you need to.
-
# If your tables share a common prefix, it will only appear once at the beginning. For example,
-
# the tables "catalog_categories" and "catalog_products" generate a join table name of "catalog_categories_products".
-
#
-
# The join table should not have a primary key or a model associated with it. You must manually generate the
-
# join table with a migration such as this:
-
#
-
# class CreateDevelopersProjectsJoinTable < ActiveRecord::Migration
-
# def change
-
# create_table :developers_projects, id: false do |t|
-
# t.integer :developer_id
-
# t.integer :project_id
-
# end
-
# end
-
# end
-
#
-
# It's also a good idea to add indexes to each of those columns to speed up the joins process.
-
# However, in MySQL it is advised to add a compound index for both of the columns as MySQL only
-
# uses one index per table during the lookup.
-
#
-
# Adds the following methods for retrieval and query:
-
#
-
# [collection(force_reload = false)]
-
# Returns an array of all the associated objects.
-
# An empty array is returned if none are found.
-
# [collection<<(object, ...)]
-
# Adds one or more objects to the collection by creating associations in the join table
-
# (<tt>collection.push</tt> and <tt>collection.concat</tt> are aliases to this method).
-
# Note that this operation instantly fires update SQL without waiting for the save or update call on the
-
# parent object, unless the parent object is a new record.
-
# [collection.delete(object, ...)]
-
# Removes one or more objects from the collection by removing their associations from the join table.
-
# This does not destroy the objects.
-
# [collection.destroy(object, ...)]
-
# Removes one or more objects from the collection by running destroy on each association in the join table, overriding any dependent option.
-
# This does not destroy the objects.
-
# [collection=objects]
-
# Replaces the collection's content by deleting and adding objects as appropriate.
-
# [collection_singular_ids]
-
# Returns an array of the associated objects' ids.
-
# [collection_singular_ids=ids]
-
# Replace the collection by the objects identified by the primary keys in +ids+.
-
# [collection.clear]
-
# Removes every object from the collection. This does not destroy the objects.
-
# [collection.empty?]
-
# Returns +true+ if there are no associated objects.
-
# [collection.size]
-
# Returns the number of associated objects.
-
# [collection.find(id)]
-
# Finds an associated object responding to the +id+ and that
-
# meets the condition that it has to be associated with this object.
-
# Uses the same rules as <tt>ActiveRecord::Base.find</tt>.
-
# [collection.exists?(...)]
-
# Checks whether an associated object with the given conditions exists.
-
# Uses the same rules as <tt>ActiveRecord::Base.exists?</tt>.
-
# [collection.build(attributes = {})]
-
# Returns a new object of the collection type that has been instantiated
-
# with +attributes+ and linked to this object through the join table, but has not yet been saved.
-
# [collection.create(attributes = {})]
-
# Returns a new object of the collection type that has been instantiated
-
# with +attributes+, linked to this object through the join table, and that has already been
-
# saved (if it passed the validation).
-
#
-
# (+collection+ is replaced with the symbol passed as the first argument, so
-
# <tt>has_and_belongs_to_many :categories</tt> would add among others <tt>categories.empty?</tt>.)
-
#
-
# === Example
-
#
-
# A Developer class declares <tt>has_and_belongs_to_many :projects</tt>, which will add:
-
# * <tt>Developer#projects</tt>
-
# * <tt>Developer#projects<<</tt>
-
# * <tt>Developer#projects.delete</tt>
-
# * <tt>Developer#projects.destroy</tt>
-
# * <tt>Developer#projects=</tt>
-
# * <tt>Developer#project_ids</tt>
-
# * <tt>Developer#project_ids=</tt>
-
# * <tt>Developer#projects.clear</tt>
-
# * <tt>Developer#projects.empty?</tt>
-
# * <tt>Developer#projects.size</tt>
-
# * <tt>Developer#projects.find(id)</tt>
-
# * <tt>Developer#projects.exists?(...)</tt>
-
# * <tt>Developer#projects.build</tt> (similar to <tt>Project.new("developer_id" => id)</tt>)
-
# * <tt>Developer#projects.create</tt> (similar to <tt>c = Project.new("developer_id" => id); c.save; c</tt>)
-
# The declaration may include an options hash to specialize the behavior of the association.
-
#
-
# === Options
-
#
-
# [:class_name]
-
# Specify the class name of the association. Use it only if that name can't be inferred
-
# from the association name. So <tt>has_and_belongs_to_many :projects</tt> will by default be linked to the
-
# Project class, but if the real class name is SuperProject, you'll have to specify it with this option.
-
# [:join_table]
-
# Specify the name of the join table if the default based on lexical order isn't what you want.
-
# <b>WARNING:</b> If you're overwriting the table name of either class, the +table_name+ method
-
# MUST be declared underneath any +has_and_belongs_to_many+ declaration in order to work.
-
# [:foreign_key]
-
# Specify the foreign key used for the association. By default this is guessed to be the name
-
# of this class in lower-case and "_id" suffixed. So a Person class that makes
-
# a +has_and_belongs_to_many+ association to Project will use "person_id" as the
-
# default <tt>:foreign_key</tt>.
-
# [:association_foreign_key]
-
# Specify the foreign key used for the association on the receiving side of the association.
-
# By default this is guessed to be the name of the associated class in lower-case and "_id" suffixed.
-
# So if a Person class makes a +has_and_belongs_to_many+ association to Project,
-
# the association will use "project_id" as the default <tt>:association_foreign_key</tt>.
-
# [:readonly]
-
# If true, all the associated objects are readonly through the association.
-
# [:validate]
-
# If +false+, don't validate the associated objects when saving the parent object. +true+ by default.
-
# [:autosave]
-
# If true, always save the associated objects or destroy them if marked for destruction, when
-
# saving the parent object.
-
# If false, never save or destroy the associated objects.
-
# By default, only save associated objects that are new records.
-
#
-
# Note that <tt>accepts_nested_attributes_for</tt> sets <tt>:autosave</tt> to <tt>true</tt>.
-
#
-
# Option examples:
-
# has_and_belongs_to_many :projects
-
# has_and_belongs_to_many :projects, -> { includes :milestones, :manager }
-
# has_and_belongs_to_many :nations, class_name: "Country"
-
# has_and_belongs_to_many :categories, join_table: "prods_cats"
-
# has_and_belongs_to_many :categories, -> { readonly }
-
1
def has_and_belongs_to_many(name, scope = nil, options = {}, &extension)
-
if scope.is_a?(Hash)
-
options = scope
-
scope = nil
-
end
-
-
habtm_reflection = ActiveRecord::Reflection::AssociationReflection.new(:has_and_belongs_to_many, name, scope, options, self)
-
-
builder = Builder::HasAndBelongsToMany.new name, self, options
-
-
join_model = builder.through_model
-
-
# FIXME: we should move this to the internal constants. Also people
-
# should never directly access this constant so I'm not happy about
-
# setting it.
-
const_set join_model.name, join_model
-
-
middle_reflection = builder.middle_reflection join_model
-
-
Builder::HasMany.define_callbacks self, middle_reflection
-
Reflection.add_reflection self, middle_reflection.name, middle_reflection
-
middle_reflection.parent_reflection = [name.to_sym, habtm_reflection]
-
-
include Module.new {
-
class_eval <<-RUBY, __FILE__, __LINE__ + 1
-
def destroy_associations
-
association(:#{middle_reflection.name}).delete_all(:delete_all)
-
association(:#{name}).reset
-
super
-
end
-
RUBY
-
}
-
-
hm_options = {}
-
hm_options[:through] = middle_reflection.name
-
hm_options[:source] = join_model.right_reflection.name
-
-
[:before_add, :after_add, :before_remove, :after_remove, :autosave, :validate, :join_table].each do |k|
-
hm_options[k] = options[k] if options.key? k
-
end
-
-
has_many name, scope, hm_options, &extension
-
self._reflections[name.to_sym].parent_reflection = [name.to_sym, habtm_reflection]
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/string/conversions'
-
-
1
module ActiveRecord
-
1
module Associations
-
# Keeps track of table aliases for ActiveRecord::Associations::ClassMethods::JoinDependency and
-
# ActiveRecord::Associations::ThroughAssociationScope
-
1
class AliasTracker # :nodoc:
-
1
attr_reader :aliases, :connection
-
-
1
def self.empty(connection)
-
8
new connection, Hash.new(0)
-
end
-
-
1
def self.create(connection, table_joins)
-
2
if table_joins.empty?
-
empty connection
-
else
-
2
aliases = Hash.new { |h,k|
-
2
h[k] = initial_count_for(connection, k, table_joins)
-
}
-
2
new connection, aliases
-
end
-
end
-
-
1
def self.initial_count_for(connection, name, table_joins)
-
# quoted_name should be downcased as some database adapters (Oracle) return quoted name in uppercase
-
2
quoted_name = connection.quote_table_name(name).downcase
-
-
2
counts = table_joins.map do |join|
-
2
if join.is_a?(Arel::Nodes::StringJoin)
-
# Table names + table aliases
-
join.left.downcase.scan(
-
/join(?:\s+\w+)?\s+(\S+\s+)?#{quoted_name}\son/
-
).size
-
elsif join.respond_to? :left
-
2
join.left.table_name == name ? 1 : 0
-
else
-
# this branch is reached by two tests:
-
#
-
# activerecord/test/cases/associations/cascaded_eager_loading_test.rb:37
-
# with :posts
-
#
-
# activerecord/test/cases/associations/eager_test.rb:1133
-
# with :comments
-
#
-
0
-
end
-
end
-
-
2
counts.sum
-
end
-
-
# table_joins is an array of arel joins which might conflict with the aliases we assign here
-
1
def initialize(connection, aliases)
-
10
@aliases = aliases
-
10
@connection = connection
-
end
-
-
1
def aliased_table_for(table_name, aliased_name)
-
12
table_alias = aliased_name_for(table_name, aliased_name)
-
-
12
if table_alias == table_name
-
12
Arel::Table.new(table_name)
-
else
-
Arel::Table.new(table_name).alias(table_alias)
-
end
-
end
-
-
1
def aliased_name_for(table_name, aliased_name)
-
14
if aliases[table_name].zero?
-
# If it's zero, we can have our table_name
-
14
aliases[table_name] = 1
-
14
table_name
-
else
-
# Otherwise, we need to use an alias
-
aliased_name = connection.table_alias_for(aliased_name)
-
-
# Update the count
-
aliases[aliased_name] += 1
-
-
if aliases[aliased_name] > 1
-
"#{truncate(aliased_name)}_#{aliases[aliased_name]}"
-
else
-
aliased_name
-
end
-
end
-
end
-
-
1
private
-
-
1
def truncate(name)
-
name.slice(0, connection.table_alias_length - 2)
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/array/wrap'
-
-
1
module ActiveRecord
-
1
module Associations
-
# = Active Record Associations
-
#
-
# This is the root class of all associations ('+ Foo' signifies an included module Foo):
-
#
-
# Association
-
# SingularAssociation
-
# HasOneAssociation
-
# HasOneThroughAssociation + ThroughAssociation
-
# BelongsToAssociation
-
# BelongsToPolymorphicAssociation
-
# CollectionAssociation
-
# HasManyAssociation
-
# HasManyThroughAssociation + ThroughAssociation
-
1
class Association #:nodoc:
-
1
attr_reader :owner, :target, :reflection
-
1
attr_accessor :inversed
-
-
1
delegate :options, :to => :reflection
-
-
1
def initialize(owner, reflection)
-
12
reflection.check_validity!
-
-
12
@owner, @reflection = owner, reflection
-
-
12
reset
-
12
reset_scope
-
end
-
-
# Returns the name of the table of the associated class:
-
#
-
# post.comments.aliased_table_name # => "comments"
-
#
-
1
def aliased_table_name
-
klass.table_name
-
end
-
-
# Resets the \loaded flag to +false+ and sets the \target to +nil+.
-
1
def reset
-
12
@loaded = false
-
12
@target = nil
-
12
@stale_state = nil
-
12
@inversed = false
-
end
-
-
# Reloads the \target and returns +self+ on success.
-
1
def reload
-
reset
-
reset_scope
-
load_target
-
self unless target.nil?
-
end
-
-
# Has the \target been already \loaded?
-
1
def loaded?
-
20
@loaded
-
end
-
-
# Asserts the \target has been loaded setting the \loaded flag to +true+.
-
1
def loaded!
-
12
@loaded = true
-
12
@stale_state = stale_state
-
12
@inversed = false
-
end
-
-
# The target is stale if the target no longer points to the record(s) that the
-
# relevant foreign_key(s) refers to. If stale, the association accessor method
-
# on the owner will reload the target. It's up to subclasses to implement the
-
# stale_state method if relevant.
-
#
-
# Note that if the target has not been loaded, it is not considered stale.
-
1
def stale_target?
-
10
!inversed && loaded? && @stale_state != stale_state
-
end
-
-
# Sets the target of this association to <tt>\target</tt>, and the \loaded flag to +true+.
-
1
def target=(target)
-
6
@target = target
-
6
loaded!
-
end
-
-
1
def scope
-
16
target_scope.merge(association_scope)
-
end
-
-
# The scope for this association.
-
#
-
# Note that the association_scope is merged into the target_scope only when the
-
# scope method is called. This is because at that point the call may be surrounded
-
# by scope.scoping { ... } or with_scope { ... } etc, which affects the scope which
-
# actually gets built.
-
1
def association_scope
-
20
if klass
-
20
@association_scope ||= AssociationScope.scope(self, klass.connection)
-
end
-
end
-
-
1
def reset_scope
-
12
@association_scope = nil
-
end
-
-
# Set the inverse association, if possible
-
1
def set_inverse_instance(record)
-
12
if invertible_for?(record)
-
4
inverse = record.association(inverse_reflection_for(record).name)
-
4
inverse.target = owner
-
4
inverse.inversed = true
-
end
-
12
record
-
end
-
-
# Returns the class of the target. belongs_to polymorphic overrides this to look at the
-
# polymorphic_type field on the owner.
-
1
def klass
-
94
reflection.klass
-
end
-
-
# Can be overridden (i.e. in ThroughAssociation) to merge in other scopes (i.e. the
-
# through association's scope)
-
1
def target_scope
-
16
AssociationRelation.create(klass, klass.arel_table, self).merge!(klass.all)
-
end
-
-
# Loads the \target if needed and returns it.
-
#
-
# This method is abstract in the sense that it relies on +find_target+,
-
# which is expected to be provided by descendants.
-
#
-
# If the \target is already \loaded it is just returned. Thus, you can call
-
# +load_target+ unconditionally to get the \target.
-
#
-
# ActiveRecord::RecordNotFound is rescued within the method, and it is
-
# not reraised. The proxy is \reset and +nil+ is the return value.
-
1
def load_target
-
4
@target = find_target if (@stale_state && stale_target?) || find_target?
-
-
4
loaded! unless loaded?
-
4
target
-
rescue ActiveRecord::RecordNotFound
-
reset
-
end
-
-
1
def interpolate(sql, record = nil)
-
if sql.respond_to?(:to_proc)
-
owner.instance_exec(record, &sql)
-
else
-
sql
-
end
-
end
-
-
# We can't dump @reflection since it contains the scope proc
-
1
def marshal_dump
-
ivars = (instance_variables - [:@reflection]).map { |name| [name, instance_variable_get(name)] }
-
[@reflection.name, ivars]
-
end
-
-
1
def marshal_load(data)
-
reflection_name, ivars = data
-
ivars.each { |name, val| instance_variable_set(name, val) }
-
@reflection = @owner.class._reflect_on_association(reflection_name)
-
end
-
-
1
def initialize_attributes(record) #:nodoc:
-
4
skip_assign = [reflection.foreign_key, reflection.type].compact
-
4
attributes = create_scope.except(*(record.changed - skip_assign))
-
4
record.assign_attributes(attributes)
-
4
set_inverse_instance(record)
-
end
-
-
1
private
-
-
1
def find_target?
-
4
!loaded? && (!owner.new_record? || foreign_key_present?) && klass
-
end
-
-
1
def creation_attributes
-
attributes = {}
-
-
if (reflection.macro == :has_one || reflection.macro == :has_many) && !options[:through]
-
attributes[reflection.foreign_key] = owner[reflection.active_record_primary_key]
-
-
if reflection.options[:as]
-
attributes[reflection.type] = owner.class.base_class.name
-
end
-
end
-
-
attributes
-
end
-
-
# Sets the owner attributes on the given record
-
1
def set_owner_attributes(record)
-
creation_attributes.each { |key, value| record[key] = value }
-
end
-
-
# Returns true if there is a foreign key present on the owner which
-
# references the target. This is used to determine whether we can load
-
# the target if the owner is currently a new record (and therefore
-
# without a key). If the owner is a new record then foreign_key must
-
# be present in order to load target.
-
#
-
# Currently implemented by belongs_to (vanilla and polymorphic) and
-
# has_one/has_many :through associations which go through a belongs_to.
-
1
def foreign_key_present?
-
false
-
end
-
-
# Raises ActiveRecord::AssociationTypeMismatch unless +record+ is of
-
# the kind of the class of the associated objects. Meant to be used as
-
# a sanity check when you are about to assign an associated record.
-
1
def raise_on_type_mismatch!(record)
-
2
unless record.is_a?(reflection.klass) || record.is_a?(reflection.class_name.constantize)
-
message = "#{reflection.class_name}(##{reflection.klass.object_id}) expected, got #{record.class}(##{record.class.object_id})"
-
raise ActiveRecord::AssociationTypeMismatch, message
-
end
-
end
-
-
# Can be redefined by subclasses, notably polymorphic belongs_to
-
# The record parameter is necessary to support polymorphic inverses as we must check for
-
# the association in the specific class of the record.
-
1
def inverse_reflection_for(record)
-
10
reflection.inverse_of
-
end
-
-
# Returns true if inverse association on the given record needs to be set.
-
# This method is redefined by subclasses.
-
1
def invertible_for?(record)
-
4
foreign_key_for?(record) && inverse_reflection_for(record)
-
end
-
-
# Returns true if record contains the foreign_key
-
1
def foreign_key_for?(record)
-
4
record.has_attribute?(reflection.foreign_key)
-
end
-
-
# This should be implemented to return the values of the relevant key(s) on the owner,
-
# so that when stale_state is different from the value stored on the last find_target,
-
# the target is stale.
-
#
-
# This is only relevant to certain associations, which is why it returns nil by default.
-
1
def stale_state
-
end
-
-
1
def build_record(attributes)
-
4
reflection.build_association(attributes) do |record|
-
4
initialize_attributes(record)
-
end
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
module Associations
-
1
class AssociationScope #:nodoc:
-
1
INSTANCE = new
-
-
1
def self.scope(association, connection)
-
8
INSTANCE.scope association, connection
-
end
-
-
1
def scope(association, connection)
-
8
klass = association.klass
-
8
reflection = association.reflection
-
8
scope = klass.unscoped
-
8
owner = association.owner
-
8
alias_tracker = AliasTracker.empty connection
-
-
8
scope.extending! Array(reflection.options[:extend])
-
8
add_constraints(scope, owner, klass, reflection, alias_tracker)
-
end
-
-
1
def join_type
-
4
Arel::Nodes::InnerJoin
-
end
-
-
1
private
-
-
1
def construct_tables(chain, klass, refl, alias_tracker)
-
8
chain.map do |reflection|
-
12
alias_tracker.aliased_table_for(
-
table_name_for(reflection, klass, refl),
-
table_alias_for(reflection, refl, reflection != refl)
-
)
-
end
-
end
-
-
1
def table_alias_for(reflection, refl, join = false)
-
12
name = "#{reflection.plural_name}_#{alias_suffix(refl)}"
-
12
name << "_join" if join
-
12
name
-
end
-
-
1
def join(table, constraint)
-
4
table.create_join(table, table.create_on(constraint), join_type)
-
end
-
-
1
def column_for(table_name, column_name, alias_tracker)
-
8
columns = alias_tracker.connection.schema_cache.columns_hash(table_name)
-
8
columns[column_name]
-
end
-
-
1
def bind_value(scope, column, value, alias_tracker)
-
8
substitute = alias_tracker.connection.substitute_at(
-
column, scope.bind_values.length)
-
8
scope.bind_values += [[column, value]]
-
8
substitute
-
end
-
-
1
def bind(scope, table_name, column_name, value, tracker)
-
8
column = column_for table_name, column_name, tracker
-
8
bind_value scope, column, value, tracker
-
end
-
-
1
def add_constraints(scope, owner, assoc_klass, refl, tracker)
-
8
chain = refl.chain
-
8
scope_chain = refl.scope_chain
-
-
8
tables = construct_tables(chain, assoc_klass, refl, tracker)
-
-
8
chain.each_with_index do |reflection, i|
-
12
table, foreign_table = tables.shift, tables.first
-
-
12
if reflection.source_macro == :belongs_to
-
4
if reflection.options[:polymorphic]
-
key = reflection.association_primary_key(assoc_klass)
-
else
-
4
key = reflection.association_primary_key
-
end
-
-
4
foreign_key = reflection.foreign_key
-
else
-
8
key = reflection.foreign_key
-
8
foreign_key = reflection.active_record_primary_key
-
end
-
-
12
if reflection == chain.last
-
8
bind_val = bind scope, table.table_name, key.to_s, owner[foreign_key], tracker
-
8
scope = scope.where(table[key].eq(bind_val))
-
-
8
if reflection.type
-
value = owner.class.base_class.name
-
bind_val = bind scope, table.table_name, reflection.type.to_s, value, tracker
-
scope = scope.where(table[reflection.type].eq(bind_val))
-
end
-
else
-
4
constraint = table[key].eq(foreign_table[foreign_key])
-
-
4
if reflection.type
-
value = chain[i + 1].klass.base_class.name
-
bind_val = bind scope, table.table_name, reflection.type.to_s, value, tracker
-
scope = scope.where(table[reflection.type].eq(bind_val))
-
end
-
-
4
scope = scope.joins(join(foreign_table, constraint))
-
end
-
-
12
is_first_chain = i == 0
-
12
klass = is_first_chain ? assoc_klass : reflection.klass
-
-
# Exclude the scope of the association itself, because that
-
# was already merged in the #scope method.
-
12
scope_chain[i].each do |scope_chain_item|
-
item = eval_scope(klass, scope_chain_item, owner)
-
-
if scope_chain_item == refl.scope
-
scope.merge! item.except(:where, :includes, :bind)
-
end
-
-
if is_first_chain
-
scope.includes! item.includes_values
-
end
-
-
scope.where_values += item.where_values
-
scope.order_values |= item.order_values
-
end
-
end
-
-
8
scope
-
end
-
-
1
def alias_suffix(refl)
-
12
refl.name
-
end
-
-
1
def table_name_for(reflection, klass, refl)
-
12
if reflection == refl
-
# If this is a polymorphic belongs_to, we want to get the klass from the
-
# association because it depends on the polymorphic_type attribute of
-
# the owner
-
8
klass.table_name
-
else
-
4
reflection.table_name
-
end
-
end
-
-
1
def eval_scope(klass, scope, owner)
-
if scope.is_a?(Relation)
-
scope
-
else
-
klass.unscoped.instance_exec(owner, &scope)
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/module/attribute_accessors'
-
-
# This is the parent Association class which defines the variables
-
# used by all associations.
-
#
-
# The hierarchy is defined as follows:
-
# Association
-
# - SingularAssociation
-
# - BelongsToAssociation
-
# - HasOneAssociation
-
# - CollectionAssociation
-
# - HasManyAssociation
-
-
1
module ActiveRecord::Associations::Builder
-
1
class Association #:nodoc:
-
1
class << self
-
1
attr_accessor :extensions
-
# TODO: This class accessor is needed to make activerecord-deprecated_finders work.
-
# We can move it to a constant in 5.0.
-
1
attr_accessor :valid_options
-
end
-
1
self.extensions = []
-
-
1
self.valid_options = [:class_name, :class, :foreign_key, :validate]
-
-
1
attr_reader :name, :scope, :options
-
-
1
def self.build(model, name, scope, options, &block)
-
14
if model.dangerous_attribute_method?(name)
-
raise ArgumentError, "You tried to define an association named #{name} on the model #{model.name}, but " \
-
"this will conflict with a method #{name} already defined by Active Record. " \
-
"Please choose a different association name."
-
end
-
-
14
builder = create_builder model, name, scope, options, &block
-
14
reflection = builder.build(model)
-
14
define_accessors model, reflection
-
14
define_callbacks model, reflection
-
14
builder.define_extensions model
-
14
reflection
-
end
-
-
1
def self.create_builder(model, name, scope, options, &block)
-
14
raise ArgumentError, "association names must be a Symbol" unless name.kind_of?(Symbol)
-
-
14
new(model, name, scope, options, &block)
-
end
-
-
1
def initialize(model, name, scope, options)
-
# TODO: Move this to create_builder as soon we drop support to activerecord-deprecated_finders.
-
14
if scope.is_a?(Hash)
-
4
options = scope
-
4
scope = nil
-
end
-
-
# TODO: Remove this model argument as soon we drop support to activerecord-deprecated_finders.
-
14
@name = name
-
14
@scope = scope
-
14
@options = options
-
-
14
validate_options
-
-
14
if scope && scope.arity == 0
-
@scope = proc { instance_exec(&scope) }
-
end
-
end
-
-
1
def build(model)
-
14
ActiveRecord::Reflection.create(macro, name, scope, options, model)
-
end
-
-
1
def macro
-
raise NotImplementedError
-
end
-
-
1
def valid_options
-
14
Association.valid_options + Association.extensions.flat_map(&:valid_options)
-
end
-
-
1
def validate_options
-
14
options.assert_valid_keys(valid_options)
-
end
-
-
1
def define_extensions(model)
-
end
-
-
1
def self.define_callbacks(model, reflection)
-
14
add_before_destroy_callbacks(model, reflection) if reflection.options[:dependent]
-
14
Association.extensions.each do |extension|
-
14
extension.build model, reflection
-
end
-
end
-
-
# Defines the setter and getter methods for the association
-
# class Post < ActiveRecord::Base
-
# has_many :comments
-
# end
-
#
-
# Post.first.comments and Post.first.comments= methods are defined by this method...
-
1
def self.define_accessors(model, reflection)
-
14
mixin = model.generated_association_methods
-
14
name = reflection.name
-
14
define_readers(mixin, name)
-
14
define_writers(mixin, name)
-
end
-
-
1
def self.define_readers(mixin, name)
-
14
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
-
def #{name}(*args)
-
association(:#{name}).reader(*args)
-
end
-
CODE
-
end
-
-
1
def self.define_writers(mixin, name)
-
14
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
-
def #{name}=(value)
-
association(:#{name}).writer(value)
-
end
-
CODE
-
end
-
-
1
def self.valid_dependent_options
-
raise NotImplementedError
-
end
-
-
1
private
-
-
1
def self.add_before_destroy_callbacks(model, reflection)
-
unless valid_dependent_options.include? reflection.options[:dependent]
-
raise ArgumentError, "The :dependent option must be one of #{valid_dependent_options}, but is :#{reflection.options[:dependent]}"
-
end
-
-
name = reflection.name
-
model.before_destroy lambda { |o| o.association(name).handle_dependency }
-
end
-
end
-
end
-
1
module ActiveRecord::Associations::Builder
-
1
class BelongsTo < SingularAssociation #:nodoc:
-
1
def macro
-
3
:belongs_to
-
end
-
-
1
def valid_options
-
3
super + [:foreign_type, :polymorphic, :touch, :counter_cache]
-
end
-
-
1
def self.valid_dependent_options
-
[:destroy, :delete]
-
end
-
-
1
def self.define_callbacks(model, reflection)
-
3
super
-
3
add_counter_cache_callbacks(model, reflection) if reflection.options[:counter_cache]
-
3
add_touch_callbacks(model, reflection) if reflection.options[:touch]
-
end
-
-
1
def self.define_accessors(mixin, reflection)
-
3
super
-
3
add_counter_cache_methods mixin
-
end
-
-
1
private
-
-
1
def self.add_counter_cache_methods(mixin)
-
3
return if mixin.method_defined? :belongs_to_counter_cache_after_create
-
-
2
mixin.class_eval do
-
2
def belongs_to_counter_cache_after_create(reflection)
-
if record = send(reflection.name)
-
cache_column = reflection.counter_cache_column
-
record.class.increment_counter(cache_column, record.id)
-
@_after_create_counter_called = true
-
end
-
end
-
-
2
def belongs_to_counter_cache_before_destroy(reflection)
-
foreign_key = reflection.foreign_key.to_sym
-
unless destroyed_by_association && destroyed_by_association.foreign_key.to_sym == foreign_key
-
record = send reflection.name
-
if record && !self.destroyed?
-
cache_column = reflection.counter_cache_column
-
record.class.decrement_counter(cache_column, record.id)
-
end
-
end
-
end
-
-
2
def belongs_to_counter_cache_after_update(reflection)
-
foreign_key = reflection.foreign_key
-
cache_column = reflection.counter_cache_column
-
-
if (@_after_create_counter_called ||= false)
-
@_after_create_counter_called = false
-
elsif attribute_changed?(foreign_key) && !new_record? && reflection.constructable?
-
model = reflection.klass
-
foreign_key_was = attribute_was foreign_key
-
foreign_key = attribute foreign_key
-
-
if foreign_key && model.respond_to?(:increment_counter)
-
model.increment_counter(cache_column, foreign_key)
-
end
-
if foreign_key_was && model.respond_to?(:decrement_counter)
-
model.decrement_counter(cache_column, foreign_key_was)
-
end
-
end
-
end
-
end
-
end
-
-
1
def self.add_counter_cache_callbacks(model, reflection)
-
cache_column = reflection.counter_cache_column
-
-
model.after_create lambda { |record|
-
record.belongs_to_counter_cache_after_create(reflection)
-
}
-
-
model.before_destroy lambda { |record|
-
record.belongs_to_counter_cache_before_destroy(reflection)
-
}
-
-
model.after_update lambda { |record|
-
record.belongs_to_counter_cache_after_update(reflection)
-
}
-
-
klass = reflection.class_name.safe_constantize
-
klass.attr_readonly cache_column if klass && klass.respond_to?(:attr_readonly)
-
end
-
-
1
def self.touch_record(o, foreign_key, name, touch) # :nodoc:
-
old_foreign_id = o.changed_attributes[foreign_key]
-
-
if old_foreign_id
-
association = o.association(name)
-
reflection = association.reflection
-
if reflection.polymorphic?
-
klass = o.public_send("#{reflection.foreign_type}_was").constantize
-
else
-
klass = association.klass
-
end
-
old_record = klass.find_by(klass.primary_key => old_foreign_id)
-
-
if old_record
-
if touch != true
-
old_record.touch touch
-
else
-
old_record.touch
-
end
-
end
-
end
-
-
record = o.send name
-
if record && record.persisted?
-
if touch != true
-
record.touch touch
-
else
-
record.touch
-
end
-
end
-
end
-
-
1
def self.add_touch_callbacks(model, reflection)
-
foreign_key = reflection.foreign_key
-
n = reflection.name
-
touch = reflection.options[:touch]
-
-
callback = lambda { |record|
-
BelongsTo.touch_record(record, foreign_key, n, touch)
-
}
-
-
model.after_save callback
-
model.after_touch callback
-
model.after_destroy callback
-
end
-
end
-
end
-
# This class is inherited by the has_many and has_many_and_belongs_to_many association classes
-
-
1
require 'active_record/associations'
-
-
1
module ActiveRecord::Associations::Builder
-
1
class CollectionAssociation < Association #:nodoc:
-
-
1
CALLBACKS = [:before_add, :after_add, :before_remove, :after_remove]
-
-
1
def valid_options
-
super + [:table_name, :before_add,
-
11
:after_add, :before_remove, :after_remove, :extend]
-
end
-
-
1
attr_reader :block_extension
-
-
1
def initialize(model, name, scope, options)
-
11
super
-
11
@mod = nil
-
11
if block_given?
-
@mod = Module.new(&Proc.new)
-
@scope = wrap_scope @scope, @mod
-
end
-
end
-
-
1
def self.define_callbacks(model, reflection)
-
11
super
-
11
name = reflection.name
-
11
options = reflection.options
-
11
CALLBACKS.each { |callback_name|
-
44
define_callback(model, callback_name, name, options)
-
}
-
end
-
-
1
def define_extensions(model)
-
11
if @mod
-
extension_module_name = "#{model.name.demodulize}#{name.to_s.camelize}AssociationExtension"
-
model.parent.const_set(extension_module_name, @mod)
-
end
-
end
-
-
1
def self.define_callback(model, callback_name, name, options)
-
44
full_callback_name = "#{callback_name}_for_#{name}"
-
-
# TODO : why do i need method_defined? I think its because of the inheritance chain
-
44
model.class_attribute full_callback_name unless model.method_defined?(full_callback_name)
-
44
callbacks = Array(options[callback_name.to_sym]).map do |callback|
-
case callback
-
when Symbol
-
->(method, owner, record) { owner.send(callback, record) }
-
when Proc
-
->(method, owner, record) { callback.call(owner, record) }
-
else
-
->(method, owner, record) { callback.send(method, owner, record) }
-
end
-
end
-
44
model.send "#{full_callback_name}=", callbacks
-
end
-
-
# Defines the setter and getter methods for the collection_singular_ids.
-
1
def self.define_readers(mixin, name)
-
11
super
-
-
11
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
-
def #{name.to_s.singularize}_ids
-
association(:#{name}).ids_reader
-
end
-
CODE
-
end
-
-
1
def self.define_writers(mixin, name)
-
11
super
-
-
11
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
-
def #{name.to_s.singularize}_ids=(ids)
-
association(:#{name}).ids_writer(ids)
-
end
-
CODE
-
end
-
-
1
private
-
-
1
def wrap_scope(scope, mod)
-
if scope
-
proc { |owner| instance_exec(owner, &scope).extending(mod) }
-
else
-
proc { extending(mod) }
-
end
-
end
-
end
-
end
-
1
module ActiveRecord::Associations::Builder
-
1
class HasMany < CollectionAssociation #:nodoc:
-
1
def macro
-
11
:has_many
-
end
-
-
1
def valid_options
-
11
super + [:primary_key, :dependent, :as, :through, :source, :source_type, :inverse_of, :counter_cache, :join_table]
-
end
-
-
1
def self.valid_dependent_options
-
[:destroy, :delete_all, :nullify, :restrict_with_error, :restrict_with_exception]
-
end
-
end
-
end
-
# This class is inherited by the has_one and belongs_to association classes
-
-
1
module ActiveRecord::Associations::Builder
-
1
class SingularAssociation < Association #:nodoc:
-
1
def valid_options
-
3
super + [:remote, :dependent, :primary_key, :inverse_of]
-
end
-
-
1
def self.define_accessors(model, reflection)
-
3
super
-
3
define_constructors(model.generated_association_methods, reflection.name) if reflection.constructable?
-
end
-
-
# Defines the (build|create)_association methods for belongs_to or has_one association
-
1
def self.define_constructors(mixin, name)
-
3
mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
-
def build_#{name}(*args, &block)
-
association(:#{name}).build(*args, &block)
-
end
-
-
def create_#{name}(*args, &block)
-
association(:#{name}).create(*args, &block)
-
end
-
-
def create_#{name}!(*args, &block)
-
association(:#{name}).create!(*args, &block)
-
end
-
CODE
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
module Associations
-
# = Active Record Association Collection
-
#
-
# CollectionAssociation is an abstract class that provides common stuff to
-
# ease the implementation of association proxies that represent
-
# collections. See the class hierarchy in Association.
-
#
-
# CollectionAssociation:
-
# HasManyAssociation => has_many
-
# HasManyThroughAssociation + ThroughAssociation => has_many :through
-
#
-
# CollectionAssociation class provides common methods to the collections
-
# defined by +has_and_belongs_to_many+, +has_many+ or +has_many+ with
-
# +:through association+ option.
-
#
-
# You need to be careful with assumptions regarding the target: The proxy
-
# does not fetch records from the database until it needs them, but new
-
# ones created with +build+ are added to the target. So, the target may be
-
# non-empty and still lack children waiting to be read from the database.
-
# If you look directly to the database you cannot assume that's the entire
-
# collection because new records may have been added to the target, etc.
-
#
-
# If you need to work on all current children, new and existing records,
-
# +load_target+ and the +loaded+ flag are your friends.
-
1
class CollectionAssociation < Association #:nodoc:
-
-
# Implements the reader method, e.g. foo.items for Foo.has_many :items
-
1
def reader(force_reload = false)
-
6
if force_reload
-
klass.uncached { reload }
-
elsif stale_target?
-
reload
-
end
-
-
6
@proxy ||= CollectionProxy.create(klass, self)
-
end
-
-
# Implements the writer method, e.g. foo.items= for Foo.has_many :items
-
1
def writer(records)
-
replace(records)
-
end
-
-
# Implements the ids reader method, e.g. foo.item_ids for Foo.has_many :items
-
1
def ids_reader
-
if loaded?
-
load_target.map do |record|
-
record.send(reflection.association_primary_key)
-
end
-
else
-
column = "#{reflection.quoted_table_name}.#{reflection.association_primary_key}"
-
scope.pluck(column)
-
end
-
end
-
-
# Implements the ids writer method, e.g. foo.item_ids= for Foo.has_many :items
-
1
def ids_writer(ids)
-
pk_column = reflection.primary_key_column
-
ids = Array(ids).reject { |id| id.blank? }
-
ids.map! { |i| pk_column.type_cast(i) }
-
replace(klass.find(ids).index_by { |r| r.id }.values_at(*ids))
-
end
-
-
1
def reset
-
8
super
-
8
@target = []
-
end
-
-
1
def select(*fields)
-
if block_given?
-
load_target.select.each { |e| yield e }
-
else
-
scope.select(*fields)
-
end
-
end
-
-
1
def find(*args)
-
if block_given?
-
load_target.find(*args) { |*block_args| yield(*block_args) }
-
else
-
if options[:inverse_of] && loaded?
-
args_flatten = args.flatten
-
raise RecordNotFound, "Couldn't find #{scope.klass.name} without an ID" if args_flatten.blank?
-
result = find_by_scan(*args)
-
-
result_size = Array(result).size
-
if !result || result_size != args_flatten.size
-
scope.raise_record_not_found_exception!(args_flatten, result_size, args_flatten.size)
-
else
-
result
-
end
-
else
-
scope.find(*args)
-
end
-
end
-
end
-
-
1
def first(*args)
-
first_nth_or_last(:first, *args)
-
end
-
-
1
def second(*args)
-
first_nth_or_last(:second, *args)
-
end
-
-
1
def third(*args)
-
first_nth_or_last(:third, *args)
-
end
-
-
1
def fourth(*args)
-
first_nth_or_last(:fourth, *args)
-
end
-
-
1
def fifth(*args)
-
first_nth_or_last(:fifth, *args)
-
end
-
-
1
def forty_two(*args)
-
first_nth_or_last(:forty_two, *args)
-
end
-
-
1
def last(*args)
-
first_nth_or_last(:last, *args)
-
end
-
-
1
def build(attributes = {}, &block)
-
2
if attributes.is_a?(Array)
-
attributes.collect { |attr| build(attr, &block) }
-
else
-
2
add_to_target(build_record(attributes)) do |record|
-
2
yield(record) if block_given?
-
end
-
end
-
end
-
-
1
def create(attributes = {}, &block)
-
2
_create_record(attributes, &block)
-
end
-
-
1
def create!(attributes = {}, &block)
-
_create_record(attributes, true, &block)
-
end
-
-
# Add +records+ to this association. Returns +self+ so method calls may
-
# be chained. Since << flattens its argument list and inserts each record,
-
# +push+ and +concat+ behave identically.
-
1
def concat(*records)
-
load_target if owner.new_record?
-
-
if owner.new_record?
-
concat_records(records)
-
else
-
transaction { concat_records(records) }
-
end
-
end
-
-
# Starts a transaction in the association class's database connection.
-
#
-
# class Author < ActiveRecord::Base
-
# has_many :books
-
# end
-
#
-
# Author.first.books.transaction do
-
# # same effect as calling Book.transaction
-
# end
-
1
def transaction(*args)
-
2
reflection.klass.transaction(*args) do
-
2
yield
-
end
-
end
-
-
# Removes all records from the association without calling callbacks
-
# on the associated records. It honors the `:dependent` option. However
-
# if the `:dependent` value is `:destroy` then in that case the `:delete_all`
-
# deletion strategy for the association is applied.
-
#
-
# You can force a particular deletion strategy by passing a parameter.
-
#
-
# Example:
-
#
-
# @author.books.delete_all(:nullify)
-
# @author.books.delete_all(:delete_all)
-
#
-
# See delete for more info.
-
1
def delete_all(dependent = nil)
-
if dependent.present? && ![:nullify, :delete_all].include?(dependent)
-
raise ArgumentError, "Valid values are :nullify or :delete_all"
-
end
-
-
dependent = if dependent.present?
-
dependent
-
elsif options[:dependent] == :destroy
-
:delete_all
-
else
-
options[:dependent]
-
end
-
-
delete(:all, dependent: dependent).tap do
-
reset
-
loaded!
-
end
-
end
-
-
# Destroy all the records from this association.
-
#
-
# See destroy for more info.
-
1
def destroy_all
-
destroy(load_target).tap do
-
reset
-
loaded!
-
end
-
end
-
-
# Count all records using SQL. Construct options and pass them with
-
# scope to the target class's +count+.
-
1
def count(column_name = nil, count_options = {})
-
# TODO: Remove count_options argument as soon we remove support to
-
# activerecord-deprecated_finders.
-
column_name, count_options = nil, column_name if column_name.is_a?(Hash)
-
-
relation = scope
-
if association_scope.distinct_value
-
# This is needed because 'SELECT count(DISTINCT *)..' is not valid SQL.
-
column_name ||= reflection.klass.primary_key
-
relation = relation.distinct
-
end
-
-
value = relation.count(column_name)
-
-
limit = options[:limit]
-
offset = options[:offset]
-
-
if limit || offset
-
[ [value - offset.to_i, 0].max, limit.to_i ].min
-
else
-
value
-
end
-
end
-
-
# Removes +records+ from this association calling +before_remove+ and
-
# +after_remove+ callbacks.
-
#
-
# This method is abstract in the sense that +delete_records+ has to be
-
# provided by descendants. Note this method does not imply the records
-
# are actually removed from the database, that depends precisely on
-
# +delete_records+. They are in any case removed from the collection.
-
1
def delete(*records)
-
_options = records.extract_options!
-
dependent = _options[:dependent] || options[:dependent]
-
-
if records.first == :all
-
if (loaded? || dependent == :destroy) && dependent != :delete_all
-
delete_or_destroy(load_target, dependent)
-
else
-
delete_records(:all, dependent)
-
end
-
else
-
records = find(records) if records.any? { |record| record.kind_of?(Fixnum) || record.kind_of?(String) }
-
delete_or_destroy(records, dependent)
-
end
-
end
-
-
# Deletes the +records+ and removes them from this association calling
-
# +before_remove+ , +after_remove+ , +before_destroy+ and +after_destroy+ callbacks.
-
#
-
# Note that this method removes records from the database ignoring the
-
# +:dependent+ option.
-
1
def destroy(*records)
-
records = find(records) if records.any? { |record| record.kind_of?(Fixnum) || record.kind_of?(String) }
-
delete_or_destroy(records, :destroy)
-
end
-
-
# Returns the size of the collection by executing a SELECT COUNT(*)
-
# query if the collection hasn't been loaded, and calling
-
# <tt>collection.size</tt> if it has.
-
#
-
# If the collection has been already loaded +size+ and +length+ are
-
# equivalent. If not and you are going to need the records anyway
-
# +length+ will take one less query. Otherwise +size+ is more efficient.
-
#
-
# This method is abstract in the sense that it relies on
-
# +count_records+, which is a method descendants have to provide.
-
1
def size
-
if !find_target? || loaded?
-
if association_scope.distinct_value
-
target.uniq.size
-
else
-
target.size
-
end
-
elsif !loaded? && !association_scope.group_values.empty?
-
load_target.size
-
elsif !loaded? && !association_scope.distinct_value && target.is_a?(Array)
-
unsaved_records = target.select { |r| r.new_record? }
-
unsaved_records.size + count_records
-
else
-
count_records
-
end
-
end
-
-
# Returns the size of the collection calling +size+ on the target.
-
#
-
# If the collection has been already loaded +length+ and +size+ are
-
# equivalent. If not and you are going to need the records anyway this
-
# method will take one less query. Otherwise +size+ is more efficient.
-
1
def length
-
load_target.size
-
end
-
-
# Returns true if the collection is empty.
-
#
-
# If the collection has been loaded
-
# it is equivalent to <tt>collection.size.zero?</tt>. If the
-
# collection has not been loaded, it is equivalent to
-
# <tt>collection.exists?</tt>. If the collection has not already been
-
# loaded and you are going to fetch the records anyway it is better to
-
# check <tt>collection.length.zero?</tt>.
-
1
def empty?
-
if loaded?
-
size.zero?
-
else
-
@target.blank? && !scope.exists?
-
end
-
end
-
-
# Returns true if the collections is not empty.
-
# Equivalent to +!collection.empty?+.
-
1
def any?
-
if block_given?
-
load_target.any? { |*block_args| yield(*block_args) }
-
else
-
!empty?
-
end
-
end
-
-
# Returns true if the collection has more than 1 record.
-
# Equivalent to +collection.size > 1+.
-
1
def many?
-
if block_given?
-
load_target.many? { |*block_args| yield(*block_args) }
-
else
-
size > 1
-
end
-
end
-
-
1
def distinct
-
seen = {}
-
load_target.find_all do |record|
-
seen[record.id] = true unless seen.key?(record.id)
-
end
-
end
-
1
alias uniq distinct
-
-
# Replace this collection with +other_array+. This will perform a diff
-
# and delete/add only records that have changed.
-
1
def replace(other_array)
-
other_array.each { |val| raise_on_type_mismatch!(val) }
-
original_target = load_target.dup
-
-
if owner.new_record?
-
replace_records(other_array, original_target)
-
else
-
transaction { replace_records(other_array, original_target) }
-
end
-
end
-
-
1
def include?(record)
-
if record.is_a?(reflection.klass)
-
if record.new_record?
-
include_in_memory?(record)
-
else
-
loaded? ? target.include?(record) : scope.exists?(record)
-
end
-
else
-
false
-
end
-
end
-
-
1
def load_target
-
4
if find_target?
-
4
@target = merge_target_lists(find_target, target)
-
end
-
-
4
loaded!
-
4
target
-
end
-
-
1
def add_to_target(record, skip_callbacks = false)
-
4
callback(:before_add, record) unless skip_callbacks
-
4
yield(record) if block_given?
-
-
4
if association_scope.distinct_value && index = @target.index(record)
-
@target[index] = record
-
else
-
4
@target << record
-
end
-
-
4
callback(:after_add, record) unless skip_callbacks
-
4
set_inverse_instance(record)
-
-
4
record
-
end
-
-
1
def scope(opts = {})
-
16
scope = super()
-
16
scope.none! if opts.fetch(:nullify, true) && null_scope?
-
16
scope
-
end
-
-
1
def null_scope?
-
10
owner.new_record? && !foreign_key_present?
-
end
-
-
1
private
-
-
1
def find_target
-
2
records = scope.to_a
-
2
records.each { |record| set_inverse_instance(record) }
-
2
records
-
end
-
-
# We have some records loaded from the database (persisted) and some that are
-
# in-memory (memory). The same record may be represented in the persisted array
-
# and in the memory array.
-
#
-
# So the task of this method is to merge them according to the following rules:
-
#
-
# * The final array must not have duplicates
-
# * The order of the persisted array is to be preserved
-
# * Any changes made to attributes on objects in the memory array are to be preserved
-
# * Otherwise, attributes should have the value found in the database
-
1
def merge_target_lists(persisted, memory)
-
4
return persisted if memory.empty?
-
return memory if persisted.empty?
-
-
persisted.map! do |record|
-
if mem_record = memory.delete(record)
-
-
((record.attribute_names & mem_record.attribute_names) - mem_record.changes.keys).each do |name|
-
mem_record[name] = record[name]
-
end
-
-
mem_record
-
else
-
record
-
end
-
end
-
-
persisted + memory
-
end
-
-
1
def _create_record(attributes, raise = false, &block)
-
2
unless owner.persisted?
-
raise ActiveRecord::RecordNotSaved, "You cannot call create unless the parent is saved"
-
end
-
-
2
if attributes.is_a?(Array)
-
attributes.collect { |attr| _create_record(attr, raise, &block) }
-
else
-
2
transaction do
-
2
add_to_target(build_record(attributes)) do |record|
-
2
yield(record) if block_given?
-
2
insert_record(record, true, raise)
-
end
-
end
-
end
-
end
-
-
# Do the relevant stuff to insert the given record into the association collection.
-
1
def insert_record(record, validate = true, raise = false)
-
raise NotImplementedError
-
end
-
-
1
def create_scope
-
4
scope.scope_for_create.stringify_keys
-
end
-
-
1
def delete_or_destroy(records, method)
-
records = records.flatten
-
records.each { |record| raise_on_type_mismatch!(record) }
-
existing_records = records.reject { |r| r.new_record? }
-
-
if existing_records.empty?
-
remove_records(existing_records, records, method)
-
else
-
transaction { remove_records(existing_records, records, method) }
-
end
-
end
-
-
1
def remove_records(existing_records, records, method)
-
records.each { |record| callback(:before_remove, record) }
-
-
delete_records(existing_records, method) if existing_records.any?
-
records.each { |record| target.delete(record) }
-
-
records.each { |record| callback(:after_remove, record) }
-
end
-
-
# Delete the given records from the association, using one of the methods :destroy,
-
# :delete_all or :nullify (or nil, in which case a default is used).
-
1
def delete_records(records, method)
-
raise NotImplementedError
-
end
-
-
1
def replace_records(new_target, original_target)
-
delete(target - new_target)
-
-
unless concat(new_target - target)
-
@target = original_target
-
raise RecordNotSaved, "Failed to replace #{reflection.name} because one or more of the " \
-
"new records could not be saved."
-
end
-
-
target
-
end
-
-
1
def concat_records(records, should_raise = false)
-
result = true
-
-
records.flatten.each do |record|
-
raise_on_type_mismatch!(record)
-
add_to_target(record) do |rec|
-
result &&= insert_record(rec, true, should_raise) unless owner.new_record?
-
end
-
end
-
-
result && records
-
end
-
-
1
def callback(method, record)
-
8
callbacks_for(method).each do |callback|
-
callback.call(method, owner, record)
-
end
-
end
-
-
1
def callbacks_for(callback_name)
-
8
full_callback_name = "#{callback_name}_for_#{reflection.name}"
-
8
owner.class.send(full_callback_name)
-
end
-
-
# Should we deal with assoc.first or assoc.last by issuing an independent query to
-
# the database, or by getting the target, and then taking the first/last item from that?
-
#
-
# If the args is just a non-empty options hash, go to the database.
-
#
-
# Otherwise, go to the database only if none of the following are true:
-
# * target already loaded
-
# * owner is new record
-
# * target contains new or changed record(s)
-
1
def fetch_first_nth_or_last_using_find?(args)
-
if args.first.is_a?(Hash)
-
true
-
else
-
!(loaded? ||
-
owner.new_record? ||
-
target.any? { |record| record.new_record? || record.changed? })
-
end
-
end
-
-
1
def include_in_memory?(record)
-
if reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
-
assoc = owner.association(reflection.through_reflection.name)
-
assoc.reader.any? { |source|
-
target = source.send(reflection.source_reflection.name)
-
target.respond_to?(:include?) ? target.include?(record) : target == record
-
} || target.include?(record)
-
else
-
target.include?(record)
-
end
-
end
-
-
# If the :inverse_of option has been
-
# specified, then #find scans the entire collection.
-
1
def find_by_scan(*args)
-
expects_array = args.first.kind_of?(Array)
-
ids = args.flatten.compact.map{ |arg| arg.to_s }.uniq
-
-
if ids.size == 1
-
id = ids.first
-
record = load_target.detect { |r| id == r.id.to_s }
-
expects_array ? [ record ] : record
-
else
-
load_target.select { |r| ids.include?(r.id.to_s) }
-
end
-
end
-
-
# Fetches the first/last using SQL if possible, otherwise from the target array.
-
1
def first_nth_or_last(type, *args)
-
args.shift if args.first.is_a?(Hash) && args.first.empty?
-
-
collection = fetch_first_nth_or_last_using_find?(args) ? scope : load_target
-
collection.send(type, *args).tap do |record|
-
set_inverse_instance record if record.is_a? ActiveRecord::Base
-
end
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
module Associations
-
# Association proxies in Active Record are middlemen between the object that
-
# holds the association, known as the <tt>@owner</tt>, and the actual associated
-
# object, known as the <tt>@target</tt>. The kind of association any proxy is
-
# about is available in <tt>@reflection</tt>. That's an instance of the class
-
# ActiveRecord::Reflection::AssociationReflection.
-
#
-
# For example, given
-
#
-
# class Blog < ActiveRecord::Base
-
# has_many :posts
-
# end
-
#
-
# blog = Blog.first
-
#
-
# the association proxy in <tt>blog.posts</tt> has the object in +blog+ as
-
# <tt>@owner</tt>, the collection of its posts as <tt>@target</tt>, and
-
# the <tt>@reflection</tt> object represents a <tt>:has_many</tt> macro.
-
#
-
# This class delegates unknown methods to <tt>@target</tt> via
-
# <tt>method_missing</tt>.
-
#
-
# The <tt>@target</tt> object is not \loaded until needed. For example,
-
#
-
# blog.posts.count
-
#
-
# is computed directly through SQL and does not trigger by itself the
-
# instantiation of the actual post records.
-
1
class CollectionProxy < Relation
-
1
delegate(*(ActiveRecord::Calculations.public_instance_methods - [:count]), to: :scope)
-
-
1
def initialize(klass, association) #:nodoc:
-
6
@association = association
-
6
super klass, klass.arel_table
-
6
merge! association.scope(nullify: false)
-
end
-
-
1
def target
-
@association.target
-
end
-
-
1
def load_target
-
4
@association.load_target
-
end
-
-
# Returns +true+ if the association has been loaded, otherwise +false+.
-
#
-
# person.pets.loaded? # => false
-
# person.pets
-
# person.pets.loaded? # => true
-
1
def loaded?
-
@association.loaded?
-
end
-
-
# Works in two ways.
-
#
-
# *First:* Specify a subset of fields to be selected from the result set.
-
#
-
# class Person < ActiveRecord::Base
-
# has_many :pets
-
# end
-
#
-
# person.pets
-
# # => [
-
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
-
# # #<Pet id: 2, name: "Spook", person_id: 1>,
-
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
-
# # ]
-
#
-
# person.pets.select(:name)
-
# # => [
-
# # #<Pet id: nil, name: "Fancy-Fancy">,
-
# # #<Pet id: nil, name: "Spook">,
-
# # #<Pet id: nil, name: "Choo-Choo">
-
# # ]
-
#
-
# person.pets.select(:id, :name )
-
# # => [
-
# # #<Pet id: 1, name: "Fancy-Fancy">,
-
# # #<Pet id: 2, name: "Spook">,
-
# # #<Pet id: 3, name: "Choo-Choo">
-
# # ]
-
#
-
# Be careful because this also means you're initializing a model
-
# object with only the fields that you've selected. If you attempt
-
# to access a field except +id+ that is not in the initialized record you'll
-
# receive:
-
#
-
# person.pets.select(:name).first.person_id
-
# # => ActiveModel::MissingAttributeError: missing attribute: person_id
-
#
-
# *Second:* You can pass a block so it can be used just like Array#select.
-
# This builds an array of objects from the database for the scope,
-
# converting them into an array and iterating through them using
-
# Array#select.
-
#
-
# person.pets.select { |pet| pet.name =~ /oo/ }
-
# # => [
-
# # #<Pet id: 2, name: "Spook", person_id: 1>,
-
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
-
# # ]
-
#
-
# person.pets.select(:name) { |pet| pet.name =~ /oo/ }
-
# # => [
-
# # #<Pet id: 2, name: "Spook">,
-
# # #<Pet id: 3, name: "Choo-Choo">
-
# # ]
-
1
def select(*fields, &block)
-
@association.select(*fields, &block)
-
end
-
-
# Finds an object in the collection responding to the +id+. Uses the same
-
# rules as <tt>ActiveRecord::Base.find</tt>. Returns <tt>ActiveRecord::RecordNotFound</tt>
-
# error if the object cannot be found.
-
#
-
# class Person < ActiveRecord::Base
-
# has_many :pets
-
# end
-
#
-
# person.pets
-
# # => [
-
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
-
# # #<Pet id: 2, name: "Spook", person_id: 1>,
-
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
-
# # ]
-
#
-
# person.pets.find(1) # => #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>
-
# person.pets.find(4) # => ActiveRecord::RecordNotFound: Couldn't find Pet with id=4
-
#
-
# person.pets.find(2) { |pet| pet.name.downcase! }
-
# # => #<Pet id: 2, name: "fancy-fancy", person_id: 1>
-
#
-
# person.pets.find(2, 3)
-
# # => [
-
# # #<Pet id: 2, name: "Spook", person_id: 1>,
-
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
-
# # ]
-
1
def find(*args, &block)
-
@association.find(*args, &block)
-
end
-
-
# Returns the first record, or the first +n+ records, from the collection.
-
# If the collection is empty, the first form returns +nil+, and the second
-
# form returns an empty array.
-
#
-
# class Person < ActiveRecord::Base
-
# has_many :pets
-
# end
-
#
-
# person.pets
-
# # => [
-
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
-
# # #<Pet id: 2, name: "Spook", person_id: 1>,
-
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
-
# # ]
-
#
-
# person.pets.first # => #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>
-
#
-
# person.pets.first(2)
-
# # => [
-
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
-
# # #<Pet id: 2, name: "Spook", person_id: 1>
-
# # ]
-
#
-
# another_person_without.pets # => []
-
# another_person_without.pets.first # => nil
-
# another_person_without.pets.first(3) # => []
-
1
def first(*args)
-
@association.first(*args)
-
end
-
-
# Same as +first+ except returns only the second record.
-
1
def second(*args)
-
@association.second(*args)
-
end
-
-
# Same as +first+ except returns only the third record.
-
1
def third(*args)
-
@association.third(*args)
-
end
-
-
# Same as +first+ except returns only the fourth record.
-
1
def fourth(*args)
-
@association.fourth(*args)
-
end
-
-
# Same as +first+ except returns only the fifth record.
-
1
def fifth(*args)
-
@association.fifth(*args)
-
end
-
-
# Same as +first+ except returns only the forty second record.
-
# Also known as accessing "the reddit".
-
1
def forty_two(*args)
-
@association.forty_two(*args)
-
end
-
-
# Returns the last record, or the last +n+ records, from the collection.
-
# If the collection is empty, the first form returns +nil+, and the second
-
# form returns an empty array.
-
#
-
# class Person < ActiveRecord::Base
-
# has_many :pets
-
# end
-
#
-
# person.pets
-
# # => [
-
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
-
# # #<Pet id: 2, name: "Spook", person_id: 1>,
-
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
-
# # ]
-
#
-
# person.pets.last # => #<Pet id: 3, name: "Choo-Choo", person_id: 1>
-
#
-
# person.pets.last(2)
-
# # => [
-
# # #<Pet id: 2, name: "Spook", person_id: 1>,
-
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
-
# # ]
-
#
-
# another_person_without.pets # => []
-
# another_person_without.pets.last # => nil
-
# another_person_without.pets.last(3) # => []
-
1
def last(*args)
-
@association.last(*args)
-
end
-
-
# Returns a new object of the collection type that has been instantiated
-
# with +attributes+ and linked to this object, but have not yet been saved.
-
# You can pass an array of attributes hashes, this will return an array
-
# with the new objects.
-
#
-
# class Person
-
# has_many :pets
-
# end
-
#
-
# person.pets.build
-
# # => #<Pet id: nil, name: nil, person_id: 1>
-
#
-
# person.pets.build(name: 'Fancy-Fancy')
-
# # => #<Pet id: nil, name: "Fancy-Fancy", person_id: 1>
-
#
-
# person.pets.build([{name: 'Spook'}, {name: 'Choo-Choo'}, {name: 'Brain'}])
-
# # => [
-
# # #<Pet id: nil, name: "Spook", person_id: 1>,
-
# # #<Pet id: nil, name: "Choo-Choo", person_id: 1>,
-
# # #<Pet id: nil, name: "Brain", person_id: 1>
-
# # ]
-
#
-
# person.pets.size # => 5 # size of the collection
-
# person.pets.count # => 0 # count from database
-
1
def build(attributes = {}, &block)
-
@association.build(attributes, &block)
-
end
-
1
alias_method :new, :build
-
-
# Returns a new object of the collection type that has been instantiated with
-
# attributes, linked to this object and that has already been saved (if it
-
# passes the validations).
-
#
-
# class Person
-
# has_many :pets
-
# end
-
#
-
# person.pets.create(name: 'Fancy-Fancy')
-
# # => #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>
-
#
-
# person.pets.create([{name: 'Spook'}, {name: 'Choo-Choo'}])
-
# # => [
-
# # #<Pet id: 2, name: "Spook", person_id: 1>,
-
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
-
# # ]
-
#
-
# person.pets.size # => 3
-
# person.pets.count # => 3
-
#
-
# person.pets.find(1, 2, 3)
-
# # => [
-
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
-
# # #<Pet id: 2, name: "Spook", person_id: 1>,
-
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
-
# # ]
-
1
def create(attributes = {}, &block)
-
2
@association.create(attributes, &block)
-
end
-
-
# Like +create+, except that if the record is invalid, raises an exception.
-
#
-
# class Person
-
# has_many :pets
-
# end
-
#
-
# class Pet
-
# validates :name, presence: true
-
# end
-
#
-
# person.pets.create!(name: nil)
-
# # => ActiveRecord::RecordInvalid: Validation failed: Name can't be blank
-
1
def create!(attributes = {}, &block)
-
@association.create!(attributes, &block)
-
end
-
-
# Add one or more records to the collection by setting their foreign keys
-
# to the association's primary key. Since << flattens its argument list and
-
# inserts each record, +push+ and +concat+ behave identically. Returns +self+
-
# so method calls may be chained.
-
#
-
# class Person < ActiveRecord::Base
-
# has_many :pets
-
# end
-
#
-
# person.pets.size # => 0
-
# person.pets.concat(Pet.new(name: 'Fancy-Fancy'))
-
# person.pets.concat(Pet.new(name: 'Spook'), Pet.new(name: 'Choo-Choo'))
-
# person.pets.size # => 3
-
#
-
# person.id # => 1
-
# person.pets
-
# # => [
-
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
-
# # #<Pet id: 2, name: "Spook", person_id: 1>,
-
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
-
# # ]
-
#
-
# person.pets.concat([Pet.new(name: 'Brain'), Pet.new(name: 'Benny')])
-
# person.pets.size # => 5
-
1
def concat(*records)
-
@association.concat(*records)
-
end
-
-
# Replaces this collection with +other_array+. This will perform a diff
-
# and delete/add only records that have changed.
-
#
-
# class Person < ActiveRecord::Base
-
# has_many :pets
-
# end
-
#
-
# person.pets
-
# # => [#<Pet id: 1, name: "Gorby", group: "cats", person_id: 1>]
-
#
-
# other_pets = [Pet.new(name: 'Puff', group: 'celebrities']
-
#
-
# person.pets.replace(other_pets)
-
#
-
# person.pets
-
# # => [#<Pet id: 2, name: "Puff", group: "celebrities", person_id: 1>]
-
#
-
# If the supplied array has an incorrect association type, it raises
-
# an <tt>ActiveRecord::AssociationTypeMismatch</tt> error:
-
#
-
# person.pets.replace(["doo", "ggie", "gaga"])
-
# # => ActiveRecord::AssociationTypeMismatch: Pet expected, got String
-
1
def replace(other_array)
-
@association.replace(other_array)
-
end
-
-
# Deletes all the records from the collection. For +has_many+ associations,
-
# the deletion is done according to the strategy specified by the <tt>:dependent</tt>
-
# option. Returns an array with the deleted records.
-
#
-
# If no <tt>:dependent</tt> option is given, then it will follow the
-
# default strategy. The default strategy is <tt>:nullify</tt>. This
-
# sets the foreign keys to <tt>NULL</tt>. For, +has_many+ <tt>:through</tt>,
-
# the default strategy is +delete_all+.
-
#
-
# class Person < ActiveRecord::Base
-
# has_many :pets # dependent: :nullify option by default
-
# end
-
#
-
# person.pets.size # => 3
-
# person.pets
-
# # => [
-
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
-
# # #<Pet id: 2, name: "Spook", person_id: 1>,
-
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
-
# # ]
-
#
-
# person.pets.delete_all
-
# # => [
-
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
-
# # #<Pet id: 2, name: "Spook", person_id: 1>,
-
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
-
# # ]
-
#
-
# person.pets.size # => 0
-
# person.pets # => []
-
#
-
# Pet.find(1, 2, 3)
-
# # => [
-
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: nil>,
-
# # #<Pet id: 2, name: "Spook", person_id: nil>,
-
# # #<Pet id: 3, name: "Choo-Choo", person_id: nil>
-
# # ]
-
#
-
# If it is set to <tt>:destroy</tt> all the objects from the collection
-
# are removed by calling their +destroy+ method. See +destroy+ for more
-
# information.
-
#
-
# class Person < ActiveRecord::Base
-
# has_many :pets, dependent: :destroy
-
# end
-
#
-
# person.pets.size # => 3
-
# person.pets
-
# # => [
-
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
-
# # #<Pet id: 2, name: "Spook", person_id: 1>,
-
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
-
# # ]
-
#
-
# person.pets.delete_all
-
# # => [
-
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
-
# # #<Pet id: 2, name: "Spook", person_id: 1>,
-
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
-
# # ]
-
#
-
# Pet.find(1, 2, 3)
-
# # => ActiveRecord::RecordNotFound
-
#
-
# If it is set to <tt>:delete_all</tt>, all the objects are deleted
-
# *without* calling their +destroy+ method.
-
#
-
# class Person < ActiveRecord::Base
-
# has_many :pets, dependent: :delete_all
-
# end
-
#
-
# person.pets.size # => 3
-
# person.pets
-
# # => [
-
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
-
# # #<Pet id: 2, name: "Spook", person_id: 1>,
-
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
-
# # ]
-
#
-
# person.pets.delete_all
-
# # => [
-
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
-
# # #<Pet id: 2, name: "Spook", person_id: 1>,
-
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
-
# # ]
-
#
-
# Pet.find(1, 2, 3)
-
# # => ActiveRecord::RecordNotFound
-
1
def delete_all(dependent = nil)
-
@association.delete_all(dependent)
-
end
-
-
# Deletes the records of the collection directly from the database
-
# ignoring the +:dependent+ option. It invokes +before_remove+,
-
# +after_remove+ , +before_destroy+ and +after_destroy+ callbacks.
-
#
-
# class Person < ActiveRecord::Base
-
# has_many :pets
-
# end
-
#
-
# person.pets.size # => 3
-
# person.pets
-
# # => [
-
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
-
# # #<Pet id: 2, name: "Spook", person_id: 1>,
-
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
-
# # ]
-
#
-
# person.pets.destroy_all
-
#
-
# person.pets.size # => 0
-
# person.pets # => []
-
#
-
# Pet.find(1) # => Couldn't find Pet with id=1
-
1
def destroy_all
-
@association.destroy_all
-
end
-
-
# Deletes the +records+ supplied and removes them from the collection. For
-
# +has_many+ associations, the deletion is done according to the strategy
-
# specified by the <tt>:dependent</tt> option. Returns an array with the
-
# deleted records.
-
#
-
# If no <tt>:dependent</tt> option is given, then it will follow the default
-
# strategy. The default strategy is <tt>:nullify</tt>. This sets the foreign
-
# keys to <tt>NULL</tt>. For, +has_many+ <tt>:through</tt>, the default
-
# strategy is +delete_all+.
-
#
-
# class Person < ActiveRecord::Base
-
# has_many :pets # dependent: :nullify option by default
-
# end
-
#
-
# person.pets.size # => 3
-
# person.pets
-
# # => [
-
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
-
# # #<Pet id: 2, name: "Spook", person_id: 1>,
-
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
-
# # ]
-
#
-
# person.pets.delete(Pet.find(1))
-
# # => [#<Pet id: 1, name: "Fancy-Fancy", person_id: 1>]
-
#
-
# person.pets.size # => 2
-
# person.pets
-
# # => [
-
# # #<Pet id: 2, name: "Spook", person_id: 1>,
-
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
-
# # ]
-
#
-
# Pet.find(1)
-
# # => #<Pet id: 1, name: "Fancy-Fancy", person_id: nil>
-
#
-
# If it is set to <tt>:destroy</tt> all the +records+ are removed by calling
-
# their +destroy+ method. See +destroy+ for more information.
-
#
-
# class Person < ActiveRecord::Base
-
# has_many :pets, dependent: :destroy
-
# end
-
#
-
# person.pets.size # => 3
-
# person.pets
-
# # => [
-
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
-
# # #<Pet id: 2, name: "Spook", person_id: 1>,
-
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
-
# # ]
-
#
-
# person.pets.delete(Pet.find(1), Pet.find(3))
-
# # => [
-
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
-
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
-
# # ]
-
#
-
# person.pets.size # => 1
-
# person.pets
-
# # => [#<Pet id: 2, name: "Spook", person_id: 1>]
-
#
-
# Pet.find(1, 3)
-
# # => ActiveRecord::RecordNotFound: Couldn't find all Pets with IDs (1, 3)
-
#
-
# If it is set to <tt>:delete_all</tt>, all the +records+ are deleted
-
# *without* calling their +destroy+ method.
-
#
-
# class Person < ActiveRecord::Base
-
# has_many :pets, dependent: :delete_all
-
# end
-
#
-
# person.pets.size # => 3
-
# person.pets
-
# # => [
-
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
-
# # #<Pet id: 2, name: "Spook", person_id: 1>,
-
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
-
# # ]
-
#
-
# person.pets.delete(Pet.find(1))
-
# # => [#<Pet id: 1, name: "Fancy-Fancy", person_id: 1>]
-
#
-
# person.pets.size # => 2
-
# person.pets
-
# # => [
-
# # #<Pet id: 2, name: "Spook", person_id: 1>,
-
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
-
# # ]
-
#
-
# Pet.find(1)
-
# # => ActiveRecord::RecordNotFound: Couldn't find Pet with id=1
-
#
-
# You can pass +Fixnum+ or +String+ values, it finds the records
-
# responding to the +id+ and executes delete on them.
-
#
-
# class Person < ActiveRecord::Base
-
# has_many :pets
-
# end
-
#
-
# person.pets.size # => 3
-
# person.pets
-
# # => [
-
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
-
# # #<Pet id: 2, name: "Spook", person_id: 1>,
-
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
-
# # ]
-
#
-
# person.pets.delete("1")
-
# # => [#<Pet id: 1, name: "Fancy-Fancy", person_id: 1>]
-
#
-
# person.pets.delete(2, 3)
-
# # => [
-
# # #<Pet id: 2, name: "Spook", person_id: 1>,
-
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
-
# # ]
-
1
def delete(*records)
-
@association.delete(*records)
-
end
-
-
# Destroys the +records+ supplied and removes them from the collection.
-
# This method will _always_ remove record from the database ignoring
-
# the +:dependent+ option. Returns an array with the removed records.
-
#
-
# class Person < ActiveRecord::Base
-
# has_many :pets
-
# end
-
#
-
# person.pets.size # => 3
-
# person.pets
-
# # => [
-
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
-
# # #<Pet id: 2, name: "Spook", person_id: 1>,
-
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
-
# # ]
-
#
-
# person.pets.destroy(Pet.find(1))
-
# # => [#<Pet id: 1, name: "Fancy-Fancy", person_id: 1>]
-
#
-
# person.pets.size # => 2
-
# person.pets
-
# # => [
-
# # #<Pet id: 2, name: "Spook", person_id: 1>,
-
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
-
# # ]
-
#
-
# person.pets.destroy(Pet.find(2), Pet.find(3))
-
# # => [
-
# # #<Pet id: 2, name: "Spook", person_id: 1>,
-
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
-
# # ]
-
#
-
# person.pets.size # => 0
-
# person.pets # => []
-
#
-
# Pet.find(1, 2, 3) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with IDs (1, 2, 3)
-
#
-
# You can pass +Fixnum+ or +String+ values, it finds the records
-
# responding to the +id+ and then deletes them from the database.
-
#
-
# person.pets.size # => 3
-
# person.pets
-
# # => [
-
# # #<Pet id: 4, name: "Benny", person_id: 1>,
-
# # #<Pet id: 5, name: "Brain", person_id: 1>,
-
# # #<Pet id: 6, name: "Boss", person_id: 1>
-
# # ]
-
#
-
# person.pets.destroy("4")
-
# # => #<Pet id: 4, name: "Benny", person_id: 1>
-
#
-
# person.pets.size # => 2
-
# person.pets
-
# # => [
-
# # #<Pet id: 5, name: "Brain", person_id: 1>,
-
# # #<Pet id: 6, name: "Boss", person_id: 1>
-
# # ]
-
#
-
# person.pets.destroy(5, 6)
-
# # => [
-
# # #<Pet id: 5, name: "Brain", person_id: 1>,
-
# # #<Pet id: 6, name: "Boss", person_id: 1>
-
# # ]
-
#
-
# person.pets.size # => 0
-
# person.pets # => []
-
#
-
# Pet.find(4, 5, 6) # => ActiveRecord::RecordNotFound: Couldn't find all Pets with IDs (4, 5, 6)
-
1
def destroy(*records)
-
@association.destroy(*records)
-
end
-
-
# Specifies whether the records should be unique or not.
-
#
-
# class Person < ActiveRecord::Base
-
# has_many :pets
-
# end
-
#
-
# person.pets.select(:name)
-
# # => [
-
# # #<Pet name: "Fancy-Fancy">,
-
# # #<Pet name: "Fancy-Fancy">
-
# # ]
-
#
-
# person.pets.select(:name).distinct
-
# # => [#<Pet name: "Fancy-Fancy">]
-
1
def distinct
-
@association.distinct
-
end
-
1
alias uniq distinct
-
-
# Count all records using SQL.
-
#
-
# class Person < ActiveRecord::Base
-
# has_many :pets
-
# end
-
#
-
# person.pets.count # => 3
-
# person.pets
-
# # => [
-
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
-
# # #<Pet id: 2, name: "Spook", person_id: 1>,
-
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
-
# # ]
-
1
def count(column_name = nil, options = {})
-
# TODO: Remove options argument as soon we remove support to
-
# activerecord-deprecated_finders.
-
@association.count(column_name, options)
-
end
-
-
# Returns the size of the collection. If the collection hasn't been loaded,
-
# it executes a <tt>SELECT COUNT(*)</tt> query. Else it calls <tt>collection.size</tt>.
-
#
-
# If the collection has been already loaded +size+ and +length+ are
-
# equivalent. If not and you are going to need the records anyway
-
# +length+ will take one less query. Otherwise +size+ is more efficient.
-
#
-
# class Person < ActiveRecord::Base
-
# has_many :pets
-
# end
-
#
-
# person.pets.size # => 3
-
# # executes something like SELECT COUNT(*) FROM "pets" WHERE "pets"."person_id" = 1
-
#
-
# person.pets # This will execute a SELECT * FROM query
-
# # => [
-
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
-
# # #<Pet id: 2, name: "Spook", person_id: 1>,
-
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
-
# # ]
-
#
-
# person.pets.size # => 3
-
# # Because the collection is already loaded, this will behave like
-
# # collection.size and no SQL count query is executed.
-
1
def size
-
@association.size
-
end
-
-
# Returns the size of the collection calling +size+ on the target.
-
# If the collection has been already loaded, +length+ and +size+ are
-
# equivalent. If not and you are going to need the records anyway this
-
# method will take one less query. Otherwise +size+ is more efficient.
-
#
-
# class Person < ActiveRecord::Base
-
# has_many :pets
-
# end
-
#
-
# person.pets.length # => 3
-
# # executes something like SELECT "pets".* FROM "pets" WHERE "pets"."person_id" = 1
-
#
-
# # Because the collection is loaded, you can
-
# # call the collection with no additional queries:
-
# person.pets
-
# # => [
-
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
-
# # #<Pet id: 2, name: "Spook", person_id: 1>,
-
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
-
# # ]
-
1
def length
-
@association.length
-
end
-
-
# Returns +true+ if the collection is empty. If the collection has been
-
# loaded it is equivalent
-
# to <tt>collection.size.zero?</tt>. If the collection has not been loaded,
-
# it is equivalent to <tt>collection.exists?</tt>. If the collection has
-
# not already been loaded and you are going to fetch the records anyway it
-
# is better to check <tt>collection.length.zero?</tt>.
-
#
-
# class Person < ActiveRecord::Base
-
# has_many :pets
-
# end
-
#
-
# person.pets.count # => 1
-
# person.pets.empty? # => false
-
#
-
# person.pets.delete_all
-
#
-
# person.pets.count # => 0
-
# person.pets.empty? # => true
-
1
def empty?
-
@association.empty?
-
end
-
-
# Returns +true+ if the collection is not empty.
-
#
-
# class Person < ActiveRecord::Base
-
# has_many :pets
-
# end
-
#
-
# person.pets.count # => 0
-
# person.pets.any? # => false
-
#
-
# person.pets << Pet.new(name: 'Snoop')
-
# person.pets.count # => 0
-
# person.pets.any? # => true
-
#
-
# You can also pass a block to define criteria. The behavior
-
# is the same, it returns true if the collection based on the
-
# criteria is not empty.
-
#
-
# person.pets
-
# # => [#<Pet name: "Snoop", group: "dogs">]
-
#
-
# person.pets.any? do |pet|
-
# pet.group == 'cats'
-
# end
-
# # => false
-
#
-
# person.pets.any? do |pet|
-
# pet.group == 'dogs'
-
# end
-
# # => true
-
1
def any?(&block)
-
@association.any?(&block)
-
end
-
-
# Returns true if the collection has more than one record.
-
# Equivalent to <tt>collection.size > 1</tt>.
-
#
-
# class Person < ActiveRecord::Base
-
# has_many :pets
-
# end
-
#
-
# person.pets.count # => 1
-
# person.pets.many? # => false
-
#
-
# person.pets << Pet.new(name: 'Snoopy')
-
# person.pets.count # => 2
-
# person.pets.many? # => true
-
#
-
# You can also pass a block to define criteria. The
-
# behavior is the same, it returns true if the collection
-
# based on the criteria has more than one record.
-
#
-
# person.pets
-
# # => [
-
# # #<Pet name: "Gorby", group: "cats">,
-
# # #<Pet name: "Puff", group: "cats">,
-
# # #<Pet name: "Snoop", group: "dogs">
-
# # ]
-
#
-
# person.pets.many? do |pet|
-
# pet.group == 'dogs'
-
# end
-
# # => false
-
#
-
# person.pets.many? do |pet|
-
# pet.group == 'cats'
-
# end
-
# # => true
-
1
def many?(&block)
-
@association.many?(&block)
-
end
-
-
# Returns +true+ if the given object is present in the collection.
-
#
-
# class Person < ActiveRecord::Base
-
# has_many :pets
-
# end
-
#
-
# person.pets # => [#<Pet id: 20, name: "Snoop">]
-
#
-
# person.pets.include?(Pet.find(20)) # => true
-
# person.pets.include?(Pet.find(21)) # => false
-
1
def include?(record)
-
!!@association.include?(record)
-
end
-
-
1
def arel
-
scope.arel
-
end
-
-
1
def proxy_association
-
@association
-
end
-
-
# We don't want this object to be put on the scoping stack, because
-
# that could create an infinite loop where we call an @association
-
# method, which gets the current scope, which is this object, which
-
# delegates to @association, and so on.
-
1
def scoping
-
@association.scope.scoping { yield }
-
end
-
-
# Returns a <tt>Relation</tt> object for the records in this association
-
1
def scope
-
@association.scope
-
end
-
1
alias spawn scope
-
-
# Equivalent to <tt>Array#==</tt>. Returns +true+ if the two arrays
-
# contain the same number of elements and if each element is equal
-
# to the corresponding element in the other array, otherwise returns
-
# +false+.
-
#
-
# class Person < ActiveRecord::Base
-
# has_many :pets
-
# end
-
#
-
# person.pets
-
# # => [
-
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
-
# # #<Pet id: 2, name: "Spook", person_id: 1>
-
# # ]
-
#
-
# other = person.pets.to_ary
-
#
-
# person.pets == other
-
# # => true
-
#
-
# other = [Pet.new(id: 1), Pet.new(id: 2)]
-
#
-
# person.pets == other
-
# # => false
-
1
def ==(other)
-
load_target == other
-
end
-
-
# Returns a new array of objects from the collection. If the collection
-
# hasn't been loaded, it fetches the records from the database.
-
#
-
# class Person < ActiveRecord::Base
-
# has_many :pets
-
# end
-
#
-
# person.pets
-
# # => [
-
# # #<Pet id: 4, name: "Benny", person_id: 1>,
-
# # #<Pet id: 5, name: "Brain", person_id: 1>,
-
# # #<Pet id: 6, name: "Boss", person_id: 1>
-
# # ]
-
#
-
# other_pets = person.pets.to_ary
-
# # => [
-
# # #<Pet id: 4, name: "Benny", person_id: 1>,
-
# # #<Pet id: 5, name: "Brain", person_id: 1>,
-
# # #<Pet id: 6, name: "Boss", person_id: 1>
-
# # ]
-
#
-
# other_pets.replace([Pet.new(name: 'BooGoo')])
-
#
-
# other_pets
-
# # => [#<Pet id: nil, name: "BooGoo", person_id: 1>]
-
#
-
# person.pets
-
# # This is not affected by replace
-
# # => [
-
# # #<Pet id: 4, name: "Benny", person_id: 1>,
-
# # #<Pet id: 5, name: "Brain", person_id: 1>,
-
# # #<Pet id: 6, name: "Boss", person_id: 1>
-
# # ]
-
1
def to_ary
-
4
load_target.dup
-
end
-
1
alias_method :to_a, :to_ary
-
-
# Adds one or more +records+ to the collection by setting their foreign keys
-
# to the association's primary key. Returns +self+, so several appends may be
-
# chained together.
-
#
-
# class Person < ActiveRecord::Base
-
# has_many :pets
-
# end
-
#
-
# person.pets.size # => 0
-
# person.pets << Pet.new(name: 'Fancy-Fancy')
-
# person.pets << [Pet.new(name: 'Spook'), Pet.new(name: 'Choo-Choo')]
-
# person.pets.size # => 3
-
#
-
# person.id # => 1
-
# person.pets
-
# # => [
-
# # #<Pet id: 1, name: "Fancy-Fancy", person_id: 1>,
-
# # #<Pet id: 2, name: "Spook", person_id: 1>,
-
# # #<Pet id: 3, name: "Choo-Choo", person_id: 1>
-
# # ]
-
1
def <<(*records)
-
proxy_association.concat(records) && self
-
end
-
1
alias_method :push, :<<
-
1
alias_method :append, :<<
-
-
1
def prepend(*args)
-
raise NoMethodError, "prepend on association is not defined. Please use << or append"
-
end
-
-
# Equivalent to +delete_all+. The difference is that returns +self+, instead
-
# of an array with the deleted objects, so methods can be chained. See
-
# +delete_all+ for more information.
-
1
def clear
-
delete_all
-
self
-
end
-
-
# Reloads the collection from the database. Returns +self+.
-
# Equivalent to <tt>collection(true)</tt>.
-
#
-
# class Person < ActiveRecord::Base
-
# has_many :pets
-
# end
-
#
-
# person.pets # fetches pets from the database
-
# # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
-
#
-
# person.pets # uses the pets cache
-
# # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
-
#
-
# person.pets.reload # fetches pets from the database
-
# # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
-
#
-
# person.pets(true) # fetches pets from the database
-
# # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
-
1
def reload
-
proxy_association.reload
-
self
-
end
-
-
# Unloads the association. Returns +self+.
-
#
-
# class Person < ActiveRecord::Base
-
# has_many :pets
-
# end
-
#
-
# person.pets # fetches pets from the database
-
# # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
-
#
-
# person.pets # uses the pets cache
-
# # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
-
#
-
# person.pets.reset # clears the pets cache
-
#
-
# person.pets # fetches pets from the database
-
# # => [#<Pet id: 1, name: "Snoop", group: "dogs", person_id: 1>]
-
1
def reset
-
proxy_association.reset
-
proxy_association.reset_scope
-
self
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
# = Active Record Has Many Association
-
1
module Associations
-
# This is the proxy that handles a has many association.
-
#
-
# If the association has a <tt>:through</tt> option further specialization
-
# is provided by its child HasManyThroughAssociation.
-
1
class HasManyAssociation < CollectionAssociation #:nodoc:
-
-
1
def handle_dependency
-
case options[:dependent]
-
when :restrict_with_exception
-
raise ActiveRecord::DeleteRestrictionError.new(reflection.name) unless empty?
-
-
when :restrict_with_error
-
unless empty?
-
record = klass.human_attribute_name(reflection.name).downcase
-
owner.errors.add(:base, :"restrict_dependent_destroy.many", record: record)
-
false
-
end
-
-
else
-
if options[:dependent] == :destroy
-
# No point in executing the counter update since we're going to destroy the parent anyway
-
load_target.each { |t| t.destroyed_by_association = reflection }
-
destroy_all
-
else
-
delete_all
-
end
-
end
-
end
-
-
1
def insert_record(record, validate = true, raise = false)
-
set_owner_attributes(record)
-
set_inverse_instance(record)
-
-
if raise
-
record.save!(:validate => validate)
-
else
-
record.save(:validate => validate)
-
end
-
end
-
-
1
private
-
-
# Returns the number of records in this collection.
-
#
-
# If the association has a counter cache it gets that value. Otherwise
-
# it will attempt to do a count via SQL, bounded to <tt>:limit</tt> if
-
# there's one. Some configuration options like :group make it impossible
-
# to do an SQL count, in those cases the array count will be used.
-
#
-
# That does not depend on whether the collection has already been loaded
-
# or not. The +size+ method is the one that takes the loaded flag into
-
# account and delegates to +count_records+ if needed.
-
#
-
# If the collection is empty the target is set to an empty array and
-
# the loaded flag is set to true as well.
-
1
def count_records
-
count = if has_cached_counter?
-
owner.send(:read_attribute, cached_counter_attribute_name)
-
else
-
scope.count
-
end
-
-
# If there's nothing in the database and @target has no new records
-
# we are certain the current target is an empty array. This is a
-
# documented side-effect of the method that may avoid an extra SELECT.
-
@target ||= [] and loaded! if count == 0
-
-
[association_scope.limit_value, count].compact.min
-
end
-
-
1
def has_cached_counter?(reflection = reflection())
-
2
owner.attribute_present?(cached_counter_attribute_name(reflection))
-
end
-
-
1
def cached_counter_attribute_name(reflection = reflection())
-
2
options[:counter_cache] || "#{reflection.name}_count"
-
end
-
-
1
def update_counter(difference, reflection = reflection())
-
2
if has_cached_counter?(reflection)
-
counter = cached_counter_attribute_name(reflection)
-
owner.class.update_counters(owner.id, counter => difference)
-
owner[counter] += difference
-
owner.changed_attributes.delete(counter) # eww
-
end
-
end
-
-
# This shit is nasty. We need to avoid the following situation:
-
#
-
# * An associated record is deleted via record.destroy
-
# * Hence the callbacks run, and they find a belongs_to on the record with a
-
# :counter_cache options which points back at our owner. So they update the
-
# counter cache.
-
# * In which case, we must make sure to *not* update the counter cache, or else
-
# it will be decremented twice.
-
#
-
# Hence this method.
-
1
def inverse_updates_counter_cache?(reflection = reflection())
-
counter_name = cached_counter_attribute_name(reflection)
-
reflection.klass._reflections.values.any? { |inverse_reflection|
-
:belongs_to == inverse_reflection.macro &&
-
inverse_reflection.counter_cache_column == counter_name
-
}
-
end
-
-
# Deletes the records according to the <tt>:dependent</tt> option.
-
1
def delete_records(records, method)
-
if method == :destroy
-
records.each(&:destroy!)
-
update_counter(-records.length) unless inverse_updates_counter_cache?
-
else
-
if records == :all || !reflection.klass.primary_key
-
scope = self.scope
-
else
-
scope = self.scope.where(reflection.klass.primary_key => records)
-
end
-
-
if method == :delete_all
-
update_counter(-scope.delete_all)
-
else
-
update_counter(-scope.update_all(reflection.foreign_key => nil))
-
end
-
end
-
end
-
-
1
def foreign_key_present?
-
if reflection.klass.primary_key
-
owner.attribute_present?(reflection.association_primary_key)
-
else
-
false
-
end
-
end
-
end
-
end
-
end
-
-
1
module ActiveRecord
-
# = Active Record Has Many Through Association
-
1
module Associations
-
1
class HasManyThroughAssociation < HasManyAssociation #:nodoc:
-
1
include ThroughAssociation
-
-
1
def initialize(owner, reflection)
-
4
super
-
-
4
@through_records = {}
-
4
@through_association = nil
-
end
-
-
# Returns the size of the collection by executing a SELECT COUNT(*) query if the collection hasn't been
-
# loaded and calling collection.size if it has. If it's more likely than not that the collection does
-
# have a size larger than zero, and you need to fetch that collection afterwards, it'll take one fewer
-
# SELECT query if you use #length.
-
1
def size
-
if has_cached_counter?
-
owner.send(:read_attribute, cached_counter_attribute_name)
-
elsif loaded?
-
target.size
-
else
-
super
-
end
-
end
-
-
1
def concat(*records)
-
unless owner.new_record?
-
records.flatten.each do |record|
-
raise_on_type_mismatch!(record)
-
end
-
end
-
-
super
-
end
-
-
1
def concat_records(records)
-
ensure_not_nested
-
-
records = super(records, true)
-
-
if owner.new_record? && records
-
records.flatten.each do |record|
-
build_through_record(record)
-
end
-
end
-
-
records
-
end
-
-
1
def insert_record(record, validate = true, raise = false)
-
2
ensure_not_nested
-
-
2
if record.new_record?
-
2
if raise
-
record.save!(:validate => validate)
-
else
-
2
return unless record.save(:validate => validate)
-
end
-
end
-
-
2
save_through_record(record)
-
2
update_counter(1)
-
2
record
-
end
-
-
1
private
-
-
1
def through_association
-
8
@through_association ||= owner.association(through_reflection.name)
-
end
-
-
# We temporarily cache through record that has been build, because if we build a
-
# through record in build_record and then subsequently call insert_record, then we
-
# want to use the exact same object.
-
#
-
# However, after insert_record has been called, we clear the cache entry because
-
# we want it to be possible to have multiple instances of the same record in an
-
# association
-
1
def build_through_record(record)
-
2
@through_records[record.object_id] ||= begin
-
2
ensure_mutable
-
-
2
through_record = through_association.build(*options_for_through_record)
-
2
through_record.send("#{source_reflection.name}=", record)
-
2
through_record
-
end
-
end
-
-
1
def options_for_through_record
-
2
[through_scope_attributes]
-
end
-
-
1
def through_scope_attributes
-
scope.where_values_hash(through_association.reflection.name.to_s).
-
2
except!(through_association.reflection.foreign_key,
-
through_association.reflection.klass.inheritance_column)
-
end
-
-
1
def save_through_record(record)
-
2
build_through_record(record).save!
-
ensure
-
2
@through_records.delete(record.object_id)
-
end
-
-
1
def build_record(attributes)
-
2
ensure_not_nested
-
-
2
record = super(attributes)
-
-
2
inverse = source_reflection.inverse_of
-
2
if inverse
-
if inverse.macro == :has_many
-
record.send(inverse.name) << build_through_record(record)
-
elsif inverse.macro == :has_one
-
record.send("#{inverse.name}=", build_through_record(record))
-
end
-
end
-
-
2
record
-
end
-
-
1
def target_reflection_has_associated_record?
-
2
!(through_reflection.macro == :belongs_to && owner[through_reflection.foreign_key].blank?)
-
end
-
-
1
def update_through_counter?(method)
-
case method
-
when :destroy
-
!inverse_updates_counter_cache?(through_reflection)
-
when :nullify
-
false
-
else
-
true
-
end
-
end
-
-
1
def delete_records(records, method)
-
ensure_not_nested
-
-
# This is unoptimised; it will load all the target records
-
# even when we just want to delete everything.
-
records = load_target if records == :all
-
-
scope = through_association.scope
-
scope.where! construct_join_attributes(*records)
-
-
case method
-
when :destroy
-
if scope.klass.primary_key
-
count = scope.destroy_all.length
-
else
-
scope.to_a.each do |record|
-
record.run_callbacks :destroy
-
end
-
-
arel = scope.arel
-
-
stmt = Arel::DeleteManager.new arel.engine
-
stmt.from scope.klass.arel_table
-
stmt.wheres = arel.constraints
-
-
count = scope.klass.connection.delete(stmt, 'SQL', scope.bind_values)
-
end
-
when :nullify
-
count = scope.update_all(source_reflection.foreign_key => nil)
-
else
-
count = scope.delete_all
-
end
-
-
delete_through_records(records)
-
-
if source_reflection.options[:counter_cache] && method != :destroy
-
counter = source_reflection.counter_cache_column
-
klass.decrement_counter counter, records.map(&:id)
-
end
-
-
if through_reflection.macro == :has_many && update_through_counter?(method)
-
update_counter(-count, through_reflection)
-
end
-
-
update_counter(-count)
-
end
-
-
1
def through_records_for(record)
-
attributes = construct_join_attributes(record)
-
candidates = Array.wrap(through_association.target)
-
candidates.find_all do |c|
-
attributes.all? do |key, value|
-
c.public_send(key) == value
-
end
-
end
-
end
-
-
1
def delete_through_records(records)
-
records.each do |record|
-
through_records = through_records_for(record)
-
-
if through_reflection.macro == :has_many
-
through_records.each { |r| through_association.target.delete(r) }
-
else
-
if through_records.include?(through_association.target)
-
through_association.target = nil
-
end
-
end
-
-
@through_records.delete(record.object_id)
-
end
-
end
-
-
1
def find_target
-
2
return [] unless target_reflection_has_associated_record?
-
2
scope.to_a
-
end
-
-
# NOTE - not sure that we can actually cope with inverses here
-
1
def invertible_for?(record)
-
6
false
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
module Associations
-
1
class JoinDependency # :nodoc:
-
1
autoload :JoinBase, 'active_record/associations/join_dependency/join_base'
-
1
autoload :JoinAssociation, 'active_record/associations/join_dependency/join_association'
-
-
1
class Aliases # :nodoc:
-
1
def initialize(tables)
-
@tables = tables
-
@alias_cache = tables.each_with_object({}) { |table,h|
-
h[table.node] = table.columns.each_with_object({}) { |column,i|
-
i[column.name] = column.alias
-
}
-
}
-
@name_and_alias_cache = tables.each_with_object({}) { |table,h|
-
h[table.node] = table.columns.map { |column|
-
[column.name, column.alias]
-
}
-
}
-
end
-
-
1
def columns
-
@tables.flat_map { |t| t.column_aliases }
-
end
-
-
# An array of [column_name, alias] pairs for the table
-
1
def column_aliases(node)
-
@name_and_alias_cache[node]
-
end
-
-
1
def column_alias(node, column)
-
@alias_cache[node][column]
-
end
-
-
1
class Table < Struct.new(:node, :columns)
-
1
def table
-
Arel::Nodes::TableAlias.new node.table, node.aliased_table_name
-
end
-
-
1
def column_aliases
-
t = table
-
columns.map { |column| t[column.name].as Arel.sql column.alias }
-
end
-
end
-
1
Column = Struct.new(:name, :alias)
-
end
-
-
1
attr_reader :alias_tracker, :base_klass, :join_root
-
-
1
def self.make_tree(associations)
-
2
hash = {}
-
2
walk_tree associations, hash
-
2
hash
-
end
-
-
1
def self.walk_tree(associations, hash)
-
2
case associations
-
when Symbol, String
-
hash[associations.to_sym] ||= {}
-
when Array
-
2
associations.each do |assoc|
-
walk_tree assoc, hash
-
end
-
when Hash
-
associations.each do |k,v|
-
cache = hash[k] ||= {}
-
walk_tree v, cache
-
end
-
else
-
raise ConfigurationError, associations.inspect
-
end
-
end
-
-
# base is the base class on which operation is taking place.
-
# associations is the list of associations which are joined using hash, symbol or array.
-
# joins is the list of all string join commands and arel nodes.
-
#
-
# Example :
-
#
-
# class Physician < ActiveRecord::Base
-
# has_many :appointments
-
# has_many :patients, through: :appointments
-
# end
-
#
-
# If I execute `@physician.patients.to_a` then
-
# base # => Physician
-
# associations # => []
-
# joins # => [#<Arel::Nodes::InnerJoin: ...]
-
#
-
# However if I execute `Physician.joins(:appointments).to_a` then
-
# base # => Physician
-
# associations # => [:appointments]
-
# joins # => []
-
#
-
1
def initialize(base, associations, joins)
-
2
@alias_tracker = AliasTracker.create(base.connection, joins)
-
2
@alias_tracker.aliased_name_for(base.table_name, base.table_name) # Updates the count for base.table_name to 1
-
2
tree = self.class.make_tree associations
-
2
@join_root = JoinBase.new base, build(tree, base)
-
2
@join_root.children.each { |child| construct_tables! @join_root, child }
-
end
-
-
1
def reflections
-
join_root.drop(1).map!(&:reflection)
-
end
-
-
1
def join_constraints(outer_joins)
-
2
joins = join_root.children.flat_map { |child|
-
make_inner_joins join_root, child
-
}
-
-
2
joins.concat outer_joins.flat_map { |oj|
-
if join_root.match? oj.join_root
-
walk join_root, oj.join_root
-
else
-
oj.join_root.children.flat_map { |child|
-
make_outer_joins oj.join_root, child
-
}
-
end
-
}
-
end
-
-
1
def aliases
-
Aliases.new join_root.each_with_index.map { |join_part,i|
-
columns = join_part.column_names.each_with_index.map { |column_name,j|
-
Aliases::Column.new column_name, "t#{i}_r#{j}"
-
}
-
Aliases::Table.new(join_part, columns)
-
}
-
end
-
-
1
def instantiate(result_set, aliases)
-
primary_key = aliases.column_alias(join_root, join_root.primary_key)
-
type_caster = result_set.column_type primary_key
-
-
seen = Hash.new { |h,parent_klass|
-
h[parent_klass] = Hash.new { |i,parent_id|
-
i[parent_id] = Hash.new { |j,child_klass| j[child_klass] = {} }
-
}
-
}
-
-
model_cache = Hash.new { |h,klass| h[klass] = {} }
-
parents = model_cache[join_root]
-
column_aliases = aliases.column_aliases join_root
-
-
result_set.each { |row_hash|
-
primary_id = type_caster.type_cast row_hash[primary_key]
-
parent = parents[primary_id] ||= join_root.instantiate(row_hash, column_aliases)
-
construct(parent, join_root, row_hash, result_set, seen, model_cache, aliases)
-
}
-
-
parents.values
-
end
-
-
1
private
-
-
1
def make_constraints(parent, child, tables, join_type)
-
chain = child.reflection.chain
-
foreign_table = parent.table
-
foreign_klass = parent.base_klass
-
child.join_constraints(foreign_table, foreign_klass, child, join_type, tables, child.reflection.scope_chain, chain)
-
end
-
-
1
def make_outer_joins(parent, child)
-
tables = table_aliases_for(parent, child)
-
join_type = Arel::Nodes::OuterJoin
-
joins = make_constraints parent, child, tables, join_type
-
-
joins.concat child.children.flat_map { |c| make_outer_joins(child, c) }
-
end
-
-
1
def make_inner_joins(parent, child)
-
tables = child.tables
-
join_type = Arel::Nodes::InnerJoin
-
joins = make_constraints parent, child, tables, join_type
-
-
joins.concat child.children.flat_map { |c| make_inner_joins(child, c) }
-
end
-
-
1
def table_aliases_for(parent, node)
-
node.reflection.chain.map { |reflection|
-
alias_tracker.aliased_table_for(
-
reflection.table_name,
-
table_alias_for(reflection, parent, reflection != node.reflection)
-
)
-
}
-
end
-
-
1
def construct_tables!(parent, node)
-
node.tables = table_aliases_for(parent, node)
-
node.children.each { |child| construct_tables! node, child }
-
end
-
-
1
def table_alias_for(reflection, parent, join)
-
name = "#{reflection.plural_name}_#{parent.table_name}"
-
name << "_join" if join
-
name
-
end
-
-
1
def walk(left, right)
-
intersection, missing = right.children.map { |node1|
-
[left.children.find { |node2| node1.match? node2 }, node1]
-
}.partition(&:first)
-
-
ojs = missing.flat_map { |_,n| make_outer_joins left, n }
-
intersection.flat_map { |l,r| walk l, r }.concat ojs
-
end
-
-
1
def find_reflection(klass, name)
-
klass._reflect_on_association(name) or
-
raise ConfigurationError, "Association named '#{ name }' was not found on #{ klass.name }; perhaps you misspelled it?"
-
end
-
-
1
def build(associations, base_klass)
-
2
associations.map do |name, right|
-
reflection = find_reflection base_klass, name
-
reflection.check_validity!
-
-
if reflection.options[:polymorphic]
-
raise EagerLoadPolymorphicError.new(reflection)
-
end
-
-
JoinAssociation.new reflection, build(right, reflection.klass)
-
end
-
end
-
-
1
def construct(ar_parent, parent, row, rs, seen, model_cache, aliases)
-
primary_id = ar_parent.id
-
-
parent.children.each do |node|
-
if node.reflection.collection?
-
other = ar_parent.association(node.reflection.name)
-
other.loaded!
-
else
-
if ar_parent.association_cache.key?(node.reflection.name)
-
model = ar_parent.association(node.reflection.name).target
-
construct(model, node, row, rs, seen, model_cache, aliases)
-
next
-
end
-
end
-
-
key = aliases.column_alias(node, node.primary_key)
-
id = row[key]
-
next if id.nil?
-
-
model = seen[parent.base_klass][primary_id][node.base_klass][id]
-
-
if model
-
construct(model, node, row, rs, seen, model_cache, aliases)
-
else
-
model = construct_model(ar_parent, node, row, model_cache, id, aliases)
-
seen[parent.base_klass][primary_id][node.base_klass][id] = model
-
construct(model, node, row, rs, seen, model_cache, aliases)
-
end
-
end
-
end
-
-
1
def construct_model(record, node, row, model_cache, id, aliases)
-
model = model_cache[node][id] ||= node.instantiate(row,
-
aliases.column_aliases(node))
-
other = record.association(node.reflection.name)
-
-
if node.reflection.collection?
-
other.target.push(model)
-
else
-
other.target = model
-
end
-
-
other.set_inverse_instance(model)
-
model
-
end
-
end
-
end
-
end
-
1
require 'active_record/associations/join_dependency/join_part'
-
-
1
module ActiveRecord
-
1
module Associations
-
1
class JoinDependency # :nodoc:
-
1
class JoinBase < JoinPart # :nodoc:
-
1
def match?(other)
-
return true if self == other
-
super && base_klass == other.base_klass
-
end
-
-
1
def table
-
base_klass.arel_table
-
end
-
-
1
def aliased_table_name
-
base_klass.table_name
-
end
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
module Associations
-
1
class JoinDependency # :nodoc:
-
# A JoinPart represents a part of a JoinDependency. It is inherited
-
# by JoinBase and JoinAssociation. A JoinBase represents the Active Record which
-
# everything else is being joined onto. A JoinAssociation represents an association which
-
# is joining to the base. A JoinAssociation may result in more than one actual join
-
# operations (for example a has_and_belongs_to_many JoinAssociation would result in
-
# two; one for the join table and one for the target table).
-
1
class JoinPart # :nodoc:
-
1
include Enumerable
-
-
# The Active Record class which this join part is associated 'about'; for a JoinBase
-
# this is the actual base model, for a JoinAssociation this is the target model of the
-
# association.
-
1
attr_reader :base_klass, :children
-
-
1
delegate :table_name, :column_names, :primary_key, :to => :base_klass
-
-
1
def initialize(base_klass, children)
-
2
@base_klass = base_klass
-
2
@column_names_with_alias = nil
-
2
@children = children
-
end
-
-
1
def name
-
reflection.name
-
end
-
-
1
def match?(other)
-
self.class == other.class
-
end
-
-
1
def each(&block)
-
yield self
-
children.each { |child| child.each(&block) }
-
end
-
-
# An Arel::Table for the active_record
-
1
def table
-
raise NotImplementedError
-
end
-
-
# The alias for the active_record's table
-
1
def aliased_table_name
-
raise NotImplementedError
-
end
-
-
1
def extract_record(row, column_names_with_alias)
-
# This code is performance critical as it is called per row.
-
# see: https://github.com/rails/rails/pull/12185
-
hash = {}
-
-
index = 0
-
length = column_names_with_alias.length
-
-
while index < length
-
column_name, alias_name = column_names_with_alias[index]
-
hash[column_name] = row[alias_name]
-
index += 1
-
end
-
-
hash
-
end
-
-
1
def instantiate(row, aliases)
-
base_klass.instantiate(extract_record(row, aliases))
-
end
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
module Associations
-
# Implements the details of eager loading of Active Record associations.
-
#
-
# Note that 'eager loading' and 'preloading' are actually the same thing.
-
# However, there are two different eager loading strategies.
-
#
-
# The first one is by using table joins. This was only strategy available
-
# prior to Rails 2.1. Suppose that you have an Author model with columns
-
# 'name' and 'age', and a Book model with columns 'name' and 'sales'. Using
-
# this strategy, Active Record would try to retrieve all data for an author
-
# and all of its books via a single query:
-
#
-
# SELECT * FROM authors
-
# LEFT OUTER JOIN books ON authors.id = books.author_id
-
# WHERE authors.name = 'Ken Akamatsu'
-
#
-
# However, this could result in many rows that contain redundant data. After
-
# having received the first row, we already have enough data to instantiate
-
# the Author object. In all subsequent rows, only the data for the joined
-
# 'books' table is useful; the joined 'authors' data is just redundant, and
-
# processing this redundant data takes memory and CPU time. The problem
-
# quickly becomes worse and worse as the level of eager loading increases
-
# (i.e. if Active Record is to eager load the associations' associations as
-
# well).
-
#
-
# The second strategy is to use multiple database queries, one for each
-
# level of association. Since Rails 2.1, this is the default strategy. In
-
# situations where a table join is necessary (e.g. when the +:conditions+
-
# option references an association's column), it will fallback to the table
-
# join strategy.
-
1
class Preloader #:nodoc:
-
1
extend ActiveSupport::Autoload
-
-
1
eager_autoload do
-
1
autoload :Association, 'active_record/associations/preloader/association'
-
1
autoload :SingularAssociation, 'active_record/associations/preloader/singular_association'
-
1
autoload :CollectionAssociation, 'active_record/associations/preloader/collection_association'
-
1
autoload :ThroughAssociation, 'active_record/associations/preloader/through_association'
-
-
1
autoload :HasMany, 'active_record/associations/preloader/has_many'
-
1
autoload :HasManyThrough, 'active_record/associations/preloader/has_many_through'
-
1
autoload :HasOne, 'active_record/associations/preloader/has_one'
-
1
autoload :HasOneThrough, 'active_record/associations/preloader/has_one_through'
-
1
autoload :BelongsTo, 'active_record/associations/preloader/belongs_to'
-
end
-
-
# Eager loads the named associations for the given Active Record record(s).
-
#
-
# In this description, 'association name' shall refer to the name passed
-
# to an association creation method. For example, a model that specifies
-
# <tt>belongs_to :author</tt>, <tt>has_many :buyers</tt> has association
-
# names +:author+ and +:buyers+.
-
#
-
# == Parameters
-
# +records+ is an array of ActiveRecord::Base. This array needs not be flat,
-
# i.e. +records+ itself may also contain arrays of records. In any case,
-
# +preload_associations+ will preload the all associations records by
-
# flattening +records+.
-
#
-
# +associations+ specifies one or more associations that you want to
-
# preload. It may be:
-
# - a Symbol or a String which specifies a single association name. For
-
# example, specifying +:books+ allows this method to preload all books
-
# for an Author.
-
# - an Array which specifies multiple association names. This array
-
# is processed recursively. For example, specifying <tt>[:avatar, :books]</tt>
-
# allows this method to preload an author's avatar as well as all of his
-
# books.
-
# - a Hash which specifies multiple association names, as well as
-
# association names for the to-be-preloaded association objects. For
-
# example, specifying <tt>{ author: :avatar }</tt> will preload a
-
# book's author, as well as that author's avatar.
-
#
-
# +:associations+ has the same format as the +:include+ option for
-
# <tt>ActiveRecord::Base.find</tt>. So +associations+ could look like this:
-
#
-
# :books
-
# [ :books, :author ]
-
# { author: :avatar }
-
# [ :books, { author: :avatar } ]
-
-
1
NULL_RELATION = Struct.new(:values).new({})
-
-
1
def preload(records, associations, preload_scope = nil)
-
records = Array.wrap(records).compact.uniq
-
associations = Array.wrap(associations)
-
preload_scope = preload_scope || NULL_RELATION
-
-
if records.empty?
-
[]
-
else
-
associations.flat_map { |association|
-
preloaders_on association, records, preload_scope
-
}
-
end
-
end
-
-
1
private
-
-
1
def preloaders_on(association, records, scope)
-
case association
-
when Hash
-
preloaders_for_hash(association, records, scope)
-
when Symbol
-
preloaders_for_one(association, records, scope)
-
when String
-
preloaders_for_one(association.to_sym, records, scope)
-
else
-
raise ArgumentError, "#{association.inspect} was not recognised for preload"
-
end
-
end
-
-
1
def preloaders_for_hash(association, records, scope)
-
association.flat_map { |parent, child|
-
loaders = preloaders_for_one parent, records, scope
-
-
recs = loaders.flat_map(&:preloaded_records).uniq
-
loaders.concat Array.wrap(child).flat_map { |assoc|
-
preloaders_on assoc, recs, scope
-
}
-
loaders
-
}
-
end
-
-
# Not all records have the same class, so group then preload group on the reflection
-
# itself so that if various subclass share the same association then we do not split
-
# them unnecessarily
-
#
-
# Additionally, polymorphic belongs_to associations can have multiple associated
-
# classes, depending on the polymorphic_type field. So we group by the classes as
-
# well.
-
1
def preloaders_for_one(association, records, scope)
-
grouped_records(association, records).flat_map do |reflection, klasses|
-
klasses.map do |rhs_klass, rs|
-
loader = preloader_for(reflection, rs, rhs_klass).new(rhs_klass, rs, reflection, scope)
-
loader.run self
-
loader
-
end
-
end
-
end
-
-
1
def grouped_records(association, records)
-
h = {}
-
records.each do |record|
-
next unless record
-
assoc = record.association(association)
-
klasses = h[assoc.reflection] ||= {}
-
(klasses[assoc.klass] ||= []) << record
-
end
-
h
-
end
-
-
1
class AlreadyLoaded
-
1
attr_reader :owners, :reflection
-
-
1
def initialize(klass, owners, reflection, preload_scope)
-
@owners = owners
-
@reflection = reflection
-
end
-
-
1
def run(preloader); end
-
-
1
def preloaded_records
-
owners.flat_map { |owner| owner.association(reflection.name).target }
-
end
-
end
-
-
1
class NullPreloader
-
1
def self.new(klass, owners, reflection, preload_scope); self; end
-
1
def self.run(preloader); end
-
end
-
-
1
def preloader_for(reflection, owners, rhs_klass)
-
return NullPreloader unless rhs_klass
-
-
if owners.first.association(reflection.name).loaded?
-
return AlreadyLoaded
-
end
-
-
case reflection.macro
-
when :has_many
-
reflection.options[:through] ? HasManyThrough : HasMany
-
when :has_one
-
reflection.options[:through] ? HasOneThrough : HasOne
-
when :belongs_to
-
BelongsTo
-
end
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
module Associations
-
1
class SingularAssociation < Association #:nodoc:
-
# Implements the reader method, e.g. foo.bar for Foo.has_one :bar
-
1
def reader(force_reload = false)
-
if force_reload
-
klass.uncached { reload }
-
elsif !loaded? || stale_target?
-
reload
-
end
-
-
target
-
end
-
-
# Implements the writer method, e.g. foo.bar= for Foo.belongs_to :bar
-
1
def writer(record)
-
2
replace(record)
-
end
-
-
1
def create(attributes = {}, &block)
-
_create_record(attributes, &block)
-
end
-
-
1
def create!(attributes = {}, &block)
-
_create_record(attributes, true, &block)
-
end
-
-
1
def build(attributes = {})
-
record = build_record(attributes)
-
yield(record) if block_given?
-
set_new_record(record)
-
record
-
end
-
-
1
private
-
-
1
def create_scope
-
scope.scope_for_create.stringify_keys.except(klass.primary_key)
-
end
-
-
1
def find_target
-
if record = scope.take
-
set_inverse_instance record
-
end
-
end
-
-
1
def replace(record)
-
raise NotImplementedError, "Subclasses must implement a replace(record) method"
-
end
-
-
1
def set_new_record(record)
-
replace(record)
-
end
-
-
1
def _create_record(attributes, raise_error = false)
-
record = build_record(attributes)
-
yield(record) if block_given?
-
saved = record.save
-
set_new_record(record)
-
raise RecordInvalid.new(record) if !saved && raise_error
-
record
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
# = Active Record Through Association
-
1
module Associations
-
1
module ThroughAssociation #:nodoc:
-
-
1
delegate :source_reflection, :through_reflection, :chain, :to => :reflection
-
-
1
protected
-
-
# We merge in these scopes for two reasons:
-
#
-
# 1. To get the default_scope conditions for any of the other reflections in the chain
-
# 2. To get the type conditions for any STI models in the chain
-
1
def target_scope
-
10
scope = super
-
10
chain.drop(1).each do |reflection|
-
10
relation = reflection.klass.all
-
10
relation.merge!(reflection.scope) if reflection.scope
-
-
10
scope.merge!(
-
relation.except(:select, :create_with, :includes, :preload, :joins, :eager_load)
-
)
-
end
-
10
scope
-
end
-
-
1
private
-
-
# Construct attributes for :through pointing to owner and associate. This is used by the
-
# methods which create and delete records on the association.
-
#
-
# We only support indirectly modifying through associations which has a belongs_to source.
-
# This is the "has_many :tags, through: :taggings" situation, where the join model
-
# typically has a belongs_to on both side. In other words, associations which could also
-
# be represented as has_and_belongs_to_many associations.
-
#
-
# We do not support creating/deleting records on the association where the source has
-
# some other type, because this opens up a whole can of worms, and in basically any
-
# situation it is more natural for the user to just create or modify their join records
-
# directly as required.
-
1
def construct_join_attributes(*records)
-
ensure_mutable
-
-
if source_reflection.association_primary_key(reflection.klass) == reflection.klass.primary_key
-
join_attributes = { source_reflection.name => records }
-
else
-
join_attributes = {
-
source_reflection.foreign_key =>
-
records.map { |record|
-
record.send(source_reflection.association_primary_key(reflection.klass))
-
}
-
}
-
end
-
-
if options[:source_type]
-
join_attributes[source_reflection.foreign_type] =
-
records.map { |record| record.class.base_class.name }
-
end
-
-
if records.count == 1
-
Hash[join_attributes.map { |k, v| [k, v.first] }]
-
else
-
join_attributes
-
end
-
end
-
-
# Note: this does not capture all cases, for example it would be crazy to try to
-
# properly support stale-checking for nested associations.
-
1
def stale_state
-
2
if through_reflection.macro == :belongs_to
-
owner[through_reflection.foreign_key] && owner[through_reflection.foreign_key].to_s
-
end
-
end
-
-
1
def foreign_key_present?
-
through_reflection.macro == :belongs_to &&
-
!owner[through_reflection.foreign_key].nil?
-
end
-
-
1
def ensure_mutable
-
2
if source_reflection.macro != :belongs_to
-
raise HasManyThroughCantAssociateThroughHasOneOrManyReflection.new(owner, reflection)
-
end
-
end
-
-
1
def ensure_not_nested
-
4
if reflection.nested?
-
raise HasManyThroughNestedAssociationsAreReadonly.new(owner, reflection)
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_model/forbidden_attributes_protection'
-
-
1
module ActiveRecord
-
1
module AttributeAssignment
-
1
extend ActiveSupport::Concern
-
1
include ActiveModel::ForbiddenAttributesProtection
-
-
# Allows you to set all the attributes by passing in a hash of attributes with
-
# keys matching the attribute names (which again matches the column names).
-
#
-
# If the passed hash responds to <tt>permitted?</tt> method and the return value
-
# of this method is +false+ an <tt>ActiveModel::ForbiddenAttributesError</tt>
-
# exception is raised.
-
1
def assign_attributes(new_attributes)
-
10
if !new_attributes.respond_to?(:stringify_keys)
-
raise ArgumentError, "When assigning attributes, you must pass a hash as an argument."
-
end
-
10
return if new_attributes.blank?
-
-
6
attributes = new_attributes.stringify_keys
-
6
multi_parameter_attributes = []
-
6
nested_parameter_attributes = []
-
-
6
attributes = sanitize_for_mass_assignment(attributes)
-
-
6
attributes.each do |k, v|
-
10
if k.include?("(")
-
multi_parameter_attributes << [ k, v ]
-
elsif v.is_a?(Hash)
-
nested_parameter_attributes << [ k, v ]
-
else
-
10
_assign_attribute(k, v)
-
end
-
end
-
-
6
assign_nested_parameter_attributes(nested_parameter_attributes) unless nested_parameter_attributes.empty?
-
6
assign_multiparameter_attributes(multi_parameter_attributes) unless multi_parameter_attributes.empty?
-
end
-
-
1
alias attributes= assign_attributes
-
-
1
private
-
-
1
def _assign_attribute(k, v)
-
10
public_send("#{k}=", v)
-
rescue NoMethodError
-
if respond_to?("#{k}=")
-
raise
-
else
-
raise UnknownAttributeError.new(self, k)
-
end
-
end
-
-
# Assign any deferred nested attributes after the base attributes have been set.
-
1
def assign_nested_parameter_attributes(pairs)
-
pairs.each { |k, v| _assign_attribute(k, v) }
-
end
-
-
# Instantiates objects for all attribute classes that needs more than one constructor parameter. This is done
-
# by calling new on the column type or aggregation type (through composed_of) object with these parameters.
-
# So having the pairs written_on(1) = "2004", written_on(2) = "6", written_on(3) = "24", will instantiate
-
# written_on (a date type) with Date.new("2004", "6", "24"). You can also specify a typecast character in the
-
# parentheses to have the parameters typecasted before they're used in the constructor. Use i for Fixnum and
-
# f for Float. If all the values for a given attribute are empty, the attribute will be set to +nil+.
-
1
def assign_multiparameter_attributes(pairs)
-
execute_callstack_for_multiparameter_attributes(
-
extract_callstack_for_multiparameter_attributes(pairs)
-
)
-
end
-
-
1
def execute_callstack_for_multiparameter_attributes(callstack)
-
errors = []
-
callstack.each do |name, values_with_empty_parameters|
-
begin
-
send("#{name}=", MultiparameterAttribute.new(self, name, values_with_empty_parameters).read_value)
-
rescue => ex
-
errors << AttributeAssignmentError.new("error on assignment #{values_with_empty_parameters.values.inspect} to #{name} (#{ex.message})", ex, name)
-
end
-
end
-
unless errors.empty?
-
error_descriptions = errors.map { |ex| ex.message }.join(",")
-
raise MultiparameterAssignmentErrors.new(errors), "#{errors.size} error(s) on assignment of multiparameter attributes [#{error_descriptions}]"
-
end
-
end
-
-
1
def extract_callstack_for_multiparameter_attributes(pairs)
-
attributes = {}
-
-
pairs.each do |(multiparameter_name, value)|
-
attribute_name = multiparameter_name.split("(").first
-
attributes[attribute_name] ||= {}
-
-
parameter_value = value.empty? ? nil : type_cast_attribute_value(multiparameter_name, value)
-
attributes[attribute_name][find_parameter_position(multiparameter_name)] ||= parameter_value
-
end
-
-
attributes
-
end
-
-
1
def type_cast_attribute_value(multiparameter_name, value)
-
multiparameter_name =~ /\([0-9]*([if])\)/ ? value.send("to_" + $1) : value
-
end
-
-
1
def find_parameter_position(multiparameter_name)
-
multiparameter_name.scan(/\(([0-9]*).*\)/).first.first.to_i
-
end
-
-
1
class MultiparameterAttribute #:nodoc:
-
1
attr_reader :object, :name, :values, :column
-
-
1
def initialize(object, name, values)
-
@object = object
-
@name = name
-
@values = values
-
end
-
-
1
def read_value
-
return if values.values.compact.empty?
-
-
@column = object.class.reflect_on_aggregation(name.to_sym) || object.column_for_attribute(name)
-
klass = column.klass
-
-
if klass == Time
-
read_time
-
elsif klass == Date
-
read_date
-
else
-
read_other(klass)
-
end
-
end
-
-
1
private
-
-
1
def instantiate_time_object(set_values)
-
if object.class.send(:create_time_zone_conversion_attribute?, name, column)
-
Time.zone.local(*set_values)
-
else
-
Time.send(object.class.default_timezone, *set_values)
-
end
-
end
-
-
1
def read_time
-
# If column is a :time (and not :date or :timestamp) there is no need to validate if
-
# there are year/month/day fields
-
if column.type == :time
-
# if the column is a time set the values to their defaults as January 1, 1970, but only if they're nil
-
{ 1 => 1970, 2 => 1, 3 => 1 }.each do |key,value|
-
values[key] ||= value
-
end
-
else
-
# else column is a timestamp, so if Date bits were not provided, error
-
validate_required_parameters!([1,2,3])
-
-
# If Date bits were provided but blank, then return nil
-
return if blank_date_parameter?
-
end
-
-
max_position = extract_max_param(6)
-
set_values = values.values_at(*(1..max_position))
-
# If Time bits are not there, then default to 0
-
(3..5).each { |i| set_values[i] = set_values[i].presence || 0 }
-
instantiate_time_object(set_values)
-
end
-
-
1
def read_date
-
return if blank_date_parameter?
-
set_values = values.values_at(1,2,3)
-
begin
-
Date.new(*set_values)
-
rescue ArgumentError # if Date.new raises an exception on an invalid date
-
instantiate_time_object(set_values).to_date # we instantiate Time object and convert it back to a date thus using Time's logic in handling invalid dates
-
end
-
end
-
-
1
def read_other(klass)
-
max_position = extract_max_param
-
positions = (1..max_position)
-
validate_required_parameters!(positions)
-
-
set_values = values.values_at(*positions)
-
klass.new(*set_values)
-
end
-
-
# Checks whether some blank date parameter exists. Note that this is different
-
# than the validate_required_parameters! method, since it just checks for blank
-
# positions instead of missing ones, and does not raise in case one blank position
-
# exists. The caller is responsible to handle the case of this returning true.
-
1
def blank_date_parameter?
-
(1..3).any? { |position| values[position].blank? }
-
end
-
-
# If some position is not provided, it errors out a missing parameter exception.
-
1
def validate_required_parameters!(positions)
-
if missing_parameter = positions.detect { |position| !values.key?(position) }
-
raise ArgumentError.new("Missing Parameter - #{name}(#{missing_parameter})")
-
end
-
end
-
-
1
def extract_max_param(upper_cap = 100)
-
[values.keys.max, upper_cap].min
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/enumerable'
-
1
require 'mutex_m'
-
1
require 'thread_safe'
-
-
1
module ActiveRecord
-
# = Active Record Attribute Methods
-
1
module AttributeMethods
-
1
extend ActiveSupport::Concern
-
1
include ActiveModel::AttributeMethods
-
-
1
included do
-
1
initialize_generated_modules
-
1
include Read
-
1
include Write
-
1
include BeforeTypeCast
-
1
include Query
-
1
include PrimaryKey
-
1
include TimeZoneConversion
-
1
include Dirty
-
1
include Serialization
-
end
-
-
1
AttrNames = Module.new {
-
1
def self.set_name_cache(name, value)
-
28
const_name = "ATTR_#{name}"
-
28
unless const_defined? const_name
-
15
const_set const_name, value.dup.freeze
-
end
-
end
-
}
-
-
1
BLACKLISTED_CLASS_METHODS = %w(private public protected)
-
-
1
class AttributeMethodCache
-
1
def initialize
-
2
@module = Module.new
-
2
@method_cache = ThreadSafe::Cache.new
-
end
-
-
1
def [](name)
-
36
@method_cache.compute_if_absent(name) do
-
28
safe_name = name.unpack('h*').first
-
28
temp_method = "__temp__#{safe_name}"
-
28
ActiveRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name
-
28
@module.module_eval method_body(temp_method, safe_name), __FILE__, __LINE__
-
28
@module.instance_method temp_method
-
end
-
end
-
-
1
private
-
1
def method_body; raise NotImplementedError; end
-
end
-
-
1
class GeneratedAttributeMethods < Module; end # :nodoc:
-
-
1
module ClassMethods
-
1
def inherited(child_class) #:nodoc:
-
5
child_class.initialize_generated_modules
-
5
super
-
end
-
-
1
def initialize_generated_modules # :nodoc:
-
12
@generated_attribute_methods = GeneratedAttributeMethods.new { extend Mutex_m }
-
6
@attribute_methods_generated = false
-
6
include @generated_attribute_methods
-
end
-
-
# Generates all the attribute related methods for columns in the database
-
# accessors, mutators and query methods.
-
1
def define_attribute_methods # :nodoc:
-
55
return false if @attribute_methods_generated
-
# Use a mutex; we don't want two thread simultaneously trying to define
-
# attribute methods.
-
4
generated_attribute_methods.synchronize do
-
4
return false if @attribute_methods_generated
-
4
superclass.define_attribute_methods unless self == base_class
-
4
super(column_names)
-
4
@attribute_methods_generated = true
-
end
-
4
true
-
end
-
-
1
def undefine_attribute_methods # :nodoc:
-
5
generated_attribute_methods.synchronize do
-
5
super if @attribute_methods_generated
-
5
@attribute_methods_generated = false
-
end
-
end
-
-
# Raises a <tt>ActiveRecord::DangerousAttributeError</tt> exception when an
-
# \Active \Record method is defined in the model, otherwise +false+.
-
#
-
# class Person < ActiveRecord::Base
-
# def save
-
# 'already defined by Active Record'
-
# end
-
# end
-
#
-
# Person.instance_method_already_implemented?(:save)
-
# # => ActiveRecord::DangerousAttributeError: save is defined by ActiveRecord
-
#
-
# Person.instance_method_already_implemented?(:name)
-
# # => false
-
1
def instance_method_already_implemented?(method_name)
-
189
if dangerous_attribute_method?(method_name)
-
raise DangerousAttributeError, "#{method_name} is defined by Active Record"
-
end
-
-
189
if superclass == Base
-
189
super
-
else
-
# If ThisClass < ... < SomeSuperClass < ... < Base and SomeSuperClass
-
# defines its own attribute method, then we don't want to overwrite that.
-
defined = method_defined_within?(method_name, superclass, Base) &&
-
! superclass.instance_method(method_name).owner.is_a?(GeneratedAttributeMethods)
-
defined || super
-
end
-
end
-
-
# A method name is 'dangerous' if it is already (re)defined by Active Record, but
-
# not by any ancestors. (So 'puts' is not dangerous but 'save' is.)
-
1
def dangerous_attribute_method?(name) # :nodoc:
-
203
method_defined_within?(name, Base)
-
end
-
-
1
def method_defined_within?(name, klass, superklass = klass.superclass) # :nodoc:
-
206
if klass.method_defined?(name) || klass.private_method_defined?(name)
-
18
if superklass.method_defined?(name) || superklass.private_method_defined?(name)
-
klass.instance_method(name).owner != superklass.instance_method(name).owner
-
else
-
18
true
-
end
-
else
-
188
false
-
end
-
end
-
-
# A class method is 'dangerous' if it is already (re)defined by Active Record, but
-
# not by any ancestors. (So 'puts' is not dangerous but 'new' is.)
-
1
def dangerous_class_method?(method_name)
-
BLACKLISTED_CLASS_METHODS.include?(method_name.to_s) || class_method_defined_within?(method_name, Base)
-
end
-
-
1
def class_method_defined_within?(name, klass, superklass = klass.superclass) # :nodoc
-
if klass.respond_to?(name, true)
-
if superklass.respond_to?(name, true)
-
klass.method(name).owner != superklass.method(name).owner
-
else
-
true
-
end
-
else
-
false
-
end
-
end
-
-
1
def find_generated_attribute_method(method_name) # :nodoc:
-
3
klass = self
-
3
until klass == Base
-
3
gen_methods = klass.generated_attribute_methods
-
3
return gen_methods.instance_method(method_name) if method_defined_within?(method_name, gen_methods, Object)
-
klass = klass.superclass
-
end
-
nil
-
end
-
-
# Returns +true+ if +attribute+ is an attribute method and table exists,
-
# +false+ otherwise.
-
#
-
# class Person < ActiveRecord::Base
-
# end
-
#
-
# Person.attribute_method?('name') # => true
-
# Person.attribute_method?(:age=) # => true
-
# Person.attribute_method?(:nothing) # => false
-
1
def attribute_method?(attribute)
-
super || (table_exists? && column_names.include?(attribute.to_s.sub(/=$/, '')))
-
end
-
-
# Returns an array of column names as strings if it's not an abstract class and
-
# table exists. Otherwise it returns an empty array.
-
#
-
# class Person < ActiveRecord::Base
-
# end
-
#
-
# Person.attribute_names
-
# # => ["id", "created_at", "updated_at", "name", "age"]
-
1
def attribute_names
-
@attribute_names ||= if !abstract_class? && table_exists?
-
column_names
-
else
-
[]
-
end
-
end
-
end
-
-
# If we haven't generated any methods yet, generate them, then
-
# see if we've created the method we're looking for.
-
1
def method_missing(method, *args, &block) # :nodoc:
-
3
self.class.define_attribute_methods
-
3
if respond_to_without_attributes?(method)
-
# make sure to invoke the correct attribute method, as we might have gotten here via a `super`
-
# call in a overwritten attribute method
-
3
if attribute_method = self.class.find_generated_attribute_method(method)
-
# this is probably horribly slow, but should only happen at most once for a given AR class
-
3
attribute_method.bind(self).call(*args, &block)
-
else
-
return super unless respond_to_missing?(method, true)
-
send(method, *args, &block)
-
end
-
else
-
super
-
end
-
end
-
-
# A Person object with a name attribute can ask <tt>person.respond_to?(:name)</tt>,
-
# <tt>person.respond_to?(:name=)</tt>, and <tt>person.respond_to?(:name?)</tt>
-
# which will all return +true+. It also define the attribute methods if they have
-
# not been generated.
-
#
-
# class Person < ActiveRecord::Base
-
# end
-
#
-
# person = Person.new
-
# person.respond_to(:name) # => true
-
# person.respond_to(:name=) # => true
-
# person.respond_to(:name?) # => true
-
# person.respond_to('age') # => true
-
# person.respond_to('age=') # => true
-
# person.respond_to('age?') # => true
-
# person.respond_to(:nothing) # => false
-
1
def respond_to?(name, include_private = false)
-
36
name = name.to_s
-
36
self.class.define_attribute_methods
-
36
result = super
-
-
# If the result is false the answer is false.
-
36
return false unless result
-
-
# If the result is true then check for the select case.
-
# For queries selecting a subset of columns, return false for unselected columns.
-
# We check defined?(@attributes) not to issue warnings if called on objects that
-
# have been allocated but not yet initialized.
-
24
if defined?(@attributes) && @attributes.any? && self.class.column_names.include?(name)
-
12
return has_attribute?(name)
-
end
-
-
12
return true
-
end
-
-
# Returns +true+ if the given attribute is in the attributes hash, otherwise +false+.
-
#
-
# class Person < ActiveRecord::Base
-
# end
-
#
-
# person = Person.new
-
# person.has_attribute?(:name) # => true
-
# person.has_attribute?('age') # => true
-
# person.has_attribute?(:nothing) # => false
-
1
def has_attribute?(attr_name)
-
28
@attributes.has_key?(attr_name.to_s)
-
end
-
-
# Returns an array of names for the attributes available on this object.
-
#
-
# class Person < ActiveRecord::Base
-
# end
-
#
-
# person = Person.new
-
# person.attribute_names
-
# # => ["id", "created_at", "updated_at", "name", "age"]
-
1
def attribute_names
-
@attributes.keys
-
end
-
-
# Returns a hash of all the attributes with their names as keys and the values of the attributes as values.
-
#
-
# class Person < ActiveRecord::Base
-
# end
-
#
-
# person = Person.create(name: 'Francesco', age: 22)
-
# person.attributes
-
# # => {"id"=>3, "created_at"=>Sun, 21 Oct 2012 04:53:04, "updated_at"=>Sun, 21 Oct 2012 04:53:04, "name"=>"Francesco", "age"=>22}
-
1
def attributes
-
attribute_names.each_with_object({}) { |name, attrs|
-
attrs[name] = read_attribute(name)
-
}
-
end
-
-
# Placeholder so it can be overriden when needed by serialization
-
1
def attributes_for_coder # :nodoc:
-
attributes
-
end
-
-
# Returns an <tt>#inspect</tt>-like string for the value of the
-
# attribute +attr_name+. String attributes are truncated upto 50
-
# characters, Date and Time attributes are returned in the
-
# <tt>:db</tt> format, Array attributes are truncated upto 10 values.
-
# Other attributes return the value of <tt>#inspect</tt> without
-
# modification.
-
#
-
# person = Person.create!(name: 'David Heinemeier Hansson ' * 3)
-
#
-
# person.attribute_for_inspect(:name)
-
# # => "\"David Heinemeier Hansson David Heinemeier Hansson ...\""
-
#
-
# person.attribute_for_inspect(:created_at)
-
# # => "\"2012-10-22 00:15:07\""
-
#
-
# person.attribute_for_inspect(:tag_ids)
-
# # => "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...]"
-
1
def attribute_for_inspect(attr_name)
-
value = read_attribute(attr_name)
-
-
if value.is_a?(String) && value.length > 50
-
"#{value[0, 50]}...".inspect
-
elsif value.is_a?(Date) || value.is_a?(Time)
-
%("#{value.to_s(:db)}")
-
elsif value.is_a?(Array) && value.size > 10
-
inspected = value.first(10).inspect
-
%(#{inspected[0...-1]}, ...])
-
else
-
value.inspect
-
end
-
end
-
-
# Returns +true+ if the specified +attribute+ has been set by the user or by a
-
# database load and is neither +nil+ nor <tt>empty?</tt> (the latter only applies
-
# to objects that respond to <tt>empty?</tt>, most notably Strings). Otherwise, +false+.
-
# Note that it always returns +true+ with boolean attributes.
-
#
-
# class Task < ActiveRecord::Base
-
# end
-
#
-
# person = Task.new(title: '', is_done: false)
-
# person.attribute_present?(:title) # => false
-
# person.attribute_present?(:is_done) # => true
-
# person.name = 'Francesco'
-
# person.is_done = true
-
# person.attribute_present?(:title) # => true
-
# person.attribute_present?(:is_done) # => true
-
1
def attribute_present?(attribute)
-
2
value = read_attribute(attribute)
-
2
!value.nil? && !(value.respond_to?(:empty?) && value.empty?)
-
end
-
-
# Returns the column object for the named attribute. Returns +nil+ if the
-
# named attribute not exists.
-
#
-
# class Person < ActiveRecord::Base
-
# end
-
#
-
# person = Person.new
-
# person.column_for_attribute(:name) # the result depends on the ConnectionAdapter
-
# # => #<ActiveRecord::ConnectionAdapters::SQLite3Column:0x007ff4ab083980 @name="name", @sql_type="varchar(255)", @null=true, ...>
-
#
-
# person.column_for_attribute(:nothing)
-
# # => nil
-
1
def column_for_attribute(name)
-
# FIXME: should this return a null object for columns that don't exist?
-
172
self.class.columns_hash[name.to_s]
-
end
-
-
# Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
-
# "2004-12-12" in a date column is cast to a date object, like Date.new(2004, 12, 12)). It raises
-
# <tt>ActiveModel::MissingAttributeError</tt> if the identified attribute is missing.
-
#
-
# Alias for the <tt>read_attribute</tt> method.
-
#
-
# class Person < ActiveRecord::Base
-
# belongs_to :organization
-
# end
-
#
-
# person = Person.new(name: 'Francesco', age: '22')
-
# person[:name] # => "Francesco"
-
# person[:age] # => 22
-
#
-
# person = Person.select('id').first
-
# person[:name] # => ActiveModel::MissingAttributeError: missing attribute: name
-
# person[:organization_id] # => ActiveModel::MissingAttributeError: missing attribute: organization_id
-
1
def [](attr_name)
-
30
read_attribute(attr_name) { |n| missing_attribute(n, caller) }
-
end
-
-
# Updates the attribute identified by <tt>attr_name</tt> with the specified +value+.
-
# (Alias for the protected <tt>write_attribute</tt> method).
-
#
-
# class Person < ActiveRecord::Base
-
# end
-
#
-
# person = Person.new
-
# person[:age] = '22'
-
# person[:age] # => 22
-
# person[:age] # => Fixnum
-
1
def []=(attr_name, value)
-
4
write_attribute(attr_name, value)
-
end
-
-
1
protected
-
-
1
def clone_attributes(reader_method = :read_attribute, attributes = {}) # :nodoc:
-
attribute_names.each do |name|
-
attributes[name] = clone_attribute_value(reader_method, name)
-
end
-
attributes
-
end
-
-
1
def clone_attribute_value(reader_method, attribute_name) # :nodoc:
-
34
value = send(reader_method, attribute_name)
-
34
value.duplicable? ? value.clone : value
-
rescue TypeError, NoMethodError
-
value
-
end
-
-
1
def arel_attributes_with_values_for_create(attribute_names) # :nodoc:
-
6
arel_attributes_with_values(attributes_for_create(attribute_names))
-
end
-
-
1
def arel_attributes_with_values_for_update(attribute_names) # :nodoc:
-
2
arel_attributes_with_values(attributes_for_update(attribute_names))
-
end
-
-
1
def attribute_method?(attr_name) # :nodoc:
-
# We check defined? because Syck calls respond_to? before actually calling initialize.
-
12
defined?(@attributes) && @attributes.include?(attr_name)
-
end
-
-
1
private
-
-
# Returns a Hash of the Arel::Attributes and attribute values that have been
-
# typecasted for use in an Arel insert/update method.
-
1
def arel_attributes_with_values(attribute_names)
-
8
attrs = {}
-
8
arel_table = self.class.arel_table
-
-
8
attribute_names.each do |name|
-
24
attrs[arel_table[name]] = typecasted_attribute_value(name)
-
end
-
8
attrs
-
end
-
-
# Filters the primary keys and readonly attributes from the attribute names.
-
1
def attributes_for_update(attribute_names)
-
2
attribute_names.select do |name|
-
column_for_attribute(name) && !readonly_attribute?(name)
-
end
-
end
-
-
# Filters out the primary keys, from the attribute names, when the primary
-
# key is to be generated (e.g. the id attribute has no value).
-
1
def attributes_for_create(attribute_names)
-
6
attribute_names.select do |name|
-
24
column_for_attribute(name) && !(pk_attribute?(name) && id.nil?)
-
end
-
end
-
-
1
def readonly_attribute?(name)
-
self.class.readonly_attributes.include?(name)
-
end
-
-
1
def pk_attribute?(name)
-
24
column_for_attribute(name).primary
-
end
-
-
1
def typecasted_attribute_value(name)
-
# FIXME: we need @attributes to be used consistently.
-
# If the values stored in @attributes were already typecasted, this code
-
# could be simplified
-
24
read_attribute(name)
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
module AttributeMethods
-
# = Active Record Attribute Methods Before Type Cast
-
#
-
# <tt>ActiveRecord::AttributeMethods::BeforeTypeCast</tt> provides a way to
-
# read the value of the attributes before typecasting and deserialization.
-
#
-
# class Task < ActiveRecord::Base
-
# end
-
#
-
# task = Task.new(id: '1', completed_on: '2012-10-21')
-
# task.id # => 1
-
# task.completed_on # => Sun, 21 Oct 2012
-
#
-
# task.attributes_before_type_cast
-
# # => {"id"=>"1", "completed_on"=>"2012-10-21", ... }
-
# task.read_attribute_before_type_cast('id') # => "1"
-
# task.read_attribute_before_type_cast('completed_on') # => "2012-10-21"
-
#
-
# In addition to #read_attribute_before_type_cast and #attributes_before_type_cast,
-
# it declares a method for all attributes with the <tt>*_before_type_cast</tt>
-
# suffix.
-
#
-
# task.id_before_type_cast # => "1"
-
# task.completed_on_before_type_cast # => "2012-10-21"
-
1
module BeforeTypeCast
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
attribute_method_suffix "_before_type_cast"
-
end
-
-
# Returns the value of the attribute identified by +attr_name+ before
-
# typecasting and deserialization.
-
#
-
# class Task < ActiveRecord::Base
-
# end
-
#
-
# task = Task.new(id: '1', completed_on: '2012-10-21')
-
# task.read_attribute('id') # => 1
-
# task.read_attribute_before_type_cast('id') # => '1'
-
# task.read_attribute('completed_on') # => Sun, 21 Oct 2012
-
# task.read_attribute_before_type_cast('completed_on') # => "2012-10-21"
-
# task.read_attribute_before_type_cast(:completed_on) # => "2012-10-21"
-
1
def read_attribute_before_type_cast(attr_name)
-
@attributes[attr_name.to_s]
-
end
-
-
# Returns a hash of attributes before typecasting and deserialization.
-
#
-
# class Task < ActiveRecord::Base
-
# end
-
#
-
# task = Task.new(title: nil, is_done: true, completed_on: '2012-10-21')
-
# task.attributes
-
# # => {"id"=>nil, "title"=>nil, "is_done"=>true, "completed_on"=>Sun, 21 Oct 2012, "created_at"=>nil, "updated_at"=>nil}
-
# task.attributes_before_type_cast
-
# # => {"id"=>nil, "title"=>nil, "is_done"=>true, "completed_on"=>"2012-10-21", "created_at"=>nil, "updated_at"=>nil}
-
1
def attributes_before_type_cast
-
@attributes
-
end
-
-
1
private
-
-
# Handle *_before_type_cast for method_missing.
-
1
def attribute_before_type_cast(attribute_name)
-
read_attribute_before_type_cast(attribute_name)
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/module/attribute_accessors'
-
-
1
module ActiveRecord
-
1
module AttributeMethods
-
1
module Dirty # :nodoc:
-
1
extend ActiveSupport::Concern
-
-
1
include ActiveModel::Dirty
-
-
1
included do
-
1
if self < ::ActiveRecord::Timestamp
-
raise "You cannot include Dirty after Timestamp"
-
end
-
-
1
class_attribute :partial_writes, instance_writer: false
-
1
self.partial_writes = true
-
end
-
-
# Attempts to +save+ the record and clears changed attributes if successful.
-
1
def save(*)
-
4
if status = super
-
4
changes_applied
-
end
-
4
status
-
end
-
-
# Attempts to <tt>save!</tt> the record and clears changed attributes if successful.
-
1
def save!(*)
-
4
super.tap do
-
4
changes_applied
-
end
-
end
-
-
# <tt>reload</tt> the record and clears changed attributes.
-
1
def reload(*)
-
super.tap do
-
reset_changes
-
end
-
end
-
-
1
def initialize_dup(other) # :nodoc:
-
super
-
init_changed_attributes
-
end
-
-
1
private
-
1
def initialize_internals_callback
-
8
super
-
8
init_changed_attributes
-
end
-
-
1
def init_changed_attributes
-
8
@changed_attributes = nil
-
# Intentionally avoid using #column_defaults since overridden defaults (as is done in
-
# optimistic locking) won't get written unless they get marked as changed
-
8
self.class.columns.each do |c|
-
52
attr, orig_value = c.name, c.default
-
52
changed_attributes[attr] = orig_value if _field_changed?(attr, orig_value, @attributes[attr])
-
end
-
end
-
-
# Wrap write_attribute to remember original attribute value.
-
1
def write_attribute(attr, value)
-
36
attr = attr.to_s
-
-
36
save_changed_attribute(attr, value)
-
-
36
super(attr, value)
-
end
-
-
1
def save_changed_attribute(attr, value)
-
# The attribute already has an unsaved change.
-
36
if attribute_changed?(attr)
-
2
old = changed_attributes[attr]
-
2
changed_attributes.delete(attr) unless _field_changed?(attr, old, value)
-
else
-
34
old = clone_attribute_value(:read_attribute, attr)
-
34
changed_attributes[attr] = old if _field_changed?(attr, old, value)
-
end
-
end
-
-
1
def _update_record(*)
-
2
partial_writes? ? super(keys_for_partial_write) : super
-
end
-
-
1
def _create_record(*)
-
6
partial_writes? ? super(keys_for_partial_write) : super
-
end
-
-
# Serialized attributes should always be written in case they've been
-
# changed in place.
-
1
def keys_for_partial_write
-
8
changed
-
end
-
-
1
def _field_changed?(attr, old, value)
-
88
if column = column_for_attribute(attr)
-
if column.number? && (changes_from_nil_to_empty_string?(column, old, value) ||
-
88
changes_from_zero_to_string?(old, value))
-
6
value = nil
-
else
-
82
value = column.type_cast(value)
-
end
-
end
-
-
88
old != value
-
end
-
-
1
def changes_from_nil_to_empty_string?(column, old, value)
-
# For nullable numeric columns, NULL gets stored in database for blank (i.e. '') values.
-
# Hence we don't record it as a change if the value changes from nil to ''.
-
# If an old value of 0 is set to '' we want this to get changed to nil as otherwise it'll
-
# be typecast back to 0 (''.to_i => 0)
-
30
column.null && (old.nil? || old == 0) && value.blank?
-
end
-
-
1
def changes_from_zero_to_string?(old, value)
-
# For columns with old 0 and value non-empty string
-
24
old == 0 && value.is_a?(String) && value.present? && non_zero?(value)
-
end
-
-
1
def non_zero?(value)
-
value !~ /\A0+(\.0+)?\z/
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
module AttributeMethods
-
1
module Query
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
attribute_method_suffix "?"
-
end
-
-
1
def query_attribute(attr_name)
-
value = read_attribute(attr_name) { |n| missing_attribute(n, caller) }
-
-
case value
-
when true then true
-
when false, nil then false
-
else
-
column = self.class.columns_hash[attr_name]
-
if column.nil?
-
if Numeric === value || value !~ /[^0-9]/
-
!value.to_i.zero?
-
else
-
return false if ActiveRecord::ConnectionAdapters::Column::FALSE_VALUES.include?(value)
-
!value.blank?
-
end
-
elsif column.number?
-
!value.zero?
-
else
-
!value.blank?
-
end
-
end
-
end
-
-
1
private
-
# Handle *? for method_missing.
-
1
def attribute?(attribute_name)
-
query_attribute(attribute_name)
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/module/method_transplanting'
-
-
1
module ActiveRecord
-
1
module AttributeMethods
-
1
module Read
-
1
ReaderMethodCache = Class.new(AttributeMethodCache) {
-
1
private
-
# We want to generate the methods via module_eval rather than
-
# define_method, because define_method is slower on dispatch.
-
# Evaluating many similar methods may use more memory as the instruction
-
# sequences are duplicated and cached (in MRI). define_method may
-
# be slower on dispatch, but if you're careful about the closure
-
# created, then define_method will consume much less memory.
-
#
-
# But sometimes the database might return columns with
-
# characters that are not allowed in normal method names (like
-
# 'my_column(omg)'. So to work around this we first define with
-
# the __temp__ identifier, and then use alias method to rename
-
# it to what we want.
-
#
-
# We are also defining a constant to hold the frozen string of
-
# the attribute name. Using a constant means that we do not have
-
# to allocate an object on each call to the attribute method.
-
# Making it frozen means that it doesn't get duped when used to
-
# key the @attributes_cache in read_attribute.
-
1
def method_body(method_name, const_name)
-
<<-EOMETHOD
-
15
def #{method_name}
-
name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{const_name}
-
read_attribute(name) { |n| missing_attribute(n, caller) }
-
end
-
EOMETHOD
-
end
-
}.new
-
-
1
extend ActiveSupport::Concern
-
-
1
ATTRIBUTE_TYPES_CACHED_BY_DEFAULT = [:datetime, :timestamp, :time, :date]
-
-
1
included do
-
1
class_attribute :attribute_types_cached_by_default, instance_writer: false
-
1
self.attribute_types_cached_by_default = ATTRIBUTE_TYPES_CACHED_BY_DEFAULT
-
end
-
-
1
module ClassMethods
-
# +cache_attributes+ allows you to declare which converted attribute
-
# values should be cached. Usually caching only pays off for attributes
-
# with expensive conversion methods, like time related columns (e.g.
-
# +created_at+, +updated_at+).
-
1
def cache_attributes(*attribute_names)
-
cached_attributes.merge attribute_names.map { |attr| attr.to_s }
-
end
-
-
# Returns the attributes which are cached. By default time related columns
-
# with datatype <tt>:datetime, :timestamp, :time, :date</tt> are cached.
-
1
def cached_attributes
-
120
@cached_attributes ||= columns.select { |c| cacheable_column?(c) }.map { |col| col.name }.to_set
-
end
-
-
# Returns +true+ if the provided attribute is being cached.
-
1
def cache_attribute?(attr_name)
-
78
cached_attributes.include?(attr_name)
-
end
-
-
1
protected
-
-
1
if Module.methods_transplantable?
-
1
def define_method_attribute(name)
-
21
method = ReaderMethodCache[name]
-
42
generated_attribute_methods.module_eval { define_method name, method }
-
end
-
else
-
def define_method_attribute(name)
-
safe_name = name.unpack('h*').first
-
temp_method = "__temp__#{safe_name}"
-
-
ActiveRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name
-
-
generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
-
def #{temp_method}
-
name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{safe_name}
-
read_attribute(name) { |n| missing_attribute(n, caller) }
-
end
-
STR
-
-
generated_attribute_methods.module_eval do
-
alias_method name, temp_method
-
undef_method temp_method
-
end
-
end
-
end
-
-
1
private
-
-
1
def cacheable_column?(column)
-
21
if attribute_types_cached_by_default == ATTRIBUTE_TYPES_CACHED_BY_DEFAULT
-
21
! serialized_attributes.include? column.name
-
else
-
attribute_types_cached_by_default.include?(column.type)
-
end
-
end
-
end
-
-
# Returns the value of the attribute identified by <tt>attr_name</tt> after
-
# it has been typecast (for example, "2004-12-12" in a date column is cast
-
# to a date object, like Date.new(2004, 12, 12)).
-
1
def read_attribute(attr_name)
-
# If it's cached, just return it
-
# We use #[] first as a perf optimization for non-nil values. See https://gist.github.com/jonleighton/3552829.
-
196
name = attr_name.to_s
-
@attributes_cache[name] || @attributes_cache.fetch(name) {
-
80
column = @column_types_override[name] if @column_types_override
-
80
column ||= @column_types[name]
-
-
return @attributes.fetch(name) {
-
2
if name == 'id' && self.class.primary_key != name
-
read_attribute(self.class.primary_key)
-
end
-
80
} unless column
-
-
78
value = @attributes.fetch(name) {
-
return block_given? ? yield(name) : nil
-
}
-
-
78
if self.class.cache_attribute?(name)
-
78
@attributes_cache[name] = column.type_cast(value)
-
else
-
column.type_cast value
-
end
-
196
}
-
end
-
-
1
private
-
-
1
def attribute(attribute_name)
-
read_attribute(attribute_name)
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
module AttributeMethods
-
1
module Serialization
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
# Returns a hash of all the attributes that have been specified for
-
# serialization as keys and their class restriction as values.
-
1
class_attribute :serialized_attributes, instance_accessor: false
-
1
self.serialized_attributes = {}
-
end
-
-
1
module ClassMethods
-
##
-
# :method: serialized_attributes
-
#
-
# Returns a hash of all the attributes that have been specified for
-
# serialization as keys and their class restriction as values.
-
-
# If you have an attribute that needs to be saved to the database as an
-
# object, and retrieved as the same object, then specify the name of that
-
# attribute using this method and it will be handled automatically. The
-
# serialization is done through YAML. If +class_name+ is specified, the
-
# serialized object must be of that class on retrieval or
-
# <tt>SerializationTypeMismatch</tt> will be raised.
-
#
-
# A notable side effect of serialized attributes is that the model will
-
# be updated on every save, even if it is not dirty.
-
#
-
# ==== Parameters
-
#
-
# * +attr_name+ - The field name that should be serialized.
-
# * +class_name_or_coder+ - Optional, a coder object, which responds to `.load` / `.dump`
-
# or a class name that the object type should be equal to.
-
#
-
# ==== Example
-
#
-
# # Serialize a preferences attribute.
-
# class User < ActiveRecord::Base
-
# serialize :preferences
-
# end
-
#
-
# # Serialize preferences using JSON as coder.
-
# class User < ActiveRecord::Base
-
# serialize :preferences, JSON
-
# end
-
#
-
# # Serialize preferences as Hash using YAML coder.
-
# class User < ActiveRecord::Base
-
# serialize :preferences, Hash
-
# end
-
1
def serialize(attr_name, class_name_or_coder = Object)
-
include Behavior
-
-
# When ::JSON is used, force it to go through the Active Support JSON encoder
-
# to ensure special objects (e.g. Active Record models) are dumped correctly
-
# using the #as_json hook.
-
coder = if class_name_or_coder == ::JSON
-
Coders::JSON
-
elsif [:load, :dump].all? { |x| class_name_or_coder.respond_to?(x) }
-
class_name_or_coder
-
else
-
Coders::YAMLColumn.new(class_name_or_coder)
-
end
-
-
# merge new serialized attribute and create new hash to ensure that each class in inheritance hierarchy
-
# has its own hash of own serialized attributes
-
self.serialized_attributes = serialized_attributes.merge(attr_name.to_s => coder)
-
end
-
end
-
-
1
class Type # :nodoc:
-
1
def initialize(column)
-
@column = column
-
end
-
-
1
def type_cast(value)
-
if value.state == :serialized
-
value.unserialized_value @column.type_cast value.value
-
else
-
value.unserialized_value
-
end
-
end
-
-
1
def type
-
@column.type
-
end
-
-
1
def accessor
-
ActiveRecord::Store::IndifferentHashAccessor
-
end
-
end
-
-
1
class Attribute < Struct.new(:coder, :value, :state) # :nodoc:
-
1
def unserialized_value(v = value)
-
state == :serialized ? unserialize(v) : value
-
end
-
-
1
def serialized_value
-
state == :unserialized ? serialize : value
-
end
-
-
1
def unserialize(v)
-
self.state = :unserialized
-
self.value = coder.load(v)
-
end
-
-
1
def serialize
-
self.state = :serialized
-
self.value = coder.dump(value)
-
end
-
end
-
-
# This is only added to the model when serialize is called, which
-
# ensures we do not make things slower when serialization is not used.
-
1
module Behavior # :nodoc:
-
1
extend ActiveSupport::Concern
-
-
1
module ClassMethods # :nodoc:
-
1
def initialize_attributes(attributes, options = {})
-
serialized = (options.delete(:serialized) { true }) ? :serialized : :unserialized
-
super(attributes, options)
-
-
serialized_attributes.each do |key, coder|
-
if attributes.key?(key)
-
attributes[key] = Attribute.new(coder, attributes[key], serialized)
-
end
-
end
-
-
attributes
-
end
-
end
-
-
1
def should_record_timestamps?
-
super || (self.record_timestamps && (attributes.keys & self.class.serialized_attributes.keys).present?)
-
end
-
-
1
def keys_for_partial_write
-
super | (attributes.keys & self.class.serialized_attributes.keys)
-
end
-
-
1
def type_cast_attribute_for_write(column, value)
-
if column && coder = self.class.serialized_attributes[column.name]
-
Attribute.new(coder, value, :unserialized)
-
else
-
super
-
end
-
end
-
-
1
def raw_type_cast_attribute_for_write(column, value)
-
if column && coder = self.class.serialized_attributes[column.name]
-
Attribute.new(coder, value, :serialized)
-
else
-
super
-
end
-
end
-
-
1
def _field_changed?(attr, old, value)
-
if self.class.serialized_attributes.include?(attr)
-
old != value
-
else
-
super
-
end
-
end
-
-
1
def read_attribute_before_type_cast(attr_name)
-
if self.class.serialized_attributes.include?(attr_name)
-
super.unserialized_value
-
else
-
super
-
end
-
end
-
-
1
def attributes_before_type_cast
-
super.dup.tap do |attributes|
-
self.class.serialized_attributes.each_key do |key|
-
if attributes.key?(key)
-
attributes[key] = attributes[key].unserialized_value
-
end
-
end
-
end
-
end
-
-
1
def typecasted_attribute_value(name)
-
if self.class.serialized_attributes.include?(name)
-
@attributes[name].serialized_value
-
else
-
super
-
end
-
end
-
-
1
def attributes_for_coder
-
attribute_names.each_with_object({}) do |name, attrs|
-
attrs[name] = if self.class.serialized_attributes.include?(name)
-
@attributes[name].serialized_value
-
else
-
read_attribute(name)
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
module AttributeMethods
-
1
module TimeZoneConversion
-
1
class Type # :nodoc:
-
1
def initialize(column)
-
10
@column = column
-
end
-
-
1
def type_cast(value)
-
24
value = @column.type_cast(value)
-
24
value.acts_like?(:time) ? value.in_time_zone : value
-
end
-
-
1
def type
-
@column.type
-
end
-
end
-
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
mattr_accessor :time_zone_aware_attributes, instance_writer: false
-
1
self.time_zone_aware_attributes = false
-
-
1
class_attribute :skip_time_zone_conversion_for_attributes, instance_writer: false
-
1
self.skip_time_zone_conversion_for_attributes = []
-
end
-
-
1
module ClassMethods
-
1
protected
-
# Defined for all +datetime+ and +timestamp+ attributes when +time_zone_aware_attributes+ are enabled.
-
# This enhanced write method will automatically convert the time passed to it to the zone stored in Time.zone.
-
1
def define_method_attribute=(attr_name)
-
21
if create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name])
-
6
method_body, line = <<-EOV, __LINE__ + 1
-
def #{attr_name}=(time)
-
time_with_zone = time.respond_to?(:in_time_zone) ? time.in_time_zone : nil
-
previous_time = attribute_changed?("#{attr_name}") ? changed_attributes["#{attr_name}"] : read_attribute(:#{attr_name})
-
write_attribute(:#{attr_name}, time)
-
#{attr_name}_will_change! if previous_time != time_with_zone
-
@attributes_cache["#{attr_name}"] = time_with_zone
-
end
-
EOV
-
6
generated_attribute_methods.module_eval(method_body, __FILE__, line)
-
else
-
15
super
-
end
-
end
-
-
1
private
-
1
def create_time_zone_conversion_attribute?(name, column)
-
time_zone_aware_attributes &&
-
42
!self.skip_time_zone_conversion_for_attributes.include?(name.to_sym) &&
-
42
(:datetime == column.type || :timestamp == column.type)
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/module/method_transplanting'
-
-
1
module ActiveRecord
-
1
module AttributeMethods
-
1
module Write
-
1
WriterMethodCache = Class.new(AttributeMethodCache) {
-
1
private
-
-
1
def method_body(method_name, const_name)
-
<<-EOMETHOD
-
13
def #{method_name}(value)
-
name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{const_name}
-
write_attribute(name, value)
-
end
-
EOMETHOD
-
end
-
}.new
-
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
attribute_method_suffix "="
-
end
-
-
1
module ClassMethods
-
1
protected
-
-
1
if Module.methods_transplantable?
-
# See define_method_attribute in read.rb for an explanation of
-
# this code.
-
1
def define_method_attribute=(name)
-
15
method = WriterMethodCache[name]
-
15
generated_attribute_methods.module_eval {
-
15
define_method "#{name}=", method
-
}
-
end
-
else
-
def define_method_attribute=(name)
-
safe_name = name.unpack('h*').first
-
ActiveRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name
-
-
generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
-
def __temp__#{safe_name}=(value)
-
name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{safe_name}
-
write_attribute(name, value)
-
end
-
alias_method #{(name + '=').inspect}, :__temp__#{safe_name}=
-
undef_method :__temp__#{safe_name}=
-
STR
-
end
-
end
-
end
-
-
# Updates the attribute identified by <tt>attr_name</tt> with the
-
# specified +value+. Empty strings for fixnum and float columns are
-
# turned into +nil+.
-
1
def write_attribute(attr_name, value)
-
36
write_attribute_with_type_cast(attr_name, value, :type_cast_attribute_for_write)
-
end
-
-
1
def raw_write_attribute(attr_name, value)
-
write_attribute_with_type_cast(attr_name, value, :raw_type_cast_attribute_for_write)
-
end
-
-
1
private
-
# Handle *= for method_missing.
-
1
def attribute=(attribute_name, value)
-
write_attribute(attribute_name, value)
-
end
-
-
1
def type_cast_attribute_for_write(column, value)
-
36
return value unless column
-
-
36
column.type_cast_for_write value
-
end
-
1
alias_method :raw_type_cast_attribute_for_write, :type_cast_attribute_for_write
-
-
1
def write_attribute_with_type_cast(attr_name, value, type_cast_method)
-
36
attr_name = attr_name.to_s
-
36
attr_name = self.class.primary_key if attr_name == 'id' && self.class.primary_key
-
36
@attributes_cache.delete(attr_name)
-
36
column = column_for_attribute(attr_name)
-
-
# If we're dealing with a binary column, write the data to the cache
-
# so we don't attempt to typecast multiple times.
-
36
if column && column.binary?
-
@attributes_cache[attr_name] = value
-
end
-
-
36
if column || @attributes.has_key?(attr_name)
-
36
@attributes[attr_name] = send(type_cast_method, column, value)
-
else
-
raise ActiveModel::MissingAttributeError, "can't write unknown attribute `#{attr_name}'"
-
end
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
# = Active Record Autosave Association
-
#
-
# +AutosaveAssociation+ is a module that takes care of automatically saving
-
# associated records when their parent is saved. In addition to saving, it
-
# also destroys any associated records that were marked for destruction.
-
# (See +mark_for_destruction+ and <tt>marked_for_destruction?</tt>).
-
#
-
# Saving of the parent, its associations, and the destruction of marked
-
# associations, all happen inside a transaction. This should never leave the
-
# database in an inconsistent state.
-
#
-
# If validations for any of the associations fail, their error messages will
-
# be applied to the parent.
-
#
-
# Note that it also means that associations marked for destruction won't
-
# be destroyed directly. They will however still be marked for destruction.
-
#
-
# Note that <tt>autosave: false</tt> is not same as not declaring <tt>:autosave</tt>.
-
# When the <tt>:autosave</tt> option is not present then new association records are
-
# saved but the updated association records are not saved.
-
#
-
# == Validation
-
#
-
# Children records are validated unless <tt>:validate</tt> is +false+.
-
#
-
# == Callbacks
-
#
-
# Association with autosave option defines several callbacks on your
-
# model (before_save, after_create, after_update). Please note that
-
# callbacks are executed in the order they were defined in
-
# model. You should avoid modifying the association content, before
-
# autosave callbacks are executed. Placing your callbacks after
-
# associations is usually a good practice.
-
#
-
# === One-to-one Example
-
#
-
# class Post < ActiveRecord::Base
-
# has_one :author, autosave: true
-
# end
-
#
-
# Saving changes to the parent and its associated model can now be performed
-
# automatically _and_ atomically:
-
#
-
# post = Post.find(1)
-
# post.title # => "The current global position of migrating ducks"
-
# post.author.name # => "alloy"
-
#
-
# post.title = "On the migration of ducks"
-
# post.author.name = "Eloy Duran"
-
#
-
# post.save
-
# post.reload
-
# post.title # => "On the migration of ducks"
-
# post.author.name # => "Eloy Duran"
-
#
-
# Destroying an associated model, as part of the parent's save action, is as
-
# simple as marking it for destruction:
-
#
-
# post.author.mark_for_destruction
-
# post.author.marked_for_destruction? # => true
-
#
-
# Note that the model is _not_ yet removed from the database:
-
#
-
# id = post.author.id
-
# Author.find_by(id: id).nil? # => false
-
#
-
# post.save
-
# post.reload.author # => nil
-
#
-
# Now it _is_ removed from the database:
-
#
-
# Author.find_by(id: id).nil? # => true
-
#
-
# === One-to-many Example
-
#
-
# When <tt>:autosave</tt> is not declared new children are saved when their parent is saved:
-
#
-
# class Post < ActiveRecord::Base
-
# has_many :comments # :autosave option is not declared
-
# end
-
#
-
# post = Post.new(title: 'ruby rocks')
-
# post.comments.build(body: 'hello world')
-
# post.save # => saves both post and comment
-
#
-
# post = Post.create(title: 'ruby rocks')
-
# post.comments.build(body: 'hello world')
-
# post.save # => saves both post and comment
-
#
-
# post = Post.create(title: 'ruby rocks')
-
# post.comments.create(body: 'hello world')
-
# post.save # => saves both post and comment
-
#
-
# When <tt>:autosave</tt> is true all children are saved, no matter whether they
-
# are new records or not:
-
#
-
# class Post < ActiveRecord::Base
-
# has_many :comments, autosave: true
-
# end
-
#
-
# post = Post.create(title: 'ruby rocks')
-
# post.comments.create(body: 'hello world')
-
# post.comments[0].body = 'hi everyone'
-
# post.comments.build(body: "good morning.")
-
# post.title += "!"
-
# post.save # => saves both post and comments.
-
#
-
# Destroying one of the associated models as part of the parent's save action
-
# is as simple as marking it for destruction:
-
#
-
# post.comments # => [#<Comment id: 1, ...>, #<Comment id: 2, ...]>
-
# post.comments[1].mark_for_destruction
-
# post.comments[1].marked_for_destruction? # => true
-
# post.comments.length # => 2
-
#
-
# Note that the model is _not_ yet removed from the database:
-
#
-
# id = post.comments.last.id
-
# Comment.find_by(id: id).nil? # => false
-
#
-
# post.save
-
# post.reload.comments.length # => 1
-
#
-
# Now it _is_ removed from the database:
-
#
-
# Comment.find_by(id: id).nil? # => true
-
-
1
module AutosaveAssociation
-
1
extend ActiveSupport::Concern
-
-
1
module AssociationBuilderExtension #:nodoc:
-
1
def self.build(model, reflection)
-
14
model.send(:add_autosave_association_callbacks, reflection)
-
end
-
-
1
def self.valid_options
-
14
[ :autosave ]
-
end
-
end
-
-
1
included do
-
1
Associations::Builder::Association.extensions << AssociationBuilderExtension
-
end
-
-
1
module ClassMethods
-
1
private
-
-
1
def define_non_cyclic_method(name, &block)
-
25
return if method_defined?(name)
-
25
define_method(name) do |*args|
-
52
result = true; @_already_called ||= {}
-
# Loop prevention for validation of associations
-
52
unless @_already_called[name]
-
52
begin
-
52
@_already_called[name]=true
-
52
result = instance_eval(&block)
-
ensure
-
52
@_already_called[name]=false
-
end
-
end
-
-
52
result
-
end
-
end
-
-
# Adds validation and save callbacks for the association as specified by
-
# the +reflection+.
-
#
-
# For performance reasons, we don't check whether to validate at runtime.
-
# However the validation and callback methods are lazy and those methods
-
# get created when they are invoked for the very first time. However,
-
# this can change, for instance, when using nested attributes, which is
-
# called _after_ the association has been defined. Since we don't want
-
# the callbacks to get defined multiple times, there are guards that
-
# check if the save or validation methods have already been defined
-
# before actually defining them.
-
1
def add_autosave_association_callbacks(reflection)
-
14
save_method = :"autosave_associated_records_for_#{reflection.name}"
-
14
validation_method = :"validate_associated_records_for_#{reflection.name}"
-
14
collection = reflection.collection?
-
-
14
if collection
-
11
before_save :before_save_collection_association
-
-
35
define_non_cyclic_method(save_method) { save_collection_association(reflection) }
-
# Doesn't use after_save as that would save associations added in after_create/after_update twice
-
11
after_create save_method
-
11
after_update save_method
-
elsif reflection.macro == :has_one
-
define_method(save_method) { save_has_one_association(reflection) } unless method_defined?(save_method)
-
# Configures two callbacks instead of a single after_save so that
-
# the model may rely on their execution order relative to its
-
# own callbacks.
-
#
-
# For example, given that after_creates run before after_saves, if
-
# we configured instead an after_save there would be no way to fire
-
# a custom after_create callback after the child association gets
-
# created.
-
after_create save_method
-
after_update save_method
-
else
-
7
define_non_cyclic_method(save_method) { save_belongs_to_association(reflection) }
-
3
before_save save_method
-
end
-
-
14
if reflection.validate? && !method_defined?(validation_method)
-
11
method = (collection ? :validate_collection_association : :validate_single_association)
-
35
define_non_cyclic_method(validation_method) { send(method, reflection) }
-
11
validate validation_method
-
end
-
end
-
end
-
-
# Reloads the attributes of the object as usual and clears <tt>marked_for_destruction</tt> flag.
-
1
def reload(options = nil)
-
@marked_for_destruction = false
-
@destroyed_by_association = nil
-
super
-
end
-
-
# Marks this record to be destroyed as part of the parents save transaction.
-
# This does _not_ actually destroy the record instantly, rather child record will be destroyed
-
# when <tt>parent.save</tt> is called.
-
#
-
# Only useful if the <tt>:autosave</tt> option on the parent is enabled for this associated model.
-
1
def mark_for_destruction
-
@marked_for_destruction = true
-
end
-
-
# Returns whether or not this record will be destroyed as part of the parents save transaction.
-
#
-
# Only useful if the <tt>:autosave</tt> option on the parent is enabled for this associated model.
-
1
def marked_for_destruction?
-
@marked_for_destruction
-
end
-
-
# Records the association that is being destroyed and destroying this
-
# record in the process.
-
1
def destroyed_by_association=(reflection)
-
@destroyed_by_association = reflection
-
end
-
-
# Returns the association for the parent being destroyed.
-
#
-
# Used to avoid updating the counter cache unnecessarily.
-
1
def destroyed_by_association
-
@destroyed_by_association
-
end
-
-
# Returns whether or not this record has been changed in any way (including whether
-
# any of its nested autosave associations are likewise changed)
-
1
def changed_for_autosave?
-
new_record? || changed? || marked_for_destruction? || nested_records_changed_for_autosave?
-
end
-
-
1
private
-
-
# Returns the record for an association collection that should be validated
-
# or saved. If +autosave+ is +false+ only new records will be returned,
-
# unless the parent is/was a new record itself.
-
1
def associated_records_to_validate_or_save(association, new_record, autosave)
-
if new_record
-
association && association.target
-
elsif autosave
-
association.target.find_all { |record| record.changed_for_autosave? }
-
else
-
association.target.find_all { |record| record.new_record? }
-
end
-
end
-
-
# go through nested autosave associations that are loaded in memory (without loading
-
# any new ones), and return true if is changed for autosave
-
1
def nested_records_changed_for_autosave?
-
self.class._reflections.values.any? do |reflection|
-
if reflection.options[:autosave]
-
association = association_instance_get(reflection.name)
-
association && Array.wrap(association.target).any? { |a| a.changed_for_autosave? }
-
end
-
end
-
end
-
-
# Validate the association if <tt>:validate</tt> or <tt>:autosave</tt> is
-
# turned on for the association.
-
1
def validate_single_association(reflection)
-
association = association_instance_get(reflection.name)
-
record = association && association.reader
-
association_valid?(reflection, record) if record
-
end
-
-
# Validate the associated records if <tt>:validate</tt> or
-
# <tt>:autosave</tt> is turned on for the association specified by
-
# +reflection+.
-
1
def validate_collection_association(reflection)
-
24
if association = association_instance_get(reflection.name)
-
if records = associated_records_to_validate_or_save(association, new_record?, reflection.options[:autosave])
-
records.each { |record| association_valid?(reflection, record) }
-
end
-
end
-
end
-
-
# Returns whether or not the association is valid and applies any errors to
-
# the parent, <tt>self</tt>, if it wasn't. Skips any <tt>:autosave</tt>
-
# enabled records if they're marked_for_destruction? or destroyed.
-
1
def association_valid?(reflection, record)
-
return true if record.destroyed? || record.marked_for_destruction?
-
-
unless valid = record.valid?
-
if reflection.options[:autosave]
-
record.errors.each do |attribute, message|
-
attribute = "#{reflection.name}.#{attribute}"
-
errors[attribute] << message
-
errors[attribute].uniq!
-
end
-
else
-
errors.add(reflection.name)
-
end
-
end
-
valid
-
end
-
-
# Is used as a before_save callback to check while saving a collection
-
# association whether or not the parent was a new record before saving.
-
1
def before_save_collection_association
-
6
@new_record_before_save = new_record?
-
6
true
-
end
-
-
# Saves any new associated records, or all loaded autosave associations if
-
# <tt>:autosave</tt> is enabled on the association.
-
#
-
# In addition, it destroys all children that were marked for destruction
-
# with mark_for_destruction.
-
#
-
# This all happens inside a transaction, _if_ the Transactions module is included into
-
# ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
-
1
def save_collection_association(reflection)
-
24
if association = association_instance_get(reflection.name)
-
autosave = reflection.options[:autosave]
-
-
if records = associated_records_to_validate_or_save(association, @new_record_before_save, autosave)
-
if autosave
-
records_to_destroy = records.select(&:marked_for_destruction?)
-
records_to_destroy.each { |record| association.destroy(record) }
-
records -= records_to_destroy
-
end
-
-
records.each do |record|
-
next if record.destroyed?
-
-
saved = true
-
-
if autosave != false && (@new_record_before_save || record.new_record?)
-
if autosave
-
saved = association.insert_record(record, false)
-
else
-
association.insert_record(record) unless reflection.nested?
-
end
-
elsif autosave
-
saved = record.save(:validate => false)
-
end
-
-
raise ActiveRecord::Rollback unless saved
-
end
-
end
-
-
# reconstruct the scope now that we know the owner's id
-
association.reset_scope if association.respond_to?(:reset_scope)
-
end
-
end
-
-
# Saves the associated record if it's new or <tt>:autosave</tt> is enabled
-
# on the association.
-
#
-
# In addition, it will destroy the association if it was marked for
-
# destruction with mark_for_destruction.
-
#
-
# This all happens inside a transaction, _if_ the Transactions module is included into
-
# ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
-
1
def save_has_one_association(reflection)
-
association = association_instance_get(reflection.name)
-
record = association && association.load_target
-
-
if record && !record.destroyed?
-
autosave = reflection.options[:autosave]
-
-
if autosave && record.marked_for_destruction?
-
record.destroy
-
elsif autosave != false
-
key = reflection.options[:primary_key] ? send(reflection.options[:primary_key]) : id
-
-
if (autosave && record.changed_for_autosave?) || new_record? || record_changed?(reflection, record, key)
-
unless reflection.through_reflection
-
record[reflection.foreign_key] = key
-
end
-
-
saved = record.save(:validate => !autosave)
-
raise ActiveRecord::Rollback if !saved && autosave
-
saved
-
end
-
end
-
end
-
end
-
-
# If the record is new or it has changed, returns true.
-
1
def record_changed?(reflection, record, key)
-
record.new_record? || record[reflection.foreign_key] != key || record.attribute_changed?(reflection.foreign_key)
-
end
-
-
# Saves the associated record if it's new or <tt>:autosave</tt> is enabled.
-
#
-
# In addition, it will destroy the association if it was marked for destruction.
-
1
def save_belongs_to_association(reflection)
-
4
association = association_instance_get(reflection.name)
-
4
record = association && association.load_target
-
4
if record && !record.destroyed?
-
4
autosave = reflection.options[:autosave]
-
-
4
if autosave && record.marked_for_destruction?
-
self[reflection.foreign_key] = nil
-
record.destroy
-
4
elsif autosave != false
-
4
saved = record.save(:validate => !autosave) if record.new_record? || (autosave && record.changed_for_autosave?)
-
-
4
if association.updated?
-
2
association_id = record.send(reflection.options[:primary_key] || :id)
-
2
self[reflection.foreign_key] = association_id
-
2
association.loaded!
-
end
-
-
4
saved if autosave
-
end
-
end
-
end
-
end
-
end
-
1
require 'yaml'
-
1
require 'set'
-
1
require 'active_support/benchmarkable'
-
1
require 'active_support/dependencies'
-
1
require 'active_support/descendants_tracker'
-
1
require 'active_support/time'
-
1
require 'active_support/core_ext/module/attribute_accessors'
-
1
require 'active_support/core_ext/class/delegating_attributes'
-
1
require 'active_support/core_ext/array/extract_options'
-
1
require 'active_support/core_ext/hash/deep_merge'
-
1
require 'active_support/core_ext/hash/slice'
-
1
require 'active_support/core_ext/string/behavior'
-
1
require 'active_support/core_ext/kernel/singleton_class'
-
1
require 'active_support/core_ext/module/introspection'
-
1
require 'active_support/core_ext/object/duplicable'
-
1
require 'active_support/core_ext/class/subclasses'
-
1
require 'arel'
-
1
require 'active_record/errors'
-
1
require 'active_record/log_subscriber'
-
1
require 'active_record/explain_subscriber'
-
1
require 'active_record/relation/delegation'
-
-
1
module ActiveRecord #:nodoc:
-
# = Active Record
-
#
-
# Active Record objects don't specify their attributes directly, but rather infer them from
-
# the table definition with which they're linked. Adding, removing, and changing attributes
-
# and their type is done directly in the database. Any change is instantly reflected in the
-
# Active Record objects. The mapping that binds a given Active Record class to a certain
-
# database table will happen automatically in most common cases, but can be overwritten for the uncommon ones.
-
#
-
# See the mapping rules in table_name and the full example in link:files/activerecord/README_rdoc.html for more insight.
-
#
-
# == Creation
-
#
-
# Active Records accept constructor parameters either in a hash or as a block. The hash
-
# method is especially useful when you're receiving the data from somewhere else, like an
-
# HTTP request. It works like this:
-
#
-
# user = User.new(name: "David", occupation: "Code Artist")
-
# user.name # => "David"
-
#
-
# You can also use block initialization:
-
#
-
# user = User.new do |u|
-
# u.name = "David"
-
# u.occupation = "Code Artist"
-
# end
-
#
-
# And of course you can just create a bare object and specify the attributes after the fact:
-
#
-
# user = User.new
-
# user.name = "David"
-
# user.occupation = "Code Artist"
-
#
-
# == Conditions
-
#
-
# Conditions can either be specified as a string, array, or hash representing the WHERE-part of an SQL statement.
-
# The array form is to be used when the condition input is tainted and requires sanitization. The string form can
-
# be used for statements that don't involve tainted data. The hash form works much like the array form, except
-
# only equality and range is possible. Examples:
-
#
-
# class User < ActiveRecord::Base
-
# def self.authenticate_unsafely(user_name, password)
-
# where("user_name = '#{user_name}' AND password = '#{password}'").first
-
# end
-
#
-
# def self.authenticate_safely(user_name, password)
-
# where("user_name = ? AND password = ?", user_name, password).first
-
# end
-
#
-
# def self.authenticate_safely_simply(user_name, password)
-
# where(user_name: user_name, password: password).first
-
# end
-
# end
-
#
-
# The <tt>authenticate_unsafely</tt> method inserts the parameters directly into the query
-
# and is thus susceptible to SQL-injection attacks if the <tt>user_name</tt> and +password+
-
# parameters come directly from an HTTP request. The <tt>authenticate_safely</tt> and
-
# <tt>authenticate_safely_simply</tt> both will sanitize the <tt>user_name</tt> and +password+
-
# before inserting them in the query, which will ensure that an attacker can't escape the
-
# query and fake the login (or worse).
-
#
-
# When using multiple parameters in the conditions, it can easily become hard to read exactly
-
# what the fourth or fifth question mark is supposed to represent. In those cases, you can
-
# resort to named bind variables instead. That's done by replacing the question marks with
-
# symbols and supplying a hash with values for the matching symbol keys:
-
#
-
# Company.where(
-
# "id = :id AND name = :name AND division = :division AND created_at > :accounting_date",
-
# { id: 3, name: "37signals", division: "First", accounting_date: '2005-01-01' }
-
# ).first
-
#
-
# Similarly, a simple hash without a statement will generate conditions based on equality with the SQL AND
-
# operator. For instance:
-
#
-
# Student.where(first_name: "Harvey", status: 1)
-
# Student.where(params[:student])
-
#
-
# A range may be used in the hash to use the SQL BETWEEN operator:
-
#
-
# Student.where(grade: 9..12)
-
#
-
# An array may be used in the hash to use the SQL IN operator:
-
#
-
# Student.where(grade: [9,11,12])
-
#
-
# When joining tables, nested hashes or keys written in the form 'table_name.column_name'
-
# can be used to qualify the table name of a particular condition. For instance:
-
#
-
# Student.joins(:schools).where(schools: { category: 'public' })
-
# Student.joins(:schools).where('schools.category' => 'public' )
-
#
-
# == Overwriting default accessors
-
#
-
# All column values are automatically available through basic accessors on the Active Record
-
# object, but sometimes you want to specialize this behavior. This can be done by overwriting
-
# the default accessors (using the same name as the attribute) and calling
-
# <tt>read_attribute(attr_name)</tt> and <tt>write_attribute(attr_name, value)</tt> to actually
-
# change things.
-
#
-
# class Song < ActiveRecord::Base
-
# # Uses an integer of seconds to hold the length of the song
-
#
-
# def length=(minutes)
-
# write_attribute(:length, minutes.to_i * 60)
-
# end
-
#
-
# def length
-
# read_attribute(:length) / 60
-
# end
-
# end
-
#
-
# You can alternatively use <tt>self[:attribute]=(value)</tt> and <tt>self[:attribute]</tt>
-
# instead of <tt>write_attribute(:attribute, value)</tt> and <tt>read_attribute(:attribute)</tt>.
-
#
-
# == Attribute query methods
-
#
-
# In addition to the basic accessors, query methods are also automatically available on the Active Record object.
-
# Query methods allow you to test whether an attribute value is present.
-
#
-
# For example, an Active Record User with the <tt>name</tt> attribute has a <tt>name?</tt> method that you can call
-
# to determine whether the user has a name:
-
#
-
# user = User.new(name: "David")
-
# user.name? # => true
-
#
-
# anonymous = User.new(name: "")
-
# anonymous.name? # => false
-
#
-
# == Accessing attributes before they have been typecasted
-
#
-
# Sometimes you want to be able to read the raw attribute data without having the column-determined
-
# typecast run its course first. That can be done by using the <tt><attribute>_before_type_cast</tt>
-
# accessors that all attributes have. For example, if your Account model has a <tt>balance</tt> attribute,
-
# you can call <tt>account.balance_before_type_cast</tt> or <tt>account.id_before_type_cast</tt>.
-
#
-
# This is especially useful in validation situations where the user might supply a string for an
-
# integer field and you want to display the original string back in an error message. Accessing the
-
# attribute normally would typecast the string to 0, which isn't what you want.
-
#
-
# == Dynamic attribute-based finders
-
#
-
# Dynamic attribute-based finders are a mildly deprecated way of getting (and/or creating) objects
-
# by simple queries without turning to SQL. They work by appending the name of an attribute
-
# to <tt>find_by_</tt> like <tt>Person.find_by_user_name</tt>.
-
# Instead of writing <tt>Person.find_by(user_name: user_name)</tt>, you can use
-
# <tt>Person.find_by_user_name(user_name)</tt>.
-
#
-
# It's possible to add an exclamation point (!) on the end of the dynamic finders to get them to raise an
-
# <tt>ActiveRecord::RecordNotFound</tt> error if they do not return any records,
-
# like <tt>Person.find_by_last_name!</tt>.
-
#
-
# It's also possible to use multiple attributes in the same find by separating them with "_and_".
-
#
-
# Person.find_by(user_name: user_name, password: password)
-
# Person.find_by_user_name_and_password(user_name, password) # with dynamic finder
-
#
-
# It's even possible to call these dynamic finder methods on relations and named scopes.
-
#
-
# Payment.order("created_on").find_by_amount(50)
-
#
-
# == Saving arrays, hashes, and other non-mappable objects in text columns
-
#
-
# Active Record can serialize any object in text columns using YAML. To do so, you must
-
# specify this with a call to the class method +serialize+.
-
# This makes it possible to store arrays, hashes, and other non-mappable objects without doing
-
# any additional work.
-
#
-
# class User < ActiveRecord::Base
-
# serialize :preferences
-
# end
-
#
-
# user = User.create(preferences: { "background" => "black", "display" => large })
-
# User.find(user.id).preferences # => { "background" => "black", "display" => large }
-
#
-
# You can also specify a class option as the second parameter that'll raise an exception
-
# if a serialized object is retrieved as a descendant of a class not in the hierarchy.
-
#
-
# class User < ActiveRecord::Base
-
# serialize :preferences, Hash
-
# end
-
#
-
# user = User.create(preferences: %w( one two three ))
-
# User.find(user.id).preferences # raises SerializationTypeMismatch
-
#
-
# When you specify a class option, the default value for that attribute will be a new
-
# instance of that class.
-
#
-
# class User < ActiveRecord::Base
-
# serialize :preferences, OpenStruct
-
# end
-
#
-
# user = User.new
-
# user.preferences.theme_color = "red"
-
#
-
#
-
# == Single table inheritance
-
#
-
# Active Record allows inheritance by storing the name of the class in a column that by
-
# default is named "type" (can be changed by overwriting <tt>Base.inheritance_column</tt>).
-
# This means that an inheritance looking like this:
-
#
-
# class Company < ActiveRecord::Base; end
-
# class Firm < Company; end
-
# class Client < Company; end
-
# class PriorityClient < Client; end
-
#
-
# When you do <tt>Firm.create(name: "37signals")</tt>, this record will be saved in
-
# the companies table with type = "Firm". You can then fetch this row again using
-
# <tt>Company.where(name: '37signals').first</tt> and it will return a Firm object.
-
#
-
# If you don't have a type column defined in your table, single-table inheritance won't
-
# be triggered. In that case, it'll work just like normal subclasses with no special magic
-
# for differentiating between them or reloading the right type with find.
-
#
-
# Note, all the attributes for all the cases are kept in the same table. Read more:
-
# http://www.martinfowler.com/eaaCatalog/singleTableInheritance.html
-
#
-
# == Connection to multiple databases in different models
-
#
-
# Connections are usually created through ActiveRecord::Base.establish_connection and retrieved
-
# by ActiveRecord::Base.connection. All classes inheriting from ActiveRecord::Base will use this
-
# connection. But you can also set a class-specific connection. For example, if Course is an
-
# ActiveRecord::Base, but resides in a different database, you can just say <tt>Course.establish_connection</tt>
-
# and Course and all of its subclasses will use this connection instead.
-
#
-
# This feature is implemented by keeping a connection pool in ActiveRecord::Base that is
-
# a Hash indexed by the class. If a connection is requested, the retrieve_connection method
-
# will go up the class-hierarchy until a connection is found in the connection pool.
-
#
-
# == Exceptions
-
#
-
# * ActiveRecordError - Generic error class and superclass of all other errors raised by Active Record.
-
# * AdapterNotSpecified - The configuration hash used in <tt>establish_connection</tt> didn't include an
-
# <tt>:adapter</tt> key.
-
# * AdapterNotFound - The <tt>:adapter</tt> key used in <tt>establish_connection</tt> specified a
-
# non-existent adapter
-
# (or a bad spelling of an existing one).
-
# * AssociationTypeMismatch - The object assigned to the association wasn't of the type
-
# specified in the association definition.
-
# * AttributeAssignmentError - An error occurred while doing a mass assignment through the
-
# <tt>attributes=</tt> method.
-
# You can inspect the +attribute+ property of the exception object to determine which attribute
-
# triggered the error.
-
# * ConnectionNotEstablished - No connection has been established. Use <tt>establish_connection</tt>
-
# before querying.
-
# * MultiparameterAssignmentErrors - Collection of errors that occurred during a mass assignment using the
-
# <tt>attributes=</tt> method. The +errors+ property of this exception contains an array of
-
# AttributeAssignmentError
-
# objects that should be inspected to determine which attributes triggered the errors.
-
# * RecordInvalid - raised by save! and create! when the record is invalid.
-
# * RecordNotFound - No record responded to the +find+ method. Either the row with the given ID doesn't exist
-
# or the row didn't meet the additional restrictions. Some +find+ calls do not raise this exception to signal
-
# nothing was found, please check its documentation for further details.
-
# * SerializationTypeMismatch - The serialized object wasn't of the class specified as the second parameter.
-
# * StatementInvalid - The database server rejected the SQL statement. The precise error is added in the message.
-
#
-
# *Note*: The attributes listed are class-level attributes (accessible from both the class and instance level).
-
# So it's possible to assign a logger to the class through <tt>Base.logger=</tt> which will then be used by all
-
# instances in the current object space.
-
1
class Base
-
1
extend ActiveModel::Naming
-
-
1
extend ActiveSupport::Benchmarkable
-
1
extend ActiveSupport::DescendantsTracker
-
-
1
extend ConnectionHandling
-
1
extend QueryCache::ClassMethods
-
1
extend Querying
-
1
extend Translation
-
1
extend DynamicMatchers
-
1
extend Explain
-
1
extend Enum
-
1
extend Delegation::DelegateCache
-
-
1
include Core
-
1
include Persistence
-
1
include ReadonlyAttributes
-
1
include ModelSchema
-
1
include Inheritance
-
1
include Scoping
-
1
include Sanitization
-
1
include AttributeAssignment
-
1
include ActiveModel::Conversion
-
1
include Integration
-
1
include Validations
-
1
include CounterCache
-
1
include Locking::Optimistic
-
1
include Locking::Pessimistic
-
1
include AttributeMethods
-
1
include Callbacks
-
1
include Timestamp
-
1
include Associations
-
1
include ActiveModel::SecurePassword
-
1
include AutosaveAssociation
-
1
include NestedAttributes
-
1
include Aggregations
-
1
include Transactions
-
1
include NoTouching
-
1
include Reflection
-
1
include Serialization
-
1
include Store
-
end
-
-
1
ActiveSupport.run_load_hooks(:active_record, Base)
-
end
-
1
module ActiveRecord
-
# = Active Record Callbacks
-
#
-
# Callbacks are hooks into the life cycle of an Active Record object that allow you to trigger logic
-
# before or after an alteration of the object state. This can be used to make sure that associated and
-
# dependent objects are deleted when +destroy+ is called (by overwriting +before_destroy+) or to massage attributes
-
# before they're validated (by overwriting +before_validation+). As an example of the callbacks initiated, consider
-
# the <tt>Base#save</tt> call for a new record:
-
#
-
# * (-) <tt>save</tt>
-
# * (-) <tt>valid</tt>
-
# * (1) <tt>before_validation</tt>
-
# * (-) <tt>validate</tt>
-
# * (2) <tt>after_validation</tt>
-
# * (3) <tt>before_save</tt>
-
# * (4) <tt>before_create</tt>
-
# * (-) <tt>create</tt>
-
# * (5) <tt>after_create</tt>
-
# * (6) <tt>after_save</tt>
-
# * (7) <tt>after_commit</tt>
-
#
-
# Also, an <tt>after_rollback</tt> callback can be configured to be triggered whenever a rollback is issued.
-
# Check out <tt>ActiveRecord::Transactions</tt> for more details about <tt>after_commit</tt> and
-
# <tt>after_rollback</tt>.
-
#
-
# Additionally, an <tt>after_touch</tt> callback is triggered whenever an
-
# object is touched.
-
#
-
# Lastly an <tt>after_find</tt> and <tt>after_initialize</tt> callback is triggered for each object that
-
# is found and instantiated by a finder, with <tt>after_initialize</tt> being triggered after new objects
-
# are instantiated as well.
-
#
-
# There are nineteen callbacks in total, which give you immense power to react and prepare for each state in the
-
# Active Record life cycle. The sequence for calling <tt>Base#save</tt> for an existing record is similar,
-
# except that each <tt>_create</tt> callback is replaced by the corresponding <tt>_update</tt> callback.
-
#
-
# Examples:
-
# class CreditCard < ActiveRecord::Base
-
# # Strip everything but digits, so the user can specify "555 234 34" or
-
# # "5552-3434" and both will mean "55523434"
-
# before_validation(on: :create) do
-
# self.number = number.gsub(/[^0-9]/, "") if attribute_present?("number")
-
# end
-
# end
-
#
-
# class Subscription < ActiveRecord::Base
-
# before_create :record_signup
-
#
-
# private
-
# def record_signup
-
# self.signed_up_on = Date.today
-
# end
-
# end
-
#
-
# class Firm < ActiveRecord::Base
-
# # Destroys the associated clients and people when the firm is destroyed
-
# before_destroy { |record| Person.destroy_all "firm_id = #{record.id}" }
-
# before_destroy { |record| Client.destroy_all "client_of = #{record.id}" }
-
# end
-
#
-
# == Inheritable callback queues
-
#
-
# Besides the overwritable callback methods, it's also possible to register callbacks through the
-
# use of the callback macros. Their main advantage is that the macros add behavior into a callback
-
# queue that is kept intact down through an inheritance hierarchy.
-
#
-
# class Topic < ActiveRecord::Base
-
# before_destroy :destroy_author
-
# end
-
#
-
# class Reply < Topic
-
# before_destroy :destroy_readers
-
# end
-
#
-
# Now, when <tt>Topic#destroy</tt> is run only +destroy_author+ is called. When <tt>Reply#destroy</tt> is
-
# run, both +destroy_author+ and +destroy_readers+ are called. Contrast this to the following situation
-
# where the +before_destroy+ method is overridden:
-
#
-
# class Topic < ActiveRecord::Base
-
# def before_destroy() destroy_author end
-
# end
-
#
-
# class Reply < Topic
-
# def before_destroy() destroy_readers end
-
# end
-
#
-
# In that case, <tt>Reply#destroy</tt> would only run +destroy_readers+ and _not_ +destroy_author+.
-
# So, use the callback macros when you want to ensure that a certain callback is called for the entire
-
# hierarchy, and use the regular overwritable methods when you want to leave it up to each descendant
-
# to decide whether they want to call +super+ and trigger the inherited callbacks.
-
#
-
# *IMPORTANT:* In order for inheritance to work for the callback queues, you must specify the
-
# callbacks before specifying the associations. Otherwise, you might trigger the loading of a
-
# child before the parent has registered the callbacks and they won't be inherited.
-
#
-
# == Types of callbacks
-
#
-
# There are four types of callbacks accepted by the callback macros: Method references (symbol), callback objects,
-
# inline methods (using a proc), and inline eval methods (using a string). Method references and callback objects
-
# are the recommended approaches, inline methods using a proc are sometimes appropriate (such as for
-
# creating mix-ins), and inline eval methods are deprecated.
-
#
-
# The method reference callbacks work by specifying a protected or private method available in the object, like this:
-
#
-
# class Topic < ActiveRecord::Base
-
# before_destroy :delete_parents
-
#
-
# private
-
# def delete_parents
-
# self.class.delete_all "parent_id = #{id}"
-
# end
-
# end
-
#
-
# The callback objects have methods named after the callback called with the record as the only parameter, such as:
-
#
-
# class BankAccount < ActiveRecord::Base
-
# before_save EncryptionWrapper.new
-
# after_save EncryptionWrapper.new
-
# after_initialize EncryptionWrapper.new
-
# end
-
#
-
# class EncryptionWrapper
-
# def before_save(record)
-
# record.credit_card_number = encrypt(record.credit_card_number)
-
# end
-
#
-
# def after_save(record)
-
# record.credit_card_number = decrypt(record.credit_card_number)
-
# end
-
#
-
# alias_method :after_initialize, :after_save
-
#
-
# private
-
# def encrypt(value)
-
# # Secrecy is committed
-
# end
-
#
-
# def decrypt(value)
-
# # Secrecy is unveiled
-
# end
-
# end
-
#
-
# So you specify the object you want messaged on a given callback. When that callback is triggered, the object has
-
# a method by the name of the callback messaged. You can make these callbacks more flexible by passing in other
-
# initialization data such as the name of the attribute to work with:
-
#
-
# class BankAccount < ActiveRecord::Base
-
# before_save EncryptionWrapper.new("credit_card_number")
-
# after_save EncryptionWrapper.new("credit_card_number")
-
# after_initialize EncryptionWrapper.new("credit_card_number")
-
# end
-
#
-
# class EncryptionWrapper
-
# def initialize(attribute)
-
# @attribute = attribute
-
# end
-
#
-
# def before_save(record)
-
# record.send("#{@attribute}=", encrypt(record.send("#{@attribute}")))
-
# end
-
#
-
# def after_save(record)
-
# record.send("#{@attribute}=", decrypt(record.send("#{@attribute}")))
-
# end
-
#
-
# alias_method :after_initialize, :after_save
-
#
-
# private
-
# def encrypt(value)
-
# # Secrecy is committed
-
# end
-
#
-
# def decrypt(value)
-
# # Secrecy is unveiled
-
# end
-
# end
-
#
-
# The callback macros usually accept a symbol for the method they're supposed to run, but you can also
-
# pass a "method string", which will then be evaluated within the binding of the callback. Example:
-
#
-
# class Topic < ActiveRecord::Base
-
# before_destroy 'self.class.delete_all "parent_id = #{id}"'
-
# end
-
#
-
# Notice that single quotes (') are used so the <tt>#{id}</tt> part isn't evaluated until the callback
-
# is triggered. Also note that these inline callbacks can be stacked just like the regular ones:
-
#
-
# class Topic < ActiveRecord::Base
-
# before_destroy 'self.class.delete_all "parent_id = #{id}"',
-
# 'puts "Evaluated after parents are destroyed"'
-
# end
-
#
-
# == <tt>before_validation*</tt> returning statements
-
#
-
# If the returning value of a +before_validation+ callback can be evaluated to +false+, the process will be
-
# aborted and <tt>Base#save</tt> will return +false+. If Base#save! is called it will raise a
-
# ActiveRecord::RecordInvalid exception. Nothing will be appended to the errors object.
-
#
-
# == Canceling callbacks
-
#
-
# If a <tt>before_*</tt> callback returns +false+, all the later callbacks and the associated action are
-
# cancelled. If an <tt>after_*</tt> callback returns +false+, all the later callbacks are cancelled.
-
# Callbacks are generally run in the order they are defined, with the exception of callbacks defined as
-
# methods on the model, which are called last.
-
#
-
# == Ordering callbacks
-
#
-
# Sometimes the code needs that the callbacks execute in a specific order. For example, a +before_destroy+
-
# callback (+log_children+ in this case) should be executed before the children get destroyed by the +dependent: destroy+ option.
-
#
-
# Let's look at the code below:
-
#
-
# class Topic < ActiveRecord::Base
-
# has_many :children, dependent: destroy
-
#
-
# before_destroy :log_children
-
#
-
# private
-
# def log_children
-
# # Child processing
-
# end
-
# end
-
#
-
# In this case, the problem is that when the +before_destroy+ callback is executed, the children are not available
-
# because the +destroy+ callback gets executed first. You can use the +prepend+ option on the +before_destroy+ callback to avoid this.
-
#
-
# class Topic < ActiveRecord::Base
-
# has_many :children, dependent: destroy
-
#
-
# before_destroy :log_children, prepend: true
-
#
-
# private
-
# def log_children
-
# # Child processing
-
# end
-
# end
-
#
-
# This way, the +before_destroy+ gets executed before the <tt>dependent: destroy</tt> is called, and the data is still available.
-
#
-
# == Transactions
-
#
-
# The entire callback chain of a +save+, <tt>save!</tt>, or +destroy+ call runs
-
# within a transaction. That includes <tt>after_*</tt> hooks. If everything
-
# goes fine a COMMIT is executed once the chain has been completed.
-
#
-
# If a <tt>before_*</tt> callback cancels the action a ROLLBACK is issued. You
-
# can also trigger a ROLLBACK raising an exception in any of the callbacks,
-
# including <tt>after_*</tt> hooks. Note, however, that in that case the client
-
# needs to be aware of it because an ordinary +save+ will raise such exception
-
# instead of quietly returning +false+.
-
#
-
# == Debugging callbacks
-
#
-
# The callback chain is accessible via the <tt>_*_callbacks</tt> method on an object. ActiveModel Callbacks support
-
# <tt>:before</tt>, <tt>:after</tt> and <tt>:around</tt> as values for the <tt>kind</tt> property. The <tt>kind</tt> property
-
# defines what part of the chain the callback runs in.
-
#
-
# To find all callbacks in the before_save callback chain:
-
#
-
# Topic._save_callbacks.select { |cb| cb.kind.eql?(:before) }
-
#
-
# Returns an array of callback objects that form the before_save chain.
-
#
-
# To further check if the before_save chain contains a proc defined as <tt>rest_when_dead</tt> use the <tt>filter</tt> property of the callback object:
-
#
-
# Topic._save_callbacks.select { |cb| cb.kind.eql?(:before) }.collect(&:filter).include?(:rest_when_dead)
-
#
-
# Returns true or false depending on whether the proc is contained in the before_save callback chain on a Topic model.
-
#
-
1
module Callbacks
-
1
extend ActiveSupport::Concern
-
-
1
CALLBACKS = [
-
:after_initialize, :after_find, :after_touch, :before_validation, :after_validation,
-
:before_save, :around_save, :after_save, :before_create, :around_create,
-
:after_create, :before_update, :around_update, :after_update,
-
:before_destroy, :around_destroy, :after_destroy, :after_commit, :after_rollback
-
]
-
-
1
module ClassMethods
-
1
include ActiveModel::Callbacks
-
end
-
-
1
included do
-
1
include ActiveModel::Validations::Callbacks
-
-
1
define_model_callbacks :initialize, :find, :touch, :only => :after
-
1
define_model_callbacks :save, :create, :update, :destroy
-
end
-
-
1
def destroy #:nodoc:
-
run_callbacks(:destroy) { super }
-
end
-
-
1
def touch(*) #:nodoc:
-
run_callbacks(:touch) { super }
-
end
-
-
1
private
-
-
1
def create_or_update #:nodoc:
-
16
run_callbacks(:save) { super }
-
end
-
-
1
def _create_record #:nodoc:
-
12
run_callbacks(:create) { super }
-
end
-
-
1
def _update_record(*) #:nodoc:
-
4
run_callbacks(:update) { super }
-
end
-
end
-
end
-
1
require 'thread'
-
1
require 'thread_safe'
-
1
require 'monitor'
-
1
require 'set'
-
-
1
module ActiveRecord
-
# Raised when a connection could not be obtained within the connection
-
# acquisition timeout period: because max connections in pool
-
# are in use.
-
1
class ConnectionTimeoutError < ConnectionNotEstablished
-
end
-
-
1
module ConnectionAdapters
-
# Connection pool base class for managing Active Record database
-
# connections.
-
#
-
# == Introduction
-
#
-
# A connection pool synchronizes thread access to a limited number of
-
# database connections. The basic idea is that each thread checks out a
-
# database connection from the pool, uses that connection, and checks the
-
# connection back in. ConnectionPool is completely thread-safe, and will
-
# ensure that a connection cannot be used by two threads at the same time,
-
# as long as ConnectionPool's contract is correctly followed. It will also
-
# handle cases in which there are more threads than connections: if all
-
# connections have been checked out, and a thread tries to checkout a
-
# connection anyway, then ConnectionPool will wait until some other thread
-
# has checked in a connection.
-
#
-
# == Obtaining (checking out) a connection
-
#
-
# Connections can be obtained and used from a connection pool in several
-
# ways:
-
#
-
# 1. Simply use ActiveRecord::Base.connection as with Active Record 2.1 and
-
# earlier (pre-connection-pooling). Eventually, when you're done with
-
# the connection(s) and wish it to be returned to the pool, you call
-
# ActiveRecord::Base.clear_active_connections!. This will be the
-
# default behavior for Active Record when used in conjunction with
-
# Action Pack's request handling cycle.
-
# 2. Manually check out a connection from the pool with
-
# ActiveRecord::Base.connection_pool.checkout. You are responsible for
-
# returning this connection to the pool when finished by calling
-
# ActiveRecord::Base.connection_pool.checkin(connection).
-
# 3. Use ActiveRecord::Base.connection_pool.with_connection(&block), which
-
# obtains a connection, yields it as the sole argument to the block,
-
# and returns it to the pool after the block completes.
-
#
-
# Connections in the pool are actually AbstractAdapter objects (or objects
-
# compatible with AbstractAdapter's interface).
-
#
-
# == Options
-
#
-
# There are several connection-pooling-related options that you can add to
-
# your database connection configuration:
-
#
-
# * +pool+: number indicating size of connection pool (default 5)
-
# * +checkout_timeout+: number of seconds to block and wait for a connection
-
# before giving up and raising a timeout error (default 5 seconds).
-
# * +reaping_frequency+: frequency in seconds to periodically run the
-
# Reaper, which attempts to find and close dead connections, which can
-
# occur if a programmer forgets to close a connection at the end of a
-
# thread or a thread dies unexpectedly. (Default nil, which means don't
-
# run the Reaper).
-
# * +dead_connection_timeout+: number of seconds from last checkout
-
# after which the Reaper will consider a connection reapable. (default
-
# 5 seconds).
-
1
class ConnectionPool
-
# Threadsafe, fair, FIFO queue. Meant to be used by ConnectionPool
-
# with which it shares a Monitor. But could be a generic Queue.
-
#
-
# The Queue in stdlib's 'thread' could replace this class except
-
# stdlib's doesn't support waiting with a timeout.
-
1
class Queue
-
1
def initialize(lock = Monitor.new)
-
1
@lock = lock
-
1
@cond = @lock.new_cond
-
1
@num_waiting = 0
-
1
@queue = []
-
end
-
-
# Test if any threads are currently waiting on the queue.
-
1
def any_waiting?
-
synchronize do
-
@num_waiting > 0
-
end
-
end
-
-
# Returns the number of threads currently waiting on this
-
# queue.
-
1
def num_waiting
-
synchronize do
-
@num_waiting
-
end
-
end
-
-
# Add +element+ to the queue. Never blocks.
-
1
def add(element)
-
4
synchronize do
-
4
@queue.push element
-
4
@cond.signal
-
end
-
end
-
-
# If +element+ is in the queue, remove and return it, or nil.
-
1
def delete(element)
-
synchronize do
-
@queue.delete(element)
-
end
-
end
-
-
# Remove all elements from the queue.
-
1
def clear
-
synchronize do
-
@queue.clear
-
end
-
end
-
-
# Remove the head of the queue.
-
#
-
# If +timeout+ is not given, remove and return the head the
-
# queue if the number of available elements is strictly
-
# greater than the number of threads currently waiting (that
-
# is, don't jump ahead in line). Otherwise, return nil.
-
#
-
# If +timeout+ is given, block if it there is no element
-
# available, waiting up to +timeout+ seconds for an element to
-
# become available.
-
#
-
# Raises:
-
# - ConnectionTimeoutError if +timeout+ is given and no element
-
# becomes available after +timeout+ seconds,
-
1
def poll(timeout = nil)
-
4
synchronize do
-
4
if timeout
-
no_wait_poll || wait_poll(timeout)
-
else
-
4
no_wait_poll
-
end
-
end
-
end
-
-
1
private
-
-
1
def synchronize(&block)
-
8
@lock.synchronize(&block)
-
end
-
-
# Test if the queue currently contains any elements.
-
1
def any?
-
!@queue.empty?
-
end
-
-
# A thread can remove an element from the queue without
-
# waiting if an only if the number of currently available
-
# connections is strictly greater than the number of waiting
-
# threads.
-
1
def can_remove_no_wait?
-
4
@queue.size > @num_waiting
-
end
-
-
# Removes and returns the head of the queue if possible, or nil.
-
1
def remove
-
3
@queue.shift
-
end
-
-
# Remove and return the head the queue if the number of
-
# available elements is strictly greater than the number of
-
# threads currently waiting. Otherwise, return nil.
-
1
def no_wait_poll
-
4
remove if can_remove_no_wait?
-
end
-
-
# Waits on the queue up to +timeout+ seconds, then removes and
-
# returns the head of the queue.
-
1
def wait_poll(timeout)
-
@num_waiting += 1
-
-
t0 = Time.now
-
elapsed = 0
-
loop do
-
@cond.wait(timeout - elapsed)
-
-
return remove if any?
-
-
elapsed = Time.now - t0
-
if elapsed >= timeout
-
msg = 'could not obtain a database connection within %0.3f seconds (waited %0.3f seconds)' %
-
[timeout, elapsed]
-
raise ConnectionTimeoutError, msg
-
end
-
end
-
ensure
-
@num_waiting -= 1
-
end
-
end
-
-
# Every +frequency+ seconds, the reaper will call +reap+ on +pool+.
-
# A reaper instantiated with a nil frequency will never reap the
-
# connection pool.
-
#
-
# Configure the frequency by setting "reaping_frequency" in your
-
# database yaml file.
-
1
class Reaper
-
1
attr_reader :pool, :frequency
-
-
1
def initialize(pool, frequency)
-
1
@pool = pool
-
1
@frequency = frequency
-
end
-
-
1
def run
-
1
return unless frequency
-
Thread.new(frequency, pool) { |t, p|
-
while true
-
sleep t
-
p.reap
-
end
-
}
-
end
-
end
-
-
1
include MonitorMixin
-
-
1
attr_accessor :automatic_reconnect, :checkout_timeout, :dead_connection_timeout
-
1
attr_reader :spec, :connections, :size, :reaper
-
-
# Creates a new ConnectionPool object. +spec+ is a ConnectionSpecification
-
# object which describes database connection information (e.g. adapter,
-
# host name, username, password, etc), as well as the maximum size for
-
# this ConnectionPool.
-
#
-
# The default ConnectionPool maximum size is 5.
-
1
def initialize(spec)
-
1
super()
-
-
1
@spec = spec
-
-
1
@checkout_timeout = (spec.config[:checkout_timeout] && spec.config[:checkout_timeout].to_f) || 5
-
1
@dead_connection_timeout = (spec.config[:dead_connection_timeout] && spec.config[:dead_connection_timeout].to_f) || 5
-
1
@reaper = Reaper.new self, spec.config[:reaping_frequency]
-
1
@reaper.run
-
-
# default max pool size to 5
-
1
@size = (spec.config[:pool] && spec.config[:pool].to_i) || 5
-
-
# The cache of reserved connections mapped to threads
-
1
@reserved_connections = ThreadSafe::Cache.new(:initial_capacity => @size)
-
-
1
@connections = []
-
1
@automatic_reconnect = true
-
-
1
@available = Queue.new self
-
end
-
-
# Retrieve the connection associated with the current thread, or call
-
# #checkout to obtain one if necessary.
-
#
-
# #connection can be called any number of times; the connection is
-
# held in a hash keyed by the thread id.
-
1
def connection
-
# this is correctly done double-checked locking
-
# (ThreadSafe::Cache's lookups have volatile semantics)
-
@reserved_connections[current_connection_id] || synchronize do
-
4
@reserved_connections[current_connection_id] ||= checkout
-
136
end
-
end
-
-
# Is there an open connection that is being used for the current thread?
-
1
def active_connection?
-
synchronize do
-
@reserved_connections.fetch(current_connection_id) {
-
return false
-
}.in_use?
-
end
-
end
-
-
# Signal that the thread is finished with the current connection.
-
# #release_connection releases the connection-thread association
-
# and returns the connection to the pool.
-
1
def release_connection(with_id = current_connection_id)
-
4
synchronize do
-
4
conn = @reserved_connections.delete(with_id)
-
4
checkin conn if conn
-
end
-
end
-
-
# If a connection already exists yield it to the block. If no connection
-
# exists checkout a connection, yield it to the block, and checkin the
-
# connection when finished.
-
1
def with_connection
-
connection_id = current_connection_id
-
fresh_connection = true unless active_connection?
-
yield connection
-
ensure
-
release_connection(connection_id) if fresh_connection
-
end
-
-
# Returns true if a connection has already been opened.
-
1
def connected?
-
12
synchronize { @connections.any? }
-
end
-
-
# Disconnects all connections in the pool, and clears the pool.
-
1
def disconnect!
-
synchronize do
-
@reserved_connections.clear
-
@connections.each do |conn|
-
checkin conn
-
conn.disconnect!
-
end
-
@connections = []
-
@available.clear
-
end
-
end
-
-
# Clears the cache which maps classes.
-
1
def clear_reloadable_connections!
-
synchronize do
-
@reserved_connections.clear
-
@connections.each do |conn|
-
checkin conn
-
conn.disconnect! if conn.requires_reloading?
-
end
-
@connections.delete_if do |conn|
-
conn.requires_reloading?
-
end
-
@available.clear
-
@connections.each do |conn|
-
@available.add conn
-
end
-
end
-
end
-
-
# Check-out a database connection from the pool, indicating that you want
-
# to use it. You should call #checkin when you no longer need this.
-
#
-
# This is done by either returning and leasing existing connection, or by
-
# creating a new connection and leasing it.
-
#
-
# If all connections are leased and the pool is at capacity (meaning the
-
# number of currently leased connections is greater than or equal to the
-
# size limit set), an ActiveRecord::ConnectionTimeoutError exception will be raised.
-
#
-
# Returns: an AbstractAdapter object.
-
#
-
# Raises:
-
# - ConnectionTimeoutError: no connection can be obtained from the pool.
-
1
def checkout
-
4
synchronize do
-
4
conn = acquire_connection
-
4
conn.lease
-
4
checkout_and_verify(conn)
-
end
-
end
-
-
# Check-in a database connection back into the pool, indicating that you
-
# no longer need this connection.
-
#
-
# +conn+: an AbstractAdapter object, which was obtained by earlier by
-
# calling +checkout+ on this pool.
-
1
def checkin(conn)
-
4
synchronize do
-
4
conn.run_callbacks :checkin do
-
4
conn.expire
-
end
-
-
4
release conn
-
-
4
@available.add conn
-
end
-
end
-
-
# Remove a connection from the connection pool. The connection will
-
# remain open and active but will no longer be managed by this pool.
-
1
def remove(conn)
-
synchronize do
-
@connections.delete conn
-
@available.delete conn
-
-
# FIXME: we might want to store the key on the connection so that removing
-
# from the reserved hash will be a little easier.
-
release conn
-
-
@available.add checkout_new_connection if @available.any_waiting?
-
end
-
end
-
-
# Removes dead connections from the pool. A dead connection can occur
-
# if a programmer forgets to close a connection at the end of a thread
-
# or a thread dies unexpectedly.
-
1
def reap
-
synchronize do
-
stale = Time.now - @dead_connection_timeout
-
connections.dup.each do |conn|
-
if conn.in_use? && stale > conn.last_use && !conn.active_threadsafe?
-
remove conn
-
end
-
end
-
end
-
end
-
-
1
private
-
-
# Acquire a connection by one of 1) immediately removing one
-
# from the queue of available connections, 2) creating a new
-
# connection if the pool is not at capacity, 3) waiting on the
-
# queue for a connection to become available.
-
#
-
# Raises:
-
# - ConnectionTimeoutError if a connection could not be acquired
-
1
def acquire_connection
-
4
if conn = @available.poll
-
3
conn
-
1
elsif @connections.size < @size
-
1
checkout_new_connection
-
else
-
@available.poll(@checkout_timeout)
-
end
-
end
-
-
1
def release(conn)
-
4
thread_id = if @reserved_connections[current_connection_id] == conn
-
current_connection_id
-
else
-
4
@reserved_connections.keys.find { |k|
-
@reserved_connections[k] == conn
-
}
-
end
-
-
4
@reserved_connections.delete thread_id if thread_id
-
end
-
-
1
def new_connection
-
1
Base.send(spec.adapter_method, spec.config)
-
end
-
-
1
def current_connection_id #:nodoc:
-
148
Base.connection_id ||= Thread.current.object_id
-
end
-
-
1
def checkout_new_connection
-
1
raise ConnectionNotEstablished unless @automatic_reconnect
-
-
1
c = new_connection
-
1
c.pool = self
-
1
@connections << c
-
1
c
-
end
-
-
1
def checkout_and_verify(c)
-
4
c.run_callbacks :checkout do
-
4
c.verify!
-
end
-
4
c
-
end
-
end
-
-
# ConnectionHandler is a collection of ConnectionPool objects. It is used
-
# for keeping separate connection pools for Active Record models that connect
-
# to different databases.
-
#
-
# For example, suppose that you have 5 models, with the following hierarchy:
-
#
-
# |
-
# +-- Book
-
# | |
-
# | +-- ScaryBook
-
# | +-- GoodBook
-
# +-- Author
-
# +-- BankAccount
-
#
-
# Suppose that Book is to connect to a separate database (i.e. one other
-
# than the default database). Then Book, ScaryBook and GoodBook will all use
-
# the same connection pool. Likewise, Author and BankAccount will use the
-
# same connection pool. However, the connection pool used by Author/BankAccount
-
# is not the same as the one used by Book/ScaryBook/GoodBook.
-
#
-
# Normally there is only a single ConnectionHandler instance, accessible via
-
# ActiveRecord::Base.connection_handler. Active Record models use this to
-
# determine the connection pool that they should use.
-
1
class ConnectionHandler
-
1
def initialize
-
# These caches are keyed by klass.name, NOT klass. Keying them by klass
-
# alone would lead to memory leaks in development mode as all previous
-
# instances of the class would stay in memory.
-
1
@owner_to_pool = ThreadSafe::Cache.new(:initial_capacity => 2) do |h,k|
-
1
h[k] = ThreadSafe::Cache.new(:initial_capacity => 2)
-
end
-
1
@class_to_pool = ThreadSafe::Cache.new(:initial_capacity => 2) do |h,k|
-
1
h[k] = ThreadSafe::Cache.new
-
end
-
end
-
-
1
def connection_pool_list
-
8
owner_to_pool.values.compact
-
end
-
-
1
def connection_pools
-
ActiveSupport::Deprecation.warn(
-
"In the next release, this will return the same as #connection_pool_list. " \
-
"(An array of pools, rather than a hash mapping specs to pools.)"
-
)
-
Hash[connection_pool_list.map { |pool| [pool.spec, pool] }]
-
end
-
-
1
def establish_connection(owner, spec)
-
1
@class_to_pool.clear
-
1
raise RuntimeError, "Anonymous class is not allowed." unless owner.name
-
1
owner_to_pool[owner.name] = ConnectionAdapters::ConnectionPool.new(spec)
-
end
-
-
# Returns true if there are any active connections among the connection
-
# pools that the ConnectionHandler is managing.
-
1
def active_connections?
-
connection_pool_list.any?(&:active_connection?)
-
end
-
-
# Returns any connections in use by the current thread back to the pool,
-
# and also returns connections to the pool cached by threads that are no
-
# longer alive.
-
1
def clear_active_connections!
-
4
connection_pool_list.each(&:release_connection)
-
end
-
-
# Clears the cache which maps classes.
-
1
def clear_reloadable_connections!
-
connection_pool_list.each(&:clear_reloadable_connections!)
-
end
-
-
1
def clear_all_connections!
-
connection_pool_list.each(&:disconnect!)
-
end
-
-
# Locate the connection of the nearest super class. This can be an
-
# active or defined connection: if it is the latter, it will be
-
# opened and set as the active connection for the class it was defined
-
# for (not necessarily the current class).
-
1
def retrieve_connection(klass) #:nodoc:
-
132
pool = retrieve_connection_pool(klass)
-
132
(pool && pool.connection) or raise ConnectionNotEstablished
-
end
-
-
# Returns true if a connection that's accessible to this class has
-
# already been opened.
-
1
def connected?(klass)
-
6
conn = retrieve_connection_pool(klass)
-
6
conn && conn.connected?
-
end
-
-
# Remove the connection for this class. This will close the active
-
# connection and the defined connection (if they exist). The result
-
# can be used as an argument for establish_connection, for easily
-
# re-establishing the connection.
-
1
def remove_connection(owner)
-
1
if pool = owner_to_pool.delete(owner.name)
-
@class_to_pool.clear
-
pool.automatic_reconnect = false
-
pool.disconnect!
-
pool.spec.config
-
end
-
end
-
-
# Retrieving the connection pool happens a lot so we cache it in @class_to_pool.
-
# This makes retrieving the connection pool O(1) once the process is warm.
-
# When a connection is established or removed, we invalidate the cache.
-
#
-
# Ideally we would use #fetch here, as class_to_pool[klass] may sometimes be nil.
-
# However, benchmarking (https://gist.github.com/jonleighton/3552829) showed that
-
# #fetch is significantly slower than #[]. So in the nil case, no caching will
-
# take place, but that's ok since the nil case is not the common one that we wish
-
# to optimise for.
-
1
def retrieve_connection_pool(klass)
-
143
class_to_pool[klass.name] ||= begin
-
6
until pool = pool_for(klass)
-
5
klass = klass.superclass
-
5
break unless klass <= Base
-
end
-
-
6
class_to_pool[klass.name] = pool
-
end
-
end
-
-
1
private
-
-
1
def owner_to_pool
-
26
@owner_to_pool[Process.pid]
-
end
-
-
1
def class_to_pool
-
149
@class_to_pool[Process.pid]
-
end
-
-
1
def pool_for(owner)
-
11
owner_to_pool.fetch(owner.name) {
-
5
if ancestor_pool = pool_from_any_process_for(owner)
-
# A connection was established in an ancestor process that must have
-
# subsequently forked. We can't reuse the connection, but we can copy
-
# the specification and establish a new connection with it.
-
establish_connection owner, ancestor_pool.spec
-
else
-
5
owner_to_pool[owner.name] = nil
-
end
-
}
-
end
-
-
1
def pool_from_any_process_for(owner)
-
10
owner_to_pool = @owner_to_pool.values.find { |v| v[owner.name] }
-
5
owner_to_pool && owner_to_pool[owner.name]
-
end
-
end
-
-
1
class ConnectionManagement
-
1
def initialize(app)
-
1
@app = app
-
end
-
-
1
def call(env)
-
testing = env.key?('rack.test')
-
-
response = @app.call(env)
-
response[2] = ::Rack::BodyProxy.new(response[2]) do
-
ActiveRecord::Base.clear_active_connections! unless testing
-
end
-
-
response
-
rescue Exception
-
ActiveRecord::Base.clear_active_connections! unless testing
-
raise
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
module ConnectionAdapters # :nodoc:
-
1
module DatabaseLimits
-
-
# Returns the maximum length of a table alias.
-
1
def table_alias_length
-
255
-
end
-
-
# Returns the maximum length of a column name.
-
1
def column_name_length
-
64
-
end
-
-
# Returns the maximum length of a table name.
-
1
def table_name_length
-
64
-
end
-
-
# Returns the maximum allowed length for an index name. This
-
# limit is enforced by rails and Is less than or equal to
-
# <tt>index_name_length</tt>. The gap between
-
# <tt>index_name_length</tt> is to allow internal rails
-
# operations to use prefixes in temporary operations.
-
1
def allowed_index_name_length
-
index_name_length
-
end
-
-
# Returns the maximum length of an index name.
-
1
def index_name_length
-
64
-
end
-
-
# Returns the maximum number of columns per table.
-
1
def columns_per_table
-
1024
-
end
-
-
# Returns the maximum number of indexes per table.
-
1
def indexes_per_table
-
16
-
end
-
-
# Returns the maximum number of columns in a multicolumn index.
-
1
def columns_per_multicolumn_index
-
16
-
end
-
-
# Returns the maximum number of elements in an IN (x,y,z) clause.
-
# nil means no limit.
-
1
def in_clause_length
-
nil
-
end
-
-
# Returns the maximum length of an SQL query.
-
1
def sql_query_length
-
1048575
-
end
-
-
# Returns maximum number of joins in a single query.
-
1
def joins_per_query
-
256
-
end
-
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
module ConnectionAdapters # :nodoc:
-
1
module DatabaseStatements
-
1
def initialize
-
1
super
-
1
reset_transaction
-
end
-
-
# Converts an arel AST to SQL
-
1
def to_sql(arel, binds = [])
-
13
if arel.respond_to?(:ast)
-
13
binds = binds.dup
-
13
visitor.accept(arel.ast) do
-
quote(*binds.shift.reverse)
-
end
-
else
-
arel
-
end
-
end
-
-
# Returns an ActiveRecord::Result instance.
-
1
def select_all(arel, name = nil, binds = [])
-
7
arel, binds = binds_from_relation arel, binds
-
7
select(to_sql(arel, binds), name, binds)
-
end
-
-
# Returns a record hash with the column names as keys and column values
-
# as values.
-
1
def select_one(arel, name = nil, binds = [])
-
select_all(arel, name, binds).first
-
end
-
-
# Returns a single value from a record
-
1
def select_value(arel, name = nil, binds = [])
-
if result = select_one(arel, name, binds)
-
result.values.first
-
end
-
end
-
-
# Returns an array of the values of the first column in a select:
-
# select_values("SELECT id FROM companies LIMIT 3") => [1,2,3]
-
1
def select_values(arel, name = nil)
-
arel, binds = binds_from_relation arel, []
-
select_rows(to_sql(arel, binds), name, binds).map(&:first)
-
end
-
-
# Returns an array of arrays containing the field values.
-
# Order is the same as that returned by +columns+.
-
1
def select_rows(sql, name = nil, binds = [])
-
end
-
1
undef_method :select_rows
-
-
# Executes the SQL statement in the context of this connection.
-
1
def execute(sql, name = nil)
-
end
-
1
undef_method :execute
-
-
# Executes +sql+ statement in the context of this connection using
-
# +binds+ as the bind substitutes. +name+ is logged along with
-
# the executed +sql+ statement.
-
1
def exec_query(sql, name = 'SQL', binds = [])
-
end
-
-
# Executes insert +sql+ statement in the context of this connection using
-
# +binds+ as the bind substitutes. +name+ is logged along with
-
# the executed +sql+ statement.
-
1
def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
-
exec_query(sql, name, binds)
-
end
-
-
# Executes delete +sql+ statement in the context of this connection using
-
# +binds+ as the bind substitutes. +name+ is logged along with
-
# the executed +sql+ statement.
-
1
def exec_delete(sql, name, binds)
-
exec_query(sql, name, binds)
-
end
-
-
# Executes update +sql+ statement in the context of this connection using
-
# +binds+ as the bind substitutes. +name+ is logged along with
-
# the executed +sql+ statement.
-
1
def exec_update(sql, name, binds)
-
exec_query(sql, name, binds)
-
end
-
-
# Returns the last auto-generated ID from the affected table.
-
#
-
# +id_value+ will be returned unless the value is nil, in
-
# which case the database will attempt to calculate the last inserted
-
# id and return that value.
-
#
-
# If the next id was calculated in advance (as in Oracle), it should be
-
# passed in as +id_value+.
-
1
def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
-
6
sql, binds = sql_for_insert(to_sql(arel, binds), pk, id_value, sequence_name, binds)
-
6
value = exec_insert(sql, name, binds, pk, sequence_name)
-
6
id_value || last_inserted_id(value)
-
end
-
-
# Executes the update statement and returns the number of rows affected.
-
1
def update(arel, name = nil, binds = [])
-
exec_update(to_sql(arel, binds), name, binds)
-
end
-
-
# Executes the delete statement and returns the number of rows affected.
-
1
def delete(arel, name = nil, binds = [])
-
exec_delete(to_sql(arel, binds), name, binds)
-
end
-
-
# Returns +true+ when the connection adapter supports prepared statement
-
# caching, otherwise returns +false+
-
1
def supports_statement_cache?
-
false
-
end
-
-
# Runs the given block in a database transaction, and returns the result
-
# of the block.
-
#
-
# == Nested transactions support
-
#
-
# Most databases don't support true nested transactions. At the time of
-
# writing, the only database that supports true nested transactions that
-
# we're aware of, is MS-SQL.
-
#
-
# In order to get around this problem, #transaction will emulate the effect
-
# of nested transactions, by using savepoints:
-
# http://dev.mysql.com/doc/refman/5.0/en/savepoint.html
-
# Savepoints are supported by MySQL and PostgreSQL. SQLite3 version >= '3.6.8'
-
# supports savepoints.
-
#
-
# It is safe to call this method if a database transaction is already open,
-
# i.e. if #transaction is called within another #transaction block. In case
-
# of a nested call, #transaction will behave as follows:
-
#
-
# - The block will be run without doing anything. All database statements
-
# that happen within the block are effectively appended to the already
-
# open database transaction.
-
# - However, if +:requires_new+ is set, the block will be wrapped in a
-
# database savepoint acting as a sub-transaction.
-
#
-
# === Caveats
-
#
-
# MySQL doesn't support DDL transactions. If you perform a DDL operation,
-
# then any created savepoints will be automatically released. For example,
-
# if you've created a savepoint, then you execute a CREATE TABLE statement,
-
# then the savepoint that was created will be automatically released.
-
#
-
# This means that, on MySQL, you shouldn't execute DDL operations inside
-
# a #transaction call that you know might create a savepoint. Otherwise,
-
# #transaction will raise exceptions when it tries to release the
-
# already-automatically-released savepoints:
-
#
-
# Model.connection.transaction do # BEGIN
-
# Model.connection.transaction(requires_new: true) do # CREATE SAVEPOINT active_record_1
-
# Model.connection.create_table(...)
-
# # active_record_1 now automatically released
-
# end # RELEASE SAVEPOINT active_record_1 <--- BOOM! database error!
-
# end
-
#
-
# == Transaction isolation
-
#
-
# If your database supports setting the isolation level for a transaction, you can set
-
# it like so:
-
#
-
# Post.transaction(isolation: :serializable) do
-
# # ...
-
# end
-
#
-
# Valid isolation levels are:
-
#
-
# * <tt>:read_uncommitted</tt>
-
# * <tt>:read_committed</tt>
-
# * <tt>:repeatable_read</tt>
-
# * <tt>:serializable</tt>
-
#
-
# You should consult the documentation for your database to understand the
-
# semantics of these different levels:
-
#
-
# * http://www.postgresql.org/docs/9.1/static/transaction-iso.html
-
# * https://dev.mysql.com/doc/refman/5.0/en/set-transaction.html
-
#
-
# An <tt>ActiveRecord::TransactionIsolationError</tt> will be raised if:
-
#
-
# * The adapter does not support setting the isolation level
-
# * You are joining an existing open transaction
-
# * You are creating a nested (savepoint) transaction
-
#
-
# The mysql, mysql2 and postgresql adapters support setting the transaction
-
# isolation level. However, support is disabled for mysql versions below 5,
-
# because they are affected by a bug[http://bugs.mysql.com/bug.php?id=39170]
-
# which means the isolation level gets persisted outside the transaction.
-
1
def transaction(options = {})
-
10
options.assert_valid_keys :requires_new, :joinable, :isolation
-
-
10
if !options[:requires_new] && current_transaction.joinable?
-
4
if options[:isolation]
-
raise ActiveRecord::TransactionIsolationError, "cannot set isolation when joining a transaction"
-
end
-
-
4
yield
-
else
-
12
within_new_transaction(options) { yield }
-
end
-
rescue ActiveRecord::Rollback
-
# rollbacks are silently swallowed
-
end
-
-
1
def within_new_transaction(options = {}) #:nodoc:
-
6
transaction = begin_transaction(options)
-
6
yield
-
rescue Exception => error
-
rollback_transaction if transaction
-
raise
-
ensure
-
6
begin
-
6
commit_transaction unless error
-
rescue Exception
-
rollback_transaction
-
raise
-
end
-
end
-
-
1
def current_transaction #:nodoc:
-
10
@transaction
-
end
-
-
1
def transaction_open?
-
4
@transaction.open?
-
end
-
-
1
def begin_transaction(options = {}) #:nodoc:
-
10
@transaction = @transaction.begin(options)
-
end
-
-
1
def commit_transaction #:nodoc:
-
6
@transaction = @transaction.commit
-
end
-
-
1
def rollback_transaction #:nodoc:
-
4
@transaction = @transaction.rollback
-
end
-
-
1
def reset_transaction #:nodoc:
-
1
@transaction = ClosedTransaction.new(self)
-
end
-
-
# Register a record with the current transaction so that its after_commit and after_rollback callbacks
-
# can be called.
-
1
def add_transaction_record(record)
-
8
@transaction.add_record(record)
-
end
-
-
# Begins the transaction (and turns off auto-committing).
-
1
def begin_db_transaction() end
-
-
1
def transaction_isolation_levels
-
{
-
read_uncommitted: "READ UNCOMMITTED",
-
read_committed: "READ COMMITTED",
-
repeatable_read: "REPEATABLE READ",
-
serializable: "SERIALIZABLE"
-
}
-
end
-
-
# Begins the transaction with the isolation level set. Raises an error by
-
# default; adapters that support setting the isolation level should implement
-
# this method.
-
1
def begin_isolated_db_transaction(isolation)
-
raise ActiveRecord::TransactionIsolationError, "adapter does not support setting transaction isolation"
-
end
-
-
# Commits the transaction (and turns on auto-committing).
-
1
def commit_db_transaction() end
-
-
# Rolls back the transaction (and turns on auto-committing). Must be
-
# done if the transaction block raises an exception or returns false.
-
1
def rollback_db_transaction() end
-
-
1
def default_sequence_name(table, column)
-
nil
-
end
-
-
# Set the sequence to the max value of the table's column.
-
1
def reset_sequence!(table, column, sequence = nil)
-
# Do nothing by default. Implement for PostgreSQL, Oracle, ...
-
end
-
-
# Inserts the given fixture into the table. Overridden in adapters that require
-
# something beyond a simple insert (eg. Oracle).
-
1
def insert_fixture(fixture, table_name)
-
columns = schema_cache.columns_hash(table_name)
-
-
key_list = []
-
value_list = fixture.map do |name, value|
-
key_list << quote_column_name(name)
-
quote(value, columns[name])
-
end
-
-
execute "INSERT INTO #{quote_table_name(table_name)} (#{key_list.join(', ')}) VALUES (#{value_list.join(', ')})", 'Fixture Insert'
-
end
-
-
1
def empty_insert_statement_value
-
"DEFAULT VALUES"
-
end
-
-
1
def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key)
-
"WHERE #{quoted_primary_key} IN (SELECT #{quoted_primary_key} FROM #{quoted_table_name} #{where_sql})"
-
end
-
-
# Sanitizes the given LIMIT parameter in order to prevent SQL injection.
-
#
-
# The +limit+ may be anything that can evaluate to a string via #to_s. It
-
# should look like an integer, or a comma-delimited list of integers, or
-
# an Arel SQL literal.
-
#
-
# Returns Integer and Arel::Nodes::SqlLiteral limits as is.
-
# Returns the sanitized limit parameter, either as an integer, or as a
-
# string which contains a comma-delimited list of integers.
-
1
def sanitize_limit(limit)
-
if limit.is_a?(Integer) || limit.is_a?(Arel::Nodes::SqlLiteral)
-
limit
-
elsif limit.to_s.include?(',')
-
Arel.sql limit.to_s.split(',').map{ |i| Integer(i) }.join(',')
-
else
-
Integer(limit)
-
end
-
end
-
-
# The default strategy for an UPDATE with joins is to use a subquery. This doesn't work
-
# on mysql (even when aliasing the tables), but mysql allows using JOIN directly in
-
# an UPDATE statement, so in the mysql adapters we redefine this to do that.
-
1
def join_to_update(update, select) #:nodoc:
-
key = update.key
-
subselect = subquery_for(key, select)
-
-
update.where key.in(subselect)
-
end
-
-
1
def join_to_delete(delete, select, key) #:nodoc:
-
subselect = subquery_for(key, select)
-
-
delete.where key.in(subselect)
-
end
-
-
1
protected
-
-
# Returns a subquery for the given key using the join information.
-
1
def subquery_for(key, select)
-
subselect = select.clone
-
subselect.projections = [key]
-
subselect
-
end
-
-
# Returns an ActiveRecord::Result instance.
-
1
def select(sql, name = nil, binds = [])
-
end
-
1
undef_method :select
-
-
# Returns the last auto-generated ID from the affected table.
-
1
def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
-
execute(sql, name)
-
id_value
-
end
-
-
# Executes the update statement and returns the number of rows affected.
-
1
def update_sql(sql, name = nil)
-
execute(sql, name)
-
end
-
-
# Executes the delete statement and returns the number of rows affected.
-
1
def delete_sql(sql, name = nil)
-
update_sql(sql, name)
-
end
-
-
1
def sql_for_insert(sql, pk, id_value, sequence_name, binds)
-
[sql, binds]
-
end
-
-
1
def last_inserted_id(result)
-
6
row = result.rows.first
-
6
row && row.first
-
end
-
-
1
def binds_from_relation(relation, binds)
-
7
if relation.is_a?(Relation) && binds.blank?
-
relation, binds = relation.arel, relation.bind_values
-
end
-
7
[relation, binds]
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
module ConnectionAdapters # :nodoc:
-
1
module QueryCache
-
1
class << self
-
1
def included(base) #:nodoc:
-
1
dirties_query_cache base, :insert, :update, :delete
-
end
-
-
1
def dirties_query_cache(base, *method_names)
-
1
method_names.each do |method_name|
-
3
base.class_eval <<-end_code, __FILE__, __LINE__ + 1
-
def #{method_name}(*)
-
clear_query_cache if @query_cache_enabled
-
super
-
end
-
end_code
-
end
-
end
-
end
-
-
1
attr_reader :query_cache, :query_cache_enabled
-
-
1
def initialize(*)
-
1
super
-
1
@query_cache = Hash.new { |h,sql| h[sql] = {} }
-
1
@query_cache_enabled = false
-
end
-
-
# Enable the query cache within the block.
-
1
def cache
-
old, @query_cache_enabled = @query_cache_enabled, true
-
yield
-
ensure
-
@query_cache_enabled = old
-
clear_query_cache unless @query_cache_enabled
-
end
-
-
1
def enable_query_cache!
-
@query_cache_enabled = true
-
end
-
-
1
def disable_query_cache!
-
@query_cache_enabled = false
-
end
-
-
# Disable the query cache within the block.
-
1
def uncached
-
old, @query_cache_enabled = @query_cache_enabled, false
-
yield
-
ensure
-
@query_cache_enabled = old
-
end
-
-
# Clears the query cache.
-
#
-
# One reason you may wish to call this method explicitly is between queries
-
# that ask the database to randomize results. Otherwise the cache would see
-
# the same SQL query and repeatedly return the same result each time, silently
-
# undermining the randomness you were expecting.
-
1
def clear_query_cache
-
@query_cache.clear
-
end
-
-
1
def select_all(arel, name = nil, binds = [])
-
7
if @query_cache_enabled && !locked?(arel)
-
arel, binds = binds_from_relation arel, binds
-
sql = to_sql(arel, binds)
-
cache_sql(sql, binds) { super(sql, name, binds) }
-
else
-
7
super
-
end
-
end
-
-
1
private
-
-
1
def cache_sql(sql, binds)
-
result =
-
if @query_cache[sql].key?(binds)
-
ActiveSupport::Notifications.instrument("sql.active_record",
-
:sql => sql, :binds => binds, :name => "CACHE", :connection_id => object_id)
-
@query_cache[sql][binds]
-
else
-
@query_cache[sql][binds] = yield
-
end
-
result.dup
-
end
-
-
# If arel is locked this is a SELECT ... FOR UPDATE or somesuch. Such
-
# queries should not be cached.
-
1
def locked?(arel)
-
arel.respond_to?(:locked) && arel.locked
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/big_decimal/conversions'
-
-
1
module ActiveRecord
-
1
module ConnectionAdapters # :nodoc:
-
1
module Quoting
-
# Quotes the column value to help prevent
-
# {SQL injection attacks}[http://en.wikipedia.org/wiki/SQL_injection].
-
1
def quote(value, column = nil)
-
# records are quoted as their primary key
-
return value.quoted_id if value.respond_to?(:quoted_id)
-
-
case value
-
when String, ActiveSupport::Multibyte::Chars
-
value = value.to_s
-
return "'#{quote_string(value)}'" unless column
-
-
case column.type
-
when :integer then value.to_i.to_s
-
when :float then value.to_f.to_s
-
else
-
"'#{quote_string(value)}'"
-
end
-
-
when true, false
-
if column && column.type == :integer
-
value ? '1' : '0'
-
else
-
value ? quoted_true : quoted_false
-
end
-
# BigDecimals need to be put in a non-normalized form and quoted.
-
when nil then "NULL"
-
when BigDecimal then value.to_s('F')
-
when Numeric, ActiveSupport::Duration then value.to_s
-
when Date, Time then "'#{quoted_date(value)}'"
-
when Symbol then "'#{quote_string(value.to_s)}'"
-
when Class then "'#{value.to_s}'"
-
else
-
"'#{quote_string(YAML.dump(value))}'"
-
end
-
end
-
-
# Cast a +value+ to a type that the database understands. For example,
-
# SQLite does not understand dates, so this method will convert a Date
-
# to a String.
-
1
def type_cast(value, column)
-
28
if value.respond_to?(:quoted_id) && value.respond_to?(:id)
-
return value.id
-
end
-
-
28
case value
-
when String, ActiveSupport::Multibyte::Chars
-
4
value = value.to_s
-
4
return value unless column
-
-
4
case column.type
-
when :integer then value.to_i
-
when :float then value.to_f
-
else
-
4
value
-
end
-
-
when true, false
-
if column && column.type == :integer
-
value ? 1 : 0
-
else
-
value ? 't' : 'f'
-
end
-
# BigDecimals need to be put in a non-normalized form and quoted.
-
when nil then nil
-
when BigDecimal then value.to_s('F')
-
8
when Numeric then value
-
16
when Date, Time then quoted_date(value)
-
when Symbol then value.to_s
-
else
-
to_type = column ? " to #{column.type}" : ""
-
raise TypeError, "can't cast #{value.class}#{to_type}"
-
end
-
end
-
-
# Quotes a string, escaping any ' (single quote) and \ (backslash)
-
# characters.
-
1
def quote_string(s)
-
s.gsub(/\\/, '\&\&').gsub(/'/, "''") # ' (for ruby-mode)
-
end
-
-
# Quotes the column name. Defaults to no quoting.
-
1
def quote_column_name(column_name)
-
column_name
-
end
-
-
# Quotes the table name. Defaults to column name quoting.
-
1
def quote_table_name(table_name)
-
quote_column_name(table_name)
-
end
-
-
# Override to return the quoted table name for assignment. Defaults to
-
# table quoting.
-
#
-
# This works for mysql and mysql2 where table.column can be used to
-
# resolve ambiguity.
-
#
-
# We override this in the sqlite and postgresql adapters to use only
-
# the column name (as per syntax requirements).
-
1
def quote_table_name_for_assignment(table, attr)
-
quote_table_name("#{table}.#{attr}")
-
end
-
-
1
def quoted_true
-
"'t'"
-
end
-
-
1
def quoted_false
-
"'f'"
-
end
-
-
1
def quoted_date(value)
-
16
if value.acts_like?(:time)
-
12
zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal
-
-
12
if value.respond_to?(zone_conversion_method)
-
12
value = value.send(zone_conversion_method)
-
end
-
end
-
-
16
value.to_s(:db)
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
module ConnectionAdapters
-
1
module Savepoints #:nodoc:
-
1
def supports_savepoints?
-
true
-
end
-
-
1
def create_savepoint(name = current_savepoint_name)
-
6
execute("SAVEPOINT #{name}")
-
end
-
-
1
def rollback_to_savepoint(name = current_savepoint_name)
-
execute("ROLLBACK TO SAVEPOINT #{name}")
-
end
-
-
1
def release_savepoint(name = current_savepoint_name)
-
6
execute("RELEASE SAVEPOINT #{name}")
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
module ConnectionAdapters
-
1
class AbstractAdapter
-
1
class SchemaCreation # :nodoc:
-
1
def initialize(conn)
-
@conn = conn
-
@cache = {}
-
end
-
-
1
def accept(o)
-
m = @cache[o.class] ||= "visit_#{o.class.name.split('::').last}"
-
send m, o
-
end
-
-
1
def visit_AddColumn(o)
-
sql_type = type_to_sql(o.type.to_sym, o.limit, o.precision, o.scale)
-
sql = "ADD #{quote_column_name(o.name)} #{sql_type}"
-
add_column_options!(sql, column_options(o))
-
end
-
-
1
private
-
-
1
def visit_AlterTable(o)
-
sql = "ALTER TABLE #{quote_table_name(o.name)} "
-
sql << o.adds.map { |col| visit_AddColumn col }.join(' ')
-
end
-
-
1
def visit_ColumnDefinition(o)
-
sql_type = type_to_sql(o.type.to_sym, o.limit, o.precision, o.scale)
-
column_sql = "#{quote_column_name(o.name)} #{sql_type}"
-
add_column_options!(column_sql, column_options(o)) unless o.primary_key?
-
column_sql
-
end
-
-
1
def visit_TableDefinition(o)
-
create_sql = "CREATE#{' TEMPORARY' if o.temporary} TABLE "
-
create_sql << "#{quote_table_name(o.name)} "
-
create_sql << "(#{o.columns.map { |c| accept c }.join(', ')}) " unless o.as
-
create_sql << "#{o.options}"
-
create_sql << " AS #{@conn.to_sql(o.as)}" if o.as
-
create_sql
-
end
-
-
1
def column_options(o)
-
column_options = {}
-
column_options[:null] = o.null unless o.null.nil?
-
column_options[:default] = o.default unless o.default.nil?
-
column_options[:column] = o
-
column_options[:first] = o.first
-
column_options[:after] = o.after
-
column_options
-
end
-
-
1
def quote_column_name(name)
-
@conn.quote_column_name name
-
end
-
-
1
def quote_table_name(name)
-
@conn.quote_table_name name
-
end
-
-
1
def type_to_sql(type, limit, precision, scale)
-
@conn.type_to_sql type.to_sym, limit, precision, scale
-
end
-
-
1
def add_column_options!(sql, options)
-
sql << " DEFAULT #{quote_value(options[:default], options[:column])}" if options_include_default?(options)
-
# must explicitly check for :null to allow change_column to work on migrations
-
if options[:null] == false
-
sql << " NOT NULL"
-
end
-
if options[:auto_increment] == true
-
sql << " AUTO_INCREMENT"
-
end
-
sql
-
end
-
-
1
def quote_value(value, column)
-
column.sql_type ||= type_to_sql(column.type, column.limit, column.precision, column.scale)
-
-
@conn.quote(value, column)
-
end
-
-
1
def options_include_default?(options)
-
options.include?(:default) && !(options[:null] == false && options[:default].nil?)
-
end
-
end
-
end
-
end
-
end
-
1
require 'date'
-
1
require 'set'
-
1
require 'bigdecimal'
-
1
require 'bigdecimal/util'
-
-
1
module ActiveRecord
-
1
module ConnectionAdapters #:nodoc:
-
# Abstract representation of an index definition on a table. Instances of
-
# this type are typically created and returned by methods in database
-
# adapters. e.g. ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter#indexes
-
1
class IndexDefinition < Struct.new(:table, :name, :unique, :columns, :lengths, :orders, :where, :type, :using) #:nodoc:
-
end
-
-
# Abstract representation of a column definition. Instances of this type
-
# are typically created by methods in TableDefinition, and added to the
-
# +columns+ attribute of said TableDefinition object, in order to be used
-
# for generating a number of table creation or table changing SQL statements.
-
1
class ColumnDefinition < Struct.new(:name, :type, :limit, :precision, :scale, :default, :null, :first, :after, :primary_key, :sql_type) #:nodoc:
-
-
1
def primary_key?
-
primary_key || type.to_sym == :primary_key
-
end
-
end
-
-
1
class ChangeColumnDefinition < Struct.new(:column, :type, :options) #:nodoc:
-
end
-
-
# Represents the schema of an SQL table in an abstract way. This class
-
# provides methods for manipulating the schema representation.
-
#
-
# Inside migration files, the +t+ object in +create_table+
-
# is actually of this type:
-
#
-
# class SomeMigration < ActiveRecord::Migration
-
# def up
-
# create_table :foo do |t|
-
# puts t.class # => "ActiveRecord::ConnectionAdapters::TableDefinition"
-
# end
-
# end
-
#
-
# def down
-
# ...
-
# end
-
# end
-
#
-
# The table definitions
-
# The Columns are stored as a ColumnDefinition in the +columns+ attribute.
-
1
class TableDefinition
-
# An array of ColumnDefinition objects, representing the column changes
-
# that have been defined.
-
1
attr_accessor :indexes
-
1
attr_reader :name, :temporary, :options, :as
-
-
1
def initialize(types, name, temporary, options, as = nil)
-
@columns_hash = {}
-
@indexes = {}
-
@native = types
-
@temporary = temporary
-
@options = options
-
@as = as
-
@name = name
-
end
-
-
1
def columns; @columns_hash.values; end
-
-
# Appends a primary key definition to the table definition.
-
# Can be called multiple times, but this is probably not a good idea.
-
1
def primary_key(name, type = :primary_key, options = {})
-
column(name, type, options.merge(:primary_key => true))
-
end
-
-
# Returns a ColumnDefinition for the column with name +name+.
-
1
def [](name)
-
@columns_hash[name.to_s]
-
end
-
-
# Instantiates a new column for the table.
-
# The +type+ parameter is normally one of the migrations native types,
-
# which is one of the following:
-
# <tt>:primary_key</tt>, <tt>:string</tt>, <tt>:text</tt>,
-
# <tt>:integer</tt>, <tt>:float</tt>, <tt>:decimal</tt>,
-
# <tt>:datetime</tt>, <tt>:timestamp</tt>, <tt>:time</tt>,
-
# <tt>:date</tt>, <tt>:binary</tt>, <tt>:boolean</tt>.
-
#
-
# You may use a type not in this list as long as it is supported by your
-
# database (for example, "polygon" in MySQL), but this will not be database
-
# agnostic and should usually be avoided.
-
#
-
# Available options are (none of these exists by default):
-
# * <tt>:limit</tt> -
-
# Requests a maximum column length. This is number of characters for <tt>:string</tt> and
-
# <tt>:text</tt> columns and number of bytes for <tt>:binary</tt> and <tt>:integer</tt> columns.
-
# * <tt>:default</tt> -
-
# The column's default value. Use nil for NULL.
-
# * <tt>:null</tt> -
-
# Allows or disallows +NULL+ values in the column. This option could
-
# have been named <tt>:null_allowed</tt>.
-
# * <tt>:precision</tt> -
-
# Specifies the precision for a <tt>:decimal</tt> column.
-
# * <tt>:scale</tt> -
-
# Specifies the scale for a <tt>:decimal</tt> column.
-
#
-
# For clarity's sake: the precision is the number of significant digits,
-
# while the scale is the number of digits that can be stored following
-
# the decimal point. For example, the number 123.45 has a precision of 5
-
# and a scale of 2. A decimal with a precision of 5 and a scale of 2 can
-
# range from -999.99 to 999.99.
-
#
-
# Please be aware of different RDBMS implementations behavior with
-
# <tt>:decimal</tt> columns:
-
# * The SQL standard says the default scale should be 0, <tt>:scale</tt> <=
-
# <tt>:precision</tt>, and makes no comments about the requirements of
-
# <tt>:precision</tt>.
-
# * MySQL: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..30].
-
# Default is (10,0).
-
# * PostgreSQL: <tt>:precision</tt> [1..infinity],
-
# <tt>:scale</tt> [0..infinity]. No default.
-
# * SQLite2: Any <tt>:precision</tt> and <tt>:scale</tt> may be used.
-
# Internal storage as strings. No default.
-
# * SQLite3: No restrictions on <tt>:precision</tt> and <tt>:scale</tt>,
-
# but the maximum supported <tt>:precision</tt> is 16. No default.
-
# * Oracle: <tt>:precision</tt> [1..38], <tt>:scale</tt> [-84..127].
-
# Default is (38,0).
-
# * DB2: <tt>:precision</tt> [1..63], <tt>:scale</tt> [0..62].
-
# Default unknown.
-
# * Firebird: <tt>:precision</tt> [1..18], <tt>:scale</tt> [0..18].
-
# Default (9,0). Internal types NUMERIC and DECIMAL have different
-
# storage rules, decimal being better.
-
# * FrontBase?: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
-
# Default (38,0). WARNING Max <tt>:precision</tt>/<tt>:scale</tt> for
-
# NUMERIC is 19, and DECIMAL is 38.
-
# * SqlServer?: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
-
# Default (38,0).
-
# * Sybase: <tt>:precision</tt> [1..38], <tt>:scale</tt> [0..38].
-
# Default (38,0).
-
# * OpenBase?: Documentation unclear. Claims storage in <tt>double</tt>.
-
#
-
# This method returns <tt>self</tt>.
-
#
-
# == Examples
-
# # Assuming +td+ is an instance of TableDefinition
-
# td.column(:granted, :boolean)
-
# # granted BOOLEAN
-
#
-
# td.column(:picture, :binary, limit: 2.megabytes)
-
# # => picture BLOB(2097152)
-
#
-
# td.column(:sales_stage, :string, limit: 20, default: 'new', null: false)
-
# # => sales_stage VARCHAR(20) DEFAULT 'new' NOT NULL
-
#
-
# td.column(:bill_gates_money, :decimal, precision: 15, scale: 2)
-
# # => bill_gates_money DECIMAL(15,2)
-
#
-
# td.column(:sensor_reading, :decimal, precision: 30, scale: 20)
-
# # => sensor_reading DECIMAL(30,20)
-
#
-
# # While <tt>:scale</tt> defaults to zero on most databases, it
-
# # probably wouldn't hurt to include it.
-
# td.column(:huge_integer, :decimal, precision: 30)
-
# # => huge_integer DECIMAL(30)
-
#
-
# # Defines a column with a database-specific type.
-
# td.column(:foo, 'polygon')
-
# # => foo polygon
-
#
-
# == Short-hand examples
-
#
-
# Instead of calling +column+ directly, you can also work with the short-hand definitions for the default types.
-
# They use the type as the method name instead of as a parameter and allow for multiple columns to be defined
-
# in a single statement.
-
#
-
# What can be written like this with the regular calls to column:
-
#
-
# create_table :products do |t|
-
# t.column :shop_id, :integer
-
# t.column :creator_id, :integer
-
# t.column :name, :string, default: "Untitled"
-
# t.column :value, :string, default: "Untitled"
-
# t.column :created_at, :datetime
-
# t.column :updated_at, :datetime
-
# end
-
#
-
# can also be written as follows using the short-hand:
-
#
-
# create_table :products do |t|
-
# t.integer :shop_id, :creator_id
-
# t.string :name, :value, default: "Untitled"
-
# t.timestamps
-
# end
-
#
-
# There's a short-hand method for each of the type values declared at the top. And then there's
-
# TableDefinition#timestamps that'll add +created_at+ and +updated_at+ as datetimes.
-
#
-
# TableDefinition#references will add an appropriately-named _id column, plus a corresponding _type
-
# column if the <tt>:polymorphic</tt> option is supplied. If <tt>:polymorphic</tt> is a hash of
-
# options, these will be used when creating the <tt>_type</tt> column. The <tt>:index</tt> option
-
# will also create an index, similar to calling <tt>add_index</tt>. So what can be written like this:
-
#
-
# create_table :taggings do |t|
-
# t.integer :tag_id, :tagger_id, :taggable_id
-
# t.string :tagger_type
-
# t.string :taggable_type, default: 'Photo'
-
# end
-
# add_index :taggings, :tag_id, name: 'index_taggings_on_tag_id'
-
# add_index :taggings, [:tagger_id, :tagger_type]
-
#
-
# Can also be written as follows using references:
-
#
-
# create_table :taggings do |t|
-
# t.references :tag, index: { name: 'index_taggings_on_tag_id' }
-
# t.references :tagger, polymorphic: true, index: true
-
# t.references :taggable, polymorphic: { default: 'Photo' }
-
# end
-
1
def column(name, type, options = {})
-
name = name.to_s
-
type = type.to_sym
-
-
if primary_key_column_name == name
-
raise ArgumentError, "you can't redefine the primary key column '#{name}'. To define a custom primary key, pass { id: false } to create_table."
-
end
-
-
@columns_hash[name] = new_column_definition(name, type, options)
-
self
-
end
-
-
1
def remove_column(name)
-
@columns_hash.delete name.to_s
-
end
-
-
1
[:string, :text, :integer, :float, :decimal, :datetime, :timestamp, :time, :date, :binary, :boolean].each do |column_type|
-
11
define_method column_type do |*args|
-
options = args.extract_options!
-
column_names = args
-
column_names.each { |name| column(name, column_type, options) }
-
end
-
end
-
-
# Adds index options to the indexes hash, keyed by column name
-
# This is primarily used to track indexes that need to be created after the table
-
#
-
# index(:account_id, name: 'index_projects_on_account_id')
-
1
def index(column_name, options = {})
-
indexes[column_name] = options
-
end
-
-
# Appends <tt>:datetime</tt> columns <tt>:created_at</tt> and
-
# <tt>:updated_at</tt> to the table.
-
1
def timestamps(*args)
-
options = args.extract_options!
-
column(:created_at, :datetime, options)
-
column(:updated_at, :datetime, options)
-
end
-
-
1
def references(*args)
-
options = args.extract_options!
-
polymorphic = options.delete(:polymorphic)
-
index_options = options.delete(:index)
-
args.each do |col|
-
column("#{col}_id", :integer, options)
-
column("#{col}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) if polymorphic
-
index(polymorphic ? %w(id type).map { |t| "#{col}_#{t}" } : "#{col}_id", index_options.is_a?(Hash) ? index_options : {}) if index_options
-
end
-
end
-
1
alias :belongs_to :references
-
-
1
def new_column_definition(name, type, options) # :nodoc:
-
column = create_column_definition name, type
-
limit = options.fetch(:limit) do
-
native[type][:limit] if native[type].is_a?(Hash)
-
end
-
-
column.limit = limit
-
column.array = options[:array] if column.respond_to?(:array)
-
column.precision = options[:precision]
-
column.scale = options[:scale]
-
column.default = options[:default]
-
column.null = options[:null]
-
column.first = options[:first]
-
column.after = options[:after]
-
column.primary_key = type == :primary_key || options[:primary_key]
-
column
-
end
-
-
1
private
-
1
def create_column_definition(name, type)
-
ColumnDefinition.new name, type
-
end
-
-
1
def primary_key_column_name
-
primary_key_column = columns.detect { |c| c.primary_key? }
-
primary_key_column && primary_key_column.name
-
end
-
-
1
def native
-
@native
-
end
-
end
-
-
1
class AlterTable # :nodoc:
-
1
attr_reader :adds
-
-
1
def initialize(td)
-
@td = td
-
@adds = []
-
end
-
-
1
def name; @td.name; end
-
-
1
def add_column(name, type, options)
-
name = name.to_s
-
type = type.to_sym
-
@adds << @td.new_column_definition(name, type, options)
-
end
-
end
-
-
# Represents an SQL table in an abstract way for updating a table.
-
# Also see TableDefinition and SchemaStatements#create_table
-
#
-
# Available transformations are:
-
#
-
# change_table :table do |t|
-
# t.column
-
# t.index
-
# t.rename_index
-
# t.timestamps
-
# t.change
-
# t.change_default
-
# t.rename
-
# t.references
-
# t.belongs_to
-
# t.string
-
# t.text
-
# t.integer
-
# t.float
-
# t.decimal
-
# t.datetime
-
# t.timestamp
-
# t.time
-
# t.date
-
# t.binary
-
# t.boolean
-
# t.remove
-
# t.remove_references
-
# t.remove_belongs_to
-
# t.remove_index
-
# t.remove_timestamps
-
# end
-
#
-
1
class Table
-
1
def initialize(table_name, base)
-
@table_name = table_name
-
@base = base
-
end
-
-
# Adds a new column to the named table.
-
# See TableDefinition#column for details of the options you can use.
-
#
-
# ====== Creating a simple column
-
# t.column(:name, :string)
-
1
def column(column_name, type, options = {})
-
@base.add_column(@table_name, column_name, type, options)
-
end
-
-
# Checks to see if a column exists. See SchemaStatements#column_exists?
-
1
def column_exists?(column_name, type = nil, options = {})
-
@base.column_exists?(@table_name, column_name, type, options)
-
end
-
-
# Adds a new index to the table. +column_name+ can be a single Symbol, or
-
# an Array of Symbols. See SchemaStatements#add_index
-
#
-
# ====== Creating a simple index
-
# t.index(:name)
-
# ====== Creating a unique index
-
# t.index([:branch_id, :party_id], unique: true)
-
# ====== Creating a named index
-
# t.index([:branch_id, :party_id], unique: true, name: 'by_branch_party')
-
1
def index(column_name, options = {})
-
@base.add_index(@table_name, column_name, options)
-
end
-
-
# Checks to see if an index exists. See SchemaStatements#index_exists?
-
1
def index_exists?(column_name, options = {})
-
@base.index_exists?(@table_name, column_name, options)
-
end
-
-
# Renames the given index on the table.
-
#
-
# t.rename_index(:user_id, :account_id)
-
1
def rename_index(index_name, new_index_name)
-
@base.rename_index(@table_name, index_name, new_index_name)
-
end
-
-
# Adds timestamps (+created_at+ and +updated_at+) columns to the table. See SchemaStatements#add_timestamps
-
#
-
# t.timestamps
-
1
def timestamps
-
@base.add_timestamps(@table_name)
-
end
-
-
# Changes the column's definition according to the new options.
-
# See TableDefinition#column for details of the options you can use.
-
#
-
# t.change(:name, :string, limit: 80)
-
# t.change(:description, :text)
-
1
def change(column_name, type, options = {})
-
@base.change_column(@table_name, column_name, type, options)
-
end
-
-
# Sets a new default value for a column. See SchemaStatements#change_column_default
-
#
-
# t.change_default(:qualification, 'new')
-
# t.change_default(:authorized, 1)
-
1
def change_default(column_name, default)
-
@base.change_column_default(@table_name, column_name, default)
-
end
-
-
# Removes the column(s) from the table definition.
-
#
-
# t.remove(:qualification)
-
# t.remove(:qualification, :experience)
-
1
def remove(*column_names)
-
@base.remove_columns(@table_name, *column_names)
-
end
-
-
# Removes the given index from the table.
-
#
-
# ====== Remove the index_table_name_on_column in the table_name table
-
# t.remove_index :column
-
# ====== Remove the index named index_table_name_on_branch_id in the table_name table
-
# t.remove_index column: :branch_id
-
# ====== Remove the index named index_table_name_on_branch_id_and_party_id in the table_name table
-
# t.remove_index column: [:branch_id, :party_id]
-
# ====== Remove the index named by_branch_party in the table_name table
-
# t.remove_index name: :by_branch_party
-
1
def remove_index(options = {})
-
@base.remove_index(@table_name, options)
-
end
-
-
# Removes the timestamp columns (+created_at+ and +updated_at+) from the table.
-
#
-
# t.remove_timestamps
-
1
def remove_timestamps
-
@base.remove_timestamps(@table_name)
-
end
-
-
# Renames a column.
-
#
-
# t.rename(:description, :name)
-
1
def rename(column_name, new_column_name)
-
@base.rename_column(@table_name, column_name, new_column_name)
-
end
-
-
# Adds a reference. Optionally adds a +type+ column, if <tt>:polymorphic</tt> option is provided.
-
# <tt>references</tt> and <tt>belongs_to</tt> are acceptable.
-
#
-
# t.references(:user)
-
# t.belongs_to(:supplier, polymorphic: true)
-
#
-
1
def references(*args)
-
options = args.extract_options!
-
args.each do |ref_name|
-
@base.add_reference(@table_name, ref_name, options)
-
end
-
end
-
1
alias :belongs_to :references
-
-
# Removes a reference. Optionally removes a +type+ column.
-
# <tt>remove_references</tt> and <tt>remove_belongs_to</tt> are acceptable.
-
#
-
# t.remove_references(:user)
-
# t.remove_belongs_to(:supplier, polymorphic: true)
-
#
-
1
def remove_references(*args)
-
options = args.extract_options!
-
args.each do |ref_name|
-
@base.remove_reference(@table_name, ref_name, options)
-
end
-
end
-
1
alias :remove_belongs_to :remove_references
-
-
# Adds a column or columns of a specified type
-
#
-
# t.string(:goat)
-
# t.string(:goat, :sheep)
-
1
[:string, :text, :integer, :float, :decimal, :datetime, :timestamp, :time, :date, :binary, :boolean].each do |column_type|
-
11
define_method column_type do |*args|
-
options = args.extract_options!
-
args.each do |name|
-
@base.add_column(@table_name, name, column_type, options)
-
end
-
end
-
end
-
-
1
private
-
1
def native
-
@base.native_database_types
-
end
-
end
-
-
end
-
end
-
1
require 'ipaddr'
-
-
1
module ActiveRecord
-
1
module ConnectionAdapters # :nodoc:
-
# The goal of this module is to move Adapter specific column
-
# definitions to the Adapter instead of having it in the schema
-
# dumper itself. This code represents the normal case.
-
# We can then redefine how certain data types may be handled in the schema dumper on the
-
# Adapter level by over-writing this code inside the database specific adapters
-
1
module ColumnDumper
-
1
def column_spec(column, types)
-
spec = prepare_column_options(column, types)
-
(spec.keys - [:name, :type]).each{ |k| spec[k].insert(0, "#{k.to_s}: ")}
-
spec
-
end
-
-
# This can be overridden on a Adapter level basis to support other
-
# extended datatypes (Example: Adding an array option in the
-
# PostgreSQLAdapter)
-
1
def prepare_column_options(column, types)
-
spec = {}
-
spec[:name] = column.name.inspect
-
-
# AR has an optimization which handles zero-scale decimals as integers. This
-
# code ensures that the dumper still dumps the column as a decimal.
-
spec[:type] = if column.type == :integer && /^(numeric|decimal)/ =~ column.sql_type
-
'decimal'
-
else
-
column.type.to_s
-
end
-
spec[:limit] = column.limit.inspect if column.limit != types[column.type][:limit] && spec[:type] != 'decimal'
-
spec[:precision] = column.precision.inspect if column.precision
-
spec[:scale] = column.scale.inspect if column.scale
-
spec[:null] = 'false' unless column.null
-
spec[:default] = default_string(column.default) if column.has_default?
-
spec
-
end
-
-
# Lists the valid migration options
-
1
def migration_keys
-
[:name, :limit, :precision, :scale, :default, :null]
-
end
-
-
1
private
-
-
1
def default_string(value)
-
case value
-
when BigDecimal
-
value.to_s
-
when Date, DateTime, Time
-
"'#{value.to_s(:db)}'"
-
when Range
-
# infinity dumps as Infinity, which causes uninitialized constant error
-
value.inspect.gsub('Infinity', '::Float::INFINITY')
-
when IPAddr
-
subnet_mask = value.instance_variable_get(:@mask_addr)
-
-
# If the subnet mask is equal to /32, don't output it
-
if subnet_mask == (2**32 - 1)
-
"\"#{value.to_s}\""
-
else
-
"\"#{value.to_s}/#{subnet_mask.to_s(2).count('1')}\""
-
end
-
else
-
value.inspect
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_record/migration/join_table'
-
-
1
module ActiveRecord
-
1
module ConnectionAdapters # :nodoc:
-
1
module SchemaStatements
-
1
include ActiveRecord::Migration::JoinTable
-
-
# Returns a hash of mappings from the abstract data types to the native
-
# database types. See TableDefinition#column for details on the recognized
-
# abstract data types.
-
1
def native_database_types
-
{}
-
end
-
-
# Truncates a table alias according to the limits of the current adapter.
-
1
def table_alias_for(table_name)
-
table_name[0...table_alias_length].tr('.', '_')
-
end
-
-
# Checks to see if the table +table_name+ exists on the database.
-
#
-
# table_exists?(:developers)
-
#
-
1
def table_exists?(table_name)
-
tables.include?(table_name.to_s)
-
end
-
-
# Returns an array of indexes for the given table.
-
# def indexes(table_name, name = nil) end
-
-
# Checks to see if an index exists on a table for a given index definition.
-
#
-
# # Check an index exists
-
# index_exists?(:suppliers, :company_id)
-
#
-
# # Check an index on multiple columns exists
-
# index_exists?(:suppliers, [:company_id, :company_type])
-
#
-
# # Check a unique index exists
-
# index_exists?(:suppliers, :company_id, unique: true)
-
#
-
# # Check an index with a custom name exists
-
# index_exists?(:suppliers, :company_id, name: "idx_company_id")
-
#
-
1
def index_exists?(table_name, column_name, options = {})
-
column_names = Array(column_name)
-
index_name = options.key?(:name) ? options[:name].to_s : index_name(table_name, :column => column_names)
-
if options[:unique]
-
indexes(table_name).any?{ |i| i.unique && i.name == index_name }
-
else
-
indexes(table_name).any?{ |i| i.name == index_name }
-
end
-
end
-
-
# Returns an array of Column objects for the table specified by +table_name+.
-
# See the concrete implementation for details on the expected parameter values.
-
1
def columns(table_name) end
-
-
# Checks to see if a column exists in a given table.
-
#
-
# # Check a column exists
-
# column_exists?(:suppliers, :name)
-
#
-
# # Check a column exists of a particular type
-
# column_exists?(:suppliers, :name, :string)
-
#
-
# # Check a column exists with a specific definition
-
# column_exists?(:suppliers, :name, :string, limit: 100)
-
# column_exists?(:suppliers, :name, :string, default: 'default')
-
# column_exists?(:suppliers, :name, :string, null: false)
-
# column_exists?(:suppliers, :tax, :decimal, precision: 8, scale: 2)
-
#
-
1
def column_exists?(table_name, column_name, type = nil, options = {})
-
columns(table_name).any?{ |c| c.name == column_name.to_s &&
-
(!type || c.type == type) &&
-
(!options.key?(:limit) || c.limit == options[:limit]) &&
-
(!options.key?(:precision) || c.precision == options[:precision]) &&
-
(!options.key?(:scale) || c.scale == options[:scale]) &&
-
(!options.key?(:default) || c.default == options[:default]) &&
-
(!options.key?(:null) || c.null == options[:null]) }
-
end
-
-
# Creates a new table with the name +table_name+. +table_name+ may either
-
# be a String or a Symbol.
-
#
-
# There are two ways to work with +create_table+. You can use the block
-
# form or the regular form, like this:
-
#
-
# === Block form
-
#
-
# # create_table() passes a TableDefinition object to the block.
-
# # This form will not only create the table, but also columns for the
-
# # table.
-
#
-
# create_table(:suppliers) do |t|
-
# t.column :name, :string, limit: 60
-
# # Other fields here
-
# end
-
#
-
# === Block form, with shorthand
-
#
-
# # You can also use the column types as method calls, rather than calling the column method.
-
# create_table(:suppliers) do |t|
-
# t.string :name, limit: 60
-
# # Other fields here
-
# end
-
#
-
# === Regular form
-
#
-
# # Creates a table called 'suppliers' with no columns.
-
# create_table(:suppliers)
-
# # Add a column to 'suppliers'.
-
# add_column(:suppliers, :name, :string, {limit: 60})
-
#
-
# The +options+ hash can include the following keys:
-
# [<tt>:id</tt>]
-
# Whether to automatically add a primary key column. Defaults to true.
-
# Join tables for +has_and_belongs_to_many+ should set it to false.
-
# [<tt>:primary_key</tt>]
-
# The name of the primary key, if one is to be added automatically.
-
# Defaults to +id+. If <tt>:id</tt> is false this option is ignored.
-
#
-
# Note that Active Record models will automatically detect their
-
# primary key. This can be avoided by using +self.primary_key=+ on the model
-
# to define the key explicitly.
-
#
-
# [<tt>:options</tt>]
-
# Any extra options you want appended to the table definition.
-
# [<tt>:temporary</tt>]
-
# Make a temporary table.
-
# [<tt>:force</tt>]
-
# Set to true to drop the table before creating it.
-
# Defaults to false.
-
# [<tt>:as</tt>]
-
# SQL to use to generate the table. When this option is used, the block is
-
# ignored, as are the <tt>:id</tt> and <tt>:primary_key</tt> options.
-
#
-
# ====== Add a backend specific option to the generated SQL (MySQL)
-
#
-
# create_table(:suppliers, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8')
-
#
-
# generates:
-
#
-
# CREATE TABLE suppliers (
-
# id int(11) DEFAULT NULL auto_increment PRIMARY KEY
-
# ) ENGINE=InnoDB DEFAULT CHARSET=utf8
-
#
-
# ====== Rename the primary key column
-
#
-
# create_table(:objects, primary_key: 'guid') do |t|
-
# t.column :name, :string, limit: 80
-
# end
-
#
-
# generates:
-
#
-
# CREATE TABLE objects (
-
# guid int(11) DEFAULT NULL auto_increment PRIMARY KEY,
-
# name varchar(80)
-
# )
-
#
-
# ====== Do not add a primary key column
-
#
-
# create_table(:categories_suppliers, id: false) do |t|
-
# t.column :category_id, :integer
-
# t.column :supplier_id, :integer
-
# end
-
#
-
# generates:
-
#
-
# CREATE TABLE categories_suppliers (
-
# category_id int,
-
# supplier_id int
-
# )
-
#
-
# ====== Create a temporary table based on a query
-
#
-
# create_table(:long_query, temporary: true,
-
# as: "SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id")
-
#
-
# generates:
-
#
-
# CREATE TEMPORARY TABLE long_query AS
-
# SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id
-
#
-
# See also TableDefinition#column for details on how to create columns.
-
1
def create_table(table_name, options = {})
-
td = create_table_definition table_name, options[:temporary], options[:options], options[:as]
-
-
if !options[:as]
-
unless options[:id] == false
-
pk = options.fetch(:primary_key) {
-
Base.get_primary_key table_name.to_s.singularize
-
}
-
-
td.primary_key pk, options.fetch(:id, :primary_key), options
-
end
-
-
yield td if block_given?
-
end
-
-
if options[:force] && table_exists?(table_name)
-
drop_table(table_name, options)
-
end
-
-
execute schema_creation.accept td
-
td.indexes.each_pair { |c,o| add_index table_name, c, o }
-
end
-
-
# Creates a new join table with the name created using the lexical order of the first two
-
# arguments. These arguments can be a String or a Symbol.
-
#
-
# # Creates a table called 'assemblies_parts' with no id.
-
# create_join_table(:assemblies, :parts)
-
#
-
# You can pass a +options+ hash can include the following keys:
-
# [<tt>:table_name</tt>]
-
# Sets the table name overriding the default
-
# [<tt>:column_options</tt>]
-
# Any extra options you want appended to the columns definition.
-
# [<tt>:options</tt>]
-
# Any extra options you want appended to the table definition.
-
# [<tt>:temporary</tt>]
-
# Make a temporary table.
-
# [<tt>:force</tt>]
-
# Set to true to drop the table before creating it.
-
# Defaults to false.
-
#
-
# Note that +create_join_table+ does not create any indices by default; you can use
-
# its block form to do so yourself:
-
#
-
# create_join_table :products, :categories do |t|
-
# t.index :product_id
-
# t.index :category_id
-
# end
-
#
-
# ====== Add a backend specific option to the generated SQL (MySQL)
-
#
-
# create_join_table(:assemblies, :parts, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8')
-
#
-
# generates:
-
#
-
# CREATE TABLE assemblies_parts (
-
# assembly_id int NOT NULL,
-
# part_id int NOT NULL,
-
# ) ENGINE=InnoDB DEFAULT CHARSET=utf8
-
#
-
1
def create_join_table(table_1, table_2, options = {})
-
join_table_name = find_join_table_name(table_1, table_2, options)
-
-
column_options = options.delete(:column_options) || {}
-
column_options.reverse_merge!(null: false)
-
-
t1_column, t2_column = [table_1, table_2].map{ |t| t.to_s.singularize.foreign_key }
-
-
create_table(join_table_name, options.merge!(id: false)) do |td|
-
td.integer t1_column, column_options
-
td.integer t2_column, column_options
-
yield td if block_given?
-
end
-
end
-
-
# Drops the join table specified by the given arguments.
-
# See +create_join_table+ for details.
-
#
-
# Although this command ignores the block if one is given, it can be helpful
-
# to provide one in a migration's +change+ method so it can be reverted.
-
# In that case, the block will be used by create_join_table.
-
1
def drop_join_table(table_1, table_2, options = {})
-
join_table_name = find_join_table_name(table_1, table_2, options)
-
drop_table(join_table_name)
-
end
-
-
# A block for changing columns in +table+.
-
#
-
# # change_table() yields a Table instance
-
# change_table(:suppliers) do |t|
-
# t.column :name, :string, limit: 60
-
# # Other column alterations here
-
# end
-
#
-
# The +options+ hash can include the following keys:
-
# [<tt>:bulk</tt>]
-
# Set this to true to make this a bulk alter query, such as
-
#
-
# ALTER TABLE `users` ADD COLUMN age INT(11), ADD COLUMN birthdate DATETIME ...
-
#
-
# Defaults to false.
-
#
-
# ====== Add a column
-
#
-
# change_table(:suppliers) do |t|
-
# t.column :name, :string, limit: 60
-
# end
-
#
-
# ====== Add 2 integer columns
-
#
-
# change_table(:suppliers) do |t|
-
# t.integer :width, :height, null: false, default: 0
-
# end
-
#
-
# ====== Add created_at/updated_at columns
-
#
-
# change_table(:suppliers) do |t|
-
# t.timestamps
-
# end
-
#
-
# ====== Add a foreign key column
-
#
-
# change_table(:suppliers) do |t|
-
# t.references :company
-
# end
-
#
-
# Creates a <tt>company_id(integer)</tt> column.
-
#
-
# ====== Add a polymorphic foreign key column
-
#
-
# change_table(:suppliers) do |t|
-
# t.belongs_to :company, polymorphic: true
-
# end
-
#
-
# Creates <tt>company_type(varchar)</tt> and <tt>company_id(integer)</tt> columns.
-
#
-
# ====== Remove a column
-
#
-
# change_table(:suppliers) do |t|
-
# t.remove :company
-
# end
-
#
-
# ====== Remove several columns
-
#
-
# change_table(:suppliers) do |t|
-
# t.remove :company_id
-
# t.remove :width, :height
-
# end
-
#
-
# ====== Remove an index
-
#
-
# change_table(:suppliers) do |t|
-
# t.remove_index :company_id
-
# end
-
#
-
# See also Table for details on all of the various column transformation.
-
1
def change_table(table_name, options = {})
-
if supports_bulk_alter? && options[:bulk]
-
recorder = ActiveRecord::Migration::CommandRecorder.new(self)
-
yield update_table_definition(table_name, recorder)
-
bulk_change_table(table_name, recorder.commands)
-
else
-
yield update_table_definition(table_name, self)
-
end
-
end
-
-
# Renames a table.
-
#
-
# rename_table('octopuses', 'octopi')
-
#
-
1
def rename_table(table_name, new_name)
-
raise NotImplementedError, "rename_table is not implemented"
-
end
-
-
# Drops a table from the database.
-
#
-
# Although this command ignores +options+ and the block if one is given, it can be helpful
-
# to provide these in a migration's +change+ method so it can be reverted.
-
# In that case, +options+ and the block will be used by create_table.
-
1
def drop_table(table_name, options = {})
-
execute "DROP TABLE #{quote_table_name(table_name)}"
-
end
-
-
# Adds a new column to the named table.
-
# See TableDefinition#column for details of the options you can use.
-
1
def add_column(table_name, column_name, type, options = {})
-
at = create_alter_table table_name
-
at.add_column(column_name, type, options)
-
execute schema_creation.accept at
-
end
-
-
# Removes the given columns from the table definition.
-
#
-
# remove_columns(:suppliers, :qualification, :experience)
-
#
-
1
def remove_columns(table_name, *column_names)
-
raise ArgumentError.new("You must specify at least one column name. Example: remove_columns(:people, :first_name)") if column_names.empty?
-
column_names.each do |column_name|
-
remove_column(table_name, column_name)
-
end
-
end
-
-
# Removes the column from the table definition.
-
#
-
# remove_column(:suppliers, :qualification)
-
#
-
# The +type+ and +options+ parameters will be ignored if present. It can be helpful
-
# to provide these in a migration's +change+ method so it can be reverted.
-
# In that case, +type+ and +options+ will be used by add_column.
-
1
def remove_column(table_name, column_name, type = nil, options = {})
-
execute "ALTER TABLE #{quote_table_name(table_name)} DROP #{quote_column_name(column_name)}"
-
end
-
-
# Changes the column's definition according to the new options.
-
# See TableDefinition#column for details of the options you can use.
-
#
-
# change_column(:suppliers, :name, :string, limit: 80)
-
# change_column(:accounts, :description, :text)
-
#
-
1
def change_column(table_name, column_name, type, options = {})
-
raise NotImplementedError, "change_column is not implemented"
-
end
-
-
# Sets a new default value for a column:
-
#
-
# change_column_default(:suppliers, :qualification, 'new')
-
# change_column_default(:accounts, :authorized, 1)
-
#
-
# Setting the default to +nil+ effectively drops the default:
-
#
-
# change_column_default(:users, :email, nil)
-
#
-
1
def change_column_default(table_name, column_name, default)
-
raise NotImplementedError, "change_column_default is not implemented"
-
end
-
-
# Sets or removes a +NOT NULL+ constraint on a column. The +null+ flag
-
# indicates whether the value can be +NULL+. For example
-
#
-
# change_column_null(:users, :nickname, false)
-
#
-
# says nicknames cannot be +NULL+ (adds the constraint), whereas
-
#
-
# change_column_null(:users, :nickname, true)
-
#
-
# allows them to be +NULL+ (drops the constraint).
-
#
-
# The method accepts an optional fourth argument to replace existing
-
# +NULL+s with some other value. Use that one when enabling the
-
# constraint if needed, since otherwise those rows would not be valid.
-
#
-
# Please note the fourth argument does not set a column's default.
-
1
def change_column_null(table_name, column_name, null, default = nil)
-
raise NotImplementedError, "change_column_null is not implemented"
-
end
-
-
# Renames a column.
-
#
-
# rename_column(:suppliers, :description, :name)
-
#
-
1
def rename_column(table_name, column_name, new_column_name)
-
raise NotImplementedError, "rename_column is not implemented"
-
end
-
-
# Adds a new index to the table. +column_name+ can be a single Symbol, or
-
# an Array of Symbols.
-
#
-
# The index will be named after the table and the column name(s), unless
-
# you pass <tt>:name</tt> as an option.
-
#
-
# ====== Creating a simple index
-
#
-
# add_index(:suppliers, :name)
-
#
-
# generates:
-
#
-
# CREATE INDEX suppliers_name_index ON suppliers(name)
-
#
-
# ====== Creating a unique index
-
#
-
# add_index(:accounts, [:branch_id, :party_id], unique: true)
-
#
-
# generates:
-
#
-
# CREATE UNIQUE INDEX accounts_branch_id_party_id_index ON accounts(branch_id, party_id)
-
#
-
# ====== Creating a named index
-
#
-
# add_index(:accounts, [:branch_id, :party_id], unique: true, name: 'by_branch_party')
-
#
-
# generates:
-
#
-
# CREATE UNIQUE INDEX by_branch_party ON accounts(branch_id, party_id)
-
#
-
# ====== Creating an index with specific key length
-
#
-
# add_index(:accounts, :name, name: 'by_name', length: 10)
-
#
-
# generates:
-
#
-
# CREATE INDEX by_name ON accounts(name(10))
-
#
-
# add_index(:accounts, [:name, :surname], name: 'by_name_surname', length: {name: 10, surname: 15})
-
#
-
# generates:
-
#
-
# CREATE INDEX by_name_surname ON accounts(name(10), surname(15))
-
#
-
# Note: SQLite doesn't support index length.
-
#
-
# ====== Creating an index with a sort order (desc or asc, asc is the default)
-
#
-
# add_index(:accounts, [:branch_id, :party_id, :surname], order: {branch_id: :desc, party_id: :asc})
-
#
-
# generates:
-
#
-
# CREATE INDEX by_branch_desc_party ON accounts(branch_id DESC, party_id ASC, surname)
-
#
-
# Note: MySQL doesn't yet support index order (it accepts the syntax but ignores it).
-
#
-
# ====== Creating a partial index
-
#
-
# add_index(:accounts, [:branch_id, :party_id], unique: true, where: "active")
-
#
-
# generates:
-
#
-
# CREATE UNIQUE INDEX index_accounts_on_branch_id_and_party_id ON accounts(branch_id, party_id) WHERE active
-
#
-
# ====== Creating an index with a specific method
-
#
-
# add_index(:developers, :name, using: 'btree')
-
#
-
# generates:
-
#
-
# CREATE INDEX index_developers_on_name ON developers USING btree (name) -- PostgreSQL
-
# CREATE INDEX index_developers_on_name USING btree ON developers (name) -- MySQL
-
#
-
# Note: only supported by PostgreSQL and MySQL
-
#
-
# ====== Creating an index with a specific type
-
#
-
# add_index(:developers, :name, type: :fulltext)
-
#
-
# generates:
-
#
-
# CREATE FULLTEXT INDEX index_developers_on_name ON developers (name) -- MySQL
-
#
-
# Note: only supported by MySQL. Supported: <tt>:fulltext</tt> and <tt>:spatial</tt> on MyISAM tables.
-
1
def add_index(table_name, column_name, options = {})
-
index_name, index_type, index_columns, index_options = add_index_options(table_name, column_name, options)
-
execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{index_columns})#{index_options}"
-
end
-
-
# Removes the given index from the table.
-
#
-
# Removes the +index_accounts_on_column+ in the +accounts+ table.
-
#
-
# remove_index :accounts, :column
-
#
-
# Removes the index named +index_accounts_on_branch_id+ in the +accounts+ table.
-
#
-
# remove_index :accounts, column: :branch_id
-
#
-
# Removes the index named +index_accounts_on_branch_id_and_party_id+ in the +accounts+ table.
-
#
-
# remove_index :accounts, column: [:branch_id, :party_id]
-
#
-
# Removes the index named +by_branch_party+ in the +accounts+ table.
-
#
-
# remove_index :accounts, name: :by_branch_party
-
#
-
1
def remove_index(table_name, options = {})
-
remove_index!(table_name, index_name_for_remove(table_name, options))
-
end
-
-
1
def remove_index!(table_name, index_name) #:nodoc:
-
execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
-
end
-
-
# Renames an index.
-
#
-
# Rename the +index_people_on_last_name+ index to +index_users_on_last_name+:
-
#
-
# rename_index :people, 'index_people_on_last_name', 'index_users_on_last_name'
-
#
-
1
def rename_index(table_name, old_name, new_name)
-
# this is a naive implementation; some DBs may support this more efficiently (Postgres, for instance)
-
old_index_def = indexes(table_name).detect { |i| i.name == old_name }
-
return unless old_index_def
-
add_index(table_name, old_index_def.columns, name: new_name, unique: old_index_def.unique)
-
remove_index(table_name, name: old_name)
-
end
-
-
1
def index_name(table_name, options) #:nodoc:
-
if Hash === options
-
if options[:column]
-
"index_#{table_name}_on_#{Array(options[:column]) * '_and_'}"
-
elsif options[:name]
-
options[:name]
-
else
-
raise ArgumentError, "You must specify the index name"
-
end
-
else
-
index_name(table_name, :column => options)
-
end
-
end
-
-
# Verifies the existence of an index with a given name.
-
#
-
# The default argument is returned if the underlying implementation does not define the indexes method,
-
# as there's no way to determine the correct answer in that case.
-
1
def index_name_exists?(table_name, index_name, default)
-
return default unless respond_to?(:indexes)
-
index_name = index_name.to_s
-
indexes(table_name).detect { |i| i.name == index_name }
-
end
-
-
# Adds a reference. Optionally adds a +type+ column, if <tt>:polymorphic</tt> option is provided.
-
# <tt>add_reference</tt> and <tt>add_belongs_to</tt> are acceptable.
-
#
-
# ====== Create a user_id column
-
#
-
# add_reference(:products, :user)
-
#
-
# ====== Create a supplier_id and supplier_type columns
-
#
-
# add_belongs_to(:products, :supplier, polymorphic: true)
-
#
-
# ====== Create a supplier_id, supplier_type columns and appropriate index
-
#
-
# add_reference(:products, :supplier, polymorphic: true, index: true)
-
#
-
1
def add_reference(table_name, ref_name, options = {})
-
polymorphic = options.delete(:polymorphic)
-
index_options = options.delete(:index)
-
add_column(table_name, "#{ref_name}_id", :integer, options)
-
add_column(table_name, "#{ref_name}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) if polymorphic
-
add_index(table_name, polymorphic ? %w[id type].map{ |t| "#{ref_name}_#{t}" } : "#{ref_name}_id", index_options.is_a?(Hash) ? index_options : {}) if index_options
-
end
-
1
alias :add_belongs_to :add_reference
-
-
# Removes the reference(s). Also removes a +type+ column if one exists.
-
# <tt>remove_reference</tt>, <tt>remove_references</tt> and <tt>remove_belongs_to</tt> are acceptable.
-
#
-
# ====== Remove the reference
-
#
-
# remove_reference(:products, :user, index: true)
-
#
-
# ====== Remove polymorphic reference
-
#
-
# remove_reference(:products, :supplier, polymorphic: true)
-
#
-
1
def remove_reference(table_name, ref_name, options = {})
-
remove_column(table_name, "#{ref_name}_id")
-
remove_column(table_name, "#{ref_name}_type") if options[:polymorphic]
-
end
-
1
alias :remove_belongs_to :remove_reference
-
-
1
def dump_schema_information #:nodoc:
-
sm_table = ActiveRecord::Migrator.schema_migrations_table_name
-
-
ActiveRecord::SchemaMigration.order('version').map { |sm|
-
"INSERT INTO #{sm_table} (version) VALUES ('#{sm.version}');"
-
}.join "\n\n"
-
end
-
-
# Should not be called normally, but this operation is non-destructive.
-
# The migrations module handles this automatically.
-
1
def initialize_schema_migrations_table
-
ActiveRecord::SchemaMigration.create_table
-
end
-
-
1
def assume_migrated_upto_version(version, migrations_paths = ActiveRecord::Migrator.migrations_paths)
-
migrations_paths = Array(migrations_paths)
-
version = version.to_i
-
sm_table = quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name)
-
-
migrated = select_values("SELECT version FROM #{sm_table}").map { |v| v.to_i }
-
paths = migrations_paths.map {|p| "#{p}/[0-9]*_*.rb" }
-
versions = Dir[*paths].map do |filename|
-
filename.split('/').last.split('_').first.to_i
-
end
-
-
unless migrated.include?(version)
-
execute "INSERT INTO #{sm_table} (version) VALUES ('#{version}')"
-
end
-
-
inserted = Set.new
-
(versions - migrated).each do |v|
-
if inserted.include?(v)
-
raise "Duplicate migration #{v}. Please renumber your migrations to resolve the conflict."
-
elsif v < version
-
execute "INSERT INTO #{sm_table} (version) VALUES ('#{v}')"
-
inserted << v
-
end
-
end
-
end
-
-
1
def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
-
if native = native_database_types[type.to_sym]
-
column_type_sql = (native.is_a?(Hash) ? native[:name] : native).dup
-
-
if type == :decimal # ignore limit, use precision and scale
-
scale ||= native[:scale]
-
-
if precision ||= native[:precision]
-
if scale
-
column_type_sql << "(#{precision},#{scale})"
-
else
-
column_type_sql << "(#{precision})"
-
end
-
elsif scale
-
raise ArgumentError, "Error adding decimal column: precision cannot be empty if scale is specified"
-
end
-
-
elsif (type != :primary_key) && (limit ||= native.is_a?(Hash) && native[:limit])
-
column_type_sql << "(#{limit})"
-
end
-
-
column_type_sql
-
else
-
type.to_s
-
end
-
end
-
-
# Given a set of columns and an ORDER BY clause, returns the columns for a SELECT DISTINCT.
-
# Both PostgreSQL and Oracle overrides this for custom DISTINCT syntax - they
-
# require the order columns appear in the SELECT.
-
#
-
# columns_for_distinct("posts.id", ["posts.created_at desc"])
-
1
def columns_for_distinct(columns, orders) #:nodoc:
-
columns
-
end
-
-
# Adds timestamps (+created_at+ and +updated_at+) columns to the named table.
-
#
-
# add_timestamps(:suppliers)
-
#
-
1
def add_timestamps(table_name)
-
add_column table_name, :created_at, :datetime
-
add_column table_name, :updated_at, :datetime
-
end
-
-
# Removes the timestamp columns (+created_at+ and +updated_at+) from the table definition.
-
#
-
# remove_timestamps(:suppliers)
-
#
-
1
def remove_timestamps(table_name)
-
remove_column table_name, :updated_at
-
remove_column table_name, :created_at
-
end
-
-
1
def update_table_definition(table_name, base) #:nodoc:
-
Table.new(table_name, base)
-
end
-
-
1
protected
-
1
def add_index_sort_order(option_strings, column_names, options = {})
-
if options.is_a?(Hash) && order = options[:order]
-
case order
-
when Hash
-
column_names.each {|name| option_strings[name] += " #{order[name].upcase}" if order.has_key?(name)}
-
when String
-
column_names.each {|name| option_strings[name] += " #{order.upcase}"}
-
end
-
end
-
-
return option_strings
-
end
-
-
# Overridden by the mysql adapter for supporting index lengths
-
1
def quoted_columns_for_index(column_names, options = {})
-
option_strings = Hash[column_names.map {|name| [name, '']}]
-
-
# add index sort order if supported
-
if supports_index_sort_order?
-
option_strings = add_index_sort_order(option_strings, column_names, options)
-
end
-
-
column_names.map {|name| quote_column_name(name) + option_strings[name]}
-
end
-
-
1
def options_include_default?(options)
-
options.include?(:default) && !(options[:null] == false && options[:default].nil?)
-
end
-
-
1
def add_index_options(table_name, column_name, options = {})
-
column_names = Array(column_name)
-
index_name = index_name(table_name, column: column_names)
-
-
options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal, :using, :algorithm, :type)
-
-
index_type = options[:unique] ? "UNIQUE" : ""
-
index_type = options[:type].to_s if options.key?(:type)
-
index_name = options[:name].to_s if options.key?(:name)
-
max_index_length = options.fetch(:internal, false) ? index_name_length : allowed_index_name_length
-
-
if options.key?(:algorithm)
-
algorithm = index_algorithms.fetch(options[:algorithm]) {
-
raise ArgumentError.new("Algorithm must be one of the following: #{index_algorithms.keys.map(&:inspect).join(', ')}")
-
}
-
end
-
-
using = "USING #{options[:using]}" if options[:using].present?
-
-
if supports_partial_index?
-
index_options = options[:where] ? " WHERE #{options[:where]}" : ""
-
end
-
-
if index_name.length > max_index_length
-
raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{max_index_length} characters"
-
end
-
if index_name_exists?(table_name, index_name, false)
-
raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' already exists"
-
end
-
index_columns = quoted_columns_for_index(column_names, options).join(", ")
-
-
[index_name, index_type, index_columns, index_options, algorithm, using]
-
end
-
-
1
def index_name_for_remove(table_name, options = {})
-
index_name = index_name(table_name, options)
-
-
unless index_name_exists?(table_name, index_name, true)
-
if options.is_a?(Hash) && options.has_key?(:name)
-
options_without_column = options.dup
-
options_without_column.delete :column
-
index_name_without_column = index_name(table_name, options_without_column)
-
-
return index_name_without_column if index_name_exists?(table_name, index_name_without_column, false)
-
end
-
-
raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' does not exist"
-
end
-
-
index_name
-
end
-
-
1
def rename_table_indexes(table_name, new_name)
-
indexes(new_name).each do |index|
-
generated_index_name = index_name(table_name, column: index.columns)
-
if generated_index_name == index.name
-
rename_index new_name, generated_index_name, index_name(new_name, column: index.columns)
-
end
-
end
-
end
-
-
1
def rename_column_indexes(table_name, column_name, new_column_name)
-
column_name, new_column_name = column_name.to_s, new_column_name.to_s
-
indexes(table_name).each do |index|
-
next unless index.columns.include?(new_column_name)
-
old_columns = index.columns.dup
-
old_columns[old_columns.index(new_column_name)] = column_name
-
generated_index_name = index_name(table_name, column: old_columns)
-
if generated_index_name == index.name
-
rename_index table_name, generated_index_name, index_name(table_name, column: index.columns)
-
end
-
end
-
end
-
-
1
private
-
1
def create_table_definition(name, temporary, options, as = nil)
-
TableDefinition.new native_database_types, name, temporary, options, as
-
end
-
-
1
def create_alter_table(name)
-
AlterTable.new create_table_definition(name, false, {})
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
module ConnectionAdapters
-
1
class Transaction #:nodoc:
-
1
attr_reader :connection
-
-
1
def initialize(connection)
-
11
@connection = connection
-
11
@state = TransactionState.new
-
end
-
-
1
def state
-
6
@state
-
end
-
end
-
-
1
class TransactionState
-
1
attr_accessor :parent
-
-
1
VALID_STATES = Set.new([:committed, :rolledback, nil])
-
-
1
def initialize(state = nil)
-
11
@state = state
-
11
@parent = nil
-
end
-
-
1
def finalized?
-
4
@state
-
end
-
-
1
def committed?
-
@state == :committed
-
end
-
-
1
def rolledback?
-
@state == :rolledback
-
end
-
-
1
def set_state(state)
-
10
if !VALID_STATES.include?(state)
-
raise ArgumentError, "Invalid transaction state: #{state}"
-
end
-
10
@state = state
-
end
-
end
-
-
1
class ClosedTransaction < Transaction #:nodoc:
-
1
def number
-
12
0
-
end
-
-
1
def begin(options = {})
-
4
RealTransaction.new(connection, self, options)
-
end
-
-
1
def closed?
-
4
true
-
end
-
-
1
def open?
-
false
-
end
-
-
1
def joinable?
-
false
-
end
-
-
# This is a noop when there are no open transactions
-
1
def add_record(record)
-
end
-
end
-
-
1
class OpenTransaction < Transaction #:nodoc:
-
1
attr_reader :parent, :records
-
1
attr_writer :joinable
-
-
1
def initialize(connection, parent, options = {})
-
10
super connection
-
-
10
@parent = parent
-
10
@records = []
-
10
@finishing = false
-
10
@joinable = options.fetch(:joinable, true)
-
end
-
-
# This state is necessary so that we correctly handle stuff that might
-
# happen in a commit/rollback. But it's kinda distasteful. Maybe we can
-
# find a better way to structure it in the future.
-
1
def finishing?
-
28
@finishing
-
end
-
-
1
def joinable?
-
10
@joinable && !finishing?
-
end
-
-
1
def number
-
18
if finishing?
-
6
parent.number
-
else
-
12
parent.number + 1
-
end
-
end
-
-
1
def begin(options = {})
-
6
if finishing?
-
parent.begin
-
else
-
6
SavepointTransaction.new(connection, self, options)
-
end
-
end
-
-
1
def rollback
-
4
@finishing = true
-
4
perform_rollback
-
4
parent
-
end
-
-
1
def commit
-
6
@finishing = true
-
6
perform_commit
-
6
parent
-
end
-
-
1
def add_record(record)
-
14
if record.has_transactional_callbacks?
-
12
records << record
-
else
-
2
record.set_transaction_state(@state)
-
end
-
end
-
-
1
def rollback_records
-
4
@state.set_state(:rolledback)
-
4
records.uniq.each do |record|
-
4
begin
-
4
record.rolledback!(parent.closed?)
-
rescue => e
-
record.logger.error(e) if record.respond_to?(:logger) && record.logger
-
end
-
end
-
end
-
-
1
def commit_records
-
@state.set_state(:committed)
-
records.uniq.each do |record|
-
begin
-
record.committed!
-
rescue => e
-
record.logger.error(e) if record.respond_to?(:logger) && record.logger
-
end
-
end
-
end
-
-
1
def closed?
-
false
-
end
-
-
1
def open?
-
4
true
-
end
-
end
-
-
1
class RealTransaction < OpenTransaction #:nodoc:
-
1
def initialize(connection, parent, options = {})
-
4
super
-
-
4
if options[:isolation]
-
connection.begin_isolated_db_transaction(options[:isolation])
-
else
-
4
connection.begin_db_transaction
-
end
-
end
-
-
1
def perform_rollback
-
4
connection.rollback_db_transaction
-
4
rollback_records
-
end
-
-
1
def perform_commit
-
connection.commit_db_transaction
-
commit_records
-
end
-
end
-
-
1
class SavepointTransaction < OpenTransaction #:nodoc:
-
1
def initialize(connection, parent, options = {})
-
6
if options[:isolation]
-
raise ActiveRecord::TransactionIsolationError, "cannot set transaction isolation in a nested transaction"
-
end
-
-
6
super
-
6
connection.create_savepoint
-
end
-
-
1
def perform_rollback
-
connection.rollback_to_savepoint
-
rollback_records
-
end
-
-
1
def perform_commit
-
6
@state.set_state(:committed)
-
6
@state.parent = parent.state
-
6
connection.release_savepoint
-
12
records.each { |r| parent.add_record(r) }
-
end
-
end
-
end
-
end
-
1
require 'date'
-
1
require 'bigdecimal'
-
1
require 'bigdecimal/util'
-
1
require 'active_support/core_ext/benchmark'
-
1
require 'active_record/connection_adapters/schema_cache'
-
1
require 'active_record/connection_adapters/abstract/schema_dumper'
-
1
require 'active_record/connection_adapters/abstract/schema_creation'
-
1
require 'monitor'
-
-
1
module ActiveRecord
-
1
module ConnectionAdapters # :nodoc:
-
1
extend ActiveSupport::Autoload
-
-
1
autoload :Column
-
1
autoload :ConnectionSpecification
-
-
1
autoload_at 'active_record/connection_adapters/abstract/schema_definitions' do
-
1
autoload :IndexDefinition
-
1
autoload :ColumnDefinition
-
1
autoload :ChangeColumnDefinition
-
1
autoload :TableDefinition
-
1
autoload :Table
-
1
autoload :AlterTable
-
end
-
-
1
autoload_at 'active_record/connection_adapters/abstract/connection_pool' do
-
1
autoload :ConnectionHandler
-
1
autoload :ConnectionManagement
-
end
-
-
1
autoload_under 'abstract' do
-
1
autoload :SchemaStatements
-
1
autoload :DatabaseStatements
-
1
autoload :DatabaseLimits
-
1
autoload :Quoting
-
1
autoload :ConnectionPool
-
1
autoload :QueryCache
-
1
autoload :Savepoints
-
end
-
-
1
autoload_at 'active_record/connection_adapters/abstract/transaction' do
-
1
autoload :ClosedTransaction
-
1
autoload :RealTransaction
-
1
autoload :SavepointTransaction
-
1
autoload :TransactionState
-
end
-
-
# Active Record supports multiple database systems. AbstractAdapter and
-
# related classes form the abstraction layer which makes this possible.
-
# An AbstractAdapter represents a connection to a database, and provides an
-
# abstract interface for database-specific functionality such as establishing
-
# a connection, escaping values, building the right SQL fragments for ':offset'
-
# and ':limit' options, etc.
-
#
-
# All the concrete database adapters follow the interface laid down in this class.
-
# ActiveRecord::Base.connection returns an AbstractAdapter object, which
-
# you can use.
-
#
-
# Most of the methods in the adapter are useful during migrations. Most
-
# notably, the instance methods provided by SchemaStatement are very useful.
-
1
class AbstractAdapter
-
1
include Quoting, DatabaseStatements, SchemaStatements
-
1
include DatabaseLimits
-
1
include QueryCache
-
1
include ActiveSupport::Callbacks
-
1
include MonitorMixin
-
1
include ColumnDumper
-
-
1
SIMPLE_INT = /\A\d+\z/
-
-
1
define_callbacks :checkout, :checkin
-
-
1
attr_accessor :visitor, :pool
-
1
attr_reader :schema_cache, :last_use, :in_use, :logger
-
1
alias :in_use? :in_use
-
-
1
def self.type_cast_config_to_integer(config)
-
1
if config =~ SIMPLE_INT
-
config.to_i
-
else
-
1
config
-
end
-
end
-
-
1
def self.type_cast_config_to_boolean(config)
-
1
if config == "false"
-
false
-
else
-
1
config
-
end
-
end
-
-
1
def initialize(connection, logger = nil, pool = nil) #:nodoc:
-
1
super()
-
-
1
@connection = connection
-
1
@in_use = false
-
1
@instrumenter = ActiveSupport::Notifications.instrumenter
-
1
@last_use = false
-
1
@logger = logger
-
1
@pool = pool
-
1
@schema_cache = SchemaCache.new self
-
1
@visitor = nil
-
1
@prepared_statements = false
-
end
-
-
1
def valid_type?(type)
-
true
-
end
-
-
1
def schema_creation
-
SchemaCreation.new self
-
end
-
-
1
def lease
-
4
synchronize do
-
4
unless in_use
-
4
@in_use = true
-
4
@last_use = Time.now
-
end
-
end
-
end
-
-
1
def schema_cache=(cache)
-
cache.connection = self
-
@schema_cache = cache
-
end
-
-
1
def expire
-
4
@in_use = false
-
end
-
-
1
def unprepared_visitor
-
self.class::BindSubstitution.new self
-
end
-
-
1
def unprepared_statement
-
old_prepared_statements, @prepared_statements = @prepared_statements, false
-
old_visitor, @visitor = @visitor, unprepared_visitor
-
yield
-
ensure
-
@visitor, @prepared_statements = old_visitor, old_prepared_statements
-
end
-
-
# Returns the human-readable name of the adapter. Use mixed case - one
-
# can always use downcase if needed.
-
1
def adapter_name
-
'Abstract'
-
end
-
-
# Does this adapter support migrations? Backend specific, as the
-
# abstract adapter always returns +false+.
-
1
def supports_migrations?
-
false
-
end
-
-
# Can this adapter determine the primary key for tables not attached
-
# to an Active Record class, such as join tables? Backend specific, as
-
# the abstract adapter always returns +false+.
-
1
def supports_primary_key?
-
false
-
end
-
-
# Does this adapter support using DISTINCT within COUNT? This is +true+
-
# for all adapters except sqlite.
-
1
def supports_count_distinct?
-
true
-
end
-
-
# Does this adapter support DDL rollbacks in transactions? That is, would
-
# CREATE TABLE or ALTER TABLE get rolled back by a transaction? PostgreSQL,
-
# SQL Server, and others support this. MySQL and others do not.
-
1
def supports_ddl_transactions?
-
false
-
end
-
-
1
def supports_bulk_alter?
-
false
-
end
-
-
# Does this adapter support savepoints? PostgreSQL and MySQL do,
-
# SQLite < 3.6.8 does not.
-
1
def supports_savepoints?
-
false
-
end
-
-
# Should primary key values be selected from their corresponding
-
# sequence before the insert statement? If true, next_sequence_value
-
# is called before each insert to set the record's primary key.
-
# This is false for all adapters but Firebird.
-
1
def prefetch_primary_key?(table_name = nil)
-
6
false
-
end
-
-
# Does this adapter support index sort order?
-
1
def supports_index_sort_order?
-
false
-
end
-
-
# Does this adapter support partial indices?
-
1
def supports_partial_index?
-
false
-
end
-
-
# Does this adapter support explain? As of this writing sqlite3,
-
# mysql2, and postgresql are the only ones that do.
-
1
def supports_explain?
-
false
-
end
-
-
# Does this adapter support setting the isolation level for a transaction?
-
1
def supports_transaction_isolation?
-
false
-
end
-
-
# Does this adapter support database extensions? As of this writing only
-
# postgresql does.
-
1
def supports_extensions?
-
false
-
end
-
-
# This is meant to be implemented by the adapters that support extensions
-
1
def disable_extension(name)
-
end
-
-
# This is meant to be implemented by the adapters that support extensions
-
1
def enable_extension(name)
-
end
-
-
# A list of extensions, to be filled in by adapters that support them. At
-
# the moment only postgresql does.
-
1
def extensions
-
[]
-
end
-
-
# A list of index algorithms, to be filled by adapters that support them.
-
# MySQL and PostgreSQL have support for them right now.
-
1
def index_algorithms
-
{}
-
end
-
-
# QUOTING ==================================================
-
-
# Returns a bind substitution value given a bind +index+ and +column+
-
# NOTE: The column param is currently being used by the sqlserver-adapter
-
1
def substitute_at(column, index)
-
Arel::Nodes::BindParam.new '?'
-
end
-
-
# REFERENTIAL INTEGRITY ====================================
-
-
# Override to turn off referential integrity while executing <tt>&block</tt>.
-
1
def disable_referential_integrity
-
yield
-
end
-
-
# CONNECTION MANAGEMENT ====================================
-
-
# Checks whether the connection to the database is still active. This includes
-
# checking whether the database is actually capable of responding, i.e. whether
-
# the connection isn't stale.
-
1
def active?
-
end
-
-
# Adapter should redefine this if it needs a threadsafe way to approximate
-
# if the connection is active
-
1
def active_threadsafe?
-
active?
-
end
-
-
# Disconnects from the database if already connected, and establishes a
-
# new connection with the database. Implementors should call super if they
-
# override the default implementation.
-
1
def reconnect!
-
clear_cache!
-
reset_transaction
-
end
-
-
# Disconnects from the database if already connected. Otherwise, this
-
# method does nothing.
-
1
def disconnect!
-
clear_cache!
-
reset_transaction
-
end
-
-
# Reset the state of this connection, directing the DBMS to clear
-
# transactions and other connection-related server-side state. Usually a
-
# database-dependent operation.
-
#
-
# The default implementation does nothing; the implementation should be
-
# overridden by concrete adapters.
-
1
def reset!
-
# this should be overridden by concrete adapters
-
end
-
-
###
-
# Clear any caching the database adapter may be doing, for example
-
# clearing the prepared statement cache. This is database specific.
-
1
def clear_cache!
-
# this should be overridden by concrete adapters
-
end
-
-
# Returns true if its required to reload the connection between requests for development mode.
-
# This is not the case for Ruby/MySQL and it's not necessary for any adapters except SQLite.
-
1
def requires_reloading?
-
false
-
end
-
-
# Checks whether the connection to the database is still active (i.e. not stale).
-
# This is done under the hood by calling <tt>active?</tt>. If the connection
-
# is no longer active, then this method will reconnect to the database.
-
1
def verify!(*ignored)
-
4
reconnect! unless active?
-
end
-
-
# Provides access to the underlying database driver for this adapter. For
-
# example, this method returns a Mysql object in case of MysqlAdapter,
-
# and a PGconn object in case of PostgreSQLAdapter.
-
#
-
# This is useful for when you need to call a proprietary method such as
-
# PostgreSQL's lo_* methods.
-
1
def raw_connection
-
@connection
-
end
-
-
1
def open_transactions
-
12
@transaction.number
-
end
-
-
1
def create_savepoint(name = nil)
-
end
-
-
1
def rollback_to_savepoint(name = nil)
-
end
-
-
1
def release_savepoint(name = nil)
-
end
-
-
1
def case_sensitive_modifier(node)
-
node
-
end
-
-
1
def case_insensitive_comparison(table, attribute, column, value)
-
table[attribute].lower.eq(table.lower(value))
-
end
-
-
1
def current_savepoint_name
-
12
"active_record_#{open_transactions}"
-
end
-
-
# Check the connection back in to the connection pool
-
1
def close
-
pool.checkin self
-
end
-
-
1
protected
-
-
1
def translate_exception_class(e, sql)
-
message = "#{e.class.name}: #{e.message}: #{sql}"
-
@logger.error message if @logger
-
exception = translate_exception(e, message)
-
exception.set_backtrace e.backtrace
-
exception
-
end
-
-
1
def log(sql, name = "SQL", binds = [], statement_name = nil)
-
@instrumenter.instrument(
-
"sql.active_record",
-
:sql => sql,
-
:name => name,
-
:connection_id => object_id,
-
:statement_name => statement_name,
-
108
:binds => binds) { yield }
-
rescue => e
-
raise translate_exception_class(e, sql)
-
end
-
-
1
def translate_exception(exception, message)
-
# override in derived class
-
ActiveRecord::StatementInvalid.new(message, exception)
-
end
-
-
1
def without_prepared_statement?(binds)
-
25
!@prepared_statements || binds.empty?
-
end
-
end
-
end
-
end
-
1
require 'set'
-
-
1
module ActiveRecord
-
# :stopdoc:
-
1
module ConnectionAdapters
-
# An abstract definition of a column in a table.
-
1
class Column
-
1
TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE', 'on', 'ON'].to_set
-
1
FALSE_VALUES = [false, 0, '0', 'f', 'F', 'false', 'FALSE', 'off', 'OFF'].to_set
-
-
1
module Format
-
1
ISO_DATE = /\A(\d{4})-(\d\d)-(\d\d)\z/
-
1
ISO_DATETIME = /\A(\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)(\.\d+)?\z/
-
end
-
-
1
attr_reader :name, :default, :type, :limit, :null, :sql_type, :precision, :scale, :default_function
-
1
attr_accessor :primary, :coder
-
-
1
alias :encoded? :coder
-
-
# Instantiates a new column in the table.
-
#
-
# +name+ is the column's name, such as <tt>supplier_id</tt> in <tt>supplier_id int(11)</tt>.
-
# +default+ is the type-casted default value, such as +new+ in <tt>sales_stage varchar(20) default 'new'</tt>.
-
# +sql_type+ is used to extract the column's length, if necessary. For example +60+ in
-
# <tt>company_name varchar(60)</tt>.
-
# It will be mapped to one of the standard Rails SQL types in the <tt>type</tt> attribute.
-
# +null+ determines if this column allows +NULL+ values.
-
1
def initialize(name, default, sql_type = nil, null = true)
-
30
@name = name
-
30
@sql_type = sql_type
-
30
@null = null
-
30
@limit = extract_limit(sql_type)
-
30
@precision = extract_precision(sql_type)
-
30
@scale = extract_scale(sql_type)
-
30
@type = simplified_type(sql_type)
-
30
@default = extract_default(default)
-
30
@default_function = nil
-
30
@primary = nil
-
30
@coder = nil
-
end
-
-
# Returns +true+ if the column is either of type string or text.
-
1
def text?
-
type == :string || type == :text
-
end
-
-
# Returns +true+ if the column is either of type integer, float or decimal.
-
1
def number?
-
124
type == :integer || type == :float || type == :decimal
-
end
-
-
1
def has_default?
-
!default.nil?
-
end
-
-
# Returns the Ruby class that corresponds to the abstract data type.
-
1
def klass
-
case type
-
when :integer then Fixnum
-
when :float then Float
-
when :decimal then BigDecimal
-
when :datetime, :timestamp, :time then Time
-
when :date then Date
-
when :text, :string, :binary then String
-
when :boolean then Object
-
end
-
end
-
-
1
def binary?
-
64
type == :binary
-
end
-
-
# Casts a Ruby value to something appropriate for writing to the database.
-
1
def type_cast_for_write(value)
-
36
return value unless number?
-
-
16
case value
-
when FalseClass
-
0
-
when TrueClass
-
1
-
when String
-
6
value.presence
-
else
-
10
value
-
end
-
end
-
-
# Casts value (which is a String) to an appropriate instance.
-
1
def type_cast(value)
-
return nil if value.nil?
-
return coder.load(value) if encoded?
-
-
klass = self.class
-
-
case type
-
when :string, :text then value
-
when :integer then klass.value_to_integer(value)
-
when :float then value.to_f
-
when :decimal then klass.value_to_decimal(value)
-
when :datetime, :timestamp then klass.string_to_time(value)
-
when :time then klass.string_to_dummy_time(value)
-
when :date then klass.value_to_date(value)
-
when :binary then klass.binary_to_string(value)
-
when :boolean then klass.value_to_boolean(value)
-
else value
-
end
-
end
-
-
# Returns the human name of the column name.
-
#
-
# ===== Examples
-
# Column.new('sales_stage', ...).human_name # => 'Sales stage'
-
1
def human_name
-
Base.human_attribute_name(@name)
-
end
-
-
1
def extract_default(default)
-
30
type_cast(default)
-
end
-
-
1
class << self
-
# Used to convert from BLOBs to Strings
-
1
def binary_to_string(value)
-
value
-
end
-
-
1
def value_to_date(value)
-
8
if value.is_a?(String)
-
8
return nil if value.empty?
-
8
fast_string_to_date(value) || fallback_string_to_date(value)
-
elsif value.respond_to?(:to_date)
-
value.to_date
-
else
-
value
-
end
-
end
-
-
1
def string_to_time(string)
-
return string unless string.is_a?(String)
-
return nil if string.empty?
-
-
fast_string_to_time(string) || fallback_string_to_time(string)
-
end
-
-
1
def string_to_dummy_time(string)
-
return string unless string.is_a?(String)
-
return nil if string.empty?
-
-
dummy_time_string = "2000-01-01 #{string}"
-
-
fast_string_to_time(dummy_time_string) || begin
-
time_hash = Date._parse(dummy_time_string)
-
return nil if time_hash[:hour].nil?
-
new_time(*time_hash.values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction))
-
end
-
end
-
-
# convert something to a boolean
-
1
def value_to_boolean(value)
-
1
if value.is_a?(String) && value.empty?
-
nil
-
else
-
1
TRUE_VALUES.include?(value)
-
end
-
end
-
-
# Used to convert values to integer.
-
# handle the case when an integer column is used to store boolean values
-
1
def value_to_integer(value)
-
28
case value
-
when TrueClass, FalseClass
-
value ? 1 : 0
-
else
-
28
value.to_i rescue nil
-
end
-
end
-
-
# convert something to a BigDecimal
-
1
def value_to_decimal(value)
-
# Using .class is faster than .is_a? and
-
# subclasses of BigDecimal will be handled
-
# in the else clause
-
if value.class == BigDecimal
-
value
-
elsif value.respond_to?(:to_d)
-
value.to_d
-
else
-
value.to_s.to_d
-
end
-
end
-
-
1
protected
-
# '0.123456' -> 123456
-
# '1.123456' -> 123456
-
1
def microseconds(time)
-
time[:sec_fraction] ? (time[:sec_fraction] * 1_000_000).to_i : 0
-
end
-
-
1
def new_date(year, mon, mday)
-
8
if year && year != 0
-
8
Date.new(year, mon, mday) rescue nil
-
end
-
end
-
-
1
def new_time(year, mon, mday, hour, min, sec, microsec, offset = nil)
-
# Treat 0000-00-00 00:00:00 as nil.
-
return nil if year.nil? || (year == 0 && mon == 0 && mday == 0)
-
-
if offset
-
time = Time.utc(year, mon, mday, hour, min, sec, microsec) rescue nil
-
return nil unless time
-
-
time -= offset
-
Base.default_timezone == :utc ? time : time.getlocal
-
else
-
Time.public_send(Base.default_timezone, year, mon, mday, hour, min, sec, microsec) rescue nil
-
end
-
end
-
-
1
def fast_string_to_date(string)
-
8
if string =~ Format::ISO_DATE
-
8
new_date $1.to_i, $2.to_i, $3.to_i
-
end
-
end
-
-
# Doesn't handle time zones.
-
1
def fast_string_to_time(string)
-
if string =~ Format::ISO_DATETIME
-
microsec = ($7.to_r * 1_000_000).to_i
-
new_time $1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, microsec
-
end
-
end
-
-
1
def fallback_string_to_date(string)
-
new_date(*::Date._parse(string, false).values_at(:year, :mon, :mday))
-
end
-
-
1
def fallback_string_to_time(string)
-
time_hash = Date._parse(string)
-
time_hash[:sec_fraction] = microseconds(time_hash)
-
-
new_time(*time_hash.values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction, :offset))
-
end
-
end
-
-
1
private
-
1
def extract_limit(sql_type)
-
22
$1.to_i if sql_type =~ /\((.*)\)/
-
end
-
-
1
def extract_precision(sql_type)
-
22
$2.to_i if sql_type =~ /^(numeric|decimal|number)\((\d+)(,\d+)?\)/i
-
end
-
-
1
def extract_scale(sql_type)
-
30
case sql_type
-
when /^(numeric|decimal|number)\((\d+)\)/i then 0
-
when /^(numeric|decimal|number)\((\d+)(,(\d+))\)/i then $4.to_i
-
end
-
end
-
-
1
def simplified_type(field_type)
-
12
case field_type
-
when /int/i
-
8
:integer
-
when /float|double/i
-
:float
-
when /decimal|numeric|number/i
-
extract_scale(field_type) == 0 ? :integer : :decimal
-
when /datetime/i
-
:datetime
-
when /timestamp/i
-
:timestamp
-
when /time/i
-
:time
-
when /date/i
-
3
:date
-
when /clob/i, /text/i
-
:text
-
when /blob/i, /binary/i
-
:binary
-
when /char/i
-
:string
-
when /boolean/i
-
1
:boolean
-
end
-
end
-
end
-
end
-
# :startdoc:
-
end
-
1
require 'uri'
-
-
1
module ActiveRecord
-
1
module ConnectionAdapters
-
1
class ConnectionSpecification #:nodoc:
-
1
attr_reader :config, :adapter_method
-
-
1
def initialize(config, adapter_method)
-
1
@config, @adapter_method = config, adapter_method
-
end
-
-
1
def initialize_dup(original)
-
@config = original.config.dup
-
end
-
-
# Expands a connection string into a hash.
-
1
class ConnectionUrlResolver # :nodoc:
-
-
# == Example
-
#
-
# url = "postgresql://foo:bar@localhost:9000/foo_test?pool=5&timeout=3000"
-
# ConnectionUrlResolver.new(url).to_hash
-
# # => {
-
# "adapter" => "postgresql",
-
# "host" => "localhost",
-
# "port" => 9000,
-
# "database" => "foo_test",
-
# "username" => "foo",
-
# "password" => "bar",
-
# "pool" => "5",
-
# "timeout" => "3000"
-
# }
-
1
def initialize(url)
-
raise "Database URL cannot be empty" if url.blank?
-
@uri = URI.parse(url)
-
@adapter = @uri.scheme.gsub('-', '_')
-
@adapter = "postgresql" if @adapter == "postgres"
-
-
if @uri.opaque
-
@uri.opaque, @query = @uri.opaque.split('?', 2)
-
else
-
@query = @uri.query
-
end
-
@authority = url =~ %r{\A[^:]*://}
-
end
-
-
# Converts the given URL to a full connection hash.
-
1
def to_hash
-
config = raw_config.reject { |_,value| value.blank? }
-
config.map { |key,value| config[key] = uri_parser.unescape(value) if value.is_a? String }
-
config
-
end
-
-
1
private
-
-
1
def uri
-
@uri
-
end
-
-
1
def uri_parser
-
@uri_parser ||= URI::Parser.new
-
end
-
-
# Converts the query parameters of the URI into a hash.
-
#
-
# "localhost?pool=5&reap_frequency=2"
-
# # => { "pool" => "5", "reap_frequency" => "2" }
-
#
-
# returns empty hash if no query present.
-
#
-
# "localhost"
-
# # => {}
-
1
def query_hash
-
Hash[(@query || '').split("&").map { |pair| pair.split("=") }]
-
end
-
-
1
def raw_config
-
if uri.opaque
-
query_hash.merge({
-
"adapter" => @adapter,
-
"database" => uri.opaque })
-
else
-
query_hash.merge({
-
"adapter" => @adapter,
-
"username" => uri.user,
-
"password" => uri.password,
-
"port" => uri.port,
-
"database" => database_from_path,
-
"host" => uri.hostname })
-
end
-
end
-
-
# Returns name of the database.
-
# Sqlite3's handling of a leading slash is in transition as of
-
# Rails 4.1.
-
1
def database_from_path
-
if @authority && @adapter == 'sqlite3'
-
# 'sqlite3:///foo' is relative, for backwards compatibility.
-
-
database_name = uri.path.sub(%r{^/}, "")
-
-
msg = "Paths in SQLite3 database URLs of the form `sqlite3:///path` will be treated as absolute in Rails 4.2. " \
-
"Please switch to `sqlite3:#{database_name}`."
-
ActiveSupport::Deprecation.warn(msg)
-
-
database_name
-
-
elsif @adapter == 'sqlite3'
-
# 'sqlite3:/foo' is absolute, because that makes sense. The
-
# corresponding relative version, 'sqlite3:foo', is handled
-
# elsewhere, as an "opaque".
-
-
uri.path
-
else
-
# Only SQLite uses a filename as the "database" name; for
-
# anything else, a leading slash would be silly.
-
-
uri.path.sub(%r{^/}, "")
-
end
-
end
-
end
-
-
##
-
# Builds a ConnectionSpecification from user input.
-
1
class Resolver # :nodoc:
-
1
attr_reader :configurations
-
-
# Accepts a hash two layers deep, keys on the first layer represent
-
# environments such as "production". Keys must be strings.
-
1
def initialize(configurations)
-
3
@configurations = configurations
-
end
-
-
# Returns a hash with database connection information.
-
#
-
# == Examples
-
#
-
# Full hash Configuration.
-
#
-
# configurations = { "production" => { "host" => "localhost", "database" => "foo", "adapter" => "sqlite3" } }
-
# Resolver.new(configurations).resolve(:production)
-
# # => { "host" => "localhost", "database" => "foo", "adapter" => "sqlite3"}
-
#
-
# Initialized with URL configuration strings.
-
#
-
# configurations = { "production" => "postgresql://localhost/foo" }
-
# Resolver.new(configurations).resolve(:production)
-
# # => { "host" => "localhost", "database" => "foo", "adapter" => "postgresql" }
-
#
-
1
def resolve(config)
-
4
if config
-
4
resolve_connection config
-
elsif env = ActiveRecord::ConnectionHandling::RAILS_ENV.call
-
resolve_symbol_connection env.to_sym
-
else
-
raise AdapterNotSpecified
-
end
-
end
-
-
# Expands each key in @configurations hash into fully resolved hash
-
1
def resolve_all
-
2
config = configurations.dup
-
2
config.each do |key, value|
-
3
config[key] = resolve(value) if value
-
end
-
2
config
-
end
-
-
# Returns an instance of ConnectionSpecification for a given adapter.
-
# Accepts a hash one layer deep that contains all connection information.
-
#
-
# == Example
-
#
-
# config = { "production" => { "host" => "localhost", "database" => "foo", "adapter" => "sqlite3" } }
-
# spec = Resolver.new(config).spec(:production)
-
# spec.adapter_method
-
# # => "sqlite3"
-
# spec.config
-
# # => { "host" => "localhost", "database" => "foo", "adapter" => "sqlite3" }
-
#
-
1
def spec(config)
-
1
spec = resolve(config).symbolize_keys
-
-
1
raise(AdapterNotSpecified, "database configuration does not specify adapter") unless spec.key?(:adapter)
-
-
1
path_to_adapter = "active_record/connection_adapters/#{spec[:adapter]}_adapter"
-
1
begin
-
1
require path_to_adapter
-
rescue Gem::LoadError => e
-
raise Gem::LoadError, "Specified '#{spec[:adapter]}' for database adapter, but the gem is not loaded. Add `gem '#{e.name}'` to your Gemfile (and ensure its version is at the minimum required by ActiveRecord)."
-
rescue LoadError => e
-
raise LoadError, "Could not load '#{path_to_adapter}'. Make sure that the adapter in config/database.yml is valid. If you use an adapter other than 'mysql', 'mysql2', 'postgresql' or 'sqlite3' add the necessary adapter gem to the Gemfile.", e.backtrace
-
end
-
-
1
adapter_method = "#{spec[:adapter]}_connection"
-
1
ConnectionSpecification.new(spec, adapter_method)
-
end
-
-
1
private
-
-
# Returns fully resolved connection, accepts hash, string or symbol.
-
# Always returns a hash.
-
#
-
# == Examples
-
#
-
# Symbol representing current environment.
-
#
-
# Resolver.new("production" => {}).resolve_connection(:production)
-
# # => {}
-
#
-
# One layer deep hash of connection values.
-
#
-
# Resolver.new({}).resolve_connection("adapter" => "sqlite3")
-
# # => { "adapter" => "sqlite3" }
-
#
-
# Connection URL.
-
#
-
# Resolver.new({}).resolve_connection("postgresql://localhost/foo")
-
# # => { "host" => "localhost", "database" => "foo", "adapter" => "postgresql" }
-
#
-
1
def resolve_connection(spec)
-
5
case spec
-
when Symbol
-
1
resolve_symbol_connection spec
-
when String
-
resolve_string_connection spec
-
when Hash
-
4
resolve_hash_connection spec
-
end
-
end
-
-
1
def resolve_string_connection(spec)
-
# Rails has historically accepted a string to mean either
-
# an environment key or a URL spec, so we have deprecated
-
# this ambiguous behaviour and in the future this function
-
# can be removed in favor of resolve_url_connection.
-
if configurations.key?(spec) || spec !~ /:/
-
ActiveSupport::Deprecation.warn "Passing a string to ActiveRecord::Base.establish_connection " \
-
"for a configuration lookup is deprecated, please pass a symbol (#{spec.to_sym.inspect}) instead"
-
resolve_symbol_connection(spec)
-
else
-
resolve_url_connection(spec)
-
end
-
end
-
-
# Takes the environment such as `:production` or `:development`.
-
# This requires that the @configurations was initialized with a key that
-
# matches.
-
#
-
# Resolver.new("production" => {}).resolve_symbol_connection(:production)
-
# # => {}
-
#
-
1
def resolve_symbol_connection(spec)
-
1
if config = configurations[spec.to_s]
-
1
resolve_connection(config)
-
else
-
raise(AdapterNotSpecified, "'#{spec}' database is not configured. Available: #{configurations.keys.inspect}")
-
end
-
end
-
-
# Accepts a hash. Expands the "url" key that contains a
-
# URL database connection to a full connection
-
# hash and merges with the rest of the hash.
-
# Connection details inside of the "url" key win any merge conflicts
-
1
def resolve_hash_connection(spec)
-
4
if spec["url"] && spec["url"] !~ /^jdbc:/
-
connection_hash = resolve_string_connection(spec.delete("url"))
-
spec.merge!(connection_hash)
-
end
-
4
spec
-
end
-
-
# Takes a connection URL.
-
#
-
# Resolver.new({}).resolve_url_connection("postgresql://localhost/foo")
-
# # => { "host" => "localhost", "database" => "foo", "adapter" => "postgresql" }
-
#
-
1
def resolve_url_connection(url)
-
ConnectionUrlResolver.new(url).to_hash
-
end
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
module ConnectionAdapters
-
1
class PostgreSQLColumn < Column
-
1
module ArrayParser
-
-
1
DOUBLE_QUOTE = '"'
-
1
BACKSLASH = "\\"
-
1
COMMA = ','
-
1
BRACKET_OPEN = '{'
-
1
BRACKET_CLOSE = '}'
-
-
1
private
-
# Loads pg_array_parser if available. String parsing can be
-
# performed quicker by a native extension, which will not create
-
# a large amount of Ruby objects that will need to be garbage
-
# collected. pg_array_parser has a C and Java extension
-
1
begin
-
1
require 'pg_array_parser'
-
include PgArrayParser
-
rescue LoadError
-
1
def parse_pg_array(string)
-
parse_data(string)
-
end
-
end
-
-
1
def parse_data(string)
-
local_index = 0
-
array = []
-
while(local_index < string.length)
-
case string[local_index]
-
when BRACKET_OPEN
-
local_index,array = parse_array_contents(array, string, local_index + 1)
-
when BRACKET_CLOSE
-
return array
-
end
-
local_index += 1
-
end
-
-
array
-
end
-
-
1
def parse_array_contents(array, string, index)
-
is_escaping = false
-
is_quoted = false
-
was_quoted = false
-
current_item = ''
-
-
local_index = index
-
while local_index
-
token = string[local_index]
-
if is_escaping
-
current_item << token
-
is_escaping = false
-
else
-
if is_quoted
-
case token
-
when DOUBLE_QUOTE
-
is_quoted = false
-
was_quoted = true
-
when BACKSLASH
-
is_escaping = true
-
else
-
current_item << token
-
end
-
else
-
case token
-
when BACKSLASH
-
is_escaping = true
-
when COMMA
-
add_item_to_array(array, current_item, was_quoted)
-
current_item = ''
-
was_quoted = false
-
when DOUBLE_QUOTE
-
is_quoted = true
-
when BRACKET_OPEN
-
internal_items = []
-
local_index,internal_items = parse_array_contents(internal_items, string, local_index + 1)
-
array.push(internal_items)
-
when BRACKET_CLOSE
-
add_item_to_array(array, current_item, was_quoted)
-
return local_index,array
-
else
-
current_item << token
-
end
-
end
-
end
-
-
local_index += 1
-
end
-
return local_index,array
-
end
-
-
1
def add_item_to_array(array, current_item, quoted)
-
return if !quoted && current_item.length == 0
-
-
if !quoted && current_item == 'NULL'
-
array.push nil
-
else
-
array.push current_item
-
end
-
end
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
module ConnectionAdapters
-
1
class PostgreSQLColumn < Column
-
1
module Cast
-
1
def point_to_string(point)
-
"(#{point[0]},#{point[1]})"
-
end
-
-
1
def string_to_point(string)
-
if string[0] == '(' && string[-1] == ')'
-
string = string[1...-1]
-
end
-
string.split(',').map{ |v| Float(v) }
-
end
-
-
1
def string_to_time(string)
-
24
return string unless String === string
-
-
case string
-
when 'infinity'; Float::INFINITY
-
when '-infinity'; -Float::INFINITY
-
when / BC$/
-
super("-" + string.sub(/ BC$/, ""))
-
else
-
super
-
end
-
end
-
-
1
def string_to_bit(value)
-
case value
-
when /^0x/i
-
value[2..-1].hex.to_s(2) # Hexadecimal notation
-
else
-
value # Bit-string notation
-
end
-
end
-
-
1
def hstore_to_string(object, array_member = false)
-
if Hash === object
-
string = object.map { |k, v| "#{escape_hstore(k)}=>#{escape_hstore(v)}" }.join(',')
-
string = escape_hstore(string) if array_member
-
string
-
else
-
object
-
end
-
end
-
-
1
def string_to_hstore(string)
-
if string.nil?
-
nil
-
elsif String === string
-
Hash[string.scan(HstorePair).map { |k, v|
-
v = v.upcase == 'NULL' ? nil : v.gsub(/\A"(.*)"\Z/m,'\1').gsub(/\\(.)/, '\1')
-
k = k.gsub(/\A"(.*)"\Z/m,'\1').gsub(/\\(.)/, '\1')
-
[k, v]
-
}]
-
else
-
string
-
end
-
end
-
-
1
def json_to_string(object)
-
if Hash === object || Array === object
-
ActiveSupport::JSON.encode(object)
-
else
-
object
-
end
-
end
-
-
1
def array_to_string(value, column, adapter)
-
casted_values = value.map do |val|
-
if String === val
-
if val == "NULL"
-
"\"#{val}\""
-
else
-
quote_and_escape(adapter.type_cast(val, column, true))
-
end
-
else
-
adapter.type_cast(val, column, true)
-
end
-
end
-
"{#{casted_values.join(',')}}"
-
end
-
-
1
def range_to_string(object)
-
from = object.begin.respond_to?(:infinite?) && object.begin.infinite? ? '' : object.begin
-
to = object.end.respond_to?(:infinite?) && object.end.infinite? ? '' : object.end
-
"[#{from},#{to}#{object.exclude_end? ? ')' : ']'}"
-
end
-
-
1
def string_to_json(string)
-
if String === string
-
ActiveSupport::JSON.decode(string)
-
else
-
string
-
end
-
end
-
-
1
def string_to_cidr(string)
-
if string.nil?
-
nil
-
elsif String === string
-
begin
-
IPAddr.new(string)
-
rescue ArgumentError
-
nil
-
end
-
else
-
string
-
end
-
end
-
-
1
def cidr_to_string(object)
-
if IPAddr === object
-
"#{object.to_s}/#{object.instance_variable_get(:@mask_addr).to_s(2).count('1')}"
-
else
-
object
-
end
-
end
-
-
1
def string_to_array(string, oid)
-
parse_pg_array(string).map {|val| type_cast_array(oid, val)}
-
end
-
-
1
private
-
-
1
HstorePair = begin
-
1
quoted_string = /"[^"\\]*(?:\\.[^"\\]*)*"/
-
1
unquoted_string = /(?:\\.|[^\s,])[^\s=,\\]*(?:\\.[^\s=,\\]*|=[^,>])*/
-
1
/(#{quoted_string}|#{unquoted_string})\s*=>\s*(#{quoted_string}|#{unquoted_string})/
-
end
-
-
1
def escape_hstore(value)
-
if value.nil?
-
'NULL'
-
else
-
if value == ""
-
'""'
-
else
-
'"%s"' % value.to_s.gsub(/(["\\])/, '\\\\\1')
-
end
-
end
-
end
-
-
1
ARRAY_ESCAPE = "\\" * 2 * 2 # escape the backslash twice for PG arrays
-
-
1
def quote_and_escape(value)
-
case value
-
when "NULL", Numeric
-
value
-
else
-
value = value.gsub(/\\/, ARRAY_ESCAPE)
-
value.gsub!(/"/,"\\\"")
-
"\"#{value}\""
-
end
-
end
-
-
1
def type_cast_array(oid, value)
-
if ::Array === value
-
value.map {|item| type_cast_array(oid, item)}
-
else
-
oid.type_cast value
-
end
-
end
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
module ConnectionAdapters
-
1
class PostgreSQLAdapter < AbstractAdapter
-
1
module DatabaseStatements
-
1
def explain(arel, binds = [])
-
sql = "EXPLAIN #{to_sql(arel, binds)}"
-
ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', binds))
-
end
-
-
1
class ExplainPrettyPrinter # :nodoc:
-
# Pretty prints the result of a EXPLAIN in a way that resembles the output of the
-
# PostgreSQL shell:
-
#
-
# QUERY PLAN
-
# ------------------------------------------------------------------------------
-
# Nested Loop Left Join (cost=0.00..37.24 rows=8 width=0)
-
# Join Filter: (posts.user_id = users.id)
-
# -> Index Scan using users_pkey on users (cost=0.00..8.27 rows=1 width=4)
-
# Index Cond: (id = 1)
-
# -> Seq Scan on posts (cost=0.00..28.88 rows=8 width=4)
-
# Filter: (posts.user_id = 1)
-
# (6 rows)
-
#
-
1
def pp(result)
-
header = result.columns.first
-
lines = result.rows.map(&:first)
-
-
# We add 2 because there's one char of padding at both sides, note
-
# the extra hyphens in the example above.
-
width = [header, *lines].map(&:length).max + 2
-
-
pp = []
-
-
pp << header.center(width).rstrip
-
pp << '-' * width
-
-
pp += lines.map {|line| " #{line}"}
-
-
nrows = result.rows.length
-
rows_label = nrows == 1 ? 'row' : 'rows'
-
pp << "(#{nrows} #{rows_label})"
-
-
pp.join("\n") + "\n"
-
end
-
end
-
-
# Executes a SELECT query and returns an array of rows. Each row is an
-
# array of field values.
-
1
def select_rows(sql, name = nil, binds = [])
-
exec_query(sql, name, binds).rows
-
end
-
-
# Executes an INSERT query and returns the new record's ID
-
1
def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
-
unless pk
-
# Extract the table from the insert sql. Yuck.
-
table_ref = extract_table_ref_from_insert_sql(sql)
-
pk = primary_key(table_ref) if table_ref
-
end
-
-
if pk && use_insert_returning?
-
select_value("#{sql} RETURNING #{quote_column_name(pk)}")
-
elsif pk
-
super
-
last_insert_id_value(sequence_name || default_sequence_name(table_ref, pk))
-
else
-
super
-
end
-
end
-
-
1
def create
-
super.insert
-
end
-
-
# create a 2D array representing the result set
-
1
def result_as_array(res) #:nodoc:
-
# check if we have any binary column and if they need escaping
-
2
ftypes = Array.new(res.nfields) do |i|
-
2
[i, res.ftype(i)]
-
end
-
-
2
rows = res.values
-
2
return rows unless ftypes.any? { |_, x|
-
2
x == BYTEA_COLUMN_TYPE_OID || x == MONEY_COLUMN_TYPE_OID
-
}
-
-
typehash = ftypes.group_by { |_, type| type }
-
binaries = typehash[BYTEA_COLUMN_TYPE_OID] || []
-
monies = typehash[MONEY_COLUMN_TYPE_OID] || []
-
-
rows.each do |row|
-
# unescape string passed BYTEA field (OID == 17)
-
binaries.each do |index, _|
-
row[index] = unescape_bytea(row[index])
-
end
-
-
# If this is a money type column and there are any currency symbols,
-
# then strip them off. Indeed it would be prettier to do this in
-
# PostgreSQLColumn.string_to_decimal but would break form input
-
# fields that call value_before_type_cast.
-
monies.each do |index, _|
-
data = row[index]
-
# Because money output is formatted according to the locale, there are two
-
# cases to consider (note the decimal separators):
-
# (1) $12,345,678.12
-
# (2) $12.345.678,12
-
case data
-
when /^-?\D+[\d,]+\.\d{2}$/ # (1)
-
data.gsub!(/[^-\d.]/, '')
-
when /^-?\D+[\d.]+,\d{2}$/ # (2)
-
data.gsub!(/[^-\d,]/, '').sub!(/,/, '.')
-
end
-
end
-
end
-
end
-
-
# Queries the database and returns the results in an Array-like object
-
1
def query(sql, name = nil) #:nodoc:
-
2
log(sql, name) do
-
2
result_as_array @connection.async_exec(sql)
-
end
-
end
-
-
# Executes an SQL statement, returning a PGresult object on success
-
# or raising a PGError exception otherwise.
-
1
def execute(sql, name = nil)
-
27
log(sql, name) do
-
27
@connection.async_exec(sql)
-
end
-
end
-
-
1
def substitute_at(column, index)
-
54
Arel::Nodes::BindParam.new "$#{index + 1}"
-
end
-
-
1
def exec_query(sql, name = 'SQL', binds = [])
-
25
result = without_prepared_statement?(binds) ? exec_no_cache(sql, name, binds) :
-
exec_cache(sql, name, binds)
-
-
25
types = {}
-
25
fields = result.fields
-
25
fields.each_with_index do |fname, i|
-
82
ftype = result.ftype i
-
82
fmod = result.fmod i
-
82
types[fname] = get_oid_type(ftype, fmod, fname)
-
end
-
-
25
ret = ActiveRecord::Result.new(fields, result.values, types)
-
25
result.clear
-
25
return ret
-
end
-
-
1
def exec_delete(sql, name = 'SQL', binds = [])
-
result = without_prepared_statement?(binds) ? exec_no_cache(sql, name, binds) :
-
exec_cache(sql, name, binds)
-
affected = result.cmd_tuples
-
result.clear
-
affected
-
end
-
1
alias :exec_update :exec_delete
-
-
1
def sql_for_insert(sql, pk, id_value, sequence_name, binds)
-
6
unless pk
-
# Extract the table from the insert sql. Yuck.
-
table_ref = extract_table_ref_from_insert_sql(sql)
-
pk = primary_key(table_ref) if table_ref
-
end
-
-
6
if pk && use_insert_returning?
-
6
sql = "#{sql} RETURNING #{quote_column_name(pk)}"
-
end
-
-
6
[sql, binds]
-
end
-
-
1
def exec_insert(sql, name, binds, pk = nil, sequence_name = nil)
-
6
val = exec_query(sql, name, binds)
-
6
if !use_insert_returning? && pk
-
unless sequence_name
-
table_ref = extract_table_ref_from_insert_sql(sql)
-
sequence_name = default_sequence_name(table_ref, pk)
-
return val unless sequence_name
-
end
-
last_insert_id_result(sequence_name)
-
else
-
6
val
-
end
-
end
-
-
# Executes an UPDATE query and returns the number of affected tuples.
-
1
def update_sql(sql, name = nil)
-
super.cmd_tuples
-
end
-
-
# Begins a transaction.
-
1
def begin_db_transaction
-
4
execute "BEGIN"
-
end
-
-
1
def begin_isolated_db_transaction(isolation)
-
begin_db_transaction
-
execute "SET TRANSACTION ISOLATION LEVEL #{transaction_isolation_levels.fetch(isolation)}"
-
end
-
-
# Commits a transaction.
-
1
def commit_db_transaction
-
execute "COMMIT"
-
end
-
-
# Aborts a transaction.
-
1
def rollback_db_transaction
-
4
execute "ROLLBACK"
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_record/connection_adapters/abstract_adapter'
-
-
1
module ActiveRecord
-
1
module ConnectionAdapters
-
1
class PostgreSQLAdapter < AbstractAdapter
-
1
module OID
-
1
class Type
-
1
def type; end
-
end
-
-
1
class Identity < Type
-
1
def type_cast(value)
-
22
value
-
end
-
end
-
-
1
class Bit < Type
-
1
def type_cast(value)
-
if String === value
-
ConnectionAdapters::PostgreSQLColumn.string_to_bit value
-
else
-
value
-
end
-
end
-
end
-
-
1
class Bytea < Type
-
1
def type_cast(value)
-
return if value.nil?
-
PGconn.unescape_bytea value
-
end
-
end
-
-
1
class Money < Type
-
1
def type_cast(value)
-
return if value.nil?
-
return value unless String === value
-
-
# Because money output is formatted according to the locale, there are two
-
# cases to consider (note the decimal separators):
-
# (1) $12,345,678.12
-
# (2) $12.345.678,12
-
# Negative values are represented as follows:
-
# (3) -$2.55
-
# (4) ($2.55)
-
-
value.sub!(/^\((.+)\)$/, '-\1') # (4)
-
case value
-
when /^-?\D+[\d,]+\.\d{2}$/ # (1)
-
value.gsub!(/[^-\d.]/, '')
-
when /^-?\D+[\d.]+,\d{2}$/ # (2)
-
value.gsub!(/[^-\d,]/, '').sub!(/,/, '.')
-
end
-
-
ConnectionAdapters::Column.value_to_decimal value
-
end
-
end
-
-
1
class Vector < Type
-
1
attr_reader :delim, :subtype
-
-
# +delim+ corresponds to the `typdelim` column in the pg_types
-
# table. +subtype+ is derived from the `typelem` column in the
-
# pg_types table.
-
1
def initialize(delim, subtype)
-
6
@delim = delim
-
6
@subtype = subtype
-
end
-
-
# FIXME: this should probably split on +delim+ and use +subtype+
-
# to cast the values. Unfortunately, the current Rails behavior
-
# is to just return the string.
-
1
def type_cast(value)
-
value
-
end
-
end
-
-
1
class Point < Type
-
1
def type_cast(value)
-
if String === value
-
ConnectionAdapters::PostgreSQLColumn.string_to_point value
-
else
-
value
-
end
-
end
-
end
-
-
1
class Array < Type
-
1
attr_reader :subtype
-
1
def initialize(subtype)
-
45
@subtype = subtype
-
end
-
-
1
def type_cast(value)
-
if String === value
-
ConnectionAdapters::PostgreSQLColumn.string_to_array value, @subtype
-
else
-
value
-
end
-
end
-
end
-
-
1
class Range < Type
-
1
attr_reader :subtype
-
1
def initialize(subtype)
-
4
@subtype = subtype
-
end
-
-
1
def extract_bounds(value)
-
from, to = value[1..-2].split(',')
-
{
-
from: (value[1] == ',' || from == '-infinity') ? infinity(:negative => true) : from,
-
to: (value[-2] == ',' || to == 'infinity') ? infinity : to,
-
exclude_start: (value[0] == '('),
-
exclude_end: (value[-1] == ')')
-
}
-
end
-
-
1
def infinity(options = {})
-
::Float::INFINITY * (options[:negative] ? -1 : 1)
-
end
-
-
1
def infinity?(value)
-
value.respond_to?(:infinite?) && value.infinite?
-
end
-
-
1
def to_integer(value)
-
infinity?(value) ? value : value.to_i
-
end
-
-
1
def type_cast(value)
-
return if value.nil? || value == 'empty'
-
return value if value.is_a?(::Range)
-
-
extracted = extract_bounds(value)
-
-
case @subtype
-
when :date
-
from = ConnectionAdapters::Column.value_to_date(extracted[:from])
-
from += 1.day if extracted[:exclude_start]
-
to = ConnectionAdapters::Column.value_to_date(extracted[:to])
-
when :decimal
-
from = BigDecimal.new(extracted[:from].to_s)
-
# FIXME: add exclude start for ::Range, same for timestamp ranges
-
to = BigDecimal.new(extracted[:to].to_s)
-
when :time
-
from = ConnectionAdapters::Column.string_to_time(extracted[:from])
-
to = ConnectionAdapters::Column.string_to_time(extracted[:to])
-
when :integer
-
from = to_integer(extracted[:from]) rescue value ? 1 : 0
-
from += 1 if extracted[:exclude_start]
-
to = to_integer(extracted[:to]) rescue value ? 1 : 0
-
else
-
return value
-
end
-
-
::Range.new(from, to, extracted[:exclude_end])
-
end
-
end
-
-
1
class Integer < Type
-
1
def type_cast(value)
-
28
return if value.nil?
-
-
28
ConnectionAdapters::Column.value_to_integer value
-
end
-
end
-
-
1
class Boolean < Type
-
1
def type_cast(value)
-
1
return if value.nil?
-
-
1
ConnectionAdapters::Column.value_to_boolean value
-
end
-
end
-
-
1
class Timestamp < Type
-
1
def type; :timestamp; end
-
-
1
def type_cast(value)
-
24
return if value.nil?
-
-
# FIXME: probably we can improve this since we know it is PG
-
# specific
-
24
ConnectionAdapters::PostgreSQLColumn.string_to_time value
-
end
-
end
-
-
1
class Date < Type
-
1
def type; :datetime; end
-
-
1
def type_cast(value)
-
8
return if value.nil?
-
-
# FIXME: probably we can improve this since we know it is PG
-
# specific
-
8
ConnectionAdapters::Column.value_to_date value
-
end
-
end
-
-
1
class Time < Type
-
1
def type_cast(value)
-
return if value.nil?
-
-
# FIXME: probably we can improve this since we know it is PG
-
# specific
-
ConnectionAdapters::Column.string_to_dummy_time value
-
end
-
end
-
-
1
class Float < Type
-
1
def type_cast(value)
-
case value
-
when nil; nil
-
when 'Infinity'; ::Float::INFINITY
-
when '-Infinity'; -::Float::INFINITY
-
when 'NaN'; ::Float::NAN
-
else
-
value.to_f
-
end
-
end
-
end
-
-
1
class Decimal < Type
-
1
def type_cast(value)
-
return if value.nil?
-
-
ConnectionAdapters::Column.value_to_decimal value
-
end
-
end
-
-
1
class Hstore < Type
-
1
def type_cast_for_write(value)
-
# roundtrip to ensure uniform uniform types
-
# TODO: This is not an efficient solution.
-
stringified = ConnectionAdapters::PostgreSQLColumn.hstore_to_string(value)
-
type_cast(stringified)
-
end
-
-
1
def type_cast(value)
-
return if value.nil?
-
-
ConnectionAdapters::PostgreSQLColumn.string_to_hstore value
-
end
-
-
1
def accessor
-
ActiveRecord::Store::StringKeyedHashAccessor
-
end
-
end
-
-
1
class Cidr < Type
-
1
def type_cast(value)
-
return if value.nil?
-
-
ConnectionAdapters::PostgreSQLColumn.string_to_cidr value
-
end
-
end
-
-
1
class Json < Type
-
1
def type_cast_for_write(value)
-
# roundtrip to ensure uniform uniform types
-
# TODO: This is not an efficient solution.
-
stringified = ConnectionAdapters::PostgreSQLColumn.json_to_string(value)
-
type_cast(stringified)
-
end
-
-
1
def type_cast(value)
-
return if value.nil?
-
-
ConnectionAdapters::PostgreSQLColumn.string_to_json value
-
end
-
-
1
def accessor
-
ActiveRecord::Store::StringKeyedHashAccessor
-
end
-
end
-
-
1
class TypeMap
-
1
def initialize
-
1
@mapping = {}
-
end
-
-
1
def []=(oid, type)
-
90
@mapping[oid] = type
-
end
-
-
1
def [](oid)
-
51
@mapping[oid]
-
end
-
-
1
def clear
-
@mapping.clear
-
end
-
-
1
def key?(oid)
-
88
@mapping.key? oid
-
end
-
-
1
def fetch(ftype, fmod)
-
# The type for the numeric depends on the width of the field,
-
# so we'll do something special here.
-
#
-
# When dealing with decimal columns:
-
#
-
# places after decimal = fmod - 4 & 0xffff
-
# places before decimal = (fmod - 4) >> 16 & 0xffff
-
112
if ftype == 1700 && (fmod - 4 & 0xffff).zero?
-
ftype = 23
-
end
-
-
112
@mapping.fetch(ftype) { |oid| yield oid, fmod }
-
end
-
end
-
-
# When the PG adapter connects, the pg_type table is queried. The
-
# key of this hash maps to the `typname` column from the table.
-
# type_map is then dynamically built with oids as the key and type
-
# objects as values.
-
1
NAMES = Hash.new { |h,k| # :nodoc:
-
h[k] = OID::Identity.new
-
}
-
-
# Register an OID type named +name+ with a typecasting object in
-
# +type+. +name+ should correspond to the `typname` column in
-
# the `pg_type` table.
-
1
def self.register_type(name, type)
-
25
NAMES[name] = type
-
end
-
-
# Alias the +old+ type to the +new+ type.
-
1
def self.alias_type(new, old)
-
15
NAMES[new] = NAMES[old]
-
end
-
-
# Is +name+ a registered type?
-
1
def self.registered_type?(name)
-
284
NAMES.key? name
-
end
-
-
1
register_type 'int2', OID::Integer.new
-
1
alias_type 'int4', 'int2'
-
1
alias_type 'int8', 'int2'
-
1
alias_type 'oid', 'int2'
-
-
1
register_type 'daterange', OID::Range.new(:date)
-
1
register_type 'numrange', OID::Range.new(:decimal)
-
1
register_type 'tsrange', OID::Range.new(:time)
-
1
register_type 'int4range', OID::Range.new(:integer)
-
1
alias_type 'tstzrange', 'tsrange'
-
1
alias_type 'int8range', 'int4range'
-
-
1
register_type 'numeric', OID::Decimal.new
-
1
register_type 'text', OID::Identity.new
-
1
alias_type 'varchar', 'text'
-
1
alias_type 'char', 'text'
-
1
alias_type 'bpchar', 'text'
-
1
alias_type 'xml', 'text'
-
-
# FIXME: why are we keeping these types as strings?
-
1
alias_type 'tsvector', 'text'
-
1
alias_type 'interval', 'text'
-
1
alias_type 'macaddr', 'text'
-
1
alias_type 'uuid', 'text'
-
-
1
register_type 'money', OID::Money.new
-
1
register_type 'bytea', OID::Bytea.new
-
1
register_type 'bool', OID::Boolean.new
-
1
register_type 'bit', OID::Bit.new
-
1
register_type 'varbit', OID::Bit.new
-
-
1
register_type 'float4', OID::Float.new
-
1
alias_type 'float8', 'float4'
-
-
1
register_type 'timestamp', OID::Timestamp.new
-
1
register_type 'timestamptz', OID::Timestamp.new
-
1
register_type 'date', OID::Date.new
-
1
register_type 'time', OID::Time.new
-
-
1
register_type 'path', OID::Identity.new
-
1
register_type 'point', OID::Point.new
-
1
register_type 'polygon', OID::Identity.new
-
1
register_type 'circle', OID::Identity.new
-
1
register_type 'hstore', OID::Hstore.new
-
1
register_type 'json', OID::Json.new
-
1
register_type 'ltree', OID::Identity.new
-
-
1
register_type 'cidr', OID::Cidr.new
-
1
alias_type 'inet', 'cidr'
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
module ConnectionAdapters
-
1
class PostgreSQLAdapter < AbstractAdapter
-
1
module Quoting
-
# Escapes binary strings for bytea input to the database.
-
1
def escape_bytea(value)
-
PGconn.escape_bytea(value) if value
-
end
-
-
# Unescapes bytea output from a database to the binary string it represents.
-
# NOTE: This is NOT an inverse of escape_bytea! This is only to be used
-
# on escaped binary output from database drive.
-
1
def unescape_bytea(value)
-
PGconn.unescape_bytea(value) if value
-
end
-
-
# Quotes PostgreSQL-specific data types for SQL input.
-
1
def quote(value, column = nil) #:nodoc:
-
return super unless column && column.type
-
-
sql_type = type_to_sql(column.type, column.limit, column.precision, column.scale)
-
-
case value
-
when Range
-
if /range$/ =~ sql_type
-
escaped = quote_string(PostgreSQLColumn.range_to_string(value))
-
"'#{escaped}'::#{sql_type}"
-
else
-
super
-
end
-
when Array
-
case sql_type
-
when 'point' then super(PostgreSQLColumn.point_to_string(value))
-
when 'json' then super(PostgreSQLColumn.json_to_string(value))
-
else
-
if column.array
-
"'#{PostgreSQLColumn.array_to_string(value, column, self).gsub(/'/, "''")}'"
-
else
-
super
-
end
-
end
-
when Hash
-
case sql_type
-
when 'hstore' then super(PostgreSQLColumn.hstore_to_string(value), column)
-
when 'json' then super(PostgreSQLColumn.json_to_string(value), column)
-
else super
-
end
-
when IPAddr
-
case sql_type
-
when 'inet', 'cidr' then super(PostgreSQLColumn.cidr_to_string(value), column)
-
else super
-
end
-
when Float
-
if value.infinite? && column.type == :datetime
-
"'#{value.to_s.downcase}'"
-
elsif value.infinite? || value.nan?
-
"'#{value.to_s}'"
-
else
-
super
-
end
-
when Numeric
-
if sql_type == 'money' || [:string, :text].include?(column.type)
-
# Not truly string input, so doesn't require (or allow) escape string syntax.
-
"'#{value}'"
-
else
-
super
-
end
-
when String
-
case sql_type
-
when 'bytea' then "'#{escape_bytea(value)}'"
-
when 'xml' then "xml '#{quote_string(value)}'"
-
when /^bit/
-
case value
-
when /\A[01]*\Z/ then "B'#{value}'" # Bit-string notation
-
when /\A[0-9A-F]*\Z/i then "X'#{value}'" # Hexadecimal notation
-
end
-
else
-
super
-
end
-
else
-
super
-
end
-
end
-
-
1
def type_cast(value, column, array_member = false)
-
28
return super(value, column) unless column
-
-
28
case value
-
when Range
-
if /range$/ =~ column.sql_type
-
PostgreSQLColumn.range_to_string(value)
-
else
-
super(value, column)
-
end
-
when NilClass
-
if column.array && array_member
-
'NULL'
-
elsif column.array
-
value
-
else
-
super(value, column)
-
end
-
when Array
-
case column.sql_type
-
when 'point' then PostgreSQLColumn.point_to_string(value)
-
when 'json' then PostgreSQLColumn.json_to_string(value)
-
else
-
if column.array
-
PostgreSQLColumn.array_to_string(value, column, self)
-
else
-
super(value, column)
-
end
-
end
-
when String
-
4
if 'bytea' == column.sql_type
-
# Return a bind param hash with format as binary.
-
# See http://deveiate.org/code/pg/PGconn.html#method-i-exec_prepared-doc
-
# for more information
-
{ value: value, format: 1 }
-
else
-
4
super(value, column)
-
end
-
when Hash
-
case column.sql_type
-
when 'hstore' then PostgreSQLColumn.hstore_to_string(value, array_member)
-
when 'json' then PostgreSQLColumn.json_to_string(value)
-
else super(value, column)
-
end
-
when IPAddr
-
if %w(inet cidr).include? column.sql_type
-
PostgreSQLColumn.cidr_to_string(value)
-
else
-
super(value, column)
-
end
-
else
-
24
super(value, column)
-
end
-
end
-
-
# Quotes strings for use in SQL input.
-
1
def quote_string(s) #:nodoc:
-
@connection.escape(s)
-
end
-
-
# Checks the following cases:
-
#
-
# - table_name
-
# - "table.name"
-
# - schema_name.table_name
-
# - schema_name."table.name"
-
# - "schema.name".table_name
-
# - "schema.name"."table.name"
-
1
def quote_table_name(name)
-
15
schema, name_part = extract_pg_identifier_from_name(name.to_s)
-
-
15
unless name_part
-
15
quote_column_name(schema)
-
else
-
table_name, name_part = extract_pg_identifier_from_name(name_part)
-
"#{quote_column_name(schema)}.#{quote_column_name(table_name)}"
-
end
-
end
-
-
1
def quote_table_name_for_assignment(table, attr)
-
quote_column_name(attr)
-
end
-
-
# Quotes column names for use in SQL queries.
-
1
def quote_column_name(name) #:nodoc:
-
30
PGconn.quote_ident(name.to_s)
-
end
-
-
# Quote date/time values for use in SQL input. Includes microseconds
-
# if the value is a Time responding to usec.
-
1
def quoted_date(value) #:nodoc:
-
16
result = super
-
16
if value.acts_like?(:time) && value.respond_to?(:usec)
-
12
result = "#{result}.#{sprintf("%06d", value.usec)}"
-
end
-
-
16
if value.year < 0
-
result = result.sub(/^-/, "") + " BC"
-
end
-
16
result
-
end
-
-
# Does not quote function default values for UUID columns
-
1
def quote_default_value(value, column) #:nodoc:
-
if column.type == :uuid && value =~ /\(\)/
-
value
-
else
-
quote(value, column)
-
end
-
end
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
module ConnectionAdapters
-
1
class PostgreSQLAdapter < AbstractAdapter
-
1
module ReferentialIntegrity
-
1
def supports_disable_referential_integrity? #:nodoc:
-
true
-
end
-
-
1
def disable_referential_integrity #:nodoc:
-
if supports_disable_referential_integrity?
-
begin
-
execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER ALL" }.join(";"))
-
rescue
-
execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER USER" }.join(";"))
-
end
-
end
-
yield
-
ensure
-
if supports_disable_referential_integrity?
-
begin
-
execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER ALL" }.join(";"))
-
rescue
-
execute(tables.collect { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER USER" }.join(";"))
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
module ConnectionAdapters
-
1
class PostgreSQLAdapter < AbstractAdapter
-
1
class SchemaCreation < AbstractAdapter::SchemaCreation
-
1
private
-
-
1
def visit_AddColumn(o)
-
sql_type = type_to_sql(o.type.to_sym, o.limit, o.precision, o.scale)
-
sql = "ADD COLUMN #{quote_column_name(o.name)} #{sql_type}"
-
add_column_options!(sql, column_options(o))
-
end
-
-
1
def visit_ColumnDefinition(o)
-
sql = super
-
if o.primary_key? && o.type != :primary_key
-
sql << " PRIMARY KEY "
-
add_column_options!(sql, column_options(o))
-
end
-
sql
-
end
-
-
1
def add_column_options!(sql, options)
-
if options[:array] || options[:column].try(:array)
-
sql << '[]'
-
end
-
-
column = options.fetch(:column) { return super }
-
if column.type == :uuid && options[:default] =~ /\(\)/
-
sql << " DEFAULT #{options[:default]}"
-
else
-
super
-
end
-
end
-
end
-
-
1
def schema_creation
-
SchemaCreation.new self
-
end
-
-
1
module SchemaStatements
-
# Drops the database specified on the +name+ attribute
-
# and creates it again using the provided +options+.
-
1
def recreate_database(name, options = {}) #:nodoc:
-
drop_database(name)
-
create_database(name, options)
-
end
-
-
# Create a new PostgreSQL database. Options include <tt>:owner</tt>, <tt>:template</tt>,
-
# <tt>:encoding</tt> (defaults to utf8), <tt>:collation</tt>, <tt>:ctype</tt>,
-
# <tt>:tablespace</tt>, and <tt>:connection_limit</tt> (note that MySQL uses
-
# <tt>:charset</tt> while PostgreSQL uses <tt>:encoding</tt>).
-
#
-
# Example:
-
# create_database config[:database], config
-
# create_database 'foo_development', encoding: 'unicode'
-
1
def create_database(name, options = {})
-
options = { encoding: 'utf8' }.merge!(options.symbolize_keys)
-
-
option_string = options.sum do |key, value|
-
case key
-
when :owner
-
" OWNER = \"#{value}\""
-
when :template
-
" TEMPLATE = \"#{value}\""
-
when :encoding
-
" ENCODING = '#{value}'"
-
when :collation
-
" LC_COLLATE = '#{value}'"
-
when :ctype
-
" LC_CTYPE = '#{value}'"
-
when :tablespace
-
" TABLESPACE = \"#{value}\""
-
when :connection_limit
-
" CONNECTION LIMIT = #{value}"
-
else
-
""
-
end
-
end
-
-
execute "CREATE DATABASE #{quote_table_name(name)}#{option_string}"
-
end
-
-
# Drops a PostgreSQL database.
-
#
-
# Example:
-
# drop_database 'matt_development'
-
1
def drop_database(name) #:nodoc:
-
execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
-
end
-
-
# Returns the list of all tables in the schema search path or a specified schema.
-
1
def tables(name = nil)
-
query(<<-SQL, 'SCHEMA').map { |row| row[0] }
-
SELECT tablename
-
FROM pg_tables
-
WHERE schemaname = ANY (current_schemas(false))
-
SQL
-
end
-
-
# Returns true if table exists.
-
# If the schema is not specified as part of +name+ then it will only find tables within
-
# the current schema search path (regardless of permissions to access tables in other schemas)
-
1
def table_exists?(name)
-
4
schema, table = Utils.extract_schema_and_table(name.to_s)
-
4
return false unless table
-
-
4
binds = [[nil, table]]
-
4
binds << [nil, schema] if schema
-
-
exec_query(<<-SQL, 'SCHEMA').rows.first[0].to_i > 0
-
SELECT COUNT(*)
-
FROM pg_class c
-
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
-
WHERE c.relkind in ('v','r')
-
AND c.relname = '#{table.gsub(/(^"|"$)/,'')}'
-
AND n.nspname = #{schema ? "'#{schema}'" : 'ANY (current_schemas(false))'}
-
4
SQL
-
end
-
-
# Returns true if schema exists.
-
1
def schema_exists?(name)
-
exec_query(<<-SQL, 'SCHEMA').rows.first[0].to_i > 0
-
SELECT COUNT(*)
-
FROM pg_namespace
-
WHERE nspname = '#{name}'
-
SQL
-
end
-
-
1
def index_name_exists?(table_name, index_name, default)
-
exec_query(<<-SQL, 'SCHEMA').rows.first[0].to_i > 0
-
SELECT COUNT(*)
-
FROM pg_class t
-
INNER JOIN pg_index d ON t.oid = d.indrelid
-
INNER JOIN pg_class i ON d.indexrelid = i.oid
-
WHERE i.relkind = 'i'
-
AND i.relname = '#{index_name}'
-
AND t.relname = '#{table_name}'
-
AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = ANY (current_schemas(false)) )
-
SQL
-
end
-
-
# Returns an array of indexes for the given table.
-
1
def indexes(table_name, name = nil)
-
result = query(<<-SQL, 'SCHEMA')
-
SELECT distinct i.relname, d.indisunique, d.indkey, pg_get_indexdef(d.indexrelid), t.oid
-
FROM pg_class t
-
INNER JOIN pg_index d ON t.oid = d.indrelid
-
INNER JOIN pg_class i ON d.indexrelid = i.oid
-
WHERE i.relkind = 'i'
-
AND d.indisprimary = 'f'
-
AND t.relname = '#{table_name}'
-
AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname = ANY (current_schemas(false)) )
-
ORDER BY i.relname
-
SQL
-
-
result.map do |row|
-
index_name = row[0]
-
unique = row[1] == 't'
-
indkey = row[2].split(" ")
-
inddef = row[3]
-
oid = row[4]
-
-
columns = Hash[query(<<-SQL, "SCHEMA")]
-
SELECT a.attnum, a.attname
-
FROM pg_attribute a
-
WHERE a.attrelid = #{oid}
-
AND a.attnum IN (#{indkey.join(",")})
-
SQL
-
-
column_names = columns.values_at(*indkey).compact
-
-
unless column_names.empty?
-
# add info on sort order for columns (only desc order is explicitly specified, asc is the default)
-
desc_order_columns = inddef.scan(/(\w+) DESC/).flatten
-
orders = desc_order_columns.any? ? Hash[desc_order_columns.map {|order_column| [order_column, :desc]}] : {}
-
where = inddef.scan(/WHERE (.+)$/).flatten[0]
-
using = inddef.scan(/USING (.+?) /).flatten[0].to_sym
-
-
IndexDefinition.new(table_name, index_name, unique, column_names, [], orders, where, nil, using)
-
end
-
end.compact
-
end
-
-
# Returns the list of all column definitions for a table.
-
1
def columns(table_name)
-
# Limit, precision, and scale are all handled by the superclass.
-
5
column_definitions(table_name).map do |column_name, type, default, notnull, oid, fmod|
-
30
oid = get_oid_type(oid.to_i, fmod.to_i, column_name)
-
30
PostgreSQLColumn.new(column_name, default, oid, type, notnull == 'f')
-
end
-
end
-
-
1
def column_for(table_name, column_name) #:nodoc:
-
columns(table_name).detect { |c| c.name == column_name.to_s }
-
end
-
-
# Returns the current database name.
-
1
def current_database
-
query('select current_database()', 'SCHEMA')[0][0]
-
end
-
-
# Returns the current schema name.
-
1
def current_schema
-
query('SELECT current_schema', 'SCHEMA')[0][0]
-
end
-
-
# Returns the current database encoding format.
-
1
def encoding
-
query(<<-end_sql, 'SCHEMA')[0][0]
-
SELECT pg_encoding_to_char(pg_database.encoding) FROM pg_database
-
WHERE pg_database.datname LIKE '#{current_database}'
-
end_sql
-
end
-
-
# Returns the current database collation.
-
1
def collation
-
query(<<-end_sql, 'SCHEMA')[0][0]
-
SELECT pg_database.datcollate FROM pg_database WHERE pg_database.datname LIKE '#{current_database}'
-
end_sql
-
end
-
-
# Returns the current database ctype.
-
1
def ctype
-
query(<<-end_sql, 'SCHEMA')[0][0]
-
SELECT pg_database.datctype FROM pg_database WHERE pg_database.datname LIKE '#{current_database}'
-
end_sql
-
end
-
-
# Returns an array of schema names.
-
1
def schema_names
-
query(<<-SQL, 'SCHEMA').flatten
-
SELECT nspname
-
FROM pg_namespace
-
WHERE nspname !~ '^pg_.*'
-
AND nspname NOT IN ('information_schema')
-
ORDER by nspname;
-
SQL
-
end
-
-
# Creates a schema for the given schema name.
-
1
def create_schema schema_name
-
execute "CREATE SCHEMA #{schema_name}"
-
end
-
-
# Drops the schema for the given schema name.
-
1
def drop_schema schema_name
-
execute "DROP SCHEMA #{schema_name} CASCADE"
-
end
-
-
# Sets the schema search path to a string of comma-separated schema names.
-
# Names beginning with $ have to be quoted (e.g. $user => '$user').
-
# See: http://www.postgresql.org/docs/current/static/ddl-schemas.html
-
#
-
# This should be not be called manually but set in database.yml.
-
1
def schema_search_path=(schema_csv)
-
1
if schema_csv
-
execute("SET search_path TO #{schema_csv}", 'SCHEMA')
-
@schema_search_path = schema_csv
-
end
-
end
-
-
# Returns the active schema search path.
-
1
def schema_search_path
-
10
@schema_search_path ||= query('SHOW search_path', 'SCHEMA')[0][0]
-
end
-
-
# Returns the current client message level.
-
1
def client_min_messages
-
1
query('SHOW client_min_messages', 'SCHEMA')[0][0]
-
end
-
-
# Set the client message level.
-
1
def client_min_messages=(level)
-
3
execute("SET client_min_messages TO '#{level}'", 'SCHEMA')
-
end
-
-
# Returns the sequence name for a table's primary key or some other specified key.
-
1
def default_sequence_name(table_name, pk = nil) #:nodoc:
-
result = serial_sequence(table_name, pk || 'id')
-
return nil unless result
-
result.split('.').last
-
rescue ActiveRecord::StatementInvalid
-
"#{table_name}_#{pk || 'id'}_seq"
-
end
-
-
1
def serial_sequence(table, column)
-
result = exec_query(<<-eosql, 'SCHEMA')
-
SELECT pg_get_serial_sequence('#{table}', '#{column}')
-
eosql
-
result.rows.first.first
-
end
-
-
# Resets the sequence of a table's primary key to the maximum value.
-
1
def reset_pk_sequence!(table, pk = nil, sequence = nil) #:nodoc:
-
unless pk and sequence
-
default_pk, default_sequence = pk_and_sequence_for(table)
-
-
pk ||= default_pk
-
sequence ||= default_sequence
-
end
-
-
if @logger && pk && !sequence
-
@logger.warn "#{table} has primary key #{pk} with no default sequence"
-
end
-
-
if pk && sequence
-
quoted_sequence = quote_table_name(sequence)
-
-
select_value <<-end_sql, 'SCHEMA'
-
SELECT setval('#{quoted_sequence}', (SELECT COALESCE(MAX(#{quote_column_name pk})+(SELECT increment_by FROM #{quoted_sequence}), (SELECT min_value FROM #{quoted_sequence})) FROM #{quote_table_name(table)}), false)
-
end_sql
-
end
-
end
-
-
# Returns a table's primary key and belonging sequence.
-
1
def pk_and_sequence_for(table) #:nodoc:
-
# First try looking for a sequence with a dependency on the
-
# given table's primary key.
-
result = query(<<-end_sql, 'SCHEMA')[0]
-
SELECT attr.attname, seq.relname
-
FROM pg_class seq,
-
pg_attribute attr,
-
pg_depend dep,
-
pg_constraint cons
-
WHERE seq.oid = dep.objid
-
AND seq.relkind = 'S'
-
AND attr.attrelid = dep.refobjid
-
AND attr.attnum = dep.refobjsubid
-
AND attr.attrelid = cons.conrelid
-
AND attr.attnum = cons.conkey[1]
-
AND cons.contype = 'p'
-
AND dep.refobjid = '#{quote_table_name(table)}'::regclass
-
end_sql
-
-
if result.nil? or result.empty?
-
result = query(<<-end_sql, 'SCHEMA')[0]
-
SELECT attr.attname,
-
CASE
-
WHEN pg_get_expr(def.adbin, def.adrelid) !~* 'nextval' THEN NULL
-
WHEN split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2) ~ '.' THEN
-
substr(split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2),
-
strpos(split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2), '.')+1)
-
ELSE split_part(pg_get_expr(def.adbin, def.adrelid), '''', 2)
-
END
-
FROM pg_class t
-
JOIN pg_attribute attr ON (t.oid = attrelid)
-
JOIN pg_attrdef def ON (adrelid = attrelid AND adnum = attnum)
-
JOIN pg_constraint cons ON (conrelid = adrelid AND adnum = conkey[1])
-
WHERE t.oid = '#{quote_table_name(table)}'::regclass
-
AND cons.contype = 'p'
-
AND pg_get_expr(def.adbin, def.adrelid) ~* 'nextval|uuid_generate'
-
end_sql
-
end
-
-
[result.first, result.last]
-
rescue
-
nil
-
end
-
-
# Returns just a table's primary key
-
1
def primary_key(table)
-
3
row = exec_query(<<-end_sql, 'SCHEMA').rows.first
-
SELECT attr.attname
-
FROM pg_attribute attr
-
INNER JOIN pg_constraint cons ON attr.attrelid = cons.conrelid AND attr.attnum = cons.conkey[1]
-
WHERE cons.contype = 'p'
-
AND cons.conrelid = '#{quote_table_name(table)}'::regclass
-
end_sql
-
-
3
row && row.first
-
end
-
-
# Renames a table.
-
# Also renames a table's primary key sequence if the sequence name matches the
-
# Active Record default.
-
#
-
# Example:
-
# rename_table('octopuses', 'octopi')
-
1
def rename_table(table_name, new_name)
-
clear_cache!
-
execute "ALTER TABLE #{quote_table_name(table_name)} RENAME TO #{quote_table_name(new_name)}"
-
pk, seq = pk_and_sequence_for(new_name)
-
if seq == "#{table_name}_#{pk}_seq"
-
new_seq = "#{new_name}_#{pk}_seq"
-
execute "ALTER TABLE #{quote_table_name(seq)} RENAME TO #{quote_table_name(new_seq)}"
-
end
-
-
rename_table_indexes(table_name, new_name)
-
end
-
-
# Adds a new column to the named table.
-
# See TableDefinition#column for details of the options you can use.
-
1
def add_column(table_name, column_name, type, options = {})
-
clear_cache!
-
super
-
end
-
-
# Changes the column of a table.
-
1
def change_column(table_name, column_name, type, options = {})
-
clear_cache!
-
quoted_table_name = quote_table_name(table_name)
-
sql_type = type_to_sql(type, options[:limit], options[:precision], options[:scale])
-
sql_type << "[]" if options[:array]
-
execute "ALTER TABLE #{quoted_table_name} ALTER COLUMN #{quote_column_name(column_name)} TYPE #{sql_type}"
-
-
change_column_default(table_name, column_name, options[:default]) if options_include_default?(options)
-
change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null)
-
end
-
-
# Changes the default value of a table column.
-
1
def change_column_default(table_name, column_name, default)
-
clear_cache!
-
column = column_for(table_name, column_name)
-
-
execute "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} SET DEFAULT #{quote_default_value(default, column)}" if column
-
end
-
-
1
def change_column_null(table_name, column_name, null, default = nil)
-
clear_cache!
-
unless null || default.nil?
-
column = column_for(table_name, column_name)
-
execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote_default_value(default, column)} WHERE #{quote_column_name(column_name)} IS NULL") if column
-
end
-
execute("ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL")
-
end
-
-
# Renames a column in a table.
-
1
def rename_column(table_name, column_name, new_column_name)
-
clear_cache!
-
execute "ALTER TABLE #{quote_table_name(table_name)} RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
-
rename_column_indexes(table_name, column_name, new_column_name)
-
end
-
-
1
def add_index(table_name, column_name, options = {}) #:nodoc:
-
index_name, index_type, index_columns, index_options, index_algorithm, index_using = add_index_options(table_name, column_name, options)
-
execute "CREATE #{index_type} INDEX #{index_algorithm} #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} #{index_using} (#{index_columns})#{index_options}"
-
end
-
-
1
def remove_index!(table_name, index_name) #:nodoc:
-
execute "DROP INDEX #{quote_table_name(index_name)}"
-
end
-
-
1
def rename_index(table_name, old_name, new_name)
-
execute "ALTER INDEX #{quote_column_name(old_name)} RENAME TO #{quote_table_name(new_name)}"
-
end
-
-
1
def index_name_length
-
63
-
end
-
-
# Maps logical Rails types to PostgreSQL-specific data types.
-
1
def type_to_sql(type, limit = nil, precision = nil, scale = nil)
-
case type.to_s
-
when 'binary'
-
# PostgreSQL doesn't support limits on binary (bytea) columns.
-
# The hard limit is 1Gb, because of a 32-bit size field, and TOAST.
-
case limit
-
when nil, 0..0x3fffffff; super(type)
-
else raise(ActiveRecordError, "No binary type has byte size #{limit}.")
-
end
-
when 'text'
-
# PostgreSQL doesn't support limits on text columns.
-
# The hard limit is 1Gb, according to section 8.3 in the manual.
-
case limit
-
when nil, 0..0x3fffffff; super(type)
-
else raise(ActiveRecordError, "The limit on text can be at most 1GB - 1byte.")
-
end
-
when 'integer'
-
return 'integer' unless limit
-
-
case limit
-
when 1, 2; 'smallint'
-
when 3, 4; 'integer'
-
when 5..8; 'bigint'
-
else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with precision 0 instead.")
-
end
-
when 'datetime'
-
return super unless precision
-
-
case precision
-
when 0..6; "timestamp(#{precision})"
-
else raise(ActiveRecordError, "No timestamp type has precision of #{precision}. The allowed range of precision is from 0 to 6")
-
end
-
else
-
super
-
end
-
end
-
-
# PostgreSQL requires the ORDER BY columns in the select list for distinct queries, and
-
# requires that the ORDER BY include the distinct column.
-
1
def columns_for_distinct(columns, orders) #:nodoc:
-
order_columns = orders.reject(&:blank?).map{ |s|
-
# Convert Arel node to string
-
s = s.to_sql unless s.is_a?(String)
-
# Remove any ASC/DESC modifiers
-
s.gsub(/\s+(?:ASC|DESC)\b/i, '')
-
.gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, '')
-
}.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
-
-
[super, *order_columns].join(', ')
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_record/connection_adapters/abstract_adapter'
-
1
require 'active_record/connection_adapters/statement_pool'
-
1
require 'active_record/connection_adapters/postgresql/oid'
-
1
require 'active_record/connection_adapters/postgresql/cast'
-
1
require 'active_record/connection_adapters/postgresql/array_parser'
-
1
require 'active_record/connection_adapters/postgresql/quoting'
-
1
require 'active_record/connection_adapters/postgresql/schema_statements'
-
1
require 'active_record/connection_adapters/postgresql/database_statements'
-
1
require 'active_record/connection_adapters/postgresql/referential_integrity'
-
1
require 'arel/visitors/bind_visitor'
-
-
# Make sure we're using pg high enough for PGResult#values
-
1
gem 'pg', '~> 0.11'
-
1
require 'pg'
-
-
1
require 'ipaddr'
-
-
1
module ActiveRecord
-
1
module ConnectionHandling # :nodoc:
-
1
VALID_CONN_PARAMS = [:host, :hostaddr, :port, :dbname, :user, :password, :connect_timeout,
-
:client_encoding, :options, :application_name, :fallback_application_name,
-
:keepalives, :keepalives_idle, :keepalives_interval, :keepalives_count,
-
:tty, :sslmode, :requiressl, :sslcompression, :sslcert, :sslkey,
-
:sslrootcert, :sslcrl, :requirepeer, :krbsrvname, :gsslib, :service]
-
-
# Establishes a connection to the database that's used by all Active Record objects
-
1
def postgresql_connection(config)
-
1
conn_params = config.symbolize_keys
-
-
5
conn_params.delete_if { |_, v| v.nil? }
-
-
# Map ActiveRecords param names to PGs.
-
1
conn_params[:user] = conn_params.delete(:username) if conn_params[:username]
-
1
conn_params[:dbname] = conn_params.delete(:database) if conn_params[:database]
-
-
# Forward only valid config params to PGconn.connect.
-
5
conn_params.keep_if { |k, _| VALID_CONN_PARAMS.include?(k) }
-
-
# The postgres drivers don't allow the creation of an unconnected PGconn object,
-
# so just pass a nil connection object for the time being.
-
1
ConnectionAdapters::PostgreSQLAdapter.new(nil, logger, conn_params, config)
-
end
-
end
-
-
1
module ConnectionAdapters
-
# PostgreSQL-specific extensions to column definitions in a table.
-
1
class PostgreSQLColumn < Column #:nodoc:
-
1
attr_accessor :array
-
-
1
def initialize(name, default, oid_type, sql_type = nil, null = true)
-
30
@oid_type = oid_type
-
30
default_value = self.class.extract_value_from_default(default)
-
-
30
if sql_type =~ /\[\]$/
-
@array = true
-
super(name, default_value, sql_type[0..sql_type.length - 3], null)
-
else
-
30
@array = false
-
30
super(name, default_value, sql_type, null)
-
end
-
-
30
@default_function = default if has_default_function?(default_value, default)
-
end
-
-
1
def number?
-
124
!array && super
-
end
-
-
1
def text?
-
!array && super
-
end
-
-
# :stopdoc:
-
1
class << self
-
1
include ConnectionAdapters::PostgreSQLColumn::Cast
-
1
include ConnectionAdapters::PostgreSQLColumn::ArrayParser
-
1
attr_accessor :money_precision
-
end
-
# :startdoc:
-
-
# Extracts the value from a PostgreSQL column default definition.
-
1
def self.extract_value_from_default(default)
-
# This is a performance optimization for Ruby 1.9.2 in development.
-
# If the value is nil, we return nil straight away without checking
-
# the regular expressions. If we check each regular expression,
-
# Regexp#=== will call NilClass#to_str, which will trigger
-
# method_missing (defined by whiny nil in ActiveSupport) which
-
# makes this method very very slow.
-
30
return default unless default
-
-
5
case default
-
when /\A'(.*)'::(num|date|tstz|ts|int4|int8)range\z/m
-
$1
-
# Numeric types
-
when /\A\(?(-?\d+(\.\d*)?\)?(::bigint)?)\z/
-
$1
-
# Character types
-
when /\A\(?'(.*)'::.*\b(?:character varying|bpchar|text)\z/m
-
$1.gsub(/''/, "'")
-
# Binary data types
-
when /\A'(.*)'::bytea\z/m
-
$1
-
# Date/time types
-
when /\A'(.+)'::(?:time(?:stamp)? with(?:out)? time zone|date)\z/
-
$1
-
when /\A'(.*)'::interval\z/
-
$1
-
# Boolean type
-
when 'true'
-
true
-
when 'false'
-
1
false
-
# Geometric types
-
when /\A'(.*)'::(?:point|line|lseg|box|"?path"?|polygon|circle)\z/
-
$1
-
# Network address types
-
when /\A'(.*)'::(?:cidr|inet|macaddr)\z/
-
$1
-
# Bit string types
-
when /\AB'(.*)'::"?bit(?: varying)?"?\z/
-
$1
-
# XML type
-
when /\A'(.*)'::xml\z/m
-
$1
-
# Arrays
-
when /\A'(.*)'::"?\D+"?\[\]\z/
-
$1
-
# Hstore
-
when /\A'(.*)'::hstore\z/
-
$1
-
# JSON
-
when /\A'(.*)'::json\z/
-
$1
-
# Object identifier types
-
when /\A-?\d+\z/
-
$1
-
else
-
# Anything else is blank, some user type, or some function
-
# and we can't know the value of that, so return nil.
-
nil
-
end
-
end
-
-
1
def type_cast_for_write(value)
-
36
if @oid_type.respond_to?(:type_cast_for_write)
-
@oid_type.type_cast_for_write(value)
-
else
-
36
super
-
end
-
end
-
-
1
def type_cast(value)
-
174
return if value.nil?
-
67
return super if encoded?
-
-
67
@oid_type.type_cast value
-
end
-
-
1
def accessor
-
@oid_type.accessor
-
end
-
-
1
private
-
-
1
def has_default_function?(default_value, default)
-
30
!default_value && (%r{\w+\(.*\)} === default)
-
end
-
-
1
def extract_limit(sql_type)
-
30
case sql_type
-
when /^bigint/i; 8
-
when /^smallint/i; 2
-
when /^timestamp/i; nil
-
22
else super
-
end
-
end
-
-
# Extracts the scale from PostgreSQL-specific data types.
-
1
def extract_scale(sql_type)
-
# Money type has a fixed scale of 2.
-
30
sql_type =~ /^money/ ? 2 : super
-
end
-
-
# Extracts the precision from PostgreSQL-specific data types.
-
1
def extract_precision(sql_type)
-
30
if sql_type == 'money'
-
self.class.money_precision
-
30
elsif sql_type =~ /timestamp/i
-
8
$1.to_i if sql_type =~ /\((\d+)\)/
-
else
-
22
super
-
end
-
end
-
-
# Maps PostgreSQL-specific data types to logical Rails types.
-
1
def simplified_type(field_type)
-
30
case field_type
-
# Numeric and monetary types
-
when /^(?:real|double precision)$/
-
:float
-
# Monetary types
-
when 'money'
-
:decimal
-
when 'hstore'
-
1
:hstore
-
when 'ltree'
-
:ltree
-
# Network address types
-
when 'inet'
-
:inet
-
when 'cidr'
-
:cidr
-
when 'macaddr'
-
:macaddr
-
# Character types
-
when /^(?:character varying|bpchar)(?:\(\d+\))?$/
-
9
:string
-
# Binary data types
-
when 'bytea'
-
:binary
-
# Date/time types
-
when /^timestamp with(?:out)? time zone$/
-
8
:datetime
-
when /^interval(?:|\(\d+\))$/
-
:string
-
# Geometric types
-
when /^(?:point|line|lseg|box|"?path"?|polygon|circle)$/
-
:string
-
# Bit strings
-
when /^bit(?: varying)?(?:\(\d+\))?$/
-
:string
-
# XML type
-
when 'xml'
-
:xml
-
# tsvector type
-
when 'tsvector'
-
:tsvector
-
# Arrays
-
when /^\D+\[\]$/
-
:string
-
# Object identifier types
-
when 'oid'
-
:integer
-
# UUID type
-
when 'uuid'
-
:uuid
-
# JSON type
-
when 'json'
-
:json
-
# Small and big integer types
-
when /^(?:small|big)int$/
-
:integer
-
when /(num|date|tstz|ts|int4|int8)range$/
-
field_type.to_sym
-
# Pass through all types that are not specific to PostgreSQL.
-
else
-
12
super
-
end
-
end
-
end
-
-
# The PostgreSQL adapter works with the native C (https://bitbucket.org/ged/ruby-pg) driver.
-
#
-
# Options:
-
#
-
# * <tt>:host</tt> - Defaults to a Unix-domain socket in /tmp. On machines without Unix-domain sockets,
-
# the default is to connect to localhost.
-
# * <tt>:port</tt> - Defaults to 5432.
-
# * <tt>:username</tt> - Defaults to be the same as the operating system name of the user running the application.
-
# * <tt>:password</tt> - Password to be used if the server demands password authentication.
-
# * <tt>:database</tt> - Defaults to be the same as the user name.
-
# * <tt>:schema_search_path</tt> - An optional schema search path for the connection given
-
# as a string of comma-separated schema names. This is backward-compatible with the <tt>:schema_order</tt> option.
-
# * <tt>:encoding</tt> - An optional client encoding that is used in a <tt>SET client_encoding TO
-
# <encoding></tt> call on the connection.
-
# * <tt>:min_messages</tt> - An optional client min messages that is used in a
-
# <tt>SET client_min_messages TO <min_messages></tt> call on the connection.
-
# * <tt>:variables</tt> - An optional hash of additional parameters that
-
# will be used in <tt>SET SESSION key = val</tt> calls on the connection.
-
# * <tt>:insert_returning</tt> - An optional boolean to control the use or <tt>RETURNING</tt> for <tt>INSERT</tt> statements
-
# defaults to true.
-
#
-
# Any further options are used as connection parameters to libpq. See
-
# http://www.postgresql.org/docs/9.1/static/libpq-connect.html for the
-
# list of parameters.
-
#
-
# In addition, default connection parameters of libpq can be set per environment variables.
-
# See http://www.postgresql.org/docs/9.1/static/libpq-envars.html .
-
1
class PostgreSQLAdapter < AbstractAdapter
-
1
class ColumnDefinition < ActiveRecord::ConnectionAdapters::ColumnDefinition
-
1
attr_accessor :array
-
end
-
-
1
module ColumnMethods
-
1
def xml(*args)
-
options = args.extract_options!
-
column(args[0], 'xml', options)
-
end
-
-
1
def tsvector(*args)
-
options = args.extract_options!
-
column(args[0], 'tsvector', options)
-
end
-
-
1
def int4range(name, options = {})
-
column(name, 'int4range', options)
-
end
-
-
1
def int8range(name, options = {})
-
column(name, 'int8range', options)
-
end
-
-
1
def tsrange(name, options = {})
-
column(name, 'tsrange', options)
-
end
-
-
1
def tstzrange(name, options = {})
-
column(name, 'tstzrange', options)
-
end
-
-
1
def numrange(name, options = {})
-
column(name, 'numrange', options)
-
end
-
-
1
def daterange(name, options = {})
-
column(name, 'daterange', options)
-
end
-
-
1
def hstore(name, options = {})
-
column(name, 'hstore', options)
-
end
-
-
1
def ltree(name, options = {})
-
column(name, 'ltree', options)
-
end
-
-
1
def inet(name, options = {})
-
column(name, 'inet', options)
-
end
-
-
1
def cidr(name, options = {})
-
column(name, 'cidr', options)
-
end
-
-
1
def macaddr(name, options = {})
-
column(name, 'macaddr', options)
-
end
-
-
1
def uuid(name, options = {})
-
column(name, 'uuid', options)
-
end
-
-
1
def json(name, options = {})
-
column(name, 'json', options)
-
end
-
end
-
-
1
class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
-
1
include ColumnMethods
-
-
# Defines the primary key field.
-
# Use of the native PostgreSQL UUID type is supported, and can be used
-
# by defining your tables as such:
-
#
-
# create_table :stuffs, id: :uuid do |t|
-
# t.string :content
-
# t.timestamps
-
# end
-
#
-
# By default, this will use the +uuid_generate_v4()+ function from the
-
# +uuid-ossp+ extension, which MUST be enabled on your database. To enable
-
# the +uuid-ossp+ extension, you can use the +enable_extension+ method in your
-
# migrations. To use a UUID primary key without +uuid-ossp+ enabled, you can
-
# set the +:default+ option to +nil+:
-
#
-
# create_table :stuffs, id: false do |t|
-
# t.primary_key :id, :uuid, default: nil
-
# t.uuid :foo_id
-
# t.timestamps
-
# end
-
#
-
# You may also pass a different UUID generation function from +uuid-ossp+
-
# or another library.
-
#
-
# Note that setting the UUID primary key default value to +nil+ will
-
# require you to assure that you always provide a UUID value before saving
-
# a record (as primary keys cannot be +nil+). This might be done via the
-
# +SecureRandom.uuid+ method and a +before_save+ callback, for instance.
-
1
def primary_key(name, type = :primary_key, options = {})
-
return super unless type == :uuid
-
options[:default] = options.fetch(:default, 'uuid_generate_v4()')
-
options[:primary_key] = true
-
column name, type, options
-
end
-
-
1
def column(name, type = nil, options = {})
-
super
-
column = self[name]
-
column.array = options[:array]
-
-
self
-
end
-
-
1
private
-
-
1
def create_column_definition(name, type)
-
ColumnDefinition.new name, type
-
end
-
end
-
-
1
class Table < ActiveRecord::ConnectionAdapters::Table
-
1
include ColumnMethods
-
end
-
-
1
ADAPTER_NAME = 'PostgreSQL'
-
-
1
NATIVE_DATABASE_TYPES = {
-
primary_key: "serial primary key",
-
string: { name: "character varying", limit: 255 },
-
text: { name: "text" },
-
integer: { name: "integer" },
-
float: { name: "float" },
-
decimal: { name: "decimal" },
-
datetime: { name: "timestamp" },
-
timestamp: { name: "timestamp" },
-
time: { name: "time" },
-
date: { name: "date" },
-
daterange: { name: "daterange" },
-
numrange: { name: "numrange" },
-
tsrange: { name: "tsrange" },
-
tstzrange: { name: "tstzrange" },
-
int4range: { name: "int4range" },
-
int8range: { name: "int8range" },
-
binary: { name: "bytea" },
-
boolean: { name: "boolean" },
-
xml: { name: "xml" },
-
tsvector: { name: "tsvector" },
-
hstore: { name: "hstore" },
-
inet: { name: "inet" },
-
cidr: { name: "cidr" },
-
macaddr: { name: "macaddr" },
-
uuid: { name: "uuid" },
-
json: { name: "json" },
-
ltree: { name: "ltree" }
-
}
-
-
1
include Quoting
-
1
include ReferentialIntegrity
-
1
include SchemaStatements
-
1
include DatabaseStatements
-
1
include Savepoints
-
-
# Returns 'PostgreSQL' as adapter name for identification purposes.
-
1
def adapter_name
-
ADAPTER_NAME
-
end
-
-
# Adds `:array` option to the default set provided by the
-
# AbstractAdapter
-
1
def prepare_column_options(column, types)
-
spec = super
-
spec[:array] = 'true' if column.respond_to?(:array) && column.array
-
spec[:default] = "\"#{column.default_function}\"" if column.default_function
-
spec
-
end
-
-
# Adds `:array` as a valid migration key
-
1
def migration_keys
-
super + [:array]
-
end
-
-
# Returns +true+, since this connection adapter supports prepared statement
-
# caching.
-
1
def supports_statement_cache?
-
true
-
end
-
-
1
def supports_index_sort_order?
-
true
-
end
-
-
1
def supports_partial_index?
-
true
-
end
-
-
1
def supports_transaction_isolation?
-
true
-
end
-
-
1
def index_algorithms
-
{ concurrently: 'CONCURRENTLY' }
-
end
-
-
1
class StatementPool < ConnectionAdapters::StatementPool
-
1
def initialize(connection, max)
-
1
super
-
1
@counter = 0
-
2
@cache = Hash.new { |h,pid| h[pid] = {} }
-
end
-
-
1
def each(&block); cache.each(&block); end
-
11
def key?(key); cache.key?(key); end
-
11
def [](key); cache[key]; end
-
1
def length; cache.length; end
-
-
1
def next_key
-
5
"a#{@counter + 1}"
-
end
-
-
1
def []=(sql, key)
-
5
while @max <= cache.size
-
dealloc(cache.shift.last)
-
end
-
5
@counter += 1
-
5
cache[sql] = key
-
end
-
-
1
def clear
-
cache.each_value do |stmt_key|
-
dealloc stmt_key
-
end
-
cache.clear
-
end
-
-
1
def delete(sql_key)
-
dealloc cache[sql_key]
-
cache.delete sql_key
-
end
-
-
1
private
-
-
1
def cache
-
30
@cache[Process.pid]
-
end
-
-
1
def dealloc(key)
-
@connection.query "DEALLOCATE #{key}" if connection_active?
-
end
-
-
1
def connection_active?
-
@connection.status == PGconn::CONNECTION_OK
-
rescue PGError
-
false
-
end
-
end
-
-
1
class BindSubstitution < Arel::Visitors::PostgreSQL # :nodoc:
-
1
include Arel::Visitors::BindVisitor
-
end
-
-
# Initializes and connects a PostgreSQL adapter.
-
1
def initialize(connection, logger, connection_parameters, config)
-
1
super(connection, logger)
-
-
2
if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
-
1
@prepared_statements = true
-
1
@visitor = Arel::Visitors::PostgreSQL.new self
-
else
-
@visitor = unprepared_visitor
-
end
-
-
1
@connection_parameters, @config = connection_parameters, config
-
-
# @local_tz is initialized as nil to avoid warnings when connect tries to use it
-
1
@local_tz = nil
-
1
@table_alias_length = nil
-
-
1
connect
-
1
@statements = StatementPool.new @connection,
-
1
self.class.type_cast_config_to_integer(config.fetch(:statement_limit) { 1000 })
-
-
1
if postgresql_version < 80200
-
raise "Your version of PostgreSQL (#{postgresql_version}) is too old, please upgrade!"
-
end
-
-
1
@type_map = OID::TypeMap.new
-
1
initialize_type_map(type_map)
-
1
@local_tz = execute('SHOW TIME ZONE', 'SCHEMA').first["TimeZone"]
-
1
@use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
-
end
-
-
# Clears the prepared statements cache.
-
1
def clear_cache!
-
@statements.clear
-
end
-
-
# Is this connection alive and ready for queries?
-
1
def active?
-
4
@connection.query 'SELECT 1'
-
4
true
-
rescue PGError
-
false
-
end
-
-
1
def active_threadsafe?
-
@connection.connect_poll != PG::PGRES_POLLING_FAILED
-
end
-
-
# Close then reopen the connection.
-
1
def reconnect!
-
super
-
@connection.reset
-
configure_connection
-
end
-
-
1
def reset!
-
clear_cache!
-
super
-
end
-
-
# Disconnects from the database if already connected. Otherwise, this
-
# method does nothing.
-
1
def disconnect!
-
super
-
@connection.close rescue nil
-
end
-
-
1
def native_database_types #:nodoc:
-
NATIVE_DATABASE_TYPES
-
end
-
-
# Returns true, since this connection adapter supports migrations.
-
1
def supports_migrations?
-
true
-
end
-
-
# Does PostgreSQL support finding primary key on non-Active Record tables?
-
1
def supports_primary_key? #:nodoc:
-
true
-
end
-
-
# Enable standard-conforming strings if available.
-
1
def set_standard_conforming_strings
-
1
old, self.client_min_messages = client_min_messages, 'panic'
-
1
execute('SET standard_conforming_strings = on', 'SCHEMA') rescue nil
-
ensure
-
1
self.client_min_messages = old
-
end
-
-
1
def supports_insert_with_returning?
-
true
-
end
-
-
1
def supports_ddl_transactions?
-
true
-
end
-
-
1
def supports_explain?
-
true
-
end
-
-
# Returns true if pg > 9.1
-
1
def supports_extensions?
-
postgresql_version >= 90100
-
end
-
-
# Range datatypes weren't introduced until PostgreSQL 9.2
-
1
def supports_ranges?
-
postgresql_version >= 90200
-
end
-
-
1
def enable_extension(name)
-
exec_query("CREATE EXTENSION IF NOT EXISTS \"#{name}\"").tap {
-
reload_type_map
-
}
-
end
-
-
1
def disable_extension(name)
-
exec_query("DROP EXTENSION IF EXISTS \"#{name}\" CASCADE").tap {
-
reload_type_map
-
}
-
end
-
-
1
def extension_enabled?(name)
-
if supports_extensions?
-
res = exec_query "SELECT EXISTS(SELECT * FROM pg_available_extensions WHERE name = '#{name}' AND installed_version IS NOT NULL) as enabled",
-
'SCHEMA'
-
res.column_types['enabled'].type_cast res.rows.first.first
-
end
-
end
-
-
1
def extensions
-
if supports_extensions?
-
res = exec_query "SELECT extname from pg_extension", "SCHEMA"
-
res.rows.map { |r| res.column_types['extname'].type_cast r.first }
-
else
-
super
-
end
-
end
-
-
# Returns the configured supported identifier length supported by PostgreSQL
-
1
def table_alias_length
-
@table_alias_length ||= query('SHOW max_identifier_length', 'SCHEMA')[0][0].to_i
-
end
-
-
# Set the authorized user for this session
-
1
def session_auth=(user)
-
clear_cache!
-
exec_query "SET SESSION AUTHORIZATION #{user}"
-
end
-
-
1
module Utils
-
1
extend self
-
-
# Returns an array of <tt>[schema_name, table_name]</tt> extracted from +name+.
-
# +schema_name+ is nil if not specified in +name+.
-
# +schema_name+ and +table_name+ exclude surrounding quotes (regardless of whether provided in +name+)
-
# +name+ supports the range of schema/table references understood by PostgreSQL, for example:
-
#
-
# * <tt>table_name</tt>
-
# * <tt>"table.name"</tt>
-
# * <tt>schema_name.table_name</tt>
-
# * <tt>schema_name."table.name"</tt>
-
# * <tt>"schema.name"."table name"</tt>
-
1
def extract_schema_and_table(name)
-
8
table, schema = name.scan(/[^".\s]+|"[^"]*"/)[0..1].collect{|m| m.gsub(/(^"|"$)/,'') }.reverse
-
4
[schema, table]
-
end
-
end
-
-
1
def use_insert_returning?
-
12
@use_insert_returning
-
end
-
-
1
def valid_type?(type)
-
!native_database_types[type].nil?
-
end
-
-
1
def update_table_definition(table_name, base) #:nodoc:
-
Table.new(table_name, base)
-
end
-
-
1
protected
-
-
# Returns the version of the connected PostgreSQL server.
-
1
def postgresql_version
-
2
@connection.server_version
-
end
-
-
# See http://www.postgresql.org/docs/9.1/static/errcodes-appendix.html
-
1
FOREIGN_KEY_VIOLATION = "23503"
-
1
UNIQUE_VIOLATION = "23505"
-
-
1
def translate_exception(exception, message)
-
return exception unless exception.respond_to?(:result)
-
-
case exception.result.try(:error_field, PGresult::PG_DIAG_SQLSTATE)
-
when UNIQUE_VIOLATION
-
RecordNotUnique.new(message, exception)
-
when FOREIGN_KEY_VIOLATION
-
InvalidForeignKey.new(message, exception)
-
else
-
super
-
end
-
end
-
-
1
private
-
-
1
def type_map
-
113
@type_map
-
end
-
-
1
def get_oid_type(oid, fmod, column_name)
-
112
type_map.fetch(oid, fmod) {
-
warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
-
type_map[oid] = OID::Identity.new
-
}
-
end
-
-
1
def reload_type_map
-
type_map.clear
-
initialize_type_map(type_map)
-
end
-
-
1
def add_oid(row, records_by_oid, type_map)
-
7
return type_map if type_map.key? row['type_elem'].to_i
-
-
7
if OID.registered_type? row['typname']
-
# this composite type is explicitly registered
-
1
vector = OID::NAMES[row['typname']]
-
else
-
# use the default for composite types
-
6
unless type_map.key? row['typelem'].to_i
-
add_oid records_by_oid[row['typelem']], records_by_oid, type_map
-
end
-
-
6
vector = OID::Vector.new row['typdelim'], type_map[row['typelem'].to_i]
-
end
-
-
7
type_map[row['oid'].to_i] = vector
-
7
type_map
-
end
-
-
1
def initialize_type_map(type_map)
-
1
result = execute('SELECT oid, typname, typelem, typdelim, typinput FROM pg_type', 'SCHEMA')
-
360
leaves, nodes = result.partition { |row| row['typelem'] == '0' }
-
-
# populate the leaf nodes
-
278
leaves.find_all { |row| OID.registered_type? row['typname'] }.each do |row|
-
38
type_map[row['oid'].to_i] = OID::NAMES[row['typname']]
-
end
-
-
360
records_by_oid = result.group_by { |row| row['oid'] }
-
-
83
arrays, nodes = nodes.partition { |row| row['typinput'] == 'array_in' }
-
-
# populate composite types
-
1
nodes.each do |row|
-
7
add_oid row, records_by_oid, type_map
-
end
-
-
# populate array types
-
76
arrays.find_all { |row| type_map.key? row['typelem'].to_i }.each do |row|
-
45
array = OID::Array.new type_map[row['typelem'].to_i]
-
45
type_map[row['oid'].to_i] = array
-
end
-
end
-
-
1
FEATURE_NOT_SUPPORTED = "0A000" #:nodoc:
-
-
1
def exec_no_cache(sql, name, binds)
-
30
log(sql, name, binds) { @connection.async_exec(sql, []) }
-
end
-
-
1
def exec_cache(sql, name, binds)
-
10
stmt_key = prepare_statement(sql)
-
10
type_casted_binds = binds.map { |col, val|
-
28
[col, type_cast(val, col)]
-
}
-
-
10
log(sql, name, type_casted_binds, stmt_key) do
-
38
@connection.send_query_prepared(stmt_key, type_casted_binds.map { |_, val| val })
-
10
@connection.block
-
10
@connection.get_last_result
-
end
-
rescue ActiveRecord::StatementInvalid => e
-
pgerror = e.original_exception
-
-
# Get the PG code for the failure. Annoyingly, the code for
-
# prepared statements whose return value may have changed is
-
# FEATURE_NOT_SUPPORTED. Check here for more details:
-
# http://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/utils/cache/plancache.c#l573
-
begin
-
code = pgerror.result.result_error_field(PGresult::PG_DIAG_SQLSTATE)
-
rescue
-
raise e
-
end
-
if FEATURE_NOT_SUPPORTED == code
-
@statements.delete sql_key(sql)
-
retry
-
else
-
raise e
-
end
-
end
-
-
# Returns the statement identifier for the client side cache
-
# of statements
-
1
def sql_key(sql)
-
10
"#{schema_search_path}-#{sql}"
-
end
-
-
# Prepare the statement if it hasn't been prepared, return
-
# the statement key.
-
1
def prepare_statement(sql)
-
10
sql_key = sql_key(sql)
-
10
unless @statements.key? sql_key
-
5
nextkey = @statements.next_key
-
5
begin
-
5
@connection.prepare nextkey, sql
-
rescue => e
-
raise translate_exception_class(e, sql)
-
end
-
# Clear the queue
-
5
@connection.get_last_result
-
5
@statements[sql_key] = nextkey
-
end
-
10
@statements[sql_key]
-
end
-
-
# The internal PostgreSQL identifier of the money data type.
-
1
MONEY_COLUMN_TYPE_OID = 790 #:nodoc:
-
# The internal PostgreSQL identifier of the BYTEA data type.
-
1
BYTEA_COLUMN_TYPE_OID = 17 #:nodoc:
-
-
# Connects to a PostgreSQL server and sets up the adapter depending on the
-
# connected server's characteristics.
-
1
def connect
-
1
@connection = PGconn.connect(@connection_parameters)
-
-
# Money type has a fixed precision of 10 in PostgreSQL 8.2 and below, and as of
-
# PostgreSQL 8.3 it has a fixed precision of 19. PostgreSQLColumn.extract_precision
-
# should know about this but can't detect it there, so deal with it here.
-
1
PostgreSQLColumn.money_precision = (postgresql_version >= 80300) ? 19 : 10
-
-
1
configure_connection
-
rescue ::PG::Error => error
-
if error.message.include?("does not exist")
-
raise ActiveRecord::NoDatabaseError.new(error.message)
-
else
-
raise error
-
end
-
end
-
-
# Configures the encoding, verbosity, schema search path, and time zone of the connection.
-
# This is called by #connect and should not be called manually.
-
1
def configure_connection
-
1
if @config[:encoding]
-
1
@connection.set_client_encoding(@config[:encoding])
-
end
-
1
self.client_min_messages = @config[:min_messages] || 'warning'
-
1
self.schema_search_path = @config[:schema_search_path] || @config[:schema_order]
-
-
# Use standard-conforming strings if available so we don't have to do the E'...' dance.
-
1
set_standard_conforming_strings
-
-
# If using Active Record's time zone support configure the connection to return
-
# TIMESTAMP WITH ZONE types in UTC.
-
# (SET TIME ZONE does not use an equals sign like other SET variables)
-
1
if ActiveRecord::Base.default_timezone == :utc
-
1
execute("SET time zone 'UTC'", 'SCHEMA')
-
elsif @local_tz
-
execute("SET time zone '#{@local_tz}'", 'SCHEMA')
-
end
-
-
# SET statements from :variables config hash
-
# http://www.postgresql.org/docs/8.3/static/sql-set.html
-
1
variables = @config[:variables] || {}
-
1
variables.map do |k, v|
-
if v == ':default' || v == :default
-
# Sets the value to the global or compile default
-
execute("SET SESSION #{k.to_s} TO DEFAULT", 'SCHEMA')
-
elsif !v.nil?
-
execute("SET SESSION #{k.to_s} TO #{quote(v)}", 'SCHEMA')
-
end
-
end
-
end
-
-
# Returns the current ID of a table's sequence.
-
1
def last_insert_id(sequence_name) #:nodoc:
-
Integer(last_insert_id_value(sequence_name))
-
end
-
-
1
def last_insert_id_value(sequence_name)
-
last_insert_id_result(sequence_name).rows.first.first
-
end
-
-
1
def last_insert_id_result(sequence_name) #:nodoc:
-
exec_query("SELECT currval('#{sequence_name}')", 'SQL')
-
end
-
-
# Executes a SELECT query and returns the results, performing any data type
-
# conversions that are required to be performed here instead of in PostgreSQLColumn.
-
1
def select(sql, name = nil, binds = [])
-
7
exec_query(sql, name, binds)
-
end
-
-
# Returns the list of a table's column names, data types, and default values.
-
#
-
# The underlying query is roughly:
-
# SELECT column.name, column.type, default.value
-
# FROM column LEFT JOIN default
-
# ON column.table_id = default.table_id
-
# AND column.num = default.column_num
-
# WHERE column.table_id = get_table_id('table_name')
-
# AND column.num > 0
-
# AND NOT column.is_dropped
-
# ORDER BY column.num
-
#
-
# If the table name is not prefixed with a schema, the database will
-
# take the first match from the schema search path.
-
#
-
# Query implementation notes:
-
# - format_type includes the column size constraint, e.g. varchar(50)
-
# - ::regclass is a function that gives the id for a table name
-
1
def column_definitions(table_name) #:nodoc:
-
exec_query(<<-end_sql, 'SCHEMA').rows
-
SELECT a.attname, format_type(a.atttypid, a.atttypmod),
-
pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod
-
FROM pg_attribute a LEFT JOIN pg_attrdef d
-
ON a.attrelid = d.adrelid AND a.attnum = d.adnum
-
WHERE a.attrelid = '#{quote_table_name(table_name)}'::regclass
-
AND a.attnum > 0 AND NOT a.attisdropped
-
ORDER BY a.attnum
-
5
end_sql
-
end
-
-
1
def extract_pg_identifier_from_name(name)
-
15
match_data = name.start_with?('"') ? name.match(/\"([^\"]+)\"/) : name.match(/([^\.]+)/)
-
-
15
if match_data
-
15
rest = name[match_data[0].length, name.length]
-
15
rest = rest[1, rest.length] if rest.start_with? "."
-
15
[match_data[1], (rest.length > 0 ? rest : nil)]
-
end
-
end
-
-
1
def extract_table_ref_from_insert_sql(sql)
-
sql[/into\s+([^\(]*).*values\s*\(/im]
-
$1.strip if $1
-
end
-
-
1
def create_table_definition(name, temporary, options, as = nil)
-
TableDefinition.new native_database_types, name, temporary, options, as
-
end
-
end
-
end
-
end
-
-
1
module ActiveRecord
-
1
module ConnectionAdapters
-
1
class SchemaCache
-
1
attr_reader :version
-
1
attr_accessor :connection
-
-
1
def initialize(conn)
-
1
@connection = conn
-
-
1
@columns = {}
-
1
@columns_hash = {}
-
1
@primary_keys = {}
-
1
@tables = {}
-
1
prepare_default_proc
-
end
-
-
1
def primary_keys(table_name)
-
3
@primary_keys[table_name]
-
end
-
-
# A cached lookup for table existence.
-
1
def table_exists?(name)
-
6
return @tables[name] if @tables.key? name
-
-
3
@tables[name] = connection.table_exists?(name)
-
end
-
-
# Add internal cache for table with +table_name+.
-
1
def add(table_name)
-
if table_exists?(table_name)
-
@primary_keys[table_name]
-
@columns[table_name]
-
@columns_hash[table_name]
-
end
-
end
-
-
1
def tables(name)
-
@tables[name]
-
end
-
-
# Get the columns for a table
-
1
def columns(table)
-
6
@columns[table]
-
end
-
-
# Get the columns for a table as a hash, key is the column name
-
# value is the column object.
-
1
def columns_hash(table)
-
8
@columns_hash[table]
-
end
-
-
# Clears out internal caches
-
1
def clear!
-
@columns.clear
-
@columns_hash.clear
-
@primary_keys.clear
-
@tables.clear
-
@version = nil
-
end
-
-
1
def size
-
[@columns, @columns_hash, @primary_keys, @tables].map { |x|
-
x.size
-
}.inject :+
-
end
-
-
# Clear out internal caches for table with +table_name+.
-
1
def clear_table_cache!(table_name)
-
@columns.delete table_name
-
@columns_hash.delete table_name
-
@primary_keys.delete table_name
-
@tables.delete table_name
-
end
-
-
1
def marshal_dump
-
# if we get current version during initialization, it happens stack over flow.
-
@version = ActiveRecord::Migrator.current_version
-
[@version] + [@columns, @columns_hash, @primary_keys, @tables].map { |val|
-
Hash[val]
-
}
-
end
-
-
1
def marshal_load(array)
-
@version, @columns, @columns_hash, @primary_keys, @tables = array
-
prepare_default_proc
-
end
-
-
1
private
-
-
1
def prepare_default_proc
-
1
@columns.default_proc = Proc.new do |h, table_name|
-
5
h[table_name] = connection.columns(table_name)
-
end
-
-
1
@columns_hash.default_proc = Proc.new do |h, table_name|
-
2
h[table_name] = Hash[columns(table_name).map { |col|
-
14
[col.name, col]
-
}]
-
end
-
-
1
@primary_keys.default_proc = Proc.new do |h, table_name|
-
3
h[table_name] = table_exists?(table_name) ? connection.primary_key(table_name) : nil
-
end
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
module ConnectionAdapters
-
1
class StatementPool
-
1
include Enumerable
-
-
1
def initialize(connection, max = 1000)
-
1
@connection = connection
-
1
@max = max
-
end
-
-
1
def each
-
raise NotImplementedError
-
end
-
-
1
def key?(key)
-
raise NotImplementedError
-
end
-
-
1
def [](key)
-
raise NotImplementedError
-
end
-
-
1
def length
-
raise NotImplementedError
-
end
-
-
1
def []=(sql, key)
-
raise NotImplementedError
-
end
-
-
1
def clear
-
raise NotImplementedError
-
end
-
-
1
def delete(key)
-
raise NotImplementedError
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
module ConnectionHandling
-
4
RAILS_ENV = -> { Rails.env if defined?(Rails) }
-
4
DEFAULT_ENV = -> { RAILS_ENV.call || "default_env" }
-
-
# Establishes the connection to the database. Accepts a hash as input where
-
# the <tt>:adapter</tt> key must be specified with the name of a database adapter (in lower-case)
-
# example for regular databases (MySQL, Postgresql, etc):
-
#
-
# ActiveRecord::Base.establish_connection(
-
# adapter: "mysql",
-
# host: "localhost",
-
# username: "myuser",
-
# password: "mypass",
-
# database: "somedatabase"
-
# )
-
#
-
# Example for SQLite database:
-
#
-
# ActiveRecord::Base.establish_connection(
-
# adapter: "sqlite3",
-
# database: "path/to/dbfile"
-
# )
-
#
-
# Also accepts keys as strings (for parsing from YAML for example):
-
#
-
# ActiveRecord::Base.establish_connection(
-
# "adapter" => "sqlite3",
-
# "database" => "path/to/dbfile"
-
# )
-
#
-
# Or a URL:
-
#
-
# ActiveRecord::Base.establish_connection(
-
# "postgres://myuser:mypass@localhost/somedatabase"
-
# )
-
#
-
# In case <tt>ActiveRecord::Base.configurations</tt> is set (Rails
-
# automatically loads the contents of config/database.yml into it),
-
# a symbol can also be given as argument, representing a key in the
-
# configuration hash:
-
#
-
# ActiveRecord::Base.establish_connection(:production)
-
#
-
# The exceptions AdapterNotSpecified, AdapterNotFound and ArgumentError
-
# may be returned on an error.
-
1
def establish_connection(spec = nil)
-
1
spec ||= DEFAULT_ENV.call.to_sym
-
1
resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new configurations
-
1
spec = resolver.spec(spec)
-
-
1
unless respond_to?(spec.adapter_method)
-
raise AdapterNotFound, "database configuration specifies nonexistent #{spec.config[:adapter]} adapter"
-
end
-
-
1
remove_connection
-
1
connection_handler.establish_connection self, spec
-
end
-
-
1
class MergeAndResolveDefaultUrlConfig # :nodoc:
-
1
def initialize(raw_configurations)
-
2
@raw_config = raw_configurations.dup
-
2
@env = DEFAULT_ENV.call.to_s
-
end
-
-
# Returns fully resolved connection hashes.
-
# Merges connection information from `ENV['DATABASE_URL']` if available.
-
1
def resolve
-
2
ConnectionAdapters::ConnectionSpecification::Resolver.new(config).resolve_all
-
end
-
-
1
private
-
1
def config
-
2
@raw_config.dup.tap do |cfg|
-
2
if url = ENV['DATABASE_URL']
-
cfg[@env] ||= {}
-
cfg[@env]["url"] ||= url
-
end
-
end
-
end
-
end
-
-
# Returns the connection currently associated with the class. This can
-
# also be used to "borrow" the connection to do database work unrelated
-
# to any of the specific Active Records.
-
1
def connection
-
132
retrieve_connection
-
end
-
-
1
def connection_id
-
148
ActiveRecord::RuntimeRegistry.connection_id
-
end
-
-
1
def connection_id=(connection_id)
-
1
ActiveRecord::RuntimeRegistry.connection_id = connection_id
-
end
-
-
# Returns the configuration of the associated connection as a hash:
-
#
-
# ActiveRecord::Base.connection_config
-
# # => {pool: 5, timeout: 5000, database: "db/development.sqlite3", adapter: "sqlite3"}
-
#
-
# Please use only for reading.
-
1
def connection_config
-
connection_pool.spec.config
-
end
-
-
1
def connection_pool
-
connection_handler.retrieve_connection_pool(self) or raise ConnectionNotEstablished
-
end
-
-
1
def retrieve_connection
-
132
connection_handler.retrieve_connection(self)
-
end
-
-
# Returns +true+ if Active Record is connected.
-
1
def connected?
-
6
connection_handler.connected?(self)
-
end
-
-
1
def remove_connection(klass = self)
-
1
connection_handler.remove_connection(klass)
-
end
-
-
1
def clear_cache! # :nodoc:
-
connection.schema_cache.clear!
-
end
-
-
1
delegate :clear_active_connections!, :clear_reloadable_connections!,
-
:clear_all_connections!, :to => :connection_handler
-
end
-
end
-
1
require 'active_support/core_ext/hash/indifferent_access'
-
1
require 'active_support/core_ext/object/duplicable'
-
1
require 'thread'
-
-
1
module ActiveRecord
-
1
module Core
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
##
-
# :singleton-method:
-
#
-
# Accepts a logger conforming to the interface of Log4r which is then
-
# passed on to any new database connections made and which can be
-
# retrieved on both a class and instance level by calling +logger+.
-
1
mattr_accessor :logger, instance_writer: false
-
-
##
-
# :singleton-method:
-
# Contains the database configuration - as is typically stored in config/database.yml -
-
# as a Hash.
-
#
-
# For example, the following database.yml...
-
#
-
# development:
-
# adapter: sqlite3
-
# database: db/development.sqlite3
-
#
-
# production:
-
# adapter: sqlite3
-
# database: db/production.sqlite3
-
#
-
# ...would result in ActiveRecord::Base.configurations to look like this:
-
#
-
# {
-
# 'development' => {
-
# 'adapter' => 'sqlite3',
-
# 'database' => 'db/development.sqlite3'
-
# },
-
# 'production' => {
-
# 'adapter' => 'sqlite3',
-
# 'database' => 'db/production.sqlite3'
-
# }
-
# }
-
1
def self.configurations=(config)
-
2
@@configurations = ActiveRecord::ConnectionHandling::MergeAndResolveDefaultUrlConfig.new(config).resolve
-
end
-
1
self.configurations = {}
-
-
# Returns fully resolved configurations hash
-
1
def self.configurations
-
1
@@configurations
-
end
-
-
##
-
# :singleton-method:
-
# Determines whether to use Time.utc (using :utc) or Time.local (using :local) when pulling
-
# dates and times from the database. This is set to :utc by default.
-
1
mattr_accessor :default_timezone, instance_writer: false
-
1
self.default_timezone = :utc
-
-
##
-
# :singleton-method:
-
# Specifies the format to use when dumping the database schema with Rails'
-
# Rakefile. If :sql, the schema is dumped as (potentially database-
-
# specific) SQL statements. If :ruby, the schema is dumped as an
-
# ActiveRecord::Schema file which can be loaded into any database that
-
# supports migrations. Use :ruby if you want to have different database
-
# adapters for, e.g., your development and test environments.
-
1
mattr_accessor :schema_format, instance_writer: false
-
1
self.schema_format = :ruby
-
-
##
-
# :singleton-method:
-
# Specify whether or not to use timestamps for migration versions
-
1
mattr_accessor :timestamped_migrations, instance_writer: false
-
1
self.timestamped_migrations = true
-
-
##
-
# :singleton-method:
-
# Specify whether schema dump should happen at the end of the
-
# db:migrate rake task. This is true by default, which is useful for the
-
# development environment. This should ideally be false in the production
-
# environment where dumping schema is rarely needed.
-
1
mattr_accessor :dump_schema_after_migration, instance_writer: false
-
1
self.dump_schema_after_migration = true
-
-
# :nodoc:
-
1
mattr_accessor :maintain_test_schema, instance_accessor: false
-
-
1
def self.disable_implicit_join_references=(value)
-
ActiveSupport::Deprecation.warn("Implicit join references were removed with Rails 4.1." \
-
"Make sure to remove this configuration because it does nothing.")
-
end
-
-
1
class_attribute :default_connection_handler, instance_writer: false
-
-
1
def self.connection_handler
-
153
ActiveRecord::RuntimeRegistry.connection_handler || default_connection_handler
-
end
-
-
1
def self.connection_handler=(handler)
-
ActiveRecord::RuntimeRegistry.connection_handler = handler
-
end
-
-
1
self.default_connection_handler = ConnectionAdapters::ConnectionHandler.new
-
end
-
-
1
module ClassMethods
-
1
def initialize_generated_modules
-
super
-
-
generated_association_methods
-
end
-
-
1
def generated_association_methods
-
@generated_association_methods ||= begin
-
4
mod = const_set(:GeneratedAssociationMethods, Module.new)
-
4
include mod
-
4
mod
-
17
end
-
end
-
-
# Returns a string like 'Post(id:integer, title:string, body:text)'
-
1
def inspect
-
if self == Base
-
super
-
elsif abstract_class?
-
"#{super}(abstract)"
-
elsif !connected?
-
"#{super} (call '#{super}.connection' to establish a connection)"
-
elsif table_exists?
-
attr_list = columns.map { |c| "#{c.name}: #{c.type}" } * ', '
-
"#{super}(#{attr_list})"
-
else
-
"#{super}(Table doesn't exist)"
-
end
-
end
-
-
# Overwrite the default class equality method to provide support for association proxies.
-
1
def ===(object)
-
object.is_a?(self)
-
end
-
-
# Returns an instance of <tt>Arel::Table</tt> loaded with the current table name.
-
#
-
# class Post < ActiveRecord::Base
-
# scope :published_and_commented, -> { published.and(self.arel_table[:comments_count].gt(0)) }
-
# end
-
1
def arel_table # :nodoc:
-
119
@arel_table ||= Arel::Table.new(table_name, arel_engine)
-
end
-
-
# Returns the Arel engine.
-
1
def arel_engine # :nodoc:
-
@arel_engine ||=
-
if Base == self || connection_handler.retrieve_connection_pool(self)
-
5
self
-
else
-
superclass.arel_engine
-
7
end
-
end
-
-
1
private
-
-
1
def relation #:nodoc:
-
72
relation = Relation.create(self, arel_table)
-
-
72
if finder_needs_type_condition?
-
relation.where(type_condition).create_with(inheritance_column.to_sym => sti_name)
-
else
-
72
relation
-
end
-
end
-
end
-
-
# New objects can be instantiated as either empty (pass no construction parameter) or pre-set with
-
# attributes but not yet saved (pass a hash with key names matching the associated table column names).
-
# In both instances, valid attribute keys are determined by the column names of the associated table --
-
# hence you can't have attributes that aren't part of the table columns.
-
#
-
# ==== Example:
-
# # Instantiates a single new object
-
# User.new(first_name: 'Jamie')
-
1
def initialize(attributes = nil, options = {})
-
8
defaults = self.class.column_defaults.dup
-
60
defaults.each { |k, v| defaults[k] = v.dup if v.duplicable? }
-
-
8
@attributes = self.class.initialize_attributes(defaults)
-
8
@column_types_override = nil
-
8
@column_types = self.class.column_types
-
-
8
init_internals
-
8
initialize_internals_callback
-
-
# +options+ argument is only needed to make protected_attributes gem easier to hook.
-
# Remove it when we drop support to this gem.
-
8
init_attributes(attributes, options) if attributes
-
-
8
yield self if block_given?
-
8
run_callbacks :initialize unless _initialize_callbacks.empty?
-
end
-
-
# Initialize an empty model object from +coder+. +coder+ must contain
-
# the attributes necessary for initializing an empty model object. For
-
# example:
-
#
-
# class Post < ActiveRecord::Base
-
# end
-
#
-
# post = Post.allocate
-
# post.init_with('attributes' => { 'title' => 'hello world' })
-
# post.title # => 'hello world'
-
1
def init_with(coder)
-
16
@attributes = self.class.initialize_attributes(coder['attributes'])
-
16
@column_types_override = coder['column_types']
-
16
@column_types = self.class.column_types
-
-
16
init_internals
-
-
16
@new_record = false
-
-
16
self.class.define_attribute_methods
-
-
16
run_callbacks :find
-
16
run_callbacks :initialize
-
-
16
self
-
end
-
-
##
-
# :method: clone
-
# Identical to Ruby's clone method. This is a "shallow" copy. Be warned that your attributes are not copied.
-
# That means that modifying attributes of the clone will modify the original, since they will both point to the
-
# same attributes hash. If you need a copy of your attributes hash, please use the #dup method.
-
#
-
# user = User.first
-
# new_user = user.clone
-
# user.name # => "Bob"
-
# new_user.name = "Joe"
-
# user.name # => "Joe"
-
#
-
# user.object_id == new_user.object_id # => false
-
# user.name.object_id == new_user.name.object_id # => true
-
#
-
# user.name.object_id == user.dup.name.object_id # => false
-
-
##
-
# :method: dup
-
# Duped objects have no id assigned and are treated as new records. Note
-
# that this is a "shallow" copy as it copies the object's attributes
-
# only, not its associations. The extent of a "deep" copy is application
-
# specific and is therefore left to the application to implement according
-
# to its need.
-
# The dup method does not preserve the timestamps (created|updated)_(at|on).
-
-
##
-
1
def initialize_dup(other) # :nodoc:
-
cloned_attributes = other.clone_attributes(:read_attribute_before_type_cast)
-
self.class.initialize_attributes(cloned_attributes, :serialized => false)
-
-
@attributes = cloned_attributes
-
@attributes[self.class.primary_key] = nil
-
-
run_callbacks(:initialize) unless _initialize_callbacks.empty?
-
-
@aggregation_cache = {}
-
@association_cache = {}
-
@attributes_cache = {}
-
-
@new_record = true
-
@destroyed = false
-
-
super
-
end
-
-
# Populate +coder+ with attributes about this record that should be
-
# serialized. The structure of +coder+ defined in this method is
-
# guaranteed to match the structure of +coder+ passed to the +init_with+
-
# method.
-
#
-
# Example:
-
#
-
# class Post < ActiveRecord::Base
-
# end
-
# coder = {}
-
# Post.new.encode_with(coder)
-
# coder # => {"attributes" => {"id" => nil, ... }}
-
1
def encode_with(coder)
-
coder['attributes'] = attributes_for_coder
-
end
-
-
# Returns true if +comparison_object+ is the same exact object, or +comparison_object+
-
# is of the same type and +self+ has an ID and it is equal to +comparison_object.id+.
-
#
-
# Note that new records are different from any other record by definition, unless the
-
# other record is the receiver itself. Besides, if you fetch existing records with
-
# +select+ and leave the ID out, you're on your own, this predicate will return false.
-
#
-
# Note also that destroying a record preserves its ID in the model instance, so deleted
-
# models are still comparable.
-
1
def ==(comparison_object)
-
super ||
-
comparison_object.instance_of?(self.class) &&
-
!id.nil? &&
-
comparison_object.id == id
-
end
-
1
alias :eql? :==
-
-
# Delegates to id in order to allow two records of the same type and id to work with something like:
-
# [ Person.find(1), Person.find(2), Person.find(3) ] & [ Person.find(1), Person.find(4) ] # => [ Person.find(1) ]
-
1
def hash
-
8
id.hash
-
end
-
-
# Clone and freeze the attributes hash such that associations are still
-
# accessible, even on destroyed records, but cloned models will not be
-
# frozen.
-
1
def freeze
-
@attributes = @attributes.clone.freeze
-
self
-
end
-
-
# Returns +true+ if the attributes hash has been frozen.
-
1
def frozen?
-
@attributes.frozen?
-
end
-
-
# Allows sort on objects
-
1
def <=>(other_object)
-
if other_object.is_a?(self.class)
-
self.to_key <=> other_object.to_key
-
else
-
super
-
end
-
end
-
-
# Returns +true+ if the record is read only. Records loaded through joins with piggy-back
-
# attributes will be marked as read only since they cannot be saved.
-
1
def readonly?
-
8
@readonly
-
end
-
-
# Marks this record as read only.
-
1
def readonly!
-
@readonly = true
-
end
-
-
1
def connection_handler
-
self.class.connection_handler
-
end
-
-
# Returns the contents of the record as a nicely formatted string.
-
1
def inspect
-
# We check defined?(@attributes) not to issue warnings if the object is
-
# allocated but not initialized.
-
inspection = if defined?(@attributes) && @attributes
-
self.class.column_names.collect { |name|
-
if has_attribute?(name)
-
"#{name}: #{attribute_for_inspect(name)}"
-
end
-
}.compact.join(", ")
-
else
-
"not initialized"
-
end
-
"#<#{self.class} #{inspection}>"
-
end
-
-
# Returns a hash of the given methods with their names as keys and returned values as values.
-
1
def slice(*methods)
-
Hash[methods.map! { |method| [method, public_send(method)] }].with_indifferent_access
-
end
-
-
1
def set_transaction_state(state) # :nodoc:
-
2
@transaction_state = state
-
end
-
-
1
def has_transactional_callbacks? # :nodoc:
-
14
!_rollback_callbacks.empty? || !_commit_callbacks.empty? || !_create_callbacks.empty?
-
end
-
-
1
private
-
-
# Updates the attributes on this particular ActiveRecord object so that
-
# if it is associated with a transaction, then the state of the AR object
-
# will be updated to reflect the current state of the transaction
-
#
-
# The @transaction_state variable stores the states of the associated
-
# transaction. This relies on the fact that a transaction can only be in
-
# one rollback or commit (otherwise a list of states would be required)
-
# Each AR object inside of a transaction carries that transaction's
-
# TransactionState.
-
#
-
# This method checks to see if the ActiveRecord object's state reflects
-
# the TransactionState, and rolls back or commits the ActiveRecord object
-
# as appropriate.
-
#
-
# Since ActiveRecord objects can be inside multiple transactions, this
-
# method recursively goes through the parent of the TransactionState and
-
# checks if the ActiveRecord object reflects the state of the object.
-
1
def sync_with_transaction_state
-
51
update_attributes_from_transaction_state(@transaction_state, 0)
-
end
-
-
1
def update_attributes_from_transaction_state(transaction_state, depth)
-
51
if transaction_state && transaction_state.finalized? && !has_transactional_callbacks?
-
unless @reflects_state[depth]
-
restore_transaction_record_state if transaction_state.rolledback?
-
clear_transaction_record_state
-
@reflects_state[depth] = true
-
end
-
-
if transaction_state.parent && !@reflects_state[depth+1]
-
update_attributes_from_transaction_state(transaction_state.parent, depth+1)
-
end
-
end
-
end
-
-
# Under Ruby 1.9, Array#flatten will call #to_ary (recursively) on each of the elements
-
# of the array, and then rescues from the possible NoMethodError. If those elements are
-
# ActiveRecord::Base's, then this triggers the various method_missing's that we have,
-
# which significantly impacts upon performance.
-
#
-
# So we can avoid the method_missing hit by explicitly defining #to_ary as nil here.
-
#
-
# See also http://tenderlovemaking.com/2011/06/28/til-its-ok-to-return-nil-from-to_ary.html
-
1
def to_ary # :nodoc:
-
nil
-
end
-
-
1
def init_internals
-
24
pk = self.class.primary_key
-
24
@attributes[pk] = nil unless @attributes.key?(pk)
-
-
24
@aggregation_cache = {}
-
24
@association_cache = {}
-
24
@attributes_cache = {}
-
24
@readonly = false
-
24
@destroyed = false
-
24
@marked_for_destruction = false
-
24
@destroyed_by_association = nil
-
24
@new_record = true
-
24
@txn = nil
-
24
@_start_transaction_state = {}
-
24
@transaction_state = nil
-
24
@reflects_state = [false]
-
end
-
-
1
def initialize_internals_callback
-
end
-
-
# This method is needed to make protected_attributes gem easier to hook.
-
# Remove it when we drop support to this gem.
-
1
def init_attributes(attributes, options)
-
6
assign_attributes(attributes)
-
end
-
end
-
end
-
1
module ActiveRecord
-
# = Active Record Counter Cache
-
1
module CounterCache
-
1
extend ActiveSupport::Concern
-
-
1
module ClassMethods
-
# Resets one or more counter caches to their correct value using an SQL
-
# count query. This is useful when adding new counter caches, or if the
-
# counter has been corrupted or modified directly by SQL.
-
#
-
# ==== Parameters
-
#
-
# * +id+ - The id of the object you wish to reset a counter on.
-
# * +counters+ - One or more association counters to reset
-
#
-
# ==== Examples
-
#
-
# # For Post with id #1 records reset the comments_count
-
# Post.reset_counters(1, :comments)
-
1
def reset_counters(id, *counters)
-
object = find(id)
-
counters.each do |association|
-
has_many_association = _reflect_on_association(association.to_sym)
-
raise ArgumentError, "'#{self.name}' has no association called '#{association}'" unless has_many_association
-
-
if has_many_association.is_a? ActiveRecord::Reflection::ThroughReflection
-
has_many_association = has_many_association.through_reflection
-
end
-
-
foreign_key = has_many_association.foreign_key.to_s
-
child_class = has_many_association.klass
-
reflection = child_class._reflections.values.find { |e| :belongs_to == e.macro && e.foreign_key.to_s == foreign_key && e.options[:counter_cache].present? }
-
counter_name = reflection.counter_cache_column
-
-
stmt = unscoped.where(arel_table[primary_key].eq(object.id)).arel.compile_update({
-
arel_table[counter_name] => object.send(association).count(:all)
-
}, primary_key)
-
connection.update stmt
-
end
-
return true
-
end
-
-
# A generic "counter updater" implementation, intended primarily to be
-
# used by increment_counter and decrement_counter, but which may also
-
# be useful on its own. It simply does a direct SQL update for the record
-
# with the given ID, altering the given hash of counters by the amount
-
# given by the corresponding value:
-
#
-
# ==== Parameters
-
#
-
# * +id+ - The id of the object you wish to update a counter on or an Array of ids.
-
# * +counters+ - A Hash containing the names of the fields
-
# to update as keys and the amount to update the field by as values.
-
#
-
# ==== Examples
-
#
-
# # For the Post with id of 5, decrement the comment_count by 1, and
-
# # increment the action_count by 1
-
# Post.update_counters 5, comment_count: -1, action_count: 1
-
# # Executes the following SQL:
-
# # UPDATE posts
-
# # SET comment_count = COALESCE(comment_count, 0) - 1,
-
# # action_count = COALESCE(action_count, 0) + 1
-
# # WHERE id = 5
-
#
-
# # For the Posts with id of 10 and 15, increment the comment_count by 1
-
# Post.update_counters [10, 15], comment_count: 1
-
# # Executes the following SQL:
-
# # UPDATE posts
-
# # SET comment_count = COALESCE(comment_count, 0) + 1
-
# # WHERE id IN (10, 15)
-
1
def update_counters(id, counters)
-
updates = counters.map do |counter_name, value|
-
operator = value < 0 ? '-' : '+'
-
quoted_column = connection.quote_column_name(counter_name)
-
"#{quoted_column} = COALESCE(#{quoted_column}, 0) #{operator} #{value.abs}"
-
end
-
-
unscoped.where(primary_key => id).update_all updates.join(', ')
-
end
-
-
# Increment a numeric field by one, via a direct SQL update.
-
#
-
# This method is used primarily for maintaining counter_cache columns that are
-
# used to store aggregate values. For example, a DiscussionBoard may cache
-
# posts_count and comments_count to avoid running an SQL query to calculate the
-
# number of posts and comments there are, each time it is displayed.
-
#
-
# ==== Parameters
-
#
-
# * +counter_name+ - The name of the field that should be incremented.
-
# * +id+ - The id of the object that should be incremented or an Array of ids.
-
#
-
# ==== Examples
-
#
-
# # Increment the post_count column for the record with an id of 5
-
# DiscussionBoard.increment_counter(:post_count, 5)
-
1
def increment_counter(counter_name, id)
-
update_counters(id, counter_name => 1)
-
end
-
-
# Decrement a numeric field by one, via a direct SQL update.
-
#
-
# This works the same as increment_counter but reduces the column value by
-
# 1 instead of increasing it.
-
#
-
# ==== Parameters
-
#
-
# * +counter_name+ - The name of the field that should be decremented.
-
# * +id+ - The id of the object that should be decremented or an Array of ids.
-
#
-
# ==== Examples
-
#
-
# # Decrement the post_count column for the record with an id of 5
-
# DiscussionBoard.decrement_counter(:post_count, 5)
-
1
def decrement_counter(counter_name, id)
-
update_counters(id, counter_name => -1)
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
module DynamicMatchers #:nodoc:
-
# This code in this file seems to have a lot of indirection, but the indirection
-
# is there to provide extension points for the activerecord-deprecated_finders
-
# gem. When we stop supporting activerecord-deprecated_finders (from Rails 5),
-
# then we can remove the indirection.
-
-
1
def respond_to?(name, include_private = false)
-
190
if self == Base
-
1
super
-
else
-
189
match = Method.match(self, name)
-
189
match && match.valid? || super
-
end
-
end
-
-
1
private
-
-
1
def method_missing(name, *arguments, &block)
-
match = Method.match(self, name)
-
-
if match && match.valid?
-
match.define
-
send(name, *arguments, &block)
-
else
-
super
-
end
-
end
-
-
1
class Method
-
1
@matchers = []
-
-
1
class << self
-
1
attr_reader :matchers
-
-
1
def match(model, name)
-
567
klass = matchers.find { |k| name =~ k.pattern }
-
189
klass.new(model, name) if klass
-
end
-
-
1
def pattern
-
378
@pattern ||= /\A#{prefix}_([_a-zA-Z]\w*)#{suffix}\Z/
-
end
-
-
1
def prefix
-
raise NotImplementedError
-
end
-
-
1
def suffix
-
1
''
-
end
-
end
-
-
1
attr_reader :model, :name, :attribute_names
-
-
1
def initialize(model, name)
-
@model = model
-
@name = name.to_s
-
@attribute_names = @name.match(self.class.pattern)[1].split('_and_')
-
@attribute_names.map! { |n| @model.attribute_aliases[n] || n }
-
end
-
-
1
def valid?
-
attribute_names.all? { |name| model.columns_hash[name] || model.reflect_on_aggregation(name.to_sym) }
-
end
-
-
1
def define
-
model.class_eval <<-CODE, __FILE__, __LINE__ + 1
-
def self.#{name}(#{signature})
-
#{body}
-
end
-
CODE
-
end
-
-
1
def body
-
raise NotImplementedError
-
end
-
end
-
-
1
module Finder
-
# Extended in activerecord-deprecated_finders
-
1
def body
-
result
-
end
-
-
# Extended in activerecord-deprecated_finders
-
1
def result
-
"#{finder}(#{attributes_hash})"
-
end
-
-
# The parameters in the signature may have reserved Ruby words, in order
-
# to prevent errors, we start each param name with `_`.
-
#
-
# Extended in activerecord-deprecated_finders
-
1
def signature
-
attribute_names.map { |name| "_#{name}" }.join(', ')
-
end
-
-
# Given that the parameters starts with `_`, the finder needs to use the
-
# same parameter name.
-
1
def attributes_hash
-
"{" + attribute_names.map { |name| ":#{name} => _#{name}" }.join(',') + "}"
-
end
-
-
1
def finder
-
raise NotImplementedError
-
end
-
end
-
-
1
class FindBy < Method
-
1
Method.matchers << self
-
1
include Finder
-
-
1
def self.prefix
-
1
"find_by"
-
end
-
-
1
def finder
-
"find_by"
-
end
-
end
-
-
1
class FindByBang < Method
-
1
Method.matchers << self
-
1
include Finder
-
-
1
def self.prefix
-
1
"find_by"
-
end
-
-
1
def self.suffix
-
1
"!"
-
end
-
-
1
def finder
-
"find_by!"
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/object/deep_dup'
-
-
1
module ActiveRecord
-
# Declare an enum attribute where the values map to integers in the database,
-
# but can be queried by name. Example:
-
#
-
# class Conversation < ActiveRecord::Base
-
# enum status: [ :active, :archived ]
-
# end
-
#
-
# # conversation.update! status: 0
-
# conversation.active!
-
# conversation.active? # => true
-
# conversation.status # => "active"
-
#
-
# # conversation.update! status: 1
-
# conversation.archived!
-
# conversation.archived? # => true
-
# conversation.status # => "archived"
-
#
-
# # conversation.update! status: 1
-
# conversation.status = "archived"
-
#
-
# # conversation.update! status: nil
-
# conversation.status = nil
-
# conversation.status.nil? # => true
-
# conversation.status # => nil
-
#
-
# Scopes based on the allowed values of the enum field will be provided
-
# as well. With the above example, it will create an +active+ and +archived+
-
# scope.
-
#
-
# You can set the default value from the database declaration, like:
-
#
-
# create_table :conversations do |t|
-
# t.column :status, :integer, default: 0
-
# end
-
#
-
# Good practice is to let the first declared status be the default.
-
#
-
# Finally, it's also possible to explicitly map the relation between attribute and
-
# database integer with a +Hash+:
-
#
-
# class Conversation < ActiveRecord::Base
-
# enum status: { active: 0, archived: 1 }
-
# end
-
#
-
# Note that when an +Array+ is used, the implicit mapping from the values to database
-
# integers is derived from the order the values appear in the array. In the example,
-
# <tt>:active</tt> is mapped to +0+ as it's the first element, and <tt>:archived</tt>
-
# is mapped to +1+. In general, the +i+-th element is mapped to <tt>i-1</tt> in the
-
# database.
-
#
-
# Therefore, once a value is added to the enum array, its position in the array must
-
# be maintained, and new values should only be added to the end of the array. To
-
# remove unused values, the explicit +Hash+ syntax should be used.
-
#
-
# In rare circumstances you might need to access the mapping directly.
-
# The mappings are exposed through a class method with the pluralized attribute
-
# name:
-
#
-
# Conversation.statuses # => { "active" => 0, "archived" => 1 }
-
#
-
# Use that class method when you need to know the ordinal value of an enum:
-
#
-
# Conversation.where("status <> ?", Conversation.statuses[:archived])
-
#
-
# Where conditions on an enum attribute must use the ordinal value of an enum.
-
1
module Enum
-
1
def self.extended(base) # :nodoc:
-
1
base.class_attribute(:defined_enums)
-
1
base.defined_enums = {}
-
end
-
-
1
def inherited(base) # :nodoc:
-
5
base.defined_enums = defined_enums.deep_dup
-
5
super
-
end
-
-
1
def enum(definitions)
-
klass = self
-
definitions.each do |name, values|
-
# statuses = { }
-
enum_values = ActiveSupport::HashWithIndifferentAccess.new
-
name = name.to_sym
-
-
# def self.statuses statuses end
-
detect_enum_conflict!(name, name.to_s.pluralize, true)
-
klass.singleton_class.send(:define_method, name.to_s.pluralize) { enum_values }
-
-
_enum_methods_module.module_eval do
-
# def status=(value) self[:status] = statuses[value] end
-
klass.send(:detect_enum_conflict!, name, "#{name}=")
-
define_method("#{name}=") { |value|
-
if enum_values.has_key?(value) || value.blank?
-
self[name] = enum_values[value]
-
elsif enum_values.has_value?(value)
-
# Assigning a value directly is not a end-user feature, hence it's not documented.
-
# This is used internally to make building objects from the generated scopes work
-
# as expected, i.e. +Conversation.archived.build.archived?+ should be true.
-
self[name] = value
-
else
-
raise ArgumentError, "'#{value}' is not a valid #{name}"
-
end
-
}
-
-
# def status() statuses.key self[:status] end
-
klass.send(:detect_enum_conflict!, name, name)
-
define_method(name) { enum_values.key self[name] }
-
-
# def status_before_type_cast() statuses.key self[:status] end
-
klass.send(:detect_enum_conflict!, name, "#{name}_before_type_cast")
-
define_method("#{name}_before_type_cast") { enum_values.key self[name] }
-
-
pairs = values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index
-
pairs.each do |value, i|
-
enum_values[value] = i
-
-
# def active?() status == 0 end
-
klass.send(:detect_enum_conflict!, name, "#{value}?")
-
define_method("#{value}?") { self[name] == i }
-
-
# def active!() update! status: :active end
-
klass.send(:detect_enum_conflict!, name, "#{value}!")
-
define_method("#{value}!") { update! name => value }
-
-
# scope :active, -> { where status: 0 }
-
klass.send(:detect_enum_conflict!, name, value, true)
-
klass.scope value, -> { klass.where name => i }
-
end
-
end
-
defined_enums[name.to_s] = enum_values
-
end
-
end
-
-
1
private
-
1
def _enum_methods_module
-
@_enum_methods_module ||= begin
-
mod = Module.new do
-
private
-
def save_changed_attribute(attr_name, value)
-
if (mapping = self.class.defined_enums[attr_name.to_s])
-
if attribute_changed?(attr_name)
-
old = changed_attributes[attr_name]
-
-
if mapping[old] == value
-
changed_attributes.delete(attr_name)
-
end
-
else
-
old = clone_attribute_value(:read_attribute, attr_name)
-
-
if old != value
-
changed_attributes[attr_name] = mapping.key old
-
end
-
end
-
else
-
super
-
end
-
end
-
end
-
include mod
-
mod
-
end
-
end
-
-
1
ENUM_CONFLICT_MESSAGE = \
-
"You tried to define an enum named \"%{enum}\" on the model \"%{klass}\", but " \
-
"this will generate a %{type} method \"%{method}\", which is already defined " \
-
"by %{source}."
-
-
1
def detect_enum_conflict!(enum_name, method_name, klass_method = false)
-
if klass_method && dangerous_class_method?(method_name)
-
raise ArgumentError, ENUM_CONFLICT_MESSAGE % {
-
enum: enum_name,
-
klass: self.name,
-
type: 'class',
-
method: method_name,
-
source: 'Active Record'
-
}
-
elsif !klass_method && dangerous_attribute_method?(method_name)
-
raise ArgumentError, ENUM_CONFLICT_MESSAGE % {
-
enum: enum_name,
-
klass: self.name,
-
type: 'instance',
-
method: method_name,
-
source: 'Active Record'
-
}
-
elsif !klass_method && method_defined_within?(method_name, _enum_methods_module, Module)
-
raise ArgumentError, ENUM_CONFLICT_MESSAGE % {
-
enum: enum_name,
-
klass: self.name,
-
type: 'instance',
-
method: method_name,
-
source: 'another enum'
-
}
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
-
# = Active Record Errors
-
#
-
# Generic Active Record exception class.
-
1
class ActiveRecordError < StandardError
-
end
-
-
# Raised when the single-table inheritance mechanism fails to locate the subclass
-
# (for example due to improper usage of column that +inheritance_column+ points to).
-
1
class SubclassNotFound < ActiveRecordError #:nodoc:
-
end
-
-
# Raised when an object assigned to an association has an incorrect type.
-
#
-
# class Ticket < ActiveRecord::Base
-
# has_many :patches
-
# end
-
#
-
# class Patch < ActiveRecord::Base
-
# belongs_to :ticket
-
# end
-
#
-
# # Comments are not patches, this assignment raises AssociationTypeMismatch.
-
# @ticket.patches << Comment.new(content: "Please attach tests to your patch.")
-
1
class AssociationTypeMismatch < ActiveRecordError
-
end
-
-
# Raised when unserialized object's type mismatches one specified for serializable field.
-
1
class SerializationTypeMismatch < ActiveRecordError
-
end
-
-
# Raised when adapter not specified on connection (or configuration file <tt>config/database.yml</tt>
-
# misses adapter field).
-
1
class AdapterNotSpecified < ActiveRecordError
-
end
-
-
# Raised when Active Record cannot find database adapter specified in <tt>config/database.yml</tt> or programmatically.
-
1
class AdapterNotFound < ActiveRecordError
-
end
-
-
# Raised when connection to the database could not been established (for example when <tt>connection=</tt>
-
# is given a nil object).
-
1
class ConnectionNotEstablished < ActiveRecordError
-
end
-
-
# Raised when Active Record cannot find record by given id or set of ids.
-
1
class RecordNotFound < ActiveRecordError
-
end
-
-
# Raised by ActiveRecord::Base.save! and ActiveRecord::Base.create! methods when record cannot be
-
# saved because record is invalid.
-
1
class RecordNotSaved < ActiveRecordError
-
end
-
-
# Raised by ActiveRecord::Base.destroy! when a call to destroy would return false.
-
1
class RecordNotDestroyed < ActiveRecordError
-
end
-
-
# Superclass for all database execution errors.
-
#
-
# Wraps the underlying database error as +original_exception+.
-
1
class StatementInvalid < ActiveRecordError
-
1
attr_reader :original_exception
-
-
1
def initialize(message, original_exception = nil)
-
super(message)
-
@original_exception = original_exception
-
end
-
end
-
-
# Defunct wrapper class kept for compatibility.
-
# +StatementInvalid+ wraps the original exception now.
-
1
class WrappedDatabaseException < StatementInvalid
-
end
-
-
# Raised when a record cannot be inserted because it would violate a uniqueness constraint.
-
1
class RecordNotUnique < WrappedDatabaseException
-
end
-
-
# Raised when a record cannot be inserted or updated because it references a non-existent record.
-
1
class InvalidForeignKey < WrappedDatabaseException
-
end
-
-
# Raised when number of bind variables in statement given to <tt>:condition</tt> key (for example,
-
# when using +find+ method)
-
# does not match number of expected variables.
-
#
-
# For example, in
-
#
-
# Location.where("lat = ? AND lng = ?", 53.7362)
-
#
-
# two placeholders are given but only one variable to fill them.
-
1
class PreparedStatementInvalid < ActiveRecordError
-
end
-
-
# Raised when a given database does not exist
-
1
class NoDatabaseError < ActiveRecordError
-
1
def initialize(message)
-
super extend_message(message)
-
end
-
-
# can be over written to add additional error information.
-
1
def extend_message(message)
-
message
-
end
-
end
-
-
# Raised on attempt to save stale record. Record is stale when it's being saved in another query after
-
# instantiation, for example, when two users edit the same wiki page and one starts editing and saves
-
# the page before the other.
-
#
-
# Read more about optimistic locking in ActiveRecord::Locking module RDoc.
-
1
class StaleObjectError < ActiveRecordError
-
1
attr_reader :record, :attempted_action
-
-
1
def initialize(record, attempted_action)
-
super("Attempted to #{attempted_action} a stale object: #{record.class.name}")
-
@record = record
-
@attempted_action = attempted_action
-
end
-
-
end
-
-
# Raised when association is being configured improperly or
-
# user tries to use offset and limit together with has_many or has_and_belongs_to_many associations.
-
1
class ConfigurationError < ActiveRecordError
-
end
-
-
# Raised on attempt to update record that is instantiated as read only.
-
1
class ReadOnlyRecord < ActiveRecordError
-
end
-
-
# ActiveRecord::Transactions::ClassMethods.transaction uses this exception
-
# to distinguish a deliberate rollback from other exceptional situations.
-
# Normally, raising an exception will cause the +transaction+ method to rollback
-
# the database transaction *and* pass on the exception. But if you raise an
-
# ActiveRecord::Rollback exception, then the database transaction will be rolled back,
-
# without passing on the exception.
-
#
-
# For example, you could do this in your controller to rollback a transaction:
-
#
-
# class BooksController < ActionController::Base
-
# def create
-
# Book.transaction do
-
# book = Book.new(params[:book])
-
# book.save!
-
# if today_is_friday?
-
# # The system must fail on Friday so that our support department
-
# # won't be out of job. We silently rollback this transaction
-
# # without telling the user.
-
# raise ActiveRecord::Rollback, "Call tech support!"
-
# end
-
# end
-
# # ActiveRecord::Rollback is the only exception that won't be passed on
-
# # by ActiveRecord::Base.transaction, so this line will still be reached
-
# # even on Friday.
-
# redirect_to root_url
-
# end
-
# end
-
1
class Rollback < ActiveRecordError
-
end
-
-
# Raised when attribute has a name reserved by Active Record (when attribute has name of one of Active Record instance methods).
-
1
class DangerousAttributeError < ActiveRecordError
-
end
-
-
# Raised when unknown attributes are supplied via mass assignment.
-
1
class UnknownAttributeError < NoMethodError
-
-
1
attr_reader :record, :attribute
-
-
1
def initialize(record, attribute)
-
@record = record
-
@attribute = attribute.to_s
-
super("unknown attribute: #{attribute}")
-
end
-
-
end
-
-
# Raised when an error occurred while doing a mass assignment to an attribute through the
-
# <tt>attributes=</tt> method. The exception has an +attribute+ property that is the name of the
-
# offending attribute.
-
1
class AttributeAssignmentError < ActiveRecordError
-
1
attr_reader :exception, :attribute
-
1
def initialize(message, exception, attribute)
-
super(message)
-
@exception = exception
-
@attribute = attribute
-
end
-
end
-
-
# Raised when there are multiple errors while doing a mass assignment through the +attributes+
-
# method. The exception has an +errors+ property that contains an array of AttributeAssignmentError
-
# objects, each corresponding to the error while assigning to an attribute.
-
1
class MultiparameterAssignmentErrors < ActiveRecordError
-
1
attr_reader :errors
-
1
def initialize(errors)
-
@errors = errors
-
end
-
end
-
-
# Raised when a primary key is needed, but not specified in the schema or model.
-
1
class UnknownPrimaryKey < ActiveRecordError
-
1
attr_reader :model
-
-
1
def initialize(model)
-
super("Unknown primary key for table #{model.table_name} in model #{model}.")
-
@model = model
-
end
-
-
end
-
-
# Raised when a relation cannot be mutated because it's already loaded.
-
#
-
# class Task < ActiveRecord::Base
-
# end
-
#
-
# relation = Task.all
-
# relation.loaded? # => true
-
#
-
# # Methods which try to mutate a loaded relation fail.
-
# relation.where!(title: 'TODO') # => ActiveRecord::ImmutableRelation
-
# relation.limit!(5) # => ActiveRecord::ImmutableRelation
-
1
class ImmutableRelation < ActiveRecordError
-
end
-
-
1
class TransactionIsolationError < ActiveRecordError
-
end
-
end
-
1
require 'active_support/lazy_load_hooks'
-
1
require 'active_record/explain_registry'
-
-
1
module ActiveRecord
-
1
module Explain
-
# Executes the block with the collect flag enabled. Queries are collected
-
# asynchronously by the subscriber and returned.
-
1
def collecting_queries_for_explain # :nodoc:
-
ExplainRegistry.collect = true
-
yield
-
ExplainRegistry.queries
-
ensure
-
ExplainRegistry.reset
-
end
-
-
# Makes the adapter execute EXPLAIN for the tuples of queries and bindings.
-
# Returns a formatted string ready to be logged.
-
1
def exec_explain(queries) # :nodoc:
-
str = queries.map do |sql, bind|
-
[].tap do |msg|
-
msg << "EXPLAIN for: #{sql}"
-
unless bind.empty?
-
bind_msg = bind.map {|col, val| [col.name, val]}.inspect
-
msg.last << " #{bind_msg}"
-
end
-
msg << connection.explain(sql, bind)
-
end.join("\n")
-
end.join("\n")
-
-
# Overriding inspect to be more human readable, specially in the console.
-
def str.inspect
-
self
-
end
-
-
str
-
end
-
end
-
end
-
1
require 'active_support/per_thread_registry'
-
-
1
module ActiveRecord
-
# This is a thread locals registry for EXPLAIN. For example
-
#
-
# ActiveRecord::ExplainRegistry.queries
-
#
-
# returns the collected queries local to the current thread.
-
#
-
# See the documentation of <tt>ActiveSupport::PerThreadRegistry</tt>
-
# for further details.
-
1
class ExplainRegistry # :nodoc:
-
1
extend ActiveSupport::PerThreadRegistry
-
-
1
attr_accessor :queries, :collect
-
-
1
def initialize
-
1
reset
-
end
-
-
1
def collect?
-
54
@collect
-
end
-
-
1
def reset
-
1
@collect = false
-
1
@queries = []
-
end
-
end
-
end
-
1
require 'active_support/notifications'
-
1
require 'active_record/explain_registry'
-
-
1
module ActiveRecord
-
1
class ExplainSubscriber # :nodoc:
-
1
def start(name, id, payload)
-
# unused
-
end
-
-
1
def finish(name, id, payload)
-
54
if ExplainRegistry.collect? && !ignore_payload?(payload)
-
ExplainRegistry.queries << payload.values_at(:sql, :binds)
-
end
-
end
-
-
# SCHEMA queries cannot be EXPLAINed, also we do not want to run EXPLAIN on
-
# our own EXPLAINs now matter how loopingly beautiful that would be.
-
#
-
# On the other hand, we want to monitor the performance of our real database
-
# queries, not the performance of the access to the query cache.
-
1
IGNORED_PAYLOADS = %w(SCHEMA EXPLAIN CACHE)
-
1
EXPLAINED_SQLS = /\A\s*(select|update|delete|insert)\b/i
-
1
def ignore_payload?(payload)
-
payload[:exception] || IGNORED_PAYLOADS.include?(payload[:name]) || payload[:sql] !~ EXPLAINED_SQLS
-
end
-
-
1
ActiveSupport::Notifications.subscribe("sql.active_record", new)
-
end
-
end
-
1
require 'erb'
-
1
require 'yaml'
-
-
1
module ActiveRecord
-
1
class FixtureSet
-
1
class File # :nodoc:
-
1
include Enumerable
-
-
##
-
# Open a fixture file named +file+. When called with a block, the block
-
# is called with the filehandle and the filehandle is automatically closed
-
# when the block finishes.
-
1
def self.open(file)
-
x = new file
-
block_given? ? yield(x) : x
-
end
-
-
1
def initialize(file)
-
@file = file
-
@rows = nil
-
end
-
-
1
def each(&block)
-
rows.each(&block)
-
end
-
-
-
1
private
-
1
def rows
-
return @rows if @rows
-
-
begin
-
data = YAML.load(render(IO.read(@file)))
-
rescue ArgumentError, Psych::SyntaxError => error
-
raise Fixture::FormatError, "a YAML error occurred parsing #{@file}. Please note that YAML must be consistently indented using spaces. Tabs are not allowed. Please have a look at http://www.yaml.org/faq.html\nThe exact error was:\n #{error.class}: #{error}", error.backtrace
-
end
-
@rows = data ? validate(data).to_a : []
-
end
-
-
1
def render(content)
-
context = ActiveRecord::FixtureSet::RenderContext.create_subclass.new
-
ERB.new(content).result(context.get_binding)
-
end
-
-
# Validate our unmarshalled data.
-
1
def validate(data)
-
unless Hash === data || YAML::Omap === data
-
raise Fixture::FormatError, 'fixture is not a hash'
-
end
-
-
raise Fixture::FormatError unless data.all? { |name, row| Hash === row }
-
data
-
end
-
end
-
end
-
end
-
1
require 'erb'
-
1
require 'yaml'
-
1
require 'zlib'
-
1
require 'active_support/dependencies'
-
1
require 'active_record/fixture_set/file'
-
1
require 'active_record/errors'
-
-
1
module ActiveRecord
-
1
class FixtureClassNotFound < ActiveRecord::ActiveRecordError #:nodoc:
-
end
-
-
# \Fixtures are a way of organizing data that you want to test against; in short, sample data.
-
#
-
# They are stored in YAML files, one file per model, which are placed in the directory
-
# appointed by <tt>ActiveSupport::TestCase.fixture_path=(path)</tt> (this is automatically
-
# configured for Rails, so you can just put your files in <tt><your-rails-app>/test/fixtures/</tt>).
-
# The fixture file ends with the <tt>.yml</tt> file extension (Rails example:
-
# <tt><your-rails-app>/test/fixtures/web_sites.yml</tt>). The format of a fixture file looks
-
# like this:
-
#
-
# rubyonrails:
-
# id: 1
-
# name: Ruby on Rails
-
# url: http://www.rubyonrails.org
-
#
-
# google:
-
# id: 2
-
# name: Google
-
# url: http://www.google.com
-
#
-
# This fixture file includes two fixtures. Each YAML fixture (ie. record) is given a name and
-
# is followed by an indented list of key/value pairs in the "key: value" format. Records are
-
# separated by a blank line for your viewing pleasure.
-
#
-
# Note that fixtures are unordered. If you want ordered fixtures, use the omap YAML type.
-
# See http://yaml.org/type/omap.html
-
# for the specification. You will need ordered fixtures when you have foreign key constraints
-
# on keys in the same table. This is commonly needed for tree structures. Example:
-
#
-
# --- !omap
-
# - parent:
-
# id: 1
-
# parent_id: NULL
-
# title: Parent
-
# - child:
-
# id: 2
-
# parent_id: 1
-
# title: Child
-
#
-
# = Using Fixtures in Test Cases
-
#
-
# Since fixtures are a testing construct, we use them in our unit and functional tests. There
-
# are two ways to use the fixtures, but first let's take a look at a sample unit test:
-
#
-
# require 'test_helper'
-
#
-
# class WebSiteTest < ActiveSupport::TestCase
-
# test "web_site_count" do
-
# assert_equal 2, WebSite.count
-
# end
-
# end
-
#
-
# By default, <tt>test_helper.rb</tt> will load all of your fixtures into your test database,
-
# so this test will succeed.
-
#
-
# The testing environment will automatically load the all fixtures into the database before each
-
# test. To ensure consistent data, the environment deletes the fixtures before running the load.
-
#
-
# In addition to being available in the database, the fixture's data may also be accessed by
-
# using a special dynamic method, which has the same name as the model, and accepts the
-
# name of the fixture to instantiate:
-
#
-
# test "find" do
-
# assert_equal "Ruby on Rails", web_sites(:rubyonrails).name
-
# end
-
#
-
# Alternatively, you may enable auto-instantiation of the fixture data. For instance, take the
-
# following tests:
-
#
-
# test "find_alt_method_1" do
-
# assert_equal "Ruby on Rails", @web_sites['rubyonrails']['name']
-
# end
-
#
-
# test "find_alt_method_2" do
-
# assert_equal "Ruby on Rails", @rubyonrails.name
-
# end
-
#
-
# In order to use these methods to access fixtured data within your testcases, you must specify one of the
-
# following in your <tt>ActiveSupport::TestCase</tt>-derived class:
-
#
-
# - to fully enable instantiated fixtures (enable alternate methods #1 and #2 above)
-
# self.use_instantiated_fixtures = true
-
#
-
# - create only the hash for the fixtures, do not 'find' each instance (enable alternate method #1 only)
-
# self.use_instantiated_fixtures = :no_instances
-
#
-
# Using either of these alternate methods incurs a performance hit, as the fixtured data must be fully
-
# traversed in the database to create the fixture hash and/or instance variables. This is expensive for
-
# large sets of fixtured data.
-
#
-
# = Dynamic fixtures with ERB
-
#
-
# Some times you don't care about the content of the fixtures as much as you care about the volume.
-
# In these cases, you can mix ERB in with your YAML fixtures to create a bunch of fixtures for load
-
# testing, like:
-
#
-
# <% 1.upto(1000) do |i| %>
-
# fix_<%= i %>:
-
# id: <%= i %>
-
# name: guy_<%= 1 %>
-
# <% end %>
-
#
-
# This will create 1000 very simple fixtures.
-
#
-
# Using ERB, you can also inject dynamic values into your fixtures with inserts like
-
# <tt><%= Date.today.strftime("%Y-%m-%d") %></tt>.
-
# This is however a feature to be used with some caution. The point of fixtures are that they're
-
# stable units of predictable sample data. If you feel that you need to inject dynamic values, then
-
# perhaps you should reexamine whether your application is properly testable. Hence, dynamic values
-
# in fixtures are to be considered a code smell.
-
#
-
# Helper methods defined in a fixture will not be available in other fixtures, to prevent against
-
# unwanted inter-test dependencies. Methods used by multiple fixtures should be defined in a module
-
# that is included in <tt>ActiveRecord::FixtureSet.context_class</tt>.
-
#
-
# - define a helper method in `test_helper.rb`
-
# class FixtureFileHelpers
-
# def file_sha(path)
-
# Digest::SHA2.hexdigest(File.read(Rails.root.join('test/fixtures', path)))
-
# end
-
# end
-
# ActiveRecord::FixtureSet.context_class.send :include, FixtureFileHelpers
-
#
-
# - use the helper method in a fixture
-
# photo:
-
# name: kitten.png
-
# sha: <%= file_sha 'files/kitten.png' %>
-
#
-
# = Transactional Fixtures
-
#
-
# Test cases can use begin+rollback to isolate their changes to the database instead of having to
-
# delete+insert for every test case.
-
#
-
# class FooTest < ActiveSupport::TestCase
-
# self.use_transactional_fixtures = true
-
#
-
# test "godzilla" do
-
# assert !Foo.all.empty?
-
# Foo.destroy_all
-
# assert Foo.all.empty?
-
# end
-
#
-
# test "godzilla aftermath" do
-
# assert !Foo.all.empty?
-
# end
-
# end
-
#
-
# If you preload your test database with all fixture data (probably in the rake task) and use
-
# transactional fixtures, then you may omit all fixtures declarations in your test cases since
-
# all the data's already there and every case rolls back its changes.
-
#
-
# In order to use instantiated fixtures with preloaded data, set +self.pre_loaded_fixtures+ to
-
# true. This will provide access to fixture data for every table that has been loaded through
-
# fixtures (depending on the value of +use_instantiated_fixtures+).
-
#
-
# When *not* to use transactional fixtures:
-
#
-
# 1. You're testing whether a transaction works correctly. Nested transactions don't commit until
-
# all parent transactions commit, particularly, the fixtures transaction which is begun in setup
-
# and rolled back in teardown. Thus, you won't be able to verify
-
# the results of your transaction until Active Record supports nested transactions or savepoints (in progress).
-
# 2. Your database does not support transactions. Every Active Record database supports transactions except MySQL MyISAM.
-
# Use InnoDB, MaxDB, or NDB instead.
-
#
-
# = Advanced Fixtures
-
#
-
# Fixtures that don't specify an ID get some extra features:
-
#
-
# * Stable, autogenerated IDs
-
# * Label references for associations (belongs_to, has_one, has_many)
-
# * HABTM associations as inline lists
-
# * Autofilled timestamp columns
-
# * Fixture label interpolation
-
# * Support for YAML defaults
-
#
-
# == Stable, Autogenerated IDs
-
#
-
# Here, have a monkey fixture:
-
#
-
# george:
-
# id: 1
-
# name: George the Monkey
-
#
-
# reginald:
-
# id: 2
-
# name: Reginald the Pirate
-
#
-
# Each of these fixtures has two unique identifiers: one for the database
-
# and one for the humans. Why don't we generate the primary key instead?
-
# Hashing each fixture's label yields a consistent ID:
-
#
-
# george: # generated id: 503576764
-
# name: George the Monkey
-
#
-
# reginald: # generated id: 324201669
-
# name: Reginald the Pirate
-
#
-
# Active Record looks at the fixture's model class, discovers the correct
-
# primary key, and generates it right before inserting the fixture
-
# into the database.
-
#
-
# The generated ID for a given label is constant, so we can discover
-
# any fixture's ID without loading anything, as long as we know the label.
-
#
-
# == Label references for associations (belongs_to, has_one, has_many)
-
#
-
# Specifying foreign keys in fixtures can be very fragile, not to
-
# mention difficult to read. Since Active Record can figure out the ID of
-
# any fixture from its label, you can specify FK's by label instead of ID.
-
#
-
# === belongs_to
-
#
-
# Let's break out some more monkeys and pirates.
-
#
-
# ### in pirates.yml
-
#
-
# reginald:
-
# id: 1
-
# name: Reginald the Pirate
-
# monkey_id: 1
-
#
-
# ### in monkeys.yml
-
#
-
# george:
-
# id: 1
-
# name: George the Monkey
-
# pirate_id: 1
-
#
-
# Add a few more monkeys and pirates and break this into multiple files,
-
# and it gets pretty hard to keep track of what's going on. Let's
-
# use labels instead of IDs:
-
#
-
# ### in pirates.yml
-
#
-
# reginald:
-
# name: Reginald the Pirate
-
# monkey: george
-
#
-
# ### in monkeys.yml
-
#
-
# george:
-
# name: George the Monkey
-
# pirate: reginald
-
#
-
# Pow! All is made clear. Active Record reflects on the fixture's model class,
-
# finds all the +belongs_to+ associations, and allows you to specify
-
# a target *label* for the *association* (monkey: george) rather than
-
# a target *id* for the *FK* (<tt>monkey_id: 1</tt>).
-
#
-
# ==== Polymorphic belongs_to
-
#
-
# Supporting polymorphic relationships is a little bit more complicated, since
-
# Active Record needs to know what type your association is pointing at. Something
-
# like this should look familiar:
-
#
-
# ### in fruit.rb
-
#
-
# belongs_to :eater, polymorphic: true
-
#
-
# ### in fruits.yml
-
#
-
# apple:
-
# id: 1
-
# name: apple
-
# eater_id: 1
-
# eater_type: Monkey
-
#
-
# Can we do better? You bet!
-
#
-
# apple:
-
# eater: george (Monkey)
-
#
-
# Just provide the polymorphic target type and Active Record will take care of the rest.
-
#
-
# === has_and_belongs_to_many
-
#
-
# Time to give our monkey some fruit.
-
#
-
# ### in monkeys.yml
-
#
-
# george:
-
# id: 1
-
# name: George the Monkey
-
#
-
# ### in fruits.yml
-
#
-
# apple:
-
# id: 1
-
# name: apple
-
#
-
# orange:
-
# id: 2
-
# name: orange
-
#
-
# grape:
-
# id: 3
-
# name: grape
-
#
-
# ### in fruits_monkeys.yml
-
#
-
# apple_george:
-
# fruit_id: 1
-
# monkey_id: 1
-
#
-
# orange_george:
-
# fruit_id: 2
-
# monkey_id: 1
-
#
-
# grape_george:
-
# fruit_id: 3
-
# monkey_id: 1
-
#
-
# Let's make the HABTM fixture go away.
-
#
-
# ### in monkeys.yml
-
#
-
# george:
-
# id: 1
-
# name: George the Monkey
-
# fruits: apple, orange, grape
-
#
-
# ### in fruits.yml
-
#
-
# apple:
-
# name: apple
-
#
-
# orange:
-
# name: orange
-
#
-
# grape:
-
# name: grape
-
#
-
# Zap! No more fruits_monkeys.yml file. We've specified the list of fruits
-
# on George's fixture, but we could've just as easily specified a list
-
# of monkeys on each fruit. As with +belongs_to+, Active Record reflects on
-
# the fixture's model class and discovers the +has_and_belongs_to_many+
-
# associations.
-
#
-
# == Autofilled Timestamp Columns
-
#
-
# If your table/model specifies any of Active Record's
-
# standard timestamp columns (+created_at+, +created_on+, +updated_at+, +updated_on+),
-
# they will automatically be set to <tt>Time.now</tt>.
-
#
-
# If you've set specific values, they'll be left alone.
-
#
-
# == Fixture label interpolation
-
#
-
# The label of the current fixture is always available as a column value:
-
#
-
# geeksomnia:
-
# name: Geeksomnia's Account
-
# subdomain: $LABEL
-
#
-
# Also, sometimes (like when porting older join table fixtures) you'll need
-
# to be able to get a hold of the identifier for a given label. ERB
-
# to the rescue:
-
#
-
# george_reginald:
-
# monkey_id: <%= ActiveRecord::FixtureSet.identify(:reginald) %>
-
# pirate_id: <%= ActiveRecord::FixtureSet.identify(:george) %>
-
#
-
# == Support for YAML defaults
-
#
-
# You probably already know how to use YAML to set and reuse defaults in
-
# your <tt>database.yml</tt> file. You can use the same technique in your fixtures:
-
#
-
# DEFAULTS: &DEFAULTS
-
# created_on: <%= 3.weeks.ago.to_s(:db) %>
-
#
-
# first:
-
# name: Smurf
-
# <<: *DEFAULTS
-
#
-
# second:
-
# name: Fraggle
-
# <<: *DEFAULTS
-
#
-
# Any fixture labeled "DEFAULTS" is safely ignored.
-
1
class FixtureSet
-
#--
-
# An instance of FixtureSet is normally stored in a single YAML file and possibly in a folder with the same name.
-
#++
-
-
1
MAX_ID = 2 ** 30 - 1
-
-
2
@@all_cached_fixtures = Hash.new { |h,k| h[k] = {} }
-
-
1
def self.default_fixture_model_name(fixture_set_name, config = ActiveRecord::Base) # :nodoc:
-
config.pluralize_table_names ?
-
fixture_set_name.singularize.camelize :
-
fixture_set_name.camelize
-
end
-
-
1
def self.default_fixture_table_name(fixture_set_name, config = ActiveRecord::Base) # :nodoc:
-
"#{ config.table_name_prefix }"\
-
"#{ fixture_set_name.tr('/', '_') }"\
-
"#{ config.table_name_suffix }".to_sym
-
end
-
-
1
def self.reset_cache
-
@@all_cached_fixtures.clear
-
end
-
-
1
def self.cache_for_connection(connection)
-
2
@@all_cached_fixtures[connection]
-
end
-
-
1
def self.fixture_is_cached?(connection, table_name)
-
cache_for_connection(connection)[table_name]
-
end
-
-
1
def self.cached_fixtures(connection, keys_to_fetch = nil)
-
2
if keys_to_fetch
-
2
cache_for_connection(connection).values_at(*keys_to_fetch)
-
else
-
cache_for_connection(connection).values
-
end
-
end
-
-
1
def self.cache_fixtures(connection, fixtures_map)
-
cache_for_connection(connection).update(fixtures_map)
-
end
-
-
1
def self.instantiate_fixtures(object, fixture_set, load_instances = true)
-
if load_instances
-
fixture_set.each do |fixture_name, fixture|
-
begin
-
object.instance_variable_set "@#{fixture_name}", fixture.find
-
rescue FixtureClassNotFound
-
nil
-
end
-
end
-
end
-
end
-
-
1
def self.instantiate_all_loaded_fixtures(object, load_instances = true)
-
all_loaded_fixtures.each_value do |fixture_set|
-
instantiate_fixtures(object, fixture_set, load_instances)
-
end
-
end
-
-
1
cattr_accessor :all_loaded_fixtures
-
1
self.all_loaded_fixtures = {}
-
-
1
class ClassCache
-
1
def initialize(class_names, config)
-
2
@class_names = class_names.stringify_keys
-
2
@config = config
-
-
# Remove string values that aren't constants or subclasses of AR
-
2
@class_names.delete_if { |k,klass|
-
unless klass.is_a? Class
-
klass = klass.safe_constantize
-
ActiveSupport::Deprecation.warn("The ability to pass in strings as a class name to `set_fixture_class` will be removed in Rails 4.2. Use the class itself instead.")
-
end
-
!insert_class(@class_names, k, klass)
-
}
-
end
-
-
1
def [](fs_name)
-
@class_names.fetch(fs_name) {
-
klass = default_fixture_model(fs_name, @config).safe_constantize
-
insert_class(@class_names, fs_name, klass)
-
}
-
end
-
-
1
private
-
-
1
def insert_class(class_names, name, klass)
-
# We only want to deal with AR objects.
-
if klass && klass < ActiveRecord::Base
-
class_names[name] = klass
-
else
-
class_names[name] = nil
-
end
-
end
-
-
1
def default_fixture_model(fs_name, config)
-
ActiveRecord::FixtureSet.default_fixture_model_name(fs_name, config)
-
end
-
end
-
-
1
def self.create_fixtures(fixtures_directory, fixture_set_names, class_names = {}, config = ActiveRecord::Base)
-
2
fixture_set_names = Array(fixture_set_names).map(&:to_s)
-
2
class_names = ClassCache.new class_names, config
-
-
# FIXME: Apparently JK uses this.
-
2
connection = block_given? ? yield : ActiveRecord::Base.connection
-
-
2
files_to_read = fixture_set_names.reject { |fs_name|
-
fixture_is_cached?(connection, fs_name)
-
}
-
-
2
unless files_to_read.empty?
-
connection.disable_referential_integrity do
-
fixtures_map = {}
-
-
fixture_sets = files_to_read.map do |fs_name|
-
klass = class_names[fs_name]
-
conn = klass ? klass.connection : connection
-
fixtures_map[fs_name] = new( # ActiveRecord::FixtureSet.new
-
conn,
-
fs_name,
-
klass,
-
::File.join(fixtures_directory, fs_name))
-
end
-
-
all_loaded_fixtures.update(fixtures_map)
-
-
connection.transaction(:requires_new => true) do
-
fixture_sets.each do |fs|
-
conn = fs.model_class.respond_to?(:connection) ? fs.model_class.connection : connection
-
table_rows = fs.table_rows
-
-
table_rows.keys.each do |table|
-
conn.delete "DELETE FROM #{conn.quote_table_name(table)}", 'Fixture Delete'
-
end
-
-
table_rows.each do |fixture_set_name, rows|
-
rows.each do |row|
-
conn.insert_fixture(row, fixture_set_name)
-
end
-
end
-
end
-
-
# Cap primary key sequences to max(pk).
-
if connection.respond_to?(:reset_pk_sequence!)
-
fixture_sets.each do |fs|
-
connection.reset_pk_sequence!(fs.table_name)
-
end
-
end
-
end
-
-
cache_fixtures(connection, fixtures_map)
-
end
-
end
-
2
cached_fixtures(connection, fixture_set_names)
-
end
-
-
# Returns a consistent, platform-independent identifier for +label+.
-
# Identifiers are positive integers less than 2^32.
-
1
def self.identify(label)
-
Zlib.crc32(label.to_s) % MAX_ID
-
end
-
-
# Superclass for the evaluation contexts used by ERB fixtures.
-
1
def self.context_class
-
@context_class ||= Class.new
-
end
-
-
1
attr_reader :table_name, :name, :fixtures, :model_class, :config
-
-
1
def initialize(connection, name, class_name, path, config = ActiveRecord::Base)
-
@name = name
-
@path = path
-
@config = config
-
@model_class = nil
-
-
if class_name.is_a?(String)
-
ActiveSupport::Deprecation.warn("The ability to pass in strings as a class name to `FixtureSet.new` will be removed in Rails 4.2. Use the class itself instead.")
-
end
-
-
if class_name.is_a?(Class) # TODO: Should be an AR::Base type class, or any?
-
@model_class = class_name
-
else
-
@model_class = class_name.safe_constantize if class_name
-
end
-
-
@connection = connection
-
-
@table_name = ( model_class.respond_to?(:table_name) ?
-
model_class.table_name :
-
self.class.default_fixture_table_name(name, config) )
-
-
@fixtures = read_fixture_files path, @model_class
-
end
-
-
1
def [](x)
-
fixtures[x]
-
end
-
-
1
def []=(k,v)
-
fixtures[k] = v
-
end
-
-
1
def each(&block)
-
fixtures.each(&block)
-
end
-
-
1
def size
-
fixtures.size
-
end
-
-
# Returns a hash of rows to be inserted. The key is the table, the value is
-
# a list of rows to insert to that table.
-
1
def table_rows
-
now = config.default_timezone == :utc ? Time.now.utc : Time.now
-
now = now.to_s(:db)
-
-
# allow a standard key to be used for doing defaults in YAML
-
fixtures.delete('DEFAULTS')
-
-
# track any join tables we need to insert later
-
rows = Hash.new { |h,table| h[table] = [] }
-
-
rows[table_name] = fixtures.map do |label, fixture|
-
row = fixture.to_hash
-
-
if model_class
-
# fill in timestamp columns if they aren't specified and the model is set to record_timestamps
-
if model_class.record_timestamps
-
timestamp_column_names.each do |c_name|
-
row[c_name] = now unless row.key?(c_name)
-
end
-
end
-
-
# interpolate the fixture label
-
row.each do |key, value|
-
row[key] = label if "$LABEL" == value
-
end
-
-
# generate a primary key if necessary
-
if has_primary_key_column? && !row.include?(primary_key_name)
-
row[primary_key_name] = ActiveRecord::FixtureSet.identify(label)
-
end
-
-
# If STI is used, find the correct subclass for association reflection
-
reflection_class =
-
if row.include?(inheritance_column_name)
-
row[inheritance_column_name].constantize rescue model_class
-
else
-
model_class
-
end
-
-
reflection_class._reflections.values.each do |association|
-
case association.macro
-
when :belongs_to
-
# Do not replace association name with association foreign key if they are named the same
-
fk_name = (association.options[:foreign_key] || "#{association.name}_id").to_s
-
-
if association.name.to_s != fk_name && value = row.delete(association.name.to_s)
-
if association.options[:polymorphic] && value.sub!(/\s*\(([^\)]*)\)\s*$/, "")
-
# support polymorphic belongs_to as "label (Type)"
-
row[association.foreign_type] = $1
-
end
-
-
row[fk_name] = ActiveRecord::FixtureSet.identify(value)
-
end
-
when :has_many
-
if association.options[:through]
-
add_join_records(rows, row, HasManyThroughProxy.new(association))
-
end
-
end
-
end
-
end
-
-
row
-
end
-
rows
-
end
-
-
1
class ReflectionProxy # :nodoc:
-
1
def initialize(association)
-
@association = association
-
end
-
-
1
def join_table
-
@association.join_table
-
end
-
-
1
def name
-
@association.name
-
end
-
end
-
-
1
class HasManyThroughProxy < ReflectionProxy # :nodoc:
-
1
def rhs_key
-
@association.foreign_key
-
end
-
-
1
def lhs_key
-
@association.through_reflection.foreign_key
-
end
-
end
-
-
1
private
-
1
def primary_key_name
-
@primary_key_name ||= model_class && model_class.primary_key
-
end
-
-
1
def add_join_records(rows, row, association)
-
# This is the case when the join table has no fixtures file
-
if (targets = row.delete(association.name.to_s))
-
table_name = association.join_table
-
lhs_key = association.lhs_key
-
rhs_key = association.rhs_key
-
-
targets = targets.is_a?(Array) ? targets : targets.split(/\s*,\s*/)
-
rows[table_name].concat targets.map { |target|
-
{ lhs_key => row[primary_key_name],
-
rhs_key => ActiveRecord::FixtureSet.identify(target) }
-
}
-
end
-
end
-
-
1
def has_primary_key_column?
-
@has_primary_key_column ||= primary_key_name &&
-
model_class.columns.any? { |c| c.name == primary_key_name }
-
end
-
-
1
def timestamp_column_names
-
@timestamp_column_names ||=
-
%w(created_at created_on updated_at updated_on) & column_names
-
end
-
-
1
def inheritance_column_name
-
@inheritance_column_name ||= model_class && model_class.inheritance_column
-
end
-
-
1
def column_names
-
@column_names ||= @connection.columns(@table_name).collect { |c| c.name }
-
end
-
-
1
def read_fixture_files(path, model_class)
-
yaml_files = Dir["#{path}/{**,*}/*.yml"].select { |f|
-
::File.file?(f)
-
} + [yaml_file_path(path)]
-
-
yaml_files.each_with_object({}) do |file, fixtures|
-
FixtureSet::File.open(file) do |fh|
-
fh.each do |fixture_name, row|
-
fixtures[fixture_name] = ActiveRecord::Fixture.new(row, model_class)
-
end
-
end
-
end
-
end
-
-
1
def yaml_file_path(path)
-
"#{path}.yml"
-
end
-
-
end
-
-
#--
-
# Deprecate 'Fixtures' in favor of 'FixtureSet'.
-
#++
-
# :nodoc:
-
1
Fixtures = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('ActiveRecord::Fixtures', 'ActiveRecord::FixtureSet')
-
-
1
class Fixture #:nodoc:
-
1
include Enumerable
-
-
1
class FixtureError < StandardError #:nodoc:
-
end
-
-
1
class FormatError < FixtureError #:nodoc:
-
end
-
-
1
attr_reader :model_class, :fixture
-
-
1
def initialize(fixture, model_class)
-
@fixture = fixture
-
@model_class = model_class
-
end
-
-
1
def class_name
-
model_class.name if model_class
-
end
-
-
1
def each
-
fixture.each { |item| yield item }
-
end
-
-
1
def [](key)
-
fixture[key]
-
end
-
-
1
alias :to_hash :fixture
-
-
1
def find
-
if model_class
-
model_class.find(fixture[model_class.primary_key])
-
else
-
raise FixtureClassNotFound, "No class attached to find."
-
end
-
end
-
end
-
end
-
-
1
module ActiveRecord
-
1
module TestFixtures
-
1
extend ActiveSupport::Concern
-
-
1
def before_setup
-
4
setup_fixtures
-
4
super
-
end
-
-
1
def after_teardown
-
4
super
-
4
teardown_fixtures
-
end
-
-
1
included do
-
2
class_attribute :fixture_path, :instance_writer => false
-
2
class_attribute :fixture_table_names
-
2
class_attribute :fixture_class_names
-
2
class_attribute :use_transactional_fixtures
-
2
class_attribute :use_instantiated_fixtures # true, false, or :no_instances
-
2
class_attribute :pre_loaded_fixtures
-
2
class_attribute :config
-
-
2
self.fixture_table_names = []
-
2
self.use_transactional_fixtures = true
-
2
self.use_instantiated_fixtures = false
-
2
self.pre_loaded_fixtures = false
-
2
self.config = ActiveRecord::Base
-
-
2
self.fixture_class_names = Hash.new do |h, fixture_set_name|
-
h[fixture_set_name] = ActiveRecord::FixtureSet.default_fixture_model_name(fixture_set_name, self.config)
-
end
-
end
-
-
1
module ClassMethods
-
# Sets the model class for a fixture when the class name cannot be inferred from the fixture name.
-
#
-
# Examples:
-
#
-
# set_fixture_class some_fixture: SomeModel,
-
# 'namespaced/fixture' => Another::Model
-
#
-
# The keys must be the fixture names, that coincide with the short paths to the fixture files.
-
1
def set_fixture_class(class_names = {})
-
self.fixture_class_names = self.fixture_class_names.merge(class_names.stringify_keys)
-
end
-
-
1
def fixtures(*fixture_set_names)
-
if fixture_set_names.first == :all
-
fixture_set_names = Dir["#{fixture_path}/{**,*}/*.{yml}"]
-
fixture_set_names.map! { |f| f[(fixture_path.to_s.size + 1)..-5] }
-
else
-
fixture_set_names = fixture_set_names.flatten.map { |n| n.to_s }
-
end
-
-
self.fixture_table_names |= fixture_set_names
-
require_fixture_classes(fixture_set_names, self.config)
-
setup_fixture_accessors(fixture_set_names)
-
end
-
-
1
def try_to_load_dependency(file_name)
-
require_dependency file_name
-
rescue LoadError => e
-
# Let's hope the developer has included it
-
# Let's warn in case this is a subdependency, otherwise
-
# subdependency error messages are totally cryptic
-
if ActiveRecord::Base.logger
-
ActiveRecord::Base.logger.warn("Unable to load #{file_name}, underlying cause #{e.message} \n\n #{e.backtrace.join("\n")}")
-
end
-
end
-
-
1
def require_fixture_classes(fixture_set_names = nil, config = ActiveRecord::Base)
-
if fixture_set_names
-
fixture_set_names = fixture_set_names.map { |n| n.to_s }
-
else
-
fixture_set_names = fixture_table_names
-
end
-
-
fixture_set_names.each do |file_name|
-
file_name = file_name.singularize if config.pluralize_table_names
-
try_to_load_dependency(file_name)
-
end
-
end
-
-
1
def setup_fixture_accessors(fixture_set_names = nil)
-
fixture_set_names = Array(fixture_set_names || fixture_table_names)
-
methods = Module.new do
-
fixture_set_names.each do |fs_name|
-
fs_name = fs_name.to_s
-
accessor_name = fs_name.tr('/', '_').to_sym
-
-
define_method(accessor_name) do |*fixture_names|
-
force_reload = fixture_names.pop if fixture_names.last == true || fixture_names.last == :reload
-
-
@fixture_cache[fs_name] ||= {}
-
-
instances = fixture_names.map do |f_name|
-
f_name = f_name.to_s
-
@fixture_cache[fs_name].delete(f_name) if force_reload
-
-
if @loaded_fixtures[fs_name][f_name]
-
@fixture_cache[fs_name][f_name] ||= @loaded_fixtures[fs_name][f_name].find
-
else
-
raise StandardError, "No fixture named '#{f_name}' found for fixture set '#{fs_name}'"
-
end
-
end
-
-
instances.size == 1 ? instances.first : instances
-
end
-
private accessor_name
-
end
-
end
-
include methods
-
end
-
-
1
def uses_transaction(*methods)
-
@uses_transaction = [] unless defined?(@uses_transaction)
-
@uses_transaction.concat methods.map { |m| m.to_s }
-
end
-
-
1
def uses_transaction?(method)
-
8
@uses_transaction = [] unless defined?(@uses_transaction)
-
8
@uses_transaction.include?(method.to_s)
-
end
-
end
-
-
1
def run_in_transaction?
-
use_transactional_fixtures &&
-
8
!self.class.uses_transaction?(method_name)
-
end
-
-
1
def setup_fixtures(config = ActiveRecord::Base)
-
4
if pre_loaded_fixtures && !use_transactional_fixtures
-
raise RuntimeError, 'pre_loaded_fixtures requires use_transactional_fixtures'
-
end
-
-
4
@fixture_cache = {}
-
4
@fixture_connections = []
-
4
@@already_loaded_fixtures ||= {}
-
-
# Load fixtures once and begin transaction.
-
4
if run_in_transaction?
-
4
if @@already_loaded_fixtures[self.class]
-
2
@loaded_fixtures = @@already_loaded_fixtures[self.class]
-
else
-
2
@loaded_fixtures = load_fixtures(config)
-
2
@@already_loaded_fixtures[self.class] = @loaded_fixtures
-
end
-
4
@fixture_connections = enlist_fixture_connections
-
4
@fixture_connections.each do |connection|
-
4
connection.begin_transaction joinable: false
-
end
-
# Load fixtures for every test.
-
else
-
ActiveRecord::FixtureSet.reset_cache
-
@@already_loaded_fixtures[self.class] = nil
-
@loaded_fixtures = load_fixtures(config)
-
end
-
-
# Instantiate fixtures for every test if requested.
-
4
instantiate_fixtures(config) if use_instantiated_fixtures
-
end
-
-
1
def teardown_fixtures
-
# Rollback changes if a transaction is active.
-
4
if run_in_transaction?
-
4
@fixture_connections.each do |connection|
-
4
connection.rollback_transaction if connection.transaction_open?
-
end
-
4
@fixture_connections.clear
-
else
-
ActiveRecord::FixtureSet.reset_cache
-
end
-
-
4
ActiveRecord::Base.clear_active_connections!
-
end
-
-
1
def enlist_fixture_connections
-
4
ActiveRecord::Base.connection_handler.connection_pool_list.map(&:connection)
-
end
-
-
1
private
-
1
def load_fixtures(config)
-
2
fixtures = ActiveRecord::FixtureSet.create_fixtures(fixture_path, fixture_table_names, fixture_class_names, config)
-
2
Hash[fixtures.map { |f| [f.name, f] }]
-
end
-
-
# for pre_loaded_fixtures, only require the classes once. huge speed improvement
-
1
@@required_fixture_classes = false
-
-
1
def instantiate_fixtures(config)
-
if pre_loaded_fixtures
-
raise RuntimeError, 'Load fixtures before instantiating them.' if ActiveRecord::FixtureSet.all_loaded_fixtures.empty?
-
unless @@required_fixture_classes
-
self.class.require_fixture_classes ActiveRecord::FixtureSet.all_loaded_fixtures.keys, config
-
@@required_fixture_classes = true
-
end
-
ActiveRecord::FixtureSet.instantiate_all_loaded_fixtures(self, load_instances?)
-
else
-
raise RuntimeError, 'Load fixtures before instantiating them.' if @loaded_fixtures.nil?
-
@loaded_fixtures.each_value do |fixture_set|
-
ActiveRecord::FixtureSet.instantiate_fixtures(self, fixture_set, load_instances?)
-
end
-
end
-
end
-
-
1
def load_instances?
-
use_instantiated_fixtures != :no_instances
-
end
-
end
-
end
-
-
1
class ActiveRecord::FixtureSet::RenderContext # :nodoc:
-
1
def self.create_subclass
-
Class.new ActiveRecord::FixtureSet.context_class do
-
def get_binding
-
binding()
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
# Returns the version of the currently loaded ActiveRecord as a <tt>Gem::Version</tt>
-
1
def self.gem_version
-
Gem::Version.new VERSION::STRING
-
end
-
-
1
module VERSION
-
1
MAJOR = 4
-
1
MINOR = 1
-
1
TINY = 6
-
1
PRE = nil
-
-
1
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
-
end
-
end
-
1
require 'active_support/core_ext/hash/indifferent_access'
-
-
1
module ActiveRecord
-
1
module Inheritance
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
# Determines whether to store the full constant name including namespace when using STI.
-
1
class_attribute :store_full_sti_class, instance_writer: false
-
1
self.store_full_sti_class = true
-
end
-
-
1
module ClassMethods
-
# Determines if one of the attributes passed in is the inheritance column,
-
# and if the inheritance column is attr accessible, it initializes an
-
# instance of the given subclass instead of the base class.
-
1
def new(*args, &block)
-
8
if abstract_class? || self == Base
-
raise NotImplementedError, "#{self} is an abstract class and cannot be instantiated."
-
end
-
-
8
attrs = args.first
-
8
if subclass_from_attributes?(attrs)
-
subclass = subclass_from_attributes(attrs)
-
end
-
-
8
if subclass
-
subclass.new(*args, &block)
-
else
-
8
super
-
end
-
end
-
-
# Returns +true+ if this does not need STI type condition. Returns
-
# +false+ if STI type condition needs to be applied.
-
1
def descends_from_active_record?
-
5
if self == Base
-
false
-
5
elsif superclass.abstract_class?
-
superclass.descends_from_active_record?
-
else
-
5
superclass == Base || !columns_hash.include?(inheritance_column)
-
end
-
end
-
-
1
def finder_needs_type_condition? #:nodoc:
-
# This is like this because benchmarking justifies the strange :false stuff
-
80
:true == (@finder_needs_type_condition ||= descends_from_active_record? ? :false : :true)
-
end
-
-
1
def symbolized_base_class
-
ActiveSupport::Deprecation.warn("ActiveRecord::Base.symbolized_base_class is deprecated and will be removed without replacement.")
-
@symbolized_base_class ||= base_class.to_s.to_sym
-
end
-
-
1
def symbolized_sti_name
-
ActiveSupport::Deprecation.warn("ActiveRecord::Base.symbolized_sti_name is deprecated and will be removed without replacement.")
-
@symbolized_sti_name ||= sti_name.present? ? sti_name.to_sym : symbolized_base_class
-
end
-
-
# Returns the class descending directly from ActiveRecord::Base, or
-
# an abstract class, if any, in the inheritance hierarchy.
-
#
-
# If A extends AR::Base, A.base_class will return A. If B descends from A
-
# through some arbitrarily deep hierarchy, B.base_class will return A.
-
#
-
# If B < A and C < B and if A is an abstract_class then both B.base_class
-
# and C.base_class would return B as the answer since A is an abstract_class.
-
1
def base_class
-
51
unless self < Base
-
raise ActiveRecordError, "#{name} doesn't belong in a hierarchy descending from ActiveRecord"
-
end
-
-
51
if superclass == Base || superclass.abstract_class?
-
51
self
-
else
-
superclass.base_class
-
end
-
end
-
-
# Set this to true if this is an abstract class (see <tt>abstract_class?</tt>).
-
# If you are using inheritance with ActiveRecord and don't want child classes
-
# to utilize the implied STI table name of the parent class, this will need to be true.
-
# For example, given the following:
-
#
-
# class SuperClass < ActiveRecord::Base
-
# self.abstract_class = true
-
# end
-
# class Child < SuperClass
-
# self.table_name = 'the_table_i_really_want'
-
# end
-
#
-
#
-
# <tt>self.abstract_class = true</tt> is required to make <tt>Child<.find,.create, or any Arel method></tt> use <tt>the_table_i_really_want</tt> instead of a table called <tt>super_classes</tt>
-
#
-
1
attr_accessor :abstract_class
-
-
# Returns whether this class is an abstract class or not.
-
1
def abstract_class?
-
21
defined?(@abstract_class) && @abstract_class == true
-
end
-
-
1
def sti_name
-
store_full_sti_class ? name : name.demodulize
-
end
-
-
1
protected
-
-
# Returns the class type of the record using the current module as a prefix. So descendants of
-
# MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.
-
1
def compute_type(type_name)
-
7
if type_name.match(/^::/)
-
# If the type is prefixed with a scope operator then we assume that
-
# the type_name is an absolute reference.
-
ActiveSupport::Dependencies.constantize(type_name)
-
else
-
# Build a list of candidates to search for
-
7
candidates = []
-
14
name.scan(/::|$/) { candidates.unshift "#{$`}::#{type_name}" }
-
7
candidates << type_name
-
-
7
candidates.each do |candidate|
-
14
begin
-
14
constant = ActiveSupport::Dependencies.constantize(candidate)
-
7
return constant if candidate == constant.to_s
-
# We don't want to swallow NoMethodError < NameError errors
-
rescue NoMethodError
-
raise
-
rescue NameError
-
end
-
end
-
-
raise NameError.new("uninitialized constant #{candidates.first}", candidates.first)
-
end
-
end
-
-
1
private
-
-
# Called by +instantiate+ to decide which class to use for a new
-
# record instance. For single-table inheritance, we check the record
-
# for a +type+ column and return the corresponding class.
-
1
def discriminate_class_for_record(record)
-
16
if using_single_table_inheritance?(record)
-
find_sti_class(record[inheritance_column])
-
else
-
16
super
-
end
-
end
-
-
1
def using_single_table_inheritance?(record)
-
16
record[inheritance_column].present? && columns_hash.include?(inheritance_column)
-
end
-
-
1
def find_sti_class(type_name)
-
if store_full_sti_class
-
ActiveSupport::Dependencies.constantize(type_name)
-
else
-
compute_type(type_name)
-
end
-
rescue NameError
-
raise SubclassNotFound,
-
"The single-table inheritance mechanism failed to locate the subclass: '#{type_name}'. " +
-
"This error is raised because the column '#{inheritance_column}' is reserved for storing the class in case of inheritance. " +
-
"Please rename this column if you didn't intend it to be used for storing the inheritance class " +
-
"or overwrite #{name}.inheritance_column to use another column for that information."
-
end
-
-
1
def type_condition(table = arel_table)
-
sti_column = table[inheritance_column]
-
sti_names = ([self] + descendants).map { |model| model.sti_name }
-
-
sti_column.in(sti_names)
-
end
-
-
# Detect the subclass from the inheritance column of attrs. If the inheritance column value
-
# is not self or a valid subclass, raises ActiveRecord::SubclassNotFound
-
# If this is a StrongParameters hash, and access to inheritance_column is not permitted,
-
# this will ignore the inheritance column and return nil
-
1
def subclass_from_attributes?(attrs)
-
8
columns_hash.include?(inheritance_column) && attrs.is_a?(Hash)
-
end
-
-
1
def subclass_from_attributes(attrs)
-
subclass_name = attrs.with_indifferent_access[inheritance_column]
-
-
if subclass_name.present? && subclass_name != self.name
-
subclass = subclass_name.safe_constantize
-
-
unless descendants.include?(subclass)
-
raise ActiveRecord::SubclassNotFound.new("Invalid single-table inheritance type: #{subclass_name} is not a subclass of #{name}")
-
end
-
-
subclass
-
end
-
end
-
end
-
-
1
def initialize_dup(other)
-
super
-
ensure_proper_type
-
end
-
-
1
private
-
-
1
def initialize_internals_callback
-
8
super
-
8
ensure_proper_type
-
end
-
-
# Sets the attribute used for single table inheritance to this class name if this is not the
-
# ActiveRecord::Base descendant.
-
# Considering the hierarchy Reply < Message < ActiveRecord::Base, this makes it possible to
-
# do Reply.new without having to set <tt>Reply[Reply.inheritance_column] = "Reply"</tt> yourself.
-
# No such attribute would be set for objects of the Message class in that example.
-
1
def ensure_proper_type
-
8
klass = self.class
-
8
if klass.finder_needs_type_condition?
-
write_attribute(klass.inheritance_column, klass.sti_name)
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/string/filters'
-
-
1
module ActiveRecord
-
1
module Integration
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
##
-
# :singleton-method:
-
# Indicates the format used to generate the timestamp in the cache key.
-
# Accepts any of the symbols in <tt>Time::DATE_FORMATS</tt>.
-
#
-
# This is +:nsec+, by default.
-
1
class_attribute :cache_timestamp_format, :instance_writer => false
-
1
self.cache_timestamp_format = :nsec
-
end
-
-
# Returns a String, which Action Pack uses for constructing an URL to this
-
# object. The default implementation returns this record's id as a String,
-
# or nil if this record's unsaved.
-
#
-
# For example, suppose that you have a User model, and that you have a
-
# <tt>resources :users</tt> route. Normally, +user_path+ will
-
# construct a path with the user object's 'id' in it:
-
#
-
# user = User.find_by(name: 'Phusion')
-
# user_path(user) # => "/users/1"
-
#
-
# You can override +to_param+ in your model to make +user_path+ construct
-
# a path using the user's name instead of the user's id:
-
#
-
# class User < ActiveRecord::Base
-
# def to_param # overridden
-
# name
-
# end
-
# end
-
#
-
# user = User.find_by(name: 'Phusion')
-
# user_path(user) # => "/users/Phusion"
-
1
def to_param
-
# We can't use alias_method here, because method 'id' optimizes itself on the fly.
-
2
id && id.to_s # Be sure to stringify the id for routes
-
end
-
-
# Returns a cache key that can be used to identify this record.
-
#
-
# Product.new.cache_key # => "products/new"
-
# Product.find(5).cache_key # => "products/5" (updated_at not available)
-
# Person.find(5).cache_key # => "people/5-20071224150000" (updated_at available)
-
#
-
# You can also pass a list of named timestamps, and the newest in the list will be
-
# used to generate the key:
-
#
-
# Person.find(5).cache_key(:updated_at, :last_reviewed_at)
-
1
def cache_key(*timestamp_names)
-
case
-
when new_record?
-
"#{self.class.model_name.cache_key}/new"
-
when timestamp_names.any?
-
timestamp = max_updated_column_timestamp(timestamp_names)
-
timestamp = timestamp.utc.to_s(cache_timestamp_format)
-
"#{self.class.model_name.cache_key}/#{id}-#{timestamp}"
-
when timestamp = max_updated_column_timestamp
-
timestamp = timestamp.utc.to_s(cache_timestamp_format)
-
"#{self.class.model_name.cache_key}/#{id}-#{timestamp}"
-
else
-
"#{self.class.model_name.cache_key}/#{id}"
-
end
-
end
-
-
1
module ClassMethods
-
# Defines your model's +to_param+ method to generate "pretty" URLs
-
# using +method_name+, which can be any attribute or method that
-
# responds to +to_s+.
-
#
-
# class User < ActiveRecord::Base
-
# to_param :name
-
# end
-
#
-
# user = User.find_by(name: 'Fancy Pants')
-
# user.id # => 123
-
# user_path(user) # => "/users/123-fancy-pants"
-
#
-
# Values longer than 20 characters will be truncated. The value
-
# is truncated word by word.
-
#
-
# user = User.find_by(name: 'David HeinemeierHansson')
-
# user.id # => 125
-
# user_path(user) # => "/users/125-david"
-
#
-
# Because the generated param begins with the record's +id+, it is
-
# suitable for passing to +find+. In a controller, for example:
-
#
-
# params[:id] # => "123-fancy-pants"
-
# User.find(params[:id]).id # => 123
-
1
def to_param(method_name = nil)
-
if method_name.nil?
-
super()
-
else
-
define_method :to_param do
-
if (default = super()) &&
-
(result = send(method_name).to_s).present? &&
-
(param = result.squish.truncate(20, separator: /\s/, omission: nil).parameterize).present?
-
"#{default}-#{param}"
-
else
-
default
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
module Locking
-
# == What is Optimistic Locking
-
#
-
# Optimistic locking allows multiple users to access the same record for edits, and assumes a minimum of
-
# conflicts with the data. It does this by checking whether another process has made changes to a record since
-
# it was opened, an <tt>ActiveRecord::StaleObjectError</tt> exception is thrown if that has occurred
-
# and the update is ignored.
-
#
-
# Check out <tt>ActiveRecord::Locking::Pessimistic</tt> for an alternative.
-
#
-
# == Usage
-
#
-
# Active Records support optimistic locking if the field +lock_version+ is present. Each update to the
-
# record increments the +lock_version+ column and the locking facilities ensure that records instantiated twice
-
# will let the last one saved raise a +StaleObjectError+ if the first was also updated. Example:
-
#
-
# p1 = Person.find(1)
-
# p2 = Person.find(1)
-
#
-
# p1.first_name = "Michael"
-
# p1.save
-
#
-
# p2.first_name = "should fail"
-
# p2.save # Raises a ActiveRecord::StaleObjectError
-
#
-
# Optimistic locking will also check for stale data when objects are destroyed. Example:
-
#
-
# p1 = Person.find(1)
-
# p2 = Person.find(1)
-
#
-
# p1.first_name = "Michael"
-
# p1.save
-
#
-
# p2.destroy # Raises a ActiveRecord::StaleObjectError
-
#
-
# You're then responsible for dealing with the conflict by rescuing the exception and either rolling back, merging,
-
# or otherwise apply the business logic needed to resolve the conflict.
-
#
-
# This locking mechanism will function inside a single Ruby process. To make it work across all
-
# web requests, the recommended approach is to add +lock_version+ as a hidden field to your form.
-
#
-
# This behavior can be turned off by setting <tt>ActiveRecord::Base.lock_optimistically = false</tt>.
-
# To override the name of the +lock_version+ column, set the <tt>locking_column</tt> class attribute:
-
#
-
# class Person < ActiveRecord::Base
-
# self.locking_column = :lock_person
-
# end
-
#
-
1
module Optimistic
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
class_attribute :lock_optimistically, instance_writer: false
-
1
self.lock_optimistically = true
-
end
-
-
1
def locking_enabled? #:nodoc:
-
2
self.class.locking_enabled?
-
end
-
-
1
private
-
1
def increment_lock
-
lock_col = self.class.locking_column
-
previous_lock_value = send(lock_col).to_i
-
send(lock_col + '=', previous_lock_value + 1)
-
end
-
-
1
def _update_record(attribute_names = @attributes.keys) #:nodoc:
-
2
return super unless locking_enabled?
-
return 0 if attribute_names.empty?
-
-
lock_col = self.class.locking_column
-
previous_lock_value = send(lock_col).to_i
-
increment_lock
-
-
attribute_names += [lock_col]
-
attribute_names.uniq!
-
-
begin
-
relation = self.class.unscoped
-
-
stmt = relation.where(
-
relation.table[self.class.primary_key].eq(id).and(
-
relation.table[lock_col].eq(self.class.quote_value(previous_lock_value, column_for_attribute(lock_col)))
-
)
-
).arel.compile_update(
-
arel_attributes_with_values_for_update(attribute_names),
-
self.class.primary_key
-
)
-
-
affected_rows = self.class.connection.update stmt
-
-
unless affected_rows == 1
-
raise ActiveRecord::StaleObjectError.new(self, "update")
-
end
-
-
affected_rows
-
-
# If something went wrong, revert the version.
-
rescue Exception
-
send(lock_col + '=', previous_lock_value)
-
raise
-
end
-
end
-
-
1
def destroy_row
-
affected_rows = super
-
-
if locking_enabled? && affected_rows != 1
-
raise ActiveRecord::StaleObjectError.new(self, "destroy")
-
end
-
-
affected_rows
-
end
-
-
1
def relation_for_destroy
-
relation = super
-
-
if locking_enabled?
-
column_name = self.class.locking_column
-
column = self.class.columns_hash[column_name]
-
substitute = self.class.connection.substitute_at(column, relation.bind_values.length)
-
-
relation = relation.where(self.class.arel_table[column_name].eq(substitute))
-
relation.bind_values << [column, self[column_name].to_i]
-
end
-
-
relation
-
end
-
-
1
module ClassMethods
-
1
DEFAULT_LOCKING_COLUMN = 'lock_version'
-
-
# Returns true if the +lock_optimistically+ flag is set to true
-
# (which it is, by default) and the table includes the
-
# +locking_column+ column (defaults to +lock_version+).
-
1
def locking_enabled?
-
2
lock_optimistically && columns_hash[locking_column]
-
end
-
-
# Set the column to use for optimistic locking. Defaults to +lock_version+.
-
1
def locking_column=(value)
-
3
@column_defaults = nil
-
3
@locking_column = value.to_s
-
end
-
-
# The version column used for optimistic locking. Defaults to +lock_version+.
-
1
def locking_column
-
5
reset_locking_column unless defined?(@locking_column)
-
5
@locking_column
-
end
-
-
# Quote the column name used for optimistic locking.
-
1
def quoted_locking_column
-
ActiveSupport::Deprecation.warn "ActiveRecord::Base.quoted_locking_column is deprecated and will be removed in Rails 4.2 or later."
-
connection.quote_column_name(locking_column)
-
end
-
-
# Reset the column used for optimistic locking back to the +lock_version+ default.
-
1
def reset_locking_column
-
3
self.locking_column = DEFAULT_LOCKING_COLUMN
-
end
-
-
# Make sure the lock version column gets updated when counters are
-
# updated.
-
1
def update_counters(id, counters)
-
counters = counters.merge(locking_column => 1) if locking_enabled?
-
super
-
end
-
-
1
def column_defaults
-
@column_defaults ||= begin
-
3
defaults = super
-
-
3
if defaults.key?(locking_column) && lock_optimistically
-
defaults[locking_column] ||= 0
-
end
-
-
3
defaults
-
8
end
-
end
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
module Locking
-
# Locking::Pessimistic provides support for row-level locking using
-
# SELECT ... FOR UPDATE and other lock types.
-
#
-
# Chain <tt>ActiveRecord::Base#find</tt> to <tt>ActiveRecord::QueryMethods#lock</tt> to obtain an exclusive
-
# lock on the selected rows:
-
# # select * from accounts where id=1 for update
-
# Account.lock.find(1)
-
#
-
# Call <tt>lock('some locking clause')</tt> to use a database-specific locking clause
-
# of your own such as 'LOCK IN SHARE MODE' or 'FOR UPDATE NOWAIT'. Example:
-
#
-
# Account.transaction do
-
# # select * from accounts where name = 'shugo' limit 1 for update
-
# shugo = Account.where("name = 'shugo'").lock(true).first
-
# yuko = Account.where("name = 'yuko'").lock(true).first
-
# shugo.balance -= 100
-
# shugo.save!
-
# yuko.balance += 100
-
# yuko.save!
-
# end
-
#
-
# You can also use <tt>ActiveRecord::Base#lock!</tt> method to lock one record by id.
-
# This may be better if you don't need to lock every row. Example:
-
#
-
# Account.transaction do
-
# # select * from accounts where ...
-
# accounts = Account.where(...)
-
# account1 = accounts.detect { |account| ... }
-
# account2 = accounts.detect { |account| ... }
-
# # select * from accounts where id=? for update
-
# account1.lock!
-
# account2.lock!
-
# account1.balance -= 100
-
# account1.save!
-
# account2.balance += 100
-
# account2.save!
-
# end
-
#
-
# You can start a transaction and acquire the lock in one go by calling
-
# <tt>with_lock</tt> with a block. The block is called from within
-
# a transaction, the object is already locked. Example:
-
#
-
# account = Account.first
-
# account.with_lock do
-
# # This block is called within a transaction,
-
# # account is already locked.
-
# account.balance -= 100
-
# account.save!
-
# end
-
#
-
# Database-specific information on row locking:
-
# MySQL: http://dev.mysql.com/doc/refman/5.1/en/innodb-locking-reads.html
-
# PostgreSQL: http://www.postgresql.org/docs/current/interactive/sql-select.html#SQL-FOR-UPDATE-SHARE
-
1
module Pessimistic
-
# Obtain a row lock on this record. Reloads the record to obtain the requested
-
# lock. Pass an SQL locking clause to append the end of the SELECT statement
-
# or pass true for "FOR UPDATE" (the default, an exclusive row lock). Returns
-
# the locked record.
-
1
def lock!(lock = true)
-
reload(:lock => lock) if persisted?
-
self
-
end
-
-
# Wraps the passed block in a transaction, locking the object
-
# before yielding. You can pass the SQL locking clause
-
# as argument (see <tt>lock!</tt>).
-
1
def with_lock(lock = true)
-
transaction do
-
lock!(lock)
-
yield
-
end
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
class LogSubscriber < ActiveSupport::LogSubscriber
-
1
IGNORE_PAYLOAD_NAMES = ["SCHEMA", "EXPLAIN"]
-
-
1
def self.runtime=(value)
-
66
ActiveRecord::RuntimeRegistry.sql_runtime = value
-
end
-
-
1
def self.runtime
-
66
ActiveRecord::RuntimeRegistry.sql_runtime ||= 0
-
end
-
-
1
def self.reset_runtime
-
12
rt, self.runtime = runtime, 0
-
12
rt
-
end
-
-
1
def initialize
-
1
super
-
1
@odd = false
-
end
-
-
1
def render_bind(column, value)
-
28
if column
-
28
if column.binary?
-
# This specifically deals with the PG adapter that casts bytea columns into a Hash.
-
value = value[:value] if value.is_a?(Hash)
-
value = value ? "<#{value.bytesize} bytes of binary data>" : "<NULL binary data>"
-
end
-
-
28
[column.name, value]
-
else
-
[nil, value]
-
end
-
end
-
-
1
def sql(event)
-
54
self.class.runtime += event.duration
-
54
return unless logger.debug?
-
-
54
payload = event.payload
-
-
54
return if IGNORE_PAYLOAD_NAMES.include?(payload[:name])
-
-
33
name = "#{payload[:name]} (#{event.duration.round(1)}ms)"
-
33
sql = payload[:sql]
-
33
binds = nil
-
-
33
unless (payload[:binds] || []).empty?
-
10
binds = " " + payload[:binds].map { |col,v|
-
28
render_bind(col, v)
-
}.inspect
-
end
-
-
33
if odd?
-
17
name = color(name, CYAN, true)
-
17
sql = color(sql, nil, true)
-
else
-
16
name = color(name, MAGENTA, true)
-
end
-
-
33
debug " #{name} #{sql}#{binds}"
-
end
-
-
1
def odd?
-
33
@odd = !@odd
-
end
-
-
1
def logger
-
228
ActiveRecord::Base.logger
-
end
-
end
-
end
-
-
1
ActiveRecord::LogSubscriber.attach_to :active_record
-
1
require "active_support/core_ext/module/attribute_accessors"
-
1
require 'set'
-
-
1
module ActiveRecord
-
1
class MigrationError < ActiveRecordError#:nodoc:
-
1
def initialize(message = nil)
-
message = "\n\n#{message}\n\n" if message
-
super
-
end
-
end
-
-
# Exception that can be raised to stop migrations from going backwards.
-
1
class IrreversibleMigration < MigrationError
-
end
-
-
1
class DuplicateMigrationVersionError < MigrationError#:nodoc:
-
1
def initialize(version)
-
super("Multiple migrations have the version number #{version}")
-
end
-
end
-
-
1
class DuplicateMigrationNameError < MigrationError#:nodoc:
-
1
def initialize(name)
-
super("Multiple migrations have the name #{name}")
-
end
-
end
-
-
1
class UnknownMigrationVersionError < MigrationError #:nodoc:
-
1
def initialize(version)
-
super("No migration with version number #{version}")
-
end
-
end
-
-
1
class IllegalMigrationNameError < MigrationError#:nodoc:
-
1
def initialize(name)
-
super("Illegal name for migration file: #{name}\n\t(only lower case letters, numbers, and '_' allowed)")
-
end
-
end
-
-
1
class PendingMigrationError < MigrationError#:nodoc:
-
1
def initialize
-
if defined?(Rails)
-
super("Migrations are pending. To resolve this issue, run:\n\n\tbin/rake db:migrate RAILS_ENV=#{::Rails.env}")
-
else
-
super("Migrations are pending. To resolve this issue, run:\n\n\tbin/rake db:migrate")
-
end
-
end
-
end
-
-
# = Active Record Migrations
-
#
-
# Migrations can manage the evolution of a schema used by several physical
-
# databases. It's a solution to the common problem of adding a field to make
-
# a new feature work in your local database, but being unsure of how to
-
# push that change to other developers and to the production server. With
-
# migrations, you can describe the transformations in self-contained classes
-
# that can be checked into version control systems and executed against
-
# another database that might be one, two, or five versions behind.
-
#
-
# Example of a simple migration:
-
#
-
# class AddSsl < ActiveRecord::Migration
-
# def up
-
# add_column :accounts, :ssl_enabled, :boolean, default: true
-
# end
-
#
-
# def down
-
# remove_column :accounts, :ssl_enabled
-
# end
-
# end
-
#
-
# This migration will add a boolean flag to the accounts table and remove it
-
# if you're backing out of the migration. It shows how all migrations have
-
# two methods +up+ and +down+ that describes the transformations
-
# required to implement or remove the migration. These methods can consist
-
# of both the migration specific methods like +add_column+ and +remove_column+,
-
# but may also contain regular Ruby code for generating data needed for the
-
# transformations.
-
#
-
# Example of a more complex migration that also needs to initialize data:
-
#
-
# class AddSystemSettings < ActiveRecord::Migration
-
# def up
-
# create_table :system_settings do |t|
-
# t.string :name
-
# t.string :label
-
# t.text :value
-
# t.string :type
-
# t.integer :position
-
# end
-
#
-
# SystemSetting.create name: 'notice',
-
# label: 'Use notice?',
-
# value: 1
-
# end
-
#
-
# def down
-
# drop_table :system_settings
-
# end
-
# end
-
#
-
# This migration first adds the +system_settings+ table, then creates the very
-
# first row in it using the Active Record model that relies on the table. It
-
# also uses the more advanced +create_table+ syntax where you can specify a
-
# complete table schema in one block call.
-
#
-
# == Available transformations
-
#
-
# * <tt>create_table(name, options)</tt>: Creates a table called +name+ and
-
# makes the table object available to a block that can then add columns to it,
-
# following the same format as +add_column+. See example above. The options hash
-
# is for fragments like "DEFAULT CHARSET=UTF-8" that are appended to the create
-
# table definition.
-
# * <tt>drop_table(name)</tt>: Drops the table called +name+.
-
# * <tt>change_table(name, options)</tt>: Allows to make column alterations to
-
# the table called +name+. It makes the table object available to a block that
-
# can then add/remove columns, indexes or foreign keys to it.
-
# * <tt>rename_table(old_name, new_name)</tt>: Renames the table called +old_name+
-
# to +new_name+.
-
# * <tt>add_column(table_name, column_name, type, options)</tt>: Adds a new column
-
# to the table called +table_name+
-
# named +column_name+ specified to be one of the following types:
-
# <tt>:string</tt>, <tt>:text</tt>, <tt>:integer</tt>, <tt>:float</tt>,
-
# <tt>:decimal</tt>, <tt>:datetime</tt>, <tt>:timestamp</tt>, <tt>:time</tt>,
-
# <tt>:date</tt>, <tt>:binary</tt>, <tt>:boolean</tt>. A default value can be
-
# specified by passing an +options+ hash like <tt>{ default: 11 }</tt>.
-
# Other options include <tt>:limit</tt> and <tt>:null</tt> (e.g.
-
# <tt>{ limit: 50, null: false }</tt>) -- see
-
# ActiveRecord::ConnectionAdapters::TableDefinition#column for details.
-
# * <tt>rename_column(table_name, column_name, new_column_name)</tt>: Renames
-
# a column but keeps the type and content.
-
# * <tt>change_column(table_name, column_name, type, options)</tt>: Changes
-
# the column to a different type using the same parameters as add_column.
-
# * <tt>remove_column(table_name, column_name, type, options)</tt>: Removes the column
-
# named +column_name+ from the table called +table_name+.
-
# * <tt>add_index(table_name, column_names, options)</tt>: Adds a new index
-
# with the name of the column. Other options include
-
# <tt>:name</tt>, <tt>:unique</tt> (e.g.
-
# <tt>{ name: 'users_name_index', unique: true }</tt>) and <tt>:order</tt>
-
# (e.g. <tt>{ order: { name: :desc } }</tt>).
-
# * <tt>remove_index(table_name, column: column_name)</tt>: Removes the index
-
# specified by +column_name+.
-
# * <tt>remove_index(table_name, name: index_name)</tt>: Removes the index
-
# specified by +index_name+.
-
#
-
# == Irreversible transformations
-
#
-
# Some transformations are destructive in a manner that cannot be reversed.
-
# Migrations of that kind should raise an <tt>ActiveRecord::IrreversibleMigration</tt>
-
# exception in their +down+ method.
-
#
-
# == Running migrations from within Rails
-
#
-
# The Rails package has several tools to help create and apply migrations.
-
#
-
# To generate a new migration, you can use
-
# rails generate migration MyNewMigration
-
#
-
# where MyNewMigration is the name of your migration. The generator will
-
# create an empty migration file <tt>timestamp_my_new_migration.rb</tt>
-
# in the <tt>db/migrate/</tt> directory where <tt>timestamp</tt> is the
-
# UTC formatted date and time that the migration was generated.
-
#
-
# You may then edit the <tt>up</tt> and <tt>down</tt> methods of
-
# MyNewMigration.
-
#
-
# There is a special syntactic shortcut to generate migrations that add fields to a table.
-
#
-
# rails generate migration add_fieldname_to_tablename fieldname:string
-
#
-
# This will generate the file <tt>timestamp_add_fieldname_to_tablename</tt>, which will look like this:
-
# class AddFieldnameToTablename < ActiveRecord::Migration
-
# def up
-
# add_column :tablenames, :fieldname, :string
-
# end
-
#
-
# def down
-
# remove_column :tablenames, :fieldname
-
# end
-
# end
-
#
-
# To run migrations against the currently configured database, use
-
# <tt>rake db:migrate</tt>. This will update the database by running all of the
-
# pending migrations, creating the <tt>schema_migrations</tt> table
-
# (see "About the schema_migrations table" section below) if missing. It will also
-
# invoke the db:schema:dump task, which will update your db/schema.rb file
-
# to match the structure of your database.
-
#
-
# To roll the database back to a previous migration version, use
-
# <tt>rake db:migrate VERSION=X</tt> where <tt>X</tt> is the version to which
-
# you wish to downgrade. If any of the migrations throw an
-
# <tt>ActiveRecord::IrreversibleMigration</tt> exception, that step will fail and you'll
-
# have some manual work to do.
-
#
-
# == Database support
-
#
-
# Migrations are currently supported in MySQL, PostgreSQL, SQLite,
-
# SQL Server, Sybase, and Oracle (all supported databases except DB2).
-
#
-
# == More examples
-
#
-
# Not all migrations change the schema. Some just fix the data:
-
#
-
# class RemoveEmptyTags < ActiveRecord::Migration
-
# def up
-
# Tag.all.each { |tag| tag.destroy if tag.pages.empty? }
-
# end
-
#
-
# def down
-
# # not much we can do to restore deleted data
-
# raise ActiveRecord::IrreversibleMigration, "Can't recover the deleted tags"
-
# end
-
# end
-
#
-
# Others remove columns when they migrate up instead of down:
-
#
-
# class RemoveUnnecessaryItemAttributes < ActiveRecord::Migration
-
# def up
-
# remove_column :items, :incomplete_items_count
-
# remove_column :items, :completed_items_count
-
# end
-
#
-
# def down
-
# add_column :items, :incomplete_items_count
-
# add_column :items, :completed_items_count
-
# end
-
# end
-
#
-
# And sometimes you need to do something in SQL not abstracted directly by migrations:
-
#
-
# class MakeJoinUnique < ActiveRecord::Migration
-
# def up
-
# execute "ALTER TABLE `pages_linked_pages` ADD UNIQUE `page_id_linked_page_id` (`page_id`,`linked_page_id`)"
-
# end
-
#
-
# def down
-
# execute "ALTER TABLE `pages_linked_pages` DROP INDEX `page_id_linked_page_id`"
-
# end
-
# end
-
#
-
# == Using a model after changing its table
-
#
-
# Sometimes you'll want to add a column in a migration and populate it
-
# immediately after. In that case, you'll need to make a call to
-
# <tt>Base#reset_column_information</tt> in order to ensure that the model has the
-
# latest column data from after the new column was added. Example:
-
#
-
# class AddPeopleSalary < ActiveRecord::Migration
-
# def up
-
# add_column :people, :salary, :integer
-
# Person.reset_column_information
-
# Person.all.each do |p|
-
# p.update_attribute :salary, SalaryCalculator.compute(p)
-
# end
-
# end
-
# end
-
#
-
# == Controlling verbosity
-
#
-
# By default, migrations will describe the actions they are taking, writing
-
# them to the console as they happen, along with benchmarks describing how
-
# long each step took.
-
#
-
# You can quiet them down by setting ActiveRecord::Migration.verbose = false.
-
#
-
# You can also insert your own messages and benchmarks by using the +say_with_time+
-
# method:
-
#
-
# def up
-
# ...
-
# say_with_time "Updating salaries..." do
-
# Person.all.each do |p|
-
# p.update_attribute :salary, SalaryCalculator.compute(p)
-
# end
-
# end
-
# ...
-
# end
-
#
-
# The phrase "Updating salaries..." would then be printed, along with the
-
# benchmark for the block when the block completes.
-
#
-
# == About the schema_migrations table
-
#
-
# Rails versions 2.0 and prior used to create a table called
-
# <tt>schema_info</tt> when using migrations. This table contained the
-
# version of the schema as of the last applied migration.
-
#
-
# Starting with Rails 2.1, the <tt>schema_info</tt> table is
-
# (automatically) replaced by the <tt>schema_migrations</tt> table, which
-
# contains the version numbers of all the migrations applied.
-
#
-
# As a result, it is now possible to add migration files that are numbered
-
# lower than the current schema version: when migrating up, those
-
# never-applied "interleaved" migrations will be automatically applied, and
-
# when migrating down, never-applied "interleaved" migrations will be skipped.
-
#
-
# == Timestamped Migrations
-
#
-
# By default, Rails generates migrations that look like:
-
#
-
# 20080717013526_your_migration_name.rb
-
#
-
# The prefix is a generation timestamp (in UTC).
-
#
-
# If you'd prefer to use numeric prefixes, you can turn timestamped migrations
-
# off by setting:
-
#
-
# config.active_record.timestamped_migrations = false
-
#
-
# In application.rb.
-
#
-
# == Reversible Migrations
-
#
-
# Starting with Rails 3.1, you will be able to define reversible migrations.
-
# Reversible migrations are migrations that know how to go +down+ for you.
-
# You simply supply the +up+ logic, and the Migration system will figure out
-
# how to execute the down commands for you.
-
#
-
# To define a reversible migration, define the +change+ method in your
-
# migration like this:
-
#
-
# class TenderloveMigration < ActiveRecord::Migration
-
# def change
-
# create_table(:horses) do |t|
-
# t.column :content, :text
-
# t.column :remind_at, :datetime
-
# end
-
# end
-
# end
-
#
-
# This migration will create the horses table for you on the way up, and
-
# automatically figure out how to drop the table on the way down.
-
#
-
# Some commands like +remove_column+ cannot be reversed. If you care to
-
# define how to move up and down in these cases, you should define the +up+
-
# and +down+ methods as before.
-
#
-
# If a command cannot be reversed, an
-
# <tt>ActiveRecord::IrreversibleMigration</tt> exception will be raised when
-
# the migration is moving down.
-
#
-
# For a list of commands that are reversible, please see
-
# <tt>ActiveRecord::Migration::CommandRecorder</tt>.
-
#
-
# == Transactional Migrations
-
#
-
# If the database adapter supports DDL transactions, all migrations will
-
# automatically be wrapped in a transaction. There are queries that you
-
# can't execute inside a transaction though, and for these situations
-
# you can turn the automatic transactions off.
-
#
-
# class ChangeEnum < ActiveRecord::Migration
-
# disable_ddl_transaction!
-
#
-
# def up
-
# execute "ALTER TYPE model_size ADD VALUE 'new_value'"
-
# end
-
# end
-
#
-
# Remember that you can still open your own transactions, even if you
-
# are in a Migration with <tt>self.disable_ddl_transaction!</tt>.
-
1
class Migration
-
1
autoload :CommandRecorder, 'active_record/migration/command_recorder'
-
-
-
# This class is used to verify that all migrations have been run before
-
# loading a web page if config.active_record.migration_error is set to :page_load
-
1
class CheckPending
-
1
def initialize(app)
-
@app = app
-
@last_check = 0
-
end
-
-
1
def call(env)
-
mtime = ActiveRecord::Migrator.last_migration.mtime.to_i
-
if @last_check < mtime
-
ActiveRecord::Migration.check_pending!
-
@last_check = mtime
-
end
-
@app.call(env)
-
end
-
end
-
-
1
class << self
-
1
attr_accessor :delegate # :nodoc:
-
1
attr_accessor :disable_ddl_transaction # :nodoc:
-
-
1
def check_pending!(connection = Base.connection)
-
raise ActiveRecord::PendingMigrationError if ActiveRecord::Migrator.needs_migration?(connection)
-
end
-
-
1
def load_schema_if_pending!
-
1
if ActiveRecord::Migrator.needs_migration?
-
ActiveRecord::Tasks::DatabaseTasks.load_schema
-
check_pending!
-
end
-
end
-
-
1
def maintain_test_schema! # :nodoc:
-
1
if ActiveRecord::Base.maintain_test_schema
-
2
suppress_messages { load_schema_if_pending! }
-
end
-
end
-
-
1
def method_missing(name, *args, &block) # :nodoc:
-
1
(delegate || superclass.delegate).send(name, *args, &block)
-
end
-
-
1
def migrate(direction)
-
new.migrate direction
-
end
-
-
# Disable DDL transactions for this migration.
-
1
def disable_ddl_transaction!
-
@disable_ddl_transaction = true
-
end
-
end
-
-
1
def disable_ddl_transaction # :nodoc:
-
self.class.disable_ddl_transaction
-
end
-
-
1
cattr_accessor :verbose
-
1
attr_accessor :name, :version
-
-
1
def initialize(name = self.class.name, version = nil)
-
1
@name = name
-
1
@version = version
-
1
@connection = nil
-
end
-
-
1
self.verbose = true
-
# instantiate the delegate object after initialize is defined
-
1
self.delegate = new
-
-
# Reverses the migration commands for the given block and
-
# the given migrations.
-
#
-
# The following migration will remove the table 'horses'
-
# and create the table 'apples' on the way up, and the reverse
-
# on the way down.
-
#
-
# class FixTLMigration < ActiveRecord::Migration
-
# def change
-
# revert do
-
# create_table(:horses) do |t|
-
# t.text :content
-
# t.datetime :remind_at
-
# end
-
# end
-
# create_table(:apples) do |t|
-
# t.string :variety
-
# end
-
# end
-
# end
-
#
-
# Or equivalently, if +TenderloveMigration+ is defined as in the
-
# documentation for Migration:
-
#
-
# require_relative '2012121212_tenderlove_migration'
-
#
-
# class FixupTLMigration < ActiveRecord::Migration
-
# def change
-
# revert TenderloveMigration
-
#
-
# create_table(:apples) do |t|
-
# t.string :variety
-
# end
-
# end
-
# end
-
#
-
# This command can be nested.
-
1
def revert(*migration_classes)
-
run(*migration_classes.reverse, revert: true) unless migration_classes.empty?
-
if block_given?
-
if @connection.respond_to? :revert
-
@connection.revert { yield }
-
else
-
recorder = CommandRecorder.new(@connection)
-
@connection = recorder
-
suppress_messages do
-
@connection.revert { yield }
-
end
-
@connection = recorder.delegate
-
recorder.commands.each do |cmd, args, block|
-
send(cmd, *args, &block)
-
end
-
end
-
end
-
end
-
-
1
def reverting?
-
@connection.respond_to?(:reverting) && @connection.reverting
-
end
-
-
1
class ReversibleBlockHelper < Struct.new(:reverting) # :nodoc:
-
1
def up
-
yield unless reverting
-
end
-
-
1
def down
-
yield if reverting
-
end
-
end
-
-
# Used to specify an operation that can be run in one direction or another.
-
# Call the methods +up+ and +down+ of the yielded object to run a block
-
# only in one given direction.
-
# The whole block will be called in the right order within the migration.
-
#
-
# In the following example, the looping on users will always be done
-
# when the three columns 'first_name', 'last_name' and 'full_name' exist,
-
# even when migrating down:
-
#
-
# class SplitNameMigration < ActiveRecord::Migration
-
# def change
-
# add_column :users, :first_name, :string
-
# add_column :users, :last_name, :string
-
#
-
# reversible do |dir|
-
# User.reset_column_information
-
# User.all.each do |u|
-
# dir.up { u.first_name, u.last_name = u.full_name.split(' ') }
-
# dir.down { u.full_name = "#{u.first_name} #{u.last_name}" }
-
# u.save
-
# end
-
# end
-
#
-
# revert { add_column :users, :full_name, :string }
-
# end
-
# end
-
1
def reversible
-
helper = ReversibleBlockHelper.new(reverting?)
-
execute_block{ yield helper }
-
end
-
-
# Runs the given migration classes.
-
# Last argument can specify options:
-
# - :direction (default is :up)
-
# - :revert (default is false)
-
1
def run(*migration_classes)
-
opts = migration_classes.extract_options!
-
dir = opts[:direction] || :up
-
dir = (dir == :down ? :up : :down) if opts[:revert]
-
if reverting?
-
# If in revert and going :up, say, we want to execute :down without reverting, so
-
revert { run(*migration_classes, direction: dir, revert: true) }
-
else
-
migration_classes.each do |migration_class|
-
migration_class.new.exec_migration(@connection, dir)
-
end
-
end
-
end
-
-
1
def up
-
self.class.delegate = self
-
return unless self.class.respond_to?(:up)
-
self.class.up
-
end
-
-
1
def down
-
self.class.delegate = self
-
return unless self.class.respond_to?(:down)
-
self.class.down
-
end
-
-
# Execute this migration in the named direction
-
1
def migrate(direction)
-
return unless respond_to?(direction)
-
-
case direction
-
when :up then announce "migrating"
-
when :down then announce "reverting"
-
end
-
-
time = nil
-
ActiveRecord::Base.connection_pool.with_connection do |conn|
-
time = Benchmark.measure do
-
exec_migration(conn, direction)
-
end
-
end
-
-
case direction
-
when :up then announce "migrated (%.4fs)" % time.real; write
-
when :down then announce "reverted (%.4fs)" % time.real; write
-
end
-
end
-
-
1
def exec_migration(conn, direction)
-
@connection = conn
-
if respond_to?(:change)
-
if direction == :down
-
revert { change }
-
else
-
change
-
end
-
else
-
send(direction)
-
end
-
ensure
-
@connection = nil
-
end
-
-
1
def write(text="")
-
puts(text) if verbose
-
end
-
-
1
def announce(message)
-
text = "#{version} #{name}: #{message}"
-
length = [0, 75 - text.length].max
-
write "== %s %s" % [text, "=" * length]
-
end
-
-
1
def say(message, subitem=false)
-
write "#{subitem ? " ->" : "--"} #{message}"
-
end
-
-
1
def say_with_time(message)
-
say(message)
-
result = nil
-
time = Benchmark.measure { result = yield }
-
say "%.4fs" % time.real, :subitem
-
say("#{result} rows", :subitem) if result.is_a?(Integer)
-
result
-
end
-
-
1
def suppress_messages
-
1
save, self.verbose = verbose, false
-
1
yield
-
ensure
-
1
self.verbose = save
-
end
-
-
1
def connection
-
@connection || ActiveRecord::Base.connection
-
end
-
-
1
def method_missing(method, *arguments, &block)
-
arg_list = arguments.map{ |a| a.inspect } * ', '
-
-
say_with_time "#{method}(#{arg_list})" do
-
unless @connection.respond_to? :revert
-
unless arguments.empty? || [:execute, :enable_extension, :disable_extension].include?(method)
-
arguments[0] = proper_table_name(arguments.first, table_name_options)
-
arguments[1] = proper_table_name(arguments.second, table_name_options) if method == :rename_table
-
end
-
end
-
return super unless connection.respond_to?(method)
-
connection.send(method, *arguments, &block)
-
end
-
end
-
-
1
def copy(destination, sources, options = {})
-
copied = []
-
-
FileUtils.mkdir_p(destination) unless File.exist?(destination)
-
-
destination_migrations = ActiveRecord::Migrator.migrations(destination)
-
last = destination_migrations.last
-
sources.each do |scope, path|
-
source_migrations = ActiveRecord::Migrator.migrations(path)
-
-
source_migrations.each do |migration|
-
source = File.binread(migration.filename)
-
inserted_comment = "# This migration comes from #{scope} (originally #{migration.version})\n"
-
if /\A#.*\b(?:en)?coding:\s*\S+/ =~ source
-
# If we have a magic comment in the original migration,
-
# insert our comment after the first newline(end of the magic comment line)
-
# so the magic keep working.
-
# Note that magic comments must be at the first line(except sh-bang).
-
source[/\n/] = "\n#{inserted_comment}"
-
else
-
source = "#{inserted_comment}#{source}"
-
end
-
-
if duplicate = destination_migrations.detect { |m| m.name == migration.name }
-
if options[:on_skip] && duplicate.scope != scope.to_s
-
options[:on_skip].call(scope, migration)
-
end
-
next
-
end
-
-
migration.version = next_migration_number(last ? last.version + 1 : 0).to_i
-
new_path = File.join(destination, "#{migration.version}_#{migration.name.underscore}.#{scope}.rb")
-
old_path, migration.filename = migration.filename, new_path
-
last = migration
-
-
File.binwrite(migration.filename, source)
-
copied << migration
-
options[:on_copy].call(scope, migration, old_path) if options[:on_copy]
-
destination_migrations << migration
-
end
-
end
-
-
copied
-
end
-
-
# Finds the correct table name given an Active Record object.
-
# Uses the Active Record object's own table_name, or pre/suffix from the
-
# options passed in.
-
1
def proper_table_name(name, options = {})
-
if name.respond_to? :table_name
-
name.table_name
-
else
-
"#{options[:table_name_prefix]}#{name}#{options[:table_name_suffix]}"
-
end
-
end
-
-
# Determines the version number of the next migration.
-
1
def next_migration_number(number)
-
if ActiveRecord::Base.timestamped_migrations
-
[Time.now.utc.strftime("%Y%m%d%H%M%S"), "%.14d" % number].max
-
else
-
SchemaMigration.normalize_migration_number(number)
-
end
-
end
-
-
1
def table_name_options(config = ActiveRecord::Base)
-
{
-
table_name_prefix: config.table_name_prefix,
-
table_name_suffix: config.table_name_suffix
-
}
-
end
-
-
1
private
-
1
def execute_block
-
if connection.respond_to? :execute_block
-
super # use normal delegation to record the block
-
else
-
yield
-
end
-
end
-
end
-
-
# MigrationProxy is used to defer loading of the actual migration classes
-
# until they are needed
-
1
class MigrationProxy < Struct.new(:name, :version, :filename, :scope)
-
-
1
def initialize(name, version, filename, scope)
-
14
super
-
14
@migration = nil
-
end
-
-
1
def basename
-
File.basename(filename)
-
end
-
-
1
def mtime
-
File.mtime filename
-
end
-
-
1
delegate :migrate, :announce, :write, :disable_ddl_transaction, to: :migration
-
-
1
private
-
-
1
def migration
-
@migration ||= load_migration
-
end
-
-
1
def load_migration
-
require(File.expand_path(filename))
-
name.constantize.new(name, version)
-
end
-
-
end
-
-
1
class NullMigration < MigrationProxy #:nodoc:
-
1
def initialize
-
super(nil, 0, nil, nil)
-
end
-
-
1
def mtime
-
0
-
end
-
end
-
-
1
class Migrator#:nodoc:
-
1
class << self
-
1
attr_writer :migrations_paths
-
1
alias :migrations_path= :migrations_paths=
-
-
1
def migrate(migrations_paths, target_version = nil, &block)
-
case
-
when target_version.nil?
-
up(migrations_paths, target_version, &block)
-
when current_version == 0 && target_version == 0
-
[]
-
when current_version > target_version
-
down(migrations_paths, target_version, &block)
-
else
-
up(migrations_paths, target_version, &block)
-
end
-
end
-
-
1
def rollback(migrations_paths, steps=1)
-
move(:down, migrations_paths, steps)
-
end
-
-
1
def forward(migrations_paths, steps=1)
-
move(:up, migrations_paths, steps)
-
end
-
-
1
def up(migrations_paths, target_version = nil)
-
migrations = migrations(migrations_paths)
-
migrations.select! { |m| yield m } if block_given?
-
-
self.new(:up, migrations, target_version).migrate
-
end
-
-
1
def down(migrations_paths, target_version = nil, &block)
-
migrations = migrations(migrations_paths)
-
migrations.select! { |m| yield m } if block_given?
-
-
self.new(:down, migrations, target_version).migrate
-
end
-
-
1
def run(direction, migrations_paths, target_version)
-
self.new(direction, migrations(migrations_paths), target_version).run
-
end
-
-
1
def open(migrations_paths)
-
self.new(:up, migrations(migrations_paths), nil)
-
end
-
-
1
def schema_migrations_table_name
-
1
SchemaMigration.table_name
-
end
-
-
1
def get_all_versions(connection = Base.connection)
-
1
if connection.table_exists?(schema_migrations_table_name)
-
15
SchemaMigration.all.map { |x| x.version.to_i }.sort
-
else
-
[]
-
end
-
end
-
-
1
def current_version(connection = Base.connection)
-
get_all_versions(connection).max || 0
-
end
-
-
1
def needs_migration?(connection = Base.connection)
-
1
(migrations(migrations_paths).collect(&:version) - get_all_versions(connection)).size > 0
-
end
-
-
1
def last_version
-
last_migration.version
-
end
-
-
1
def last_migration #:nodoc:
-
migrations(migrations_paths).last || NullMigration.new
-
end
-
-
1
def proper_table_name(name, options = {})
-
ActiveSupport::Deprecation.warn "ActiveRecord::Migrator.proper_table_name is deprecated and will be removed in Rails 4.2. Use the proper_table_name instance method on ActiveRecord::Migration instead"
-
options = {
-
table_name_prefix: ActiveRecord::Base.table_name_prefix,
-
table_name_suffix: ActiveRecord::Base.table_name_suffix
-
}.merge(options)
-
if name.respond_to? :table_name
-
name.table_name
-
else
-
"#{options[:table_name_prefix]}#{name}#{options[:table_name_suffix]}"
-
end
-
end
-
-
1
def migrations_paths
-
1
@migrations_paths ||= ['db/migrate']
-
# just to not break things if someone uses: migration_path = some_string
-
1
Array(@migrations_paths)
-
end
-
-
1
def migrations_path
-
migrations_paths.first
-
end
-
-
1
def migrations(paths)
-
1
paths = Array(paths)
-
-
2
files = Dir[*paths.map { |p| "#{p}/**/[0-9]*_*.rb" }]
-
-
1
migrations = files.map do |file|
-
14
version, name, scope = file.scan(/([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?\.rb\z/).first
-
-
14
raise IllegalMigrationNameError.new(file) unless version
-
14
version = version.to_i
-
14
name = name.camelize
-
-
14
MigrationProxy.new(name, version, file, scope)
-
end
-
-
1
migrations.sort_by(&:version)
-
end
-
-
1
private
-
-
1
def move(direction, migrations_paths, steps)
-
migrator = self.new(direction, migrations(migrations_paths))
-
start_index = migrator.migrations.index(migrator.current_migration)
-
-
if start_index
-
finish = migrator.migrations[start_index + steps]
-
version = finish ? finish.version : 0
-
send(direction, migrations_paths, version)
-
end
-
end
-
end
-
-
1
def initialize(direction, migrations, target_version = nil)
-
raise StandardError.new("This database does not yet support migrations") unless Base.connection.supports_migrations?
-
-
@direction = direction
-
@target_version = target_version
-
@migrated_versions = nil
-
@migrations = migrations
-
-
validate(@migrations)
-
-
Base.connection.initialize_schema_migrations_table
-
end
-
-
1
def current_version
-
migrated.max || 0
-
end
-
-
1
def current_migration
-
migrations.detect { |m| m.version == current_version }
-
end
-
1
alias :current :current_migration
-
-
1
def run
-
migration = migrations.detect { |m| m.version == @target_version }
-
raise UnknownMigrationVersionError.new(@target_version) if migration.nil?
-
unless (up? && migrated.include?(migration.version.to_i)) || (down? && !migrated.include?(migration.version.to_i))
-
begin
-
execute_migration_in_transaction(migration, @direction)
-
rescue => e
-
canceled_msg = use_transaction?(migration) ? ", this migration was canceled" : ""
-
raise StandardError, "An error has occurred#{canceled_msg}:\n\n#{e}", e.backtrace
-
end
-
end
-
end
-
-
1
def migrate
-
if !target && @target_version && @target_version > 0
-
raise UnknownMigrationVersionError.new(@target_version)
-
end
-
-
runnable.each do |migration|
-
Base.logger.info "Migrating to #{migration.name} (#{migration.version})" if Base.logger
-
-
begin
-
execute_migration_in_transaction(migration, @direction)
-
rescue => e
-
canceled_msg = use_transaction?(migration) ? "this and " : ""
-
raise StandardError, "An error has occurred, #{canceled_msg}all later migrations canceled:\n\n#{e}", e.backtrace
-
end
-
end
-
end
-
-
1
def runnable
-
runnable = migrations[start..finish]
-
if up?
-
runnable.reject { |m| ran?(m) }
-
else
-
# skip the last migration if we're headed down, but not ALL the way down
-
runnable.pop if target
-
runnable.find_all { |m| ran?(m) }
-
end
-
end
-
-
1
def migrations
-
down? ? @migrations.reverse : @migrations.sort_by(&:version)
-
end
-
-
1
def pending_migrations
-
already_migrated = migrated
-
migrations.reject { |m| already_migrated.include?(m.version) }
-
end
-
-
1
def migrated
-
@migrated_versions ||= Set.new(self.class.get_all_versions)
-
end
-
-
1
private
-
1
def ran?(migration)
-
migrated.include?(migration.version.to_i)
-
end
-
-
1
def execute_migration_in_transaction(migration, direction)
-
ddl_transaction(migration) do
-
migration.migrate(direction)
-
record_version_state_after_migrating(migration.version)
-
end
-
end
-
-
1
def target
-
migrations.detect { |m| m.version == @target_version }
-
end
-
-
1
def finish
-
migrations.index(target) || migrations.size - 1
-
end
-
-
1
def start
-
up? ? 0 : (migrations.index(current) || 0)
-
end
-
-
1
def validate(migrations)
-
name ,= migrations.group_by(&:name).find { |_,v| v.length > 1 }
-
raise DuplicateMigrationNameError.new(name) if name
-
-
version ,= migrations.group_by(&:version).find { |_,v| v.length > 1 }
-
raise DuplicateMigrationVersionError.new(version) if version
-
end
-
-
1
def record_version_state_after_migrating(version)
-
if down?
-
migrated.delete(version)
-
ActiveRecord::SchemaMigration.where(:version => version.to_s).delete_all
-
else
-
migrated << version
-
ActiveRecord::SchemaMigration.create!(:version => version.to_s)
-
end
-
end
-
-
1
def up?
-
@direction == :up
-
end
-
-
1
def down?
-
@direction == :down
-
end
-
-
# Wrap the migration in a transaction only if supported by the adapter.
-
1
def ddl_transaction(migration)
-
if use_transaction?(migration)
-
Base.transaction { yield }
-
else
-
yield
-
end
-
end
-
-
1
def use_transaction?(migration)
-
!migration.disable_ddl_transaction && Base.connection.supports_ddl_transactions?
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
class Migration
-
1
module JoinTable #:nodoc:
-
1
private
-
-
1
def find_join_table_name(table_1, table_2, options = {})
-
options.delete(:table_name) || join_table_name(table_1, table_2)
-
end
-
-
1
def join_table_name(table_1, table_2)
-
[table_1.to_s, table_2.to_s].sort.join("_").to_sym
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
module ModelSchema
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
##
-
# :singleton-method:
-
# Accessor for the prefix type that will be prepended to every primary key column name.
-
# The options are :table_name and :table_name_with_underscore. If the first is specified,
-
# the Product class will look for "productid" instead of "id" as the primary column. If the
-
# latter is specified, the Product class will look for "product_id" instead of "id". Remember
-
# that this is a global setting for all Active Records.
-
1
mattr_accessor :primary_key_prefix_type, instance_writer: false
-
-
##
-
# :singleton-method:
-
# Accessor for the name of the prefix string to prepend to every table name. So if set
-
# to "basecamp_", all table names will be named like "basecamp_projects", "basecamp_people",
-
# etc. This is a convenient way of creating a namespace for tables in a shared database.
-
# By default, the prefix is the empty string.
-
#
-
# If you are organising your models within modules you can add a prefix to the models within
-
# a namespace by defining a singleton method in the parent module called table_name_prefix which
-
# returns your chosen prefix.
-
1
class_attribute :table_name_prefix, instance_writer: false
-
1
self.table_name_prefix = ""
-
-
##
-
# :singleton-method:
-
# Works like +table_name_prefix+, but appends instead of prepends (set to "_basecamp" gives "projects_basecamp",
-
# "people_basecamp"). By default, the suffix is the empty string.
-
1
class_attribute :table_name_suffix, instance_writer: false
-
1
self.table_name_suffix = ""
-
-
##
-
# :singleton-method:
-
# Accessor for the name of the schema migrations table. By default, the value is "schema_migrations"
-
1
class_attribute :schema_migrations_table_name, instance_accessor: false
-
1
self.schema_migrations_table_name = "schema_migrations"
-
-
##
-
# :singleton-method:
-
# Indicates whether table names should be the pluralized versions of the corresponding class names.
-
# If true, the default table name for a Product class will be +products+. If false, it would just be +product+.
-
# See table_name for the full rules on table/class naming. This is true, by default.
-
1
class_attribute :pluralize_table_names, instance_writer: false
-
1
self.pluralize_table_names = true
-
-
1
self.inheritance_column = 'type'
-
end
-
-
1
module ClassMethods
-
# Guesses the table name (in forced lower-case) based on the name of the class in the
-
# inheritance hierarchy descending directly from ActiveRecord::Base. So if the hierarchy
-
# looks like: Reply < Message < ActiveRecord::Base, then Message is used
-
# to guess the table name even when called on Reply. The rules used to do the guess
-
# are handled by the Inflector class in Active Support, which knows almost all common
-
# English inflections. You can add new inflections in config/initializers/inflections.rb.
-
#
-
# Nested classes are given table names prefixed by the singular form of
-
# the parent's table name. Enclosing modules are not considered.
-
#
-
# ==== Examples
-
#
-
# class Invoice < ActiveRecord::Base
-
# end
-
#
-
# file class table_name
-
# invoice.rb Invoice invoices
-
#
-
# class Invoice < ActiveRecord::Base
-
# class Lineitem < ActiveRecord::Base
-
# end
-
# end
-
#
-
# file class table_name
-
# invoice.rb Invoice::Lineitem invoice_lineitems
-
#
-
# module Invoice
-
# class Lineitem < ActiveRecord::Base
-
# end
-
# end
-
#
-
# file class table_name
-
# invoice/lineitem.rb Invoice::Lineitem lineitems
-
#
-
# Additionally, the class-level +table_name_prefix+ is prepended and the
-
# +table_name_suffix+ is appended. So if you have "myapp_" as a prefix,
-
# the table name guess for an Invoice class becomes "myapp_invoices".
-
# Invoice::Lineitem becomes "myapp_invoice_lineitems".
-
#
-
# You can also set your own table name explicitly:
-
#
-
# class Mouse < ActiveRecord::Base
-
# self.table_name = "mice"
-
# end
-
#
-
# Alternatively, you can override the table_name method to define your
-
# own computation. (Possibly using <tt>super</tt> to manipulate the default
-
# table name.) Example:
-
#
-
# class Post < ActiveRecord::Base
-
# def self.table_name
-
# "special_" + super
-
# end
-
# end
-
# Post.table_name # => "special_posts"
-
1
def table_name
-
45
reset_table_name unless defined?(@table_name)
-
45
@table_name
-
end
-
-
# Sets the table name explicitly. Example:
-
#
-
# class Project < ActiveRecord::Base
-
# self.table_name = "project"
-
# end
-
#
-
# You can also just define your own <tt>self.table_name</tt> method; see
-
# the documentation for ActiveRecord::Base#table_name.
-
1
def table_name=(value)
-
4
value = value && value.to_s
-
-
4
if defined?(@table_name)
-
return if value == @table_name
-
reset_column_information if connected?
-
end
-
-
4
@table_name = value
-
4
@quoted_table_name = nil
-
4
@arel_table = nil
-
4
@sequence_name = nil unless defined?(@explicit_sequence_name) && @explicit_sequence_name
-
4
@relation = Relation.create(self, arel_table)
-
end
-
-
# Returns a quoted version of the table name, used to construct SQL statements.
-
1
def quoted_table_name
-
@quoted_table_name ||= connection.quote_table_name(table_name)
-
end
-
-
# Computes the table name, (re)sets it internally, and returns it.
-
1
def reset_table_name #:nodoc:
-
4
self.table_name = if abstract_class?
-
superclass == Base ? nil : superclass.table_name
-
elsif superclass.abstract_class?
-
superclass.table_name || compute_table_name
-
else
-
4
compute_table_name
-
end
-
end
-
-
1
def full_table_name_prefix #:nodoc:
-
8
(parents.detect{ |p| p.respond_to?(:table_name_prefix) } || self).table_name_prefix
-
end
-
-
# Defines the name of the table column which will store the class name on single-table
-
# inheritance situations.
-
#
-
# The default inheritance column name is +type+, which means it's a
-
# reserved word inside Active Record. To be able to use single-table
-
# inheritance with another column name, or to use the column +type+ in
-
# your own model for something else, you can set +inheritance_column+:
-
#
-
# self.inheritance_column = 'zoink'
-
1
def inheritance_column
-
52
(@inheritance_column ||= nil) || superclass.inheritance_column
-
end
-
-
# Sets the value of inheritance_column
-
1
def inheritance_column=(value)
-
1
@inheritance_column = value.to_s
-
1
@explicit_inheritance_column = true
-
end
-
-
1
def sequence_name
-
if base_class == self
-
@sequence_name ||= reset_sequence_name
-
else
-
(@sequence_name ||= nil) || base_class.sequence_name
-
end
-
end
-
-
1
def reset_sequence_name #:nodoc:
-
@explicit_sequence_name = false
-
@sequence_name = connection.default_sequence_name(table_name, primary_key)
-
end
-
-
# Sets the name of the sequence to use when generating ids to the given
-
# value, or (if the value is nil or false) to the value returned by the
-
# given block. This is required for Oracle and is useful for any
-
# database which relies on sequences for primary key generation.
-
#
-
# If a sequence name is not explicitly set when using Oracle or Firebird,
-
# it will default to the commonly used pattern of: #{table_name}_seq
-
#
-
# If a sequence name is not explicitly set when using PostgreSQL, it
-
# will discover the sequence corresponding to your primary key for you.
-
#
-
# class Project < ActiveRecord::Base
-
# self.sequence_name = "projectseq" # default would have been "project_seq"
-
# end
-
1
def sequence_name=(value)
-
@sequence_name = value.to_s
-
@explicit_sequence_name = true
-
end
-
-
# Indicates whether the table associated with this class exists
-
1
def table_exists?
-
3
connection.schema_cache.table_exists?(table_name)
-
end
-
-
# Returns an array of column objects for the table associated with this class.
-
1
def columns
-
@columns ||= connection.schema_cache.columns(table_name).map do |col|
-
21
col = col.dup
-
21
col.primary = (col.name == primary_key)
-
21
col
-
23
end
-
end
-
-
# Returns a hash of column objects for the table associated with this class.
-
1
def columns_hash
-
264
@columns_hash ||= Hash[columns.map { |c| [c.name, c] }]
-
end
-
-
1
def column_types # :nodoc:
-
24
@column_types ||= decorate_columns(columns_hash.dup)
-
end
-
-
1
def decorate_columns(columns_hash) # :nodoc:
-
20
return if columns_hash.empty?
-
-
@serialized_column_names ||= self.columns_hash.keys.find_all do |name|
-
21
serialized_attributes.key?(name)
-
20
end
-
-
20
@serialized_column_names.each do |name|
-
columns_hash[name] = AttributeMethods::Serialization::Type.new(columns_hash[name])
-
end
-
-
@time_zone_column_names ||= self.columns_hash.find_all do |name, col|
-
21
create_time_zone_conversion_attribute?(name, col)
-
20
end.map!(&:first)
-
-
20
@time_zone_column_names.each do |name|
-
10
columns_hash[name] = AttributeMethods::TimeZoneConversion::Type.new(columns_hash[name])
-
end
-
-
20
columns_hash
-
end
-
-
# Returns a hash where the keys are column names and the values are
-
# default values when instantiating the AR object for this table.
-
1
def column_defaults
-
23
@column_defaults ||= Hash[columns.map { |c| [c.name, c.default] }]
-
end
-
-
# Returns an array of column names as strings.
-
1
def column_names
-
51
@column_names ||= columns.map { |column| column.name }
-
end
-
-
# Returns an array of column objects where the primary id, all columns ending in "_id" or "_count",
-
# and columns used for single table inheritance have been removed.
-
1
def content_columns
-
@content_columns ||= columns.reject { |c| c.primary || c.name =~ /(_id|_count)$/ || c.name == inheritance_column }
-
end
-
-
# Resets all the cached information about columns, which will cause them
-
# to be reloaded on the next request.
-
#
-
# The most common usage pattern for this method is probably in a migration,
-
# when just after creating a table you want to populate it with some default
-
# values, eg:
-
#
-
# class CreateJobLevels < ActiveRecord::Migration
-
# def up
-
# create_table :job_levels do |t|
-
# t.integer :id
-
# t.string :name
-
#
-
# t.timestamps
-
# end
-
#
-
# JobLevel.reset_column_information
-
# %w{assistant executive manager director}.each do |type|
-
# JobLevel.create(name: type)
-
# end
-
# end
-
#
-
# def down
-
# drop_table :job_levels
-
# end
-
# end
-
1
def reset_column_information
-
connection.clear_cache!
-
undefine_attribute_methods
-
connection.schema_cache.clear_table_cache!(table_name) if table_exists?
-
-
@arel_engine = nil
-
@column_defaults = nil
-
@column_names = nil
-
@columns = nil
-
@columns_hash = nil
-
@column_types = nil
-
@content_columns = nil
-
@dynamic_methods_hash = nil
-
@inheritance_column = nil unless defined?(@explicit_inheritance_column) && @explicit_inheritance_column
-
@relation = nil
-
@serialized_column_names = nil
-
@time_zone_column_names = nil
-
@cached_time_zone = nil
-
end
-
-
# This is a hook for use by modules that need to do extra stuff to
-
# attributes when they are initialized. (e.g. attribute
-
# serialization)
-
1
def initialize_attributes(attributes, options = {}) #:nodoc:
-
24
attributes
-
end
-
-
1
private
-
-
# Guesses the table name, but does not decorate it with prefix and suffix information.
-
1
def undecorated_table_name(class_name = base_class.name)
-
4
table_name = class_name.to_s.demodulize.underscore
-
4
pluralize_table_names ? table_name.pluralize : table_name
-
end
-
-
# Computes and returns a table name according to default conventions.
-
1
def compute_table_name
-
4
base = base_class
-
4
if self == base
-
# Nested classes are prefixed with singular parent table name.
-
4
if parent < Base && !parent.abstract_class?
-
contained = parent.table_name
-
contained = contained.singularize if parent.pluralize_table_names
-
contained += '_'
-
end
-
4
"#{full_table_name_prefix}#{contained}#{undecorated_table_name(name)}#{table_name_suffix}"
-
else
-
# STI subclasses always use their superclass' table.
-
base.table_name
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/hash/except'
-
1
require 'active_support/core_ext/object/try'
-
1
require 'active_support/core_ext/hash/indifferent_access'
-
-
1
module ActiveRecord
-
1
module NestedAttributes #:nodoc:
-
1
class TooManyRecords < ActiveRecordError
-
end
-
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
class_attribute :nested_attributes_options, instance_writer: false
-
1
self.nested_attributes_options = {}
-
end
-
-
# = Active Record Nested Attributes
-
#
-
# Nested attributes allow you to save attributes on associated records
-
# through the parent. By default nested attribute updating is turned off
-
# and you can enable it using the accepts_nested_attributes_for class
-
# method. When you enable nested attributes an attribute writer is
-
# defined on the model.
-
#
-
# The attribute writer is named after the association, which means that
-
# in the following example, two new methods are added to your model:
-
#
-
# <tt>author_attributes=(attributes)</tt> and
-
# <tt>pages_attributes=(attributes)</tt>.
-
#
-
# class Book < ActiveRecord::Base
-
# has_one :author
-
# has_many :pages
-
#
-
# accepts_nested_attributes_for :author, :pages
-
# end
-
#
-
# Note that the <tt>:autosave</tt> option is automatically enabled on every
-
# association that accepts_nested_attributes_for is used for.
-
#
-
# === One-to-one
-
#
-
# Consider a Member model that has one Avatar:
-
#
-
# class Member < ActiveRecord::Base
-
# has_one :avatar
-
# accepts_nested_attributes_for :avatar
-
# end
-
#
-
# Enabling nested attributes on a one-to-one association allows you to
-
# create the member and avatar in one go:
-
#
-
# params = { member: { name: 'Jack', avatar_attributes: { icon: 'smiling' } } }
-
# member = Member.create(params[:member])
-
# member.avatar.id # => 2
-
# member.avatar.icon # => 'smiling'
-
#
-
# It also allows you to update the avatar through the member:
-
#
-
# params = { member: { avatar_attributes: { id: '2', icon: 'sad' } } }
-
# member.update params[:member]
-
# member.avatar.icon # => 'sad'
-
#
-
# By default you will only be able to set and update attributes on the
-
# associated model. If you want to destroy the associated model through the
-
# attributes hash, you have to enable it first using the
-
# <tt>:allow_destroy</tt> option.
-
#
-
# class Member < ActiveRecord::Base
-
# has_one :avatar
-
# accepts_nested_attributes_for :avatar, allow_destroy: true
-
# end
-
#
-
# Now, when you add the <tt>_destroy</tt> key to the attributes hash, with a
-
# value that evaluates to +true+, you will destroy the associated model:
-
#
-
# member.avatar_attributes = { id: '2', _destroy: '1' }
-
# member.avatar.marked_for_destruction? # => true
-
# member.save
-
# member.reload.avatar # => nil
-
#
-
# Note that the model will _not_ be destroyed until the parent is saved.
-
#
-
# === One-to-many
-
#
-
# Consider a member that has a number of posts:
-
#
-
# class Member < ActiveRecord::Base
-
# has_many :posts
-
# accepts_nested_attributes_for :posts
-
# end
-
#
-
# You can now set or update attributes on the associated posts through
-
# an attribute hash for a member: include the key +:posts_attributes+
-
# with an array of hashes of post attributes as a value.
-
#
-
# For each hash that does _not_ have an <tt>id</tt> key a new record will
-
# be instantiated, unless the hash also contains a <tt>_destroy</tt> key
-
# that evaluates to +true+.
-
#
-
# params = { member: {
-
# name: 'joe', posts_attributes: [
-
# { title: 'Kari, the awesome Ruby documentation browser!' },
-
# { title: 'The egalitarian assumption of the modern citizen' },
-
# { title: '', _destroy: '1' } # this will be ignored
-
# ]
-
# }}
-
#
-
# member = Member.create(params[:member])
-
# member.posts.length # => 2
-
# member.posts.first.title # => 'Kari, the awesome Ruby documentation browser!'
-
# member.posts.second.title # => 'The egalitarian assumption of the modern citizen'
-
#
-
# You may also set a :reject_if proc to silently ignore any new record
-
# hashes if they fail to pass your criteria. For example, the previous
-
# example could be rewritten as:
-
#
-
# class Member < ActiveRecord::Base
-
# has_many :posts
-
# accepts_nested_attributes_for :posts, reject_if: proc { |attributes| attributes['title'].blank? }
-
# end
-
#
-
# params = { member: {
-
# name: 'joe', posts_attributes: [
-
# { title: 'Kari, the awesome Ruby documentation browser!' },
-
# { title: 'The egalitarian assumption of the modern citizen' },
-
# { title: '' } # this will be ignored because of the :reject_if proc
-
# ]
-
# }}
-
#
-
# member = Member.create(params[:member])
-
# member.posts.length # => 2
-
# member.posts.first.title # => 'Kari, the awesome Ruby documentation browser!'
-
# member.posts.second.title # => 'The egalitarian assumption of the modern citizen'
-
#
-
# Alternatively, :reject_if also accepts a symbol for using methods:
-
#
-
# class Member < ActiveRecord::Base
-
# has_many :posts
-
# accepts_nested_attributes_for :posts, reject_if: :new_record?
-
# end
-
#
-
# class Member < ActiveRecord::Base
-
# has_many :posts
-
# accepts_nested_attributes_for :posts, reject_if: :reject_posts
-
#
-
# def reject_posts(attributed)
-
# attributed['title'].blank?
-
# end
-
# end
-
#
-
# If the hash contains an <tt>id</tt> key that matches an already
-
# associated record, the matching record will be modified:
-
#
-
# member.attributes = {
-
# name: 'Joe',
-
# posts_attributes: [
-
# { id: 1, title: '[UPDATED] An, as of yet, undisclosed awesome Ruby documentation browser!' },
-
# { id: 2, title: '[UPDATED] other post' }
-
# ]
-
# }
-
#
-
# member.posts.first.title # => '[UPDATED] An, as of yet, undisclosed awesome Ruby documentation browser!'
-
# member.posts.second.title # => '[UPDATED] other post'
-
#
-
# By default the associated records are protected from being destroyed. If
-
# you want to destroy any of the associated records through the attributes
-
# hash, you have to enable it first using the <tt>:allow_destroy</tt>
-
# option. This will allow you to also use the <tt>_destroy</tt> key to
-
# destroy existing records:
-
#
-
# class Member < ActiveRecord::Base
-
# has_many :posts
-
# accepts_nested_attributes_for :posts, allow_destroy: true
-
# end
-
#
-
# params = { member: {
-
# posts_attributes: [{ id: '2', _destroy: '1' }]
-
# }}
-
#
-
# member.attributes = params[:member]
-
# member.posts.detect { |p| p.id == 2 }.marked_for_destruction? # => true
-
# member.posts.length # => 2
-
# member.save
-
# member.reload.posts.length # => 1
-
#
-
# Nested attributes for an associated collection can also be passed in
-
# the form of a hash of hashes instead of an array of hashes:
-
#
-
# Member.create(name: 'joe',
-
# posts_attributes: { first: { title: 'Foo' },
-
# second: { title: 'Bar' } })
-
#
-
# has the same effect as
-
#
-
# Member.create(name: 'joe',
-
# posts_attributes: [ { title: 'Foo' },
-
# { title: 'Bar' } ])
-
#
-
# The keys of the hash which is the value for +:posts_attributes+ are
-
# ignored in this case.
-
# However, it is not allowed to use +'id'+ or +:id+ for one of
-
# such keys, otherwise the hash will be wrapped in an array and
-
# interpreted as an attribute hash for a single post.
-
#
-
# Passing attributes for an associated collection in the form of a hash
-
# of hashes can be used with hashes generated from HTTP/HTML parameters,
-
# where there maybe no natural way to submit an array of hashes.
-
#
-
# === Saving
-
#
-
# All changes to models, including the destruction of those marked for
-
# destruction, are saved and destroyed automatically and atomically when
-
# the parent model is saved. This happens inside the transaction initiated
-
# by the parents save method. See ActiveRecord::AutosaveAssociation.
-
#
-
# === Validating the presence of a parent model
-
#
-
# If you want to validate that a child record is associated with a parent
-
# record, you can use <tt>validates_presence_of</tt> and
-
# <tt>inverse_of</tt> as this example illustrates:
-
#
-
# class Member < ActiveRecord::Base
-
# has_many :posts, inverse_of: :member
-
# accepts_nested_attributes_for :posts
-
# end
-
#
-
# class Post < ActiveRecord::Base
-
# belongs_to :member, inverse_of: :posts
-
# validates_presence_of :member
-
# end
-
#
-
# Note that if you do not specify the <tt>inverse_of</tt> option, then
-
# Active Record will try to automatically guess the inverse association
-
# based on heuristics.
-
#
-
# For one-to-one nested associations, if you build the new (in-memory)
-
# child object yourself before assignment, then this module will not
-
# overwrite it, e.g.:
-
#
-
# class Member < ActiveRecord::Base
-
# has_one :avatar
-
# accepts_nested_attributes_for :avatar
-
#
-
# def avatar
-
# super || build_avatar(width: 200)
-
# end
-
# end
-
#
-
# member = Member.new
-
# member.avatar_attributes = {icon: 'sad'}
-
# member.avatar.width # => 200
-
1
module ClassMethods
-
1
REJECT_ALL_BLANK_PROC = proc { |attributes| attributes.all? { |key, value| key == '_destroy' || value.blank? } }
-
-
# Defines an attributes writer for the specified association(s).
-
#
-
# Supported options:
-
# [:allow_destroy]
-
# If true, destroys any members from the attributes hash with a
-
# <tt>_destroy</tt> key and a value that evaluates to +true+
-
# (eg. 1, '1', true, or 'true'). This option is off by default.
-
# [:reject_if]
-
# Allows you to specify a Proc or a Symbol pointing to a method
-
# that checks whether a record should be built for a certain attribute
-
# hash. The hash is passed to the supplied Proc or the method
-
# and it should return either +true+ or +false+. When no :reject_if
-
# is specified, a record will be built for all attribute hashes that
-
# do not have a <tt>_destroy</tt> value that evaluates to true.
-
# Passing <tt>:all_blank</tt> instead of a Proc will create a proc
-
# that will reject a record where all the attributes are blank excluding
-
# any value for _destroy.
-
# [:limit]
-
# Allows you to specify the maximum number of the associated records that
-
# can be processed with the nested attributes. Limit also can be specified as a
-
# Proc or a Symbol pointing to a method that should return number. If the size of the
-
# nested attributes array exceeds the specified limit, NestedAttributes::TooManyRecords
-
# exception is raised. If omitted, any number associations can be processed.
-
# Note that the :limit option is only applicable to one-to-many associations.
-
# [:update_only]
-
# For a one-to-one association, this option allows you to specify how
-
# nested attributes are to be used when an associated record already
-
# exists. In general, an existing record may either be updated with the
-
# new set of attribute values or be replaced by a wholly new record
-
# containing those values. By default the :update_only option is +false+
-
# and the nested attributes are used to update the existing record only
-
# if they include the record's <tt>:id</tt> value. Otherwise a new
-
# record will be instantiated and used to replace the existing one.
-
# However if the :update_only option is +true+, the nested attributes
-
# are used to update the record's attributes always, regardless of
-
# whether the <tt>:id</tt> is present. The option is ignored for collection
-
# associations.
-
#
-
# Examples:
-
# # creates avatar_attributes=
-
# accepts_nested_attributes_for :avatar, reject_if: proc { |attributes| attributes['name'].blank? }
-
# # creates avatar_attributes=
-
# accepts_nested_attributes_for :avatar, reject_if: :all_blank
-
# # creates avatar_attributes= and posts_attributes=
-
# accepts_nested_attributes_for :avatar, :posts, allow_destroy: true
-
1
def accepts_nested_attributes_for(*attr_names)
-
options = { :allow_destroy => false, :update_only => false }
-
options.update(attr_names.extract_options!)
-
options.assert_valid_keys(:allow_destroy, :reject_if, :limit, :update_only)
-
options[:reject_if] = REJECT_ALL_BLANK_PROC if options[:reject_if] == :all_blank
-
-
attr_names.each do |association_name|
-
if reflection = _reflect_on_association(association_name)
-
reflection.autosave = true
-
add_autosave_association_callbacks(reflection)
-
-
nested_attributes_options = self.nested_attributes_options.dup
-
nested_attributes_options[association_name.to_sym] = options
-
self.nested_attributes_options = nested_attributes_options
-
-
type = (reflection.collection? ? :collection : :one_to_one)
-
generate_association_writer(association_name, type)
-
else
-
raise ArgumentError, "No association found for name `#{association_name}'. Has it been defined yet?"
-
end
-
end
-
end
-
-
1
private
-
-
# Generates a writer method for this association. Serves as a point for
-
# accessing the objects in the association. For example, this method
-
# could generate the following:
-
#
-
# def pirate_attributes=(attributes)
-
# assign_nested_attributes_for_one_to_one_association(:pirate, attributes)
-
# end
-
#
-
# This redirects the attempts to write objects in an association through
-
# the helper methods defined below. Makes it seem like the nested
-
# associations are just regular associations.
-
1
def generate_association_writer(association_name, type)
-
generated_association_methods.module_eval <<-eoruby, __FILE__, __LINE__ + 1
-
if method_defined?(:#{association_name}_attributes=)
-
remove_method(:#{association_name}_attributes=)
-
end
-
def #{association_name}_attributes=(attributes)
-
assign_nested_attributes_for_#{type}_association(:#{association_name}, attributes)
-
end
-
eoruby
-
end
-
end
-
-
# Returns ActiveRecord::AutosaveAssociation::marked_for_destruction? It's
-
# used in conjunction with fields_for to build a form element for the
-
# destruction of this association.
-
#
-
# See ActionView::Helpers::FormHelper::fields_for for more info.
-
1
def _destroy
-
marked_for_destruction?
-
end
-
-
1
private
-
-
# Attribute hash keys that should not be assigned as normal attributes.
-
# These hash keys are nested attributes implementation details.
-
1
UNASSIGNABLE_KEYS = %w( id _destroy )
-
-
# Assigns the given attributes to the association.
-
#
-
# If an associated record does not yet exist, one will be instantiated. If
-
# an associated record already exists, the method's behavior depends on
-
# the value of the update_only option. If update_only is +false+ and the
-
# given attributes include an <tt>:id</tt> that matches the existing record's
-
# id, then the existing record will be modified. If no <tt>:id</tt> is provided
-
# it will be replaced with a new record. If update_only is +true+ the existing
-
# record will be modified regardless of whether an <tt>:id</tt> is provided.
-
#
-
# If the given attributes include a matching <tt>:id</tt> attribute, or
-
# update_only is true, and a <tt>:_destroy</tt> key set to a truthy value,
-
# then the existing record will be marked for destruction.
-
1
def assign_nested_attributes_for_one_to_one_association(association_name, attributes)
-
options = self.nested_attributes_options[association_name]
-
attributes = attributes.with_indifferent_access
-
existing_record = send(association_name)
-
-
if (options[:update_only] || !attributes['id'].blank?) && existing_record &&
-
(options[:update_only] || existing_record.id.to_s == attributes['id'].to_s)
-
assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy]) unless call_reject_if(association_name, attributes)
-
-
elsif attributes['id'].present?
-
raise_nested_attributes_record_not_found!(association_name, attributes['id'])
-
-
elsif !reject_new_record?(association_name, attributes)
-
assignable_attributes = attributes.except(*UNASSIGNABLE_KEYS)
-
-
if existing_record && existing_record.new_record?
-
existing_record.assign_attributes(assignable_attributes)
-
association(association_name).initialize_attributes(existing_record)
-
else
-
method = "build_#{association_name}"
-
if respond_to?(method)
-
send(method, assignable_attributes)
-
else
-
raise ArgumentError, "Cannot build association `#{association_name}'. Are you trying to build a polymorphic one-to-one association?"
-
end
-
end
-
end
-
end
-
-
# Assigns the given attributes to the collection association.
-
#
-
# Hashes with an <tt>:id</tt> value matching an existing associated record
-
# will update that record. Hashes without an <tt>:id</tt> value will build
-
# a new record for the association. Hashes with a matching <tt>:id</tt>
-
# value and a <tt>:_destroy</tt> key set to a truthy value will mark the
-
# matched record for destruction.
-
#
-
# For example:
-
#
-
# assign_nested_attributes_for_collection_association(:people, {
-
# '1' => { id: '1', name: 'Peter' },
-
# '2' => { name: 'John' },
-
# '3' => { id: '2', _destroy: true }
-
# })
-
#
-
# Will update the name of the Person with ID 1, build a new associated
-
# person with the name 'John', and mark the associated Person with ID 2
-
# for destruction.
-
#
-
# Also accepts an Array of attribute hashes:
-
#
-
# assign_nested_attributes_for_collection_association(:people, [
-
# { id: '1', name: 'Peter' },
-
# { name: 'John' },
-
# { id: '2', _destroy: true }
-
# ])
-
1
def assign_nested_attributes_for_collection_association(association_name, attributes_collection)
-
options = self.nested_attributes_options[association_name]
-
-
unless attributes_collection.is_a?(Hash) || attributes_collection.is_a?(Array)
-
raise ArgumentError, "Hash or Array expected, got #{attributes_collection.class.name} (#{attributes_collection.inspect})"
-
end
-
-
check_record_limit!(options[:limit], attributes_collection)
-
-
if attributes_collection.is_a? Hash
-
keys = attributes_collection.keys
-
attributes_collection = if keys.include?('id') || keys.include?(:id)
-
[attributes_collection]
-
else
-
attributes_collection.values
-
end
-
end
-
-
association = association(association_name)
-
-
existing_records = if association.loaded?
-
association.target
-
else
-
attribute_ids = attributes_collection.map {|a| a['id'] || a[:id] }.compact
-
attribute_ids.empty? ? [] : association.scope.where(association.klass.primary_key => attribute_ids)
-
end
-
-
attributes_collection.each do |attributes|
-
attributes = attributes.with_indifferent_access
-
-
if attributes['id'].blank?
-
unless reject_new_record?(association_name, attributes)
-
association.build(attributes.except(*UNASSIGNABLE_KEYS))
-
end
-
elsif existing_record = existing_records.detect { |record| record.id.to_s == attributes['id'].to_s }
-
unless call_reject_if(association_name, attributes)
-
# Make sure we are operating on the actual object which is in the association's
-
# proxy_target array (either by finding it, or adding it if not found)
-
# Take into account that the proxy_target may have changed due to callbacks
-
target_record = association.target.detect { |record| record.id.to_s == attributes['id'].to_s }
-
if target_record
-
existing_record = target_record
-
else
-
association.add_to_target(existing_record, :skip_callbacks)
-
end
-
-
assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy])
-
end
-
else
-
raise_nested_attributes_record_not_found!(association_name, attributes['id'])
-
end
-
end
-
end
-
-
# Takes in a limit and checks if the attributes_collection has too many
-
# records. The method will take limits in the form of symbols, procs, and
-
# number-like objects (anything that can be compared with an integer).
-
#
-
# Will raise an TooManyRecords error if the attributes_collection is
-
# larger than the limit.
-
1
def check_record_limit!(limit, attributes_collection)
-
if limit
-
limit = case limit
-
when Symbol
-
send(limit)
-
when Proc
-
limit.call
-
else
-
limit
-
end
-
-
if limit && attributes_collection.size > limit
-
raise TooManyRecords, "Maximum #{limit} records are allowed. Got #{attributes_collection.size} records instead."
-
end
-
end
-
end
-
-
# Updates a record with the +attributes+ or marks it for destruction if
-
# +allow_destroy+ is +true+ and has_destroy_flag? returns +true+.
-
1
def assign_to_or_mark_for_destruction(record, attributes, allow_destroy)
-
record.assign_attributes(attributes.except(*UNASSIGNABLE_KEYS))
-
record.mark_for_destruction if has_destroy_flag?(attributes) && allow_destroy
-
end
-
-
# Determines if a hash contains a truthy _destroy key.
-
1
def has_destroy_flag?(hash)
-
ConnectionAdapters::Column.value_to_boolean(hash['_destroy'])
-
end
-
-
# Determines if a new record should be build by checking for
-
# has_destroy_flag? or if a <tt>:reject_if</tt> proc exists for this
-
# association and evaluates to +true+.
-
1
def reject_new_record?(association_name, attributes)
-
has_destroy_flag?(attributes) || call_reject_if(association_name, attributes)
-
end
-
-
# Determines if a record with the particular +attributes+ should be
-
# rejected by calling the reject_if Symbol or Proc (if defined).
-
# The reject_if option is defined by +accepts_nested_attributes_for+.
-
#
-
# Returns false if there is a +destroy_flag+ on the attributes.
-
1
def call_reject_if(association_name, attributes)
-
return false if has_destroy_flag?(attributes)
-
case callback = self.nested_attributes_options[association_name][:reject_if]
-
when Symbol
-
method(callback).arity == 0 ? send(callback) : send(callback, attributes)
-
when Proc
-
callback.call(attributes)
-
end
-
end
-
-
1
def raise_nested_attributes_record_not_found!(association_name, record_id)
-
raise RecordNotFound, "Couldn't find #{self.class._reflect_on_association(association_name).klass.name} with ID=#{record_id} for #{self.class.name} with ID=#{id}"
-
end
-
end
-
end
-
1
module ActiveRecord
-
# = Active Record No Touching
-
1
module NoTouching
-
1
extend ActiveSupport::Concern
-
-
1
module ClassMethods
-
# Lets you selectively disable calls to `touch` for the
-
# duration of a block.
-
#
-
# ==== Examples
-
# ActiveRecord::Base.no_touching do
-
# Project.first.touch # does nothing
-
# Message.first.touch # does nothing
-
# end
-
#
-
# Project.no_touching do
-
# Project.first.touch # does nothing
-
# Message.first.touch # works, but does not touch the associated project
-
# end
-
#
-
1
def no_touching(&block)
-
NoTouching.apply_to(self, &block)
-
end
-
end
-
-
1
class << self
-
1
def apply_to(klass) #:nodoc:
-
klasses.push(klass)
-
yield
-
ensure
-
klasses.pop
-
end
-
-
1
def applied_to?(klass) #:nodoc:
-
klasses.any? { |k| k >= klass }
-
end
-
-
1
private
-
1
def klasses
-
Thread.current[:no_touching_classes] ||= []
-
end
-
end
-
-
1
def no_touching?
-
NoTouching.applied_to?(self.class)
-
end
-
-
1
def touch(*)
-
super unless no_touching?
-
end
-
end
-
end
-
1
module ActiveRecord
-
# = Active Record Persistence
-
1
module Persistence
-
1
extend ActiveSupport::Concern
-
-
1
module ClassMethods
-
# Creates an object (or multiple objects) and saves it to the database, if validations pass.
-
# The resulting object is returned whether the object was saved successfully to the database or not.
-
#
-
# The +attributes+ parameter can be either a Hash or an Array of Hashes. These Hashes describe the
-
# attributes on the objects that are to be created.
-
#
-
# ==== Examples
-
# # Create a single new object
-
# User.create(first_name: 'Jamie')
-
#
-
# # Create an Array of new objects
-
# User.create([{ first_name: 'Jamie' }, { first_name: 'Jeremy' }])
-
#
-
# # Create a single object and pass it into a block to set other attributes.
-
# User.create(first_name: 'Jamie') do |u|
-
# u.is_admin = false
-
# end
-
#
-
# # Creating an Array of new objects using a block, where the block is executed for each object:
-
# User.create([{ first_name: 'Jamie' }, { first_name: 'Jeremy' }]) do |u|
-
# u.is_admin = false
-
# end
-
1
def create(attributes = nil, &block)
-
if attributes.is_a?(Array)
-
attributes.collect { |attr| create(attr, &block) }
-
else
-
object = new(attributes, &block)
-
object.save
-
object
-
end
-
end
-
-
# Given an attributes hash, +instantiate+ returns a new instance of
-
# the appropriate class. Accepts only keys as strings.
-
#
-
# For example, +Post.all+ may return Comments, Messages, and Emails
-
# by storing the record's subclass in a +type+ attribute. By calling
-
# +instantiate+ instead of +new+, finder methods ensure they get new
-
# instances of the appropriate class for each record.
-
#
-
# See +ActiveRecord::Inheritance#discriminate_class_for_record+ to see
-
# how this "single-table" inheritance mapping is implemented.
-
1
def instantiate(attributes, column_types = {})
-
16
klass = discriminate_class_for_record(attributes)
-
16
column_types = klass.decorate_columns(column_types.dup)
-
16
klass.allocate.init_with('attributes' => attributes, 'column_types' => column_types)
-
end
-
-
1
private
-
# Called by +instantiate+ to decide which class to use for a new
-
# record instance.
-
#
-
# See +ActiveRecord::Inheritance#discriminate_class_for_record+ for
-
# the single-table inheritance discriminator.
-
1
def discriminate_class_for_record(record)
-
16
self
-
end
-
end
-
-
# Returns true if this object hasn't been saved yet -- that is, a record
-
# for the object doesn't exist in the database yet; otherwise, returns false.
-
1
def new_record?
-
45
sync_with_transaction_state
-
45
@new_record
-
end
-
-
# Returns true if this object has been destroyed, otherwise returns false.
-
1
def destroyed?
-
6
sync_with_transaction_state
-
6
@destroyed
-
end
-
-
# Returns true if the record is persisted, i.e. it's not a new record and it was
-
# not destroyed, otherwise returns false.
-
1
def persisted?
-
2
!(new_record? || destroyed?)
-
end
-
-
# Saves the model.
-
#
-
# If the model is new a record gets created in the database, otherwise
-
# the existing record gets updated.
-
#
-
# By default, save always run validations. If any of them fail the action
-
# is cancelled and +save+ returns +false+. However, if you supply
-
# validate: false, validations are bypassed altogether. See
-
# ActiveRecord::Validations for more information.
-
#
-
# There's a series of callbacks associated with +save+. If any of the
-
# <tt>before_*</tt> callbacks return +false+ the action is cancelled and
-
# +save+ returns +false+. See ActiveRecord::Callbacks for further
-
# details.
-
#
-
# Attributes marked as readonly are silently ignored if the record is
-
# being updated.
-
1
def save(*)
-
4
create_or_update
-
rescue ActiveRecord::RecordInvalid
-
false
-
end
-
-
# Saves the model.
-
#
-
# If the model is new a record gets created in the database, otherwise
-
# the existing record gets updated.
-
#
-
# With <tt>save!</tt> validations always run. If any of them fail
-
# ActiveRecord::RecordInvalid gets raised. See ActiveRecord::Validations
-
# for more information.
-
#
-
# There's a series of callbacks associated with <tt>save!</tt>. If any of
-
# the <tt>before_*</tt> callbacks return +false+ the action is cancelled
-
# and <tt>save!</tt> raises ActiveRecord::RecordNotSaved. See
-
# ActiveRecord::Callbacks for further details.
-
#
-
# Attributes marked as readonly are silently ignored if the record is
-
# being updated.
-
1
def save!(*)
-
4
create_or_update || raise(RecordNotSaved)
-
end
-
-
# Deletes the record in the database and freezes this instance to
-
# reflect that no changes should be made (since they can't be
-
# persisted). Returns the frozen instance.
-
#
-
# The row is simply removed with an SQL +DELETE+ statement on the
-
# record's primary key, and no callbacks are executed.
-
#
-
# To enforce the object's +before_destroy+ and +after_destroy+
-
# callbacks or any <tt>:dependent</tt> association
-
# options, use <tt>#destroy</tt>.
-
1
def delete
-
self.class.delete(id) if persisted?
-
@destroyed = true
-
freeze
-
end
-
-
# Deletes the record in the database and freezes this instance to reflect
-
# that no changes should be made (since they can't be persisted).
-
#
-
# There's a series of callbacks associated with <tt>destroy</tt>. If
-
# the <tt>before_destroy</tt> callback return +false+ the action is cancelled
-
# and <tt>destroy</tt> returns +false+. See
-
# ActiveRecord::Callbacks for further details.
-
1
def destroy
-
raise ReadOnlyRecord if readonly?
-
destroy_associations
-
destroy_row if persisted?
-
@destroyed = true
-
freeze
-
end
-
-
# Deletes the record in the database and freezes this instance to reflect
-
# that no changes should be made (since they can't be persisted).
-
#
-
# There's a series of callbacks associated with <tt>destroy!</tt>. If
-
# the <tt>before_destroy</tt> callback return +false+ the action is cancelled
-
# and <tt>destroy!</tt> raises ActiveRecord::RecordNotDestroyed. See
-
# ActiveRecord::Callbacks for further details.
-
1
def destroy!
-
destroy || raise(ActiveRecord::RecordNotDestroyed)
-
end
-
-
# Returns an instance of the specified +klass+ with the attributes of the
-
# current record. This is mostly useful in relation to single-table
-
# inheritance structures where you want a subclass to appear as the
-
# superclass. This can be used along with record identification in
-
# Action Pack to allow, say, <tt>Client < Company</tt> to do something
-
# like render <tt>partial: @client.becomes(Company)</tt> to render that
-
# instance using the companies/company partial instead of clients/client.
-
#
-
# Note: The new instance will share a link to the same attributes as the original class.
-
# So any change to the attributes in either instance will affect the other.
-
1
def becomes(klass)
-
became = klass.new
-
became.instance_variable_set("@attributes", @attributes)
-
became.instance_variable_set("@attributes_cache", @attributes_cache)
-
became.instance_variable_set("@changed_attributes", @changed_attributes) if defined?(@changed_attributes)
-
became.instance_variable_set("@new_record", new_record?)
-
became.instance_variable_set("@destroyed", destroyed?)
-
became.instance_variable_set("@errors", errors)
-
became
-
end
-
-
# Wrapper around +becomes+ that also changes the instance's sti column value.
-
# This is especially useful if you want to persist the changed class in your
-
# database.
-
#
-
# Note: The old instance's sti column value will be changed too, as both objects
-
# share the same set of attributes.
-
1
def becomes!(klass)
-
became = becomes(klass)
-
sti_type = nil
-
if !klass.descends_from_active_record?
-
sti_type = klass.sti_name
-
end
-
became.public_send("#{klass.inheritance_column}=", sti_type)
-
became
-
end
-
-
# Updates a single attribute and saves the record.
-
# This is especially useful for boolean flags on existing records. Also note that
-
#
-
# * Validation is skipped.
-
# * Callbacks are invoked.
-
# * updated_at/updated_on column is updated if that column is available.
-
# * Updates all the attributes that are dirty in this object.
-
#
-
# This method raises an +ActiveRecord::ActiveRecordError+ if the
-
# attribute is marked as readonly.
-
1
def update_attribute(name, value)
-
name = name.to_s
-
verify_readonly_attribute(name)
-
send("#{name}=", value)
-
save(validate: false)
-
end
-
-
# Updates the attributes of the model from the passed-in hash and saves the
-
# record, all wrapped in a transaction. If the object is invalid, the saving
-
# will fail and false will be returned.
-
1
def update(attributes)
-
# The following transaction covers any possible database side-effects of the
-
# attributes assignment. For example, setting the IDs of a child collection.
-
with_transaction_returning_status do
-
assign_attributes(attributes)
-
save
-
end
-
end
-
-
1
alias update_attributes update
-
-
# Updates its receiver just like +update+ but calls <tt>save!</tt> instead
-
# of +save+, so an exception is raised if the record is invalid.
-
1
def update!(attributes)
-
# The following transaction covers any possible database side-effects of the
-
# attributes assignment. For example, setting the IDs of a child collection.
-
with_transaction_returning_status do
-
assign_attributes(attributes)
-
save!
-
end
-
end
-
-
1
alias update_attributes! update!
-
-
# Equivalent to <code>update_columns(name => value)</code>.
-
1
def update_column(name, value)
-
update_columns(name => value)
-
end
-
-
# Updates the attributes directly in the database issuing an UPDATE SQL
-
# statement and sets them in the receiver:
-
#
-
# user.update_columns(last_request_at: Time.current)
-
#
-
# This is the fastest way to update attributes because it goes straight to
-
# the database, but take into account that in consequence the regular update
-
# procedures are totally bypassed. In particular:
-
#
-
# * Validations are skipped.
-
# * Callbacks are skipped.
-
# * +updated_at+/+updated_on+ are not updated.
-
#
-
# This method raises an +ActiveRecord::ActiveRecordError+ when called on new
-
# objects, or when at least one of the attributes is marked as readonly.
-
1
def update_columns(attributes)
-
raise ActiveRecordError, "cannot update on a new record object" unless persisted?
-
-
attributes.each_key do |key|
-
verify_readonly_attribute(key.to_s)
-
end
-
-
updated_count = self.class.unscoped.where(self.class.primary_key => id).update_all(attributes)
-
-
attributes.each do |k, v|
-
raw_write_attribute(k, v)
-
end
-
-
updated_count == 1
-
end
-
-
# Initializes +attribute+ to zero if +nil+ and adds the value passed as +by+ (default is 1).
-
# The increment is performed directly on the underlying attribute, no setter is invoked.
-
# Only makes sense for number-based attributes. Returns +self+.
-
1
def increment(attribute, by = 1)
-
self[attribute] ||= 0
-
self[attribute] += by
-
self
-
end
-
-
# Wrapper around +increment+ that saves the record. This method differs from
-
# its non-bang version in that it passes through the attribute setter.
-
# Saving is not subjected to validation checks. Returns +true+ if the
-
# record could be saved.
-
1
def increment!(attribute, by = 1)
-
increment(attribute, by).update_attribute(attribute, self[attribute])
-
end
-
-
# Initializes +attribute+ to zero if +nil+ and subtracts the value passed as +by+ (default is 1).
-
# The decrement is performed directly on the underlying attribute, no setter is invoked.
-
# Only makes sense for number-based attributes. Returns +self+.
-
1
def decrement(attribute, by = 1)
-
self[attribute] ||= 0
-
self[attribute] -= by
-
self
-
end
-
-
# Wrapper around +decrement+ that saves the record. This method differs from
-
# its non-bang version in that it passes through the attribute setter.
-
# Saving is not subjected to validation checks. Returns +true+ if the
-
# record could be saved.
-
1
def decrement!(attribute, by = 1)
-
decrement(attribute, by).update_attribute(attribute, self[attribute])
-
end
-
-
# Assigns to +attribute+ the boolean opposite of <tt>attribute?</tt>. So
-
# if the predicate returns +true+ the attribute will become +false+. This
-
# method toggles directly the underlying value without calling any setter.
-
# Returns +self+.
-
1
def toggle(attribute)
-
self[attribute] = !send("#{attribute}?")
-
self
-
end
-
-
# Wrapper around +toggle+ that saves the record. This method differs from
-
# its non-bang version in that it passes through the attribute setter.
-
# Saving is not subjected to validation checks. Returns +true+ if the
-
# record could be saved.
-
1
def toggle!(attribute)
-
toggle(attribute).update_attribute(attribute, self[attribute])
-
end
-
-
# Reloads the record from the database.
-
#
-
# This method finds record by its primary key (which could be assigned manually) and
-
# modifies the receiver in-place:
-
#
-
# account = Account.new
-
# # => #<Account id: nil, email: nil>
-
# account.id = 1
-
# account.reload
-
# # Account Load (1.2ms) SELECT "accounts".* FROM "accounts" WHERE "accounts"."id" = $1 LIMIT 1 [["id", 1]]
-
# # => #<Account id: 1, email: 'account@example.com'>
-
#
-
# Attributes are reloaded from the database, and caches busted, in
-
# particular the associations cache.
-
#
-
# If the record no longer exists in the database <tt>ActiveRecord::RecordNotFound</tt>
-
# is raised. Otherwise, in addition to the in-place modification the method
-
# returns +self+ for convenience.
-
#
-
# The optional <tt>:lock</tt> flag option allows you to lock the reloaded record:
-
#
-
# reload(lock: true) # reload with pessimistic locking
-
#
-
# Reloading is commonly used in test suites to test something is actually
-
# written to the database, or when some action modifies the corresponding
-
# row in the database but not the object in memory:
-
#
-
# assert account.deposit!(25)
-
# assert_equal 25, account.credit # check it is updated in memory
-
# assert_equal 25, account.reload.credit # check it is also persisted
-
#
-
# Another common use case is optimistic locking handling:
-
#
-
# def with_optimistic_retry
-
# begin
-
# yield
-
# rescue ActiveRecord::StaleObjectError
-
# begin
-
# # Reload lock_version in particular.
-
# reload
-
# rescue ActiveRecord::RecordNotFound
-
# # If the record is gone there is nothing to do.
-
# else
-
# retry
-
# end
-
# end
-
# end
-
#
-
1
def reload(options = nil)
-
clear_aggregation_cache
-
clear_association_cache
-
-
fresh_object =
-
if options && options[:lock]
-
self.class.unscoped { self.class.lock(options[:lock]).find(id) }
-
else
-
self.class.unscoped { self.class.find(id) }
-
end
-
-
@attributes.update(fresh_object.instance_variable_get('@attributes'))
-
-
@column_types = self.class.column_types
-
@column_types_override = fresh_object.instance_variable_get('@column_types_override')
-
@attributes_cache = {}
-
@new_record = false
-
self
-
end
-
-
# Saves the record with the updated_at/on attributes set to the current time.
-
# Please note that no validation is performed and only the +after_touch+
-
# callback is executed.
-
# If an attribute name is passed, that attribute is updated along with
-
# updated_at/on attributes.
-
#
-
# product.touch # updates updated_at/on
-
# product.touch(:designed_at) # updates the designed_at attribute and updated_at/on
-
#
-
# If used along with +belongs_to+ then +touch+ will invoke +touch+ method on associated object.
-
#
-
# class Brake < ActiveRecord::Base
-
# belongs_to :car, touch: true
-
# end
-
#
-
# class Car < ActiveRecord::Base
-
# belongs_to :corporation, touch: true
-
# end
-
#
-
# # triggers @brake.car.touch and @brake.car.corporation.touch
-
# @brake.touch
-
#
-
# Note that +touch+ must be used on a persisted object, or else an
-
# ActiveRecordError will be thrown. For example:
-
#
-
# ball = Ball.new
-
# ball.touch(:updated_at) # => raises ActiveRecordError
-
#
-
1
def touch(name = nil)
-
raise ActiveRecordError, "cannot touch on a new record object" unless persisted?
-
-
attributes = timestamp_attributes_for_update_in_model
-
attributes << name if name
-
-
unless attributes.empty?
-
current_time = current_time_from_proper_timezone
-
changes = {}
-
-
attributes.each do |column|
-
column = column.to_s
-
changes[column] = write_attribute(column, current_time)
-
end
-
-
changes[self.class.locking_column] = increment_lock if locking_enabled?
-
-
changed_attributes.except!(*changes.keys)
-
primary_key = self.class.primary_key
-
self.class.unscoped.where(primary_key => self[primary_key]).update_all(changes) == 1
-
else
-
true
-
end
-
end
-
-
1
private
-
-
# A hook to be overridden by association modules.
-
1
def destroy_associations
-
end
-
-
1
def destroy_row
-
relation_for_destroy.delete_all
-
end
-
-
1
def relation_for_destroy
-
pk = self.class.primary_key
-
column = self.class.columns_hash[pk]
-
substitute = self.class.connection.substitute_at(column, 0)
-
-
relation = self.class.unscoped.where(
-
self.class.arel_table[pk].eq(substitute))
-
-
relation.bind_values = [[column, id]]
-
relation
-
end
-
-
1
def create_or_update
-
8
raise ReadOnlyRecord if readonly?
-
8
result = new_record? ? _create_record : _update_record
-
8
result != false
-
end
-
-
# Updates the associated record with values matching those of the instance attributes.
-
# Returns the number of affected rows.
-
1
def _update_record(attribute_names = @attributes.keys)
-
2
attributes_values = arel_attributes_with_values_for_update(attribute_names)
-
2
if attributes_values.empty?
-
2
0
-
else
-
self.class.unscoped._update_record attributes_values, id, id_was
-
end
-
end
-
-
# Creates a record with values matching those of the instance attributes
-
# and returns its id.
-
1
def _create_record(attribute_names = @attributes.keys)
-
6
attributes_values = arel_attributes_with_values_for_create(attribute_names)
-
-
6
new_id = self.class.unscoped.insert attributes_values
-
6
self.id ||= new_id if self.class.primary_key
-
-
6
@new_record = false
-
6
id
-
end
-
-
1
def verify_readonly_attribute(name)
-
raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attributes.include?(name)
-
end
-
end
-
end
-
-
1
module ActiveRecord
-
# = Active Record Query Cache
-
1
class QueryCache
-
1
module ClassMethods
-
# Enable the query cache within the block if Active Record is configured.
-
# If it's not, it will execute the given block.
-
1
def cache(&block)
-
if ActiveRecord::Base.connected?
-
connection.cache(&block)
-
else
-
yield
-
end
-
end
-
-
# Disable the query cache within the block if Active Record is configured.
-
# If it's not, it will execute the given block.
-
1
def uncached(&block)
-
if ActiveRecord::Base.connected?
-
connection.uncached(&block)
-
else
-
yield
-
end
-
end
-
end
-
-
1
def initialize(app)
-
1
@app = app
-
end
-
-
1
def call(env)
-
enabled = ActiveRecord::Base.connection.query_cache_enabled
-
connection_id = ActiveRecord::Base.connection_id
-
ActiveRecord::Base.connection.enable_query_cache!
-
-
response = @app.call(env)
-
response[2] = Rack::BodyProxy.new(response[2]) do
-
restore_query_cache_settings(connection_id, enabled)
-
end
-
-
response
-
rescue Exception => e
-
restore_query_cache_settings(connection_id, enabled)
-
raise e
-
end
-
-
1
private
-
-
1
def restore_query_cache_settings(connection_id, enabled)
-
ActiveRecord::Base.connection_id = connection_id
-
ActiveRecord::Base.connection.clear_query_cache
-
ActiveRecord::Base.connection.disable_query_cache! unless enabled
-
end
-
-
end
-
end
-
1
module ActiveRecord
-
1
module Querying
-
1
delegate :find, :take, :take!, :first, :first!, :last, :last!, :exists?, :any?, :many?, to: :all
-
1
delegate :second, :second!, :third, :third!, :fourth, :fourth!, :fifth, :fifth!, :forty_two, :forty_two!, to: :all
-
1
delegate :first_or_create, :first_or_create!, :first_or_initialize, to: :all
-
1
delegate :find_or_create_by, :find_or_create_by!, :find_or_initialize_by, to: :all
-
1
delegate :find_by, :find_by!, to: :all
-
1
delegate :destroy, :destroy_all, :delete, :delete_all, :update, :update_all, to: :all
-
1
delegate :find_each, :find_in_batches, to: :all
-
1
delegate :select, :group, :order, :except, :reorder, :limit, :offset, :joins,
-
:where, :rewhere, :preload, :eager_load, :includes, :from, :lock, :readonly,
-
:having, :create_with, :uniq, :distinct, :references, :none, :unscope, to: :all
-
1
delegate :count, :average, :minimum, :maximum, :sum, :calculate, to: :all
-
1
delegate :pluck, :ids, to: :all
-
-
# Executes a custom SQL query against your database and returns all the results. The results will
-
# be returned as an array with columns requested encapsulated as attributes of the model you call
-
# this method from. If you call <tt>Product.find_by_sql</tt> then the results will be returned in
-
# a +Product+ object with the attributes you specified in the SQL query.
-
#
-
# If you call a complicated SQL query which spans multiple tables the columns specified by the
-
# SELECT will be attributes of the model, whether or not they are columns of the corresponding
-
# table.
-
#
-
# The +sql+ parameter is a full SQL query as a string. It will be called as is, there will be
-
# no database agnostic conversions performed. This should be a last resort because using, for example,
-
# MySQL specific terms will lock you to using that particular database engine or require you to
-
# change your call if you switch engines.
-
#
-
# # A simple SQL query spanning multiple tables
-
# Post.find_by_sql "SELECT p.title, c.author FROM posts p, comments c WHERE p.id = c.post_id"
-
# # => [#<Post:0x36bff9c @attributes={"title"=>"Ruby Meetup", "first_name"=>"Quentin"}>, ...]
-
#
-
# You can use the same string replacement techniques as you can with <tt>ActiveRecord::QueryMethods#where</tt>:
-
#
-
# Post.find_by_sql ["SELECT title FROM posts WHERE author = ? AND created > ?", author_id, start_date]
-
# Post.find_by_sql ["SELECT body FROM comments WHERE author = :user_id OR approved_by = :user_id", { :user_id => user_id }]
-
1
def find_by_sql(sql, binds = [])
-
5
result_set = connection.select_all(sanitize_sql(sql), "#{name} Load", binds)
-
5
column_types = {}
-
-
5
if result_set.respond_to? :column_types
-
5
column_types = result_set.column_types
-
else
-
ActiveSupport::Deprecation.warn "the object returned from `select_all` must respond to `column_types`"
-
end
-
-
21
result_set.map { |record| instantiate(record, column_types) }
-
end
-
-
# Returns the result of an SQL statement that should only include a COUNT(*) in the SELECT part.
-
# The use of this method should be restricted to complicated SQL queries that can't be executed
-
# using the ActiveRecord::Calculations class methods. Look into those before using this.
-
#
-
# ==== Parameters
-
#
-
# * +sql+ - An SQL statement which should return a count query from the database, see the example below.
-
#
-
# Product.count_by_sql "SELECT COUNT(*) FROM sales s, customers c WHERE s.customer_id = c.id"
-
1
def count_by_sql(sql)
-
sql = sanitize_conditions(sql)
-
connection.select_value(sql, "#{name} Count").to_i
-
end
-
end
-
end
-
1
require "active_record"
-
1
require "rails"
-
1
require "active_model/railtie"
-
-
# For now, action_controller must always be present with
-
# rails, so let's make sure that it gets required before
-
# here. This is needed for correctly setting up the middleware.
-
# In the future, this might become an optional require.
-
1
require "action_controller/railtie"
-
-
1
module ActiveRecord
-
# = Active Record Railtie
-
1
class Railtie < Rails::Railtie # :nodoc:
-
1
config.active_record = ActiveSupport::OrderedOptions.new
-
-
1
config.app_generators.orm :active_record, :migration => true,
-
:timestamps => true
-
-
1
config.app_middleware.insert_after "::ActionDispatch::Callbacks",
-
"ActiveRecord::QueryCache"
-
-
1
config.app_middleware.insert_after "::ActionDispatch::Callbacks",
-
"ActiveRecord::ConnectionAdapters::ConnectionManagement"
-
-
1
config.action_dispatch.rescue_responses.merge!(
-
'ActiveRecord::RecordNotFound' => :not_found,
-
'ActiveRecord::StaleObjectError' => :conflict,
-
'ActiveRecord::RecordInvalid' => :unprocessable_entity,
-
'ActiveRecord::RecordNotSaved' => :unprocessable_entity
-
)
-
-
-
1
config.active_record.use_schema_cache_dump = true
-
1
config.active_record.maintain_test_schema = true
-
-
1
config.eager_load_namespaces << ActiveRecord
-
-
1
rake_tasks do
-
require "active_record/base"
-
-
namespace :db do
-
task :load_config do
-
ActiveRecord::Tasks::DatabaseTasks.database_configuration = Rails.application.config.database_configuration
-
-
if defined?(ENGINE_PATH) && engine = Rails::Engine.find(ENGINE_PATH)
-
if engine.paths['db/migrate'].existent
-
ActiveRecord::Tasks::DatabaseTasks.migrations_paths += engine.paths['db/migrate'].to_a
-
end
-
end
-
end
-
end
-
-
load "active_record/railties/databases.rake"
-
end
-
-
# When loading console, force ActiveRecord::Base to be loaded
-
# to avoid cross references when loading a constant for the
-
# first time. Also, make it output to STDERR.
-
1
console do |app|
-
require "active_record/railties/console_sandbox" if app.sandbox?
-
require "active_record/base"
-
console = ActiveSupport::Logger.new(STDERR)
-
Rails.logger.extend ActiveSupport::Logger.broadcast console
-
end
-
-
1
runner do
-
require "active_record/base"
-
end
-
-
1
initializer "active_record.initialize_timezone" do
-
1
ActiveSupport.on_load(:active_record) do
-
1
self.time_zone_aware_attributes = true
-
1
self.default_timezone = :utc
-
end
-
end
-
-
1
initializer "active_record.logger" do
-
2
ActiveSupport.on_load(:active_record) { self.logger ||= ::Rails.logger }
-
end
-
-
1
initializer "active_record.migration_error" do
-
1
if config.active_record.delete(:migration_error) == :page_load
-
config.app_middleware.insert_after "::ActionDispatch::Callbacks",
-
"ActiveRecord::Migration::CheckPending"
-
end
-
end
-
-
1
initializer "active_record.check_schema_cache_dump" do
-
1
if config.active_record.delete(:use_schema_cache_dump)
-
1
config.after_initialize do |app|
-
1
ActiveSupport.on_load(:active_record) do
-
1
filename = File.join(app.config.paths["db"].first, "schema_cache.dump")
-
-
1
if File.file?(filename)
-
cache = Marshal.load File.binread filename
-
if cache.version == ActiveRecord::Migrator.current_version
-
self.connection.schema_cache = cache
-
else
-
warn "Ignoring db/schema_cache.dump because it has expired. The current schema version is #{ActiveRecord::Migrator.current_version}, but the one in the cache is #{cache.version}."
-
end
-
end
-
end
-
end
-
end
-
end
-
-
1
initializer "active_record.set_configs" do |app|
-
1
ActiveSupport.on_load(:active_record) do
-
1
app.config.active_record.each do |k,v|
-
1
send "#{k}=", v
-
end
-
end
-
end
-
-
# This sets the database configuration from Configuration#database_configuration
-
# and then establishes the connection.
-
1
initializer "active_record.initialize_database" do |app|
-
1
ActiveSupport.on_load(:active_record) do
-
-
1
class ActiveRecord::NoDatabaseError
-
1
remove_possible_method :extend_message
-
1
def extend_message(message)
-
message << "Run `$ bin/rake db:create db:migrate` to create your database"
-
message
-
end
-
end
-
-
1
self.configurations = Rails.application.config.database_configuration
-
1
establish_connection
-
end
-
end
-
-
# Expose database runtime to controller for logging.
-
1
initializer "active_record.log_runtime" do
-
1
require "active_record/railties/controller_runtime"
-
1
ActiveSupport.on_load(:action_controller) do
-
1
include ActiveRecord::Railties::ControllerRuntime
-
end
-
end
-
-
1
initializer "active_record.set_reloader_hooks" do |app|
-
1
hook = app.config.reload_classes_only_on_change ? :to_prepare : :to_cleanup
-
-
1
ActiveSupport.on_load(:active_record) do
-
1
ActionDispatch::Reloader.send(hook) do
-
if ActiveRecord::Base.connected?
-
ActiveRecord::Base.clear_reloadable_connections!
-
ActiveRecord::Base.clear_cache!
-
end
-
end
-
end
-
end
-
-
1
initializer "active_record.add_watchable_files" do |app|
-
1
path = app.paths["db"].first
-
1
config.watchable_files.concat ["#{path}/schema.rb", "#{path}/structure.sql"]
-
end
-
end
-
end
-
1
require 'active_support/core_ext/module/attr_internal'
-
1
require 'active_record/log_subscriber'
-
-
1
module ActiveRecord
-
1
module Railties # :nodoc:
-
1
module ControllerRuntime #:nodoc:
-
1
extend ActiveSupport::Concern
-
-
1
protected
-
-
1
attr_internal :db_runtime
-
-
1
def process_action(action, *args)
-
# We also need to reset the runtime before each action
-
# because of queries in middleware or in cases we are streaming
-
# and it won't be cleaned up by the method below.
-
4
ActiveRecord::LogSubscriber.reset_runtime
-
4
super
-
end
-
-
1
def cleanup_view_runtime
-
2
if ActiveRecord::Base.connected?
-
2
db_rt_before_render = ActiveRecord::LogSubscriber.reset_runtime
-
2
self.db_runtime = (db_runtime || 0) + db_rt_before_render
-
2
runtime = super
-
2
db_rt_after_render = ActiveRecord::LogSubscriber.reset_runtime
-
2
self.db_runtime += db_rt_after_render
-
2
runtime - db_rt_after_render
-
else
-
super
-
end
-
end
-
-
1
def append_info_to_payload(payload)
-
4
super
-
4
if ActiveRecord::Base.connected?
-
4
payload[:db_runtime] = (db_runtime || 0) + ActiveRecord::LogSubscriber.reset_runtime
-
end
-
end
-
-
1
module ClassMethods # :nodoc:
-
1
def log_process_action(payload)
-
4
messages, db_runtime = super, payload[:db_runtime]
-
4
messages << ("ActiveRecord: %.1fms" % db_runtime.to_f) if db_runtime
-
4
messages
-
end
-
end
-
end
-
end
-
end
-
-
1
module ActiveRecord
-
1
module ReadonlyAttributes
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
class_attribute :_attr_readonly, instance_accessor: false
-
1
self._attr_readonly = []
-
end
-
-
1
module ClassMethods
-
# Attributes listed as readonly will be used to create a new record but update operations will
-
# ignore these fields.
-
1
def attr_readonly(*attributes)
-
self._attr_readonly = Set.new(attributes.map { |a| a.to_s }) + (self._attr_readonly || [])
-
end
-
-
# Returns an array of all the attributes that have been specified as readonly.
-
1
def readonly_attributes
-
self._attr_readonly
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
# = Active Record Reflection
-
1
module Reflection # :nodoc:
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
class_attribute :_reflections
-
1
class_attribute :aggregate_reflections
-
1
self._reflections = {}
-
1
self.aggregate_reflections = {}
-
end
-
-
1
def self.create(macro, name, scope, options, ar)
-
14
case macro
-
when :has_many, :belongs_to, :has_one
-
14
klass = options[:through] ? ThroughReflection : AssociationReflection
-
when :composed_of
-
klass = AggregateReflection
-
end
-
-
14
klass.new(macro, name, scope, options, ar)
-
end
-
-
1
def self.add_reflection(ar, name, reflection)
-
14
ar._reflections = ar._reflections.merge(name.to_sym => reflection)
-
end
-
-
1
def self.add_aggregate_reflection(ar, name, reflection)
-
ar.aggregate_reflections = ar.aggregate_reflections.merge(name.to_sym => reflection)
-
end
-
-
# \Reflection enables to interrogate Active Record classes and objects
-
# about their associations and aggregations. This information can,
-
# for example, be used in a form builder that takes an Active Record object
-
# and creates input fields for all of the attributes depending on their type
-
# and displays the associations to other objects.
-
#
-
# MacroReflection class has info for AggregateReflection and AssociationReflection
-
# classes.
-
1
module ClassMethods
-
# Returns an array of AggregateReflection objects for all the aggregations in the class.
-
1
def reflect_on_all_aggregations
-
aggregate_reflections.values
-
end
-
-
# Returns the AggregateReflection object for the named +aggregation+ (use the symbol).
-
#
-
# Account.reflect_on_aggregation(:balance) # => the balance AggregateReflection
-
#
-
1
def reflect_on_aggregation(aggregation)
-
aggregate_reflections[aggregation.to_sym]
-
end
-
-
# Returns a Hash of name of the reflection as the key and a AssociationReflection as the value.
-
#
-
# Account.reflections # => {balance: AggregateReflection}
-
#
-
# @api public
-
1
def reflections
-
ref = {}
-
_reflections.each do |name, reflection|
-
parent_name, parent_reflection = reflection.parent_reflection
-
if parent_name
-
ref[parent_name] = parent_reflection
-
else
-
ref[name] = reflection
-
end
-
end
-
ref
-
end
-
-
# Returns an array of AssociationReflection objects for all the
-
# associations in the class. If you only want to reflect on a certain
-
# association type, pass in the symbol (<tt>:has_many</tt>, <tt>:has_one</tt>,
-
# <tt>:belongs_to</tt>) as the first parameter.
-
#
-
# Example:
-
#
-
# Account.reflect_on_all_associations # returns an array of all associations
-
# Account.reflect_on_all_associations(:has_many) # returns an array of all has_many associations
-
#
-
# @api public
-
1
def reflect_on_all_associations(macro = nil)
-
association_reflections = reflections.values
-
macro ? association_reflections.select { |reflection| reflection.macro == macro } : association_reflections
-
end
-
-
# Returns the AssociationReflection object for the +association+ (use the symbol).
-
#
-
# Account.reflect_on_association(:owner) # returns the owner AssociationReflection
-
# Invoice.reflect_on_association(:line_items).macro # returns :has_many
-
#
-
# @api public
-
1
def reflect_on_association(association)
-
reflections[association.to_sym]
-
end
-
-
# @api private
-
1
def _reflect_on_association(association) #:nodoc:
-
136
_reflections[association.to_sym]
-
end
-
-
# Returns an array of AssociationReflection objects for all associations which have <tt>:autosave</tt> enabled.
-
#
-
# @api public
-
1
def reflect_on_all_autosave_associations
-
reflections.values.select { |reflection| reflection.options[:autosave] }
-
end
-
end
-
-
# Base class for AggregateReflection and AssociationReflection. Objects of
-
# AggregateReflection and AssociationReflection are returned by the Reflection::ClassMethods.
-
#
-
# MacroReflection
-
# AggregateReflection
-
# AssociationReflection
-
# ThroughReflection
-
1
class MacroReflection
-
# Returns the name of the macro.
-
#
-
# <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>:balance</tt>
-
# <tt>has_many :clients</tt> returns <tt>:clients</tt>
-
1
attr_reader :name
-
-
# Returns the macro type.
-
#
-
# <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>:composed_of</tt>
-
# <tt>has_many :clients</tt> returns <tt>:has_many</tt>
-
1
attr_reader :macro
-
-
1
attr_reader :scope
-
-
# Returns the hash of options used for the macro.
-
#
-
# <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>{ class_name: "Money" }</tt>
-
# <tt>has_many :clients</tt> returns <tt>{}</tt>
-
1
attr_reader :options
-
-
1
attr_reader :active_record
-
-
1
attr_reader :plural_name # :nodoc:
-
-
1
def initialize(macro, name, scope, options, active_record)
-
14
@macro = macro
-
14
@name = name
-
14
@scope = scope
-
14
@options = options
-
14
@active_record = active_record
-
14
@klass = options[:class]
-
14
@plural_name = active_record.pluralize_table_names ?
-
name.to_s.pluralize : name.to_s
-
end
-
-
1
def autosave=(autosave)
-
@automatic_inverse_of = false
-
@options[:autosave] = autosave
-
_, parent_reflection = self.parent_reflection
-
if parent_reflection
-
parent_reflection.autosave = autosave
-
end
-
end
-
-
# Returns the class for the macro.
-
#
-
# <tt>composed_of :balance, class_name: 'Money'</tt> returns the Money class
-
# <tt>has_many :clients</tt> returns the Client class
-
1
def klass
-
@klass ||= class_name.constantize
-
end
-
-
# Returns the class name for the macro.
-
#
-
# <tt>composed_of :balance, class_name: 'Money'</tt> returns <tt>'Money'</tt>
-
# <tt>has_many :clients</tt> returns <tt>'Client'</tt>
-
1
def class_name
-
9
@class_name ||= (options[:class_name] || derive_class_name).to_s
-
end
-
-
# Returns +true+ if +self+ and +other_aggregation+ have the same +name+ attribute, +active_record+ attribute,
-
# and +other_aggregation+ has an options hash assigned to it.
-
1
def ==(other_aggregation)
-
super ||
-
other_aggregation.kind_of?(self.class) &&
-
name == other_aggregation.name &&
-
!other_aggregation.options.nil? &&
-
36
active_record == other_aggregation.active_record
-
end
-
-
1
private
-
1
def derive_class_name
-
name.to_s.camelize
-
end
-
end
-
-
-
# Holds all the meta-data about an aggregation as it was specified in the
-
# Active Record class.
-
1
class AggregateReflection < MacroReflection #:nodoc:
-
1
def mapping
-
mapping = options[:mapping] || [name, name]
-
mapping.first.is_a?(Array) ? mapping : [mapping]
-
end
-
end
-
-
# Holds all the meta-data about an association as it was specified in the
-
# Active Record class.
-
1
class AssociationReflection < MacroReflection #:nodoc:
-
# Returns the target association's class.
-
#
-
# class Author < ActiveRecord::Base
-
# has_many :books
-
# end
-
#
-
# Author.reflect_on_association(:books).klass
-
# # => Book
-
#
-
# <b>Note:</b> Do not call +klass.new+ or +klass.create+ to instantiate
-
# a new association object. Use +build_association+ or +create_association+
-
# instead. This allows plugins to hook into association object creation.
-
1
def klass
-
178
@klass ||= active_record.send(:compute_type, class_name)
-
end
-
-
1
attr_reader :type, :foreign_type
-
1
attr_accessor :parent_reflection # [:name, Reflection]
-
-
1
def initialize(macro, name, scope, options, active_record)
-
14
super
-
14
@collection = [:has_many, :has_and_belongs_to_many].include?(macro)
-
14
@automatic_inverse_of = nil
-
14
@type = options[:as] && "#{options[:as]}_type"
-
14
@foreign_type = options[:foreign_type] || "#{name}_type"
-
14
@constructable = calculate_constructable(macro, options)
-
end
-
-
# Returns a new, unsaved instance of the associated class. +attributes+ will
-
# be passed to the class's constructor.
-
1
def build_association(attributes, &block)
-
4
klass.new(attributes, &block)
-
end
-
-
1
def constructable? # :nodoc:
-
3
@constructable
-
end
-
-
1
def table_name
-
4
klass.table_name
-
end
-
-
1
def quoted_table_name
-
klass.quoted_table_name
-
end
-
-
1
def join_table
-
@join_table ||= options[:join_table] || derive_join_table
-
end
-
-
1
def foreign_key
-
46
@foreign_key ||= options[:foreign_key] || derive_foreign_key
-
end
-
-
1
def primary_key_column
-
klass.columns_hash[klass.primary_key]
-
end
-
-
1
def association_foreign_key
-
@association_foreign_key ||= options[:association_foreign_key] || class_name.foreign_key
-
end
-
-
# klass option is necessary to support loading polymorphic associations
-
1
def association_primary_key(klass = nil)
-
2
options[:primary_key] || primary_key(klass || self.klass)
-
end
-
-
1
def active_record_primary_key
-
8
@active_record_primary_key ||= options[:primary_key] || primary_key(active_record)
-
end
-
-
1
def counter_cache_column
-
2
if options[:counter_cache] == true
-
"#{active_record.name.demodulize.underscore.pluralize}_count"
-
2
elsif options[:counter_cache]
-
options[:counter_cache].to_s
-
end
-
end
-
-
1
def check_validity!
-
8
check_validity_of_inverse!
-
end
-
-
1
def check_validity_of_inverse!
-
12
unless options[:polymorphic]
-
12
if has_inverse? && inverse_of.nil?
-
raise InverseOfAssociationNotFoundError.new(self)
-
end
-
end
-
end
-
-
1
def through_reflection
-
nil
-
end
-
-
1
def source_reflection
-
self
-
end
-
-
# A chain of reflections from this one back to the owner. For more see the explanation in
-
# ThroughReflection.
-
1
def chain
-
8
[self]
-
end
-
-
1
def nested?
-
false
-
end
-
-
# An array of arrays of scopes. Each item in the outside array corresponds to a reflection
-
# in the #chain.
-
1
def scope_chain
-
8
scope ? [[scope]] : [[]]
-
end
-
-
1
alias :source_macro :macro
-
-
1
def has_inverse?
-
12
inverse_name
-
end
-
-
1
def inverse_of
-
16
return unless inverse_name
-
-
12
@inverse_of ||= klass._reflect_on_association inverse_name
-
end
-
-
1
def polymorphic_inverse_of(associated_class)
-
if has_inverse?
-
if inverse_relationship = associated_class._reflect_on_association(options[:inverse_of])
-
inverse_relationship
-
else
-
raise InverseOfAssociationNotFoundError.new(self, associated_class)
-
end
-
end
-
end
-
-
# Returns whether or not this association reflection is for a collection
-
# association. Returns +true+ if the +macro+ is either +has_many+ or
-
# +has_and_belongs_to_many+, +false+ otherwise.
-
1
def collection?
-
19
@collection
-
end
-
-
# Returns whether or not the association should be validated as part of
-
# the parent's validation.
-
#
-
# Unless you explicitly disable validation with
-
# <tt>validate: false</tt>, validation will take place when:
-
#
-
# * you explicitly enable validation; <tt>validate: true</tt>
-
# * you use autosave; <tt>autosave: true</tt>
-
# * the association is a +has_many+ association
-
1
def validate?
-
14
!options[:validate].nil? ? options[:validate] : (options[:autosave] == true || macro == :has_many)
-
end
-
-
# Returns +true+ if +self+ is a +belongs_to+ reflection.
-
1
def belongs_to?
-
5
macro == :belongs_to
-
end
-
-
1
def association_class
-
12
case macro
-
when :belongs_to
-
4
if options[:polymorphic]
-
Associations::BelongsToPolymorphicAssociation
-
else
-
4
Associations::BelongsToAssociation
-
end
-
when :has_many
-
8
if options[:through]
-
4
Associations::HasManyThroughAssociation
-
else
-
4
Associations::HasManyAssociation
-
end
-
when :has_one
-
if options[:through]
-
Associations::HasOneThroughAssociation
-
else
-
Associations::HasOneAssociation
-
end
-
end
-
end
-
-
1
def polymorphic?
-
options.key? :polymorphic
-
end
-
-
1
VALID_AUTOMATIC_INVERSE_MACROS = [:has_many, :has_one, :belongs_to]
-
1
INVALID_AUTOMATIC_INVERSE_OPTIONS = [:conditions, :through, :polymorphic, :foreign_key]
-
-
1
protected
-
-
1
def actual_source_reflection # FIXME: this is a horrible name
-
4
self
-
end
-
-
1
private
-
-
1
def calculate_constructable(macro, options)
-
14
case macro
-
when :belongs_to
-
3
!options[:polymorphic]
-
when :has_one
-
!options[:through]
-
else
-
11
true
-
end
-
end
-
-
# Attempts to find the inverse association name automatically.
-
# If it cannot find a suitable inverse association name, it returns
-
# nil.
-
1
def inverse_name
-
30
options.fetch(:inverse_of) do
-
30
if @automatic_inverse_of == false
-
8
nil
-
else
-
22
@automatic_inverse_of ||= automatic_inverse_of
-
end
-
end
-
end
-
-
# returns either nil or the inverse association name that it finds.
-
1
def automatic_inverse_of
-
6
if can_find_inverse_of_automatically?(self)
-
4
inverse_name = ActiveSupport::Inflector.underscore(options[:as] || active_record.name).to_sym
-
-
4
begin
-
4
reflection = klass._reflect_on_association(inverse_name)
-
rescue NameError
-
# Give up: we couldn't compute the klass type so we won't be able
-
# to find any associations either.
-
reflection = false
-
end
-
-
4
if valid_inverse_reflection?(reflection)
-
2
return inverse_name
-
end
-
end
-
-
4
false
-
end
-
-
# Checks if the inverse reflection that is returned from the
-
# +automatic_inverse_of+ method is a valid reflection. We must
-
# make sure that the reflection's active_record name matches up
-
# with the current reflection's klass name.
-
#
-
# Note: klass will always be valid because when there's a NameError
-
# from calling +klass+, +reflection+ will already be set to false.
-
1
def valid_inverse_reflection?(reflection)
-
reflection &&
-
4
klass.name == reflection.active_record.name &&
-
can_find_inverse_of_automatically?(reflection)
-
end
-
-
# Checks to see if the reflection doesn't have any options that prevent
-
# us from being able to guess the inverse automatically. First, the
-
# <tt>inverse_of</tt> option cannot be set to false. Second, we must
-
# have <tt>has_many</tt>, <tt>has_one</tt>, <tt>belongs_to</tt> associations.
-
# Third, we must not have options such as <tt>:polymorphic</tt> or
-
# <tt>:foreign_key</tt> which prevent us from correctly guessing the
-
# inverse association.
-
#
-
# Anything with a scope can additionally ruin our attempt at finding an
-
# inverse, so we exclude reflections with scopes.
-
1
def can_find_inverse_of_automatically?(reflection)
-
reflection.options[:inverse_of] != false &&
-
8
VALID_AUTOMATIC_INVERSE_MACROS.include?(reflection.macro) &&
-
28
!INVALID_AUTOMATIC_INVERSE_OPTIONS.any? { |opt| reflection.options[opt] } &&
-
!reflection.scope
-
end
-
-
1
def derive_class_name
-
5
class_name = name.to_s
-
5
class_name = class_name.singularize if collection?
-
5
class_name.camelize
-
end
-
-
1
def derive_foreign_key
-
5
if belongs_to?
-
2
"#{name}_id"
-
3
elsif options[:as]
-
"#{options[:as]}_id"
-
else
-
3
active_record.name.foreign_key
-
end
-
end
-
-
1
def derive_join_table
-
[active_record.table_name, klass.table_name].sort.join("\0").gsub(/^(.*_)(.+)\0\1(.+)/, '\1\2_\3').gsub("\0", "_")
-
end
-
-
1
def primary_key(klass)
-
9
klass.primary_key || raise(UnknownPrimaryKey.new(klass))
-
end
-
end
-
-
# Holds all the meta-data about a :through association as it was specified
-
# in the Active Record class.
-
1
class ThroughReflection < AssociationReflection #:nodoc:
-
1
delegate :foreign_key, :foreign_type, :association_foreign_key,
-
:active_record_primary_key, :type, :to => :source_reflection
-
-
1
def initialize(macro, name, scope, options, active_record)
-
4
super
-
4
@source_reflection_name = options[:source]
-
end
-
-
# Returns the source of the through reflection. It checks both a singularized
-
# and pluralized form for <tt>:belongs_to</tt> or <tt>:has_many</tt>.
-
#
-
# class Post < ActiveRecord::Base
-
# has_many :taggings
-
# has_many :tags, through: :taggings
-
# end
-
#
-
# class Tagging < ActiveRecord::Base
-
# belongs_to :post
-
# belongs_to :tag
-
# end
-
#
-
# tags_reflection = Post.reflect_on_association(:tags)
-
# tags_reflection.source_reflection
-
# # => <ActiveRecord::Reflection::AssociationReflection: @macro=:belongs_to, @name=:tag, @active_record=Tagging, @plural_name="tags">
-
#
-
1
def source_reflection
-
40
through_reflection.klass._reflect_on_association(source_reflection_name)
-
end
-
-
# Returns the AssociationReflection object specified in the <tt>:through</tt> option
-
# of a HasManyThrough or HasOneThrough association.
-
#
-
# class Post < ActiveRecord::Base
-
# has_many :taggings
-
# has_many :tags, through: :taggings
-
# end
-
#
-
# tags_reflection = Post.reflect_on_association(:tags)
-
# tags_reflection.through_reflection
-
# # => <ActiveRecord::Reflection::AssociationReflection: @macro=:has_many, @name=:taggings, @active_record=Post, @plural_name="taggings">
-
#
-
1
def through_reflection
-
62
active_record._reflect_on_association(options[:through])
-
end
-
-
# Returns an array of reflections which are involved in this association. Each item in the
-
# array corresponds to a table which will be part of the query for this association.
-
#
-
# The chain is built by recursively calling #chain on the source reflection and the through
-
# reflection. The base case for the recursion is a normal association, which just returns
-
# [self] as its #chain.
-
#
-
# class Post < ActiveRecord::Base
-
# has_many :taggings
-
# has_many :tags, through: :taggings
-
# end
-
#
-
# tags_reflection = Post.reflect_on_association(:tags)
-
# tags_reflection.chain
-
# # => [<ActiveRecord::Reflection::ThroughReflection: @macro=:has_many, @name=:tags, @options={:through=>:taggings}, @active_record=Post>,
-
# <ActiveRecord::Reflection::AssociationReflection: @macro=:has_many, @name=:taggings, @options={}, @active_record=Post>]
-
#
-
1
def chain
-
@chain ||= begin
-
2
a = source_reflection.chain
-
2
b = through_reflection.chain
-
2
chain = a + b
-
2
chain[0] = self # Use self so we don't lose the information from :source_type
-
2
chain
-
18
end
-
end
-
-
# Consider the following example:
-
#
-
# class Person
-
# has_many :articles
-
# has_many :comment_tags, through: :articles
-
# end
-
#
-
# class Article
-
# has_many :comments
-
# has_many :comment_tags, through: :comments, source: :tags
-
# end
-
#
-
# class Comment
-
# has_many :tags
-
# end
-
#
-
# There may be scopes on Person.comment_tags, Article.comment_tags and/or Comment.tags,
-
# but only Comment.tags will be represented in the #chain. So this method creates an array
-
# of scopes corresponding to the chain.
-
1
def scope_chain
-
@scope_chain ||= begin
-
2
scope_chain = source_reflection.scope_chain.map(&:dup)
-
-
# Add to it the scope from this reflection (if any)
-
2
scope_chain.first << scope if scope
-
-
2
through_scope_chain = through_reflection.scope_chain.map(&:dup)
-
-
2
if options[:source_type]
-
through_scope_chain.first <<
-
through_reflection.klass.where(foreign_type => options[:source_type])
-
end
-
-
# Recursively fill out the rest of the array from the through reflection
-
2
scope_chain + through_scope_chain
-
4
end
-
end
-
-
# The macro used by the source association
-
1
def source_macro
-
4
source_reflection.source_macro
-
end
-
-
# A through association is nested if there would be more than one join table
-
1
def nested?
-
4
chain.length > 2
-
end
-
-
# We want to use the klass from this reflection, rather than just delegate straight to
-
# the source_reflection, because the source_reflection may be polymorphic. We still
-
# need to respect the source_reflection's :primary_key option, though.
-
1
def association_primary_key(klass = nil)
-
# Get the "actual" source reflection if the immediate source reflection has a
-
# source reflection itself
-
4
actual_source_reflection.options[:primary_key] || primary_key(klass || self.klass)
-
end
-
-
# Gets an array of possible <tt>:through</tt> source reflection names in both singular and plural form.
-
#
-
# class Post < ActiveRecord::Base
-
# has_many :taggings
-
# has_many :tags, through: :taggings
-
# end
-
#
-
# tags_reflection = Post.reflect_on_association(:tags)
-
# tags_reflection.source_reflection_names
-
# # => [:tag, :tags]
-
#
-
1
def source_reflection_names
-
(options[:source] ? [options[:source]] : [name.to_s.singularize, name]).collect { |n| n.to_sym }.uniq
-
end
-
-
1
def source_reflection_name # :nodoc:
-
40
return @source_reflection_name.to_sym if @source_reflection_name
-
-
6
names = [name.to_s.singularize, name].collect { |n| n.to_sym }.uniq
-
2
names = names.find_all { |n|
-
4
through_reflection.klass._reflect_on_association(n)
-
}
-
-
2
if names.length > 1
-
example_options = options.dup
-
example_options[:source] = source_reflection_names.first
-
ActiveSupport::Deprecation.warn <<-eowarn
-
Ambiguous source reflection for through association. Please specify a :source
-
directive on your declaration like:
-
-
class #{active_record.name} < ActiveRecord::Base
-
#{macro} :#{name}, #{example_options}
-
end
-
-
eowarn
-
end
-
-
2
@source_reflection_name = names.first
-
end
-
-
1
def source_options
-
source_reflection.options
-
end
-
-
1
def through_options
-
through_reflection.options
-
end
-
-
1
def check_validity!
-
4
if through_reflection.nil?
-
raise HasManyThroughAssociationNotFoundError.new(active_record.name, self)
-
end
-
-
4
if through_reflection.options[:polymorphic]
-
raise HasManyThroughAssociationPolymorphicThroughError.new(active_record.name, self)
-
end
-
-
4
if source_reflection.nil?
-
raise HasManyThroughSourceAssociationNotFoundError.new(self)
-
end
-
-
4
if options[:source_type] && source_reflection.options[:polymorphic].nil?
-
raise HasManyThroughAssociationPointlessSourceTypeError.new(active_record.name, self, source_reflection)
-
end
-
-
4
if source_reflection.options[:polymorphic] && options[:source_type].nil?
-
raise HasManyThroughAssociationPolymorphicSourceError.new(active_record.name, self, source_reflection)
-
end
-
-
4
if macro == :has_one && through_reflection.collection?
-
raise HasOneThroughCantAssociateThroughCollection.new(active_record.name, self, through_reflection)
-
end
-
-
4
check_validity_of_inverse!
-
end
-
-
1
protected
-
-
1
def actual_source_reflection # FIXME: this is a horrible name
-
4
source_reflection.actual_source_reflection
-
end
-
-
1
private
-
1
def derive_class_name
-
# get the class_name of the belongs_to association of the through reflection
-
2
options[:source_type] || source_reflection.class_name
-
end
-
end
-
end
-
end
-
# -*- coding: utf-8 -*-
-
-
1
module ActiveRecord
-
# = Active Record Relation
-
1
class Relation
-
1
JoinOperation = Struct.new(:relation, :join_class, :on)
-
-
1
MULTI_VALUE_METHODS = [:includes, :eager_load, :preload, :select, :group,
-
:order, :joins, :where, :having, :bind, :references,
-
:extending, :unscope]
-
-
1
SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :from, :reordering,
-
:reverse_order, :distinct, :create_with, :uniq]
-
-
1
VALUE_METHODS = MULTI_VALUE_METHODS + SINGLE_VALUE_METHODS
-
-
1
include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches, Explain, Delegation
-
-
1
attr_reader :table, :klass, :loaded
-
1
alias :model :klass
-
1
alias :loaded? :loaded
-
-
1
def initialize(klass, table, values = {})
-
108
@klass = klass
-
108
@table = table
-
108
@values = values
-
108
@offsets = {}
-
108
@loaded = false
-
end
-
-
1
def initialize_copy(other)
-
# This method is a hot spot, so for now, use Hash[] to dup the hash.
-
# https://bugs.ruby-lang.org/issues/7166
-
30
@values = Hash[@values]
-
30
@values[:bind] = @values[:bind].dup if @values.key? :bind
-
30
reset
-
end
-
-
1
def insert(values) # :nodoc:
-
6
primary_key_value = nil
-
-
6
if primary_key && Hash === values
-
6
primary_key_value = values[values.keys.find { |k|
-
24
k.name == primary_key
-
}]
-
-
6
if !primary_key_value && connection.prefetch_primary_key?(klass.table_name)
-
primary_key_value = connection.next_sequence_value(klass.sequence_name)
-
values[klass.arel_table[klass.primary_key]] = primary_key_value
-
end
-
end
-
-
6
im = arel.create_insert
-
6
im.into @table
-
-
6
substitutes, binds = substitute_values values
-
-
6
if values.empty? # empty insert
-
im.values = Arel.sql(connection.empty_insert_statement_value)
-
else
-
6
im.insert substitutes
-
end
-
-
6
@klass.connection.insert(
-
im,
-
'SQL',
-
primary_key,
-
primary_key_value,
-
nil,
-
binds)
-
end
-
-
1
def _update_record(values, id, id_was) # :nodoc:
-
substitutes, binds = substitute_values values
-
-
scope = @klass.unscoped
-
-
if @klass.finder_needs_type_condition?
-
scope.unscope!(where: @klass.inheritance_column)
-
end
-
-
um = scope.where(@klass.arel_table[@klass.primary_key].eq(id_was || id)).arel.compile_update(substitutes, @klass.primary_key)
-
-
@klass.connection.update(
-
um,
-
'SQL',
-
binds)
-
end
-
-
1
def substitute_values(values) # :nodoc:
-
30
substitutes = values.sort_by { |arel_attr,_| arel_attr.name }
-
6
binds = substitutes.map do |arel_attr, value|
-
24
[@klass.columns_hash[arel_attr.name], value]
-
end
-
-
6
substitutes.each_with_index do |tuple, i|
-
24
tuple[1] = @klass.connection.substitute_at(binds[i][0], i)
-
end
-
-
6
[substitutes, binds]
-
end
-
-
# Initializes new record from relation while maintaining the current
-
# scope.
-
#
-
# Expects arguments in the same format as +Base.new+.
-
#
-
# users = User.where(name: 'DHH')
-
# user = users.new # => #<User id: nil, name: "DHH", created_at: nil, updated_at: nil>
-
#
-
# You can also pass a block to new with the new record as argument:
-
#
-
# user = users.new { |user| user.name = 'Oscar' }
-
# user.name # => Oscar
-
1
def new(*args, &block)
-
scoping { @klass.new(*args, &block) }
-
end
-
-
1
alias build new
-
-
# Tries to create a new record with the same scoped attributes
-
# defined in the relation. Returns the initialized object if validation fails.
-
#
-
# Expects arguments in the same format as +Base.create+.
-
#
-
# ==== Examples
-
# users = User.where(name: 'Oscar')
-
# users.create # #<User id: 3, name: "oscar", ...>
-
#
-
# users.create(name: 'fxn')
-
# users.create # #<User id: 4, name: "fxn", ...>
-
#
-
# users.create { |user| user.name = 'tenderlove' }
-
# # #<User id: 5, name: "tenderlove", ...>
-
#
-
# users.create(name: nil) # validation on name
-
# # #<User id: nil, name: nil, ...>
-
1
def create(*args, &block)
-
scoping { @klass.create(*args, &block) }
-
end
-
-
# Similar to #create, but calls +create!+ on the base class. Raises
-
# an exception if a validation error occurs.
-
#
-
# Expects arguments in the same format as <tt>Base.create!</tt>.
-
1
def create!(*args, &block)
-
scoping { @klass.create!(*args, &block) }
-
end
-
-
1
def first_or_create(attributes = nil, &block) # :nodoc:
-
first || create(attributes, &block)
-
end
-
-
1
def first_or_create!(attributes = nil, &block) # :nodoc:
-
first || create!(attributes, &block)
-
end
-
-
1
def first_or_initialize(attributes = nil, &block) # :nodoc:
-
first || new(attributes, &block)
-
end
-
-
# Finds the first record with the given attributes, or creates a record
-
# with the attributes if one is not found:
-
#
-
# # Find the first user named "Penélope" or create a new one.
-
# User.find_or_create_by(first_name: 'Penélope')
-
# # => #<User id: 1, first_name: "Penélope", last_name: nil>
-
#
-
# # Find the first user named "Penélope" or create a new one.
-
# # We already have one so the existing record will be returned.
-
# User.find_or_create_by(first_name: 'Penélope')
-
# # => #<User id: 1, first_name: "Penélope", last_name: nil>
-
#
-
# # Find the first user named "Scarlett" or create a new one with
-
# # a particular last name.
-
# User.create_with(last_name: 'Johansson').find_or_create_by(first_name: 'Scarlett')
-
# # => #<User id: 2, first_name: "Scarlett", last_name: "Johansson">
-
#
-
# This method accepts a block, which is passed down to +create+. The last example
-
# above can be alternatively written this way:
-
#
-
# # Find the first user named "Scarlett" or create a new one with a
-
# # different last name.
-
# User.find_or_create_by(first_name: 'Scarlett') do |user|
-
# user.last_name = 'Johansson'
-
# end
-
# # => #<User id: 2, first_name: "Scarlett", last_name: "Johansson">
-
#
-
# This method always returns a record, but if creation was attempted and
-
# failed due to validation errors it won't be persisted, you get what
-
# +create+ returns in such situation.
-
#
-
# Please note *this method is not atomic*, it runs first a SELECT, and if
-
# there are no results an INSERT is attempted. If there are other threads
-
# or processes there is a race condition between both calls and it could
-
# be the case that you end up with two similar records.
-
#
-
# Whether that is a problem or not depends on the logic of the
-
# application, but in the particular case in which rows have a UNIQUE
-
# constraint an exception may be raised, just retry:
-
#
-
# begin
-
# CreditAccount.find_or_create_by(user_id: user.id)
-
# rescue ActiveRecord::RecordNotUnique
-
# retry
-
# end
-
#
-
1
def find_or_create_by(attributes, &block)
-
find_by(attributes) || create(attributes, &block)
-
end
-
-
# Like <tt>find_or_create_by</tt>, but calls <tt>create!</tt> so an exception
-
# is raised if the created record is invalid.
-
1
def find_or_create_by!(attributes, &block)
-
find_by(attributes) || create!(attributes, &block)
-
end
-
-
# Like <tt>find_or_create_by</tt>, but calls <tt>new</tt> instead of <tt>create</tt>.
-
1
def find_or_initialize_by(attributes, &block)
-
find_by(attributes) || new(attributes, &block)
-
end
-
-
# Runs EXPLAIN on the query or queries triggered by this relation and
-
# returns the result as a string. The string is formatted imitating the
-
# ones printed by the database shell.
-
#
-
# Note that this method actually runs the queries, since the results of some
-
# are needed by the next ones when eager loading is going on.
-
#
-
# Please see further details in the
-
# {Active Record Query Interface guide}[http://guides.rubyonrails.org/active_record_querying.html#running-explain].
-
1
def explain
-
exec_explain(collecting_queries_for_explain { exec_queries })
-
end
-
-
# Converts relation objects to Array.
-
1
def to_a
-
5
load
-
5
@records
-
end
-
-
1
def as_json(options = nil) #:nodoc:
-
to_a.as_json(options)
-
end
-
-
# Returns size of the records.
-
1
def size
-
loaded? ? @records.length : count(:all)
-
end
-
-
# Returns true if there are no records.
-
1
def empty?
-
return @records.empty? if loaded?
-
-
if limit_value == 0
-
true
-
else
-
c = count(:all)
-
c.respond_to?(:zero?) ? c.zero? : c.empty?
-
end
-
end
-
-
# Returns true if there are any records.
-
1
def any?
-
if block_given?
-
to_a.any? { |*block_args| yield(*block_args) }
-
else
-
!empty?
-
end
-
end
-
-
# Returns true if there is more than one record.
-
1
def many?
-
if block_given?
-
to_a.many? { |*block_args| yield(*block_args) }
-
else
-
limit_value ? to_a.many? : size > 1
-
end
-
end
-
-
# Scope all queries to the current scope.
-
#
-
# Comment.where(post_id: 1).scoping do
-
# Comment.first
-
# end
-
# # => SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = 1 ORDER BY "comments"."id" ASC LIMIT 1
-
#
-
# Please check unscoped if you want to remove all previous scopes (including
-
# the default_scope) during the execution of a block.
-
1
def scoping
-
previous, klass.current_scope = klass.current_scope, self
-
yield
-
ensure
-
klass.current_scope = previous
-
end
-
-
# Updates all records with details given if they match a set of conditions supplied, limits and order can
-
# also be supplied. This method constructs a single SQL UPDATE statement and sends it straight to the
-
# database. It does not instantiate the involved models and it does not trigger Active Record callbacks
-
# or validations.
-
#
-
# ==== Parameters
-
#
-
# * +updates+ - A string, array, or hash representing the SET part of an SQL statement.
-
#
-
# ==== Examples
-
#
-
# # Update all customers with the given attributes
-
# Customer.update_all wants_email: true
-
#
-
# # Update all books with 'Rails' in their title
-
# Book.where('title LIKE ?', '%Rails%').update_all(author: 'David')
-
#
-
# # Update all books that match conditions, but limit it to 5 ordered by date
-
# Book.where('title LIKE ?', '%Rails%').order(:created_at).limit(5).update_all(author: 'David')
-
1
def update_all(updates)
-
raise ArgumentError, "Empty list of attributes to change" if updates.blank?
-
-
stmt = Arel::UpdateManager.new(arel.engine)
-
-
stmt.set Arel.sql(@klass.send(:sanitize_sql_for_assignment, updates))
-
stmt.table(table)
-
stmt.key = table[primary_key]
-
-
if joins_values.any?
-
@klass.connection.join_to_update(stmt, arel)
-
else
-
stmt.take(arel.limit)
-
stmt.order(*arel.orders)
-
stmt.wheres = arel.constraints
-
end
-
-
@klass.connection.update stmt, 'SQL', bind_values
-
end
-
-
# Updates an object (or multiple objects) and saves it to the database, if validations pass.
-
# The resulting object is returned whether the object was saved successfully to the database or not.
-
#
-
# ==== Parameters
-
#
-
# * +id+ - This should be the id or an array of ids to be updated.
-
# * +attributes+ - This should be a hash of attributes or an array of hashes.
-
#
-
# ==== Examples
-
#
-
# # Updates one record
-
# Person.update(15, user_name: 'Samuel', group: 'expert')
-
#
-
# # Updates multiple records
-
# people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy" } }
-
# Person.update(people.keys, people.values)
-
1
def update(id, attributes)
-
if id.is_a?(Array)
-
id.map.with_index { |one_id, idx| update(one_id, attributes[idx]) }
-
else
-
object = find(id)
-
object.update(attributes)
-
object
-
end
-
end
-
-
# Destroys the records matching +conditions+ by instantiating each
-
# record and calling its +destroy+ method. Each object's callbacks are
-
# executed (including <tt>:dependent</tt> association options). Returns the
-
# collection of objects that were destroyed; each will be frozen, to
-
# reflect that no changes should be made (since they can't be persisted).
-
#
-
# Note: Instantiation, callback execution, and deletion of each
-
# record can be time consuming when you're removing many records at
-
# once. It generates at least one SQL +DELETE+ query per record (or
-
# possibly more, to enforce your callbacks). If you want to delete many
-
# rows quickly, without concern for their associations or callbacks, use
-
# +delete_all+ instead.
-
#
-
# ==== Parameters
-
#
-
# * +conditions+ - A string, array, or hash that specifies which records
-
# to destroy. If omitted, all records are destroyed. See the
-
# Conditions section in the introduction to ActiveRecord::Base for
-
# more information.
-
#
-
# ==== Examples
-
#
-
# Person.destroy_all("last_login < '2004-04-04'")
-
# Person.destroy_all(status: "inactive")
-
# Person.where(age: 0..18).destroy_all
-
1
def destroy_all(conditions = nil)
-
if conditions
-
where(conditions).destroy_all
-
else
-
to_a.each {|object| object.destroy }.tap { reset }
-
end
-
end
-
-
# Destroy an object (or multiple objects) that has the given id. The object is instantiated first,
-
# therefore all callbacks and filters are fired off before the object is deleted. This method is
-
# less efficient than ActiveRecord#delete but allows cleanup methods and other actions to be run.
-
#
-
# This essentially finds the object (or multiple objects) with the given id, creates a new object
-
# from the attributes, and then calls destroy on it.
-
#
-
# ==== Parameters
-
#
-
# * +id+ - Can be either an Integer or an Array of Integers.
-
#
-
# ==== Examples
-
#
-
# # Destroy a single object
-
# Todo.destroy(1)
-
#
-
# # Destroy multiple objects
-
# todos = [1,2,3]
-
# Todo.destroy(todos)
-
1
def destroy(id)
-
if id.is_a?(Array)
-
id.map { |one_id| destroy(one_id) }
-
else
-
find(id).destroy
-
end
-
end
-
-
# Deletes the records matching +conditions+ without instantiating the records
-
# first, and hence not calling the +destroy+ method nor invoking callbacks. This
-
# is a single SQL DELETE statement that goes straight to the database, much more
-
# efficient than +destroy_all+. Be careful with relations though, in particular
-
# <tt>:dependent</tt> rules defined on associations are not honored. Returns the
-
# number of rows affected.
-
#
-
# Post.delete_all("person_id = 5 AND (category = 'Something' OR category = 'Else')")
-
# Post.delete_all(["person_id = ? AND (category = ? OR category = ?)", 5, 'Something', 'Else'])
-
# Post.where(person_id: 5).where(category: ['Something', 'Else']).delete_all
-
#
-
# Both calls delete the affected posts all at once with a single DELETE statement.
-
# If you need to destroy dependent associations or call your <tt>before_*</tt> or
-
# +after_destroy+ callbacks, use the +destroy_all+ method instead.
-
#
-
# If a limit scope is supplied, +delete_all+ raises an ActiveRecord error:
-
#
-
# Post.limit(100).delete_all
-
# # => ActiveRecord::ActiveRecordError: delete_all doesn't support limit scope
-
1
def delete_all(conditions = nil)
-
raise ActiveRecordError.new("delete_all doesn't support limit scope") if self.limit_value
-
-
if conditions
-
where(conditions).delete_all
-
else
-
stmt = Arel::DeleteManager.new(arel.engine)
-
stmt.from(table)
-
-
if joins_values.any?
-
@klass.connection.join_to_delete(stmt, arel, table[primary_key])
-
else
-
stmt.wheres = arel.constraints
-
end
-
-
affected = @klass.connection.delete(stmt, 'SQL', bind_values)
-
-
reset
-
affected
-
end
-
end
-
-
# Deletes the row with a primary key matching the +id+ argument, using a
-
# SQL +DELETE+ statement, and returns the number of rows deleted. Active
-
# Record objects are not instantiated, so the object's callbacks are not
-
# executed, including any <tt>:dependent</tt> association options.
-
#
-
# You can delete multiple rows at once by passing an Array of <tt>id</tt>s.
-
#
-
# Note: Although it is often much faster than the alternative,
-
# <tt>#destroy</tt>, skipping callbacks might bypass business logic in
-
# your application that ensures referential integrity or performs other
-
# essential jobs.
-
#
-
# ==== Examples
-
#
-
# # Delete a single row
-
# Todo.delete(1)
-
#
-
# # Delete multiple rows
-
# Todo.delete([2,3,4])
-
1
def delete(id_or_array)
-
where(primary_key => id_or_array).delete_all
-
end
-
-
# Causes the records to be loaded from the database if they have not
-
# been loaded already. You can use this if for some reason you need
-
# to explicitly load some records before actually using them. The
-
# return value is the relation itself, not the records.
-
#
-
# Post.where(published: true).load # => #<ActiveRecord::Relation>
-
1
def load
-
5
exec_queries unless loaded?
-
-
5
self
-
end
-
-
# Forces reloading of relation.
-
1
def reload
-
reset
-
load
-
end
-
-
1
def reset
-
30
@last = @to_sql = @order_clause = @scope_for_create = @arel = @loaded = nil
-
30
@should_eager_load = @join_dependency = nil
-
30
@records = []
-
30
@offsets = {}
-
30
self
-
end
-
-
# Returns sql statement for the relation.
-
#
-
# User.where(name: 'Oscar').to_sql
-
# # => SELECT "users".* FROM "users" WHERE "users"."name" = 'Oscar'
-
1
def to_sql
-
@to_sql ||= begin
-
relation = self
-
connection = klass.connection
-
visitor = connection.visitor
-
-
if eager_loading?
-
find_with_associations { |rel| relation = rel }
-
end
-
-
ast = relation.arel.ast
-
binds = relation.bind_values.dup
-
visitor.accept(ast) do
-
connection.quote(*binds.shift.reverse)
-
end
-
end
-
end
-
-
# Returns a hash of where conditions.
-
#
-
# User.where(name: 'Oscar').where_values_hash
-
# # => {name: "Oscar"}
-
1
def where_values_hash(relation_table_name = table_name)
-
6
equalities = where_values.grep(Arel::Nodes::Equality).find_all { |node|
-
6
node.left.relation.name == relation_table_name
-
}
-
-
12
binds = Hash[bind_values.find_all(&:first).map { |column, v| [column.name, v] }]
-
-
6
Hash[equalities.map { |where|
-
4
name = where.left.name
-
4
[name, binds.fetch(name.to_s) { where.right }]
-
}]
-
end
-
-
1
def scope_for_create
-
4
@scope_for_create ||= where_values_hash.merge(create_with_value)
-
end
-
-
# Returns true if relation needs eager loading.
-
1
def eager_loading?
-
@should_eager_load ||=
-
eager_load_values.any? ||
-
12
includes_values.any? && (joined_includes_values.any? || references_eager_loaded_tables?)
-
end
-
-
# Joins that are also marked for preloading. In which case we should just eager load them.
-
# Note that this is a naive implementation because we could have strings and symbols which
-
# represent the same association, but that aren't matched by this. Also, we could have
-
# nested hashes which partially match, e.g. { a: :b } & { a: [:b, :c] }
-
1
def joined_includes_values
-
includes_values & joins_values
-
end
-
-
# +uniq+ and +uniq!+ are silently deprecated. +uniq_value+ delegates to +distinct_value+
-
# to maintain backwards compatibility. Use +distinct_value+ instead.
-
1
def uniq_value
-
distinct_value
-
end
-
-
# Compares two relations for equality.
-
1
def ==(other)
-
case other
-
when Associations::CollectionProxy, AssociationRelation
-
self == other.to_a
-
when Relation
-
other.to_sql == to_sql
-
when Array
-
to_a == other
-
end
-
end
-
-
1
def pretty_print(q)
-
q.pp(self.to_a)
-
end
-
-
# Returns true if relation is blank.
-
1
def blank?
-
to_a.blank?
-
end
-
-
1
def values
-
58
Hash[@values]
-
end
-
-
1
def inspect
-
entries = to_a.take([limit_value, 11].compact.min).map!(&:inspect)
-
entries[10] = '...' if entries.size == 11
-
-
"#<#{self.class.name} [#{entries.join(', ')}]>"
-
end
-
-
1
private
-
-
1
def exec_queries
-
5
@records = eager_loading? ? find_with_associations : @klass.find_by_sql(arel, bind_values)
-
-
5
preload = preload_values
-
5
preload += includes_values unless eager_loading?
-
5
preloader = ActiveRecord::Associations::Preloader.new
-
5
preload.each do |associations|
-
preloader.preload @records, associations
-
end
-
-
5
@records.each { |record| record.readonly! } if readonly_value
-
-
5
@loaded = true
-
5
@records
-
end
-
-
1
def references_eager_loaded_tables?
-
joined_tables = arel.join_sources.map do |join|
-
if join.is_a?(Arel::Nodes::StringJoin)
-
tables_in_string(join.left)
-
else
-
[join.left.table_name, join.left.table_alias]
-
end
-
end
-
-
joined_tables += [table.name, table.table_alias]
-
-
# always convert table names to downcase as in Oracle quoted table names are in uppercase
-
joined_tables = joined_tables.flatten.compact.map { |t| t.downcase }.uniq
-
-
(references_values - joined_tables).any?
-
end
-
-
1
def tables_in_string(string)
-
return [] if string.blank?
-
# always convert table names to downcase as in Oracle quoted table names are in uppercase
-
# ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries
-
string.scan(/([a-zA-Z_][.\w]+).?\./).flatten.map{ |s| s.downcase }.uniq - ['raw_sql_']
-
end
-
end
-
end
-
-
1
module ActiveRecord
-
1
module Batches
-
# Looping through a collection of records from the database
-
# (using the +all+ method, for example) is very inefficient
-
# since it will try to instantiate all the objects at once.
-
#
-
# In that case, batch processing methods allow you to work
-
# with the records in batches, thereby greatly reducing memory consumption.
-
#
-
# The #find_each method uses #find_in_batches with a batch size of 1000 (or as
-
# specified by the +:batch_size+ option).
-
#
-
# Person.find_each do |person|
-
# person.do_awesome_stuff
-
# end
-
#
-
# Person.where("age > 21").find_each do |person|
-
# person.party_all_night!
-
# end
-
#
-
# If you do not provide a block to #find_each, it will return an Enumerator
-
# for chaining with other methods:
-
#
-
# Person.find_each.with_index do |person, index|
-
# person.award_trophy(index + 1)
-
# end
-
#
-
# ==== Options
-
# * <tt>:batch_size</tt> - Specifies the size of the batch. Default to 1000.
-
# * <tt>:start</tt> - Specifies the starting point for the batch processing.
-
# This is especially useful if you want multiple workers dealing with
-
# the same processing queue. You can make worker 1 handle all the records
-
# between id 0 and 10,000 and worker 2 handle from 10,000 and beyond
-
# (by setting the +:start+ option on that worker).
-
#
-
# # Let's process for a batch of 2000 records, skipping the first 2000 rows
-
# Person.find_each(start: 2000, batch_size: 2000) do |person|
-
# person.party_all_night!
-
# end
-
#
-
# NOTE: It's not possible to set the order. That is automatically set to
-
# ascending on the primary key ("id ASC") to make the batch ordering
-
# work. This also means that this method only works with integer-based
-
# primary keys.
-
#
-
# NOTE: You can't set the limit either, that's used to control
-
# the batch sizes.
-
1
def find_each(options = {})
-
if block_given?
-
find_in_batches(options) do |records|
-
records.each { |record| yield record }
-
end
-
else
-
enum_for :find_each, options do
-
options[:start] ? where(table[primary_key].gteq(options[:start])).size : size
-
end
-
end
-
end
-
-
# Yields each batch of records that was found by the find +options+ as
-
# an array.
-
#
-
# Person.where("age > 21").find_in_batches do |group|
-
# sleep(50) # Make sure it doesn't get too crowded in there!
-
# group.each { |person| person.party_all_night! }
-
# end
-
#
-
# If you do not provide a block to #find_in_batches, it will return an Enumerator
-
# for chaining with other methods:
-
#
-
# Person.find_in_batches.with_index do |group, batch|
-
# puts "Processing group ##{batch}"
-
# group.each(&:recover_from_last_night!)
-
# end
-
#
-
# To be yielded each record one by one, use #find_each instead.
-
#
-
# ==== Options
-
# * <tt>:batch_size</tt> - Specifies the size of the batch. Default to 1000.
-
# * <tt>:start</tt> - Specifies the starting point for the batch processing.
-
# This is especially useful if you want multiple workers dealing with
-
# the same processing queue. You can make worker 1 handle all the records
-
# between id 0 and 10,000 and worker 2 handle from 10,000 and beyond
-
# (by setting the +:start+ option on that worker).
-
#
-
# # Let's process the next 2000 records
-
# Person.find_in_batches(start: 2000, batch_size: 2000) do |group|
-
# group.each { |person| person.party_all_night! }
-
# end
-
#
-
# NOTE: It's not possible to set the order. That is automatically set to
-
# ascending on the primary key ("id ASC") to make the batch ordering
-
# work. This also means that this method only works with integer-based
-
# primary keys.
-
#
-
# NOTE: You can't set the limit either, that's used to control
-
# the batch sizes.
-
1
def find_in_batches(options = {})
-
options.assert_valid_keys(:start, :batch_size)
-
-
relation = self
-
start = options[:start]
-
batch_size = options[:batch_size] || 1000
-
-
unless block_given?
-
return to_enum(:find_in_batches, options) do
-
total = start ? where(table[primary_key].gteq(start)).size : size
-
(total - 1).div(batch_size) + 1
-
end
-
end
-
-
if logger && (arel.orders.present? || arel.taken.present?)
-
logger.warn("Scoped order and limit are ignored, it's forced to be batch order and batch size")
-
end
-
-
relation = relation.reorder(batch_order).limit(batch_size)
-
records = start ? relation.where(table[primary_key].gteq(start)).to_a : relation.to_a
-
-
while records.any?
-
records_size = records.size
-
primary_key_offset = records.last.id
-
raise "Primary key not included in the custom select clause" unless primary_key_offset
-
-
yield records
-
-
break if records_size < batch_size
-
-
records = relation.where(table[primary_key].gt(primary_key_offset)).to_a
-
end
-
end
-
-
1
private
-
-
1
def batch_order
-
"#{quoted_table_name}.#{quoted_primary_key} ASC"
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
module Calculations
-
# Count the records.
-
#
-
# Person.count
-
# # => the total count of all people
-
#
-
# Person.count(:age)
-
# # => returns the total count of all people whose age is present in database
-
#
-
# Person.count(:all)
-
# # => performs a COUNT(*) (:all is an alias for '*')
-
#
-
# Person.distinct.count(:age)
-
# # => counts the number of different age values
-
#
-
# If +count+ is used with +group+, it returns a Hash whose keys represent the aggregated column,
-
# and the values are the respective amounts:
-
#
-
# Person.group(:city).count
-
# # => { 'Rome' => 5, 'Paris' => 3 }
-
#
-
# If +count+ is used with +select+, it will count the selected columns:
-
#
-
# Person.select(:age).count
-
# # => counts the number of different age values
-
#
-
# Note: not all valid +select+ expressions are valid +count+ expressions. The specifics differ
-
# between databases. In invalid cases, an error from the databsae is thrown.
-
1
def count(column_name = nil, options = {})
-
# TODO: Remove options argument as soon we remove support to
-
# activerecord-deprecated_finders.
-
2
column_name, options = nil, column_name if column_name.is_a?(Hash)
-
2
calculate(:count, column_name, options)
-
end
-
-
# Calculates the average value on a given column. Returns +nil+ if there's
-
# no row. See +calculate+ for examples with options.
-
#
-
# Person.average(:age) # => 35.8
-
1
def average(column_name, options = {})
-
# TODO: Remove options argument as soon we remove support to
-
# activerecord-deprecated_finders.
-
calculate(:average, column_name, options)
-
end
-
-
# Calculates the minimum value on a given column. The value is returned
-
# with the same data type of the column, or +nil+ if there's no row. See
-
# +calculate+ for examples with options.
-
#
-
# Person.minimum(:age) # => 7
-
1
def minimum(column_name, options = {})
-
# TODO: Remove options argument as soon we remove support to
-
# activerecord-deprecated_finders.
-
calculate(:minimum, column_name, options)
-
end
-
-
# Calculates the maximum value on a given column. The value is returned
-
# with the same data type of the column, or +nil+ if there's no row. See
-
# +calculate+ for examples with options.
-
#
-
# Person.maximum(:age) # => 93
-
1
def maximum(column_name, options = {})
-
# TODO: Remove options argument as soon we remove support to
-
# activerecord-deprecated_finders.
-
calculate(:maximum, column_name, options)
-
end
-
-
# Calculates the sum of values on a given column. The value is returned
-
# with the same data type of the column, 0 if there's no row. See
-
# +calculate+ for examples with options.
-
#
-
# Person.sum(:age) # => 4562
-
1
def sum(*args)
-
calculate(:sum, *args)
-
end
-
-
# This calculates aggregate values in the given column. Methods for count, sum, average,
-
# minimum, and maximum have been added as shortcuts.
-
#
-
# There are two basic forms of output:
-
#
-
# * Single aggregate value: The single value is type cast to Fixnum for COUNT, Float
-
# for AVG, and the given column's type for everything else.
-
#
-
# * Grouped values: This returns an ordered hash of the values and groups them. It
-
# takes either a column name, or the name of a belongs_to association.
-
#
-
# values = Person.group('last_name').maximum(:age)
-
# puts values["Drake"]
-
# # => 43
-
#
-
# drake = Family.find_by(last_name: 'Drake')
-
# values = Person.group(:family).maximum(:age) # Person belongs_to :family
-
# puts values[drake]
-
# # => 43
-
#
-
# values.each do |family, max_age|
-
# ...
-
# end
-
#
-
# Person.calculate(:count, :all) # The same as Person.count
-
# Person.average(:age) # SELECT AVG(age) FROM people...
-
#
-
# # Selects the minimum age for any family without any minors
-
# Person.group(:last_name).having("min(age) > 17").minimum(:age)
-
#
-
# Person.sum("2 * age")
-
1
def calculate(operation, column_name, options = {})
-
# TODO: Remove options argument as soon we remove support to
-
# activerecord-deprecated_finders.
-
2
if column_name.is_a?(Symbol) && attribute_alias?(column_name)
-
column_name = attribute_alias(column_name)
-
end
-
-
2
if has_include?(column_name)
-
construct_relation_for_association_calculations.calculate(operation, column_name, options)
-
else
-
2
perform_calculation(operation, column_name, options)
-
end
-
end
-
-
# Use <tt>pluck</tt> as a shortcut to select one or more attributes without
-
# loading a bunch of records just to grab the attributes you want.
-
#
-
# Person.pluck(:name)
-
#
-
# instead of
-
#
-
# Person.all.map(&:name)
-
#
-
# Pluck returns an <tt>Array</tt> of attribute values type-casted to match
-
# the plucked column names, if they can be deduced. Plucking an SQL fragment
-
# returns String values by default.
-
#
-
# Person.pluck(:id)
-
# # SELECT people.id FROM people
-
# # => [1, 2, 3]
-
#
-
# Person.pluck(:id, :name)
-
# # SELECT people.id, people.name FROM people
-
# # => [[1, 'David'], [2, 'Jeremy'], [3, 'Jose']]
-
#
-
# Person.pluck('DISTINCT role')
-
# # SELECT DISTINCT role FROM people
-
# # => ['admin', 'member', 'guest']
-
#
-
# Person.where(age: 21).limit(5).pluck(:id)
-
# # SELECT people.id FROM people WHERE people.age = 21 LIMIT 5
-
# # => [2, 3]
-
#
-
# Person.pluck('DATEDIFF(updated_at, created_at)')
-
# # SELECT DATEDIFF(updated_at, created_at) FROM people
-
# # => ['0', '27761', '173']
-
#
-
1
def pluck(*column_names)
-
column_names.map! do |column_name|
-
if column_name.is_a?(Symbol) && attribute_alias?(column_name)
-
attribute_alias(column_name)
-
else
-
column_name.to_s
-
end
-
end
-
-
if has_include?(column_names.first)
-
construct_relation_for_association_calculations.pluck(*column_names)
-
else
-
relation = spawn
-
relation.select_values = column_names.map { |cn|
-
columns_hash.key?(cn) ? arel_table[cn] : cn
-
}
-
result = klass.connection.select_all(relation.arel, nil, bind_values)
-
columns = result.columns.map do |key|
-
klass.column_types.fetch(key) {
-
result.column_types.fetch(key) { result.identity_type }
-
}
-
end
-
-
result = result.rows.map do |values|
-
values = result.columns.zip(values).map do |column_name, value|
-
single_attr_hash = { column_name => value }
-
klass.initialize_attributes(single_attr_hash).values.first
-
end
-
-
columns.zip(values).map { |column, value| column.type_cast value }
-
end
-
columns.one? ? result.map!(&:first) : result
-
end
-
end
-
-
# Pluck all the ID's for the relation using the table's primary key
-
#
-
# Person.ids # SELECT people.id FROM people
-
# Person.joins(:companies).ids # SELECT people.id FROM people INNER JOIN companies ON companies.person_id = people.id
-
1
def ids
-
pluck primary_key
-
end
-
-
1
private
-
-
1
def has_include?(column_name)
-
2
eager_loading? || (includes_values.present? && ((column_name && column_name != :all) || references_eager_loaded_tables?))
-
end
-
-
1
def perform_calculation(operation, column_name, options = {})
-
# TODO: Remove options argument as soon we remove support to
-
# activerecord-deprecated_finders.
-
2
operation = operation.to_s.downcase
-
-
# If #count is used with #distinct / #uniq it is considered distinct. (eg. relation.distinct.count)
-
2
distinct = self.distinct_value
-
-
2
if operation == "count"
-
2
column_name ||= select_for_count
-
-
2
unless arel.ast.grep(Arel::Nodes::OuterJoin).empty?
-
distinct = true
-
end
-
-
2
column_name = primary_key if column_name == :all && distinct
-
2
distinct = nil if column_name =~ /\s*DISTINCT[\s(]+/i
-
end
-
-
2
if group_values.any?
-
execute_grouped_calculation(operation, column_name, distinct)
-
else
-
2
execute_simple_calculation(operation, column_name, distinct)
-
end
-
end
-
-
1
def aggregate_column(column_name)
-
2
if @klass.column_names.include?(column_name.to_s)
-
Arel::Attribute.new(@klass.unscoped.table, column_name)
-
else
-
2
Arel.sql(column_name == :all ? "*" : column_name.to_s)
-
end
-
end
-
-
1
def operation_over_aggregate_column(column, operation, distinct)
-
2
operation == 'count' ? column.count(distinct) : column.send(operation)
-
end
-
-
1
def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
-
# Postgresql doesn't like ORDER BY when there are no GROUP BY
-
2
relation = unscope(:order)
-
-
2
column_alias = column_name
-
-
2
if operation == "count" && (relation.limit_value || relation.offset_value)
-
# Shortcut when limit is zero.
-
return 0 if relation.limit_value == 0
-
-
query_builder = build_count_subquery(relation, column_name, distinct)
-
else
-
2
column = aggregate_column(column_name)
-
-
2
select_value = operation_over_aggregate_column(column, operation, distinct)
-
-
2
column_alias = select_value.alias
-
2
relation.select_values = [select_value]
-
-
2
query_builder = relation.arel
-
end
-
-
2
result = @klass.connection.select_all(query_builder, nil, relation.bind_values)
-
2
row = result.first
-
2
value = row && row.values.first
-
2
column = result.column_types.fetch(column_alias) do
-
2
column_for(column_name)
-
end
-
-
2
type_cast_calculated_value(value, column, operation)
-
end
-
-
1
def execute_grouped_calculation(operation, column_name, distinct) #:nodoc:
-
group_attrs = group_values
-
-
if group_attrs.first.respond_to?(:to_sym)
-
association = @klass._reflect_on_association(group_attrs.first.to_sym)
-
associated = group_attrs.size == 1 && association && association.macro == :belongs_to # only count belongs_to associations
-
group_fields = Array(associated ? association.foreign_key : group_attrs)
-
else
-
group_fields = group_attrs
-
end
-
-
group_aliases = group_fields.map { |field|
-
column_alias_for(field)
-
}
-
group_columns = group_aliases.zip(group_fields).map { |aliaz,field|
-
[aliaz, field]
-
}
-
-
group = group_fields
-
-
if operation == 'count' && column_name == :all
-
aggregate_alias = 'count_all'
-
else
-
aggregate_alias = column_alias_for([operation, column_name].join(' '))
-
end
-
-
select_values = [
-
operation_over_aggregate_column(
-
aggregate_column(column_name),
-
operation,
-
distinct).as(aggregate_alias)
-
]
-
select_values += select_values unless having_values.empty?
-
-
select_values.concat group_fields.zip(group_aliases).map { |field,aliaz|
-
if field.respond_to?(:as)
-
field.as(aliaz)
-
else
-
"#{field} AS #{aliaz}"
-
end
-
}
-
-
relation = except(:group)
-
relation.group_values = group
-
relation.select_values = select_values
-
-
calculated_data = @klass.connection.select_all(relation, nil, bind_values)
-
-
if association
-
key_ids = calculated_data.collect { |row| row[group_aliases.first] }
-
key_records = association.klass.base_class.find(key_ids)
-
key_records = Hash[key_records.map { |r| [r.id, r] }]
-
end
-
-
Hash[calculated_data.map do |row|
-
key = group_columns.map { |aliaz, col_name|
-
column = calculated_data.column_types.fetch(aliaz) do
-
column_for(col_name)
-
end
-
type_cast_calculated_value(row[aliaz], column)
-
}
-
key = key.first if key.size == 1
-
key = key_records[key] if associated
-
-
column_type = calculated_data.column_types.fetch(aggregate_alias) { column_for(column_name) }
-
[key, type_cast_calculated_value(row[aggregate_alias], column_type, operation)]
-
end]
-
end
-
-
# Converts the given keys to the value that the database adapter returns as
-
# a usable column name:
-
#
-
# column_alias_for("users.id") # => "users_id"
-
# column_alias_for("sum(id)") # => "sum_id"
-
# column_alias_for("count(distinct users.id)") # => "count_distinct_users_id"
-
# column_alias_for("count(*)") # => "count_all"
-
# column_alias_for("count", "id") # => "count_id"
-
1
def column_alias_for(keys)
-
if keys.respond_to? :name
-
keys = "#{keys.relation.name}.#{keys.name}"
-
end
-
-
table_name = keys.to_s.downcase
-
table_name.gsub!(/\*/, 'all')
-
table_name.gsub!(/\W+/, ' ')
-
table_name.strip!
-
table_name.gsub!(/ +/, '_')
-
-
@klass.connection.table_alias_for(table_name)
-
end
-
-
1
def column_for(field)
-
2
field_name = field.respond_to?(:name) ? field.name.to_s : field.to_s.split('.').last
-
2
@klass.columns_hash[field_name]
-
end
-
-
1
def type_cast_calculated_value(value, column, operation = nil)
-
2
case operation
-
2
when 'count' then value.to_i
-
when 'sum' then type_cast_using_column(value || 0, column)
-
when 'average' then value.respond_to?(:to_d) ? value.to_d : value
-
else type_cast_using_column(value, column)
-
end
-
end
-
-
1
def type_cast_using_column(value, column)
-
column ? column.type_cast(value) : value
-
end
-
-
# TODO: refactor to allow non-string `select_values` (eg. Arel nodes).
-
1
def select_for_count
-
2
if select_values.present?
-
select_values.join(", ")
-
else
-
2
:all
-
end
-
end
-
-
1
def build_count_subquery(relation, column_name, distinct)
-
column_alias = Arel.sql('count_column')
-
subquery_alias = Arel.sql('subquery_for_count')
-
-
aliased_column = aggregate_column(column_name == :all ? 1 : column_name).as(column_alias)
-
relation.select_values = [aliased_column]
-
subquery = relation.arel.as(subquery_alias)
-
-
sm = Arel::SelectManager.new relation.engine
-
select_value = operation_over_aggregate_column(column_alias, 'count', distinct)
-
sm.project(select_value).from(subquery)
-
end
-
end
-
end
-
1
require 'set'
-
1
require 'active_support/concern'
-
1
require 'active_support/deprecation'
-
-
1
module ActiveRecord
-
1
module Delegation # :nodoc:
-
1
module DelegateCache
-
1
def relation_delegate_class(klass) # :nodoc:
-
108
@relation_delegate_cache[klass]
-
end
-
-
1
def initialize_relation_delegate_cache # :nodoc:
-
5
@relation_delegate_cache = cache = {}
-
[
-
ActiveRecord::Relation,
-
ActiveRecord::Associations::CollectionProxy,
-
ActiveRecord::AssociationRelation
-
5
].each do |klass|
-
15
delegate = Class.new(klass) {
-
15
include ClassSpecificRelation
-
}
-
15
const_set klass.name.gsub('::', '_'), delegate
-
15
cache[klass] = delegate
-
end
-
end
-
-
1
def inherited(child_class)
-
5
child_class.initialize_relation_delegate_cache
-
5
super
-
end
-
end
-
-
1
extend ActiveSupport::Concern
-
-
# This module creates compiled delegation methods dynamically at runtime, which makes
-
# subsequent calls to that method faster by avoiding method_missing. The delegations
-
# may vary depending on the klass of a relation, so we create a subclass of Relation
-
# for each different klass, and the delegations are compiled into that subclass only.
-
-
1
BLACKLISTED_ARRAY_METHODS = [
-
:compact!, :flatten!, :reject!, :reverse!, :rotate!, :map!,
-
:shuffle!, :slice!, :sort!, :sort_by!, :delete_if,
-
:keep_if, :pop, :shift, :delete_at, :compact, :select!
-
].to_set # :nodoc:
-
-
1
delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to_ary, :join, to: :to_a
-
-
1
delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key,
-
:connection, :columns_hash, :to => :klass
-
-
1
module ClassSpecificRelation # :nodoc:
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
15
@delegation_mutex = Mutex.new
-
end
-
-
1
module ClassMethods # :nodoc:
-
1
def name
-
superclass.name
-
end
-
-
1
def delegate_to_scoped_klass(method)
-
@delegation_mutex.synchronize do
-
return if method_defined?(method)
-
-
if method.to_s =~ /\A[a-zA-Z_]\w*[!?]?\z/
-
module_eval <<-RUBY, __FILE__, __LINE__ + 1
-
def #{method}(*args, &block)
-
scoping { @klass.#{method}(*args, &block) }
-
end
-
RUBY
-
else
-
define_method method do |*args, &block|
-
scoping { @klass.public_send(method, *args, &block) }
-
end
-
end
-
end
-
end
-
-
1
def delegate(method, opts = {})
-
@delegation_mutex.synchronize do
-
return if method_defined?(method)
-
super
-
end
-
end
-
end
-
-
1
protected
-
-
1
def method_missing(method, *args, &block)
-
if @klass.respond_to?(method)
-
self.class.delegate_to_scoped_klass(method)
-
scoping { @klass.public_send(method, *args, &block) }
-
elsif arel.respond_to?(method)
-
self.class.delegate method, :to => :arel
-
arel.public_send(method, *args, &block)
-
else
-
super
-
end
-
end
-
end
-
-
1
module ClassMethods # :nodoc:
-
1
def create(klass, *args)
-
108
relation_class_for(klass).new(klass, *args)
-
end
-
-
1
private
-
-
1
def relation_class_for(klass)
-
108
klass.relation_delegate_class(self)
-
end
-
end
-
-
1
def respond_to?(method, include_private = false)
-
super || @klass.respond_to?(method, include_private) ||
-
array_delegable?(method) ||
-
arel.respond_to?(method, include_private)
-
end
-
-
1
protected
-
-
1
def array_delegable?(method)
-
Array.method_defined?(method) && BLACKLISTED_ARRAY_METHODS.exclude?(method)
-
end
-
-
1
def method_missing(method, *args, &block)
-
if @klass.respond_to?(method)
-
scoping { @klass.public_send(method, *args, &block) }
-
elsif array_delegable?(method)
-
to_a.public_send(method, *args, &block)
-
elsif arel.respond_to?(method)
-
arel.public_send(method, *args, &block)
-
else
-
super
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
module FinderMethods
-
1
ONE_AS_ONE = '1 AS one'
-
-
# Find by id - This can either be a specific id (1), a list of ids (1, 5, 6), or an array of ids ([5, 6, 10]).
-
# If no record can be found for all of the listed ids, then RecordNotFound will be raised. If the primary key
-
# is an integer, find by id coerces its arguments using +to_i+.
-
#
-
# Person.find(1) # returns the object for ID = 1
-
# Person.find("1") # returns the object for ID = 1
-
# Person.find("31-sarah") # returns the object for ID = 31
-
# Person.find(1, 2, 6) # returns an array for objects with IDs in (1, 2, 6)
-
# Person.find([7, 17]) # returns an array for objects with IDs in (7, 17)
-
# Person.find([1]) # returns an array for the object with ID = 1
-
# Person.where("administrator = 1").order("created_on DESC").find(1)
-
#
-
# <tt>ActiveRecord::RecordNotFound</tt> will be raised if one or more ids are not found.
-
#
-
# NOTE: The returned records may not be in the same order as the ids you
-
# provide since database rows are unordered. You'd need to provide an explicit <tt>order</tt>
-
# option if you want the results are sorted.
-
#
-
# ==== Find with lock
-
#
-
# Example for find with a lock: Imagine two concurrent transactions:
-
# each will read <tt>person.visits == 2</tt>, add 1 to it, and save, resulting
-
# in two saves of <tt>person.visits = 3</tt>. By locking the row, the second
-
# transaction has to wait until the first is finished; we get the
-
# expected <tt>person.visits == 4</tt>.
-
#
-
# Person.transaction do
-
# person = Person.lock(true).find(1)
-
# person.visits += 1
-
# person.save!
-
# end
-
#
-
# ==== Variations of +find+
-
#
-
# Person.where(name: 'Spartacus', rating: 4)
-
# # returns a chainable list (which can be empty).
-
#
-
# Person.find_by(name: 'Spartacus', rating: 4)
-
# # returns the first item or nil.
-
#
-
# Person.where(name: 'Spartacus', rating: 4).first_or_initialize
-
# # returns the first item or returns a new instance (requires you call .save to persist against the database).
-
#
-
# Person.where(name: 'Spartacus', rating: 4).first_or_create
-
# # returns the first item or creates it and returns it, available since Rails 3.2.1.
-
#
-
# ==== Alternatives for +find+
-
#
-
# Person.where(name: 'Spartacus', rating: 4).exists?(conditions = :none)
-
# # returns a boolean indicating if any record with the given conditions exist.
-
#
-
# Person.where(name: 'Spartacus', rating: 4).select("field1, field2, field3")
-
# # returns a chainable list of instances with only the mentioned fields.
-
#
-
# Person.where(name: 'Spartacus', rating: 4).ids
-
# # returns an Array of ids, available since Rails 3.2.1.
-
#
-
# Person.where(name: 'Spartacus', rating: 4).pluck(:field1, :field2)
-
# # returns an Array of the required fields, available since Rails 3.1.
-
1
def find(*args)
-
if block_given?
-
to_a.find(*args) { |*block_args| yield(*block_args) }
-
else
-
find_with_ids(*args)
-
end
-
end
-
-
# Finds the first record matching the specified conditions. There
-
# is no implied ordering so if order matters, you should specify it
-
# yourself.
-
#
-
# If no record is found, returns <tt>nil</tt>.
-
#
-
# Post.find_by name: 'Spartacus', rating: 4
-
# Post.find_by "published_at < ?", 2.weeks.ago
-
1
def find_by(*args)
-
where(*args).take
-
end
-
-
# Like <tt>find_by</tt>, except that if no record is found, raises
-
# an <tt>ActiveRecord::RecordNotFound</tt> error.
-
1
def find_by!(*args)
-
where(*args).take!
-
end
-
-
# Gives a record (or N records if a parameter is supplied) without any implied
-
# order. The order will depend on the database implementation.
-
# If an order is supplied it will be respected.
-
#
-
# Person.take # returns an object fetched by SELECT * FROM people LIMIT 1
-
# Person.take(5) # returns 5 objects fetched by SELECT * FROM people LIMIT 5
-
# Person.where(["name LIKE '%?'", name]).take
-
1
def take(limit = nil)
-
limit ? limit(limit).to_a : find_take
-
end
-
-
# Same as +take+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
-
# is found. Note that <tt>take!</tt> accepts no arguments.
-
1
def take!
-
take or raise RecordNotFound
-
end
-
-
# Find the first record (or first N records if a parameter is supplied).
-
# If no order is defined it will order by primary key.
-
#
-
# Person.first # returns the first object fetched by SELECT * FROM people
-
# Person.where(["user_name = ?", user_name]).first
-
# Person.where(["user_name = :u", { u: user_name }]).first
-
# Person.order("created_on DESC").offset(5).first
-
# Person.first(3) # returns the first three objects fetched by SELECT * FROM people LIMIT 3
-
#
-
# ==== Rails 3
-
#
-
# Person.first # SELECT "people".* FROM "people" LIMIT 1
-
#
-
# NOTE: Rails 3 may not order this query by the primary key and the order
-
# will depend on the database implementation. In order to ensure that behavior,
-
# use <tt>User.order(:id).first</tt> instead.
-
#
-
# ==== Rails 4
-
#
-
# Person.first # SELECT "people".* FROM "people" ORDER BY "people"."id" ASC LIMIT 1
-
#
-
1
def first(limit = nil)
-
if limit
-
find_nth_with_limit(offset_value, limit)
-
else
-
find_nth(:first, offset_value)
-
end
-
end
-
-
# Same as +first+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
-
# is found. Note that <tt>first!</tt> accepts no arguments.
-
1
def first!
-
first or raise RecordNotFound
-
end
-
-
# Find the last record (or last N records if a parameter is supplied).
-
# If no order is defined it will order by primary key.
-
#
-
# Person.last # returns the last object fetched by SELECT * FROM people
-
# Person.where(["user_name = ?", user_name]).last
-
# Person.order("created_on DESC").offset(5).last
-
# Person.last(3) # returns the last three objects fetched by SELECT * FROM people.
-
#
-
# Take note that in that last case, the results are sorted in ascending order:
-
#
-
# [#<Person id:2>, #<Person id:3>, #<Person id:4>]
-
#
-
# and not:
-
#
-
# [#<Person id:4>, #<Person id:3>, #<Person id:2>]
-
1
def last(limit = nil)
-
if limit
-
if order_values.empty? && primary_key
-
order(arel_table[primary_key].desc).limit(limit).reverse
-
else
-
to_a.last(limit)
-
end
-
else
-
find_last
-
end
-
end
-
-
# Same as +last+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
-
# is found. Note that <tt>last!</tt> accepts no arguments.
-
1
def last!
-
last or raise RecordNotFound
-
end
-
-
# Find the second record.
-
# If no order is defined it will order by primary key.
-
#
-
# Person.second # returns the second object fetched by SELECT * FROM people
-
# Person.offset(3).second # returns the second object from OFFSET 3 (which is OFFSET 4)
-
# Person.where(["user_name = :u", { u: user_name }]).second
-
1
def second
-
find_nth(:second, offset_value ? offset_value + 1 : 1)
-
end
-
-
# Same as +second+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
-
# is found.
-
1
def second!
-
second or raise RecordNotFound
-
end
-
-
# Find the third record.
-
# If no order is defined it will order by primary key.
-
#
-
# Person.third # returns the third object fetched by SELECT * FROM people
-
# Person.offset(3).third # returns the third object from OFFSET 3 (which is OFFSET 5)
-
# Person.where(["user_name = :u", { u: user_name }]).third
-
1
def third
-
find_nth(:third, offset_value ? offset_value + 2 : 2)
-
end
-
-
# Same as +third+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
-
# is found.
-
1
def third!
-
third or raise RecordNotFound
-
end
-
-
# Find the fourth record.
-
# If no order is defined it will order by primary key.
-
#
-
# Person.fourth # returns the fourth object fetched by SELECT * FROM people
-
# Person.offset(3).fourth # returns the fourth object from OFFSET 3 (which is OFFSET 6)
-
# Person.where(["user_name = :u", { u: user_name }]).fourth
-
1
def fourth
-
find_nth(:fourth, offset_value ? offset_value + 3 : 3)
-
end
-
-
# Same as +fourth+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
-
# is found.
-
1
def fourth!
-
fourth or raise RecordNotFound
-
end
-
-
# Find the fifth record.
-
# If no order is defined it will order by primary key.
-
#
-
# Person.fifth # returns the fifth object fetched by SELECT * FROM people
-
# Person.offset(3).fifth # returns the fifth object from OFFSET 3 (which is OFFSET 7)
-
# Person.where(["user_name = :u", { u: user_name }]).fifth
-
1
def fifth
-
find_nth(:fifth, offset_value ? offset_value + 4 : 4)
-
end
-
-
# Same as +fifth+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
-
# is found.
-
1
def fifth!
-
fifth or raise RecordNotFound
-
end
-
-
# Find the forty-second record. Also known as accessing "the reddit".
-
# If no order is defined it will order by primary key.
-
#
-
# Person.forty_two # returns the forty-second object fetched by SELECT * FROM people
-
# Person.offset(3).forty_two # returns the forty-second object from OFFSET 3 (which is OFFSET 44)
-
# Person.where(["user_name = :u", { u: user_name }]).forty_two
-
1
def forty_two
-
find_nth(:forty_two, offset_value ? offset_value + 41 : 41)
-
end
-
-
# Same as +forty_two+ but raises <tt>ActiveRecord::RecordNotFound</tt> if no record
-
# is found.
-
1
def forty_two!
-
forty_two or raise RecordNotFound
-
end
-
-
# Returns +true+ if a record exists in the table that matches the +id+ or
-
# conditions given, or +false+ otherwise. The argument can take six forms:
-
#
-
# * Integer - Finds the record with this primary key.
-
# * String - Finds the record with a primary key corresponding to this
-
# string (such as <tt>'5'</tt>).
-
# * Array - Finds the record that matches these +find+-style conditions
-
# (such as <tt>['name LIKE ?', "%#{query}%"]</tt>).
-
# * Hash - Finds the record that matches these +find+-style conditions
-
# (such as <tt>{name: 'David'}</tt>).
-
# * +false+ - Returns always +false+.
-
# * No args - Returns +false+ if the table is empty, +true+ otherwise.
-
#
-
# For more information about specifying conditions as a hash or array,
-
# see the Conditions section in the introduction to <tt>ActiveRecord::Base</tt>.
-
#
-
# Note: You can't pass in a condition as a string (like <tt>name =
-
# 'Jamie'</tt>), since it would be sanitized and then queried against
-
# the primary key column, like <tt>id = 'name = \'Jamie\''</tt>.
-
#
-
# Person.exists?(5)
-
# Person.exists?('5')
-
# Person.exists?(['name LIKE ?', "%#{query}%"])
-
# Person.exists?(id: [1, 4, 8])
-
# Person.exists?(name: 'David')
-
# Person.exists?(false)
-
# Person.exists?
-
1
def exists?(conditions = :none)
-
conditions = conditions.id if Base === conditions
-
return false if !conditions
-
-
relation = apply_join_dependency(self, construct_join_dependency)
-
return false if ActiveRecord::NullRelation === relation
-
-
relation = relation.except(:select, :order).select(ONE_AS_ONE).limit(1)
-
-
case conditions
-
when Array, Hash
-
relation = relation.where(conditions)
-
else
-
relation = relation.where(table[primary_key].eq(conditions)) if conditions != :none
-
end
-
-
connection.select_value(relation, "#{name} Exists", relation.bind_values) ? true : false
-
end
-
-
# This method is called whenever no records are found with either a single
-
# id or multiple ids and raises a +ActiveRecord::RecordNotFound+ exception.
-
#
-
# The error message is different depending on whether a single id or
-
# multiple ids are provided. If multiple ids are provided, then the number
-
# of results obtained should be provided in the +result_size+ argument and
-
# the expected number of results should be provided in the +expected_size+
-
# argument.
-
1
def raise_record_not_found_exception!(ids, result_size, expected_size) #:nodoc:
-
conditions = arel.where_sql
-
conditions = " [#{conditions}]" if conditions
-
-
if Array(ids).size == 1
-
error = "Couldn't find #{@klass.name} with '#{primary_key}'=#{ids}#{conditions}"
-
else
-
error = "Couldn't find all #{@klass.name.pluralize} with '#{primary_key}': "
-
error << "(#{ids.join(", ")})#{conditions} (found #{result_size} results, but was looking for #{expected_size})"
-
end
-
-
raise RecordNotFound, error
-
end
-
-
1
private
-
-
1
def find_with_associations
-
# NOTE: the JoinDependency constructed here needs to know about
-
# any joins already present in `self`, so pass them in
-
#
-
# failing to do so means that in cases like activerecord/test/cases/associations/inner_join_association_test.rb:136
-
# incorrect SQL is generated. In that case, the join dependency for
-
# SpecialCategorizations is constructed without knowledge of the
-
# preexisting join in joins_values to categorizations (by way of
-
# the `has_many :through` for categories).
-
#
-
join_dependency = construct_join_dependency(joins_values)
-
-
aliases = join_dependency.aliases
-
relation = select aliases.columns
-
relation = apply_join_dependency(relation, join_dependency)
-
-
if block_given?
-
yield relation
-
else
-
if ActiveRecord::NullRelation === relation
-
[]
-
else
-
rows = connection.select_all(relation.arel, 'SQL', relation.bind_values.dup)
-
join_dependency.instantiate(rows, aliases)
-
end
-
end
-
end
-
-
1
def construct_join_dependency(joins = [])
-
including = eager_load_values + includes_values
-
ActiveRecord::Associations::JoinDependency.new(@klass, including, joins)
-
end
-
-
1
def construct_relation_for_association_calculations
-
from = arel.froms.first
-
if Arel::Table === from
-
apply_join_dependency(self, construct_join_dependency)
-
else
-
# FIXME: as far as I can tell, `from` will always be an Arel::Table.
-
# There are no tests that test this branch, but presumably it's
-
# possible for `from` to be a list?
-
apply_join_dependency(self, construct_join_dependency(from))
-
end
-
end
-
-
1
def apply_join_dependency(relation, join_dependency)
-
relation = relation.except(:includes, :eager_load, :preload)
-
relation = relation.joins join_dependency
-
-
if using_limitable_reflections?(join_dependency.reflections)
-
relation
-
else
-
if relation.limit_value
-
limited_ids = limited_ids_for(relation)
-
limited_ids.empty? ? relation.none! : relation.where!(table[primary_key].in(limited_ids))
-
end
-
relation.except(:limit, :offset)
-
end
-
end
-
-
1
def limited_ids_for(relation)
-
values = @klass.connection.columns_for_distinct(
-
"#{quoted_table_name}.#{quoted_primary_key}", relation.order_values)
-
-
relation = relation.except(:select).select(values).distinct!
-
-
id_rows = @klass.connection.select_all(relation.arel, 'SQL', relation.bind_values)
-
id_rows.map {|row| row[primary_key]}
-
end
-
-
1
def using_limitable_reflections?(reflections)
-
reflections.none? { |r| r.collection? }
-
end
-
-
1
protected
-
-
1
def find_with_ids(*ids)
-
raise UnknownPrimaryKey.new(@klass) if primary_key.nil?
-
-
expects_array = ids.first.kind_of?(Array)
-
return ids.first if expects_array && ids.first.empty?
-
-
ids = ids.flatten.compact.uniq
-
-
case ids.size
-
when 0
-
raise RecordNotFound, "Couldn't find #{@klass.name} without an ID"
-
when 1
-
result = find_one(ids.first)
-
expects_array ? [ result ] : result
-
else
-
find_some(ids)
-
end
-
end
-
-
1
def find_one(id)
-
id = id.id if ActiveRecord::Base === id
-
-
column = columns_hash[primary_key]
-
substitute = connection.substitute_at(column, bind_values.length)
-
relation = where(table[primary_key].eq(substitute))
-
relation.bind_values += [[column, id]]
-
record = relation.take
-
-
raise_record_not_found_exception!(id, 0, 1) unless record
-
-
record
-
end
-
-
1
def find_some(ids)
-
result = where(table[primary_key].in(ids)).to_a
-
-
expected_size =
-
if limit_value && ids.size > limit_value
-
limit_value
-
else
-
ids.size
-
end
-
-
# 11 ids with limit 3, offset 9 should give 2 results.
-
if offset_value && (ids.size - offset_value < expected_size)
-
expected_size = ids.size - offset_value
-
end
-
-
if result.size == expected_size
-
result
-
else
-
raise_record_not_found_exception!(ids, result.size, expected_size)
-
end
-
end
-
-
1
def find_take
-
if loaded?
-
@records.first
-
else
-
@take ||= limit(1).to_a.first
-
end
-
end
-
-
1
def find_nth(ordinal, offset)
-
if loaded?
-
@records.send(ordinal)
-
else
-
@offsets[offset] ||= find_nth_with_limit(offset, 1).first
-
end
-
end
-
-
1
def find_nth_with_limit(offset, limit)
-
if order_values.empty? && primary_key
-
order(arel_table[primary_key].asc).limit(limit).offset(offset).to_a
-
else
-
limit(limit).offset(offset).to_a
-
end
-
end
-
-
1
def find_last
-
if loaded?
-
@records.last
-
else
-
@last ||=
-
if limit_value
-
to_a.last
-
else
-
reverse_order.limit(1).to_a.first
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/hash/keys'
-
1
require "set"
-
-
1
module ActiveRecord
-
1
class Relation
-
1
class HashMerger # :nodoc:
-
1
attr_reader :relation, :hash
-
-
1
def initialize(relation, hash)
-
hash.assert_valid_keys(*Relation::VALUE_METHODS)
-
-
@relation = relation
-
@hash = hash
-
end
-
-
1
def merge
-
Merger.new(relation, other).merge
-
end
-
-
# Applying values to a relation has some side effects. E.g.
-
# interpolation might take place for where values. So we should
-
# build a relation to merge in rather than directly merging
-
# the values.
-
1
def other
-
other = Relation.create(relation.klass, relation.table)
-
hash.each { |k, v|
-
if k == :joins
-
if Hash === v
-
other.joins!(v)
-
else
-
other.joins!(*v)
-
end
-
elsif k == :select
-
other._select!(v)
-
else
-
other.send("#{k}!", v)
-
end
-
}
-
other
-
end
-
end
-
-
1
class Merger # :nodoc:
-
1
attr_reader :relation, :values, :other
-
-
1
def initialize(relation, other)
-
48
@relation = relation
-
48
@values = other.values
-
48
@other = other
-
end
-
-
1
NORMAL_VALUES = Relation::SINGLE_VALUE_METHODS +
-
Relation::MULTI_VALUE_METHODS -
-
[:joins, :where, :order, :bind, :reverse_order, :lock, :create_with, :reordering, :from] # :nodoc:
-
-
1
def normal_values
-
48
NORMAL_VALUES
-
end
-
-
1
def merge
-
48
normal_values.each do |name|
-
672
value = values[name]
-
# The unless clause is here mostly for performance reasons (since the `send` call might be moderately
-
# expensive), most of the time the value is going to be `nil` or `.blank?`, the only catch is that
-
# `false.blank?` returns `true`, so there needs to be an extra check so that explicit `false` values
-
# don't fall through the cracks.
-
672
unless value.nil? || (value.blank? && false != value)
-
if name == :select
-
relation._select!(*value)
-
else
-
relation.send("#{name}!", *value)
-
end
-
end
-
end
-
-
48
merge_multi_values
-
48
merge_single_values
-
48
merge_joins
-
-
48
relation
-
end
-
-
1
private
-
-
1
def merge_joins
-
48
return if values[:joins].blank?
-
-
14
if other.klass == relation.klass
-
14
relation.joins!(*values[:joins])
-
else
-
joins_dependency, rest = values[:joins].partition do |join|
-
case join
-
when Hash, Symbol, Array
-
true
-
else
-
false
-
end
-
end
-
-
join_dependency = ActiveRecord::Associations::JoinDependency.new(other.klass,
-
joins_dependency,
-
[])
-
relation.joins! rest
-
-
@relation = relation.joins join_dependency
-
end
-
end
-
-
1
def merge_multi_values
-
48
lhs_wheres = relation.where_values
-
48
rhs_wheres = values[:where] || []
-
-
48
lhs_binds = relation.bind_values
-
48
rhs_binds = values[:bind] || []
-
-
48
removed, kept = partition_overwrites(lhs_wheres, rhs_wheres)
-
-
48
where_values = kept + rhs_wheres
-
48
bind_values = filter_binds(lhs_binds, removed) + rhs_binds
-
-
48
conn = relation.klass.connection
-
48
bv_index = 0
-
48
where_values.map! do |node|
-
22
if Arel::Nodes::Equality === node && Arel::Nodes::BindParam === node.right
-
22
substitute = conn.substitute_at(bind_values[bv_index].first, bv_index)
-
22
bv_index += 1
-
22
Arel::Nodes::Equality.new(node.left, substitute)
-
else
-
node
-
end
-
end
-
-
48
relation.where_values = where_values
-
48
relation.bind_values = bind_values
-
-
48
if values[:reordering]
-
# override any order specified in the original relation
-
relation.reorder! values[:order]
-
elsif values[:order]
-
# merge in order_values from relation
-
relation.order! values[:order]
-
end
-
-
48
relation.extend(*values[:extending]) unless values[:extending].blank?
-
end
-
-
1
def merge_single_values
-
48
relation.from_value = values[:from] unless relation.from_value
-
48
relation.lock_value = values[:lock] unless relation.lock_value
-
48
relation.reverse_order_value = values[:reverse_order]
-
-
48
unless values[:create_with].blank?
-
relation.create_with_value = (relation.create_with_value || {}).merge(values[:create_with])
-
end
-
end
-
-
1
def filter_binds(lhs_binds, removed_wheres)
-
48
return lhs_binds if removed_wheres.empty?
-
-
set = Set.new removed_wheres.map { |x| x.left.name.to_s }
-
lhs_binds.dup.delete_if { |col,_| set.include? col.name }
-
end
-
-
# Remove equalities from the existing relation with a LHS which is
-
# present in the relation being merged in.
-
# returns [things_to_remove, things_to_keep]
-
1
def partition_overwrites(lhs_wheres, rhs_wheres)
-
48
if lhs_wheres.empty? || rhs_wheres.empty?
-
48
return [[], lhs_wheres]
-
end
-
-
nodes = rhs_wheres.find_all do |w|
-
w.respond_to?(:operator) && w.operator == :==
-
end
-
seen = Set.new(nodes) { |node| node.left }
-
-
lhs_wheres.partition do |w|
-
w.respond_to?(:operator) && w.operator == :== && seen.include?(w.left)
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/array/wrap'
-
1
require 'active_model/forbidden_attributes_protection'
-
-
1
module ActiveRecord
-
1
module QueryMethods
-
1
extend ActiveSupport::Concern
-
-
1
include ActiveModel::ForbiddenAttributesProtection
-
-
# WhereChain objects act as placeholder for queries in which #where does not have any parameter.
-
# In this case, #where must be chained with #not to return a new relation.
-
1
class WhereChain
-
1
def initialize(scope)
-
@scope = scope
-
end
-
-
# Returns a new relation expressing WHERE + NOT condition according to
-
# the conditions in the arguments.
-
#
-
# +not+ accepts conditions as a string, array, or hash. See #where for
-
# more details on each format.
-
#
-
# User.where.not("name = 'Jon'")
-
# # SELECT * FROM users WHERE NOT (name = 'Jon')
-
#
-
# User.where.not(["name = ?", "Jon"])
-
# # SELECT * FROM users WHERE NOT (name = 'Jon')
-
#
-
# User.where.not(name: "Jon")
-
# # SELECT * FROM users WHERE name != 'Jon'
-
#
-
# User.where.not(name: nil)
-
# # SELECT * FROM users WHERE name IS NOT NULL
-
#
-
# User.where.not(name: %w(Ko1 Nobu))
-
# # SELECT * FROM users WHERE name NOT IN ('Ko1', 'Nobu')
-
#
-
# User.where.not(name: "Jon", role: "admin")
-
# # SELECT * FROM users WHERE name != 'Jon' AND role != 'admin'
-
1
def not(opts, *rest)
-
where_value = @scope.send(:build_where, opts, rest).map do |rel|
-
case rel
-
when NilClass
-
raise ArgumentError, 'Invalid argument for .where.not(), got nil.'
-
when Arel::Nodes::In
-
Arel::Nodes::NotIn.new(rel.left, rel.right)
-
when Arel::Nodes::Equality
-
Arel::Nodes::NotEqual.new(rel.left, rel.right)
-
when String
-
Arel::Nodes::Not.new(Arel::Nodes::SqlLiteral.new(rel))
-
else
-
Arel::Nodes::Not.new(rel)
-
end
-
end
-
-
@scope.references!(PredicateBuilder.references(opts)) if Hash === opts
-
@scope.where_values += where_value
-
@scope
-
end
-
end
-
-
1
Relation::MULTI_VALUE_METHODS.each do |name|
-
13
class_eval <<-CODE, __FILE__, __LINE__ + 1
-
def #{name}_values # def select_values
-
@values[:#{name}] || [] # @values[:select] || []
-
end # end
-
#
-
def #{name}_values=(values) # def select_values=(values)
-
raise ImmutableRelation if @loaded # raise ImmutableRelation if @loaded
-
@values[:#{name}] = values # @values[:select] = values
-
end # end
-
CODE
-
end
-
-
1
(Relation::SINGLE_VALUE_METHODS - [:create_with]).each do |name|
-
9
class_eval <<-CODE, __FILE__, __LINE__ + 1
-
def #{name}_value # def readonly_value
-
@values[:#{name}] # @values[:readonly]
-
end # end
-
CODE
-
end
-
-
1
Relation::SINGLE_VALUE_METHODS.each do |name|
-
10
class_eval <<-CODE, __FILE__, __LINE__ + 1
-
def #{name}_value=(value) # def readonly_value=(value)
-
raise ImmutableRelation if @loaded # raise ImmutableRelation if @loaded
-
@values[:#{name}] = value # @values[:readonly] = value
-
end # end
-
CODE
-
end
-
-
1
def create_with_value # :nodoc:
-
4
@values[:create_with] || {}
-
end
-
-
1
alias extensions extending_values
-
-
# Specify relationships to be included in the result set. For
-
# example:
-
#
-
# users = User.includes(:address)
-
# users.each do |user|
-
# user.address.city
-
# end
-
#
-
# allows you to access the +address+ attribute of the +User+ model without
-
# firing an additional query. This will often result in a
-
# performance improvement over a simple +join+.
-
#
-
# You can also specify multiple relationships, like this:
-
#
-
# users = User.includes(:address, :friends)
-
#
-
# Loading nested relationships is possible using a Hash:
-
#
-
# users = User.includes(:address, friends: [:address, :followers])
-
#
-
# === conditions
-
#
-
# If you want to add conditions to your included models you'll have
-
# to explicitly reference them. For example:
-
#
-
# User.includes(:posts).where('posts.name = ?', 'example')
-
#
-
# Will throw an error, but this will work:
-
#
-
# User.includes(:posts).where('posts.name = ?', 'example').references(:posts)
-
#
-
# Note that +includes+ works with association names while +references+ needs
-
# the actual table name.
-
1
def includes(*args)
-
check_if_method_has_arguments!(:includes, args)
-
spawn.includes!(*args)
-
end
-
-
1
def includes!(*args) # :nodoc:
-
args.reject!(&:blank?)
-
args.flatten!
-
-
self.includes_values |= args
-
self
-
end
-
-
# Forces eager loading by performing a LEFT OUTER JOIN on +args+:
-
#
-
# User.eager_load(:posts)
-
# => SELECT "users"."id" AS t0_r0, "users"."name" AS t0_r1, ...
-
# FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" =
-
# "users"."id"
-
1
def eager_load(*args)
-
check_if_method_has_arguments!(:eager_load, args)
-
spawn.eager_load!(*args)
-
end
-
-
1
def eager_load!(*args) # :nodoc:
-
self.eager_load_values += args
-
self
-
end
-
-
# Allows preloading of +args+, in the same way that +includes+ does:
-
#
-
# User.preload(:posts)
-
# => SELECT "posts".* FROM "posts" WHERE "posts"."user_id" IN (1, 2, 3)
-
1
def preload(*args)
-
check_if_method_has_arguments!(:preload, args)
-
spawn.preload!(*args)
-
end
-
-
1
def preload!(*args) # :nodoc:
-
self.preload_values += args
-
self
-
end
-
-
# Use to indicate that the given +table_names+ are referenced by an SQL string,
-
# and should therefore be JOINed in any query rather than loaded separately.
-
# This method only works in conjuction with +includes+.
-
# See #includes for more details.
-
#
-
# User.includes(:posts).where("posts.name = 'foo'")
-
# # => Doesn't JOIN the posts table, resulting in an error.
-
#
-
# User.includes(:posts).where("posts.name = 'foo'").references(:posts)
-
# # => Query now knows the string references posts, so adds a JOIN
-
1
def references(*table_names)
-
check_if_method_has_arguments!(:references, table_names)
-
spawn.references!(*table_names)
-
end
-
-
1
def references!(*table_names) # :nodoc:
-
table_names.flatten!
-
table_names.map!(&:to_s)
-
-
self.references_values |= table_names
-
self
-
end
-
-
# Works in two unique ways.
-
#
-
# First: takes a block so it can be used just like Array#select.
-
#
-
# Model.all.select { |m| m.field == value }
-
#
-
# This will build an array of objects from the database for the scope,
-
# converting them into an array and iterating through them using Array#select.
-
#
-
# Second: Modifies the SELECT statement for the query so that only certain
-
# fields are retrieved:
-
#
-
# Model.select(:field)
-
# # => [#<Model field:value>]
-
#
-
# Although in the above example it looks as though this method returns an
-
# array, it actually returns a relation object and can have other query
-
# methods appended to it, such as the other methods in ActiveRecord::QueryMethods.
-
#
-
# The argument to the method can also be an array of fields.
-
#
-
# Model.select(:field, :other_field, :and_one_more)
-
# # => [#<Model field: "value", other_field: "value", and_one_more: "value">]
-
#
-
# You can also use one or more strings, which will be used unchanged as SELECT fields.
-
#
-
# Model.select('field AS field_one', 'other_field AS field_two')
-
# # => [#<Model field: "value", other_field: "value">]
-
#
-
# If an alias was specified, it will be accessible from the resulting objects:
-
#
-
# Model.select('field AS field_one').first.field_one
-
# # => "value"
-
#
-
# Accessing attributes of an object that do not have fields retrieved by a select
-
# will throw <tt>ActiveModel::MissingAttributeError</tt>:
-
#
-
# Model.select(:field).first.other_field
-
# # => ActiveModel::MissingAttributeError: missing attribute: other_field
-
1
def select(*fields)
-
if block_given?
-
to_a.select { |*block_args| yield(*block_args) }
-
else
-
raise ArgumentError, 'Call this with at least one field' if fields.empty?
-
spawn._select!(*fields)
-
end
-
end
-
-
1
def _select!(*fields) # :nodoc:
-
fields.flatten!
-
fields.map! do |field|
-
klass.attribute_alias?(field) ? klass.attribute_alias(field) : field
-
end
-
self.select_values += fields
-
self
-
end
-
-
# Allows to specify a group attribute:
-
#
-
# User.group(:name)
-
# => SELECT "users".* FROM "users" GROUP BY name
-
#
-
# Returns an array with distinct records based on the +group+ attribute:
-
#
-
# User.select([:id, :name])
-
# => [#<User id: 1, name: "Oscar">, #<User id: 2, name: "Oscar">, #<User id: 3, name: "Foo">
-
#
-
# User.group(:name)
-
# => [#<User id: 3, name: "Foo", ...>, #<User id: 2, name: "Oscar", ...>]
-
#
-
# User.group('name AS grouped_name, age')
-
# => [#<User id: 3, name: "Foo", age: 21, ...>, #<User id: 2, name: "Oscar", age: 21, ...>, #<User id: 5, name: "Foo", age: 23, ...>]
-
1
def group(*args)
-
check_if_method_has_arguments!(:group, args)
-
spawn.group!(*args)
-
end
-
-
1
def group!(*args) # :nodoc:
-
args.flatten!
-
-
self.group_values += args
-
self
-
end
-
-
# Allows to specify an order attribute:
-
#
-
# User.order('name')
-
# => SELECT "users".* FROM "users" ORDER BY name
-
#
-
# User.order('name DESC')
-
# => SELECT "users".* FROM "users" ORDER BY name DESC
-
#
-
# User.order('name DESC, email')
-
# => SELECT "users".* FROM "users" ORDER BY name DESC, email
-
#
-
# User.order(:name)
-
# => SELECT "users".* FROM "users" ORDER BY "users"."name" ASC
-
#
-
# User.order(email: :desc)
-
# => SELECT "users".* FROM "users" ORDER BY "users"."email" DESC
-
#
-
# User.order(:name, email: :desc)
-
# => SELECT "users".* FROM "users" ORDER BY "users"."name" ASC, "users"."email" DESC
-
1
def order(*args)
-
check_if_method_has_arguments!(:order, args)
-
spawn.order!(*args)
-
end
-
-
1
def order!(*args) # :nodoc:
-
preprocess_order_args(args)
-
-
self.order_values += args
-
self
-
end
-
-
# Replaces any existing order defined on the relation with the specified order.
-
#
-
# User.order('email DESC').reorder('id ASC') # generated SQL has 'ORDER BY id ASC'
-
#
-
# Subsequent calls to order on the same relation will be appended. For example:
-
#
-
# User.order('email DESC').reorder('id ASC').order('name ASC')
-
#
-
# generates a query with 'ORDER BY id ASC, name ASC'.
-
1
def reorder(*args)
-
check_if_method_has_arguments!(:reorder, args)
-
spawn.reorder!(*args)
-
end
-
-
1
def reorder!(*args) # :nodoc:
-
preprocess_order_args(args)
-
-
self.reordering_value = true
-
self.order_values = args
-
self
-
end
-
-
1
VALID_UNSCOPING_VALUES = Set.new([:where, :select, :group, :order, :lock,
-
:limit, :offset, :joins, :includes, :from,
-
:readonly, :having])
-
-
# Removes an unwanted relation that is already defined on a chain of relations.
-
# This is useful when passing around chains of relations and would like to
-
# modify the relations without reconstructing the entire chain.
-
#
-
# User.order('email DESC').unscope(:order) == User.all
-
#
-
# The method arguments are symbols which correspond to the names of the methods
-
# which should be unscoped. The valid arguments are given in VALID_UNSCOPING_VALUES.
-
# The method can also be called with multiple arguments. For example:
-
#
-
# User.order('email DESC').select('id').where(name: "John")
-
# .unscope(:order, :select, :where) == User.all
-
#
-
# One can additionally pass a hash as an argument to unscope specific :where values.
-
# This is done by passing a hash with a single key-value pair. The key should be
-
# :where and the value should be the where value to unscope. For example:
-
#
-
# User.where(name: "John", active: true).unscope(where: :name)
-
# == User.where(active: true)
-
#
-
# This method is similar to <tt>except</tt>, but unlike
-
# <tt>except</tt>, it persists across merges:
-
#
-
# User.order('email').merge(User.except(:order))
-
# == User.order('email')
-
#
-
# User.order('email').merge(User.unscope(:order))
-
# == User.all
-
#
-
# This means it can be used in association definitions:
-
#
-
# has_many :comments, -> { unscope where: :trashed }
-
#
-
1
def unscope(*args)
-
2
check_if_method_has_arguments!(:unscope, args)
-
2
spawn.unscope!(*args)
-
end
-
-
1
def unscope!(*args) # :nodoc:
-
2
args.flatten!
-
2
self.unscope_values += args
-
-
2
args.each do |scope|
-
2
case scope
-
when Symbol
-
2
symbol_unscoping(scope)
-
when Hash
-
scope.each do |key, target_value|
-
if key != :where
-
raise ArgumentError, "Hash arguments in .unscope(*args) must have :where as the key."
-
end
-
-
Array(target_value).each do |val|
-
where_unscoping(val)
-
end
-
end
-
else
-
raise ArgumentError, "Unrecognized scoping: #{args.inspect}. Use .unscope(where: :attribute_name) or .unscope(:order), for example."
-
end
-
end
-
-
2
self
-
end
-
-
# Performs a joins on +args+:
-
#
-
# User.joins(:posts)
-
# => SELECT "users".* FROM "users" INNER JOIN "posts" ON "posts"."user_id" = "users"."id"
-
#
-
# You can use strings in order to customize your joins:
-
#
-
# User.joins("LEFT JOIN bookmarks ON bookmarks.bookmarkable_type = 'Post' AND bookmarks.user_id = users.id")
-
# => SELECT "users".* FROM "users" LEFT JOIN bookmarks ON bookmarks.bookmarkable_type = 'Post' AND bookmarks.user_id = users.id
-
1
def joins(*args)
-
4
check_if_method_has_arguments!(:joins, args)
-
-
4
args.compact!
-
4
args.flatten!
-
-
4
spawn.joins!(*args)
-
end
-
-
1
def joins!(*args) # :nodoc:
-
18
self.joins_values += args
-
18
self
-
end
-
-
1
def bind(value)
-
spawn.bind!(value)
-
end
-
-
1
def bind!(value) # :nodoc:
-
self.bind_values += [value]
-
self
-
end
-
-
# Returns a new relation, which is the result of filtering the current relation
-
# according to the conditions in the arguments.
-
#
-
# #where accepts conditions in one of several formats. In the examples below, the resulting
-
# SQL is given as an illustration; the actual query generated may be different depending
-
# on the database adapter.
-
#
-
# === string
-
#
-
# A single string, without additional arguments, is passed to the query
-
# constructor as an SQL fragment, and used in the where clause of the query.
-
#
-
# Client.where("orders_count = '2'")
-
# # SELECT * from clients where orders_count = '2';
-
#
-
# Note that building your own string from user input may expose your application
-
# to injection attacks if not done properly. As an alternative, it is recommended
-
# to use one of the following methods.
-
#
-
# === array
-
#
-
# If an array is passed, then the first element of the array is treated as a template, and
-
# the remaining elements are inserted into the template to generate the condition.
-
# Active Record takes care of building the query to avoid injection attacks, and will
-
# convert from the ruby type to the database type where needed. Elements are inserted
-
# into the string in the order in which they appear.
-
#
-
# User.where(["name = ? and email = ?", "Joe", "joe@example.com"])
-
# # SELECT * FROM users WHERE name = 'Joe' AND email = 'joe@example.com';
-
#
-
# Alternatively, you can use named placeholders in the template, and pass a hash as the
-
# second element of the array. The names in the template are replaced with the corresponding
-
# values from the hash.
-
#
-
# User.where(["name = :name and email = :email", { name: "Joe", email: "joe@example.com" }])
-
# # SELECT * FROM users WHERE name = 'Joe' AND email = 'joe@example.com';
-
#
-
# This can make for more readable code in complex queries.
-
#
-
# Lastly, you can use sprintf-style % escapes in the template. This works slightly differently
-
# than the previous methods; you are responsible for ensuring that the values in the template
-
# are properly quoted. The values are passed to the connector for quoting, but the caller
-
# is responsible for ensuring they are enclosed in quotes in the resulting SQL. After quoting,
-
# the values are inserted using the same escapes as the Ruby core method <tt>Kernel::sprintf</tt>.
-
#
-
# User.where(["name = '%s' and email = '%s'", "Joe", "joe@example.com"])
-
# # SELECT * FROM users WHERE name = 'Joe' AND email = 'joe@example.com';
-
#
-
# If #where is called with multiple arguments, these are treated as if they were passed as
-
# the elements of a single array.
-
#
-
# User.where("name = :name and email = :email", { name: "Joe", email: "joe@example.com" })
-
# # SELECT * FROM users WHERE name = 'Joe' AND email = 'joe@example.com';
-
#
-
# When using strings to specify conditions, you can use any operator available from
-
# the database. While this provides the most flexibility, you can also unintentionally introduce
-
# dependencies on the underlying database. If your code is intended for general consumption,
-
# test with multiple database backends.
-
#
-
# === hash
-
#
-
# #where will also accept a hash condition, in which the keys are fields and the values
-
# are values to be searched for.
-
#
-
# Fields can be symbols or strings. Values can be single values, arrays, or ranges.
-
#
-
# User.where({ name: "Joe", email: "joe@example.com" })
-
# # SELECT * FROM users WHERE name = 'Joe' AND email = 'joe@example.com'
-
#
-
# User.where({ name: ["Alice", "Bob"]})
-
# # SELECT * FROM users WHERE name IN ('Alice', 'Bob')
-
#
-
# User.where({ created_at: (Time.now.midnight - 1.day)..Time.now.midnight })
-
# # SELECT * FROM users WHERE (created_at BETWEEN '2012-06-09 07:00:00.000000' AND '2012-06-10 07:00:00.000000')
-
#
-
# In the case of a belongs_to relationship, an association key can be used
-
# to specify the model if an ActiveRecord object is used as the value.
-
#
-
# author = Author.find(1)
-
#
-
# # The following queries will be equivalent:
-
# Post.where(author: author)
-
# Post.where(author_id: author)
-
#
-
# This also works with polymorphic belongs_to relationships:
-
#
-
# treasure = Treasure.create(name: 'gold coins')
-
# treasure.price_estimates << PriceEstimate.create(price: 125)
-
#
-
# # The following queries will be equivalent:
-
# PriceEstimate.where(estimate_of: treasure)
-
# PriceEstimate.where(estimate_of_type: 'Treasure', estimate_of_id: treasure)
-
#
-
# === Joins
-
#
-
# If the relation is the result of a join, you may create a condition which uses any of the
-
# tables in the join. For string and array conditions, use the table name in the condition.
-
#
-
# User.joins(:posts).where("posts.created_at < ?", Time.now)
-
#
-
# For hash conditions, you can either use the table name in the key, or use a sub-hash.
-
#
-
# User.joins(:posts).where({ "posts.published" => true })
-
# User.joins(:posts).where({ posts: { published: true } })
-
#
-
# === no argument
-
#
-
# If no argument is passed, #where returns a new instance of WhereChain, that
-
# can be chained with #not to return a new relation that negates the where clause.
-
#
-
# User.where.not(name: "Jon")
-
# # SELECT * FROM users WHERE name != 'Jon'
-
#
-
# See WhereChain for more details on #not.
-
#
-
# === blank condition
-
#
-
# If the condition is any blank-ish object, then #where is a no-op and returns
-
# the current relation.
-
1
def where(opts = :chain, *rest)
-
8
if opts == :chain
-
WhereChain.new(spawn)
-
8
elsif opts.blank?
-
self
-
else
-
8
spawn.where!(opts, *rest)
-
end
-
end
-
-
1
def where!(opts = :chain, *rest) # :nodoc:
-
8
if opts == :chain
-
WhereChain.new(self)
-
else
-
8
if Hash === opts
-
opts = sanitize_forbidden_attributes(opts)
-
references!(PredicateBuilder.references(opts))
-
end
-
-
8
self.where_values += build_where(opts, rest)
-
8
self
-
end
-
end
-
-
# Allows you to change a previously set where condition for a given attribute, instead of appending to that condition.
-
#
-
# Post.where(trashed: true).where(trashed: false) # => WHERE `trashed` = 1 AND `trashed` = 0
-
# Post.where(trashed: true).rewhere(trashed: false) # => WHERE `trashed` = 0
-
# Post.where(active: true).where(trashed: true).rewhere(trashed: false) # => WHERE `active` = 1 AND `trashed` = 0
-
#
-
# This is short-hand for unscope(where: conditions.keys).where(conditions). Note that unlike reorder, we're only unscoping
-
# the named conditions -- not the entire where statement.
-
1
def rewhere(conditions)
-
unscope(where: conditions.keys).where(conditions)
-
end
-
-
# Allows to specify a HAVING clause. Note that you can't use HAVING
-
# without also specifying a GROUP clause.
-
#
-
# Order.having('SUM(price) > 30').group('user_id')
-
1
def having(opts, *rest)
-
opts.blank? ? self : spawn.having!(opts, *rest)
-
end
-
-
1
def having!(opts, *rest) # :nodoc:
-
references!(PredicateBuilder.references(opts)) if Hash === opts
-
-
self.having_values += build_where(opts, rest)
-
self
-
end
-
-
# Specifies a limit for the number of records to retrieve.
-
#
-
# User.limit(10) # generated SQL has 'LIMIT 10'
-
#
-
# User.limit(10).limit(20) # generated SQL has 'LIMIT 20'
-
1
def limit(value)
-
spawn.limit!(value)
-
end
-
-
1
def limit!(value) # :nodoc:
-
self.limit_value = value
-
self
-
end
-
-
# Specifies the number of rows to skip before returning rows.
-
#
-
# User.offset(10) # generated SQL has "OFFSET 10"
-
#
-
# Should be used with order.
-
#
-
# User.offset(10).order("name ASC")
-
1
def offset(value)
-
spawn.offset!(value)
-
end
-
-
1
def offset!(value) # :nodoc:
-
self.offset_value = value
-
self
-
end
-
-
# Specifies locking settings (default to +true+). For more information
-
# on locking, please see +ActiveRecord::Locking+.
-
1
def lock(locks = true)
-
spawn.lock!(locks)
-
end
-
-
1
def lock!(locks = true) # :nodoc:
-
case locks
-
when String, TrueClass, NilClass
-
self.lock_value = locks || true
-
else
-
self.lock_value = false
-
end
-
-
self
-
end
-
-
# Returns a chainable relation with zero records.
-
#
-
# The returned relation implements the Null Object pattern. It is an
-
# object with defined null behavior and always returns an empty array of
-
# records without querying the database.
-
#
-
# Any subsequent condition chained to the returned relation will continue
-
# generating an empty relation and will not fire any query to the database.
-
#
-
# Used in cases where a method or scope could return zero records but the
-
# result needs to be chainable.
-
#
-
# For example:
-
#
-
# @posts = current_user.visible_posts.where(name: params[:name])
-
# # => the visible_posts method is expected to return a chainable Relation
-
#
-
# def visible_posts
-
# case role
-
# when 'Country Manager'
-
# Post.where(country: country)
-
# when 'Reviewer'
-
# Post.published
-
# when 'Bad User'
-
# Post.none # It can't be chained if [] is returned.
-
# end
-
# end
-
#
-
1
def none
-
extending(NullRelation)
-
end
-
-
1
def none! # :nodoc:
-
extending!(NullRelation)
-
end
-
-
# Sets readonly attributes for the returned relation. If value is
-
# true (default), attempting to update a record will result in an error.
-
#
-
# users = User.readonly
-
# users.first.save
-
# => ActiveRecord::ReadOnlyRecord: ActiveRecord::ReadOnlyRecord
-
1
def readonly(value = true)
-
spawn.readonly!(value)
-
end
-
-
1
def readonly!(value = true) # :nodoc:
-
self.readonly_value = value
-
self
-
end
-
-
# Sets attributes to be used when creating new records from a
-
# relation object.
-
#
-
# users = User.where(name: 'Oscar')
-
# users.new.name # => 'Oscar'
-
#
-
# users = users.create_with(name: 'DHH')
-
# users.new.name # => 'DHH'
-
#
-
# You can pass +nil+ to +create_with+ to reset attributes:
-
#
-
# users = users.create_with(nil)
-
# users.new.name # => 'Oscar'
-
1
def create_with(value)
-
spawn.create_with!(value)
-
end
-
-
1
def create_with!(value) # :nodoc:
-
if value
-
value = sanitize_forbidden_attributes(value)
-
self.create_with_value = create_with_value.merge(value)
-
else
-
self.create_with_value = {}
-
end
-
-
self
-
end
-
-
# Specifies table from which the records will be fetched. For example:
-
#
-
# Topic.select('title').from('posts')
-
# # => SELECT title FROM posts
-
#
-
# Can accept other relation objects. For example:
-
#
-
# Topic.select('title').from(Topic.approved)
-
# # => SELECT title FROM (SELECT * FROM topics WHERE approved = 't') subquery
-
#
-
# Topic.select('a.title').from(Topic.approved, :a)
-
# # => SELECT a.title FROM (SELECT * FROM topics WHERE approved = 't') a
-
#
-
1
def from(value, subquery_name = nil)
-
spawn.from!(value, subquery_name)
-
end
-
-
1
def from!(value, subquery_name = nil) # :nodoc:
-
self.from_value = [value, subquery_name]
-
self
-
end
-
-
# Specifies whether the records should be unique or not. For example:
-
#
-
# User.select(:name)
-
# # => Might return two records with the same name
-
#
-
# User.select(:name).distinct
-
# # => Returns 1 record per distinct name
-
#
-
# User.select(:name).distinct.distinct(false)
-
# # => You can also remove the uniqueness
-
1
def distinct(value = true)
-
spawn.distinct!(value)
-
end
-
1
alias uniq distinct
-
-
# Like #distinct, but modifies relation in place.
-
1
def distinct!(value = true) # :nodoc:
-
self.distinct_value = value
-
self
-
end
-
1
alias uniq! distinct!
-
-
# Used to extend a scope with additional methods, either through
-
# a module or through a block provided.
-
#
-
# The object returned is a relation, which can be further extended.
-
#
-
# === Using a module
-
#
-
# module Pagination
-
# def page(number)
-
# # pagination code goes here
-
# end
-
# end
-
#
-
# scope = Model.all.extending(Pagination)
-
# scope.page(params[:page])
-
#
-
# You can also pass a list of modules:
-
#
-
# scope = Model.all.extending(Pagination, SomethingElse)
-
#
-
# === Using a block
-
#
-
# scope = Model.all.extending do
-
# def page(number)
-
# # pagination code goes here
-
# end
-
# end
-
# scope.page(params[:page])
-
#
-
# You can also use a block and a module list:
-
#
-
# scope = Model.all.extending(Pagination) do
-
# def per_page(number)
-
# # pagination code goes here
-
# end
-
# end
-
1
def extending(*modules, &block)
-
if modules.any? || block
-
spawn.extending!(*modules, &block)
-
else
-
self
-
end
-
end
-
-
1
def extending!(*modules, &block) # :nodoc:
-
8
modules << Module.new(&block) if block
-
8
modules.flatten!
-
-
8
self.extending_values += modules
-
8
extend(*extending_values) if extending_values.any?
-
-
8
self
-
end
-
-
# Reverse the existing order clause on the relation.
-
#
-
# User.order('name ASC').reverse_order # generated SQL has 'ORDER BY name DESC'
-
1
def reverse_order
-
spawn.reverse_order!
-
end
-
-
1
def reverse_order! # :nodoc:
-
self.reverse_order_value = !reverse_order_value
-
self
-
end
-
-
# Returns the Arel object associated with the relation.
-
1
def arel # :nodoc:
-
15
@arel ||= build_arel
-
end
-
-
1
private
-
-
1
def build_arel
-
15
arel = Arel::SelectManager.new(table.engine, table)
-
-
15
build_joins(arel, joins_values.flatten) unless joins_values.empty?
-
-
15
collapse_wheres(arel, (where_values - ['']).uniq)
-
-
15
arel.having(*having_values.uniq.reject(&:blank?)) unless having_values.empty?
-
-
15
arel.take(connection.sanitize_limit(limit_value)) if limit_value
-
15
arel.skip(offset_value.to_i) if offset_value
-
-
15
arel.group(*group_values.uniq.reject(&:blank?)) unless group_values.empty?
-
-
15
build_order(arel)
-
-
15
build_select(arel, select_values.uniq)
-
-
15
arel.distinct(distinct_value)
-
15
arel.from(build_from) if from_value
-
15
arel.lock(lock_value) if lock_value
-
-
15
arel
-
end
-
-
1
def symbol_unscoping(scope)
-
2
if !VALID_UNSCOPING_VALUES.include?(scope)
-
raise ArgumentError, "Called unscope() with invalid unscoping argument ':#{scope}'. Valid arguments are :#{VALID_UNSCOPING_VALUES.to_a.join(", :")}."
-
end
-
-
2
single_val_method = Relation::SINGLE_VALUE_METHODS.include?(scope)
-
2
unscope_code = "#{scope}_value#{'s' unless single_val_method}="
-
-
2
case scope
-
when :order
-
2
self.reverse_order_value = false
-
2
result = []
-
else
-
result = [] unless single_val_method
-
end
-
-
2
self.send(unscope_code, result)
-
end
-
-
1
def where_unscoping(target_value)
-
target_value = target_value.to_s
-
-
where_values.reject! do |rel|
-
case rel
-
when Arel::Nodes::In, Arel::Nodes::NotIn, Arel::Nodes::Equality, Arel::Nodes::NotEqual
-
subrelation = (rel.left.kind_of?(Arel::Attributes::Attribute) ? rel.left : rel.right)
-
subrelation.name == target_value
-
end
-
end
-
-
bind_values.reject! { |col,_| col.name == target_value }
-
end
-
-
1
def custom_join_ast(table, joins)
-
2
joins = joins.reject(&:blank?)
-
-
2
return [] if joins.empty?
-
-
joins.map! do |join|
-
case join
-
when Array
-
join = Arel.sql(join.join(' ')) if array_of_strings?(join)
-
when String
-
join = Arel.sql(join)
-
end
-
table.create_string_join(join)
-
end
-
end
-
-
1
def collapse_wheres(arel, wheres)
-
15
predicates = wheres.map do |where|
-
4
next where if ::Arel::Nodes::Equality === where
-
where = Arel.sql(where) if String === where
-
Arel::Nodes::Grouping.new(where)
-
end
-
-
15
arel.where(Arel::Nodes::And.new(predicates)) if predicates.present?
-
end
-
-
1
def build_where(opts, other = [])
-
8
case opts
-
when String, Array
-
#TODO: Remove duplication with: /activerecord/lib/active_record/sanitization.rb:113
-
values = Hash === other.first ? other.first.values : other
-
-
values.grep(ActiveRecord::Relation) do |rel|
-
self.bind_values += rel.bind_values
-
end
-
-
[@klass.send(:sanitize_sql, other.empty? ? opts : ([opts] + other))]
-
when Hash
-
opts = PredicateBuilder.resolve_column_aliases(klass, opts)
-
attributes = @klass.send(:expand_hash_conditions_for_aggregates, opts)
-
-
add_relations_to_bind_values(attributes)
-
-
PredicateBuilder.build_from_hash(klass, attributes, table)
-
else
-
8
[opts]
-
end
-
end
-
-
1
def build_from
-
opts, name = from_value
-
case opts
-
when Relation
-
name ||= 'subquery'
-
self.bind_values = opts.bind_values + self.bind_values
-
opts.arel.as(name.to_s)
-
else
-
opts
-
end
-
end
-
-
1
def build_joins(manager, joins)
-
2
buckets = joins.group_by do |join|
-
2
case join
-
when String
-
:string_join
-
when Hash, Symbol, Array
-
:association_join
-
when ActiveRecord::Associations::JoinDependency
-
:stashed_join
-
when Arel::Nodes::Join
-
2
:join_node
-
else
-
raise 'unknown class: %s' % join.class.name
-
end
-
end
-
-
2
association_joins = buckets[:association_join] || []
-
2
stashed_association_joins = buckets[:stashed_join] || []
-
2
join_nodes = (buckets[:join_node] || []).uniq
-
2
string_joins = (buckets[:string_join] || []).map(&:strip).uniq
-
-
2
join_list = join_nodes + custom_join_ast(manager, string_joins)
-
-
2
join_dependency = ActiveRecord::Associations::JoinDependency.new(
-
@klass,
-
association_joins,
-
join_list
-
)
-
-
2
joins = join_dependency.join_constraints stashed_association_joins
-
-
2
joins.each { |join| manager.from(join) }
-
-
2
manager.join_sources.concat(join_list)
-
-
2
manager
-
end
-
-
1
def build_select(arel, selects)
-
15
if !selects.empty?
-
2
expanded_select = selects.map do |field|
-
2
columns_hash.key?(field.to_s) ? arel_table[field] : field
-
end
-
2
arel.project(*expanded_select)
-
else
-
13
arel.project(@klass.arel_table[Arel.star])
-
end
-
end
-
-
1
def reverse_sql_order(order_query)
-
order_query = ["#{quoted_table_name}.#{quoted_primary_key} ASC"] if order_query.empty?
-
-
order_query.flat_map do |o|
-
case o
-
when Arel::Nodes::Ordering
-
o.reverse
-
when String
-
o.to_s.split(',').map! do |s|
-
s.strip!
-
s.gsub!(/\sasc\Z/i, ' DESC') || s.gsub!(/\sdesc\Z/i, ' ASC') || s.concat(' DESC')
-
end
-
else
-
o
-
end
-
end
-
end
-
-
1
def array_of_strings?(o)
-
o.is_a?(Array) && o.all? { |obj| obj.is_a?(String) }
-
end
-
-
1
def build_order(arel)
-
15
orders = order_values.uniq
-
15
orders.reject!(&:blank?)
-
15
orders = reverse_sql_order(orders) if reverse_order_value
-
-
15
arel.order(*orders) unless orders.empty?
-
end
-
-
1
def validate_order_args(args)
-
args.grep(Hash) do |h|
-
unless (h.values - [:asc, :desc]).empty?
-
raise ArgumentError, 'Direction should be :asc or :desc'
-
end
-
end
-
end
-
-
1
def preprocess_order_args(order_args)
-
order_args.flatten!
-
validate_order_args(order_args)
-
-
references = order_args.grep(String)
-
references.map! { |arg| arg =~ /^([a-zA-Z]\w*)\.(\w+)/ && $1 }.compact!
-
references!(references) if references.any?
-
-
# if a symbol is given we prepend the quoted table name
-
order_args.map! do |arg|
-
case arg
-
when Symbol
-
arg = klass.attribute_alias(arg) if klass.attribute_alias?(arg)
-
table[arg].asc
-
when Hash
-
arg.map { |field, dir|
-
field = klass.attribute_alias(field) if klass.attribute_alias?(field)
-
table[field].send(dir)
-
}
-
else
-
arg
-
end
-
end.flatten!
-
end
-
-
# Checks to make sure that the arguments are not blank. Note that if some
-
# blank-like object were initially passed into the query method, then this
-
# method will not raise an error.
-
#
-
# Example:
-
#
-
# Post.references() # => raises an error
-
# Post.references([]) # => does not raise an error
-
#
-
# This particular method should be called with a method_name and the args
-
# passed into that method as an input. For example:
-
#
-
# def references(*args)
-
# check_if_method_has_arguments!("references", args)
-
# ...
-
# end
-
1
def check_if_method_has_arguments!(method_name, args)
-
6
if args.blank?
-
raise ArgumentError, "The method .#{method_name}() must contain arguments."
-
end
-
end
-
-
# This function is recursive just for better readablity.
-
# #where argument doesn't support more than one level nested hash in real world.
-
1
def add_relations_to_bind_values(attributes)
-
if attributes.is_a?(Hash)
-
attributes.each_value do |value|
-
if value.is_a?(ActiveRecord::Relation)
-
self.bind_values += value.bind_values
-
else
-
add_relations_to_bind_values(value)
-
end
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/hash/except'
-
1
require 'active_support/core_ext/hash/slice'
-
1
require 'active_record/relation/merger'
-
-
1
module ActiveRecord
-
1
module SpawnMethods
-
-
# This is overridden by Associations::CollectionProxy
-
1
def spawn #:nodoc:
-
30
clone
-
end
-
-
# Merges in the conditions from <tt>other</tt>, if <tt>other</tt> is an <tt>ActiveRecord::Relation</tt>.
-
# Returns an array representing the intersection of the resulting records with <tt>other</tt>, if <tt>other</tt> is an array.
-
# Post.where(published: true).joins(:comments).merge( Comment.where(spam: false) )
-
# # Performs a single join query with both where conditions.
-
#
-
# recent_posts = Post.order('created_at DESC').first(5)
-
# Post.where(published: true).merge(recent_posts)
-
# # Returns the intersection of all published posts with the 5 most recently created posts.
-
# # (This is just an example. You'd probably want to do this with a single query!)
-
#
-
# Procs will be evaluated by merge:
-
#
-
# Post.where(published: true).merge(-> { joins(:comments) })
-
# # => Post.where(published: true).joins(:comments)
-
#
-
# This is mainly intended for sharing common conditions between multiple associations.
-
1
def merge(other)
-
45
if other.is_a?(Array)
-
to_a & other
-
45
elsif other
-
16
spawn.merge!(other)
-
else
-
29
self
-
end
-
end
-
-
1
def merge!(other) # :nodoc:
-
48
if !other.is_a?(Relation) && other.respond_to?(:to_proc)
-
instance_exec(&other)
-
else
-
48
klass = other.is_a?(Hash) ? Relation::HashMerger : Relation::Merger
-
48
klass.new(self, other).merge
-
end
-
end
-
-
# Removes from the query the condition(s) specified in +skips+.
-
#
-
# Post.order('id asc').except(:order) # discards the order condition
-
# Post.where('id > 10').order('id asc').except(:where) # discards the where condition but keeps the order
-
1
def except(*skips)
-
10
relation_with values.except(*skips)
-
end
-
-
# Removes any condition from the query other than the one(s) specified in +onlies+.
-
#
-
# Post.order('id asc').only(:where) # discards the order condition
-
# Post.order('id asc').only(:where, :order) # uses the specified order
-
1
def only(*onlies)
-
relation_with values.slice(*onlies)
-
end
-
-
1
private
-
-
1
def relation_with(values) # :nodoc:
-
10
result = Relation.create(klass, table, values)
-
10
result.extend(*extending_values) if extending_values.any?
-
10
result
-
end
-
end
-
end
-
1
module ActiveRecord
-
###
-
# This class encapsulates a Result returned from calling +exec_query+ on any
-
# database connection adapter. For example:
-
#
-
# result = ActiveRecord::Base.connection.exec_query('SELECT id, title, body FROM posts')
-
# result # => #<ActiveRecord::Result:0xdeadbeef>
-
#
-
# # Get the column names of the result:
-
# result.columns
-
# # => ["id", "title", "body"]
-
#
-
# # Get the record values of the result:
-
# result.rows
-
# # => [[1, "title_1", "body_1"],
-
# [2, "title_2", "body_2"],
-
# ...
-
# ]
-
#
-
# # Get an array of hashes representing the result (column => value):
-
# result.to_hash
-
# # => [{"id" => 1, "title" => "title_1", "body" => "body_1"},
-
# {"id" => 2, "title" => "title_2", "body" => "body_2"},
-
# ...
-
# ]
-
#
-
# # ActiveRecord::Result also includes Enumerable.
-
# result.each do |row|
-
# puts row['title'] + " " + row['body']
-
# end
-
1
class Result
-
1
include Enumerable
-
-
2
IDENTITY_TYPE = Class.new { def type_cast(v); v; end }.new # :nodoc:
-
-
1
attr_reader :columns, :rows, :column_types
-
-
1
def initialize(columns, rows, column_types = {})
-
25
@columns = columns
-
25
@rows = rows
-
25
@hash_rows = nil
-
25
@column_types = column_types
-
end
-
-
1
def identity_type # :nodoc:
-
IDENTITY_TYPE
-
end
-
-
1
def column_type(name)
-
@column_types[name] || identity_type
-
end
-
-
1
def each
-
7
if block_given?
-
25
hash_rows.each { |row| yield row }
-
else
-
hash_rows.to_enum { @rows.size }
-
end
-
end
-
-
1
def to_hash
-
hash_rows
-
end
-
-
1
alias :map! :map
-
1
alias :collect! :map
-
-
# Returns true if there are no records.
-
1
def empty?
-
rows.empty?
-
end
-
-
1
def to_ary
-
hash_rows
-
end
-
-
1
def [](idx)
-
hash_rows[idx]
-
end
-
-
1
def last
-
hash_rows.last
-
end
-
-
1
def initialize_copy(other)
-
@columns = columns.dup
-
@rows = rows.dup
-
@column_types = column_types.dup
-
@hash_rows = nil
-
end
-
-
1
private
-
-
1
def hash_rows
-
@hash_rows ||=
-
begin
-
# We freeze the strings to prevent them getting duped when
-
# used as keys in ActiveRecord::Base's @attributes hash
-
46
columns = @columns.map { |c| c.dup.freeze }
-
7
@rows.map { |row|
-
# In the past we used Hash[columns.zip(row)]
-
# though elegant, the verbose way is much more efficient
-
# both time and memory wise cause it avoids a big array allocation
-
# this method is called a lot and needs to be micro optimised
-
18
hash = {}
-
-
18
index = 0
-
18
length = columns.length
-
-
18
while index < length
-
34
hash[columns[index]] = row[index]
-
34
index += 1
-
end
-
-
18
hash
-
}
-
7
end
-
end
-
end
-
end
-
1
require 'active_support/per_thread_registry'
-
-
1
module ActiveRecord
-
# This is a thread locals registry for Active Record. For example:
-
#
-
# ActiveRecord::RuntimeRegistry.connection_handler
-
#
-
# returns the connection handler local to the current thread.
-
#
-
# See the documentation of <tt>ActiveSupport::PerThreadRegistry</tt>
-
# for further details.
-
1
class RuntimeRegistry # :nodoc:
-
1
extend ActiveSupport::PerThreadRegistry
-
-
1
attr_accessor :connection_handler, :sql_runtime, :connection_id
-
-
1
[:connection_handler, :sql_runtime, :connection_id].each do |val|
-
3
class_eval %{ def self.#{val}; instance.#{val}; end }, __FILE__, __LINE__
-
3
class_eval %{ def self.#{val}=(x); instance.#{val}=x; end }, __FILE__, __LINE__
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
module Sanitization
-
1
extend ActiveSupport::Concern
-
-
1
module ClassMethods
-
1
def quote_value(value, column) #:nodoc:
-
connection.quote(value, column)
-
end
-
-
# Used to sanitize objects before they're used in an SQL SELECT statement. Delegates to <tt>connection.quote</tt>.
-
1
def sanitize(object) #:nodoc:
-
connection.quote(object)
-
end
-
-
1
protected
-
-
# Accepts an array, hash, or string of SQL conditions and sanitizes
-
# them into a valid SQL fragment for a WHERE clause.
-
# ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'"
-
# { name: "foo'bar", group_id: 4 } returns "name='foo''bar' and group_id='4'"
-
# "name='foo''bar' and group_id='4'" returns "name='foo''bar' and group_id='4'"
-
1
def sanitize_sql_for_conditions(condition, table_name = self.table_name)
-
5
return nil if condition.blank?
-
-
5
case condition
-
when Array; sanitize_sql_array(condition)
-
when Hash; sanitize_sql_hash_for_conditions(condition, table_name)
-
5
else condition
-
end
-
end
-
1
alias_method :sanitize_sql, :sanitize_sql_for_conditions
-
1
alias_method :sanitize_conditions, :sanitize_sql
-
-
# Accepts an array, hash, or string of SQL conditions and sanitizes
-
# them into a valid SQL fragment for a SET clause.
-
# { name: nil, group_id: 4 } returns "name = NULL , group_id='4'"
-
1
def sanitize_sql_for_assignment(assignments, default_table_name = self.table_name)
-
case assignments
-
when Array; sanitize_sql_array(assignments)
-
when Hash; sanitize_sql_hash_for_assignment(assignments, default_table_name)
-
else assignments
-
end
-
end
-
-
# Accepts a hash of SQL conditions and replaces those attributes
-
# that correspond to a +composed_of+ relationship with their expanded
-
# aggregate attribute values.
-
# Given:
-
# class Person < ActiveRecord::Base
-
# composed_of :address, class_name: "Address",
-
# mapping: [%w(address_street street), %w(address_city city)]
-
# end
-
# Then:
-
# { address: Address.new("813 abc st.", "chicago") }
-
# # => { address_street: "813 abc st.", address_city: "chicago" }
-
1
def expand_hash_conditions_for_aggregates(attrs)
-
expanded_attrs = {}
-
attrs.each do |attr, value|
-
if aggregation = reflect_on_aggregation(attr.to_sym)
-
mapping = aggregation.mapping
-
mapping.each do |field_attr, aggregate_attr|
-
if mapping.size == 1 && !value.respond_to?(aggregate_attr)
-
expanded_attrs[field_attr] = value
-
else
-
expanded_attrs[field_attr] = value.send(aggregate_attr)
-
end
-
end
-
else
-
expanded_attrs[attr] = value
-
end
-
end
-
expanded_attrs
-
end
-
-
# Sanitizes a hash of attribute/value pairs into SQL conditions for a WHERE clause.
-
# { name: "foo'bar", group_id: 4 }
-
# # => "name='foo''bar' and group_id= 4"
-
# { status: nil, group_id: [1,2,3] }
-
# # => "status IS NULL and group_id IN (1,2,3)"
-
# { age: 13..18 }
-
# # => "age BETWEEN 13 AND 18"
-
# { 'other_records.id' => 7 }
-
# # => "`other_records`.`id` = 7"
-
# { other_records: { id: 7 } }
-
# # => "`other_records`.`id` = 7"
-
# And for value objects on a composed_of relationship:
-
# { address: Address.new("123 abc st.", "chicago") }
-
# # => "address_street='123 abc st.' and address_city='chicago'"
-
1
def sanitize_sql_hash_for_conditions(attrs, default_table_name = self.table_name)
-
attrs = PredicateBuilder.resolve_column_aliases self, attrs
-
attrs = expand_hash_conditions_for_aggregates(attrs)
-
-
table = Arel::Table.new(table_name, arel_engine).alias(default_table_name)
-
PredicateBuilder.build_from_hash(self, attrs, table).map { |b|
-
connection.visitor.accept b
-
}.join(' AND ')
-
end
-
1
alias_method :sanitize_sql_hash, :sanitize_sql_hash_for_conditions
-
-
# Sanitizes a hash of attribute/value pairs into SQL conditions for a SET clause.
-
# { status: nil, group_id: 1 }
-
# # => "status = NULL , group_id = 1"
-
1
def sanitize_sql_hash_for_assignment(attrs, table)
-
c = connection
-
attrs.map do |attr, value|
-
"#{c.quote_table_name_for_assignment(table, attr)} = #{quote_bound_value(value, c, columns_hash[attr.to_s])}"
-
end.join(', ')
-
end
-
-
# Accepts an array of conditions. The array has each value
-
# sanitized and interpolated into the SQL statement.
-
# ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'"
-
1
def sanitize_sql_array(ary)
-
statement, *values = ary
-
if values.first.is_a?(Hash) && statement =~ /:\w+/
-
replace_named_bind_variables(statement, values.first)
-
elsif statement.include?('?')
-
replace_bind_variables(statement, values)
-
elsif statement.blank?
-
statement
-
else
-
statement % values.collect { |value| connection.quote_string(value.to_s) }
-
end
-
end
-
-
1
def replace_bind_variables(statement, values) #:nodoc:
-
raise_if_bind_arity_mismatch(statement, statement.count('?'), values.size)
-
bound = values.dup
-
c = connection
-
statement.gsub('?') do
-
replace_bind_variable(bound.shift, c)
-
end
-
end
-
-
1
def replace_bind_variable(value, c = connection) #:nodoc:
-
if ActiveRecord::Relation === value
-
value.to_sql
-
else
-
quote_bound_value(value, c)
-
end
-
end
-
-
1
def replace_named_bind_variables(statement, bind_vars) #:nodoc:
-
statement.gsub(/(:?):([a-zA-Z]\w*)/) do
-
if $1 == ':' # skip postgresql casts
-
$& # return the whole match
-
elsif bind_vars.include?(match = $2.to_sym)
-
replace_bind_variable(bind_vars[match])
-
else
-
raise PreparedStatementInvalid, "missing value for :#{match} in #{statement}"
-
end
-
end
-
end
-
-
1
def quote_bound_value(value, c = connection, column = nil) #:nodoc:
-
if column
-
c.quote(value, column)
-
elsif value.respond_to?(:map) && !value.acts_like?(:string)
-
if value.respond_to?(:empty?) && value.empty?
-
c.quote(nil)
-
else
-
value.map { |v| c.quote(v) }.join(',')
-
end
-
else
-
c.quote(value)
-
end
-
end
-
-
1
def raise_if_bind_arity_mismatch(statement, expected, provided) #:nodoc:
-
unless expected == provided
-
raise PreparedStatementInvalid, "wrong number of bind variables (#{provided} for #{expected}) in: #{statement}"
-
end
-
end
-
end
-
-
# TODO: Deprecate this
-
1
def quoted_id
-
self.class.quote_value(id, column_for_attribute(self.class.primary_key))
-
end
-
end
-
end
-
1
require 'active_record/scoping/default'
-
1
require 'active_record/scoping/named'
-
1
require 'active_record/base'
-
-
1
module ActiveRecord
-
1
class SchemaMigration < ActiveRecord::Base
-
1
class << self
-
1
def primary_key
-
nil
-
end
-
-
1
def table_name
-
4
"#{table_name_prefix}#{ActiveRecord::Base.schema_migrations_table_name}#{table_name_suffix}"
-
end
-
-
1
def index_name
-
"#{table_name_prefix}unique_#{ActiveRecord::Base.schema_migrations_table_name}#{table_name_suffix}"
-
end
-
-
1
def table_exists?
-
connection.table_exists?(table_name)
-
end
-
-
1
def create_table(limit=nil)
-
unless table_exists?
-
version_options = {null: false}
-
version_options[:limit] = limit if limit
-
-
connection.create_table(table_name, id: false) do |t|
-
t.column :version, :string, version_options
-
end
-
connection.add_index table_name, :version, unique: true, name: index_name
-
end
-
end
-
-
1
def drop_table
-
if table_exists?
-
connection.remove_index table_name, name: index_name
-
connection.drop_table(table_name)
-
end
-
end
-
-
1
def normalize_migration_number(number)
-
"%.3d" % number.to_i
-
end
-
end
-
-
1
def version
-
14
super.to_i
-
end
-
end
-
end
-
1
require 'active_support/per_thread_registry'
-
-
1
module ActiveRecord
-
1
module Scoping
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
include Default
-
1
include Named
-
end
-
-
1
module ClassMethods
-
1
def current_scope #:nodoc:
-
37
ScopeRegistry.value_for(:current_scope, base_class.to_s)
-
end
-
-
1
def current_scope=(scope) #:nodoc:
-
ScopeRegistry.set_value_for(:current_scope, base_class.to_s, scope)
-
end
-
end
-
-
1
def populate_with_current_scope_attributes
-
8
return unless self.class.scope_attributes?
-
-
self.class.scope_attributes.each do |att,value|
-
send("#{att}=", value) if respond_to?("#{att}=")
-
end
-
end
-
-
1
def initialize_internals_callback
-
8
super
-
8
populate_with_current_scope_attributes
-
end
-
-
# This class stores the +:current_scope+ and +:ignore_default_scope+ values
-
# for different classes. The registry is stored as a thread local, which is
-
# accessed through +ScopeRegistry.current+.
-
#
-
# This class allows you to store and get the scope values on different
-
# classes and different types of scopes. For example, if you are attempting
-
# to get the current_scope for the +Board+ model, then you would use the
-
# following code:
-
#
-
# registry = ActiveRecord::Scoping::ScopeRegistry
-
# registry.set_value_for(:current_scope, "Board", some_new_scope)
-
#
-
# Now when you run:
-
#
-
# registry.value_for(:current_scope, "Board")
-
#
-
# You will obtain whatever was defined in +some_new_scope+. The +value_for+
-
# and +set_value_for+ methods are delegated to the current +ScopeRegistry+
-
# object, so the above example code can also be called as:
-
#
-
# ActiveRecord::Scoping::ScopeRegistry.set_value_for(:current_scope,
-
# "Board", some_new_scope)
-
1
class ScopeRegistry # :nodoc:
-
1
extend ActiveSupport::PerThreadRegistry
-
-
1
VALID_SCOPE_TYPES = [:current_scope, :ignore_default_scope]
-
-
1
def initialize
-
2
@registry = Hash.new { |hash, key| hash[key] = {} }
-
end
-
-
# Obtains the value for a given +scope_name+ and +variable_name+.
-
1
def value_for(scope_type, variable_name)
-
37
raise_invalid_scope_type!(scope_type)
-
37
@registry[scope_type][variable_name]
-
end
-
-
# Sets the +value+ for a given +scope_type+ and +variable_name+.
-
1
def set_value_for(scope_type, variable_name, value)
-
raise_invalid_scope_type!(scope_type)
-
@registry[scope_type][variable_name] = value
-
end
-
-
1
private
-
-
1
def raise_invalid_scope_type!(scope_type)
-
37
if !VALID_SCOPE_TYPES.include?(scope_type)
-
raise ArgumentError, "Invalid scope type '#{scope_type}' sent to the registry. Scope types must be included in VALID_SCOPE_TYPES"
-
end
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
module Scoping
-
1
module Default
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
# Stores the default scope for the class.
-
1
class_attribute :default_scopes, instance_writer: false, instance_predicate: false
-
-
1
self.default_scopes = []
-
end
-
-
1
module ClassMethods
-
# Returns a scope for the model without the +default_scope+.
-
#
-
# class Post < ActiveRecord::Base
-
# def self.default_scope
-
# where published: true
-
# end
-
# end
-
#
-
# Post.all # Fires "SELECT * FROM posts WHERE published = true"
-
# Post.unscoped.all # Fires "SELECT * FROM posts"
-
#
-
# This method also accepts a block. All queries inside the block will
-
# not use the +default_scope+:
-
#
-
# Post.unscoped {
-
# Post.limit(10) # Fires "SELECT * FROM posts LIMIT 10"
-
# }
-
1
def unscoped
-
14
block_given? ? relation.scoping { yield } : relation
-
end
-
-
1
def before_remove_const #:nodoc:
-
self.current_scope = nil
-
end
-
-
1
protected
-
-
# Use this macro in your model to set a default scope for all operations on
-
# the model.
-
#
-
# class Article < ActiveRecord::Base
-
# default_scope { where(published: true) }
-
# end
-
#
-
# Article.all # => SELECT * FROM articles WHERE published = true
-
#
-
# The +default_scope+ is also applied while creating/building a record.
-
# It is not applied while updating a record.
-
#
-
# Article.new.published # => true
-
# Article.create.published # => true
-
#
-
# (You can also pass any object which responds to +call+ to the
-
# +default_scope+ macro, and it will be called when building the
-
# default scope.)
-
#
-
# If you use multiple +default_scope+ declarations in your model then
-
# they will be merged together:
-
#
-
# class Article < ActiveRecord::Base
-
# default_scope { where(published: true) }
-
# default_scope { where(rating: 'G') }
-
# end
-
#
-
# Article.all # => SELECT * FROM articles WHERE published = true AND rating = 'G'
-
#
-
# This is also the case with inheritance and module includes where the
-
# parent or module defines a +default_scope+ and the child or including
-
# class defines a second one.
-
#
-
# If you need to do more complex things with a default scope, you can
-
# alternatively define it as a class method:
-
#
-
# class Article < ActiveRecord::Base
-
# def self.default_scope
-
# # Should return a scope, you can call 'super' here etc.
-
# end
-
# end
-
1
def default_scope(scope = nil)
-
scope = Proc.new if block_given?
-
-
if scope.is_a?(Relation) || !scope.respond_to?(:call)
-
raise ArgumentError,
-
"Support for calling #default_scope without a block is removed. For example instead " \
-
"of `default_scope where(color: 'red')`, please use " \
-
"`default_scope { where(color: 'red') }`. (Alternatively you can just redefine " \
-
"self.default_scope.)"
-
end
-
-
self.default_scopes += [scope]
-
end
-
-
1
def build_default_scope(base_rel = relation) # :nodoc:
-
29
if !Base.is_a?(method(:default_scope).owner)
-
# The user has defined their own default scope method, so call that
-
evaluate_default_scope { default_scope }
-
29
elsif default_scopes.any?
-
evaluate_default_scope do
-
default_scopes.inject(base_rel) do |default_scope, scope|
-
default_scope.merge(base_rel.scoping { scope.call })
-
end
-
end
-
end
-
end
-
-
1
def ignore_default_scope? # :nodoc:
-
ScopeRegistry.value_for(:ignore_default_scope, self)
-
end
-
-
1
def ignore_default_scope=(ignore) # :nodoc:
-
ScopeRegistry.set_value_for(:ignore_default_scope, self, ignore)
-
end
-
-
# The ignore_default_scope flag is used to prevent an infinite recursion
-
# situation where a default scope references a scope which has a default
-
# scope which references a scope...
-
1
def evaluate_default_scope # :nodoc:
-
return if ignore_default_scope?
-
-
begin
-
self.ignore_default_scope = true
-
yield
-
ensure
-
self.ignore_default_scope = false
-
end
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/array'
-
1
require 'active_support/core_ext/hash/except'
-
1
require 'active_support/core_ext/kernel/singleton_class'
-
-
1
module ActiveRecord
-
# = Active Record \Named \Scopes
-
1
module Scoping
-
1
module Named
-
1
extend ActiveSupport::Concern
-
-
1
module ClassMethods
-
# Returns an <tt>ActiveRecord::Relation</tt> scope object.
-
#
-
# posts = Post.all
-
# posts.size # Fires "select count(*) from posts" and returns the count
-
# posts.each {|p| puts p.name } # Fires "select * from posts" and loads post objects
-
#
-
# fruits = Fruit.all
-
# fruits = fruits.where(color: 'red') if options[:red_only]
-
# fruits = fruits.limit(10) if limited?
-
#
-
# You can define a scope that applies to all finders using
-
# <tt>ActiveRecord::Base.default_scope</tt>.
-
1
def all
-
29
if current_scope
-
current_scope.clone
-
else
-
29
default_scoped
-
end
-
end
-
-
1
def default_scoped # :nodoc:
-
29
relation.merge(build_default_scope)
-
end
-
-
# Collects attributes from scopes that should be applied when creating
-
# an AR instance for the particular class this is called on.
-
1
def scope_attributes # :nodoc:
-
all.scope_for_create
-
end
-
-
# Are there default attributes associated with this scope?
-
1
def scope_attributes? # :nodoc:
-
8
current_scope || default_scopes.any?
-
end
-
-
# Adds a class method for retrieving and querying objects. A \scope
-
# represents a narrowing of a database query, such as
-
# <tt>where(color: :red).select('shirts.*').includes(:washing_instructions)</tt>.
-
#
-
# class Shirt < ActiveRecord::Base
-
# scope :red, -> { where(color: 'red') }
-
# scope :dry_clean_only, -> { joins(:washing_instructions).where('washing_instructions.dry_clean_only = ?', true) }
-
# end
-
#
-
# The above calls to +scope+ define class methods <tt>Shirt.red</tt> and
-
# <tt>Shirt.dry_clean_only</tt>. <tt>Shirt.red</tt>, in effect,
-
# represents the query <tt>Shirt.where(color: 'red')</tt>.
-
#
-
# You should always pass a callable object to the scopes defined
-
# with +scope+. This ensures that the scope is re-evaluated each
-
# time it is called.
-
#
-
# Note that this is simply 'syntactic sugar' for defining an actual
-
# class method:
-
#
-
# class Shirt < ActiveRecord::Base
-
# def self.red
-
# where(color: 'red')
-
# end
-
# end
-
#
-
# Unlike <tt>Shirt.find(...)</tt>, however, the object returned by
-
# <tt>Shirt.red</tt> is not an Array; it resembles the association object
-
# constructed by a +has_many+ declaration. For instance, you can invoke
-
# <tt>Shirt.red.first</tt>, <tt>Shirt.red.count</tt>,
-
# <tt>Shirt.red.where(size: 'small')</tt>. Also, just as with the
-
# association objects, named \scopes act like an Array, implementing
-
# Enumerable; <tt>Shirt.red.each(&block)</tt>, <tt>Shirt.red.first</tt>,
-
# and <tt>Shirt.red.inject(memo, &block)</tt> all behave as if
-
# <tt>Shirt.red</tt> really was an Array.
-
#
-
# These named \scopes are composable. For instance,
-
# <tt>Shirt.red.dry_clean_only</tt> will produce all shirts that are
-
# both red and dry clean only. Nested finds and calculations also work
-
# with these compositions: <tt>Shirt.red.dry_clean_only.count</tt>
-
# returns the number of garments for which these criteria obtain.
-
# Similarly with <tt>Shirt.red.dry_clean_only.average(:thread_count)</tt>.
-
#
-
# All scopes are available as class methods on the ActiveRecord::Base
-
# descendant upon which the \scopes were defined. But they are also
-
# available to +has_many+ associations. If,
-
#
-
# class Person < ActiveRecord::Base
-
# has_many :shirts
-
# end
-
#
-
# then <tt>elton.shirts.red.dry_clean_only</tt> will return all of
-
# Elton's red, dry clean only shirts.
-
#
-
# \Named scopes can also have extensions, just as with +has_many+
-
# declarations:
-
#
-
# class Shirt < ActiveRecord::Base
-
# scope :red, -> { where(color: 'red') } do
-
# def dom_id
-
# 'red_shirts'
-
# end
-
# end
-
# end
-
#
-
# Scopes can also be used while creating/building a record.
-
#
-
# class Article < ActiveRecord::Base
-
# scope :published, -> { where(published: true) }
-
# end
-
#
-
# Article.published.new.published # => true
-
# Article.published.create.published # => true
-
#
-
# \Class methods on your model are automatically available
-
# on scopes. Assuming the following setup:
-
#
-
# class Article < ActiveRecord::Base
-
# scope :published, -> { where(published: true) }
-
# scope :featured, -> { where(featured: true) }
-
#
-
# def self.latest_article
-
# order('published_at desc').first
-
# end
-
#
-
# def self.titles
-
# pluck(:title)
-
# end
-
# end
-
#
-
# We are able to call the methods like this:
-
#
-
# Article.published.featured.latest_article
-
# Article.featured.titles
-
1
def scope(name, body, &block)
-
if dangerous_class_method?(name)
-
raise ArgumentError, "You tried to define a scope named \"#{name}\" " \
-
"on the model \"#{self.name}\", but Active Record already defined " \
-
"a class method with the same name."
-
end
-
-
extension = Module.new(&block) if block
-
-
singleton_class.send(:define_method, name) do |*args|
-
scope = all.scoping { body.call(*args) }
-
scope = scope.extending(extension) if extension
-
-
scope || all
-
end
-
end
-
end
-
end
-
end
-
end
-
1
module ActiveRecord #:nodoc:
-
# = Active Record Serialization
-
1
module Serialization
-
1
extend ActiveSupport::Concern
-
1
include ActiveModel::Serializers::JSON
-
-
1
included do
-
1
self.include_root_in_json = false
-
end
-
-
1
def serializable_hash(options = nil)
-
options = options.try(:clone) || {}
-
-
options[:except] = Array(options[:except]).map { |n| n.to_s }
-
options[:except] |= Array(self.class.inheritance_column)
-
-
super(options)
-
end
-
end
-
end
-
-
1
require 'active_record/serializers/xml_serializer'
-
1
require 'active_support/core_ext/hash/conversions'
-
-
1
module ActiveRecord #:nodoc:
-
1
module Serialization
-
1
include ActiveModel::Serializers::Xml
-
-
# Builds an XML document to represent the model. Some configuration is
-
# available through +options+. However more complicated cases should
-
# override ActiveRecord::Base#to_xml.
-
#
-
# By default the generated XML document will include the processing
-
# instruction and all the object's attributes. For example:
-
#
-
# <?xml version="1.0" encoding="UTF-8"?>
-
# <topic>
-
# <title>The First Topic</title>
-
# <author-name>David</author-name>
-
# <id type="integer">1</id>
-
# <approved type="boolean">false</approved>
-
# <replies-count type="integer">0</replies-count>
-
# <bonus-time type="dateTime">2000-01-01T08:28:00+12:00</bonus-time>
-
# <written-on type="dateTime">2003-07-16T09:28:00+1200</written-on>
-
# <content>Have a nice day</content>
-
# <author-email-address>david@loudthinking.com</author-email-address>
-
# <parent-id></parent-id>
-
# <last-read type="date">2004-04-15</last-read>
-
# </topic>
-
#
-
# This behavior can be controlled with <tt>:only</tt>, <tt>:except</tt>,
-
# <tt>:skip_instruct</tt>, <tt>:skip_types</tt>, <tt>:dasherize</tt> and <tt>:camelize</tt> .
-
# The <tt>:only</tt> and <tt>:except</tt> options are the same as for the
-
# +attributes+ method. The default is to dasherize all column names, but you
-
# can disable this setting <tt>:dasherize</tt> to +false+. Setting <tt>:camelize</tt>
-
# to +true+ will camelize all column names - this also overrides <tt>:dasherize</tt>.
-
# To not have the column type included in the XML output set <tt>:skip_types</tt> to +true+.
-
#
-
# For instance:
-
#
-
# topic.to_xml(skip_instruct: true, except: [ :id, :bonus_time, :written_on, :replies_count ])
-
#
-
# <topic>
-
# <title>The First Topic</title>
-
# <author-name>David</author-name>
-
# <approved type="boolean">false</approved>
-
# <content>Have a nice day</content>
-
# <author-email-address>david@loudthinking.com</author-email-address>
-
# <parent-id></parent-id>
-
# <last-read type="date">2004-04-15</last-read>
-
# </topic>
-
#
-
# To include first level associations use <tt>:include</tt>:
-
#
-
# firm.to_xml include: [ :account, :clients ]
-
#
-
# <?xml version="1.0" encoding="UTF-8"?>
-
# <firm>
-
# <id type="integer">1</id>
-
# <rating type="integer">1</rating>
-
# <name>37signals</name>
-
# <clients type="array">
-
# <client>
-
# <rating type="integer">1</rating>
-
# <name>Summit</name>
-
# </client>
-
# <client>
-
# <rating type="integer">1</rating>
-
# <name>Microsoft</name>
-
# </client>
-
# </clients>
-
# <account>
-
# <id type="integer">1</id>
-
# <credit-limit type="integer">50</credit-limit>
-
# </account>
-
# </firm>
-
#
-
# Additionally, the record being serialized will be passed to a Proc's second
-
# parameter. This allows for ad hoc additions to the resultant document that
-
# incorporate the context of the record being serialized. And by leveraging the
-
# closure created by a Proc, to_xml can be used to add elements that normally fall
-
# outside of the scope of the model -- for example, generating and appending URLs
-
# associated with models.
-
#
-
# proc = Proc.new { |options, record| options[:builder].tag!('name-reverse', record.name.reverse) }
-
# firm.to_xml procs: [ proc ]
-
#
-
# <firm>
-
# # ... normal attributes as shown above ...
-
# <name-reverse>slangis73</name-reverse>
-
# </firm>
-
#
-
# To include deeper levels of associations pass a hash like this:
-
#
-
# firm.to_xml include: {account: {}, clients: {include: :address}}
-
# <?xml version="1.0" encoding="UTF-8"?>
-
# <firm>
-
# <id type="integer">1</id>
-
# <rating type="integer">1</rating>
-
# <name>37signals</name>
-
# <clients type="array">
-
# <client>
-
# <rating type="integer">1</rating>
-
# <name>Summit</name>
-
# <address>
-
# ...
-
# </address>
-
# </client>
-
# <client>
-
# <rating type="integer">1</rating>
-
# <name>Microsoft</name>
-
# <address>
-
# ...
-
# </address>
-
# </client>
-
# </clients>
-
# <account>
-
# <id type="integer">1</id>
-
# <credit-limit type="integer">50</credit-limit>
-
# </account>
-
# </firm>
-
#
-
# To include any methods on the model being called use <tt>:methods</tt>:
-
#
-
# firm.to_xml methods: [ :calculated_earnings, :real_earnings ]
-
#
-
# <firm>
-
# # ... normal attributes as shown above ...
-
# <calculated-earnings>100000000000000000</calculated-earnings>
-
# <real-earnings>5</real-earnings>
-
# </firm>
-
#
-
# To call any additional Procs use <tt>:procs</tt>. The Procs are passed a
-
# modified version of the options hash that was given to +to_xml+:
-
#
-
# proc = Proc.new { |options| options[:builder].tag!('abc', 'def') }
-
# firm.to_xml procs: [ proc ]
-
#
-
# <firm>
-
# # ... normal attributes as shown above ...
-
# <abc>def</abc>
-
# </firm>
-
#
-
# Alternatively, you can yield the builder object as part of the +to_xml+ call:
-
#
-
# firm.to_xml do |xml|
-
# xml.creator do
-
# xml.first_name "David"
-
# xml.last_name "Heinemeier Hansson"
-
# end
-
# end
-
#
-
# <firm>
-
# # ... normal attributes as shown above ...
-
# <creator>
-
# <first_name>David</first_name>
-
# <last_name>Heinemeier Hansson</last_name>
-
# </creator>
-
# </firm>
-
#
-
# As noted above, you may override +to_xml+ in your ActiveRecord::Base
-
# subclasses to have complete control about what's generated. The general
-
# form of doing this is:
-
#
-
# class IHaveMyOwnXML < ActiveRecord::Base
-
# def to_xml(options = {})
-
# require 'builder'
-
# options[:indent] ||= 2
-
# xml = options[:builder] ||= ::Builder::XmlMarkup.new(indent: options[:indent])
-
# xml.instruct! unless options[:skip_instruct]
-
# xml.level_one do
-
# xml.tag!(:second_level, 'content')
-
# end
-
# end
-
# end
-
1
def to_xml(options = {}, &block)
-
XmlSerializer.new(self, options).serialize(&block)
-
end
-
end
-
-
1
class XmlSerializer < ActiveModel::Serializers::Xml::Serializer #:nodoc:
-
1
class Attribute < ActiveModel::Serializers::Xml::Serializer::Attribute #:nodoc:
-
1
def compute_type
-
klass = @serializable.class
-
type = if klass.serialized_attributes.key?(name)
-
super
-
elsif klass.columns_hash.key?(name)
-
klass.columns_hash[name].type
-
else
-
NilClass
-
end
-
-
{ :text => :string,
-
:time => :datetime }[type] || type
-
end
-
1
protected :compute_type
-
end
-
end
-
end
-
1
require 'active_support/core_ext/hash/indifferent_access'
-
-
1
module ActiveRecord
-
# Store gives you a thin wrapper around serialize for the purpose of storing hashes in a single column.
-
# It's like a simple key/value store baked into your record when you don't care about being able to
-
# query that store outside the context of a single record.
-
#
-
# You can then declare accessors to this store that are then accessible just like any other attribute
-
# of the model. This is very helpful for easily exposing store keys to a form or elsewhere that's
-
# already built around just accessing attributes on the model.
-
#
-
# Make sure that you declare the database column used for the serialized store as a text, so there's
-
# plenty of room.
-
#
-
# You can set custom coder to encode/decode your serialized attributes to/from different formats.
-
# JSON, YAML, Marshal are supported out of the box. Generally it can be any wrapper that provides +load+ and +dump+.
-
#
-
# NOTE - If you are using PostgreSQL specific columns like +hstore+ or +json+ there is no need for
-
# the serialization provided by +store+. Simply use +store_accessor+ instead to generate
-
# the accessor methods. Be aware that these columns use a string keyed hash and do not allow access
-
# using a symbol.
-
#
-
# Examples:
-
#
-
# class User < ActiveRecord::Base
-
# store :settings, accessors: [ :color, :homepage ], coder: JSON
-
# end
-
#
-
# u = User.new(color: 'black', homepage: '37signals.com')
-
# u.color # Accessor stored attribute
-
# u.settings[:country] = 'Denmark' # Any attribute, even if not specified with an accessor
-
#
-
# # There is no difference between strings and symbols for accessing custom attributes
-
# u.settings[:country] # => 'Denmark'
-
# u.settings['country'] # => 'Denmark'
-
#
-
# # Add additional accessors to an existing store through store_accessor
-
# class SuperUser < User
-
# store_accessor :settings, :privileges, :servants
-
# end
-
#
-
# The stored attribute names can be retrieved using +stored_attributes+.
-
#
-
# User.stored_attributes[:settings] # [:color, :homepage]
-
#
-
# == Overwriting default accessors
-
#
-
# All stored values are automatically available through accessors on the Active Record
-
# object, but sometimes you want to specialize this behavior. This can be done by overwriting
-
# the default accessors (using the same name as the attribute) and calling <tt>super</tt>
-
# to actually change things.
-
#
-
# class Song < ActiveRecord::Base
-
# # Uses a stored integer to hold the volume adjustment of the song
-
# store :settings, accessors: [:volume_adjustment]
-
#
-
# def volume_adjustment=(decibels)
-
# super(decibels.to_i)
-
# end
-
#
-
# def volume_adjustment
-
# super.to_i
-
# end
-
# end
-
1
module Store
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
class << self
-
1
attr_accessor :local_stored_attributes
-
end
-
end
-
-
1
module ClassMethods
-
1
def store(store_attribute, options = {})
-
serialize store_attribute, IndifferentCoder.new(options[:coder])
-
store_accessor(store_attribute, options[:accessors]) if options.has_key? :accessors
-
end
-
-
1
def store_accessor(store_attribute, *keys)
-
1
keys = keys.flatten
-
-
1
_store_accessors_module.module_eval do
-
1
keys.each do |key|
-
2
define_method("#{key}=") do |value|
-
write_store_attribute(store_attribute, key, value)
-
end
-
-
2
define_method(key) do
-
read_store_attribute(store_attribute, key)
-
end
-
end
-
end
-
-
# assign new store attribute and create new hash to ensure that each class in the hierarchy
-
# has its own hash of stored attributes.
-
1
self.local_stored_attributes ||= {}
-
1
self.local_stored_attributes[store_attribute] ||= []
-
1
self.local_stored_attributes[store_attribute] |= keys
-
end
-
-
1
def _store_accessors_module
-
@_store_accessors_module ||= begin
-
1
mod = Module.new
-
1
include mod
-
1
mod
-
1
end
-
end
-
-
1
def stored_attributes
-
parent = superclass.respond_to?(:stored_attributes) ? superclass.stored_attributes : {}
-
if self.local_stored_attributes
-
parent.merge!(self.local_stored_attributes) { |k, a, b| a | b }
-
end
-
parent
-
end
-
end
-
-
1
protected
-
1
def read_store_attribute(store_attribute, key)
-
accessor = store_accessor_for(store_attribute)
-
accessor.read(self, store_attribute, key)
-
end
-
-
1
def write_store_attribute(store_attribute, key, value)
-
accessor = store_accessor_for(store_attribute)
-
accessor.write(self, store_attribute, key, value)
-
end
-
-
1
private
-
1
def store_accessor_for(store_attribute)
-
@column_types[store_attribute.to_s].accessor
-
end
-
-
1
class HashAccessor
-
1
def self.read(object, attribute, key)
-
prepare(object, attribute)
-
object.public_send(attribute)[key]
-
end
-
-
1
def self.write(object, attribute, key, value)
-
prepare(object, attribute)
-
if value != read(object, attribute, key)
-
object.public_send :"#{attribute}_will_change!"
-
object.public_send(attribute)[key] = value
-
end
-
end
-
-
1
def self.prepare(object, attribute)
-
object.public_send :"#{attribute}=", {} unless object.send(attribute)
-
end
-
end
-
-
1
class StringKeyedHashAccessor < HashAccessor
-
1
def self.read(object, attribute, key)
-
super object, attribute, key.to_s
-
end
-
-
1
def self.write(object, attribute, key, value)
-
super object, attribute, key.to_s, value
-
end
-
end
-
-
1
class IndifferentHashAccessor < ActiveRecord::Store::HashAccessor
-
1
def self.prepare(object, store_attribute)
-
attribute = object.send(store_attribute)
-
unless attribute.is_a?(ActiveSupport::HashWithIndifferentAccess)
-
attribute = IndifferentCoder.as_indifferent_hash(attribute)
-
object.send :"#{store_attribute}=", attribute
-
end
-
attribute
-
end
-
end
-
-
1
class IndifferentCoder # :nodoc:
-
1
def initialize(coder_or_class_name)
-
@coder =
-
if coder_or_class_name.respond_to?(:load) && coder_or_class_name.respond_to?(:dump)
-
coder_or_class_name
-
else
-
ActiveRecord::Coders::YAMLColumn.new(coder_or_class_name || Object)
-
end
-
end
-
-
1
def dump(obj)
-
@coder.dump self.class.as_indifferent_hash(obj)
-
end
-
-
1
def load(yaml)
-
self.class.as_indifferent_hash(@coder.load(yaml || ''))
-
end
-
-
1
def self.as_indifferent_hash(obj)
-
case obj
-
when ActiveSupport::HashWithIndifferentAccess
-
obj
-
when Hash
-
obj.with_indifferent_access
-
else
-
ActiveSupport::HashWithIndifferentAccess.new
-
end
-
end
-
end
-
end
-
end
-
1
require 'thread'
-
-
1
module ActiveRecord
-
# See ActiveRecord::Transactions::ClassMethods for documentation.
-
1
module Transactions
-
1
extend ActiveSupport::Concern
-
1
ACTIONS = [:create, :destroy, :update]
-
-
1
included do
-
1
define_callbacks :commit, :rollback,
-
terminator: ->(_, result) { result == false },
-
scope: [:kind, :name]
-
end
-
-
# = Active Record Transactions
-
#
-
# Transactions are protective blocks where SQL statements are only permanent
-
# if they can all succeed as one atomic action. The classic example is a
-
# transfer between two accounts where you can only have a deposit if the
-
# withdrawal succeeded and vice versa. Transactions enforce the integrity of
-
# the database and guard the data against program errors or database
-
# break-downs. So basically you should use transaction blocks whenever you
-
# have a number of statements that must be executed together or not at all.
-
#
-
# For example:
-
#
-
# ActiveRecord::Base.transaction do
-
# david.withdrawal(100)
-
# mary.deposit(100)
-
# end
-
#
-
# This example will only take money from David and give it to Mary if neither
-
# +withdrawal+ nor +deposit+ raise an exception. Exceptions will force a
-
# ROLLBACK that returns the database to the state before the transaction
-
# began. Be aware, though, that the objects will _not_ have their instance
-
# data returned to their pre-transactional state.
-
#
-
# == Different Active Record classes in a single transaction
-
#
-
# Though the transaction class method is called on some Active Record class,
-
# the objects within the transaction block need not all be instances of
-
# that class. This is because transactions are per-database connection, not
-
# per-model.
-
#
-
# In this example a +balance+ record is transactionally saved even
-
# though +transaction+ is called on the +Account+ class:
-
#
-
# Account.transaction do
-
# balance.save!
-
# account.save!
-
# end
-
#
-
# The +transaction+ method is also available as a model instance method.
-
# For example, you can also do this:
-
#
-
# balance.transaction do
-
# balance.save!
-
# account.save!
-
# end
-
#
-
# == Transactions are not distributed across database connections
-
#
-
# A transaction acts on a single database connection. If you have
-
# multiple class-specific databases, the transaction will not protect
-
# interaction among them. One workaround is to begin a transaction
-
# on each class whose models you alter:
-
#
-
# Student.transaction do
-
# Course.transaction do
-
# course.enroll(student)
-
# student.units += course.units
-
# end
-
# end
-
#
-
# This is a poor solution, but fully distributed transactions are beyond
-
# the scope of Active Record.
-
#
-
# == +save+ and +destroy+ are automatically wrapped in a transaction
-
#
-
# Both +save+ and +destroy+ come wrapped in a transaction that ensures
-
# that whatever you do in validations or callbacks will happen under its
-
# protected cover. So you can use validations to check for values that
-
# the transaction depends on or you can raise exceptions in the callbacks
-
# to rollback, including <tt>after_*</tt> callbacks.
-
#
-
# As a consequence changes to the database are not seen outside your connection
-
# until the operation is complete. For example, if you try to update the index
-
# of a search engine in +after_save+ the indexer won't see the updated record.
-
# The +after_commit+ callback is the only one that is triggered once the update
-
# is committed. See below.
-
#
-
# == Exception handling and rolling back
-
#
-
# Also have in mind that exceptions thrown within a transaction block will
-
# be propagated (after triggering the ROLLBACK), so you should be ready to
-
# catch those in your application code.
-
#
-
# One exception is the <tt>ActiveRecord::Rollback</tt> exception, which will trigger
-
# a ROLLBACK when raised, but not be re-raised by the transaction block.
-
#
-
# *Warning*: one should not catch <tt>ActiveRecord::StatementInvalid</tt> exceptions
-
# inside a transaction block. <tt>ActiveRecord::StatementInvalid</tt> exceptions indicate that an
-
# error occurred at the database level, for example when a unique constraint
-
# is violated. On some database systems, such as PostgreSQL, database errors
-
# inside a transaction cause the entire transaction to become unusable
-
# until it's restarted from the beginning. Here is an example which
-
# demonstrates the problem:
-
#
-
# # Suppose that we have a Number model with a unique column called 'i'.
-
# Number.transaction do
-
# Number.create(i: 0)
-
# begin
-
# # This will raise a unique constraint error...
-
# Number.create(i: 0)
-
# rescue ActiveRecord::StatementInvalid
-
# # ...which we ignore.
-
# end
-
#
-
# # On PostgreSQL, the transaction is now unusable. The following
-
# # statement will cause a PostgreSQL error, even though the unique
-
# # constraint is no longer violated:
-
# Number.create(i: 1)
-
# # => "PGError: ERROR: current transaction is aborted, commands
-
# # ignored until end of transaction block"
-
# end
-
#
-
# One should restart the entire transaction if an
-
# <tt>ActiveRecord::StatementInvalid</tt> occurred.
-
#
-
# == Nested transactions
-
#
-
# +transaction+ calls can be nested. By default, this makes all database
-
# statements in the nested transaction block become part of the parent
-
# transaction. For example, the following behavior may be surprising:
-
#
-
# User.transaction do
-
# User.create(username: 'Kotori')
-
# User.transaction do
-
# User.create(username: 'Nemu')
-
# raise ActiveRecord::Rollback
-
# end
-
# end
-
#
-
# creates both "Kotori" and "Nemu". Reason is the <tt>ActiveRecord::Rollback</tt>
-
# exception in the nested block does not issue a ROLLBACK. Since these exceptions
-
# are captured in transaction blocks, the parent block does not see it and the
-
# real transaction is committed.
-
#
-
# In order to get a ROLLBACK for the nested transaction you may ask for a real
-
# sub-transaction by passing <tt>requires_new: true</tt>. If anything goes wrong,
-
# the database rolls back to the beginning of the sub-transaction without rolling
-
# back the parent transaction. If we add it to the previous example:
-
#
-
# User.transaction do
-
# User.create(username: 'Kotori')
-
# User.transaction(requires_new: true) do
-
# User.create(username: 'Nemu')
-
# raise ActiveRecord::Rollback
-
# end
-
# end
-
#
-
# only "Kotori" is created. This works on MySQL and PostgreSQL. SQLite3 version >= '3.6.8' also supports it.
-
#
-
# Most databases don't support true nested transactions. At the time of
-
# writing, the only database that we're aware of that supports true nested
-
# transactions, is MS-SQL. Because of this, Active Record emulates nested
-
# transactions by using savepoints on MySQL and PostgreSQL. See
-
# http://dev.mysql.com/doc/refman/5.6/en/savepoint.html
-
# for more information about savepoints.
-
#
-
# === Callbacks
-
#
-
# There are two types of callbacks associated with committing and rolling back transactions:
-
# +after_commit+ and +after_rollback+.
-
#
-
# +after_commit+ callbacks are called on every record saved or destroyed within a
-
# transaction immediately after the transaction is committed. +after_rollback+ callbacks
-
# are called on every record saved or destroyed within a transaction immediately after the
-
# transaction or savepoint is rolled back.
-
#
-
# These callbacks are useful for interacting with other systems since you will be guaranteed
-
# that the callback is only executed when the database is in a permanent state. For example,
-
# +after_commit+ is a good spot to put in a hook to clearing a cache since clearing it from
-
# within a transaction could trigger the cache to be regenerated before the database is updated.
-
#
-
# === Caveats
-
#
-
# If you're on MySQL, then do not use DDL operations in nested transactions
-
# blocks that are emulated with savepoints. That is, do not execute statements
-
# like 'CREATE TABLE' inside such blocks. This is because MySQL automatically
-
# releases all savepoints upon executing a DDL operation. When +transaction+
-
# is finished and tries to release the savepoint it created earlier, a
-
# database error will occur because the savepoint has already been
-
# automatically released. The following example demonstrates the problem:
-
#
-
# Model.connection.transaction do # BEGIN
-
# Model.connection.transaction(requires_new: true) do # CREATE SAVEPOINT active_record_1
-
# Model.connection.create_table(...) # active_record_1 now automatically released
-
# end # RELEASE savepoint active_record_1
-
# # ^^^^ BOOM! database error!
-
# end
-
#
-
# Note that "TRUNCATE" is also a MySQL DDL statement!
-
1
module ClassMethods
-
# See ActiveRecord::Transactions::ClassMethods for detailed documentation.
-
1
def transaction(options = {}, &block)
-
# See the ConnectionAdapters::DatabaseStatements#transaction API docs.
-
10
connection.transaction(options, &block)
-
end
-
-
# This callback is called after a record has been created, updated, or destroyed.
-
#
-
# You can specify that the callback should only be fired by a certain action with
-
# the +:on+ option:
-
#
-
# after_commit :do_foo, on: :create
-
# after_commit :do_bar, on: :update
-
# after_commit :do_baz, on: :destroy
-
#
-
# after_commit :do_foo_bar, on: [:create, :update]
-
# after_commit :do_bar_baz, on: [:update, :destroy]
-
#
-
# Note that transactional fixtures do not play well with this feature. Please
-
# use the +test_after_commit+ gem to have these hooks fired in tests.
-
1
def after_commit(*args, &block)
-
set_options_for_callbacks!(args)
-
set_callback(:commit, :after, *args, &block)
-
end
-
-
# This callback is called after a create, update, or destroy are rolled back.
-
#
-
# Please check the documentation of +after_commit+ for options.
-
1
def after_rollback(*args, &block)
-
set_options_for_callbacks!(args)
-
set_callback(:rollback, :after, *args, &block)
-
end
-
-
1
private
-
-
1
def set_options_for_callbacks!(args)
-
options = args.last
-
if options.is_a?(Hash) && options[:on]
-
fire_on = Array(options[:on])
-
assert_valid_transaction_action(fire_on)
-
options[:if] = Array(options[:if])
-
options[:if] << "transaction_include_any_action?(#{fire_on})"
-
end
-
end
-
-
1
def assert_valid_transaction_action(actions)
-
if (actions - ACTIONS).any?
-
raise ArgumentError, ":on conditions for after_commit and after_rollback callbacks have to be one of #{ACTIONS.join(",")}"
-
end
-
end
-
end
-
-
# See ActiveRecord::Transactions::ClassMethods for detailed documentation.
-
1
def transaction(options = {}, &block)
-
self.class.transaction(options, &block)
-
end
-
-
1
def destroy #:nodoc:
-
with_transaction_returning_status { super }
-
end
-
-
1
def save(*) #:nodoc:
-
4
rollback_active_record_state! do
-
8
with_transaction_returning_status { super }
-
end
-
end
-
-
1
def save!(*) #:nodoc:
-
8
with_transaction_returning_status { super }
-
end
-
-
1
def touch(*) #:nodoc:
-
with_transaction_returning_status { super }
-
end
-
-
# Reset id and @new_record if the transaction rolls back.
-
1
def rollback_active_record_state!
-
4
remember_transaction_record_state
-
4
yield
-
rescue Exception
-
restore_transaction_record_state
-
raise
-
ensure
-
4
clear_transaction_record_state
-
end
-
-
# Call the +after_commit+ callbacks.
-
#
-
# Ensure that it is not called if the object was never persisted (failed create),
-
# but call it after the commit of a destroyed object.
-
1
def committed! #:nodoc:
-
run_callbacks :commit if destroyed? || persisted?
-
ensure
-
@_start_transaction_state.clear
-
end
-
-
# Call the +after_rollback+ callbacks. The +force_restore_state+ argument indicates if the record
-
# state should be rolled back to the beginning or just to the last savepoint.
-
1
def rolledback!(force_restore_state = false) #:nodoc:
-
4
run_callbacks :rollback
-
ensure
-
4
restore_transaction_record_state(force_restore_state)
-
4
clear_transaction_record_state
-
end
-
-
# Add the record to the current transaction so that the +after_rollback+ and +after_commit+ callbacks
-
# can be called.
-
1
def add_to_transaction
-
8
if self.class.connection.add_transaction_record(self)
-
8
remember_transaction_record_state
-
end
-
end
-
-
# Executes +method+ within a transaction and captures its return value as a
-
# status flag. If the status is true the transaction is committed, otherwise
-
# a ROLLBACK is issued. In any case the status flag is returned.
-
#
-
# This method is available within the context of an ActiveRecord::Base
-
# instance.
-
1
def with_transaction_returning_status
-
8
status = nil
-
8
self.class.transaction do
-
8
add_to_transaction
-
8
begin
-
8
status = yield
-
rescue ActiveRecord::Rollback
-
@_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1
-
status = nil
-
end
-
-
8
raise ActiveRecord::Rollback unless status
-
end
-
8
status
-
end
-
-
1
protected
-
-
# Save the new record state and id of a record so it can be restored later if a transaction fails.
-
1
def remember_transaction_record_state #:nodoc:
-
12
@_start_transaction_state[:id] = id if has_attribute?(self.class.primary_key)
-
12
unless @_start_transaction_state.include?(:new_record)
-
6
@_start_transaction_state[:new_record] = @new_record
-
end
-
12
unless @_start_transaction_state.include?(:destroyed)
-
6
@_start_transaction_state[:destroyed] = @destroyed
-
end
-
12
@_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) + 1
-
12
@_start_transaction_state[:frozen?] = @attributes.frozen?
-
end
-
-
# Clear the new record state and id of a record.
-
1
def clear_transaction_record_state #:nodoc:
-
8
@_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1
-
8
@_start_transaction_state.clear if @_start_transaction_state[:level] < 1
-
end
-
-
# Restore the new record state and id of a record that was previously saved by a call to save_record_state.
-
1
def restore_transaction_record_state(force = false) #:nodoc:
-
4
unless @_start_transaction_state.empty?
-
4
transaction_level = (@_start_transaction_state[:level] || 0) - 1
-
4
if transaction_level < 1 || force
-
4
restore_state = @_start_transaction_state
-
4
was_frozen = restore_state[:frozen?]
-
4
@attributes = @attributes.dup if @attributes.frozen?
-
4
@new_record = restore_state[:new_record]
-
4
@destroyed = restore_state[:destroyed]
-
4
if restore_state.has_key?(:id)
-
4
write_attribute(self.class.primary_key, restore_state[:id])
-
else
-
@attributes.delete(self.class.primary_key)
-
@attributes_cache.delete(self.class.primary_key)
-
end
-
4
@attributes.freeze if was_frozen
-
end
-
end
-
end
-
-
# Determine if a record was created or destroyed in a transaction. State should be one of :new_record or :destroyed.
-
1
def transaction_record_state(state) #:nodoc:
-
@_start_transaction_state[state]
-
end
-
-
# Determine if a transaction included an action for :create, :update, or :destroy. Used in filtering callbacks.
-
1
def transaction_include_any_action?(actions) #:nodoc:
-
actions.any? do |action|
-
case action
-
when :create
-
transaction_record_state(:new_record)
-
when :destroy
-
destroyed?
-
when :update
-
!(transaction_record_state(:new_record) || destroyed?)
-
end
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
module Translation
-
1
include ActiveModel::Translation
-
-
# Set the lookup ancestors for ActiveModel.
-
1
def lookup_ancestors #:nodoc:
-
klass = self
-
classes = [klass]
-
return classes if klass == ActiveRecord::Base
-
-
while klass != klass.base_class
-
classes << klass = klass.superclass
-
end
-
classes
-
end
-
-
# Set the i18n scope to overwrite ActiveModel.
-
1
def i18n_scope #:nodoc:
-
:activerecord
-
end
-
end
-
end
-
1
module ActiveRecord
-
# = Active Record RecordInvalid
-
#
-
# Raised by <tt>save!</tt> and <tt>create!</tt> when the record is invalid. Use the
-
# +record+ method to retrieve the record which did not validate.
-
#
-
# begin
-
# complex_operation_that_calls_save!_internally
-
# rescue ActiveRecord::RecordInvalid => invalid
-
# puts invalid.record.errors
-
# end
-
1
class RecordInvalid < ActiveRecordError
-
1
attr_reader :record # :nodoc:
-
1
def initialize(record) # :nodoc:
-
@record = record
-
errors = @record.errors.full_messages.join(", ")
-
super(I18n.t(:"#{@record.class.i18n_scope}.errors.messages.record_invalid", :errors => errors, :default => :"errors.messages.record_invalid"))
-
end
-
end
-
-
# = Active Record Validations
-
#
-
# Active Record includes the majority of its validations from <tt>ActiveModel::Validations</tt>
-
# all of which accept the <tt>:on</tt> argument to define the context where the
-
# validations are active. Active Record will always supply either the context of
-
# <tt>:create</tt> or <tt>:update</tt> dependent on whether the model is a
-
# <tt>new_record?</tt>.
-
1
module Validations
-
1
extend ActiveSupport::Concern
-
1
include ActiveModel::Validations
-
-
1
module ClassMethods
-
# Creates an object just like Base.create but calls <tt>save!</tt> instead of +save+
-
# so an exception is raised if the record is invalid.
-
1
def create!(attributes = nil, &block)
-
2
if attributes.is_a?(Array)
-
attributes.collect { |attr| create!(attr, &block) }
-
else
-
2
object = new(attributes)
-
2
yield(object) if block_given?
-
2
object.save!
-
2
object
-
end
-
end
-
end
-
-
# The validation process on save can be skipped by passing <tt>validate: false</tt>.
-
# The regular Base#save method is replaced with this when the validations
-
# module is mixed in, which it is by default.
-
1
def save(options={})
-
4
perform_validations(options) ? super : false
-
end
-
-
# Attempts to save the record just like Base#save but will raise a +RecordInvalid+
-
# exception instead of returning +false+ if the record is not valid.
-
1
def save!(options={})
-
4
perform_validations(options) ? super : raise(RecordInvalid.new(self))
-
end
-
-
# Runs all the validations within the specified context. Returns +true+ if
-
# no errors are found, +false+ otherwise.
-
#
-
# If the argument is +false+ (default is +nil+), the context is set to <tt>:create</tt> if
-
# <tt>new_record?</tt> is +true+, and to <tt>:update</tt> if it is not.
-
#
-
# Validations with no <tt>:on</tt> option will run no matter the context. Validations with
-
# some <tt>:on</tt> option will only run in the specified context.
-
1
def valid?(context = nil)
-
8
context ||= (new_record? ? :create : :update)
-
8
output = super(context)
-
8
errors.empty? && output
-
end
-
-
1
protected
-
-
1
def perform_validations(options={}) # :nodoc:
-
8
options[:validate] == false || valid?(options[:context])
-
end
-
end
-
end
-
-
1
require "active_record/validations/associated"
-
1
require "active_record/validations/uniqueness"
-
1
require "active_record/validations/presence"
-
1
module ActiveRecord
-
1
module Validations
-
1
class AssociatedValidator < ActiveModel::EachValidator #:nodoc:
-
1
def validate_each(record, attribute, value)
-
if Array.wrap(value).reject {|r| r.marked_for_destruction? || r.valid?}.any?
-
record.errors.add(attribute, :invalid, options.merge(:value => value))
-
end
-
end
-
end
-
-
1
module ClassMethods
-
# Validates whether the associated object or objects are all valid.
-
# Works with any kind of association.
-
#
-
# class Book < ActiveRecord::Base
-
# has_many :pages
-
# belongs_to :library
-
#
-
# validates_associated :pages, :library
-
# end
-
#
-
# WARNING: This validation must not be used on both ends of an association.
-
# Doing so will lead to a circular dependency and cause infinite recursion.
-
#
-
# NOTE: This validation will not fail if the association hasn't been
-
# assigned. If you want to ensure that the association is both present and
-
# guaranteed to be valid, you also need to use +validates_presence_of+.
-
#
-
# Configuration options:
-
#
-
# * <tt>:message</tt> - A custom error message (default is: "is invalid").
-
# * <tt>:on</tt> - Specifies when this validation is active. Runs in all
-
# validation contexts by default (+nil+), other options are <tt>:create</tt>
-
# and <tt>:update</tt>.
-
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine
-
# if the validation should occur (e.g. <tt>if: :allow_validation</tt>,
-
# or <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>). The method,
-
# proc or string should return or evaluate to a +true+ or +false+ value.
-
# * <tt>:unless</tt> - Specifies a method, proc or string to call to
-
# determine if the validation should not occur (e.g. <tt>unless: :skip_validation</tt>,
-
# or <tt>unless: Proc.new { |user| user.signup_step <= 2 }</tt>). The
-
# method, proc or string should return or evaluate to a +true+ or +false+
-
# value.
-
1
def validates_associated(*attr_names)
-
validates_with AssociatedValidator, _merge_attributes(attr_names)
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
module Validations
-
1
class PresenceValidator < ActiveModel::Validations::PresenceValidator # :nodoc:
-
1
def validate(record)
-
12
super
-
12
attributes.each do |attribute|
-
12
next unless record.class._reflect_on_association(attribute)
-
associated_records = Array.wrap(record.send(attribute))
-
-
# Superclass validates presence. Ensure present records aren't about to be destroyed.
-
if associated_records.present? && associated_records.all? { |r| r.marked_for_destruction? }
-
record.errors.add(attribute, :blank, options)
-
end
-
end
-
end
-
end
-
-
1
module ClassMethods
-
# Validates that the specified attributes are not blank (as defined by
-
# Object#blank?), and, if the attribute is an association, that the
-
# associated object is not marked for destruction. Happens by default
-
# on save.
-
#
-
# class Person < ActiveRecord::Base
-
# has_one :face
-
# validates_presence_of :face
-
# end
-
#
-
# The face attribute must be in the object and it cannot be blank or marked
-
# for destruction.
-
#
-
# If you want to validate the presence of a boolean field (where the real values
-
# are true and false), you will want to use
-
# <tt>validates_inclusion_of :field_name, in: [true, false]</tt>.
-
#
-
# This is due to the way Object#blank? handles boolean values:
-
# <tt>false.blank? # => true</tt>.
-
#
-
# This validator defers to the ActiveModel validation for presence, adding the
-
# check to see that an associated object is not marked for destruction. This
-
# prevents the parent object from validating successfully and saving, which then
-
# deletes the associated object, thus putting the parent object into an invalid
-
# state.
-
#
-
# Configuration options:
-
# * <tt>:message</tt> - A custom error message (default is: "can't be blank").
-
# * <tt>:on</tt> - Specifies when this validation is active. Runs in all
-
# validation contexts by default (+nil+), other options are <tt>:create</tt>
-
# and <tt>:update</tt>.
-
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine if
-
# the validation should occur (e.g. <tt>if: :allow_validation</tt>, or
-
# <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>). The method, proc
-
# or string should return or evaluate to a +true+ or +false+ value.
-
# * <tt>:unless</tt> - Specifies a method, proc or string to call to determine
-
# if the validation should not occur (e.g. <tt>unless: :skip_validation</tt>,
-
# or <tt>unless: Proc.new { |user| user.signup_step <= 2 }</tt>). The method,
-
# proc or string should return or evaluate to a +true+ or +false+ value.
-
# * <tt>:strict</tt> - Specifies whether validation should be strict.
-
# See <tt>ActiveModel::Validation#validates!</tt> for more information.
-
1
def validates_presence_of(*attr_names)
-
validates_with PresenceValidator, _merge_attributes(attr_names)
-
end
-
end
-
end
-
end
-
1
module ActiveRecord
-
1
module Validations
-
1
class UniquenessValidator < ActiveModel::EachValidator # :nodoc:
-
1
def initialize(options)
-
if options[:conditions] && !options[:conditions].respond_to?(:call)
-
raise ArgumentError, "#{options[:conditions]} was passed as :conditions but is not callable. " \
-
"Pass a callable instead: `conditions: -> { where(approved: true) }`"
-
end
-
super({ case_sensitive: true }.merge!(options))
-
@klass = options[:class]
-
end
-
-
1
def validate_each(record, attribute, value)
-
finder_class = find_finder_class_for(record)
-
table = finder_class.arel_table
-
value = map_enum_attribute(finder_class,attribute,value)
-
value = deserialize_attribute(record, attribute, value)
-
-
relation = build_relation(finder_class, table, attribute, value)
-
relation = relation.and(table[finder_class.primary_key.to_sym].not_eq(record.id)) if record.persisted?
-
relation = scope_relation(record, table, relation)
-
relation = finder_class.unscoped.where(relation)
-
relation = relation.merge(options[:conditions]) if options[:conditions]
-
-
if relation.exists?
-
error_options = options.except(:case_sensitive, :scope, :conditions)
-
error_options[:value] = value
-
-
record.errors.add(attribute, :taken, error_options)
-
end
-
end
-
-
1
protected
-
# The check for an existing value should be run from a class that
-
# isn't abstract. This means working down from the current class
-
# (self), to the first non-abstract class. Since classes don't know
-
# their subclasses, we have to build the hierarchy between self and
-
# the record's class.
-
1
def find_finder_class_for(record) #:nodoc:
-
class_hierarchy = [record.class]
-
-
while class_hierarchy.first != @klass
-
class_hierarchy.unshift(class_hierarchy.first.superclass)
-
end
-
-
class_hierarchy.detect { |klass| !klass.abstract_class? }
-
end
-
-
1
def build_relation(klass, table, attribute, value) #:nodoc:
-
if reflection = klass._reflect_on_association(attribute)
-
attribute = reflection.foreign_key
-
value = value.attributes[reflection.primary_key_column.name] unless value.nil?
-
end
-
-
attribute_name = attribute.to_s
-
-
# the attribute may be an aliased attribute
-
if klass.attribute_aliases[attribute_name]
-
attribute = klass.attribute_aliases[attribute_name]
-
attribute_name = attribute.to_s
-
end
-
-
column = klass.columns_hash[attribute_name]
-
value = klass.connection.type_cast(value, column)
-
value = value.to_s[0, column.limit] if value && column.limit && column.text?
-
-
if !options[:case_sensitive] && value && column.text?
-
# will use SQL LOWER function before comparison, unless it detects a case insensitive collation
-
klass.connection.case_insensitive_comparison(table, attribute, column, value)
-
else
-
value = klass.connection.case_sensitive_modifier(value) unless value.nil?
-
table[attribute].eq(value)
-
end
-
end
-
-
1
def scope_relation(record, table, relation)
-
Array(options[:scope]).each do |scope_item|
-
if reflection = record.class._reflect_on_association(scope_item)
-
scope_value = record.send(reflection.foreign_key)
-
scope_item = reflection.foreign_key
-
else
-
scope_value = record.read_attribute(scope_item)
-
end
-
relation = relation.and(table[scope_item].eq(scope_value))
-
end
-
-
relation
-
end
-
-
1
def deserialize_attribute(record, attribute, value)
-
coder = record.class.serialized_attributes[attribute.to_s]
-
value = coder.dump value if value && coder
-
value
-
end
-
-
1
def map_enum_attribute(klass, attribute, value)
-
mapping = klass.defined_enums[attribute.to_s]
-
value = mapping[value] if value && mapping
-
value
-
end
-
end
-
-
1
module ClassMethods
-
# Validates whether the value of the specified attributes are unique
-
# across the system. Useful for making sure that only one user
-
# can be named "davidhh".
-
#
-
# class Person < ActiveRecord::Base
-
# validates_uniqueness_of :user_name
-
# end
-
#
-
# It can also validate whether the value of the specified attributes are
-
# unique based on a <tt>:scope</tt> parameter:
-
#
-
# class Person < ActiveRecord::Base
-
# validates_uniqueness_of :user_name, scope: :account_id
-
# end
-
#
-
# Or even multiple scope parameters. For example, making sure that a
-
# teacher can only be on the schedule once per semester for a particular
-
# class.
-
#
-
# class TeacherSchedule < ActiveRecord::Base
-
# validates_uniqueness_of :teacher_id, scope: [:semester_id, :class_id]
-
# end
-
#
-
# It is also possible to limit the uniqueness constraint to a set of
-
# records matching certain conditions. In this example archived articles
-
# are not being taken into consideration when validating uniqueness
-
# of the title attribute:
-
#
-
# class Article < ActiveRecord::Base
-
# validates_uniqueness_of :title, conditions: -> { where.not(status: 'archived') }
-
# end
-
#
-
# When the record is created, a check is performed to make sure that no
-
# record exists in the database with the given value for the specified
-
# attribute (that maps to a column). When the record is updated,
-
# the same check is made but disregarding the record itself.
-
#
-
# Configuration options:
-
#
-
# * <tt>:message</tt> - Specifies a custom error message (default is:
-
# "has already been taken").
-
# * <tt>:scope</tt> - One or more columns by which to limit the scope of
-
# the uniqueness constraint.
-
# * <tt>:conditions</tt> - Specify the conditions to be included as a
-
# <tt>WHERE</tt> SQL fragment to limit the uniqueness constraint lookup
-
# (e.g. <tt>conditions: -> { where(status: 'active') }</tt>).
-
# * <tt>:case_sensitive</tt> - Looks for an exact match. Ignored by
-
# non-text columns (+true+ by default).
-
# * <tt>:allow_nil</tt> - If set to +true+, skips this validation if the
-
# attribute is +nil+ (default is +false+).
-
# * <tt>:allow_blank</tt> - If set to +true+, skips this validation if the
-
# attribute is blank (default is +false+).
-
# * <tt>:if</tt> - Specifies a method, proc or string to call to determine
-
# if the validation should occur (e.g. <tt>if: :allow_validation</tt>,
-
# or <tt>if: Proc.new { |user| user.signup_step > 2 }</tt>). The method,
-
# proc or string should return or evaluate to a +true+ or +false+ value.
-
# * <tt>:unless</tt> - Specifies a method, proc or string to call to
-
# determine if the validation should ot occur (e.g. <tt>unless: :skip_validation</tt>,
-
# or <tt>unless: Proc.new { |user| user.signup_step <= 2 }</tt>). The
-
# method, proc or string should return or evaluate to a +true+ or +false+
-
# value.
-
#
-
# === Concurrency and integrity
-
#
-
# Using this validation method in conjunction with ActiveRecord::Base#save
-
# does not guarantee the absence of duplicate record insertions, because
-
# uniqueness checks on the application level are inherently prone to race
-
# conditions. For example, suppose that two users try to post a Comment at
-
# the same time, and a Comment's title must be unique. At the database-level,
-
# the actions performed by these users could be interleaved in the following manner:
-
#
-
# User 1 | User 2
-
# ------------------------------------+--------------------------------------
-
# # User 1 checks whether there's |
-
# # already a comment with the title |
-
# # 'My Post'. This is not the case. |
-
# SELECT * FROM comments |
-
# WHERE title = 'My Post' |
-
# |
-
# | # User 2 does the same thing and also
-
# | # infers that their title is unique.
-
# | SELECT * FROM comments
-
# | WHERE title = 'My Post'
-
# |
-
# # User 1 inserts their comment. |
-
# INSERT INTO comments |
-
# (title, content) VALUES |
-
# ('My Post', 'hi!') |
-
# |
-
# | # User 2 does the same thing.
-
# | INSERT INTO comments
-
# | (title, content) VALUES
-
# | ('My Post', 'hello!')
-
# |
-
# | # ^^^^^^
-
# | # Boom! We now have a duplicate
-
# | # title!
-
#
-
# This could even happen if you use transactions with the 'serializable'
-
# isolation level. The best way to work around this problem is to add a unique
-
# index to the database table using
-
# ActiveRecord::ConnectionAdapters::SchemaStatements#add_index. In the
-
# rare case that a race condition occurs, the database will guarantee
-
# the field's uniqueness.
-
#
-
# When the database catches such a duplicate insertion,
-
# ActiveRecord::Base#save will raise an ActiveRecord::StatementInvalid
-
# exception. You can either choose to let this error propagate (which
-
# will result in the default Rails exception page being shown), or you
-
# can catch it and restart the transaction (e.g. by telling the user
-
# that the title already exists, and asking them to re-enter the title).
-
# This technique is also known as
-
# {optimistic concurrency control}[http://en.wikipedia.org/wiki/Optimistic_concurrency_control].
-
#
-
# The bundled ActiveRecord::ConnectionAdapters distinguish unique index
-
# constraint errors from other types of database errors by throwing an
-
# ActiveRecord::RecordNotUnique exception. For other adapters you will
-
# have to parse the (database-specific) exception message to detect such
-
# a case.
-
#
-
# The following bundled adapters throw the ActiveRecord::RecordNotUnique exception:
-
#
-
# * ActiveRecord::ConnectionAdapters::MysqlAdapter.
-
# * ActiveRecord::ConnectionAdapters::Mysql2Adapter.
-
# * ActiveRecord::ConnectionAdapters::SQLite3Adapter.
-
# * ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.
-
1
def validates_uniqueness_of(*attr_names)
-
validates_with UniquenessValidator, _merge_attributes(attr_names)
-
end
-
end
-
end
-
end
-
1
require_relative 'gem_version'
-
-
1
module ActiveRecord
-
# Returns the version of the currently loaded ActiveRecord as a <tt>Gem::Version</tt>
-
1
def self.version
-
gem_version
-
end
-
end
-
#--
-
# Copyright (c) 2005-2014 David Heinemeier Hansson
-
#
-
# Permission is hereby granted, free of charge, to any person obtaining
-
# a copy of this software and associated documentation files (the
-
# "Software"), to deal in the Software without restriction, including
-
# without limitation the rights to use, copy, modify, merge, publish,
-
# distribute, sublicense, and/or sell copies of the Software, and to
-
# permit persons to whom the Software is furnished to do so, subject to
-
# the following conditions:
-
#
-
# The above copyright notice and this permission notice shall be
-
# included in all copies or substantial portions of the Software.
-
#
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
#++
-
-
1
require 'securerandom'
-
1
require "active_support/dependencies/autoload"
-
1
require "active_support/version"
-
1
require "active_support/logger"
-
1
require "active_support/lazy_load_hooks"
-
-
1
module ActiveSupport
-
1
extend ActiveSupport::Autoload
-
-
1
autoload :Concern
-
1
autoload :Dependencies
-
1
autoload :DescendantsTracker
-
1
autoload :FileUpdateChecker
-
1
autoload :LogSubscriber
-
1
autoload :Notifications
-
-
1
eager_autoload do
-
1
autoload :BacktraceCleaner
-
1
autoload :ProxyObject
-
1
autoload :Benchmarkable
-
1
autoload :Cache
-
1
autoload :Callbacks
-
1
autoload :Configurable
-
1
autoload :Deprecation
-
1
autoload :Gzip
-
1
autoload :Inflector
-
1
autoload :JSON
-
1
autoload :KeyGenerator
-
1
autoload :MessageEncryptor
-
1
autoload :MessageVerifier
-
1
autoload :Multibyte
-
1
autoload :NumberHelper
-
1
autoload :OptionMerger
-
1
autoload :OrderedHash
-
1
autoload :OrderedOptions
-
1
autoload :StringInquirer
-
1
autoload :TaggedLogging
-
1
autoload :XmlMini
-
end
-
-
1
autoload :Rescuable
-
1
autoload :SafeBuffer, "active_support/core_ext/string/output_safety"
-
1
autoload :TestCase
-
-
1
def self.eager_load!
-
super
-
-
NumberHelper.eager_load!
-
end
-
end
-
-
1
autoload :I18n, "active_support/i18n"
-
1
require 'active_support'
-
1
require 'active_support/time'
-
1
require 'active_support/core_ext'
-
1
module ActiveSupport
-
# Backtraces often include many lines that are not relevant for the context
-
# under review. This makes it hard to find the signal amongst the backtrace
-
# noise, and adds debugging time. With a BacktraceCleaner, filters and
-
# silencers are used to remove the noisy lines, so that only the most relevant
-
# lines remain.
-
#
-
# Filters are used to modify lines of data, while silencers are used to remove
-
# lines entirely. The typical filter use case is to remove lengthy path
-
# information from the start of each line, and view file paths relevant to the
-
# app directory instead of the file system root. The typical silencer use case
-
# is to exclude the output of a noisy library from the backtrace, so that you
-
# can focus on the rest.
-
#
-
# bc = BacktraceCleaner.new
-
# bc.add_filter { |line| line.gsub(Rails.root, '') } # strip the Rails.root prefix
-
# bc.add_silencer { |line| line =~ /mongrel|rubygems/ } # skip any lines from mongrel or rubygems
-
# bc.clean(exception.backtrace) # perform the cleanup
-
#
-
# To reconfigure an existing BacktraceCleaner (like the default one in Rails)
-
# and show as much data as possible, you can always call
-
# <tt>BacktraceCleaner#remove_silencers!</tt>, which will restore the
-
# backtrace to a pristine state. If you need to reconfigure an existing
-
# BacktraceCleaner so that it does not filter or modify the paths of any lines
-
# of the backtrace, you can call <tt>BacktraceCleaner#remove_filters!</tt>
-
# These two methods will give you a completely untouched backtrace.
-
#
-
# Inspired by the Quiet Backtrace gem by Thoughtbot.
-
1
class BacktraceCleaner
-
1
def initialize
-
1
@filters, @silencers = [], []
-
end
-
-
# Returns the backtrace after all filters and silencers have been run
-
# against it. Filters run first, then silencers.
-
1
def clean(backtrace, kind = :silent)
-
filtered = filter_backtrace(backtrace)
-
-
case kind
-
when :silent
-
silence(filtered)
-
when :noise
-
noise(filtered)
-
else
-
filtered
-
end
-
end
-
1
alias :filter :clean
-
-
# Adds a filter from the block provided. Each line in the backtrace will be
-
# mapped against this filter.
-
#
-
# # Will turn "/my/rails/root/app/models/person.rb" into "/app/models/person.rb"
-
# backtrace_cleaner.add_filter { |line| line.gsub(Rails.root, '') }
-
1
def add_filter(&block)
-
4
@filters << block
-
end
-
-
# Adds a silencer from the block provided. If the silencer returns +true+
-
# for a given line, it will be excluded from the clean backtrace.
-
#
-
# # Will reject all lines that include the word "mongrel", like "/gems/mongrel/server.rb" or "/app/my_mongrel_server/rb"
-
# backtrace_cleaner.add_silencer { |line| line =~ /mongrel/ }
-
1
def add_silencer(&block)
-
1
@silencers << block
-
end
-
-
# Will remove all silencers, but leave in the filters. This is useful if
-
# your context of debugging suddenly expands as you suspect a bug in one of
-
# the libraries you use.
-
1
def remove_silencers!
-
@silencers = []
-
end
-
-
# Removes all filters, but leaves in silencers. Useful if you suddenly
-
# need to see entire filepaths in the backtrace that you had already
-
# filtered out.
-
1
def remove_filters!
-
@filters = []
-
end
-
-
1
private
-
1
def filter_backtrace(backtrace)
-
@filters.each do |f|
-
backtrace = backtrace.map { |line| f.call(line) }
-
end
-
-
backtrace
-
end
-
-
1
def silence(backtrace)
-
@silencers.each do |s|
-
backtrace = backtrace.reject { |line| s.call(line) }
-
end
-
-
backtrace
-
end
-
-
1
def noise(backtrace)
-
backtrace - silence(backtrace)
-
end
-
end
-
end
-
1
require 'active_support/core_ext/benchmark'
-
1
require 'active_support/core_ext/hash/keys'
-
-
1
module ActiveSupport
-
1
module Benchmarkable
-
# Allows you to measure the execution time of a block in a template and
-
# records the result to the log. Wrap this block around expensive operations
-
# or possible bottlenecks to get a time reading for the operation. For
-
# example, let's say you thought your file processing method was taking too
-
# long; you could wrap it in a benchmark block.
-
#
-
# <% benchmark 'Process data files' do %>
-
# <%= expensive_files_operation %>
-
# <% end %>
-
#
-
# That would add something like "Process data files (345.2ms)" to the log,
-
# which you can then use to compare timings when optimizing your code.
-
#
-
# You may give an optional logger level (<tt>:debug</tt>, <tt>:info</tt>,
-
# <tt>:warn</tt>, <tt>:error</tt>) as the <tt>:level</tt> option. The
-
# default logger level value is <tt>:info</tt>.
-
#
-
# <% benchmark 'Low-level files', level: :debug do %>
-
# <%= lowlevel_files_operation %>
-
# <% end %>
-
#
-
# Finally, you can pass true as the third argument to silence all log
-
# activity (other than the timing information) from inside the block. This
-
# is great for boiling down a noisy block to just a single statement that
-
# produces one log line:
-
#
-
# <% benchmark 'Process data files', level: :info, silence: true do %>
-
# <%= expensive_and_chatty_files_operation %>
-
# <% end %>
-
1
def benchmark(message = "Benchmarking", options = {})
-
if logger
-
options.assert_valid_keys(:level, :silence)
-
options[:level] ||= :info
-
-
result = nil
-
ms = Benchmark.ms { result = options[:silence] ? silence { yield } : yield }
-
logger.send(options[:level], '%s (%.1fms)' % [ message, ms ])
-
result
-
else
-
yield
-
end
-
end
-
end
-
end
-
1
require 'benchmark'
-
1
require 'zlib'
-
1
require 'active_support/core_ext/array/extract_options'
-
1
require 'active_support/core_ext/array/wrap'
-
1
require 'active_support/core_ext/benchmark'
-
1
require 'active_support/core_ext/module/attribute_accessors'
-
1
require 'active_support/core_ext/numeric/bytes'
-
1
require 'active_support/core_ext/numeric/time'
-
1
require 'active_support/core_ext/object/to_param'
-
1
require 'active_support/core_ext/string/inflections'
-
-
1
module ActiveSupport
-
# See ActiveSupport::Cache::Store for documentation.
-
1
module Cache
-
1
autoload :FileStore, 'active_support/cache/file_store'
-
1
autoload :MemoryStore, 'active_support/cache/memory_store'
-
1
autoload :MemCacheStore, 'active_support/cache/mem_cache_store'
-
1
autoload :NullStore, 'active_support/cache/null_store'
-
-
# These options mean something to all cache implementations. Individual cache
-
# implementations may support additional options.
-
1
UNIVERSAL_OPTIONS = [:namespace, :compress, :compress_threshold, :expires_in, :race_condition_ttl]
-
-
1
module Strategy
-
1
autoload :LocalCache, 'active_support/cache/strategy/local_cache'
-
end
-
-
1
class << self
-
# Creates a new CacheStore object according to the given options.
-
#
-
# If no arguments are passed to this method, then a new
-
# ActiveSupport::Cache::MemoryStore object will be returned.
-
#
-
# If you pass a Symbol as the first argument, then a corresponding cache
-
# store class under the ActiveSupport::Cache namespace will be created.
-
# For example:
-
#
-
# ActiveSupport::Cache.lookup_store(:memory_store)
-
# # => returns a new ActiveSupport::Cache::MemoryStore object
-
#
-
# ActiveSupport::Cache.lookup_store(:mem_cache_store)
-
# # => returns a new ActiveSupport::Cache::MemCacheStore object
-
#
-
# Any additional arguments will be passed to the corresponding cache store
-
# class's constructor:
-
#
-
# ActiveSupport::Cache.lookup_store(:file_store, '/tmp/cache')
-
# # => same as: ActiveSupport::Cache::FileStore.new('/tmp/cache')
-
#
-
# If the first argument is not a Symbol, then it will simply be returned:
-
#
-
# ActiveSupport::Cache.lookup_store(MyOwnCacheStore.new)
-
# # => returns MyOwnCacheStore.new
-
1
def lookup_store(*store_option)
-
2
store, *parameters = *Array.wrap(store_option).flatten
-
-
2
case store
-
when Symbol
-
1
retrieve_store_class(store).new(*parameters)
-
when nil
-
ActiveSupport::Cache::MemoryStore.new
-
else
-
1
store
-
end
-
end
-
-
# Expands out the +key+ argument into a key that can be used for the
-
# cache store. Optionally accepts a namespace, and all keys will be
-
# scoped within that namespace.
-
#
-
# If the +key+ argument provided is an array, or responds to +to_a+, then
-
# each of elements in the array will be turned into parameters/keys and
-
# concatenated into a single key. For example:
-
#
-
# expand_cache_key([:foo, :bar]) # => "foo/bar"
-
# expand_cache_key([:foo, :bar], "namespace") # => "namespace/foo/bar"
-
#
-
# The +key+ argument can also respond to +cache_key+ or +to_param+.
-
1
def expand_cache_key(key, namespace = nil)
-
expanded_cache_key = namespace ? "#{namespace}/" : ""
-
-
if prefix = ENV["RAILS_CACHE_ID"] || ENV["RAILS_APP_VERSION"]
-
expanded_cache_key << "#{prefix}/"
-
end
-
-
expanded_cache_key << retrieve_cache_key(key)
-
expanded_cache_key
-
end
-
-
1
private
-
1
def retrieve_cache_key(key)
-
case
-
when key.respond_to?(:cache_key) then key.cache_key
-
when key.is_a?(Array) then key.map { |element| retrieve_cache_key(element) }.to_param
-
when key.respond_to?(:to_a) then retrieve_cache_key(key.to_a)
-
else key.to_param
-
end.to_s
-
end
-
-
# Obtains the specified cache store class, given the name of the +store+.
-
# Raises an error when the store class cannot be found.
-
1
def retrieve_store_class(store)
-
1
require "active_support/cache/#{store}"
-
rescue LoadError => e
-
raise "Could not find cache store adapter for #{store} (#{e})"
-
else
-
1
ActiveSupport::Cache.const_get(store.to_s.camelize)
-
end
-
end
-
-
# An abstract cache store class. There are multiple cache store
-
# implementations, each having its own additional features. See the classes
-
# under the ActiveSupport::Cache module, e.g.
-
# ActiveSupport::Cache::MemCacheStore. MemCacheStore is currently the most
-
# popular cache store for large production websites.
-
#
-
# Some implementations may not support all methods beyond the basic cache
-
# methods of +fetch+, +write+, +read+, +exist?+, and +delete+.
-
#
-
# ActiveSupport::Cache::Store can store any serializable Ruby object.
-
#
-
# cache = ActiveSupport::Cache::MemoryStore.new
-
#
-
# cache.read('city') # => nil
-
# cache.write('city', "Duckburgh")
-
# cache.read('city') # => "Duckburgh"
-
#
-
# Keys are always translated into Strings and are case sensitive. When an
-
# object is specified as a key and has a +cache_key+ method defined, this
-
# method will be called to define the key. Otherwise, the +to_param+
-
# method will be called. Hashes and Arrays can also be used as keys. The
-
# elements will be delimited by slashes, and the elements within a Hash
-
# will be sorted by key so they are consistent.
-
#
-
# cache.read('city') == cache.read(:city) # => true
-
#
-
# Nil values can be cached.
-
#
-
# If your cache is on a shared infrastructure, you can define a namespace
-
# for your cache entries. If a namespace is defined, it will be prefixed on
-
# to every key. The namespace can be either a static value or a Proc. If it
-
# is a Proc, it will be invoked when each key is evaluated so that you can
-
# use application logic to invalidate keys.
-
#
-
# cache.namespace = -> { @last_mod_time } # Set the namespace to a variable
-
# @last_mod_time = Time.now # Invalidate the entire cache by changing namespace
-
#
-
# Caches can also store values in a compressed format to save space and
-
# reduce time spent sending data. Since there is overhead, values must be
-
# large enough to warrant compression. To turn on compression either pass
-
# <tt>compress: true</tt> in the initializer or as an option to +fetch+
-
# or +write+. To specify the threshold at which to compress values, set the
-
# <tt>:compress_threshold</tt> option. The default threshold is 16K.
-
1
class Store
-
1
cattr_accessor :logger, :instance_writer => true
-
-
1
attr_reader :silence, :options
-
1
alias :silence? :silence
-
-
# Create a new cache. The options will be passed to any write method calls
-
# except for <tt>:namespace</tt> which can be used to set the global
-
# namespace for the cache.
-
1
def initialize(options = nil)
-
1
@options = options ? options.dup : {}
-
end
-
-
# Silence the logger.
-
1
def silence!
-
@silence = true
-
self
-
end
-
-
# Silence the logger within a block.
-
1
def mute
-
previous_silence, @silence = defined?(@silence) && @silence, true
-
yield
-
ensure
-
@silence = previous_silence
-
end
-
-
# Set to +true+ if cache stores should be instrumented.
-
# Default is +false+.
-
1
def self.instrument=(boolean)
-
Thread.current[:instrument_cache_store] = boolean
-
end
-
-
1
def self.instrument
-
Thread.current[:instrument_cache_store] || false
-
end
-
-
# Fetches data from the cache, using the given key. If there is data in
-
# the cache with the given key, then that data is returned.
-
#
-
# If there is no such data in the cache (a cache miss), then +nil+ will be
-
# returned. However, if a block has been passed, that block will be passed
-
# the key and executed in the event of a cache miss. The return value of the
-
# block will be written to the cache under the given cache key, and that
-
# return value will be returned.
-
#
-
# cache.write('today', 'Monday')
-
# cache.fetch('today') # => "Monday"
-
#
-
# cache.fetch('city') # => nil
-
# cache.fetch('city') do
-
# 'Duckburgh'
-
# end
-
# cache.fetch('city') # => "Duckburgh"
-
#
-
# You may also specify additional options via the +options+ argument.
-
# Setting <tt>force: true</tt> will force a cache miss:
-
#
-
# cache.write('today', 'Monday')
-
# cache.fetch('today', force: true) # => nil
-
#
-
# Setting <tt>:compress</tt> will store a large cache entry set by the call
-
# in a compressed format.
-
#
-
# Setting <tt>:expires_in</tt> will set an expiration time on the cache.
-
# All caches support auto-expiring content after a specified number of
-
# seconds. This value can be specified as an option to the constructor
-
# (in which case all entries will be affected), or it can be supplied to
-
# the +fetch+ or +write+ method to effect just one entry.
-
#
-
# cache = ActiveSupport::Cache::MemoryStore.new(expires_in: 5.minutes)
-
# cache.write(key, value, expires_in: 1.minute) # Set a lower value for one entry
-
#
-
# Setting <tt>:race_condition_ttl</tt> is very useful in situations where
-
# a cache entry is used very frequently and is under heavy load. If a
-
# cache expires and due to heavy load several different processes will try
-
# to read data natively and then they all will try to write to cache. To
-
# avoid that case the first process to find an expired cache entry will
-
# bump the cache expiration time by the value set in <tt>:race_condition_ttl</tt>.
-
# Yes, this process is extending the time for a stale value by another few
-
# seconds. Because of extended life of the previous cache, other processes
-
# will continue to use slightly stale data for a just a bit longer. In the
-
# meantime that first process will go ahead and will write into cache the
-
# new value. After that all the processes will start getting new value.
-
# The key is to keep <tt>:race_condition_ttl</tt> small.
-
#
-
# If the process regenerating the entry errors out, the entry will be
-
# regenerated after the specified number of seconds. Also note that the
-
# life of stale cache is extended only if it expired recently. Otherwise
-
# a new value is generated and <tt>:race_condition_ttl</tt> does not play
-
# any role.
-
#
-
# # Set all values to expire after one minute.
-
# cache = ActiveSupport::Cache::MemoryStore.new(expires_in: 1.minute)
-
#
-
# cache.write('foo', 'original value')
-
# val_1 = nil
-
# val_2 = nil
-
# sleep 60
-
#
-
# Thread.new do
-
# val_1 = cache.fetch('foo', race_condition_ttl: 10) do
-
# sleep 1
-
# 'new value 1'
-
# end
-
# end
-
#
-
# Thread.new do
-
# val_2 = cache.fetch('foo', race_condition_ttl: 10) do
-
# 'new value 2'
-
# end
-
# end
-
#
-
# # val_1 => "new value 1"
-
# # val_2 => "original value"
-
# # sleep 10 # First thread extend the life of cache by another 10 seconds
-
# # cache.fetch('foo') => "new value 1"
-
#
-
# Other options will be handled by the specific cache store implementation.
-
# Internally, #fetch calls #read_entry, and calls #write_entry on a cache
-
# miss. +options+ will be passed to the #read and #write calls.
-
#
-
# For example, MemCacheStore's #write method supports the +:raw+
-
# option, which tells the memcached server to store all values as strings.
-
# We can use this option with #fetch too:
-
#
-
# cache = ActiveSupport::Cache::MemCacheStore.new
-
# cache.fetch("foo", force: true, raw: true) do
-
# :bar
-
# end
-
# cache.fetch('foo') # => "bar"
-
1
def fetch(name, options = nil)
-
if block_given?
-
options = merged_options(options)
-
key = namespaced_key(name, options)
-
-
cached_entry = find_cached_entry(key, name, options) unless options[:force]
-
entry = handle_expired_entry(cached_entry, key, options)
-
-
if entry
-
get_entry_value(entry, name, options)
-
else
-
save_block_result_to_cache(name, options) { |_name| yield _name }
-
end
-
else
-
read(name, options)
-
end
-
end
-
-
# Fetches data from the cache, using the given key. If there is data in
-
# the cache with the given key, then that data is returned. Otherwise,
-
# +nil+ is returned.
-
#
-
# Options are passed to the underlying cache implementation.
-
1
def read(name, options = nil)
-
options = merged_options(options)
-
key = namespaced_key(name, options)
-
instrument(:read, name, options) do |payload|
-
entry = read_entry(key, options)
-
if entry
-
if entry.expired?
-
delete_entry(key, options)
-
payload[:hit] = false if payload
-
nil
-
else
-
payload[:hit] = true if payload
-
entry.value
-
end
-
else
-
payload[:hit] = false if payload
-
nil
-
end
-
end
-
end
-
-
# Read multiple values at once from the cache. Options can be passed
-
# in the last argument.
-
#
-
# Some cache implementation may optimize this method.
-
#
-
# Returns a hash mapping the names provided to the values found.
-
1
def read_multi(*names)
-
options = names.extract_options!
-
options = merged_options(options)
-
results = {}
-
names.each do |name|
-
key = namespaced_key(name, options)
-
entry = read_entry(key, options)
-
if entry
-
if entry.expired?
-
delete_entry(key, options)
-
else
-
results[name] = entry.value
-
end
-
end
-
end
-
results
-
end
-
-
# Fetches data from the cache, using the given keys. If there is data in
-
# the cache with the given keys, then that data is returned. Otherwise,
-
# the supplied block is called for each key for which there was no data,
-
# and the result will be written to the cache and returned.
-
#
-
# Options are passed to the underlying cache implementation.
-
#
-
# Returns an array with the data for each of the names. For example:
-
#
-
# cache.write("bim", "bam")
-
# cache.fetch_multi("bim", "boom") {|key| key * 2 }
-
# # => ["bam", "boomboom"]
-
#
-
1
def fetch_multi(*names)
-
options = names.extract_options!
-
options = merged_options(options)
-
-
results = read_multi(*names, options)
-
-
names.map do |name|
-
results.fetch(name) do
-
value = yield name
-
write(name, value, options)
-
value
-
end
-
end
-
end
-
-
# Writes the value to the cache, with the key.
-
#
-
# Options are passed to the underlying cache implementation.
-
1
def write(name, value, options = nil)
-
options = merged_options(options)
-
-
instrument(:write, name, options) do
-
entry = Entry.new(value, options)
-
write_entry(namespaced_key(name, options), entry, options)
-
end
-
end
-
-
# Deletes an entry in the cache. Returns +true+ if an entry is deleted.
-
#
-
# Options are passed to the underlying cache implementation.
-
1
def delete(name, options = nil)
-
options = merged_options(options)
-
-
instrument(:delete, name) do
-
delete_entry(namespaced_key(name, options), options)
-
end
-
end
-
-
# Returns +true+ if the cache contains an entry for the given key.
-
#
-
# Options are passed to the underlying cache implementation.
-
1
def exist?(name, options = nil)
-
options = merged_options(options)
-
-
instrument(:exist?, name) do
-
entry = read_entry(namespaced_key(name, options), options)
-
(entry && !entry.expired?) || false
-
end
-
end
-
-
# Delete all entries with keys matching the pattern.
-
#
-
# Options are passed to the underlying cache implementation.
-
#
-
# All implementations may not support this method.
-
1
def delete_matched(matcher, options = nil)
-
raise NotImplementedError.new("#{self.class.name} does not support delete_matched")
-
end
-
-
# Increment an integer value in the cache.
-
#
-
# Options are passed to the underlying cache implementation.
-
#
-
# All implementations may not support this method.
-
1
def increment(name, amount = 1, options = nil)
-
raise NotImplementedError.new("#{self.class.name} does not support increment")
-
end
-
-
# Decrement an integer value in the cache.
-
#
-
# Options are passed to the underlying cache implementation.
-
#
-
# All implementations may not support this method.
-
1
def decrement(name, amount = 1, options = nil)
-
raise NotImplementedError.new("#{self.class.name} does not support decrement")
-
end
-
-
# Cleanup the cache by removing expired entries.
-
#
-
# Options are passed to the underlying cache implementation.
-
#
-
# All implementations may not support this method.
-
1
def cleanup(options = nil)
-
raise NotImplementedError.new("#{self.class.name} does not support cleanup")
-
end
-
-
# Clear the entire cache. Be careful with this method since it could
-
# affect other processes if shared cache is being used.
-
#
-
# The options hash is passed to the underlying cache implementation.
-
#
-
# All implementations may not support this method.
-
1
def clear(options = nil)
-
raise NotImplementedError.new("#{self.class.name} does not support clear")
-
end
-
-
1
protected
-
# Add the namespace defined in the options to a pattern designed to
-
# match keys. Implementations that support delete_matched should call
-
# this method to translate a pattern that matches names into one that
-
# matches namespaced keys.
-
1
def key_matcher(pattern, options)
-
prefix = options[:namespace].is_a?(Proc) ? options[:namespace].call : options[:namespace]
-
if prefix
-
source = pattern.source
-
if source.start_with?('^')
-
source = source[1, source.length]
-
else
-
source = ".*#{source[0, source.length]}"
-
end
-
Regexp.new("^#{Regexp.escape(prefix)}:#{source}", pattern.options)
-
else
-
pattern
-
end
-
end
-
-
# Read an entry from the cache implementation. Subclasses must implement
-
# this method.
-
1
def read_entry(key, options) # :nodoc:
-
raise NotImplementedError.new
-
end
-
-
# Write an entry to the cache implementation. Subclasses must implement
-
# this method.
-
1
def write_entry(key, entry, options) # :nodoc:
-
raise NotImplementedError.new
-
end
-
-
# Delete an entry from the cache implementation. Subclasses must
-
# implement this method.
-
1
def delete_entry(key, options) # :nodoc:
-
raise NotImplementedError.new
-
end
-
-
1
private
-
# Merge the default options with ones specific to a method call.
-
1
def merged_options(call_options) # :nodoc:
-
if call_options
-
options.merge(call_options)
-
else
-
options.dup
-
end
-
end
-
-
# Expand key to be a consistent string value. Invoke +cache_key+ if
-
# object responds to +cache_key+. Otherwise, +to_param+ method will be
-
# called. If the key is a Hash, then keys will be sorted alphabetically.
-
1
def expanded_key(key) # :nodoc:
-
return key.cache_key.to_s if key.respond_to?(:cache_key)
-
-
case key
-
when Array
-
if key.size > 1
-
key = key.collect{|element| expanded_key(element)}
-
else
-
key = key.first
-
end
-
when Hash
-
key = key.sort_by { |k,_| k.to_s }.collect{|k,v| "#{k}=#{v}"}
-
end
-
-
key.to_param
-
end
-
-
# Prefix a key with the namespace. Namespace and key will be delimited
-
# with a colon.
-
1
def namespaced_key(key, options)
-
key = expanded_key(key)
-
namespace = options[:namespace] if options
-
prefix = namespace.is_a?(Proc) ? namespace.call : namespace
-
key = "#{prefix}:#{key}" if prefix
-
key
-
end
-
-
1
def instrument(operation, key, options = nil)
-
log(operation, key, options)
-
-
if self.class.instrument
-
payload = { :key => key }
-
payload.merge!(options) if options.is_a?(Hash)
-
ActiveSupport::Notifications.instrument("cache_#{operation}.active_support", payload){ yield(payload) }
-
else
-
yield(nil)
-
end
-
end
-
-
1
def log(operation, key, options = nil)
-
return unless logger && logger.debug? && !silence?
-
logger.debug("Cache #{operation}: #{key}#{options.blank? ? "" : " (#{options.inspect})"}")
-
end
-
-
1
def find_cached_entry(key, name, options)
-
instrument(:read, name, options) do |payload|
-
payload[:super_operation] = :fetch if payload
-
read_entry(key, options)
-
end
-
end
-
-
1
def handle_expired_entry(entry, key, options)
-
if entry && entry.expired?
-
race_ttl = options[:race_condition_ttl].to_i
-
if race_ttl && (Time.now.to_f - entry.expires_at <= race_ttl)
-
# When an entry has :race_condition_ttl defined, put the stale entry back into the cache
-
# for a brief period while the entry is begin recalculated.
-
entry.expires_at = Time.now + race_ttl
-
write_entry(key, entry, :expires_in => race_ttl * 2)
-
else
-
delete_entry(key, options)
-
end
-
entry = nil
-
end
-
entry
-
end
-
-
1
def get_entry_value(entry, name, options)
-
instrument(:fetch_hit, name, options) { |payload| }
-
entry.value
-
end
-
-
1
def save_block_result_to_cache(name, options)
-
result = instrument(:generate, name, options) do |payload|
-
yield(name)
-
end
-
-
write(name, result, options)
-
result
-
end
-
end
-
-
# This class is used to represent cache entries. Cache entries have a value and an optional
-
# expiration time. The expiration time is used to support the :race_condition_ttl option
-
# on the cache.
-
#
-
# Since cache entries in most instances will be serialized, the internals of this class are highly optimized
-
# using short instance variable names that are lazily defined.
-
1
class Entry # :nodoc:
-
1
DEFAULT_COMPRESS_LIMIT = 16.kilobytes
-
-
# Create a new cache entry for the specified value. Options supported are
-
# +:compress+, +:compress_threshold+, and +:expires_in+.
-
1
def initialize(value, options = {})
-
if should_compress?(value, options)
-
@value = compress(value)
-
@compressed = true
-
else
-
@value = value
-
end
-
-
@created_at = Time.now.to_f
-
@expires_in = options[:expires_in]
-
@expires_in = @expires_in.to_f if @expires_in
-
end
-
-
1
def value
-
convert_version_4beta1_entry! if defined?(@v)
-
compressed? ? uncompress(@value) : @value
-
end
-
-
# Check if the entry is expired. The +expires_in+ parameter can override
-
# the value set when the entry was created.
-
1
def expired?
-
convert_version_4beta1_entry! if defined?(@value)
-
@expires_in && @created_at + @expires_in <= Time.now.to_f
-
end
-
-
1
def expires_at
-
@expires_in ? @created_at + @expires_in : nil
-
end
-
-
1
def expires_at=(value)
-
if value
-
@expires_in = value.to_f - @created_at
-
else
-
@expires_in = nil
-
end
-
end
-
-
# Returns the size of the cached value. This could be less than
-
# <tt>value.size</tt> if the data is compressed.
-
1
def size
-
if defined?(@s)
-
@s
-
else
-
case value
-
when NilClass
-
0
-
when String
-
@value.bytesize
-
else
-
@s = Marshal.dump(@value).bytesize
-
end
-
end
-
end
-
-
# Duplicate the value in a class. This is used by cache implementations that don't natively
-
# serialize entries to protect against accidental cache modifications.
-
1
def dup_value!
-
convert_version_4beta1_entry! if defined?(@v)
-
-
if @value && !compressed? && !(@value.is_a?(Numeric) || @value == true || @value == false)
-
if @value.is_a?(String)
-
@value = @value.dup
-
else
-
@value = Marshal.load(Marshal.dump(@value))
-
end
-
end
-
end
-
-
1
private
-
1
def should_compress?(value, options)
-
if value && options[:compress]
-
compress_threshold = options[:compress_threshold] || DEFAULT_COMPRESS_LIMIT
-
serialized_value_size = (value.is_a?(String) ? value : Marshal.dump(value)).bytesize
-
-
return true if serialized_value_size >= compress_threshold
-
end
-
-
false
-
end
-
-
1
def compressed?
-
defined?(@compressed) ? @compressed : false
-
end
-
-
1
def compress(value)
-
Zlib::Deflate.deflate(Marshal.dump(value))
-
end
-
-
1
def uncompress(value)
-
Marshal.load(Zlib::Inflate.inflate(value))
-
end
-
-
# The internals of this method changed between Rails 3.x and 4.0. This method provides the glue
-
# to ensure that cache entries created under the old version still work with the new class definition.
-
1
def convert_version_4beta1_entry!
-
if defined?(@v)
-
@value = @v
-
remove_instance_variable(:@v)
-
end
-
-
if defined?(@c)
-
@compressed = @c
-
remove_instance_variable(:@c)
-
end
-
-
if defined?(@x) && @x
-
@created_at ||= Time.now.to_f
-
@expires_in = @x - @created_at
-
remove_instance_variable(:@x)
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/marshal'
-
1
require 'active_support/core_ext/file/atomic'
-
1
require 'active_support/core_ext/string/conversions'
-
1
require 'uri/common'
-
-
1
module ActiveSupport
-
1
module Cache
-
# A cache store implementation which stores everything on the filesystem.
-
#
-
# FileStore implements the Strategy::LocalCache strategy which implements
-
# an in-memory cache inside of a block.
-
1
class FileStore < Store
-
1
attr_reader :cache_path
-
-
1
DIR_FORMATTER = "%03X"
-
1
FILENAME_MAX_SIZE = 228 # max filename size on file system is 255, minus room for timestamp and random characters appended by Tempfile (used by atomic write)
-
1
EXCLUDED_DIRS = ['.', '..'].freeze
-
-
1
def initialize(cache_path, options = nil)
-
1
super(options)
-
1
@cache_path = cache_path.to_s
-
1
extend Strategy::LocalCache
-
end
-
-
# Deletes all items from the cache. In this case it deletes all the entries in the specified
-
# file store directory except for .gitkeep. Be careful which directory is specified in your
-
# config file when using +FileStore+ because everything in that directory will be deleted.
-
1
def clear(options = nil)
-
root_dirs = Dir.entries(cache_path).reject {|f| (EXCLUDED_DIRS + [".gitkeep"]).include?(f)}
-
FileUtils.rm_r(root_dirs.collect{|f| File.join(cache_path, f)})
-
end
-
-
# Preemptively iterates through all stored keys and removes the ones which have expired.
-
1
def cleanup(options = nil)
-
options = merged_options(options)
-
search_dir(cache_path) do |fname|
-
key = file_path_key(fname)
-
entry = read_entry(key, options)
-
delete_entry(key, options) if entry && entry.expired?
-
end
-
end
-
-
# Increments an already existing integer value that is stored in the cache.
-
# If the key is not found nothing is done.
-
1
def increment(name, amount = 1, options = nil)
-
modify_value(name, amount, options)
-
end
-
-
# Decrements an already existing integer value that is stored in the cache.
-
# If the key is not found nothing is done.
-
1
def decrement(name, amount = 1, options = nil)
-
modify_value(name, -amount, options)
-
end
-
-
1
def delete_matched(matcher, options = nil)
-
options = merged_options(options)
-
instrument(:delete_matched, matcher.inspect) do
-
matcher = key_matcher(matcher, options)
-
search_dir(cache_path) do |path|
-
key = file_path_key(path)
-
delete_entry(key, options) if key.match(matcher)
-
end
-
end
-
end
-
-
1
protected
-
-
1
def read_entry(key, options)
-
file_name = key_file_path(key)
-
if File.exist?(file_name)
-
File.open(file_name) { |f| Marshal.load(f) }
-
end
-
rescue => e
-
logger.error("FileStoreError (#{e}): #{e.message}") if logger
-
nil
-
end
-
-
1
def write_entry(key, entry, options)
-
file_name = key_file_path(key)
-
return false if options[:unless_exist] && File.exist?(file_name)
-
ensure_cache_path(File.dirname(file_name))
-
File.atomic_write(file_name, cache_path) {|f| Marshal.dump(entry, f)}
-
true
-
end
-
-
1
def delete_entry(key, options)
-
file_name = key_file_path(key)
-
if File.exist?(file_name)
-
begin
-
File.delete(file_name)
-
delete_empty_directories(File.dirname(file_name))
-
true
-
rescue => e
-
# Just in case the error was caused by another process deleting the file first.
-
raise e if File.exist?(file_name)
-
false
-
end
-
end
-
end
-
-
1
private
-
# Lock a file for a block so only one process can modify it at a time.
-
1
def lock_file(file_name, &block) # :nodoc:
-
if File.exist?(file_name)
-
File.open(file_name, 'r+') do |f|
-
begin
-
f.flock File::LOCK_EX
-
yield
-
ensure
-
f.flock File::LOCK_UN
-
end
-
end
-
else
-
yield
-
end
-
end
-
-
# Translate a key into a file path.
-
1
def key_file_path(key)
-
fname = URI.encode_www_form_component(key)
-
hash = Zlib.adler32(fname)
-
hash, dir_1 = hash.divmod(0x1000)
-
dir_2 = hash.modulo(0x1000)
-
fname_paths = []
-
-
# Make sure file name doesn't exceed file system limits.
-
begin
-
fname_paths << fname[0, FILENAME_MAX_SIZE]
-
fname = fname[FILENAME_MAX_SIZE..-1]
-
end until fname.blank?
-
-
File.join(cache_path, DIR_FORMATTER % dir_1, DIR_FORMATTER % dir_2, *fname_paths)
-
end
-
-
# Translate a file path into a key.
-
1
def file_path_key(path)
-
fname = path[cache_path.to_s.size..-1].split(File::SEPARATOR, 4).last
-
URI.decode_www_form_component(fname, Encoding::UTF_8)
-
end
-
-
# Delete empty directories in the cache.
-
1
def delete_empty_directories(dir)
-
return if File.realpath(dir) == File.realpath(cache_path)
-
if Dir.entries(dir).reject {|f| EXCLUDED_DIRS.include?(f)}.empty?
-
Dir.delete(dir) rescue nil
-
delete_empty_directories(File.dirname(dir))
-
end
-
end
-
-
# Make sure a file path's directories exist.
-
1
def ensure_cache_path(path)
-
FileUtils.makedirs(path) unless File.exist?(path)
-
end
-
-
1
def search_dir(dir, &callback)
-
return if !File.exist?(dir)
-
Dir.foreach(dir) do |d|
-
next if EXCLUDED_DIRS.include?(d)
-
name = File.join(dir, d)
-
if File.directory?(name)
-
search_dir(name, &callback)
-
else
-
callback.call name
-
end
-
end
-
end
-
-
# Modifies the amount of an already existing integer value that is stored in the cache.
-
# If the key is not found nothing is done.
-
1
def modify_value(name, amount, options)
-
file_name = key_file_path(namespaced_key(name, options))
-
-
lock_file(file_name) do
-
options = merged_options(options)
-
-
if num = read(name, options)
-
num = num.to_i + amount
-
write(name, num, options)
-
num
-
end
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/object/duplicable'
-
1
require 'active_support/core_ext/string/inflections'
-
1
require 'active_support/per_thread_registry'
-
-
1
module ActiveSupport
-
1
module Cache
-
1
module Strategy
-
# Caches that implement LocalCache will be backed by an in-memory cache for the
-
# duration of a block. Repeated calls to the cache for the same key will hit the
-
# in-memory cache for faster access.
-
1
module LocalCache
-
1
autoload :Middleware, 'active_support/cache/strategy/local_cache_middleware'
-
-
# Class for storing and registering the local caches.
-
1
class LocalCacheRegistry # :nodoc:
-
1
extend ActiveSupport::PerThreadRegistry
-
-
1
def initialize
-
@registry = {}
-
end
-
-
1
def cache_for(local_cache_key)
-
@registry[local_cache_key]
-
end
-
-
1
def set_cache_for(local_cache_key, value)
-
@registry[local_cache_key] = value
-
end
-
-
1
def self.set_cache_for(l, v); instance.set_cache_for l, v; end
-
1
def self.cache_for(l); instance.cache_for l; end
-
end
-
-
# Simple memory backed cache. This cache is not thread safe and is intended only
-
# for serving as a temporary memory cache for a single thread.
-
1
class LocalStore < Store
-
1
def initialize
-
super
-
@data = {}
-
end
-
-
# Don't allow synchronizing since it isn't thread safe,
-
1
def synchronize # :nodoc:
-
yield
-
end
-
-
1
def clear(options = nil)
-
@data.clear
-
end
-
-
1
def read_entry(key, options)
-
@data[key]
-
end
-
-
1
def write_entry(key, value, options)
-
@data[key] = value
-
true
-
end
-
-
1
def delete_entry(key, options)
-
!!@data.delete(key)
-
end
-
end
-
-
# Use a local cache for the duration of block.
-
1
def with_local_cache
-
use_temporary_local_cache(LocalStore.new) { yield }
-
end
-
# Middleware class can be inserted as a Rack handler to be local cache for the
-
# duration of request.
-
1
def middleware
-
@middleware ||= Middleware.new(
-
"ActiveSupport::Cache::Strategy::LocalCache",
-
1
local_cache_key)
-
end
-
-
1
def clear(options = nil) # :nodoc:
-
local_cache.clear(options) if local_cache
-
super
-
end
-
-
1
def cleanup(options = nil) # :nodoc:
-
local_cache.clear(options) if local_cache
-
super
-
end
-
-
1
def increment(name, amount = 1, options = nil) # :nodoc:
-
value = bypass_local_cache{super}
-
increment_or_decrement(value, name, amount, options)
-
value
-
end
-
-
1
def decrement(name, amount = 1, options = nil) # :nodoc:
-
value = bypass_local_cache{super}
-
increment_or_decrement(value, name, amount, options)
-
value
-
end
-
-
1
protected
-
1
def read_entry(key, options) # :nodoc:
-
if local_cache
-
entry = local_cache.read_entry(key, options)
-
unless entry
-
entry = super
-
local_cache.write_entry(key, entry, options)
-
end
-
entry
-
else
-
super
-
end
-
end
-
-
1
def write_entry(key, entry, options) # :nodoc:
-
local_cache.write_entry(key, entry, options) if local_cache
-
super
-
end
-
-
1
def delete_entry(key, options) # :nodoc:
-
local_cache.delete_entry(key, options) if local_cache
-
super
-
end
-
-
1
private
-
1
def increment_or_decrement(value, name, amount, options)
-
if local_cache
-
local_cache.mute do
-
if value
-
local_cache.write(name, value, options)
-
else
-
local_cache.delete(name, options)
-
end
-
end
-
end
-
end
-
-
1
def local_cache_key
-
1
@local_cache_key ||= "#{self.class.name.underscore}_local_cache_#{object_id}".gsub(/[\/-]/, '_').to_sym
-
end
-
-
1
def local_cache
-
LocalCacheRegistry.cache_for(local_cache_key)
-
end
-
-
1
def bypass_local_cache
-
use_temporary_local_cache(nil) { yield }
-
end
-
-
1
def use_temporary_local_cache(temporary_cache)
-
save_cache = LocalCacheRegistry.cache_for(local_cache_key)
-
begin
-
LocalCacheRegistry.set_cache_for(local_cache_key, temporary_cache)
-
yield
-
ensure
-
LocalCacheRegistry.set_cache_for(local_cache_key, save_cache)
-
end
-
end
-
end
-
end
-
end
-
end
-
1
require 'rack/body_proxy'
-
1
module ActiveSupport
-
1
module Cache
-
1
module Strategy
-
1
module LocalCache
-
-
#--
-
# This class wraps up local storage for middlewares. Only the middleware method should
-
# construct them.
-
1
class Middleware # :nodoc:
-
1
attr_reader :name, :local_cache_key
-
-
1
def initialize(name, local_cache_key)
-
1
@name = name
-
1
@local_cache_key = local_cache_key
-
1
@app = nil
-
end
-
-
1
def new(app)
-
1
@app = app
-
1
self
-
end
-
-
1
def call(env)
-
LocalCacheRegistry.set_cache_for(local_cache_key, LocalStore.new)
-
response = @app.call(env)
-
response[2] = ::Rack::BodyProxy.new(response[2]) do
-
LocalCacheRegistry.set_cache_for(local_cache_key, nil)
-
end
-
response
-
rescue Exception
-
LocalCacheRegistry.set_cache_for(local_cache_key, nil)
-
raise
-
end
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/concern'
-
1
require 'active_support/descendants_tracker'
-
1
require 'active_support/core_ext/array/extract_options'
-
1
require 'active_support/core_ext/class/attribute'
-
1
require 'active_support/core_ext/kernel/reporting'
-
1
require 'active_support/core_ext/kernel/singleton_class'
-
1
require 'thread'
-
-
1
module ActiveSupport
-
# Callbacks are code hooks that are run at key points in an object's life cycle.
-
# The typical use case is to have a base class define a set of callbacks
-
# relevant to the other functionality it supplies, so that subclasses can
-
# install callbacks that enhance or modify the base functionality without
-
# needing to override or redefine methods of the base class.
-
#
-
# Mixing in this module allows you to define the events in the object's
-
# life cycle that will support callbacks (via +ClassMethods.define_callbacks+),
-
# set the instance methods, procs, or callback objects to be called (via
-
# +ClassMethods.set_callback+), and run the installed callbacks at the
-
# appropriate times (via +run_callbacks+).
-
#
-
# Three kinds of callbacks are supported: before callbacks, run before a
-
# certain event; after callbacks, run after the event; and around callbacks,
-
# blocks that surround the event, triggering it when they yield. Callback code
-
# can be contained in instance methods, procs or lambdas, or callback objects
-
# that respond to certain predetermined methods. See +ClassMethods.set_callback+
-
# for details.
-
#
-
# class Record
-
# include ActiveSupport::Callbacks
-
# define_callbacks :save
-
#
-
# def save
-
# run_callbacks :save do
-
# puts "- save"
-
# end
-
# end
-
# end
-
#
-
# class PersonRecord < Record
-
# set_callback :save, :before, :saving_message
-
# def saving_message
-
# puts "saving..."
-
# end
-
#
-
# set_callback :save, :after do |object|
-
# puts "saved"
-
# end
-
# end
-
#
-
# person = PersonRecord.new
-
# person.save
-
#
-
# Output:
-
# saving...
-
# - save
-
# saved
-
1
module Callbacks
-
1
extend Concern
-
-
1
included do
-
7
extend ActiveSupport::DescendantsTracker
-
end
-
-
1
CALLBACK_FILTER_TYPES = [:before, :after, :around]
-
-
# Runs the callbacks for the given event.
-
#
-
# Calls the before and around callbacks in the order they were set, yields
-
# the block (if given one), and then runs the after callbacks in reverse
-
# order.
-
#
-
# If the callback chain was halted, returns +false+. Otherwise returns the
-
# result of the block, or +true+ if no block is given.
-
#
-
# run_callbacks :save do
-
# save
-
# end
-
1
def run_callbacks(kind, &block)
-
81
cbs = send("_#{kind}_callbacks")
-
81
if cbs.empty?
-
57
yield if block_given?
-
else
-
24
runner = cbs.compile
-
24
e = Filters::Environment.new(self, false, nil, block)
-
24
runner.call(e).value
-
end
-
end
-
-
1
private
-
-
# A hook invoked every time a before callback is halted.
-
# This can be overridden in AS::Callback implementors in order
-
# to provide better debugging/logging.
-
1
def halted_callback_hook(filter)
-
end
-
-
1
module Conditionals # :nodoc:
-
1
class Value
-
1
def initialize(&block)
-
22
@block = block
-
end
-
25
def call(target, value); @block.call(value); end
-
end
-
end
-
-
1
module Filters
-
1
Environment = Struct.new(:target, :halted, :value, :run_block)
-
-
1
class End
-
1
def call(env)
-
24
block = env.run_block
-
24
env.value = !env.halted && (!block || block.call)
-
24
env
-
end
-
end
-
1
ENDING = End.new
-
-
1
class Before
-
1
def self.build(next_callback, user_callback, user_conditions, chain_config, filter)
-
17
halted_lambda = chain_config[:terminator]
-
-
17
if chain_config.key?(:terminator) && user_conditions.any?
-
1
halting_and_conditional(next_callback, user_callback, user_conditions, halted_lambda, filter)
-
16
elsif chain_config.key? :terminator
-
5
halting(next_callback, user_callback, halted_lambda, filter)
-
11
elsif user_conditions.any?
-
conditional(next_callback, user_callback, user_conditions)
-
else
-
11
simple next_callback, user_callback
-
end
-
end
-
-
1
private
-
-
1
def self.halting_and_conditional(next_callback, user_callback, user_conditions, halted_lambda, filter)
-
1
lambda { |env|
-
4
target = env.target
-
4
value = env.value
-
4
halted = env.halted
-
-
8
if !halted && user_conditions.all? { |c| c.call(target, value) }
-
result = user_callback.call target, value
-
env.halted = halted_lambda.call(target, result)
-
if env.halted
-
target.send :halted_callback_hook, filter
-
end
-
end
-
4
next_callback.call env
-
}
-
end
-
-
1
def self.halting(next_callback, user_callback, halted_lambda, filter)
-
5
lambda { |env|
-
14
target = env.target
-
14
value = env.value
-
14
halted = env.halted
-
-
14
unless halted
-
14
result = user_callback.call target, value
-
14
env.halted = halted_lambda.call(target, result)
-
14
if env.halted
-
target.send :halted_callback_hook, filter
-
end
-
end
-
14
next_callback.call env
-
}
-
end
-
-
1
def self.conditional(next_callback, user_callback, user_conditions)
-
lambda { |env|
-
target = env.target
-
value = env.value
-
-
if user_conditions.all? { |c| c.call(target, value) }
-
user_callback.call target, value
-
end
-
next_callback.call env
-
}
-
end
-
-
1
def self.simple(next_callback, user_callback)
-
11
lambda { |env|
-
36
user_callback.call env.target, env.value
-
36
next_callback.call env
-
}
-
end
-
end
-
-
1
class After
-
1
def self.build(next_callback, user_callback, user_conditions, chain_config)
-
13
if chain_config[:skip_after_callbacks_if_terminated]
-
13
if chain_config.key?(:terminator) && user_conditions.any?
-
12
halting_and_conditional(next_callback, user_callback, user_conditions)
-
1
elsif chain_config.key?(:terminator)
-
1
halting(next_callback, user_callback)
-
elsif user_conditions.any?
-
conditional next_callback, user_callback, user_conditions
-
else
-
simple next_callback, user_callback
-
end
-
else
-
if user_conditions.any?
-
conditional next_callback, user_callback, user_conditions
-
else
-
simple next_callback, user_callback
-
end
-
end
-
end
-
-
1
private
-
-
1
def self.halting_and_conditional(next_callback, user_callback, user_conditions)
-
12
lambda { |env|
-
24
env = next_callback.call env
-
24
target = env.target
-
24
value = env.value
-
24
halted = env.halted
-
-
48
if !halted && user_conditions.all? { |c| c.call(target, value) }
-
24
user_callback.call target, value
-
end
-
24
env
-
}
-
end
-
-
1
def self.halting(next_callback, user_callback)
-
1
lambda { |env|
-
4
env = next_callback.call env
-
4
unless env.halted
-
4
user_callback.call env.target, env.value
-
end
-
4
env
-
}
-
end
-
-
1
def self.conditional(next_callback, user_callback, user_conditions)
-
lambda { |env|
-
env = next_callback.call env
-
target = env.target
-
value = env.value
-
-
if user_conditions.all? { |c| c.call(target, value) }
-
user_callback.call target, value
-
end
-
env
-
}
-
end
-
-
1
def self.simple(next_callback, user_callback)
-
lambda { |env|
-
env = next_callback.call env
-
user_callback.call env.target, env.value
-
env
-
}
-
end
-
end
-
-
1
class Around
-
1
def self.build(next_callback, user_callback, user_conditions, chain_config)
-
if chain_config.key?(:terminator) && user_conditions.any?
-
halting_and_conditional(next_callback, user_callback, user_conditions)
-
elsif chain_config.key? :terminator
-
halting(next_callback, user_callback)
-
elsif user_conditions.any?
-
conditional(next_callback, user_callback, user_conditions)
-
else
-
simple(next_callback, user_callback)
-
end
-
end
-
-
1
private
-
-
1
def self.halting_and_conditional(next_callback, user_callback, user_conditions)
-
lambda { |env|
-
target = env.target
-
value = env.value
-
halted = env.halted
-
-
if !halted && user_conditions.all? { |c| c.call(target, value) }
-
user_callback.call(target, value) {
-
env = next_callback.call env
-
env.value
-
}
-
env
-
else
-
next_callback.call env
-
end
-
}
-
end
-
-
1
def self.halting(next_callback, user_callback)
-
lambda { |env|
-
target = env.target
-
value = env.value
-
-
unless env.halted
-
user_callback.call(target, value) {
-
env = next_callback.call env
-
env.value
-
}
-
env
-
else
-
next_callback.call env
-
end
-
}
-
end
-
-
1
def self.conditional(next_callback, user_callback, user_conditions)
-
lambda { |env|
-
target = env.target
-
value = env.value
-
-
if user_conditions.all? { |c| c.call(target, value) }
-
user_callback.call(target, value) {
-
env = next_callback.call env
-
env.value
-
}
-
env
-
else
-
next_callback.call env
-
end
-
}
-
end
-
-
1
def self.simple(next_callback, user_callback)
-
lambda { |env|
-
user_callback.call(env.target, env.value) {
-
env = next_callback.call env
-
env.value
-
}
-
env
-
}
-
end
-
end
-
end
-
-
1
class Callback #:nodoc:#
-
1
def self.build(chain, filter, kind, options)
-
67
new chain.name, filter, kind, options, chain.config
-
end
-
-
1
attr_accessor :kind, :name
-
1
attr_reader :chain_config
-
-
1
def initialize(name, filter, kind, options, chain_config)
-
67
@chain_config = chain_config
-
67
@name = name
-
67
@kind = kind
-
67
@filter = filter
-
67
@key = compute_identifier filter
-
67
@if = Array(options[:if])
-
67
@unless = Array(options[:unless])
-
end
-
-
149
def filter; @key; end
-
1
def raw_filter; @filter; end
-
-
1
def merge(chain, new_options)
-
options = {
-
:if => @if.dup,
-
:unless => @unless.dup
-
}
-
-
options[:if].concat Array(new_options.fetch(:unless, []))
-
options[:unless].concat Array(new_options.fetch(:if, []))
-
-
self.class.build chain, @filter, @kind, options
-
end
-
-
1
def matches?(_kind, _filter)
-
75
@kind == _kind && filter == _filter
-
end
-
-
1
def duplicates?(other)
-
84
case @filter
-
when Symbol, String
-
75
matches?(other.kind, other.filter)
-
else
-
9
false
-
end
-
end
-
-
# Wraps code with filter
-
1
def apply(next_callback)
-
30
user_conditions = conditions_lambdas
-
30
user_callback = make_lambda @filter
-
-
30
case kind
-
when :before
-
17
Filters::Before.build(next_callback, user_callback, user_conditions, chain_config, @filter)
-
when :after
-
13
Filters::After.build(next_callback, user_callback, user_conditions, chain_config)
-
when :around
-
Filters::Around.build(next_callback, user_callback, user_conditions, chain_config)
-
end
-
end
-
-
1
private
-
-
1
def invert_lambda(l)
-
lambda { |*args, &blk| !l.call(*args, &blk) }
-
end
-
-
# Filters support:
-
#
-
# Symbols:: A method to call.
-
# Strings:: Some content to evaluate.
-
# Procs:: A proc to call with the object.
-
# Objects:: An object with a <tt>before_foo</tt> method on it to call.
-
#
-
# All of these objects are compiled into methods and handled
-
# the same after this point:
-
#
-
# Symbols:: Already methods.
-
# Strings:: class_eval'd into methods.
-
# Procs:: using define_method compiled into methods.
-
# Objects::
-
# a method is created that calls the before_foo method
-
# on the object.
-
1
def make_lambda(filter)
-
43
case filter
-
when Symbol
-
93
lambda { |target, _, &blk| target.send filter, &blk }
-
when String
-
1
l = eval "lambda { |value| #{filter} }"
-
5
lambda { |target, value| target.instance_exec(value, &l) }
-
12
when Conditionals::Value then filter
-
when ::Proc
-
if filter.arity > 1
-
return lambda { |target, _, &block|
-
raise ArgumentError unless block
-
target.instance_exec(target, block, &filter)
-
}
-
end
-
-
if filter.arity <= 0
-
lambda { |target, _| target.instance_exec(&filter) }
-
else
-
lambda { |target, _| target.instance_exec(target, &filter) }
-
end
-
else
-
3
scopes = Array(chain_config[:scope])
-
6
method_to_call = scopes.map{ |s| public_send(s) }.join("_")
-
-
3
lambda { |target, _, &blk|
-
12
filter.public_send method_to_call, target, &blk
-
}
-
end
-
end
-
-
1
def compute_identifier(filter)
-
67
case filter
-
when String, ::Proc
-
4
filter.object_id
-
else
-
63
filter
-
end
-
end
-
-
1
def conditions_lambdas
-
13
@if.map { |c| make_lambda c } +
-
30
@unless.map { |c| invert_lambda make_lambda c }
-
end
-
end
-
-
# An Array with a compile method.
-
1
class CallbackChain #:nodoc:#
-
1
include Enumerable
-
-
1
attr_reader :name, :config
-
-
1
def initialize(name, config)
-
20
@name = name
-
20
@config = {
-
:scope => [ :kind ]
-
}.merge!(config)
-
20
@chain = []
-
20
@callbacks = nil
-
20
@mutex = Mutex.new
-
end
-
-
1
def each(&block); @chain.each(&block); end
-
1
def index(o); @chain.index(o); end
-
132
def empty?; @chain.empty?; end
-
-
1
def insert(index, o)
-
@callbacks = nil
-
@chain.insert(index, o)
-
end
-
-
1
def delete(o)
-
@callbacks = nil
-
@chain.delete(o)
-
end
-
-
1
def clear
-
@callbacks = nil
-
@chain.clear
-
self
-
end
-
-
1
def initialize_copy(other)
-
67
@callbacks = nil
-
67
@chain = other.chain.dup
-
67
@mutex = Mutex.new
-
end
-
-
1
def compile
-
@callbacks || @mutex.synchronize do
-
@callbacks ||= @chain.reverse.inject(Filters::ENDING) do |chain, callback|
-
30
callback.apply chain
-
9
end
-
24
end
-
end
-
-
1
def append(*callbacks)
-
86
callbacks.each { |c| append_one(c) }
-
end
-
-
1
def prepend(*callbacks)
-
48
callbacks.each { |c| prepend_one(c) }
-
end
-
-
1
protected
-
68
def chain; @chain; end
-
-
1
private
-
-
1
def append_one(callback)
-
43
@callbacks = nil
-
43
remove_duplicates(callback)
-
43
@chain.push(callback)
-
end
-
-
1
def prepend_one(callback)
-
24
@callbacks = nil
-
24
remove_duplicates(callback)
-
24
@chain.unshift(callback)
-
end
-
-
1
def remove_duplicates(callback)
-
67
@callbacks = nil
-
151
@chain.delete_if { |c| callback.duplicates?(c) }
-
end
-
end
-
-
1
module ClassMethods
-
1
def normalize_callback_params(filters, block) # :nodoc:
-
67
type = CALLBACK_FILTER_TYPES.include?(filters.first) ? filters.shift : :before
-
67
options = filters.extract_options!
-
67
filters.unshift(block) if block
-
67
[type, filters, options.dup]
-
end
-
-
# This is used internally to append, prepend and skip callbacks to the
-
# CallbackChain.
-
1
def __update_callbacks(name) #:nodoc:
-
67
([self] + ActiveSupport::DescendantsTracker.descendants(self)).reverse.each do |target|
-
67
chain = target.get_callbacks name
-
67
yield target, chain.dup
-
end
-
end
-
-
# Install a callback for the given event.
-
#
-
# set_callback :save, :before, :before_meth
-
# set_callback :save, :after, :after_meth, if: :condition
-
# set_callback :save, :around, ->(r, &block) { stuff; result = block.call; stuff }
-
#
-
# The second arguments indicates whether the callback is to be run +:before+,
-
# +:after+, or +:around+ the event. If omitted, +:before+ is assumed. This
-
# means the first example above can also be written as:
-
#
-
# set_callback :save, :before_meth
-
#
-
# The callback can be specified as a symbol naming an instance method; as a
-
# proc, lambda, or block; as a string to be instance evaluated; or as an
-
# object that responds to a certain method determined by the <tt>:scope</tt>
-
# argument to +define_callbacks+.
-
#
-
# If a proc, lambda, or block is given, its body is evaluated in the context
-
# of the current object. It can also optionally accept the current object as
-
# an argument.
-
#
-
# Before and around callbacks are called in the order that they are set;
-
# after callbacks are called in the reverse order.
-
#
-
# Around callbacks can access the return value from the event, if it
-
# wasn't halted, from the +yield+ call.
-
#
-
# ===== Options
-
#
-
# * <tt>:if</tt> - A symbol naming an instance method or a proc; the
-
# callback will be called only when it returns a +true+ value.
-
# * <tt>:unless</tt> - A symbol naming an instance method or a proc; the
-
# callback will be called only when it returns a +false+ value.
-
# * <tt>:prepend</tt> - If +true+, the callback will be prepended to the
-
# existing chain rather than appended.
-
1
def set_callback(name, *filter_list, &block)
-
67
type, filters, options = normalize_callback_params(filter_list, block)
-
67
self_chain = get_callbacks name
-
67
mapped = filters.map do |filter|
-
67
Callback.build(self_chain, filter, type, options)
-
end
-
-
67
__update_callbacks(name) do |target, chain|
-
67
options[:prepend] ? chain.prepend(*mapped) : chain.append(*mapped)
-
67
target.set_callbacks name, chain
-
end
-
end
-
-
# Skip a previously set callback. Like +set_callback+, <tt>:if</tt> or
-
# <tt>:unless</tt> options may be passed in order to control when the
-
# callback is skipped.
-
#
-
# class Writer < Person
-
# skip_callback :validate, :before, :check_membership, if: -> { self.age > 18 }
-
# end
-
1
def skip_callback(name, *filter_list, &block)
-
type, filters, options = normalize_callback_params(filter_list, block)
-
-
__update_callbacks(name) do |target, chain|
-
filters.each do |filter|
-
filter = chain.find {|c| c.matches?(type, filter) }
-
-
if filter && options.any?
-
new_filter = filter.merge(chain, options)
-
chain.insert(chain.index(filter), new_filter)
-
end
-
-
chain.delete(filter)
-
end
-
target.set_callbacks name, chain
-
end
-
end
-
-
# Remove all set callbacks for the given event.
-
1
def reset_callbacks(name)
-
callbacks = get_callbacks name
-
-
ActiveSupport::DescendantsTracker.descendants(self).each do |target|
-
chain = target.get_callbacks(name).dup
-
callbacks.each { |c| chain.delete(c) }
-
target.set_callbacks name, chain
-
end
-
-
self.set_callbacks name, callbacks.dup.clear
-
end
-
-
# Define sets of events in the object life cycle that support callbacks.
-
#
-
# define_callbacks :validate
-
# define_callbacks :initialize, :save, :destroy
-
#
-
# ===== Options
-
#
-
# * <tt>:terminator</tt> - Determines when a before filter will halt the
-
# callback chain, preventing following callbacks from being called and
-
# the event from being triggered. This should be a lambda to be executed.
-
# The current object and the return result of the callback will be called
-
# with the lambda.
-
#
-
# define_callbacks :validate, terminator: ->(target, result) { result == false }
-
#
-
# In this example, if any before validate callbacks returns +false+,
-
# other callbacks are not executed. Defaults to +false+, meaning no value
-
# halts the chain.
-
#
-
# * <tt>:skip_after_callbacks_if_terminated</tt> - Determines if after
-
# callbacks should be terminated by the <tt>:terminator</tt> option. By
-
# default after callbacks executed no matter if callback chain was
-
# terminated or not. Option makes sense only when <tt>:terminator</tt>
-
# option is specified.
-
#
-
# * <tt>:scope</tt> - Indicates which methods should be executed when an
-
# object is used as a callback.
-
#
-
# class Audit
-
# def before(caller)
-
# puts 'Audit: before is called'
-
# end
-
#
-
# def before_save(caller)
-
# puts 'Audit: before_save is called'
-
# end
-
# end
-
#
-
# class Account
-
# include ActiveSupport::Callbacks
-
#
-
# define_callbacks :save
-
# set_callback :save, :before, Audit.new
-
#
-
# def save
-
# run_callbacks :save do
-
# puts 'save in main'
-
# end
-
# end
-
# end
-
#
-
# In the above case whenever you save an account the method
-
# <tt>Audit#before</tt> will be called. On the other hand
-
#
-
# define_callbacks :save, scope: [:kind, :name]
-
#
-
# would trigger <tt>Audit#before_save</tt> instead. That's constructed
-
# by calling <tt>#{kind}_#{name}</tt> on the given instance. In this
-
# case "kind" is "before" and "name" is "save". In this context +:kind+
-
# and +:name+ have special meanings: +:kind+ refers to the kind of
-
# callback (before/after/around) and +:name+ refers to the method on
-
# which callbacks are being defined.
-
#
-
# A declaration like
-
#
-
# define_callbacks :save, scope: [:name]
-
#
-
# would call <tt>Audit#save</tt>.
-
1
def define_callbacks(*names)
-
17
options = names.extract_options!
-
17
if options.key?(:terminator) && String === options[:terminator]
-
ActiveSupport::Deprecation.warn "String based terminators are deprecated, please use a lambda"
-
value = options[:terminator]
-
line = class_eval "lambda { |result| #{value} }", __FILE__, __LINE__
-
options[:terminator] = lambda { |target, result| target.instance_exec(result, &line) }
-
end
-
-
17
names.each do |name|
-
20
class_attribute "_#{name}_callbacks"
-
20
set_callbacks name, CallbackChain.new(name, options)
-
end
-
end
-
-
1
protected
-
-
1
def get_callbacks(name)
-
134
send "_#{name}_callbacks"
-
end
-
-
1
def set_callbacks(name, callbacks)
-
87
send "_#{name}_callbacks=", callbacks
-
end
-
end
-
end
-
end
-
1
module ActiveSupport
-
# A typical module looks like this:
-
#
-
# module M
-
# def self.included(base)
-
# base.extend ClassMethods
-
# base.class_eval do
-
# scope :disabled, -> { where(disabled: true) }
-
# end
-
# end
-
#
-
# module ClassMethods
-
# ...
-
# end
-
# end
-
#
-
# By using <tt>ActiveSupport::Concern</tt> the above module could instead be
-
# written as:
-
#
-
# require 'active_support/concern'
-
#
-
# module M
-
# extend ActiveSupport::Concern
-
#
-
# included do
-
# scope :disabled, -> { where(disabled: true) }
-
# end
-
#
-
# module ClassMethods
-
# ...
-
# end
-
# end
-
#
-
# Moreover, it gracefully handles module dependencies. Given a +Foo+ module
-
# and a +Bar+ module which depends on the former, we would typically write the
-
# following:
-
#
-
# module Foo
-
# def self.included(base)
-
# base.class_eval do
-
# def self.method_injected_by_foo
-
# ...
-
# end
-
# end
-
# end
-
# end
-
#
-
# module Bar
-
# def self.included(base)
-
# base.method_injected_by_foo
-
# end
-
# end
-
#
-
# class Host
-
# include Foo # We need to include this dependency for Bar
-
# include Bar # Bar is the module that Host really needs
-
# end
-
#
-
# But why should +Host+ care about +Bar+'s dependencies, namely +Foo+? We
-
# could try to hide these from +Host+ directly including +Foo+ in +Bar+:
-
#
-
# module Bar
-
# include Foo
-
# def self.included(base)
-
# base.method_injected_by_foo
-
# end
-
# end
-
#
-
# class Host
-
# include Bar
-
# end
-
#
-
# Unfortunately this won't work, since when +Foo+ is included, its <tt>base</tt>
-
# is the +Bar+ module, not the +Host+ class. With <tt>ActiveSupport::Concern</tt>,
-
# module dependencies are properly resolved:
-
#
-
# require 'active_support/concern'
-
#
-
# module Foo
-
# extend ActiveSupport::Concern
-
# included do
-
# def self.method_injected_by_foo
-
# ...
-
# end
-
# end
-
# end
-
#
-
# module Bar
-
# extend ActiveSupport::Concern
-
# include Foo
-
#
-
# included do
-
# self.method_injected_by_foo
-
# end
-
# end
-
#
-
# class Host
-
# include Bar # works, Bar takes care now of its dependencies
-
# end
-
1
module Concern
-
1
class MultipleIncludedBlocks < StandardError #:nodoc:
-
1
def initialize
-
super "Cannot define multiple 'included' blocks for a Concern"
-
end
-
end
-
-
1
def self.extended(base) #:nodoc:
-
119
base.instance_variable_set(:@_dependencies, [])
-
end
-
-
1
def append_features(base)
-
299
if base.instance_variable_defined?(:@_dependencies)
-
76
base.instance_variable_get(:@_dependencies) << self
-
76
return false
-
else
-
223
return false if base < self
-
269
@_dependencies.each { |dep| base.send(:include, dep) }
-
177
super
-
177
base.extend const_get(:ClassMethods) if const_defined?(:ClassMethods)
-
177
base.class_eval(&@_included_block) if instance_variable_defined?(:@_included_block)
-
end
-
end
-
-
1
def included(base = nil, &block)
-
372
if base.nil?
-
73
raise MultipleIncludedBlocks if instance_variable_defined?(:@_included_block)
-
-
73
@_included_block = block
-
else
-
299
super
-
end
-
end
-
end
-
end
-
1
require 'active_support/concern'
-
1
require 'active_support/ordered_options'
-
1
require 'active_support/core_ext/array/extract_options'
-
-
1
module ActiveSupport
-
# Configurable provides a <tt>config</tt> method to store and retrieve
-
# configuration options as an <tt>OrderedHash</tt>.
-
1
module Configurable
-
1
extend ActiveSupport::Concern
-
-
1
class Configuration < ActiveSupport::InheritableOptions
-
1
def compile_methods!
-
2
self.class.compile_methods!(keys)
-
end
-
-
# Compiles reader methods so we don't have to go through method_missing.
-
1
def self.compile_methods!(keys)
-
20
keys.reject { |m| method_defined?(m) }.each do |key|
-
12
class_eval <<-RUBY, __FILE__, __LINE__ + 1
-
def #{key}; _get(#{key.inspect}); end
-
RUBY
-
end
-
end
-
end
-
-
1
module ClassMethods
-
1
def config
-
@_config ||= if respond_to?(:superclass) && superclass.respond_to?(:config)
-
5
superclass.config.inheritable_copy
-
else
-
# create a new "anonymous" class that will host the compiled reader methods
-
1
Class.new(Configuration).new
-
104
end
-
end
-
-
1
def configure
-
yield config
-
end
-
-
# Allows you to add shortcut so that you don't have to refer to attribute
-
# through config. Also look at the example for config to contrast.
-
#
-
# Defines both class and instance config accessors.
-
#
-
# class User
-
# include ActiveSupport::Configurable
-
# config_accessor :allowed_access
-
# end
-
#
-
# User.allowed_access # => nil
-
# User.allowed_access = false
-
# User.allowed_access # => false
-
#
-
# user = User.new
-
# user.allowed_access # => false
-
# user.allowed_access = true
-
# user.allowed_access # => true
-
#
-
# User.allowed_access # => false
-
#
-
# The attribute name must be a valid method name in Ruby.
-
#
-
# class User
-
# include ActiveSupport::Configurable
-
# config_accessor :"1_Badname"
-
# end
-
# # => NameError: invalid config attribute name
-
#
-
# To opt out of the instance writer method, pass <tt>instance_writer: false</tt>.
-
# To opt out of the instance reader method, pass <tt>instance_reader: false</tt>.
-
#
-
# class User
-
# include ActiveSupport::Configurable
-
# config_accessor :allowed_access, instance_reader: false, instance_writer: false
-
# end
-
#
-
# User.allowed_access = false
-
# User.allowed_access # => false
-
#
-
# User.new.allowed_access = true # => NoMethodError
-
# User.new.allowed_access # => NoMethodError
-
#
-
# Or pass <tt>instance_accessor: false</tt>, to opt out both instance methods.
-
#
-
# class User
-
# include ActiveSupport::Configurable
-
# config_accessor :allowed_access, instance_accessor: false
-
# end
-
#
-
# User.allowed_access = false
-
# User.allowed_access # => false
-
#
-
# User.new.allowed_access = true # => NoMethodError
-
# User.new.allowed_access # => NoMethodError
-
#
-
# Also you can pass a block to set up the attribute with a default value.
-
#
-
# class User
-
# include ActiveSupport::Configurable
-
# config_accessor :hair_colors do
-
# [:brown, :black, :blonde, :red]
-
# end
-
# end
-
#
-
# User.hair_colors # => [:brown, :black, :blonde, :red]
-
1
def config_accessor(*names)
-
9
options = names.extract_options!
-
-
9
names.each do |name|
-
19
raise NameError.new('invalid config attribute name') unless name =~ /\A[_A-Za-z]\w*\z/
-
-
19
reader, reader_line = "def #{name}; config.#{name}; end", __LINE__
-
19
writer, writer_line = "def #{name}=(value); config.#{name} = value; end", __LINE__
-
-
19
singleton_class.class_eval reader, __FILE__, reader_line
-
19
singleton_class.class_eval writer, __FILE__, writer_line
-
-
19
unless options[:instance_accessor] == false
-
19
class_eval reader, __FILE__, reader_line unless options[:instance_reader] == false
-
19
class_eval writer, __FILE__, writer_line unless options[:instance_writer] == false
-
end
-
19
send("#{name}=", yield) if block_given?
-
end
-
end
-
end
-
-
# Reads and writes attributes from a configuration <tt>OrderedHash</tt>.
-
#
-
# require 'active_support/configurable'
-
#
-
# class User
-
# include ActiveSupport::Configurable
-
# end
-
#
-
# user = User.new
-
#
-
# user.config.allowed_access = true
-
# user.config.level = 1
-
#
-
# user.config.allowed_access # => true
-
# user.config.level # => 1
-
1
def config
-
10
@_config ||= self.class.config.inheritable_copy
-
end
-
end
-
end
-
-
1
Dir["#{File.dirname(__FILE__)}/core_ext/*.rb"].each do |path|
-
24
require path
-
end
-
1
require 'active_support/core_ext/array/wrap'
-
1
require 'active_support/core_ext/array/access'
-
1
require 'active_support/core_ext/array/conversions'
-
1
require 'active_support/core_ext/array/extract_options'
-
1
require 'active_support/core_ext/array/grouping'
-
1
require 'active_support/core_ext/array/prepend_and_append'
-
1
class Array
-
# Returns the tail of the array from +position+.
-
#
-
# %w( a b c d ).from(0) # => ["a", "b", "c", "d"]
-
# %w( a b c d ).from(2) # => ["c", "d"]
-
# %w( a b c d ).from(10) # => []
-
# %w().from(0) # => []
-
1
def from(position)
-
self[position, length] || []
-
end
-
-
# Returns the beginning of the array up to +position+.
-
#
-
# %w( a b c d ).to(0) # => ["a"]
-
# %w( a b c d ).to(2) # => ["a", "b", "c"]
-
# %w( a b c d ).to(10) # => ["a", "b", "c", "d"]
-
# %w().to(0) # => []
-
1
def to(position)
-
first position + 1
-
end
-
-
# Equal to <tt>self[1]</tt>.
-
#
-
# %w( a b c d e ).second # => "b"
-
1
def second
-
self[1]
-
end
-
-
# Equal to <tt>self[2]</tt>.
-
#
-
# %w( a b c d e ).third # => "c"
-
1
def third
-
self[2]
-
end
-
-
# Equal to <tt>self[3]</tt>.
-
#
-
# %w( a b c d e ).fourth # => "d"
-
1
def fourth
-
self[3]
-
end
-
-
# Equal to <tt>self[4]</tt>.
-
#
-
# %w( a b c d e ).fifth # => "e"
-
1
def fifth
-
self[4]
-
end
-
-
# Equal to <tt>self[41]</tt>. Also known as accessing "the reddit".
-
#
-
# (1..42).to_a.forty_two # => 42
-
1
def forty_two
-
self[41]
-
end
-
end
-
1
require 'active_support/xml_mini'
-
1
require 'active_support/core_ext/hash/keys'
-
1
require 'active_support/core_ext/string/inflections'
-
1
require 'active_support/core_ext/object/to_param'
-
1
require 'active_support/core_ext/object/to_query'
-
-
1
class Array
-
# Converts the array to a comma-separated sentence where the last element is
-
# joined by the connector word.
-
#
-
# You can pass the following options to change the default behavior. If you
-
# pass an option key that doesn't exist in the list below, it will raise an
-
# <tt>ArgumentError</tt>.
-
#
-
# ==== Options
-
#
-
# * <tt>:words_connector</tt> - The sign or word used to join the elements
-
# in arrays with two or more elements (default: ", ").
-
# * <tt>:two_words_connector</tt> - The sign or word used to join the elements
-
# in arrays with two elements (default: " and ").
-
# * <tt>:last_word_connector</tt> - The sign or word used to join the last element
-
# in arrays with three or more elements (default: ", and ").
-
# * <tt>:locale</tt> - If +i18n+ is available, you can set a locale and use
-
# the connector options defined on the 'support.array' namespace in the
-
# corresponding dictionary file.
-
#
-
# ==== Examples
-
#
-
# [].to_sentence # => ""
-
# ['one'].to_sentence # => "one"
-
# ['one', 'two'].to_sentence # => "one and two"
-
# ['one', 'two', 'three'].to_sentence # => "one, two, and three"
-
#
-
# ['one', 'two'].to_sentence(passing: 'invalid option')
-
# # => ArgumentError: Unknown key :passing
-
#
-
# ['one', 'two'].to_sentence(two_words_connector: '-')
-
# # => "one-two"
-
#
-
# ['one', 'two', 'three'].to_sentence(words_connector: ' or ', last_word_connector: ' or at least ')
-
# # => "one or two or at least three"
-
#
-
# Using <tt>:locale</tt> option:
-
#
-
# # Given this locale dictionary:
-
# #
-
# # es:
-
# # support:
-
# # array:
-
# # words_connector: " o "
-
# # two_words_connector: " y "
-
# # last_word_connector: " o al menos "
-
#
-
# ['uno', 'dos'].to_sentence(locale: :es)
-
# # => "uno y dos"
-
#
-
# ['uno', 'dos', 'tres'].to_sentence(locale: :es)
-
# # => "uno o dos o al menos tres"
-
1
def to_sentence(options = {})
-
options.assert_valid_keys(:words_connector, :two_words_connector, :last_word_connector, :locale)
-
-
default_connectors = {
-
:words_connector => ', ',
-
:two_words_connector => ' and ',
-
:last_word_connector => ', and '
-
}
-
if defined?(I18n)
-
i18n_connectors = I18n.translate(:'support.array', locale: options[:locale], default: {})
-
default_connectors.merge!(i18n_connectors)
-
end
-
options = default_connectors.merge!(options)
-
-
case length
-
when 0
-
''
-
when 1
-
self[0].to_s.dup
-
when 2
-
"#{self[0]}#{options[:two_words_connector]}#{self[1]}"
-
else
-
"#{self[0...-1].join(options[:words_connector])}#{options[:last_word_connector]}#{self[-1]}"
-
end
-
end
-
-
# Extends <tt>Array#to_s</tt> to convert a collection of elements into a
-
# comma separated id list if <tt>:db</tt> argument is given as the format.
-
#
-
# Blog.all.to_formatted_s(:db) # => "1,2,3"
-
1
def to_formatted_s(format = :default)
-
1
case format
-
when :db
-
if empty?
-
'null'
-
else
-
collect { |element| element.id }.join(',')
-
end
-
else
-
1
to_default_s
-
end
-
end
-
1
alias_method :to_default_s, :to_s
-
1
alias_method :to_s, :to_formatted_s
-
-
# Returns a string that represents the array in XML by invoking +to_xml+
-
# on each element. Active Record collections delegate their representation
-
# in XML to this method.
-
#
-
# All elements are expected to respond to +to_xml+, if any of them does
-
# not then an exception is raised.
-
#
-
# The root node reflects the class name of the first element in plural
-
# if all elements belong to the same type and that's not Hash:
-
#
-
# customer.projects.to_xml
-
#
-
# <?xml version="1.0" encoding="UTF-8"?>
-
# <projects type="array">
-
# <project>
-
# <amount type="decimal">20000.0</amount>
-
# <customer-id type="integer">1567</customer-id>
-
# <deal-date type="date">2008-04-09</deal-date>
-
# ...
-
# </project>
-
# <project>
-
# <amount type="decimal">57230.0</amount>
-
# <customer-id type="integer">1567</customer-id>
-
# <deal-date type="date">2008-04-15</deal-date>
-
# ...
-
# </project>
-
# </projects>
-
#
-
# Otherwise the root element is "objects":
-
#
-
# [{ foo: 1, bar: 2}, { baz: 3}].to_xml
-
#
-
# <?xml version="1.0" encoding="UTF-8"?>
-
# <objects type="array">
-
# <object>
-
# <bar type="integer">2</bar>
-
# <foo type="integer">1</foo>
-
# </object>
-
# <object>
-
# <baz type="integer">3</baz>
-
# </object>
-
# </objects>
-
#
-
# If the collection is empty the root element is "nil-classes" by default:
-
#
-
# [].to_xml
-
#
-
# <?xml version="1.0" encoding="UTF-8"?>
-
# <nil-classes type="array"/>
-
#
-
# To ensure a meaningful root element use the <tt>:root</tt> option:
-
#
-
# customer_with_no_projects.projects.to_xml(root: 'projects')
-
#
-
# <?xml version="1.0" encoding="UTF-8"?>
-
# <projects type="array"/>
-
#
-
# By default name of the node for the children of root is <tt>root.singularize</tt>.
-
# You can change it with the <tt>:children</tt> option.
-
#
-
# The +options+ hash is passed downwards:
-
#
-
# Message.all.to_xml(skip_types: true)
-
#
-
# <?xml version="1.0" encoding="UTF-8"?>
-
# <messages>
-
# <message>
-
# <created-at>2008-03-07T09:58:18+01:00</created-at>
-
# <id>1</id>
-
# <name>1</name>
-
# <updated-at>2008-03-07T09:58:18+01:00</updated-at>
-
# <user-id>1</user-id>
-
# </message>
-
# </messages>
-
#
-
1
def to_xml(options = {})
-
require 'active_support/builder' unless defined?(Builder)
-
-
options = options.dup
-
options[:indent] ||= 2
-
options[:builder] ||= Builder::XmlMarkup.new(indent: options[:indent])
-
options[:root] ||= \
-
if first.class != Hash && all? { |e| e.is_a?(first.class) }
-
underscored = ActiveSupport::Inflector.underscore(first.class.name)
-
ActiveSupport::Inflector.pluralize(underscored).tr('/', '_')
-
else
-
'objects'
-
end
-
-
builder = options[:builder]
-
builder.instruct! unless options.delete(:skip_instruct)
-
-
root = ActiveSupport::XmlMini.rename_key(options[:root].to_s, options)
-
children = options.delete(:children) || root.singularize
-
attributes = options[:skip_types] ? {} : { type: 'array' }
-
-
if empty?
-
builder.tag!(root, attributes)
-
else
-
builder.tag!(root, attributes) do
-
each { |value| ActiveSupport::XmlMini.to_tag(children, value, options) }
-
yield builder if block_given?
-
end
-
end
-
end
-
end
-
1
class Hash
-
# By default, only instances of Hash itself are extractable.
-
# Subclasses of Hash may implement this method and return
-
# true to declare themselves as extractable. If a Hash
-
# is extractable, Array#extract_options! pops it from
-
# the Array when it is the last element of the Array.
-
1
def extractable_options?
-
136
instance_of?(Hash)
-
end
-
end
-
-
1
class Array
-
# Extracts options from a set of arguments. Removes and returns the last
-
# element in the array if it's a hash, otherwise returns a blank hash.
-
#
-
# def options(*args)
-
# args.extract_options!
-
# end
-
#
-
# options(1, 2) # => {}
-
# options(1, 2, a: :b) # => {:a=>:b}
-
1
def extract_options!
-
506
if last.is_a?(Hash) && last.extractable_options?
-
136
pop
-
else
-
370
{}
-
end
-
end
-
end
-
1
class Array
-
# Splits or iterates over the array in groups of size +number+,
-
# padding any remaining slots with +fill_with+ unless it is +false+.
-
#
-
# %w(1 2 3 4 5 6 7 8 9 10).in_groups_of(3) {|group| p group}
-
# ["1", "2", "3"]
-
# ["4", "5", "6"]
-
# ["7", "8", "9"]
-
# ["10", nil, nil]
-
#
-
# %w(1 2 3 4 5).in_groups_of(2, ' ') {|group| p group}
-
# ["1", "2"]
-
# ["3", "4"]
-
# ["5", " "]
-
#
-
# %w(1 2 3 4 5).in_groups_of(2, false) {|group| p group}
-
# ["1", "2"]
-
# ["3", "4"]
-
# ["5"]
-
1
def in_groups_of(number, fill_with = nil)
-
if fill_with == false
-
collection = self
-
else
-
# size % number gives how many extra we have;
-
# subtracting from number gives how many to add;
-
# modulo number ensures we don't add group of just fill.
-
padding = (number - size % number) % number
-
collection = dup.concat(Array.new(padding, fill_with))
-
end
-
-
if block_given?
-
collection.each_slice(number) { |slice| yield(slice) }
-
else
-
collection.each_slice(number).to_a
-
end
-
end
-
-
# Splits or iterates over the array in +number+ of groups, padding any
-
# remaining slots with +fill_with+ unless it is +false+.
-
#
-
# %w(1 2 3 4 5 6 7 8 9 10).in_groups(3) {|group| p group}
-
# ["1", "2", "3", "4"]
-
# ["5", "6", "7", nil]
-
# ["8", "9", "10", nil]
-
#
-
# %w(1 2 3 4 5 6 7 8 9 10).in_groups(3, ' ') {|group| p group}
-
# ["1", "2", "3", "4"]
-
# ["5", "6", "7", " "]
-
# ["8", "9", "10", " "]
-
#
-
# %w(1 2 3 4 5 6 7).in_groups(3, false) {|group| p group}
-
# ["1", "2", "3"]
-
# ["4", "5"]
-
# ["6", "7"]
-
1
def in_groups(number, fill_with = nil)
-
# size.div number gives minor group size;
-
# size % number gives how many objects need extra accommodation;
-
# each group hold either division or division + 1 items.
-
division = size.div number
-
modulo = size % number
-
-
# create a new array avoiding dup
-
groups = []
-
start = 0
-
-
number.times do |index|
-
length = division + (modulo > 0 && modulo > index ? 1 : 0)
-
groups << last_group = slice(start, length)
-
last_group << fill_with if fill_with != false &&
-
modulo > 0 && length == division
-
start += length
-
end
-
-
if block_given?
-
groups.each { |g| yield(g) }
-
else
-
groups
-
end
-
end
-
-
# Divides the array into one or more subarrays based on a delimiting +value+
-
# or the result of an optional block.
-
#
-
# [1, 2, 3, 4, 5].split(3) # => [[1, 2], [4, 5]]
-
# (1..10).to_a.split { |i| i % 3 == 0 } # => [[1, 2], [4, 5], [7, 8], [10]]
-
1
def split(value = nil)
-
if block_given?
-
inject([[]]) do |results, element|
-
if yield(element)
-
results << []
-
else
-
results.last << element
-
end
-
-
results
-
end
-
else
-
results, arr = [[]], self.dup
-
until arr.empty?
-
if (idx = arr.index(value))
-
results.last.concat(arr.shift(idx))
-
arr.shift
-
results << []
-
else
-
results.last.concat(arr.shift(arr.size))
-
end
-
end
-
results
-
end
-
end
-
end
-
1
class Array
-
# The human way of thinking about adding stuff to the end of a list is with append.
-
1
alias_method :append, :<<
-
-
# The human way of thinking about adding stuff to the beginning of a list is with prepend.
-
1
alias_method :prepend, :unshift
-
end
-
1
class Array
-
# Wraps its argument in an array unless it is already an array (or array-like).
-
#
-
# Specifically:
-
#
-
# * If the argument is +nil+ an empty list is returned.
-
# * Otherwise, if the argument responds to +to_ary+ it is invoked, and its result returned.
-
# * Otherwise, returns an array with the argument as its single element.
-
#
-
# Array.wrap(nil) # => []
-
# Array.wrap([1, 2, 3]) # => [1, 2, 3]
-
# Array.wrap(0) # => [0]
-
#
-
# This method is similar in purpose to <tt>Kernel#Array</tt>, but there are some differences:
-
#
-
# * If the argument responds to +to_ary+ the method is invoked. <tt>Kernel#Array</tt>
-
# moves on to try +to_a+ if the returned value is +nil+, but <tt>Array.wrap</tt> returns
-
# +nil+ right away.
-
# * If the returned value from +to_ary+ is neither +nil+ nor an +Array+ object, <tt>Kernel#Array</tt>
-
# raises an exception, while <tt>Array.wrap</tt> does not, it just returns the value.
-
# * It does not call +to_a+ on the argument, but returns an empty array if argument is +nil+.
-
#
-
# The second point is easily explained with some enumerables:
-
#
-
# Array(foo: :bar) # => [[:foo, :bar]]
-
# Array.wrap(foo: :bar) # => [{:foo=>:bar}]
-
#
-
# There's also a related idiom that uses the splat operator:
-
#
-
# [*object]
-
#
-
# which returns <tt>[]</tt> for +nil+, but calls to <tt>Array(object)</tt> otherwise.
-
#
-
# The differences with <tt>Kernel#Array</tt> explained above
-
# apply to the rest of <tt>object</tt>s.
-
1
def self.wrap(object)
-
8
if object.nil?
-
[]
-
8
elsif object.respond_to?(:to_ary)
-
2
object.to_ary || [object]
-
else
-
6
[object]
-
end
-
end
-
end
-
1
require 'benchmark'
-
-
1
class << Benchmark
-
# Benchmark realtime in milliseconds.
-
#
-
# Benchmark.realtime { User.all }
-
# # => 8.0e-05
-
#
-
# Benchmark.ms { User.all }
-
# # => 0.074
-
1
def ms
-
4
1000 * realtime { yield }
-
end
-
end
-
1
require 'active_support/core_ext/big_decimal/conversions'
-
1
require 'bigdecimal'
-
1
require 'bigdecimal/util'
-
-
1
class BigDecimal
-
1
DEFAULT_STRING_FORMAT = 'F'
-
1
def to_formatted_s(*args)
-
if args[0].is_a?(Symbol)
-
super
-
else
-
format = args[0] || DEFAULT_STRING_FORMAT
-
_original_to_s(format)
-
end
-
end
-
1
alias_method :_original_to_s, :to_s
-
1
alias_method :to_s, :to_formatted_s
-
end
-
1
require 'active_support/core_ext/class/attribute'
-
1
require 'active_support/core_ext/class/delegating_attributes'
-
1
require 'active_support/core_ext/class/subclasses'
-
1
require 'active_support/core_ext/kernel/singleton_class'
-
1
require 'active_support/core_ext/module/remove_method'
-
1
require 'active_support/core_ext/array/extract_options'
-
-
1
class Class
-
# Declare a class-level attribute whose value is inheritable by subclasses.
-
# Subclasses can change their own value and it will not impact parent class.
-
#
-
# class Base
-
# class_attribute :setting
-
# end
-
#
-
# class Subclass < Base
-
# end
-
#
-
# Base.setting = true
-
# Subclass.setting # => true
-
# Subclass.setting = false
-
# Subclass.setting # => false
-
# Base.setting # => true
-
#
-
# In the above case as long as Subclass does not assign a value to setting
-
# by performing <tt>Subclass.setting = _something_ </tt>, <tt>Subclass.setting</tt>
-
# would read value assigned to parent class. Once Subclass assigns a value then
-
# the value assigned by Subclass would be returned.
-
#
-
# This matches normal Ruby method inheritance: think of writing an attribute
-
# on a subclass as overriding the reader method. However, you need to be aware
-
# when using +class_attribute+ with mutable structures as +Array+ or +Hash+.
-
# In such cases, you don't want to do changes in places but use setters:
-
#
-
# Base.setting = []
-
# Base.setting # => []
-
# Subclass.setting # => []
-
#
-
# # Appending in child changes both parent and child because it is the same object:
-
# Subclass.setting << :foo
-
# Base.setting # => [:foo]
-
# Subclass.setting # => [:foo]
-
#
-
# # Use setters to not propagate changes:
-
# Base.setting = []
-
# Subclass.setting += [:foo]
-
# Base.setting # => []
-
# Subclass.setting # => [:foo]
-
#
-
# For convenience, an instance predicate method is defined as well.
-
# To skip it, pass <tt>instance_predicate: false</tt>.
-
#
-
# Subclass.setting? # => false
-
#
-
# Instances may overwrite the class value in the same way:
-
#
-
# Base.setting = true
-
# object = Base.new
-
# object.setting # => true
-
# object.setting = false
-
# object.setting # => false
-
# Base.setting # => true
-
#
-
# To opt out of the instance reader method, pass <tt>instance_reader: false</tt>.
-
#
-
# object.setting # => NoMethodError
-
# object.setting? # => NoMethodError
-
#
-
# To opt out of the instance writer method, pass <tt>instance_writer: false</tt>.
-
#
-
# object.setting = false # => NoMethodError
-
#
-
# To opt out of both instance methods, pass <tt>instance_accessor: false</tt>.
-
1
def class_attribute(*attrs)
-
151
options = attrs.extract_options!
-
151
instance_reader = options.fetch(:instance_accessor, true) && options.fetch(:instance_reader, true)
-
151
instance_writer = options.fetch(:instance_accessor, true) && options.fetch(:instance_writer, true)
-
151
instance_predicate = options.fetch(:instance_predicate, true)
-
-
151
attrs.each do |name|
-
166
define_singleton_method(name) { nil }
-
164
define_singleton_method("#{name}?") { !!public_send(name) } if instance_predicate
-
-
164
ivar = "@#{name}"
-
-
164
define_singleton_method("#{name}=") do |val|
-
287
singleton_class.class_eval do
-
287
remove_possible_method(name)
-
1305
define_method(name) { val }
-
end
-
-
287
if singleton_class?
-
class_eval do
-
remove_possible_method(name)
-
define_method(name) do
-
if instance_variable_defined? ivar
-
instance_variable_get ivar
-
else
-
singleton_class.send name
-
end
-
end
-
end
-
end
-
287
val
-
end
-
-
164
if instance_reader
-
156
remove_possible_method name
-
156
define_method(name) do
-
185
if instance_variable_defined?(ivar)
-
instance_variable_get ivar
-
else
-
185
self.class.public_send name
-
end
-
end
-
166
define_method("#{name}?") { !!public_send(name) } if instance_predicate
-
end
-
-
164
attr_writer name if instance_writer
-
end
-
end
-
-
1
private
-
-
1
unless respond_to?(:singleton_class?)
-
def singleton_class?
-
ancestors.first != self
-
end
-
end
-
end
-
1
require 'active_support/core_ext/kernel/singleton_class'
-
1
require 'active_support/core_ext/module/remove_method'
-
-
1
class Class
-
1
def superclass_delegating_accessor(name, options = {})
-
# Create private _name and _name= methods that can still be used if the public
-
# methods are overridden.
-
_superclass_delegating_accessor("_#{name}", options)
-
-
# Generate the public methods name, name=, and name?.
-
# These methods dispatch to the private _name, and _name= methods, making them
-
# overridable.
-
singleton_class.send(:define_method, name) { send("_#{name}") }
-
singleton_class.send(:define_method, "#{name}?") { !!send("_#{name}") }
-
singleton_class.send(:define_method, "#{name}=") { |value| send("_#{name}=", value) }
-
-
# If an instance_reader is needed, generate public instance methods name and name?.
-
if options[:instance_reader] != false
-
define_method(name) { send("_#{name}") }
-
define_method("#{name}?") { !!send("#{name}") }
-
end
-
end
-
-
1
private
-
# Take the object being set and store it in a method. This gives us automatic
-
# inheritance behavior, without having to store the object in an instance
-
# variable and look up the superclass chain manually.
-
1
def _stash_object_in_method(object, method, instance_reader = true)
-
singleton_class.remove_possible_method(method)
-
singleton_class.send(:define_method, method) { object }
-
remove_possible_method(method)
-
define_method(method) { object } if instance_reader
-
end
-
-
1
def _superclass_delegating_accessor(name, options = {})
-
singleton_class.send(:define_method, "#{name}=") do |value|
-
_stash_object_in_method(value, name, options[:instance_reader] != false)
-
end
-
send("#{name}=", nil)
-
end
-
end
-
1
require 'active_support/core_ext/module/anonymous'
-
1
require 'active_support/core_ext/module/reachable'
-
-
1
class Class
-
1
begin
-
1
ObjectSpace.each_object(Class.new) {}
-
-
1
def descendants # :nodoc:
-
descendants = []
-
ObjectSpace.each_object(singleton_class) do |k|
-
descendants.unshift k unless k == self
-
end
-
descendants
-
end
-
rescue StandardError # JRuby
-
def descendants # :nodoc:
-
descendants = []
-
ObjectSpace.each_object(Class) do |k|
-
descendants.unshift k if k < self
-
end
-
descendants.uniq!
-
descendants
-
end
-
end
-
-
# Returns an array with the direct children of +self+.
-
#
-
# Integer.subclasses # => [Fixnum, Bignum]
-
#
-
# class Foo; end
-
# class Bar < Foo; end
-
# class Baz < Bar; end
-
#
-
# Foo.subclasses # => [Bar]
-
1
def subclasses
-
subclasses, chain = [], descendants
-
chain.each do |k|
-
subclasses << k unless chain.any? { |c| c > k }
-
end
-
subclasses
-
end
-
end
-
1
require 'active_support/core_ext/date/acts_like'
-
1
require 'active_support/core_ext/date/calculations'
-
1
require 'active_support/core_ext/date/conversions'
-
1
require 'active_support/core_ext/date/zones'
-
-
1
require 'active_support/core_ext/object/acts_like'
-
-
1
class Date
-
# Duck-types as a Date-like class. See Object#acts_like?.
-
1
def acts_like_date?
-
true
-
end
-
end
-
1
require 'date'
-
1
require 'active_support/duration'
-
1
require 'active_support/core_ext/object/acts_like'
-
1
require 'active_support/core_ext/date/zones'
-
1
require 'active_support/core_ext/time/zones'
-
1
require 'active_support/core_ext/date_and_time/calculations'
-
-
1
class Date
-
1
include DateAndTime::Calculations
-
-
1
class << self
-
1
attr_accessor :beginning_of_week_default
-
-
# Returns the week start (e.g. :monday) for the current request, if this has been set (via Date.beginning_of_week=).
-
# If <tt>Date.beginning_of_week</tt> has not been set for the current request, returns the week start specified in <tt>config.beginning_of_week</tt>.
-
# If no config.beginning_of_week was specified, returns :monday.
-
1
def beginning_of_week
-
Thread.current[:beginning_of_week] || beginning_of_week_default || :monday
-
end
-
-
# Sets <tt>Date.beginning_of_week</tt> to a week start (e.g. :monday) for current request/thread.
-
#
-
# This method accepts any of the following day symbols:
-
# :monday, :tuesday, :wednesday, :thursday, :friday, :saturday, :sunday
-
1
def beginning_of_week=(week_start)
-
Thread.current[:beginning_of_week] = find_beginning_of_week!(week_start)
-
end
-
-
# Returns week start day symbol (e.g. :monday), or raises an ArgumentError for invalid day symbol.
-
1
def find_beginning_of_week!(week_start)
-
1
raise ArgumentError, "Invalid beginning of week: #{week_start}" unless ::Date::DAYS_INTO_WEEK.key?(week_start)
-
1
week_start
-
end
-
-
# Returns a new Date representing the date 1 day ago (i.e. yesterday's date).
-
1
def yesterday
-
::Date.current.yesterday
-
end
-
-
# Returns a new Date representing the date 1 day after today (i.e. tomorrow's date).
-
1
def tomorrow
-
::Date.current.tomorrow
-
end
-
-
# Returns Time.zone.today when <tt>Time.zone</tt> or <tt>config.time_zone</tt> are set, otherwise just returns Date.today.
-
1
def current
-
::Time.zone ? ::Time.zone.today : ::Date.today
-
end
-
end
-
-
# Converts Date to a Time (or DateTime if necessary) with the time portion set to the beginning of the day (0:00)
-
# and then subtracts the specified number of seconds.
-
1
def ago(seconds)
-
in_time_zone.since(-seconds)
-
end
-
-
# Converts Date to a Time (or DateTime if necessary) with the time portion set to the beginning of the day (0:00)
-
# and then adds the specified number of seconds
-
1
def since(seconds)
-
in_time_zone.since(seconds)
-
end
-
1
alias :in :since
-
-
# Converts Date to a Time (or DateTime if necessary) with the time portion set to the beginning of the day (0:00)
-
1
def beginning_of_day
-
in_time_zone
-
end
-
1
alias :midnight :beginning_of_day
-
1
alias :at_midnight :beginning_of_day
-
1
alias :at_beginning_of_day :beginning_of_day
-
-
# Converts Date to a Time (or DateTime if necessary) with the time portion set to the middle of the day (12:00)
-
1
def middle_of_day
-
in_time_zone.middle_of_day
-
end
-
1
alias :midday :middle_of_day
-
1
alias :noon :middle_of_day
-
1
alias :at_midday :middle_of_day
-
1
alias :at_noon :middle_of_day
-
1
alias :at_middle_of_day :middle_of_day
-
-
# Converts Date to a Time (or DateTime if necessary) with the time portion set to the end of the day (23:59:59)
-
1
def end_of_day
-
in_time_zone.end_of_day
-
end
-
1
alias :at_end_of_day :end_of_day
-
-
1
def plus_with_duration(other) #:nodoc:
-
if ActiveSupport::Duration === other
-
other.since(self)
-
else
-
plus_without_duration(other)
-
end
-
end
-
1
alias_method :plus_without_duration, :+
-
1
alias_method :+, :plus_with_duration
-
-
1
def minus_with_duration(other) #:nodoc:
-
if ActiveSupport::Duration === other
-
plus_with_duration(-other)
-
else
-
minus_without_duration(other)
-
end
-
end
-
1
alias_method :minus_without_duration, :-
-
1
alias_method :-, :minus_with_duration
-
-
# Provides precise Date calculations for years, months, and days. The +options+ parameter takes a hash with
-
# any of these keys: <tt>:years</tt>, <tt>:months</tt>, <tt>:weeks</tt>, <tt>:days</tt>.
-
1
def advance(options)
-
options = options.dup
-
d = self
-
d = d >> options.delete(:years) * 12 if options[:years]
-
d = d >> options.delete(:months) if options[:months]
-
d = d + options.delete(:weeks) * 7 if options[:weeks]
-
d = d + options.delete(:days) if options[:days]
-
d
-
end
-
-
# Returns a new Date where one or more of the elements have been changed according to the +options+ parameter.
-
# The +options+ parameter is a hash with a combination of these keys: <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>.
-
#
-
# Date.new(2007, 5, 12).change(day: 1) # => Date.new(2007, 5, 1)
-
# Date.new(2007, 5, 12).change(year: 2005, month: 1) # => Date.new(2005, 1, 12)
-
1
def change(options)
-
::Date.new(
-
options.fetch(:year, year),
-
options.fetch(:month, month),
-
options.fetch(:day, day)
-
)
-
end
-
-
# Allow Date to be compared with Time by converting to DateTime and relying on the <=> from there.
-
1
def compare_with_coercion(other)
-
if other.is_a?(Time)
-
self.to_datetime <=> other
-
else
-
compare_without_coercion(other)
-
end
-
end
-
1
alias_method :compare_without_coercion, :<=>
-
1
alias_method :<=>, :compare_with_coercion
-
end
-
1
require 'date'
-
1
require 'active_support/inflector/methods'
-
1
require 'active_support/core_ext/date/zones'
-
1
require 'active_support/core_ext/module/remove_method'
-
-
1
class Date
-
1
DATE_FORMATS = {
-
:short => '%e %b',
-
:long => '%B %e, %Y',
-
:db => '%Y-%m-%d',
-
:number => '%Y%m%d',
-
:long_ordinal => lambda { |date|
-
day_format = ActiveSupport::Inflector.ordinalize(date.day)
-
date.strftime("%B #{day_format}, %Y") # => "April 25th, 2007"
-
},
-
:rfc822 => '%e %b %Y',
-
:iso8601 => lambda { |date| date.iso8601 }
-
}
-
-
# Ruby 1.9 has Date#to_time which converts to localtime only.
-
1
remove_method :to_time
-
-
# Ruby 1.9 has Date#xmlschema which converts to a string without the time
-
# component. This removal may generate an issue on FreeBSD, that's why we
-
# need to use remove_possible_method here
-
1
remove_possible_method :xmlschema
-
-
# Convert to a formatted string. See DATE_FORMATS for predefined formats.
-
#
-
# This method is aliased to <tt>to_s</tt>.
-
#
-
# date = Date.new(2007, 11, 10) # => Sat, 10 Nov 2007
-
#
-
# date.to_formatted_s(:db) # => "2007-11-10"
-
# date.to_s(:db) # => "2007-11-10"
-
#
-
# date.to_formatted_s(:short) # => "10 Nov"
-
# date.to_formatted_s(:long) # => "November 10, 2007"
-
# date.to_formatted_s(:long_ordinal) # => "November 10th, 2007"
-
# date.to_formatted_s(:rfc822) # => "10 Nov 2007"
-
# date.to_formatted_s(:iso8601) # => "2007-11-10"
-
#
-
# == Adding your own date formats to to_formatted_s
-
# You can add your own formats to the Date::DATE_FORMATS hash.
-
# Use the format name as the hash key and either a strftime string
-
# or Proc instance that takes a date argument as the value.
-
#
-
# # config/initializers/date_formats.rb
-
# Date::DATE_FORMATS[:month_and_year] = '%B %Y'
-
# Date::DATE_FORMATS[:short_ordinal] = ->(date) { date.strftime("%B #{date.day.ordinalize}") }
-
1
def to_formatted_s(format = :default)
-
4
if formatter = DATE_FORMATS[format]
-
4
if formatter.respond_to?(:call)
-
formatter.call(self).to_s
-
else
-
4
strftime(formatter)
-
end
-
else
-
to_default_s
-
end
-
end
-
1
alias_method :to_default_s, :to_s
-
1
alias_method :to_s, :to_formatted_s
-
-
# Overrides the default inspect method with a human readable one, e.g., "Mon, 21 Feb 2005"
-
1
def readable_inspect
-
strftime('%a, %d %b %Y')
-
end
-
1
alias_method :default_inspect, :inspect
-
1
alias_method :inspect, :readable_inspect
-
-
# Converts a Date instance to a Time, where the time is set to the beginning of the day.
-
# The timezone can be either :local or :utc (default :local).
-
#
-
# date = Date.new(2007, 11, 10) # => Sat, 10 Nov 2007
-
#
-
# date.to_time # => Sat Nov 10 00:00:00 0800 2007
-
# date.to_time(:local) # => Sat Nov 10 00:00:00 0800 2007
-
#
-
# date.to_time(:utc) # => Sat Nov 10 00:00:00 UTC 2007
-
1
def to_time(form = :local)
-
::Time.send(form, year, month, day)
-
end
-
-
1
def xmlschema
-
in_time_zone.xmlschema
-
end
-
end
-
1
require 'date'
-
1
require 'active_support/core_ext/date_and_time/zones'
-
-
1
class Date
-
1
include DateAndTime::Zones
-
end
-
1
module DateAndTime
-
1
module Calculations
-
1
DAYS_INTO_WEEK = {
-
:monday => 0,
-
:tuesday => 1,
-
:wednesday => 2,
-
:thursday => 3,
-
:friday => 4,
-
:saturday => 5,
-
:sunday => 6
-
}
-
-
# Returns a new date/time representing yesterday.
-
1
def yesterday
-
advance(:days => -1)
-
end
-
-
# Returns a new date/time representing tomorrow.
-
1
def tomorrow
-
advance(:days => 1)
-
end
-
-
# Returns true if the date/time is today.
-
1
def today?
-
to_date == ::Date.current
-
end
-
-
# Returns true if the date/time is in the past.
-
1
def past?
-
self < self.class.current
-
end
-
-
# Returns true if the date/time is in the future.
-
1
def future?
-
self > self.class.current
-
end
-
-
# Returns a new date/time the specified number of days ago.
-
1
def days_ago(days)
-
advance(:days => -days)
-
end
-
-
# Returns a new date/time the specified number of days in the future.
-
1
def days_since(days)
-
advance(:days => days)
-
end
-
-
# Returns a new date/time the specified number of weeks ago.
-
1
def weeks_ago(weeks)
-
advance(:weeks => -weeks)
-
end
-
-
# Returns a new date/time the specified number of weeks in the future.
-
1
def weeks_since(weeks)
-
advance(:weeks => weeks)
-
end
-
-
# Returns a new date/time the specified number of months ago.
-
1
def months_ago(months)
-
advance(:months => -months)
-
end
-
-
# Returns a new date/time the specified number of months in the future.
-
1
def months_since(months)
-
advance(:months => months)
-
end
-
-
# Returns a new date/time the specified number of years ago.
-
1
def years_ago(years)
-
advance(:years => -years)
-
end
-
-
# Returns a new date/time the specified number of years in the future.
-
1
def years_since(years)
-
advance(:years => years)
-
end
-
-
# Returns a new date/time at the start of the month.
-
# DateTime objects will have a time set to 0:00.
-
1
def beginning_of_month
-
first_hour(change(:day => 1))
-
end
-
1
alias :at_beginning_of_month :beginning_of_month
-
-
# Returns a new date/time at the start of the quarter.
-
# Example: 1st January, 1st July, 1st October.
-
# DateTime objects will have a time set to 0:00.
-
1
def beginning_of_quarter
-
first_quarter_month = [10, 7, 4, 1].detect { |m| m <= month }
-
beginning_of_month.change(:month => first_quarter_month)
-
end
-
1
alias :at_beginning_of_quarter :beginning_of_quarter
-
-
# Returns a new date/time at the end of the quarter.
-
# Example: 31st March, 30th June, 30th September.
-
# DateTime objects will have a time set to 23:59:59.
-
1
def end_of_quarter
-
last_quarter_month = [3, 6, 9, 12].detect { |m| m >= month }
-
beginning_of_month.change(:month => last_quarter_month).end_of_month
-
end
-
1
alias :at_end_of_quarter :end_of_quarter
-
-
# Return a new date/time at the beginning of the year.
-
# Example: 1st January.
-
# DateTime objects will have a time set to 0:00.
-
1
def beginning_of_year
-
change(:month => 1).beginning_of_month
-
end
-
1
alias :at_beginning_of_year :beginning_of_year
-
-
# Returns a new date/time representing the given day in the next week.
-
# The +given_day_in_next_week+ defaults to the beginning of the week
-
# which is determined by +Date.beginning_of_week+ or +config.beginning_of_week+
-
# when set. +DateTime+ objects have their time set to 0:00.
-
1
def next_week(given_day_in_next_week = Date.beginning_of_week)
-
first_hour(weeks_since(1).beginning_of_week.days_since(days_span(given_day_in_next_week)))
-
end
-
-
# Short-hand for months_since(1).
-
1
def next_month
-
months_since(1)
-
end
-
-
# Short-hand for months_since(3)
-
1
def next_quarter
-
months_since(3)
-
end
-
-
# Short-hand for years_since(1).
-
1
def next_year
-
years_since(1)
-
end
-
-
# Returns a new date/time representing the given day in the previous week.
-
# Week is assumed to start on +start_day+, default is
-
# +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
-
# DateTime objects have their time set to 0:00.
-
1
def prev_week(start_day = Date.beginning_of_week)
-
first_hour(weeks_ago(1).beginning_of_week.days_since(days_span(start_day)))
-
end
-
1
alias_method :last_week, :prev_week
-
-
# Short-hand for months_ago(1).
-
1
def prev_month
-
months_ago(1)
-
end
-
1
alias_method :last_month, :prev_month
-
-
# Short-hand for months_ago(3).
-
1
def prev_quarter
-
months_ago(3)
-
end
-
1
alias_method :last_quarter, :prev_quarter
-
-
# Short-hand for years_ago(1).
-
1
def prev_year
-
years_ago(1)
-
end
-
1
alias_method :last_year, :prev_year
-
-
# Returns the number of days to the start of the week on the given day.
-
# Week is assumed to start on +start_day+, default is
-
# +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
-
1
def days_to_week_start(start_day = Date.beginning_of_week)
-
start_day_number = DAYS_INTO_WEEK[start_day]
-
current_day_number = wday != 0 ? wday - 1 : 6
-
(current_day_number - start_day_number) % 7
-
end
-
-
# Returns a new date/time representing the start of this week on the given day.
-
# Week is assumed to start on +start_day+, default is
-
# +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
-
# +DateTime+ objects have their time set to 0:00.
-
1
def beginning_of_week(start_day = Date.beginning_of_week)
-
result = days_ago(days_to_week_start(start_day))
-
acts_like?(:time) ? result.midnight : result
-
end
-
1
alias :at_beginning_of_week :beginning_of_week
-
-
# Returns Monday of this week assuming that week starts on Monday.
-
# +DateTime+ objects have their time set to 0:00.
-
1
def monday
-
beginning_of_week(:monday)
-
end
-
-
# Returns a new date/time representing the end of this week on the given day.
-
# Week is assumed to start on +start_day+, default is
-
# +Date.beginning_of_week+ or +config.beginning_of_week+ when set.
-
# DateTime objects have their time set to 23:59:59.
-
1
def end_of_week(start_day = Date.beginning_of_week)
-
last_hour(days_since(6 - days_to_week_start(start_day)))
-
end
-
1
alias :at_end_of_week :end_of_week
-
-
# Returns Sunday of this week assuming that week starts on Monday.
-
# +DateTime+ objects have their time set to 23:59:59.
-
1
def sunday
-
end_of_week(:monday)
-
end
-
-
# Returns a new date/time representing the end of the month.
-
# DateTime objects will have a time set to 23:59:59.
-
1
def end_of_month
-
last_day = ::Time.days_in_month(month, year)
-
last_hour(days_since(last_day - day))
-
end
-
1
alias :at_end_of_month :end_of_month
-
-
# Returns a new date/time representing the end of the year.
-
# DateTime objects will have a time set to 23:59:59.
-
1
def end_of_year
-
change(:month => 12).end_of_month
-
end
-
1
alias :at_end_of_year :end_of_year
-
-
# Returns a Range representing the whole week of the current date/time.
-
# Week starts on start_day, default is <tt>Date.week_start</tt> or <tt>config.week_start</tt> when set.
-
1
def all_week(start_day = Date.beginning_of_week)
-
beginning_of_week(start_day)..end_of_week(start_day)
-
end
-
-
# Returns a Range representing the whole month of the current date/time.
-
1
def all_month
-
beginning_of_month..end_of_month
-
end
-
-
# Returns a Range representing the whole quarter of the current date/time.
-
1
def all_quarter
-
beginning_of_quarter..end_of_quarter
-
end
-
-
# Returns a Range representing the whole year of the current date/time.
-
1
def all_year
-
beginning_of_year..end_of_year
-
end
-
-
1
private
-
-
1
def first_hour(date_or_time)
-
date_or_time.acts_like?(:time) ? date_or_time.beginning_of_day : date_or_time
-
end
-
-
1
def last_hour(date_or_time)
-
date_or_time.acts_like?(:time) ? date_or_time.end_of_day : date_or_time
-
end
-
-
1
def days_span(day)
-
(DAYS_INTO_WEEK[day] - DAYS_INTO_WEEK[Date.beginning_of_week]) % 7
-
end
-
end
-
end
-
1
module DateAndTime
-
1
module Zones
-
# Returns the simultaneous time in <tt>Time.zone</tt> if a zone is given or
-
# if Time.zone_default is set. Otherwise, it returns the current time.
-
#
-
# Time.zone = 'Hawaii' # => 'Hawaii'
-
# DateTime.utc(2000).in_time_zone # => Fri, 31 Dec 1999 14:00:00 HST -10:00
-
# Date.new(2000).in_time_zone # => Sat, 01 Jan 2000 00:00:00 HST -10:00
-
#
-
# This method is similar to Time#localtime, except that it uses <tt>Time.zone</tt> as the local zone
-
# instead of the operating system's time zone.
-
#
-
# You can also pass in a TimeZone instance or string that identifies a TimeZone as an argument,
-
# and the conversion will be based on that zone instead of <tt>Time.zone</tt>.
-
#
-
# Time.utc(2000).in_time_zone('Alaska') # => Fri, 31 Dec 1999 15:00:00 AKST -09:00
-
# DateTime.utc(2000).in_time_zone('Alaska') # => Fri, 31 Dec 1999 15:00:00 AKST -09:00
-
# Date.new(2000).in_time_zone('Alaska') # => Sat, 01 Jan 2000 00:00:00 AKST -09:00
-
1
def in_time_zone(zone = ::Time.zone)
-
12
time_zone = ::Time.find_zone! zone
-
12
time = acts_like?(:time) ? self : nil
-
-
12
if time_zone
-
12
time_with_zone(time, time_zone)
-
else
-
time || self.to_time
-
end
-
end
-
-
1
private
-
-
1
def time_with_zone(time, zone)
-
12
if time
-
12
ActiveSupport::TimeWithZone.new(time.utc? ? time : time.getutc, zone)
-
else
-
ActiveSupport::TimeWithZone.new(nil, zone, to_time(:utc))
-
end
-
end
-
end
-
end
-
-
1
require 'active_support/core_ext/date_time/acts_like'
-
1
require 'active_support/core_ext/date_time/calculations'
-
1
require 'active_support/core_ext/date_time/conversions'
-
1
require 'active_support/core_ext/date_time/zones'
-
1
require 'date'
-
1
require 'active_support/core_ext/object/acts_like'
-
-
1
class DateTime
-
# Duck-types as a Date-like class. See Object#acts_like?.
-
1
def acts_like_date?
-
true
-
end
-
-
# Duck-types as a Time-like class. See Object#acts_like?.
-
1
def acts_like_time?
-
true
-
end
-
end
-
1
require 'date'
-
-
1
class DateTime
-
1
class << self
-
# Returns <tt>Time.zone.now.to_datetime</tt> when <tt>Time.zone</tt> or
-
# <tt>config.time_zone</tt> are set, otherwise returns
-
# <tt>Time.now.to_datetime</tt>.
-
1
def current
-
::Time.zone ? ::Time.zone.now.to_datetime : ::Time.now.to_datetime
-
end
-
end
-
-
# Seconds since midnight: DateTime.now.seconds_since_midnight.
-
1
def seconds_since_midnight
-
sec + (min * 60) + (hour * 3600)
-
end
-
-
# Returns the number of seconds until 23:59:59.
-
#
-
# DateTime.new(2012, 8, 29, 0, 0, 0).seconds_until_end_of_day # => 86399
-
# DateTime.new(2012, 8, 29, 12, 34, 56).seconds_until_end_of_day # => 41103
-
# DateTime.new(2012, 8, 29, 23, 59, 59).seconds_until_end_of_day # => 0
-
1
def seconds_until_end_of_day
-
end_of_day.to_i - to_i
-
end
-
-
# Returns a new DateTime where one or more of the elements have been changed
-
# according to the +options+ parameter. The time options (<tt>:hour</tt>,
-
# <tt>:min</tt>, <tt>:sec</tt>) reset cascadingly, so if only the hour is
-
# passed, then minute and sec is set to 0. If the hour and minute is passed,
-
# then sec is set to 0. The +options+ parameter takes a hash with any of these
-
# keys: <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>, <tt>:hour</tt>,
-
# <tt>:min</tt>, <tt>:sec</tt>, <tt>:offset</tt>, <tt>:start</tt>.
-
#
-
# DateTime.new(2012, 8, 29, 22, 35, 0).change(day: 1) # => DateTime.new(2012, 8, 1, 22, 35, 0)
-
# DateTime.new(2012, 8, 29, 22, 35, 0).change(year: 1981, day: 1) # => DateTime.new(1981, 8, 1, 22, 35, 0)
-
# DateTime.new(2012, 8, 29, 22, 35, 0).change(year: 1981, hour: 0) # => DateTime.new(1981, 8, 29, 0, 0, 0)
-
1
def change(options)
-
::DateTime.civil(
-
options.fetch(:year, year),
-
options.fetch(:month, month),
-
options.fetch(:day, day),
-
options.fetch(:hour, hour),
-
options.fetch(:min, options[:hour] ? 0 : min),
-
options.fetch(:sec, (options[:hour] || options[:min]) ? 0 : sec + sec_fraction),
-
options.fetch(:offset, offset),
-
options.fetch(:start, start)
-
)
-
end
-
-
# Uses Date to provide precise Time calculations for years, months, and days.
-
# The +options+ parameter takes a hash with any of these keys: <tt>:years</tt>,
-
# <tt>:months</tt>, <tt>:weeks</tt>, <tt>:days</tt>, <tt>:hours</tt>,
-
# <tt>:minutes</tt>, <tt>:seconds</tt>.
-
1
def advance(options)
-
d = to_date.advance(options)
-
datetime_advanced_by_date = change(:year => d.year, :month => d.month, :day => d.day)
-
seconds_to_advance = \
-
options.fetch(:seconds, 0) +
-
options.fetch(:minutes, 0) * 60 +
-
options.fetch(:hours, 0) * 3600
-
-
if seconds_to_advance.zero?
-
datetime_advanced_by_date
-
else
-
datetime_advanced_by_date.since seconds_to_advance
-
end
-
end
-
-
# Returns a new DateTime representing the time a number of seconds ago.
-
# Do not use this method in combination with x.months, use months_ago instead!
-
1
def ago(seconds)
-
since(-seconds)
-
end
-
-
# Returns a new DateTime representing the time a number of seconds since the
-
# instance time. Do not use this method in combination with x.months, use
-
# months_since instead!
-
1
def since(seconds)
-
self + Rational(seconds.round, 86400)
-
end
-
1
alias :in :since
-
-
# Returns a new DateTime representing the start of the day (0:00).
-
1
def beginning_of_day
-
change(:hour => 0)
-
end
-
1
alias :midnight :beginning_of_day
-
1
alias :at_midnight :beginning_of_day
-
1
alias :at_beginning_of_day :beginning_of_day
-
-
# Returns a new DateTime representing the middle of the day (12:00)
-
1
def middle_of_day
-
change(:hour => 12)
-
end
-
1
alias :midday :middle_of_day
-
1
alias :noon :middle_of_day
-
1
alias :at_midday :middle_of_day
-
1
alias :at_noon :middle_of_day
-
1
alias :at_middle_of_day :middle_of_day
-
-
# Returns a new DateTime representing the end of the day (23:59:59).
-
1
def end_of_day
-
change(:hour => 23, :min => 59, :sec => 59)
-
end
-
1
alias :at_end_of_day :end_of_day
-
-
# Returns a new DateTime representing the start of the hour (hh:00:00).
-
1
def beginning_of_hour
-
change(:min => 0)
-
end
-
1
alias :at_beginning_of_hour :beginning_of_hour
-
-
# Returns a new DateTime representing the end of the hour (hh:59:59).
-
1
def end_of_hour
-
change(:min => 59, :sec => 59)
-
end
-
1
alias :at_end_of_hour :end_of_hour
-
-
# Returns a new DateTime representing the start of the minute (hh:mm:00).
-
1
def beginning_of_minute
-
change(:sec => 0)
-
end
-
1
alias :at_beginning_of_minute :beginning_of_minute
-
-
# Returns a new DateTime representing the end of the minute (hh:mm:59).
-
1
def end_of_minute
-
change(:sec => 59)
-
end
-
1
alias :at_end_of_minute :end_of_minute
-
-
# Adjusts DateTime to UTC by adding its offset value; offset is set to 0.
-
#
-
# DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(-6, 24)) # => Mon, 21 Feb 2005 10:11:12 -0600
-
# DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(-6, 24)).utc # => Mon, 21 Feb 2005 16:11:12 +0000
-
1
def utc
-
new_offset(0)
-
end
-
1
alias_method :getutc, :utc
-
-
# Returns +true+ if <tt>offset == 0</tt>.
-
1
def utc?
-
offset == 0
-
end
-
-
# Returns the offset value in seconds.
-
1
def utc_offset
-
(offset * 86400).to_i
-
end
-
-
# Layers additional behavior on DateTime#<=> so that Time and
-
# ActiveSupport::TimeWithZone instances can be compared with a DateTime.
-
1
def <=>(other)
-
if other.kind_of?(Infinity)
-
super
-
elsif other.respond_to? :to_datetime
-
super other.to_datetime
-
else
-
nil
-
end
-
end
-
-
end
-
1
require 'date'
-
1
require 'active_support/inflector/methods'
-
1
require 'active_support/core_ext/time/conversions'
-
1
require 'active_support/core_ext/date_time/calculations'
-
1
require 'active_support/values/time_zone'
-
-
1
class DateTime
-
# Convert to a formatted string. See Time::DATE_FORMATS for predefined formats.
-
#
-
# This method is aliased to <tt>to_s</tt>.
-
#
-
# === Examples
-
# datetime = DateTime.civil(2007, 12, 4, 0, 0, 0, 0) # => Tue, 04 Dec 2007 00:00:00 +0000
-
#
-
# datetime.to_formatted_s(:db) # => "2007-12-04 00:00:00"
-
# datetime.to_s(:db) # => "2007-12-04 00:00:00"
-
# datetime.to_s(:number) # => "20071204000000"
-
# datetime.to_formatted_s(:short) # => "04 Dec 00:00"
-
# datetime.to_formatted_s(:long) # => "December 04, 2007 00:00"
-
# datetime.to_formatted_s(:long_ordinal) # => "December 4th, 2007 00:00"
-
# datetime.to_formatted_s(:rfc822) # => "Tue, 04 Dec 2007 00:00:00 +0000"
-
# datetime.to_formatted_s(:iso8601) # => "2007-12-04T00:00:00+00:00"
-
#
-
# == Adding your own datetime formats to to_formatted_s
-
# DateTime formats are shared with Time. You can add your own to the
-
# Time::DATE_FORMATS hash. Use the format name as the hash key and
-
# either a strftime string or Proc instance that takes a time or
-
# datetime argument as the value.
-
#
-
# # config/initializers/time_formats.rb
-
# Time::DATE_FORMATS[:month_and_year] = '%B %Y'
-
# Time::DATE_FORMATS[:short_ordinal] = lambda { |time| time.strftime("%B #{time.day.ordinalize}") }
-
1
def to_formatted_s(format = :default)
-
if formatter = ::Time::DATE_FORMATS[format]
-
formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter)
-
else
-
to_default_s
-
end
-
end
-
1
alias_method :to_default_s, :to_s if instance_methods(false).include?(:to_s)
-
1
alias_method :to_s, :to_formatted_s
-
-
#
-
# datetime = DateTime.civil(2000, 1, 1, 0, 0, 0, Rational(-6, 24))
-
# datetime.formatted_offset # => "-06:00"
-
# datetime.formatted_offset(false) # => "-0600"
-
1
def formatted_offset(colon = true, alternate_utc_string = nil)
-
utc? && alternate_utc_string || ActiveSupport::TimeZone.seconds_to_utc_offset(utc_offset, colon)
-
end
-
-
# Overrides the default inspect method with a human readable one, e.g., "Mon, 21 Feb 2005 14:30:00 +0000".
-
1
def readable_inspect
-
to_s(:rfc822)
-
end
-
1
alias_method :default_inspect, :inspect
-
1
alias_method :inspect, :readable_inspect
-
-
# Returns DateTime with local offset for given year if format is local else
-
# offset is zero.
-
#
-
# DateTime.civil_from_format :local, 2012
-
# # => Sun, 01 Jan 2012 00:00:00 +0300
-
# DateTime.civil_from_format :local, 2012, 12, 17
-
# # => Mon, 17 Dec 2012 00:00:00 +0000
-
1
def self.civil_from_format(utc_or_local, year, month=1, day=1, hour=0, min=0, sec=0)
-
if utc_or_local.to_sym == :local
-
offset = ::Time.local(year, month, day).utc_offset.to_r / 86400
-
else
-
offset = 0
-
end
-
civil(year, month, day, hour, min, sec, offset)
-
end
-
-
# Converts +self+ to a floating-point number of seconds since the Unix epoch.
-
1
def to_f
-
seconds_since_unix_epoch.to_f
-
end
-
-
# Converts +self+ to an integer number of seconds since the Unix epoch.
-
1
def to_i
-
seconds_since_unix_epoch.to_i
-
end
-
-
# Returns the fraction of a second as microseconds
-
1
def usec
-
(sec_fraction * 1_000_000).to_i
-
end
-
-
# Returns the fraction of a second as nanoseconds
-
1
def nsec
-
(sec_fraction * 1_000_000_000).to_i
-
end
-
-
1
private
-
-
1
def offset_in_seconds
-
(offset * 86400).to_i
-
end
-
-
1
def seconds_since_unix_epoch
-
(jd - 2440588) * 86400 - offset_in_seconds + seconds_since_midnight
-
end
-
end
-
1
require 'date'
-
1
require 'active_support/core_ext/date_and_time/zones'
-
-
1
class DateTime
-
1
include DateAndTime::Zones
-
end
-
1
module Enumerable
-
# Calculates a sum from the elements.
-
#
-
# payments.sum { |p| p.price * p.tax_rate }
-
# payments.sum(&:price)
-
#
-
# The latter is a shortcut for:
-
#
-
# payments.inject(0) { |sum, p| sum + p.price }
-
#
-
# It can also calculate the sum without the use of a block.
-
#
-
# [5, 15, 10].sum # => 30
-
# ['foo', 'bar'].sum # => "foobar"
-
# [[1, 2], [3, 1, 5]].sum => [1, 2, 3, 1, 5]
-
#
-
# The default sum of an empty list is zero. You can override this default:
-
#
-
# [].sum(Payment.new(0)) { |i| i.amount } # => Payment.new(0)
-
1
def sum(identity = 0, &block)
-
2
if block_given?
-
map(&block).sum(identity)
-
else
-
2
inject { |sum, element| sum + element } || identity
-
end
-
end
-
-
# Convert an enumerable to a hash.
-
#
-
# people.index_by(&:login)
-
# => { "nextangle" => <Person ...>, "chade-" => <Person ...>, ...}
-
# people.index_by { |person| "#{person.first_name} #{person.last_name}" }
-
# => { "Chade- Fowlersburg-e" => <Person ...>, "David Heinemeier Hansson" => <Person ...>, ...}
-
1
def index_by
-
if block_given?
-
Hash[map { |elem| [yield(elem), elem] }]
-
else
-
to_enum(:index_by) { size if respond_to?(:size) }
-
end
-
end
-
-
# Returns +true+ if the enumerable has more than 1 element. Functionally
-
# equivalent to <tt>enum.to_a.size > 1</tt>. Can be called with a block too,
-
# much like any?, so <tt>people.many? { |p| p.age > 26 }</tt> returns +true+
-
# if more than one person is over 26.
-
1
def many?
-
cnt = 0
-
if block_given?
-
any? do |element|
-
cnt += 1 if yield element
-
cnt > 1
-
end
-
else
-
any? { (cnt += 1) > 1 }
-
end
-
end
-
-
# The negative of the <tt>Enumerable#include?</tt>. Returns +true+ if the
-
# collection does not include the object.
-
1
def exclude?(object)
-
!include?(object)
-
end
-
end
-
-
1
class Range #:nodoc:
-
# Optimize range sum to use arithmetic progression if a block is not given and
-
# we have a range of numeric values.
-
1
def sum(identity = 0)
-
if block_given? || !(first.is_a?(Integer) && last.is_a?(Integer))
-
super
-
else
-
actual_last = exclude_end? ? (last - 1) : last
-
if actual_last >= first
-
(actual_last - first + 1) * (actual_last + first) / 2
-
else
-
identity
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/file/atomic'
-
1
require 'fileutils'
-
-
1
class File
-
# Write to a file atomically. Useful for situations where you don't
-
# want other processes or threads to see half-written files.
-
#
-
# File.atomic_write('important.file') do |file|
-
# file.write('hello')
-
# end
-
#
-
# If your temp directory is not on the same filesystem as the file you're
-
# trying to write, you can provide a different temporary directory.
-
#
-
# File.atomic_write('/data/something.important', '/data/tmp') do |file|
-
# file.write('hello')
-
# end
-
1
def self.atomic_write(file_name, temp_dir = Dir.tmpdir)
-
require 'tempfile' unless defined?(Tempfile)
-
require 'fileutils' unless defined?(FileUtils)
-
-
temp_file = Tempfile.new(basename(file_name), temp_dir)
-
temp_file.binmode
-
yield temp_file
-
temp_file.close
-
-
if File.exist?(file_name)
-
# Get original file permissions
-
old_stat = stat(file_name)
-
else
-
# If not possible, probe which are the default permissions in the
-
# destination directory.
-
old_stat = probe_stat_in(dirname(file_name))
-
end
-
-
# Overwrite original file with temp file
-
FileUtils.mv(temp_file.path, file_name)
-
-
# Set correct permissions on new file
-
begin
-
chown(old_stat.uid, old_stat.gid, file_name)
-
# This operation will affect filesystem ACL's
-
chmod(old_stat.mode, file_name)
-
rescue Errno::EPERM
-
# Changing file ownership failed, moving on.
-
end
-
end
-
-
# Private utility method.
-
1
def self.probe_stat_in(dir) #:nodoc:
-
basename = [
-
'.permissions_check',
-
Thread.current.object_id,
-
Process.pid,
-
rand(1000000)
-
].join('.')
-
-
file_name = join(dir, basename)
-
FileUtils.touch(file_name)
-
stat(file_name)
-
ensure
-
FileUtils.rm_f(file_name) if file_name
-
end
-
end
-
1
require 'active_support/core_ext/hash/compact'
-
1
require 'active_support/core_ext/hash/conversions'
-
1
require 'active_support/core_ext/hash/deep_merge'
-
1
require 'active_support/core_ext/hash/except'
-
1
require 'active_support/core_ext/hash/indifferent_access'
-
1
require 'active_support/core_ext/hash/keys'
-
1
require 'active_support/core_ext/hash/reverse_merge'
-
1
require 'active_support/core_ext/hash/slice'
-
1
class Hash
-
# Returns a hash with non +nil+ values.
-
#
-
# hash = { a: true, b: false, c: nil}
-
# hash.compact # => { a: true, b: false}
-
# hash # => { a: true, b: false, c: nil}
-
# { c: nil }.compact # => {}
-
1
def compact
-
self.select { |_, value| !value.nil? }
-
end
-
-
# Replaces current hash with non +nil+ values.
-
#
-
# hash = { a: true, b: false, c: nil}
-
# hash.compact! # => { a: true, b: false}
-
# hash # => { a: true, b: false}
-
1
def compact!
-
self.reject! { |_, value| value.nil? }
-
end
-
end
-
1
require 'active_support/xml_mini'
-
1
require 'active_support/time'
-
1
require 'active_support/core_ext/object/blank'
-
1
require 'active_support/core_ext/object/to_param'
-
1
require 'active_support/core_ext/object/to_query'
-
1
require 'active_support/core_ext/array/wrap'
-
1
require 'active_support/core_ext/hash/reverse_merge'
-
1
require 'active_support/core_ext/string/inflections'
-
-
1
class Hash
-
# Returns a string containing an XML representation of its receiver:
-
#
-
# { foo: 1, bar: 2 }.to_xml
-
# # =>
-
# # <?xml version="1.0" encoding="UTF-8"?>
-
# # <hash>
-
# # <foo type="integer">1</foo>
-
# # <bar type="integer">2</bar>
-
# # </hash>
-
#
-
# To do so, the method loops over the pairs and builds nodes that depend on
-
# the _values_. Given a pair +key+, +value+:
-
#
-
# * If +value+ is a hash there's a recursive call with +key+ as <tt>:root</tt>.
-
#
-
# * If +value+ is an array there's a recursive call with +key+ as <tt>:root</tt>,
-
# and +key+ singularized as <tt>:children</tt>.
-
#
-
# * If +value+ is a callable object it must expect one or two arguments. Depending
-
# on the arity, the callable is invoked with the +options+ hash as first argument
-
# with +key+ as <tt>:root</tt>, and +key+ singularized as second argument. The
-
# callable can add nodes by using <tt>options[:builder]</tt>.
-
#
-
# 'foo'.to_xml(lambda { |options, key| options[:builder].b(key) })
-
# # => "<b>foo</b>"
-
#
-
# * If +value+ responds to +to_xml+ the method is invoked with +key+ as <tt>:root</tt>.
-
#
-
# class Foo
-
# def to_xml(options)
-
# options[:builder].bar 'fooing!'
-
# end
-
# end
-
#
-
# { foo: Foo.new }.to_xml(skip_instruct: true)
-
# # =>
-
# # <hash>
-
# # <bar>fooing!</bar>
-
# # </hash>
-
#
-
# * Otherwise, a node with +key+ as tag is created with a string representation of
-
# +value+ as text node. If +value+ is +nil+ an attribute "nil" set to "true" is added.
-
# Unless the option <tt>:skip_types</tt> exists and is true, an attribute "type" is
-
# added as well according to the following mapping:
-
#
-
# XML_TYPE_NAMES = {
-
# "Symbol" => "symbol",
-
# "Fixnum" => "integer",
-
# "Bignum" => "integer",
-
# "BigDecimal" => "decimal",
-
# "Float" => "float",
-
# "TrueClass" => "boolean",
-
# "FalseClass" => "boolean",
-
# "Date" => "date",
-
# "DateTime" => "dateTime",
-
# "Time" => "dateTime"
-
# }
-
#
-
# By default the root node is "hash", but that's configurable via the <tt>:root</tt> option.
-
#
-
# The default XML builder is a fresh instance of <tt>Builder::XmlMarkup</tt>. You can
-
# configure your own builder with the <tt>:builder</tt> option. The method also accepts
-
# options like <tt>:dasherize</tt> and friends, they are forwarded to the builder.
-
1
def to_xml(options = {})
-
require 'active_support/builder' unless defined?(Builder)
-
-
options = options.dup
-
options[:indent] ||= 2
-
options[:root] ||= 'hash'
-
options[:builder] ||= Builder::XmlMarkup.new(indent: options[:indent])
-
-
builder = options[:builder]
-
builder.instruct! unless options.delete(:skip_instruct)
-
-
root = ActiveSupport::XmlMini.rename_key(options[:root].to_s, options)
-
-
builder.tag!(root) do
-
each { |key, value| ActiveSupport::XmlMini.to_tag(key, value, options) }
-
yield builder if block_given?
-
end
-
end
-
-
1
class << self
-
# Returns a Hash containing a collection of pairs when the key is the node name and the value is
-
# its content
-
#
-
# xml = <<-XML
-
# <?xml version="1.0" encoding="UTF-8"?>
-
# <hash>
-
# <foo type="integer">1</foo>
-
# <bar type="integer">2</bar>
-
# </hash>
-
# XML
-
#
-
# hash = Hash.from_xml(xml)
-
# # => {"hash"=>{"foo"=>1, "bar"=>2}}
-
#
-
# DisallowedType is raised if the XML contains attributes with <tt>type="yaml"</tt> or
-
# <tt>type="symbol"</tt>. Use <tt>Hash.from_trusted_xml</tt> to parse this XML.
-
1
def from_xml(xml, disallowed_types = nil)
-
ActiveSupport::XMLConverter.new(xml, disallowed_types).to_h
-
end
-
-
# Builds a Hash from XML just like <tt>Hash.from_xml</tt>, but also allows Symbol and YAML.
-
1
def from_trusted_xml(xml)
-
from_xml xml, []
-
end
-
end
-
end
-
-
1
module ActiveSupport
-
1
class XMLConverter # :nodoc:
-
1
class DisallowedType < StandardError
-
1
def initialize(type)
-
super "Disallowed type attribute: #{type.inspect}"
-
end
-
end
-
-
1
DISALLOWED_TYPES = %w(symbol yaml)
-
-
1
def initialize(xml, disallowed_types = nil)
-
@xml = normalize_keys(XmlMini.parse(xml))
-
@disallowed_types = disallowed_types || DISALLOWED_TYPES
-
end
-
-
1
def to_h
-
deep_to_h(@xml)
-
end
-
-
1
private
-
1
def normalize_keys(params)
-
case params
-
when Hash
-
Hash[params.map { |k,v| [k.to_s.tr('-', '_'), normalize_keys(v)] } ]
-
when Array
-
params.map { |v| normalize_keys(v) }
-
else
-
params
-
end
-
end
-
-
1
def deep_to_h(value)
-
case value
-
when Hash
-
process_hash(value)
-
when Array
-
process_array(value)
-
when String
-
value
-
else
-
raise "can't typecast #{value.class.name} - #{value.inspect}"
-
end
-
end
-
-
1
def process_hash(value)
-
if value.include?('type') && !value['type'].is_a?(Hash) && @disallowed_types.include?(value['type'])
-
raise DisallowedType, value['type']
-
end
-
-
if become_array?(value)
-
_, entries = Array.wrap(value.detect { |k,v| not v.is_a?(String) })
-
if entries.nil? || value['__content__'].try(:empty?)
-
[]
-
else
-
case entries
-
when Array
-
entries.collect { |v| deep_to_h(v) }
-
when Hash
-
[deep_to_h(entries)]
-
else
-
raise "can't typecast #{entries.inspect}"
-
end
-
end
-
elsif become_content?(value)
-
process_content(value)
-
-
elsif become_empty_string?(value)
-
''
-
elsif become_hash?(value)
-
xml_value = Hash[value.map { |k,v| [k, deep_to_h(v)] }]
-
-
# Turn { files: { file: #<StringIO> } } into { files: #<StringIO> } so it is compatible with
-
# how multipart uploaded files from HTML appear
-
xml_value['file'].is_a?(StringIO) ? xml_value['file'] : xml_value
-
end
-
end
-
-
1
def become_content?(value)
-
value['type'] == 'file' || (value['__content__'] && (value.keys.size == 1 || value['__content__'].present?))
-
end
-
-
1
def become_array?(value)
-
value['type'] == 'array'
-
end
-
-
1
def become_empty_string?(value)
-
# { "string" => true }
-
# No tests fail when the second term is removed.
-
value['type'] == 'string' && value['nil'] != 'true'
-
end
-
-
1
def become_hash?(value)
-
!nothing?(value) && !garbage?(value)
-
end
-
-
1
def nothing?(value)
-
# blank or nil parsed values are represented by nil
-
value.blank? || value['nil'] == 'true'
-
end
-
-
1
def garbage?(value)
-
# If the type is the only element which makes it then
-
# this still makes the value nil, except if type is
-
# a XML node(where type['value'] is a Hash)
-
value['type'] && !value['type'].is_a?(::Hash) && value.size == 1
-
end
-
-
1
def process_content(value)
-
content = value['__content__']
-
if parser = ActiveSupport::XmlMini::PARSING[value['type']]
-
parser.arity == 1 ? parser.call(content) : parser.call(content, value)
-
else
-
content
-
end
-
end
-
-
1
def process_array(value)
-
value.map! { |i| deep_to_h(i) }
-
value.length > 1 ? value : value.first
-
end
-
-
end
-
end
-
-
1
class Hash
-
# Returns a new hash with +self+ and +other_hash+ merged recursively.
-
#
-
# h1 = { a: true, b: { c: [1, 2, 3] } }
-
# h2 = { a: false, b: { x: [3, 4, 5] } }
-
#
-
# h1.deep_merge(h2) #=> { a: false, b: { c: [1, 2, 3], x: [3, 4, 5] } }
-
#
-
# Like with Hash#merge in the standard library, a block can be provided
-
# to merge values:
-
#
-
# h1 = { a: 100, b: 200, c: { c1: 100 } }
-
# h2 = { b: 250, c: { c1: 200 } }
-
# h1.deep_merge(h2) { |key, this_val, other_val| this_val + other_val }
-
# # => { a: 100, b: 450, c: { c1: 300 } }
-
1
def deep_merge(other_hash, &block)
-
dup.deep_merge!(other_hash, &block)
-
end
-
-
# Same as +deep_merge+, but modifies +self+.
-
1
def deep_merge!(other_hash, &block)
-
other_hash.each_pair do |current_key, other_value|
-
this_value = self[current_key]
-
-
self[current_key] = if this_value.is_a?(Hash) && other_value.is_a?(Hash)
-
this_value.deep_merge(other_value, &block)
-
else
-
if block_given? && key?(current_key)
-
block.call(current_key, this_value, other_value)
-
else
-
other_value
-
end
-
end
-
end
-
-
self
-
end
-
end
-
1
class Hash
-
# Returns a hash that includes everything but the given keys. This is useful for
-
# limiting a set of parameters to everything but a few known toggles:
-
#
-
# @person.update(params[:person].except(:admin))
-
1
def except(*keys)
-
277
dup.except!(*keys)
-
end
-
-
# Replaces the hash without the given keys.
-
1
def except!(*keys)
-
1339
keys.each { |key| delete(key) }
-
279
self
-
end
-
end
-
1
require 'active_support/hash_with_indifferent_access'
-
-
1
class Hash
-
-
# Returns an <tt>ActiveSupport::HashWithIndifferentAccess</tt> out of its receiver:
-
#
-
# { a: 1 }.with_indifferent_access['a'] # => 1
-
1
def with_indifferent_access
-
11
ActiveSupport::HashWithIndifferentAccess.new_from_hash_copying_default(self)
-
end
-
-
# Called when object is nested under an object that receives
-
# #with_indifferent_access. This method will be called on the current object
-
# by the enclosing object and is aliased to #with_indifferent_access by
-
# default. Subclasses of Hash may overwrite this method to return +self+ if
-
# converting to an <tt>ActiveSupport::HashWithIndifferentAccess</tt> would not be
-
# desirable.
-
#
-
# b = { b: 1 }
-
# { a: b }.with_indifferent_access['a'] # calls b.nested_under_indifferent_access
-
# # => {"b"=>32}
-
1
alias nested_under_indifferent_access with_indifferent_access
-
end
-
1
class Hash
-
# Returns a new hash with all keys converted using the block operation.
-
#
-
# hash = { name: 'Rob', age: '28' }
-
#
-
# hash.transform_keys{ |key| key.to_s.upcase }
-
# # => {"NAME"=>"Rob", "AGE"=>"28"}
-
1
def transform_keys
-
38
result = {}
-
38
each_key do |key|
-
25
result[yield(key)] = self[key]
-
end
-
38
result
-
end
-
-
# Destructively convert all keys using the block operations.
-
# Same as transform_keys but modifies +self+.
-
1
def transform_keys!
-
keys.each do |key|
-
self[yield(key)] = delete(key)
-
end
-
self
-
end
-
-
# Returns a new hash with all keys converted to strings.
-
#
-
# hash = { name: 'Rob', age: '28' }
-
#
-
# hash.stringify_keys
-
# # => { "name" => "Rob", "age" => "28" }
-
1
def stringify_keys
-
33
transform_keys{ |key| key.to_s }
-
end
-
-
# Destructively convert all keys to strings. Same as
-
# +stringify_keys+, but modifies +self+.
-
1
def stringify_keys!
-
transform_keys!{ |key| key.to_s }
-
end
-
-
# Returns a new hash with all keys converted to symbols, as long as
-
# they respond to +to_sym+.
-
#
-
# hash = { 'name' => 'Rob', 'age' => '28' }
-
#
-
# hash.symbolize_keys
-
# # => { name: "Rob", age: "28" }
-
1
def symbolize_keys
-
30
transform_keys{ |key| key.to_sym rescue key }
-
end
-
1
alias_method :to_options, :symbolize_keys
-
-
# Destructively convert all keys to symbols, as long as they respond
-
# to +to_sym+. Same as +symbolize_keys+, but modifies +self+.
-
1
def symbolize_keys!
-
transform_keys!{ |key| key.to_sym rescue key }
-
end
-
1
alias_method :to_options!, :symbolize_keys!
-
-
# Validate all keys in a hash match <tt>*valid_keys</tt>, raising ArgumentError
-
# on a mismatch. Note that keys are NOT treated indifferently, meaning if you
-
# use strings for keys but assert symbols as keys, this will fail.
-
#
-
# { name: 'Rob', years: '28' }.assert_valid_keys(:name, :age) # => raises "ArgumentError: Unknown key: :years. Valid keys are: :name, :age"
-
# { name: 'Rob', age: '28' }.assert_valid_keys('name', 'age') # => raises "ArgumentError: Unknown key: :name. Valid keys are: 'name', 'age'"
-
# { name: 'Rob', age: '28' }.assert_valid_keys(:name, :age) # => passes, raises nothing
-
1
def assert_valid_keys(*valid_keys)
-
24
valid_keys.flatten!
-
24
each_key do |k|
-
4
unless valid_keys.include?(k)
-
raise ArgumentError.new("Unknown key: #{k.inspect}. Valid keys are: #{valid_keys.map(&:inspect).join(', ')}")
-
end
-
end
-
end
-
-
# Returns a new hash with all keys converted by the block operation.
-
# This includes the keys from the root hash and from all
-
# nested hashes and arrays.
-
#
-
# hash = { person: { name: 'Rob', age: '28' } }
-
#
-
# hash.deep_transform_keys{ |key| key.to_s.upcase }
-
# # => {"PERSON"=>{"NAME"=>"Rob", "AGE"=>"28"}}
-
1
def deep_transform_keys(&block)
-
_deep_transform_keys_in_object(self, &block)
-
end
-
-
# Destructively convert all keys by using the block operation.
-
# This includes the keys from the root hash and from all
-
# nested hashes and arrays.
-
1
def deep_transform_keys!(&block)
-
_deep_transform_keys_in_object!(self, &block)
-
end
-
-
# Returns a new hash with all keys converted to strings.
-
# This includes the keys from the root hash and from all
-
# nested hashes and arrays.
-
#
-
# hash = { person: { name: 'Rob', age: '28' } }
-
#
-
# hash.deep_stringify_keys
-
# # => {"person"=>{"name"=>"Rob", "age"=>"28"}}
-
1
def deep_stringify_keys
-
deep_transform_keys{ |key| key.to_s }
-
end
-
-
# Destructively convert all keys to strings.
-
# This includes the keys from the root hash and from all
-
# nested hashes and arrays.
-
1
def deep_stringify_keys!
-
deep_transform_keys!{ |key| key.to_s }
-
end
-
-
# Returns a new hash with all keys converted to symbols, as long as
-
# they respond to +to_sym+. This includes the keys from the root hash
-
# and from all nested hashes and arrays.
-
#
-
# hash = { 'person' => { 'name' => 'Rob', 'age' => '28' } }
-
#
-
# hash.deep_symbolize_keys
-
# # => {:person=>{:name=>"Rob", :age=>"28"}}
-
1
def deep_symbolize_keys
-
deep_transform_keys{ |key| key.to_sym rescue key }
-
end
-
-
# Destructively convert all keys to symbols, as long as they respond
-
# to +to_sym+. This includes the keys from the root hash and from all
-
# nested hashes and arrays.
-
1
def deep_symbolize_keys!
-
deep_transform_keys!{ |key| key.to_sym rescue key }
-
end
-
-
1
private
-
# support methods for deep transforming nested hashes and arrays
-
1
def _deep_transform_keys_in_object(object, &block)
-
case object
-
when Hash
-
object.each_with_object({}) do |(key, value), result|
-
result[yield(key)] = _deep_transform_keys_in_object(value, &block)
-
end
-
when Array
-
object.map {|e| _deep_transform_keys_in_object(e, &block) }
-
else
-
object
-
end
-
end
-
-
1
def _deep_transform_keys_in_object!(object, &block)
-
case object
-
when Hash
-
object.keys.each do |key|
-
value = object.delete(key)
-
object[yield(key)] = _deep_transform_keys_in_object!(value, &block)
-
end
-
object
-
when Array
-
object.map! {|e| _deep_transform_keys_in_object!(e, &block)}
-
else
-
object
-
end
-
end
-
end
-
1
class Hash
-
# Merges the caller into +other_hash+. For example,
-
#
-
# options = options.reverse_merge(size: 25, velocity: 10)
-
#
-
# is equivalent to
-
#
-
# options = { size: 25, velocity: 10 }.merge(options)
-
#
-
# This is particularly useful for initializing an options hash
-
# with default values.
-
1
def reverse_merge(other_hash)
-
6
other_hash.merge(self)
-
end
-
-
# Destructive +reverse_merge+.
-
1
def reverse_merge!(other_hash)
-
# right wins if there is no left
-
88
merge!( other_hash ){|key,left,right| left }
-
end
-
1
alias_method :reverse_update, :reverse_merge!
-
end
-
1
class Hash
-
# Slice a hash to include only the given keys. This is useful for
-
# limiting an options hash to valid keys before passing to a method:
-
#
-
# def search(criteria = {})
-
# criteria.assert_valid_keys(:mass, :velocity, :time)
-
# end
-
#
-
# search(options.slice(:mass, :velocity, :time))
-
#
-
# If you have an array of keys you want to limit to, you should splat them:
-
#
-
# valid_keys = [:mass, :velocity, :time]
-
# search(options.slice(*valid_keys))
-
1
def slice(*keys)
-
30
keys.map! { |key| convert_key(key) } if respond_to?(:convert_key, true)
-
125
keys.each_with_object(self.class.new) { |k, hash| hash[k] = self[k] if has_key?(k) }
-
end
-
-
# Replaces the hash with only the given keys.
-
# Returns a hash containing the removed key/value pairs.
-
#
-
# { a: 1, b: 2, c: 3, d: 4 }.slice!(:a, :b)
-
# # => {:c=>3, :d=>4}
-
1
def slice!(*keys)
-
13
keys.map! { |key| convert_key(key) } if respond_to?(:convert_key, true)
-
13
omit = slice(*self.keys - keys)
-
13
hash = slice(*keys)
-
13
hash.default = default
-
13
hash.default_proc = default_proc if default_proc
-
13
replace(hash)
-
13
omit
-
end
-
-
# Removes and returns the key/value pairs matching the given keys.
-
#
-
# { a: 1, b: 2, c: 3, d: 4 }.extract!(:a, :b) # => {:a=>1, :b=>2}
-
# { a: 1, b: 2 }.extract!(:a, :x) # => {:a=>1}
-
1
def extract!(*keys)
-
keys.each_with_object(self.class.new) { |key, result| result[key] = delete(key) if has_key?(key) }
-
end
-
end
-
1
require 'active_support/core_ext/integer/multiple'
-
1
require 'active_support/core_ext/integer/inflections'
-
1
require 'active_support/core_ext/integer/time'
-
1
require 'active_support/inflector'
-
-
1
class Integer
-
# Ordinalize turns a number into an ordinal string used to denote the
-
# position in an ordered sequence such as 1st, 2nd, 3rd, 4th.
-
#
-
# 1.ordinalize # => "1st"
-
# 2.ordinalize # => "2nd"
-
# 1002.ordinalize # => "1002nd"
-
# 1003.ordinalize # => "1003rd"
-
# -11.ordinalize # => "-11th"
-
# -1001.ordinalize # => "-1001st"
-
1
def ordinalize
-
ActiveSupport::Inflector.ordinalize(self)
-
end
-
-
# Ordinal returns the suffix used to denote the position
-
# in an ordered sequence such as 1st, 2nd, 3rd, 4th.
-
#
-
# 1.ordinal # => "st"
-
# 2.ordinal # => "nd"
-
# 1002.ordinal # => "nd"
-
# 1003.ordinal # => "rd"
-
# -11.ordinal # => "th"
-
# -1001.ordinal # => "st"
-
1
def ordinal
-
ActiveSupport::Inflector.ordinal(self)
-
end
-
end
-
1
class Integer
-
# Check whether the integer is evenly divisible by the argument.
-
#
-
# 0.multiple_of?(0) # => true
-
# 6.multiple_of?(5) # => false
-
# 10.multiple_of?(2) # => true
-
1
def multiple_of?(number)
-
number != 0 ? self % number == 0 : zero?
-
end
-
end
-
1
require 'active_support/duration'
-
1
require 'active_support/core_ext/numeric/time'
-
-
1
class Integer
-
# Enables the use of time calculations and declarations, like <tt>45.minutes +
-
# 2.hours + 4.years</tt>.
-
#
-
# These methods use Time#advance for precise date calculations when using
-
# <tt>from_now</tt>, +ago+, etc. as well as adding or subtracting their
-
# results from a Time object.
-
#
-
# # equivalent to Time.now.advance(months: 1)
-
# 1.month.from_now
-
#
-
# # equivalent to Time.now.advance(years: 2)
-
# 2.years.from_now
-
#
-
# # equivalent to Time.now.advance(months: 4, years: 5)
-
# (4.months + 5.years).from_now
-
#
-
# While these methods provide precise calculation when used as in the examples
-
# above, care should be taken to note that this is not true if the result of
-
# +months+, +years+, etc is converted before use:
-
#
-
# # equivalent to 30.days.to_i.from_now
-
# 1.month.to_i.from_now
-
#
-
# # equivalent to 365.25.days.to_f.from_now
-
# 1.year.to_f.from_now
-
#
-
# In such cases, Ruby's core
-
# Date[http://ruby-doc.org/stdlib/libdoc/date/rdoc/Date.html] and
-
# Time[http://ruby-doc.org/stdlib/libdoc/time/rdoc/Time.html] should be used for precision
-
# date and time arithmetic.
-
1
def months
-
ActiveSupport::Duration.new(self * 30.days, [[:months, self]])
-
end
-
1
alias :month :months
-
-
1
def years
-
ActiveSupport::Duration.new(self * 365.25.days, [[:years, self]])
-
end
-
1
alias :year :years
-
end
-
1
require 'active_support/core_ext/kernel/reporting'
-
1
require 'active_support/core_ext/kernel/agnostics'
-
1
require 'active_support/core_ext/kernel/debugger'
-
1
require 'active_support/core_ext/kernel/singleton_class'
-
1
class Object
-
# Makes backticks behave (somewhat more) similarly on all platforms.
-
# On win32 `nonexistent_command` raises Errno::ENOENT; on Unix, the
-
# spawned shell prints a message to stderr and sets $?. We emulate
-
# Unix on the former but not the latter.
-
1
def `(command) #:nodoc:
-
super
-
rescue Errno::ENOENT => e
-
STDERR.puts "#$0: #{e}"
-
end
-
end
-
1
module Kernel
-
1
unless respond_to?(:debugger)
-
# Starts a debugging session if the +debugger+ gem has been loaded (call rails server --debugger to do load it).
-
def debugger
-
message = "\n***** Debugger requested, but was not available (ensure the debugger gem is listed in Gemfile/installed as gem): Start server with --debugger to enable *****\n"
-
defined?(Rails) ? Rails.logger.info(message) : $stderr.puts(message)
-
end
-
alias breakpoint debugger unless respond_to?(:breakpoint)
-
end
-
end
-
1
module Kernel
-
# class_eval on an object acts like singleton_class.class_eval.
-
1
def class_eval(*args, &block)
-
singleton_class.class_eval(*args, &block)
-
end
-
end
-
1
class LoadError
-
1
REGEXPS = [
-
/^no such file to load -- (.+)$/i,
-
/^Missing \w+ (?:file\s*)?([^\s]+.rb)$/i,
-
/^Missing API definition file in (.+)$/i,
-
/^cannot load such file -- (.+)$/i,
-
]
-
-
1
unless method_defined?(:path)
-
def path
-
@path ||= begin
-
REGEXPS.find do |regex|
-
message =~ regex
-
end
-
$1
-
end
-
end
-
end
-
-
1
def is_missing?(location)
-
2
location.sub(/\.rb$/, '') == path.sub(/\.rb$/, '')
-
end
-
end
-
-
1
MissingSourceFile = LoadError
-
1
require 'active_support/core_ext/module/aliasing'
-
-
1
module Marshal
-
1
class << self
-
1
def load_with_autoloading(source)
-
load_without_autoloading(source)
-
rescue ArgumentError, NameError => exc
-
if exc.message.match(%r|undefined class/module (.+)|)
-
# try loading the class/module
-
$1.constantize
-
# if it is a IO we need to go back to read the object
-
source.rewind if source.respond_to?(:rewind)
-
retry
-
else
-
raise exc
-
end
-
end
-
-
1
alias_method_chain :load, :autoloading
-
end
-
end
-
1
require 'active_support/core_ext/module/aliasing'
-
1
require 'active_support/core_ext/module/introspection'
-
1
require 'active_support/core_ext/module/anonymous'
-
1
require 'active_support/core_ext/module/reachable'
-
1
require 'active_support/core_ext/module/attribute_accessors'
-
1
require 'active_support/core_ext/module/attr_internal'
-
1
require 'active_support/core_ext/module/concerning'
-
1
require 'active_support/core_ext/module/delegation'
-
1
require 'active_support/core_ext/module/deprecation'
-
1
require 'active_support/core_ext/module/remove_method'
-
1
require 'active_support/core_ext/module/qualified_const'
-
1
class Module
-
# Encapsulates the common pattern of:
-
#
-
# alias_method :foo_without_feature, :foo
-
# alias_method :foo, :foo_with_feature
-
#
-
# With this, you simply do:
-
#
-
# alias_method_chain :foo, :feature
-
#
-
# And both aliases are set up for you.
-
#
-
# Query and bang methods (foo?, foo!) keep the same punctuation:
-
#
-
# alias_method_chain :foo?, :feature
-
#
-
# is equivalent to
-
#
-
# alias_method :foo_without_feature?, :foo?
-
# alias_method :foo?, :foo_with_feature?
-
#
-
# so you can safely chain foo, foo?, and foo! with the same feature.
-
1
def alias_method_chain(target, feature)
-
# Strip out punctuation on predicates or bang methods since
-
# e.g. target?_without_feature is not a valid method name.
-
13
aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1
-
13
yield(aliased_target, punctuation) if block_given?
-
-
13
with_method = "#{aliased_target}_with_#{feature}#{punctuation}"
-
13
without_method = "#{aliased_target}_without_#{feature}#{punctuation}"
-
-
13
alias_method without_method, target
-
13
alias_method target, with_method
-
-
case
-
when public_method_defined?(without_method)
-
13
public target
-
when protected_method_defined?(without_method)
-
protected target
-
when private_method_defined?(without_method)
-
private target
-
13
end
-
end
-
-
# Allows you to make aliases for attributes, which includes
-
# getter, setter, and query methods.
-
#
-
# class Content < ActiveRecord::Base
-
# # has a title attribute
-
# end
-
#
-
# class Email < Content
-
# alias_attribute :subject, :title
-
# end
-
#
-
# e = Email.find(1)
-
# e.title # => "Superstars"
-
# e.subject # => "Superstars"
-
# e.subject? # => true
-
# e.subject = "Megastars"
-
# e.title # => "Megastars"
-
1
def alias_attribute(new_name, old_name)
-
module_eval <<-STR, __FILE__, __LINE__ + 1
-
def #{new_name}; self.#{old_name}; end # def subject; self.title; end
-
def #{new_name}?; self.#{old_name}?; end # def subject?; self.title?; end
-
def #{new_name}=(v); self.#{old_name} = v; end # def subject=(v); self.title = v; end
-
STR
-
end
-
end
-
1
class Module
-
# A module may or may not have a name.
-
#
-
# module M; end
-
# M.name # => "M"
-
#
-
# m = Module.new
-
# m.name # => nil
-
#
-
# A module gets a name when it is first assigned to a constant. Either
-
# via the +module+ or +class+ keyword or by an explicit assignment:
-
#
-
# m = Module.new # creates an anonymous module
-
# M = m # => m gets a name here as a side-effect
-
# m.name # => "M"
-
1
def anonymous?
-
42
name.nil?
-
end
-
end
-
1
class Module
-
# Declares an attribute reader backed by an internally-named instance variable.
-
1
def attr_internal_reader(*attrs)
-
22
attrs.each {|attr_name| attr_internal_define(attr_name, :reader)}
-
end
-
-
# Declares an attribute writer backed by an internally-named instance variable.
-
1
def attr_internal_writer(*attrs)
-
28
attrs.each {|attr_name| attr_internal_define(attr_name, :writer)}
-
end
-
-
# Declares an attribute reader and writer backed by an internally-named instance
-
# variable.
-
1
def attr_internal_accessor(*attrs)
-
9
attr_internal_reader(*attrs)
-
9
attr_internal_writer(*attrs)
-
end
-
1
alias_method :attr_internal, :attr_internal_accessor
-
-
2
class << self; attr_accessor :attr_internal_naming_format end
-
1
self.attr_internal_naming_format = '@_%s'
-
-
1
private
-
1
def attr_internal_ivar_name(attr)
-
29
Module.attr_internal_naming_format % attr
-
end
-
-
1
def attr_internal_define(attr_name, type)
-
29
internal_name = attr_internal_ivar_name(attr_name).sub(/\A@/, '')
-
# class_eval is necessary on 1.9 or else the methods are made private
-
29
class_eval do
-
# use native attr_* methods as they are faster on some Ruby implementations
-
29
send("attr_#{type}", internal_name)
-
end
-
29
attr_name, internal_name = "#{attr_name}=", "#{internal_name}=" if type == :writer
-
29
alias_method attr_name, internal_name
-
29
remove_method internal_name
-
end
-
end
-
1
require 'active_support/core_ext/array/extract_options'
-
-
# Extends the module object with class/module and instance accessors for
-
# class/module attributes, just like the native attr* accessors for instance
-
# attributes.
-
1
class Module
-
# Defines a class attribute and creates a class and instance reader methods.
-
# The underlying the class variable is set to +nil+, if it is not previously
-
# defined.
-
#
-
# module HairColors
-
# mattr_reader :hair_colors
-
# end
-
#
-
# HairColors.hair_colors # => nil
-
# HairColors.class_variable_set("@@hair_colors", [:brown, :black])
-
# HairColors.hair_colors # => [:brown, :black]
-
#
-
# The attribute name must be a valid method name in Ruby.
-
#
-
# module Foo
-
# mattr_reader :"1_Badname "
-
# end
-
# # => NameError: invalid attribute name
-
#
-
# If you want to opt out the creation on the instance reader method, pass
-
# <tt>instance_reader: false</tt> or <tt>instance_accessor: false</tt>.
-
#
-
# module HairColors
-
# mattr_writer :hair_colors, instance_reader: false
-
# end
-
#
-
# class Person
-
# include HairColors
-
# end
-
#
-
# Person.new.hair_colors # => NoMethodError
-
#
-
#
-
# Also, you can pass a block to set up the attribute with a default value.
-
#
-
# module HairColors
-
# cattr_reader :hair_colors do
-
# [:brown, :black, :blonde, :red]
-
# end
-
# end
-
#
-
# class Person
-
# include HairColors
-
# end
-
#
-
# Person.hair_colors # => [:brown, :black, :blonde, :red]
-
1
def mattr_reader(*syms)
-
54
options = syms.extract_options!
-
54
syms.each do |sym|
-
54
raise NameError.new("invalid attribute name: #{sym}") unless sym =~ /^[_A-Za-z]\w*$/
-
54
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
-
@@#{sym} = nil unless defined? @@#{sym}
-
-
def self.#{sym}
-
@@#{sym}
-
end
-
EOS
-
-
54
unless options[:instance_reader] == false || options[:instance_accessor] == false
-
51
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
-
def #{sym}
-
@@#{sym}
-
end
-
EOS
-
end
-
54
class_variable_set("@@#{sym}", yield) if block_given?
-
end
-
end
-
1
alias :cattr_reader :mattr_reader
-
-
# Defines a class attribute and creates a class and instance writer methods to
-
# allow assignment to the attribute.
-
#
-
# module HairColors
-
# mattr_writer :hair_colors
-
# end
-
#
-
# class Person
-
# include HairColors
-
# end
-
#
-
# HairColors.hair_colors = [:brown, :black]
-
# Person.class_variable_get("@@hair_colors") # => [:brown, :black]
-
# Person.new.hair_colors = [:blonde, :red]
-
# HairColors.class_variable_get("@@hair_colors") # => [:blonde, :red]
-
#
-
# If you want to opt out the instance writer method, pass
-
# <tt>instance_writer: false</tt> or <tt>instance_accessor: false</tt>.
-
#
-
# module HairColors
-
# mattr_writer :hair_colors, instance_writer: false
-
# end
-
#
-
# class Person
-
# include HairColors
-
# end
-
#
-
# Person.new.hair_colors = [:blonde, :red] # => NoMethodError
-
#
-
# Also, you can pass a block to set up the attribute with a default value.
-
#
-
# class HairColors
-
# mattr_writer :hair_colors do
-
# [:brown, :black, :blonde, :red]
-
# end
-
# end
-
#
-
# class Person
-
# include HairColors
-
# end
-
#
-
# Person.class_variable_get("@@hair_colors") # => [:brown, :black, :blonde, :red]
-
1
def mattr_writer(*syms)
-
54
options = syms.extract_options!
-
54
syms.each do |sym|
-
54
raise NameError.new("invalid attribute name: #{sym}") unless sym =~ /^[_A-Za-z]\w*$/
-
54
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
-
@@#{sym} = nil unless defined? @@#{sym}
-
-
def self.#{sym}=(obj)
-
@@#{sym} = obj
-
end
-
EOS
-
-
54
unless options[:instance_writer] == false || options[:instance_accessor] == false
-
42
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
-
def #{sym}=(obj)
-
@@#{sym} = obj
-
end
-
EOS
-
end
-
54
send("#{sym}=", yield) if block_given?
-
end
-
end
-
1
alias :cattr_writer :mattr_writer
-
-
# Defines both class and instance accessors for class attributes.
-
#
-
# module HairColors
-
# mattr_accessor :hair_colors
-
# end
-
#
-
# class Person
-
# include HairColors
-
# end
-
#
-
# Person.hair_colors = [:brown, :black, :blonde, :red]
-
# Person.hair_colors # => [:brown, :black, :blonde, :red]
-
# Person.new.hair_colors # => [:brown, :black, :blonde, :red]
-
#
-
# If a subclass changes the value then that would also change the value for
-
# parent class. Similarly if parent class changes the value then that would
-
# change the value of subclasses too.
-
#
-
# class Male < Person
-
# end
-
#
-
# Male.hair_colors << :blue
-
# Person.hair_colors # => [:brown, :black, :blonde, :red, :blue]
-
#
-
# To opt out of the instance writer method, pass <tt>instance_writer: false</tt>.
-
# To opt out of the instance reader method, pass <tt>instance_reader: false</tt>.
-
#
-
# module HairColors
-
# mattr_accessor :hair_colors, instance_writer: false, instance_reader: false
-
# end
-
#
-
# class Person
-
# include HairColors
-
# end
-
#
-
# Person.new.hair_colors = [:brown] # => NoMethodError
-
# Person.new.hair_colors # => NoMethodError
-
#
-
# Or pass <tt>instance_accessor: false</tt>, to opt out both instance methods.
-
#
-
# module HairColors
-
# mattr_accessor :hair_colors, instance_accessor: false
-
# end
-
#
-
# class Person
-
# include HairColors
-
# end
-
#
-
# Person.new.hair_colors = [:brown] # => NoMethodError
-
# Person.new.hair_colors # => NoMethodError
-
#
-
# Also you can pass a block to set up the attribute with a default value.
-
#
-
# module HairColors
-
# mattr_accessor :hair_colors do
-
# [:brown, :black, :blonde, :red]
-
# end
-
# end
-
#
-
# class Person
-
# include HairColors
-
# end
-
#
-
# Person.class_variable_get("@@hair_colors") #=> [:brown, :black, :blonde, :red]
-
1
def mattr_accessor(*syms, &blk)
-
53
mattr_reader(*syms, &blk)
-
53
mattr_writer(*syms, &blk)
-
end
-
1
alias :cattr_accessor :mattr_accessor
-
end
-
1
require 'active_support/concern'
-
-
1
class Module
-
# = Bite-sized separation of concerns
-
#
-
# We often find ourselves with a medium-sized chunk of behavior that we'd
-
# like to extract, but only mix in to a single class.
-
#
-
# Extracting a plain old Ruby object to encapsulate it and collaborate or
-
# delegate to the original object is often a good choice, but when there's
-
# no additional state to encapsulate or we're making DSL-style declarations
-
# about the parent class, introducing new collaborators can obfuscate rather
-
# than simplify.
-
#
-
# The typical route is to just dump everything in a monolithic class, perhaps
-
# with a comment, as a least-bad alternative. Using modules in separate files
-
# means tedious sifting to get a big-picture view.
-
#
-
# = Dissatisfying ways to separate small concerns
-
#
-
# == Using comments:
-
#
-
# class Todo
-
# # Other todo implementation
-
# # ...
-
#
-
# ## Event tracking
-
# has_many :events
-
#
-
# before_create :track_creation
-
# after_destroy :track_deletion
-
#
-
# private
-
# def track_creation
-
# # ...
-
# end
-
# end
-
#
-
# == With an inline module:
-
#
-
# Noisy syntax.
-
#
-
# class Todo
-
# # Other todo implementation
-
# # ...
-
#
-
# module EventTracking
-
# extend ActiveSupport::Concern
-
#
-
# included do
-
# has_many :events
-
# before_create :track_creation
-
# after_destroy :track_deletion
-
# end
-
#
-
# private
-
# def track_creation
-
# # ...
-
# end
-
# end
-
# include EventTracking
-
# end
-
#
-
# == Mix-in noise exiled to its own file:
-
#
-
# Once our chunk of behavior starts pushing the scroll-to-understand it's
-
# boundary, we give in and move it to a separate file. At this size, the
-
# overhead feels in good proportion to the size of our extraction, despite
-
# diluting our at-a-glance sense of how things really work.
-
#
-
# class Todo
-
# # Other todo implementation
-
# # ...
-
#
-
# include TodoEventTracking
-
# end
-
#
-
# = Introducing Module#concerning
-
#
-
# By quieting the mix-in noise, we arrive at a natural, low-ceremony way to
-
# separate bite-sized concerns.
-
#
-
# class Todo
-
# # Other todo implementation
-
# # ...
-
#
-
# concerning :EventTracking do
-
# included do
-
# has_many :events
-
# before_create :track_creation
-
# after_destroy :track_deletion
-
# end
-
#
-
# private
-
# def track_creation
-
# # ...
-
# end
-
# end
-
# end
-
#
-
# Todo.ancestors
-
# # => Todo, Todo::EventTracking, Object
-
#
-
# This small step has some wonderful ripple effects. We can
-
# * grok the behavior of our class in one glance,
-
# * clean up monolithic junk-drawer classes by separating their concerns, and
-
# * stop leaning on protected/private for crude "this is internal stuff" modularity.
-
1
module Concerning
-
# Define a new concern and mix it in.
-
1
def concerning(topic, &block)
-
include concern(topic, &block)
-
end
-
-
# A low-cruft shortcut to define a concern.
-
#
-
# concern :EventTracking do
-
# ...
-
# end
-
#
-
# is equivalent to
-
#
-
# module EventTracking
-
# extend ActiveSupport::Concern
-
#
-
# ...
-
# end
-
1
def concern(topic, &module_definition)
-
const_set topic, Module.new {
-
extend ::ActiveSupport::Concern
-
module_eval(&module_definition)
-
}
-
end
-
end
-
1
include Concerning
-
end
-
1
class Module
-
# Error generated by +delegate+ when a method is called on +nil+ and +allow_nil+
-
# option is not used.
-
1
class DelegationError < NoMethodError; end
-
-
# Provides a +delegate+ class method to easily expose contained objects'
-
# public methods as your own.
-
#
-
# ==== Options
-
# * <tt>:to</tt> - Specifies the target object
-
# * <tt>:prefix</tt> - Prefixes the new method with the target name or a custom prefix
-
# * <tt>:allow_nil</tt> - if set to true, prevents a +NoMethodError+ to be raised
-
#
-
# The macro receives one or more method names (specified as symbols or
-
# strings) and the name of the target object via the <tt>:to</tt> option
-
# (also a symbol or string).
-
#
-
# Delegation is particularly useful with Active Record associations:
-
#
-
# class Greeter < ActiveRecord::Base
-
# def hello
-
# 'hello'
-
# end
-
#
-
# def goodbye
-
# 'goodbye'
-
# end
-
# end
-
#
-
# class Foo < ActiveRecord::Base
-
# belongs_to :greeter
-
# delegate :hello, to: :greeter
-
# end
-
#
-
# Foo.new.hello # => "hello"
-
# Foo.new.goodbye # => NoMethodError: undefined method `goodbye' for #<Foo:0x1af30c>
-
#
-
# Multiple delegates to the same target are allowed:
-
#
-
# class Foo < ActiveRecord::Base
-
# belongs_to :greeter
-
# delegate :hello, :goodbye, to: :greeter
-
# end
-
#
-
# Foo.new.goodbye # => "goodbye"
-
#
-
# Methods can be delegated to instance variables, class variables, or constants
-
# by providing them as a symbols:
-
#
-
# class Foo
-
# CONSTANT_ARRAY = [0,1,2,3]
-
# @@class_array = [4,5,6,7]
-
#
-
# def initialize
-
# @instance_array = [8,9,10,11]
-
# end
-
# delegate :sum, to: :CONSTANT_ARRAY
-
# delegate :min, to: :@@class_array
-
# delegate :max, to: :@instance_array
-
# end
-
#
-
# Foo.new.sum # => 6
-
# Foo.new.min # => 4
-
# Foo.new.max # => 11
-
#
-
# It's also possible to delegate a method to the class by using +:class+:
-
#
-
# class Foo
-
# def self.hello
-
# "world"
-
# end
-
#
-
# delegate :hello, to: :class
-
# end
-
#
-
# Foo.new.hello # => "world"
-
#
-
# Delegates can optionally be prefixed using the <tt>:prefix</tt> option. If the value
-
# is <tt>true</tt>, the delegate methods are prefixed with the name of the object being
-
# delegated to.
-
#
-
# Person = Struct.new(:name, :address)
-
#
-
# class Invoice < Struct.new(:client)
-
# delegate :name, :address, to: :client, prefix: true
-
# end
-
#
-
# john_doe = Person.new('John Doe', 'Vimmersvej 13')
-
# invoice = Invoice.new(john_doe)
-
# invoice.client_name # => "John Doe"
-
# invoice.client_address # => "Vimmersvej 13"
-
#
-
# It is also possible to supply a custom prefix.
-
#
-
# class Invoice < Struct.new(:client)
-
# delegate :name, :address, to: :client, prefix: :customer
-
# end
-
#
-
# invoice = Invoice.new(john_doe)
-
# invoice.customer_name # => 'John Doe'
-
# invoice.customer_address # => 'Vimmersvej 13'
-
#
-
# If the target is +nil+ and does not respond to the delegated method a
-
# +NoMethodError+ is raised, as with any other value. Sometimes, however, it
-
# makes sense to be robust to that situation and that is the purpose of the
-
# <tt>:allow_nil</tt> option: If the target is not +nil+, or it is and
-
# responds to the method, everything works as usual. But if it is +nil+ and
-
# does not respond to the delegated method, +nil+ is returned.
-
#
-
# class User < ActiveRecord::Base
-
# has_one :profile
-
# delegate :age, to: :profile
-
# end
-
#
-
# User.new.age # raises NoMethodError: undefined method `age'
-
#
-
# But if not having a profile yet is fine and should not be an error
-
# condition:
-
#
-
# class User < ActiveRecord::Base
-
# has_one :profile
-
# delegate :age, to: :profile, allow_nil: true
-
# end
-
#
-
# User.new.age # nil
-
#
-
# Note that if the target is not +nil+ then the call is attempted regardless of the
-
# <tt>:allow_nil</tt> option, and thus an exception is still raised if said object
-
# does not respond to the method:
-
#
-
# class Foo
-
# def initialize(bar)
-
# @bar = bar
-
# end
-
#
-
# delegate :name, to: :@bar, allow_nil: true
-
# end
-
#
-
# Foo.new("Bar").name # raises NoMethodError: undefined method `name'
-
#
-
# The target method must be public, otherwise it will raise +NoMethodError+.
-
#
-
1
def delegate(*methods)
-
74
options = methods.pop
-
74
unless options.is_a?(Hash) && to = options[:to]
-
raise ArgumentError, 'Delegation needs a target. Supply an options hash with a :to key as the last argument (e.g. delegate :hello, to: :greeter).'
-
end
-
-
74
prefix, allow_nil = options.values_at(:prefix, :allow_nil)
-
-
74
if prefix == true && to =~ /^[^a-z_]/
-
raise ArgumentError, 'Can only automatically set the delegation prefix when delegating to a method.'
-
end
-
-
74
method_prefix = \
-
if prefix
-
"#{prefix == true ? to : prefix}_"
-
else
-
74
''
-
end
-
-
74
file, line = caller.first.split(':', 2)
-
74
line = line.to_i
-
-
74
to = to.to_s
-
74
to = 'self.class' if to == 'class'
-
-
74
methods.each do |method|
-
# Attribute writer methods only accept one argument. Makes sure []=
-
# methods still accept two arguments.
-
236
definition = (method =~ /[^\]]=$/) ? 'arg' : '*args, &block'
-
-
# The following generated methods call the target exactly once, storing
-
# the returned value in a dummy variable.
-
#
-
# Reason is twofold: On one hand doing less calls is in general better.
-
# On the other hand it could be that the target has side-effects,
-
# whereas conceptually, from the user point of view, the delegator should
-
# be doing one call.
-
236
if allow_nil
-
6
method_def = [
-
"def #{method_prefix}#{method}(#{definition})", # def customer_name(*args, &block)
-
"_ = #{to}", # _ = client
-
"if !_.nil? || nil.respond_to?(:#{method})", # if !_.nil? || nil.respond_to?(:name)
-
" _.#{method}(#{definition})", # _.name(*args, &block)
-
"end", # end
-
"end" # end
-
].join ';'
-
else
-
230
exception = %(raise DelegationError, "#{self}##{method_prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}")
-
-
230
method_def = [
-
"def #{method_prefix}#{method}(#{definition})", # def customer_name(*args, &block)
-
" _ = #{to}", # _ = client
-
" _.#{method}(#{definition})", # _.name(*args, &block)
-
"rescue NoMethodError => e", # rescue NoMethodError => e
-
" if _.nil? && e.name == :#{method}", # if _.nil? && e.name == :name
-
" #{exception}", # # add helpful message to the exception
-
" else", # else
-
" raise", # raise
-
" end", # end
-
"end" # end
-
].join ';'
-
end
-
-
236
module_eval(method_def, file, line)
-
end
-
end
-
end
-
1
class Module
-
# deprecate :foo
-
# deprecate bar: 'message'
-
# deprecate :foo, :bar, baz: 'warning!', qux: 'gone!'
-
#
-
# You can also use custom deprecator instance:
-
#
-
# deprecate :foo, deprecator: MyLib::Deprecator.new
-
# deprecate :foo, bar: "warning!", deprecator: MyLib::Deprecator.new
-
#
-
# \Custom deprecators must respond to <tt>deprecation_warning(deprecated_method_name, message, caller_backtrace)</tt>
-
# method where you can implement your custom warning behavior.
-
#
-
# class MyLib::Deprecator
-
# def deprecation_warning(deprecated_method_name, message, caller_backtrace = nil)
-
# message = "#{deprecated_method_name} is deprecated and will be removed from MyLibrary | #{message}"
-
# Kernel.warn message
-
# end
-
# end
-
1
def deprecate(*method_names)
-
ActiveSupport::Deprecation.deprecate_methods(self, *method_names)
-
end
-
end
-
1
require 'active_support/inflector'
-
-
1
class Module
-
# Returns the name of the module containing this one.
-
#
-
# M::N.parent_name # => "M"
-
1
def parent_name
-
36
if defined? @parent_name
-
29
@parent_name
-
else
-
7
@parent_name = name =~ /::[^:]+\Z/ ? $`.freeze : nil
-
end
-
end
-
-
# Returns the module which contains this one according to its name.
-
#
-
# module M
-
# module N
-
# end
-
# end
-
# X = M::N
-
#
-
# M::N.parent # => M
-
# X.parent # => M
-
#
-
# The parent of top-level and anonymous modules is Object.
-
#
-
# M.parent # => Object
-
# Module.new.parent # => Object
-
1
def parent
-
14
parent_name ? ActiveSupport::Inflector.constantize(parent_name) : Object
-
end
-
-
# Returns all the parents of this module according to its name, ordered from
-
# nested outwards. The receiver is not contained within the result.
-
#
-
# module M
-
# module N
-
# end
-
# end
-
# X = M::N
-
#
-
# M.parents # => [Object]
-
# M::N.parents # => [M, Object]
-
# X.parents # => [M, Object]
-
1
def parents
-
20
parents = []
-
20
if parent_name
-
2
parts = parent_name.split('::')
-
2
until parts.empty?
-
4
parents << ActiveSupport::Inflector.constantize(parts * '::')
-
4
parts.pop
-
end
-
end
-
20
parents << Object unless parents.include? Object
-
20
parents
-
end
-
-
1
def local_constants #:nodoc:
-
constants(false)
-
end
-
end
-
1
class Module
-
###
-
# TODO: remove this after 1.9 support is dropped
-
1
def methods_transplantable? # :nodoc:
-
4
x = Module.new { def foo; end }
-
4
Module.new { define_method :bar, x.instance_method(:foo) }
-
2
true
-
rescue TypeError
-
false
-
end
-
end
-
1
require 'active_support/core_ext/string/inflections'
-
-
#--
-
# Allows code reuse in the methods below without polluting Module.
-
#++
-
1
module QualifiedConstUtils
-
1
def self.raise_if_absolute(path)
-
16
raise NameError.new("wrong constant name #$&") if path =~ /\A::[^:]+/
-
end
-
-
1
def self.names(path)
-
16
path.split('::')
-
end
-
end
-
-
##
-
# Extends the API for constants to be able to deal with qualified names. Arguments
-
# are assumed to be relative to the receiver.
-
#
-
#--
-
# Qualified names are required to be relative because we are extending existing
-
# methods that expect constant names, ie, relative paths of length 1. For example,
-
# Object.const_get('::String') raises NameError and so does qualified_const_get.
-
#++
-
1
class Module
-
1
def qualified_const_defined?(path, search_parents=true)
-
16
QualifiedConstUtils.raise_if_absolute(path)
-
-
16
QualifiedConstUtils.names(path).inject(self) do |mod, name|
-
16
return unless mod.const_defined?(name, search_parents)
-
16
mod.const_get(name)
-
end
-
16
return true
-
end
-
-
1
def qualified_const_get(path)
-
QualifiedConstUtils.raise_if_absolute(path)
-
-
QualifiedConstUtils.names(path).inject(self) do |mod, name|
-
mod.const_get(name)
-
end
-
end
-
-
1
def qualified_const_set(path, value)
-
QualifiedConstUtils.raise_if_absolute(path)
-
-
const_name = path.demodulize
-
mod_name = path.deconstantize
-
mod = mod_name.empty? ? self : qualified_const_get(mod_name)
-
mod.const_set(const_name, value)
-
end
-
end
-
1
require 'active_support/core_ext/module/anonymous'
-
1
require 'active_support/core_ext/string/inflections'
-
-
1
class Module
-
1
def reachable? #:nodoc:
-
!anonymous? && name.safe_constantize.equal?(self)
-
end
-
end
-
1
class Module
-
1
def remove_possible_method(method)
-
536
if method_defined?(method) || private_method_defined?(method)
-
304
undef_method(method)
-
end
-
end
-
-
1
def redefine_method(method, &block)
-
3
remove_possible_method(method)
-
3
define_method(method, &block)
-
end
-
end
-
1
class NameError
-
# Extract the name of the missing constant from the exception message.
-
1
def missing_name
-
if /undefined local variable or method/ !~ message
-
$1 if /((::)?([A-Z]\w*)(::[A-Z]\w*)*)$/ =~ message
-
end
-
end
-
-
# Was this exception raised because the given name was missing?
-
1
def missing_name?(name)
-
if name.is_a? Symbol
-
last_name = (missing_name || '').split('::').last
-
last_name == name.to_s
-
else
-
missing_name == name.to_s
-
end
-
end
-
end
-
1
require 'active_support/core_ext/numeric/bytes'
-
1
require 'active_support/core_ext/numeric/time'
-
1
require 'active_support/core_ext/numeric/conversions'
-
1
class Numeric
-
1
KILOBYTE = 1024
-
1
MEGABYTE = KILOBYTE * 1024
-
1
GIGABYTE = MEGABYTE * 1024
-
1
TERABYTE = GIGABYTE * 1024
-
1
PETABYTE = TERABYTE * 1024
-
1
EXABYTE = PETABYTE * 1024
-
-
# Enables the use of byte calculations and declarations, like 45.bytes + 2.6.megabytes
-
1
def bytes
-
self
-
end
-
1
alias :byte :bytes
-
-
1
def kilobytes
-
1
self * KILOBYTE
-
end
-
1
alias :kilobyte :kilobytes
-
-
1
def megabytes
-
self * MEGABYTE
-
end
-
1
alias :megabyte :megabytes
-
-
1
def gigabytes
-
self * GIGABYTE
-
end
-
1
alias :gigabyte :gigabytes
-
-
1
def terabytes
-
self * TERABYTE
-
end
-
1
alias :terabyte :terabytes
-
-
1
def petabytes
-
self * PETABYTE
-
end
-
1
alias :petabyte :petabytes
-
-
1
def exabytes
-
self * EXABYTE
-
end
-
1
alias :exabyte :exabytes
-
end
-
1
require 'active_support/core_ext/big_decimal/conversions'
-
1
require 'active_support/number_helper'
-
-
1
class Numeric
-
-
# Provides options for converting numbers into formatted strings.
-
# Options are provided for phone numbers, currency, percentage,
-
# precision, positional notation, file size and pretty printing.
-
#
-
# ==== Options
-
#
-
# For details on which formats use which options, see ActiveSupport::NumberHelper
-
#
-
# ==== Examples
-
#
-
# Phone Numbers:
-
# 5551234.to_s(:phone) # => 555-1234
-
# 1235551234.to_s(:phone) # => 123-555-1234
-
# 1235551234.to_s(:phone, area_code: true) # => (123) 555-1234
-
# 1235551234.to_s(:phone, delimiter: ' ') # => 123 555 1234
-
# 1235551234.to_s(:phone, area_code: true, extension: 555) # => (123) 555-1234 x 555
-
# 1235551234.to_s(:phone, country_code: 1) # => +1-123-555-1234
-
# 1235551234.to_s(:phone, country_code: 1, extension: 1343, delimiter: '.')
-
# # => +1.123.555.1234 x 1343
-
#
-
# Currency:
-
# 1234567890.50.to_s(:currency) # => $1,234,567,890.50
-
# 1234567890.506.to_s(:currency) # => $1,234,567,890.51
-
# 1234567890.506.to_s(:currency, precision: 3) # => $1,234,567,890.506
-
# 1234567890.506.to_s(:currency, locale: :fr) # => 1 234 567 890,51 €
-
# -1234567890.50.to_s(:currency, negative_format: '(%u%n)')
-
# # => ($1,234,567,890.50)
-
# 1234567890.50.to_s(:currency, unit: '£', separator: ',', delimiter: '')
-
# # => £1234567890,50
-
# 1234567890.50.to_s(:currency, unit: '£', separator: ',', delimiter: '', format: '%n %u')
-
# # => 1234567890,50 £
-
#
-
# Percentage:
-
# 100.to_s(:percentage) # => 100.000%
-
# 100.to_s(:percentage, precision: 0) # => 100%
-
# 1000.to_s(:percentage, delimiter: '.', separator: ',') # => 1.000,000%
-
# 302.24398923423.to_s(:percentage, precision: 5) # => 302.24399%
-
# 1000.to_s(:percentage, locale: :fr) # => 1 000,000%
-
# 100.to_s(:percentage, format: '%n %') # => 100 %
-
#
-
# Delimited:
-
# 12345678.to_s(:delimited) # => 12,345,678
-
# 12345678.05.to_s(:delimited) # => 12,345,678.05
-
# 12345678.to_s(:delimited, delimiter: '.') # => 12.345.678
-
# 12345678.to_s(:delimited, delimiter: ',') # => 12,345,678
-
# 12345678.05.to_s(:delimited, separator: ' ') # => 12,345,678 05
-
# 12345678.05.to_s(:delimited, locale: :fr) # => 12 345 678,05
-
# 98765432.98.to_s(:delimited, delimiter: ' ', separator: ',')
-
# # => 98 765 432,98
-
#
-
# Rounded:
-
# 111.2345.to_s(:rounded) # => 111.235
-
# 111.2345.to_s(:rounded, precision: 2) # => 111.23
-
# 13.to_s(:rounded, precision: 5) # => 13.00000
-
# 389.32314.to_s(:rounded, precision: 0) # => 389
-
# 111.2345.to_s(:rounded, significant: true) # => 111
-
# 111.2345.to_s(:rounded, precision: 1, significant: true) # => 100
-
# 13.to_s(:rounded, precision: 5, significant: true) # => 13.000
-
# 111.234.to_s(:rounded, locale: :fr) # => 111,234
-
# 13.to_s(:rounded, precision: 5, significant: true, strip_insignificant_zeros: true)
-
# # => 13
-
# 389.32314.to_s(:rounded, precision: 4, significant: true) # => 389.3
-
# 1111.2345.to_s(:rounded, precision: 2, separator: ',', delimiter: '.')
-
# # => 1.111,23
-
#
-
# Human-friendly size in Bytes:
-
# 123.to_s(:human_size) # => 123 Bytes
-
# 1234.to_s(:human_size) # => 1.21 KB
-
# 12345.to_s(:human_size) # => 12.1 KB
-
# 1234567.to_s(:human_size) # => 1.18 MB
-
# 1234567890.to_s(:human_size) # => 1.15 GB
-
# 1234567890123.to_s(:human_size) # => 1.12 TB
-
# 1234567.to_s(:human_size, precision: 2) # => 1.2 MB
-
# 483989.to_s(:human_size, precision: 2) # => 470 KB
-
# 1234567.to_s(:human_size, precision: 2, separator: ',') # => 1,2 MB
-
# 1234567890123.to_s(:human_size, precision: 5) # => "1.1229 TB"
-
# 524288000.to_s(:human_size, precision: 5) # => "500 MB"
-
#
-
# Human-friendly format:
-
# 123.to_s(:human) # => "123"
-
# 1234.to_s(:human) # => "1.23 Thousand"
-
# 12345.to_s(:human) # => "12.3 Thousand"
-
# 1234567.to_s(:human) # => "1.23 Million"
-
# 1234567890.to_s(:human) # => "1.23 Billion"
-
# 1234567890123.to_s(:human) # => "1.23 Trillion"
-
# 1234567890123456.to_s(:human) # => "1.23 Quadrillion"
-
# 1234567890123456789.to_s(:human) # => "1230 Quadrillion"
-
# 489939.to_s(:human, precision: 2) # => "490 Thousand"
-
# 489939.to_s(:human, precision: 4) # => "489.9 Thousand"
-
# 1234567.to_s(:human, precision: 4,
-
# significant: false) # => "1.2346 Million"
-
# 1234567.to_s(:human, precision: 1,
-
# separator: ',',
-
# significant: false) # => "1,2 Million"
-
1
def to_formatted_s(format = :default, options = {})
-
case format
-
when :phone
-
return ActiveSupport::NumberHelper.number_to_phone(self, options)
-
when :currency
-
return ActiveSupport::NumberHelper.number_to_currency(self, options)
-
when :percentage
-
return ActiveSupport::NumberHelper.number_to_percentage(self, options)
-
when :delimited
-
return ActiveSupport::NumberHelper.number_to_delimited(self, options)
-
when :rounded
-
return ActiveSupport::NumberHelper.number_to_rounded(self, options)
-
when :human
-
return ActiveSupport::NumberHelper.number_to_human(self, options)
-
when :human_size
-
return ActiveSupport::NumberHelper.number_to_human_size(self, options)
-
else
-
self.to_default_s
-
end
-
end
-
-
1
[Float, Fixnum, Bignum, BigDecimal].each do |klass|
-
4
klass.send(:alias_method, :to_default_s, :to_s)
-
-
4
klass.send(:define_method, :to_s) do |*args|
-
200
if args[0].is_a?(Symbol)
-
format = args[0]
-
options = args[1] || {}
-
-
self.to_formatted_s(format, options)
-
else
-
200
to_default_s(*args)
-
end
-
end
-
end
-
end
-
1
require 'active_support/duration'
-
1
require 'active_support/core_ext/time/calculations'
-
1
require 'active_support/core_ext/time/acts_like'
-
-
1
class Numeric
-
# Enables the use of time calculations and declarations, like 45.minutes + 2.hours + 4.years.
-
#
-
# These methods use Time#advance for precise date calculations when using from_now, ago, etc.
-
# as well as adding or subtracting their results from a Time object. For example:
-
#
-
# # equivalent to Time.current.advance(months: 1)
-
# 1.month.from_now
-
#
-
# # equivalent to Time.current.advance(years: 2)
-
# 2.years.from_now
-
#
-
# # equivalent to Time.current.advance(months: 4, years: 5)
-
# (4.months + 5.years).from_now
-
#
-
# While these methods provide precise calculation when used as in the examples above, care
-
# should be taken to note that this is not true if the result of `months', `years', etc is
-
# converted before use:
-
#
-
# # equivalent to 30.days.to_i.from_now
-
# 1.month.to_i.from_now
-
#
-
# # equivalent to 365.25.days.to_f.from_now
-
# 1.year.to_f.from_now
-
#
-
# In such cases, Ruby's core
-
# Date[http://ruby-doc.org/stdlib/libdoc/date/rdoc/Date.html] and
-
# Time[http://ruby-doc.org/stdlib/libdoc/time/rdoc/Time.html] should be used for precision
-
# date and time arithmetic.
-
1
def seconds
-
ActiveSupport::Duration.new(self, [[:seconds, self]])
-
end
-
1
alias :second :seconds
-
-
1
def minutes
-
ActiveSupport::Duration.new(self * 60, [[:seconds, self * 60]])
-
end
-
1
alias :minute :minutes
-
-
1
def hours
-
ActiveSupport::Duration.new(self * 3600, [[:seconds, self * 3600]])
-
end
-
1
alias :hour :hours
-
-
1
def days
-
ActiveSupport::Duration.new(self * 24.hours, [[:days, self]])
-
end
-
1
alias :day :days
-
-
1
def weeks
-
ActiveSupport::Duration.new(self * 7.days, [[:days, self * 7]])
-
end
-
1
alias :week :weeks
-
-
1
def fortnights
-
ActiveSupport::Duration.new(self * 2.weeks, [[:days, self * 14]])
-
end
-
1
alias :fortnight :fortnights
-
-
# Reads best without arguments: 10.minutes.ago
-
1
def ago(time = ::Time.current)
-
ActiveSupport::Deprecation.warn "Calling #ago or #until on a number (e.g. 5.ago) is deprecated and will be removed in the future, use 5.seconds.ago instead"
-
time - self
-
end
-
-
# Reads best with argument: 10.minutes.until(time)
-
1
alias :until :ago
-
-
# Reads best with argument: 10.minutes.since(time)
-
1
def since(time = ::Time.current)
-
ActiveSupport::Deprecation.warn "Calling #since or #from_now on a number (e.g. 5.since) is deprecated and will be removed in the future, use 5.seconds.since instead"
-
time + self
-
end
-
-
# Reads best without arguments: 10.minutes.from_now
-
1
alias :from_now :since
-
-
# Used with the standard time durations, like 1.hour.in_milliseconds --
-
# so we can feed them to JavaScript functions like getTime().
-
1
def in_milliseconds
-
self * 1000
-
end
-
end
-
1
require 'active_support/core_ext/object/acts_like'
-
1
require 'active_support/core_ext/object/blank'
-
1
require 'active_support/core_ext/object/duplicable'
-
1
require 'active_support/core_ext/object/deep_dup'
-
1
require 'active_support/core_ext/object/try'
-
1
require 'active_support/core_ext/object/inclusion'
-
-
1
require 'active_support/core_ext/object/conversions'
-
1
require 'active_support/core_ext/object/instance_variables'
-
-
1
require 'active_support/core_ext/object/json'
-
1
require 'active_support/core_ext/object/to_param'
-
1
require 'active_support/core_ext/object/to_query'
-
1
require 'active_support/core_ext/object/with_options'
-
1
class Object
-
# A duck-type assistant method. For example, Active Support extends Date
-
# to define an <tt>acts_like_date?</tt> method, and extends Time to define
-
# <tt>acts_like_time?</tt>. As a result, we can do <tt>x.acts_like?(:time)</tt> and
-
# <tt>x.acts_like?(:date)</tt> to do duck-type-safe comparisons, since classes that
-
# we want to act like Time simply need to define an <tt>acts_like_time?</tt> method.
-
1
def acts_like?(duck)
-
68
respond_to? :"acts_like_#{duck}?"
-
end
-
end
-
# encoding: utf-8
-
-
1
class Object
-
# An object is blank if it's false, empty, or a whitespace string.
-
# For example, '', ' ', +nil+, [], and {} are all blank.
-
#
-
# This simplifies
-
#
-
# address.nil? || address.empty?
-
#
-
# to
-
#
-
# address.blank?
-
#
-
# @return [true, false]
-
1
def blank?
-
89
respond_to?(:empty?) ? !!empty? : !self
-
end
-
-
# An object is present if it's not blank.
-
#
-
# @return [true, false]
-
1
def present?
-
397
!blank?
-
end
-
-
# Returns the receiver if it's present otherwise returns +nil+.
-
# <tt>object.presence</tt> is equivalent to
-
#
-
# object.present? ? object : nil
-
#
-
# For example, something like
-
#
-
# state = params[:state] if params[:state].present?
-
# country = params[:country] if params[:country].present?
-
# region = state || country || 'US'
-
#
-
# becomes
-
#
-
# region = params[:state].presence || params[:country].presence || 'US'
-
#
-
# @return [Object]
-
1
def presence
-
87
self if present?
-
end
-
end
-
-
1
class NilClass
-
# +nil+ is blank:
-
#
-
# nil.blank? # => true
-
#
-
# @return [true]
-
1
def blank?
-
396
true
-
end
-
end
-
-
1
class FalseClass
-
# +false+ is blank:
-
#
-
# false.blank? # => true
-
#
-
# @return [true]
-
1
def blank?
-
true
-
end
-
end
-
-
1
class TrueClass
-
# +true+ is not blank:
-
#
-
# true.blank? # => false
-
#
-
# @return [false]
-
1
def blank?
-
false
-
end
-
end
-
-
1
class Array
-
# An array is blank if it's empty:
-
#
-
# [].blank? # => true
-
# [1,2,3].blank? # => false
-
#
-
# @return [true, false]
-
1
alias_method :blank?, :empty?
-
end
-
-
1
class Hash
-
# A hash is blank if it's empty:
-
#
-
# {}.blank? # => true
-
# { key: 'value' }.blank? # => false
-
#
-
# @return [true, false]
-
1
alias_method :blank?, :empty?
-
end
-
-
1
class String
-
1
BLANK_RE = /\A[[:space:]]*\z/
-
-
# A string is blank if it's empty or contains whitespaces only:
-
#
-
# ''.blank? # => true
-
# ' '.blank? # => true
-
# "\t\n\r".blank? # => true
-
# ' blah '.blank? # => false
-
#
-
# Unicode whitespace is supported:
-
#
-
# "\u00a0".blank? # => true
-
#
-
# @return [true, false]
-
1
def blank?
-
511
BLANK_RE === self
-
end
-
end
-
-
1
class Numeric #:nodoc:
-
# No number is blank:
-
#
-
# 1.blank? # => false
-
# 0.blank? # => false
-
#
-
# @return [false]
-
1
def blank?
-
6
false
-
end
-
end
-
1
require 'active_support/core_ext/object/to_param'
-
1
require 'active_support/core_ext/object/to_query'
-
1
require 'active_support/core_ext/array/conversions'
-
1
require 'active_support/core_ext/hash/conversions'
-
1
require 'active_support/core_ext/object/duplicable'
-
-
1
class Object
-
# Returns a deep copy of object if it's duplicable. If it's
-
# not duplicable, returns +self+.
-
#
-
# object = Object.new
-
# dup = object.deep_dup
-
# dup.instance_variable_set(:@a, 1)
-
#
-
# object.instance_variable_defined?(:@a) # => false
-
# dup.instance_variable_defined?(:@a) # => true
-
1
def deep_dup
-
90
duplicable? ? dup : self
-
end
-
end
-
-
1
class Array
-
# Returns a deep copy of array.
-
#
-
# array = [1, [2, 3]]
-
# dup = array.deep_dup
-
# dup[1][2] = 4
-
#
-
# array[1][2] # => nil
-
# dup[1][2] # => 4
-
1
def deep_dup
-
map { |it| it.deep_dup }
-
end
-
end
-
-
1
class Hash
-
# Returns a deep copy of hash.
-
#
-
# hash = { a: { b: 'b' } }
-
# dup = hash.deep_dup
-
# dup[:a][:c] = 'c'
-
#
-
# hash[:a][:c] # => nil
-
# dup[:a][:c] # => "c"
-
1
def deep_dup
-
35
each_with_object(dup) do |(key, value), hash|
-
51
hash[key.deep_dup] = value.deep_dup
-
end
-
end
-
end
-
#--
-
# Most objects are cloneable, but not all. For example you can't dup +nil+:
-
#
-
# nil.dup # => TypeError: can't dup NilClass
-
#
-
# Classes may signal their instances are not duplicable removing +dup+/+clone+
-
# or raising exceptions from them. So, to dup an arbitrary object you normally
-
# use an optimistic approach and are ready to catch an exception, say:
-
#
-
# arbitrary_object.dup rescue object
-
#
-
# Rails dups objects in a few critical spots where they are not that arbitrary.
-
# That rescue is very expensive (like 40 times slower than a predicate), and it
-
# is often triggered.
-
#
-
# That's why we hardcode the following cases and check duplicable? instead of
-
# using that rescue idiom.
-
#++
-
1
class Object
-
# Can you safely dup this object?
-
#
-
# False for +nil+, +false+, +true+, symbol, and number objects;
-
# true otherwise.
-
1
def duplicable?
-
true
-
end
-
end
-
-
1
class NilClass
-
# +nil+ is not duplicable:
-
#
-
# nil.duplicable? # => false
-
# nil.dup # => TypeError: can't dup NilClass
-
1
def duplicable?
-
82
false
-
end
-
end
-
-
1
class FalseClass
-
# +false+ is not duplicable:
-
#
-
# false.duplicable? # => false
-
# false.dup # => TypeError: can't dup FalseClass
-
1
def duplicable?
-
false
-
end
-
end
-
-
1
class TrueClass
-
# +true+ is not duplicable:
-
#
-
# true.duplicable? # => false
-
# true.dup # => TypeError: can't dup TrueClass
-
1
def duplicable?
-
12
false
-
end
-
end
-
-
1
class Symbol
-
# Symbols are not duplicable:
-
#
-
# :my_symbol.duplicable? # => false
-
# :my_symbol.dup # => TypeError: can't dup Symbol
-
1
def duplicable?
-
78
false
-
end
-
end
-
-
1
class Numeric
-
# Numbers are not duplicable:
-
#
-
# 3.duplicable? # => false
-
# 3.dup # => TypeError: can't dup Fixnum
-
1
def duplicable?
-
4
false
-
end
-
end
-
-
1
require 'bigdecimal'
-
1
class BigDecimal
-
1
begin
-
1
BigDecimal.new('4.56').dup
-
-
1
def duplicable?
-
true
-
end
-
rescue TypeError
-
# can't dup, so use superclass implementation
-
end
-
end
-
1
class Object
-
# Returns true if this object is included in the argument. Argument must be
-
# any object which responds to +#include?+. Usage:
-
#
-
# characters = ["Konata", "Kagami", "Tsukasa"]
-
# "Konata".in?(characters) # => true
-
#
-
# This will throw an ArgumentError if the argument doesn't respond
-
# to +#include?+.
-
1
def in?(another_object)
-
another_object.include?(self)
-
rescue NoMethodError
-
raise ArgumentError.new("The parameter passed to #in? must respond to #include?")
-
end
-
-
# Returns the receiver if it's included in the argument otherwise returns +nil+.
-
# Argument must be any object which responds to +#include?+. Usage:
-
#
-
# params[:bucket_type].presence_in %w( project calendar )
-
#
-
# This will throw an ArgumentError if the argument doesn't respond to +#include?+.
-
#
-
# @return [Object]
-
1
def presence_in(another_object)
-
self.in?(another_object) ? self : nil
-
end
-
end
-
1
class Object
-
# Returns a hash with string keys that maps instance variable names without "@" to their
-
# corresponding values.
-
#
-
# class C
-
# def initialize(x, y)
-
# @x, @y = x, y
-
# end
-
# end
-
#
-
# C.new(0, 1).instance_values # => {"x" => 0, "y" => 1}
-
1
def instance_values
-
Hash[instance_variables.map { |name| [name[1..-1], instance_variable_get(name)] }]
-
end
-
-
# Returns an array of instance variable names as strings including "@".
-
#
-
# class C
-
# def initialize(x, y)
-
# @x, @y = x, y
-
# end
-
# end
-
#
-
# C.new(0, 1).instance_variable_names # => ["@y", "@x"]
-
1
def instance_variable_names
-
instance_variables.map { |var| var.to_s }
-
end
-
end
-
# Hack to load json gem first so we can overwrite its to_json.
-
1
require 'json'
-
1
require 'bigdecimal'
-
1
require 'active_support/core_ext/big_decimal/conversions' # for #to_s
-
1
require 'active_support/core_ext/hash/except'
-
1
require 'active_support/core_ext/hash/slice'
-
1
require 'active_support/core_ext/object/instance_variables'
-
1
require 'time'
-
1
require 'active_support/core_ext/time/conversions'
-
1
require 'active_support/core_ext/date_time/conversions'
-
1
require 'active_support/core_ext/date/conversions'
-
1
require 'active_support/core_ext/module/aliasing'
-
-
# The JSON gem adds a few modules to Ruby core classes containing :to_json definition, overwriting
-
# their default behavior. That said, we need to define the basic to_json method in all of them,
-
# otherwise they will always use to_json gem implementation, which is backwards incompatible in
-
# several cases (for instance, the JSON implementation for Hash does not work) with inheritance
-
# and consequently classes as ActiveSupport::OrderedHash cannot be serialized to json.
-
#
-
# On the other hand, we should avoid conflict with ::JSON.{generate,dump}(obj). Unfortunately, the
-
# JSON gem's encoder relies on its own to_json implementation to encode objects. Since it always
-
# passes a ::JSON::State object as the only argument to to_json, we can detect that and forward the
-
# calls to the original to_json method.
-
#
-
# It should be noted that when using ::JSON.{generate,dump} directly, ActiveSupport's encoder is
-
# bypassed completely. This means that as_json won't be invoked and the JSON gem will simply
-
# ignore any options it does not natively understand. This also means that ::JSON.{generate,dump}
-
# should give exactly the same results with or without active support.
-
1
[Object, Array, FalseClass, Float, Hash, Integer, NilClass, String, TrueClass].each do |klass|
-
9
klass.class_eval do
-
9
def to_json_with_active_support_encoder(options = nil)
-
if options.is_a?(::JSON::State)
-
# Called from JSON.{generate,dump}, forward it to JSON gem's to_json
-
self.to_json_without_active_support_encoder(options)
-
else
-
# to_json is being invoked directly, use ActiveSupport's encoder
-
ActiveSupport::JSON.encode(self, options)
-
end
-
end
-
-
9
alias_method_chain :to_json, :active_support_encoder
-
end
-
end
-
-
1
class Object
-
1
def as_json(options = nil) #:nodoc:
-
if respond_to?(:to_hash)
-
to_hash.as_json(options)
-
else
-
instance_values.as_json(options)
-
end
-
end
-
end
-
-
1
class Struct #:nodoc:
-
1
def as_json(options = nil)
-
Hash[members.zip(values)].as_json(options)
-
end
-
end
-
-
1
class TrueClass
-
1
def as_json(options = nil) #:nodoc:
-
self
-
end
-
end
-
-
1
class FalseClass
-
1
def as_json(options = nil) #:nodoc:
-
self
-
end
-
end
-
-
1
class NilClass
-
1
def as_json(options = nil) #:nodoc:
-
self
-
end
-
end
-
-
1
class String
-
1
def as_json(options = nil) #:nodoc:
-
self
-
end
-
end
-
-
1
class Symbol
-
1
def as_json(options = nil) #:nodoc:
-
to_s
-
end
-
end
-
-
1
class Numeric
-
1
def as_json(options = nil) #:nodoc:
-
self
-
end
-
end
-
-
1
class Float
-
# Encoding Infinity or NaN to JSON should return "null". The default returns
-
# "Infinity" or "NaN" which are not valid JSON.
-
1
def as_json(options = nil) #:nodoc:
-
finite? ? self : nil
-
end
-
end
-
-
1
class BigDecimal
-
# A BigDecimal would be naturally represented as a JSON number. Most libraries,
-
# however, parse non-integer JSON numbers directly as floats. Clients using
-
# those libraries would get in general a wrong number and no way to recover
-
# other than manually inspecting the string with the JSON code itself.
-
#
-
# That's why a JSON string is returned. The JSON literal is not numeric, but
-
# if the other end knows by contract that the data is supposed to be a
-
# BigDecimal, it still has the chance to post-process the string and get the
-
# real value.
-
1
def as_json(options = nil) #:nodoc:
-
finite? ? to_s : nil
-
end
-
end
-
-
1
class Regexp
-
1
def as_json(options = nil) #:nodoc:
-
to_s
-
end
-
end
-
-
1
module Enumerable
-
1
def as_json(options = nil) #:nodoc:
-
to_a.as_json(options)
-
end
-
end
-
-
1
class Range
-
1
def as_json(options = nil) #:nodoc:
-
to_s
-
end
-
end
-
-
1
class Array
-
1
def as_json(options = nil) #:nodoc:
-
map { |v| options ? v.as_json(options.dup) : v.as_json }
-
end
-
end
-
-
1
class Hash
-
1
def as_json(options = nil) #:nodoc:
-
# create a subset of the hash by applying :only or :except
-
subset = if options
-
if attrs = options[:only]
-
slice(*Array(attrs))
-
elsif attrs = options[:except]
-
except(*Array(attrs))
-
else
-
self
-
end
-
else
-
self
-
end
-
-
Hash[subset.map { |k, v| [k.to_s, options ? v.as_json(options.dup) : v.as_json] }]
-
end
-
end
-
-
1
class Time
-
1
def as_json(options = nil) #:nodoc:
-
if ActiveSupport::JSON::Encoding.use_standard_json_time_format
-
xmlschema(ActiveSupport::JSON::Encoding.time_precision)
-
else
-
%(#{strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)})
-
end
-
end
-
end
-
-
1
class Date
-
1
def as_json(options = nil) #:nodoc:
-
if ActiveSupport::JSON::Encoding.use_standard_json_time_format
-
strftime("%Y-%m-%d")
-
else
-
strftime("%Y/%m/%d")
-
end
-
end
-
end
-
-
1
class DateTime
-
1
def as_json(options = nil) #:nodoc:
-
if ActiveSupport::JSON::Encoding.use_standard_json_time_format
-
xmlschema(ActiveSupport::JSON::Encoding.time_precision)
-
else
-
strftime('%Y/%m/%d %H:%M:%S %z')
-
end
-
end
-
end
-
-
1
class Process::Status #:nodoc:
-
1
def as_json(options = nil)
-
{ :exitstatus => exitstatus, :pid => pid }
-
end
-
end
-
1
require 'active_support/core_ext/object/to_query'
-
1
class Object
-
# Alias of <tt>to_s</tt>.
-
1
def to_param
-
51
to_s
-
end
-
-
# Converts an object into a string suitable for use as a URL query string,
-
# using the given <tt>key</tt> as the param name.
-
1
def to_query(key)
-
18
require 'cgi' unless defined?(CGI) && defined?(CGI::escape)
-
18
"#{CGI.escape(key.to_param)}=#{CGI.escape(to_param.to_s)}"
-
end
-
end
-
-
1
class NilClass
-
# Returns +self+.
-
1
def to_param
-
6
self
-
end
-
end
-
-
1
class TrueClass
-
# Returns +self+.
-
1
def to_param
-
self
-
end
-
end
-
-
1
class FalseClass
-
# Returns +self+.
-
1
def to_param
-
self
-
end
-
end
-
-
1
class Array
-
# Calls <tt>to_param</tt> on all its elements and joins the result with
-
# slashes. This is used by <tt>url_for</tt> in Action Pack.
-
1
def to_param
-
collect { |e| e.to_param }.join '/'
-
end
-
-
# Converts an array into a string suitable for use as a URL query string,
-
# using the given +key+ as the param name.
-
#
-
# ['Rails', 'coding'].to_query('hobbies') # => "hobbies%5B%5D=Rails&hobbies%5B%5D=coding"
-
1
def to_query(key)
-
prefix = "#{key}[]"
-
-
if empty?
-
nil.to_query(prefix)
-
else
-
collect { |value| value.to_query(prefix) }.join '&'
-
end
-
end
-
end
-
-
1
class Hash
-
# Returns a string representation of the receiver suitable for use as a URL
-
# query string:
-
#
-
# {name: 'David', nationality: 'Danish'}.to_query
-
# # => "name=David&nationality=Danish"
-
#
-
# An optional namespace can be passed to enclose key names:
-
#
-
# {name: 'David', nationality: 'Danish'}.to_query('user')
-
# # => "user%5Bname%5D=David&user%5Bnationality%5D=Danish"
-
#
-
# The string pairs "key=value" that conform the query string
-
# are sorted lexicographically in ascending order.
-
#
-
# This method is also aliased as +to_param+.
-
1
def to_query(namespace = nil)
-
collect do |key, value|
-
22
unless (value.is_a?(Hash) || value.is_a?(Array)) && value.empty?
-
22
value.to_query(namespace ? "#{namespace}[#{key}]" : key)
-
end
-
12
end.compact.sort! * '&'
-
end
-
-
1
alias_method :to_param, :to_query
-
end
-
1
class Object
-
# Invokes the public method whose name goes as first argument just like
-
# +public_send+ does, except that if the receiver does not respond to it the
-
# call returns +nil+ rather than raising an exception.
-
#
-
# This method is defined to be able to write
-
#
-
# @person.try(:name)
-
#
-
# instead of
-
#
-
# @person ? @person.name : nil
-
#
-
# +try+ returns +nil+ when called on +nil+ regardless of whether it responds
-
# to the method:
-
#
-
# nil.try(:to_i) # => nil, rather than 0
-
#
-
# Arguments and blocks are forwarded to the method if invoked:
-
#
-
# @posts.try(:each_slice, 2) do |a, b|
-
# ...
-
# end
-
#
-
# The number of arguments in the signature must match. If the object responds
-
# to the method the call is attempted and +ArgumentError+ is still raised
-
# otherwise.
-
#
-
# If +try+ is called without arguments it yields the receiver to a given
-
# block unless it is +nil+:
-
#
-
# @person.try do |p|
-
# ...
-
# end
-
#
-
# Please also note that +try+ is defined on +Object+, therefore it won't work
-
# with instances of classes that do not have +Object+ among their ancestors,
-
# like direct subclasses of +BasicObject+. For example, using +try+ with
-
# +SimpleDelegator+ will delegate +try+ to the target instead of calling it on
-
# delegator itself.
-
1
def try(*a, &b)
-
8
if a.empty? && block_given?
-
yield self
-
else
-
8
public_send(*a, &b) if respond_to?(a.first)
-
end
-
end
-
-
# Same as #try, but will raise a NoMethodError exception if the receiving is not nil and
-
# does not implement the tried method.
-
1
def try!(*a, &b)
-
if a.empty? && block_given?
-
yield self
-
else
-
public_send(*a, &b)
-
end
-
end
-
end
-
-
1
class NilClass
-
# Calling +try+ on +nil+ always returns +nil+.
-
# It becomes specially helpful when navigating through associations that may return +nil+.
-
#
-
# nil.try(:name) # => nil
-
#
-
# Without +try+
-
# @person && !@person.children.blank? && @person.children.first.name
-
#
-
# With +try+
-
# @person.try(:children).try(:first).try(:name)
-
1
def try(*args)
-
nil
-
end
-
-
1
def try!(*args)
-
nil
-
end
-
end
-
1
require 'active_support/option_merger'
-
-
1
class Object
-
# An elegant way to factor duplication out of options passed to a series of
-
# method calls. Each method called in the block, with the block variable as
-
# the receiver, will have its options merged with the default +options+ hash
-
# provided. Each method called on the block variable must take an options
-
# hash as its final argument.
-
#
-
# Without <tt>with_options></tt>, this code contains duplication:
-
#
-
# class Account < ActiveRecord::Base
-
# has_many :customers, dependent: :destroy
-
# has_many :products, dependent: :destroy
-
# has_many :invoices, dependent: :destroy
-
# has_many :expenses, dependent: :destroy
-
# end
-
#
-
# Using <tt>with_options</tt>, we can remove the duplication:
-
#
-
# class Account < ActiveRecord::Base
-
# with_options dependent: :destroy do |assoc|
-
# assoc.has_many :customers
-
# assoc.has_many :products
-
# assoc.has_many :invoices
-
# assoc.has_many :expenses
-
# end
-
# end
-
#
-
# It can also be used with an explicit receiver:
-
#
-
# I18n.with_options locale: user.locale, scope: 'newsletter' do |i18n|
-
# subject i18n.t :subject
-
# body i18n.t :body, user_name: user.name
-
# end
-
#
-
# <tt>with_options</tt> can also be nested since the call is forwarded to its receiver.
-
# Each nesting level will merge inherited defaults in addition to their own.
-
1
def with_options(options)
-
yield ActiveSupport::OptionMerger.new(self, options)
-
end
-
end
-
1
require 'active_support/core_ext/range/conversions'
-
1
require 'active_support/core_ext/range/include_range'
-
1
require 'active_support/core_ext/range/overlaps'
-
1
require 'active_support/core_ext/range/each'
-
1
class Range
-
1
RANGE_FORMATS = {
-
:db => Proc.new { |start, stop| "BETWEEN '#{start.to_s(:db)}' AND '#{stop.to_s(:db)}'" }
-
}
-
-
# Gives a human readable format of the range.
-
#
-
# (1..100).to_formatted_s # => "1..100"
-
1
def to_formatted_s(format = :default)
-
if formatter = RANGE_FORMATS[format]
-
formatter.call(first, last)
-
else
-
to_default_s
-
end
-
end
-
-
1
alias_method :to_default_s, :to_s
-
1
alias_method :to_s, :to_formatted_s
-
end
-
1
require 'active_support/core_ext/module/aliasing'
-
-
1
class Range #:nodoc:
-
-
1
def each_with_time_with_zone(&block)
-
3
ensure_iteration_allowed
-
3
each_without_time_with_zone(&block)
-
end
-
1
alias_method_chain :each, :time_with_zone
-
-
1
def step_with_time_with_zone(n = 1, &block)
-
ensure_iteration_allowed
-
step_without_time_with_zone(n, &block)
-
end
-
1
alias_method_chain :step, :time_with_zone
-
-
1
private
-
1
def ensure_iteration_allowed
-
3
if first.is_a?(Time)
-
raise TypeError, "can't iterate from #{first.class}"
-
end
-
end
-
end
-
1
require 'active_support/core_ext/module/aliasing'
-
-
1
class Range
-
# Extends the default Range#include? to support range comparisons.
-
# (1..5).include?(1..5) # => true
-
# (1..5).include?(2..3) # => true
-
# (1..5).include?(2..6) # => false
-
#
-
# The native Range#include? behavior is untouched.
-
# ('a'..'f').include?('c') # => true
-
# (5..9).include?(11) # => false
-
1
def include_with_range?(value)
-
if value.is_a?(::Range)
-
# 1...10 includes 1..9 but it does not include 1..10.
-
operator = exclude_end? && !value.exclude_end? ? :< : :<=
-
include_without_range?(value.first) && value.last.send(operator, last)
-
else
-
include_without_range?(value)
-
end
-
end
-
-
1
alias_method_chain :include?, :range
-
end
-
1
class Range
-
# Compare two ranges and see if they overlap each other
-
# (1..5).overlaps?(4..6) # => true
-
# (1..5).overlaps?(7..9) # => false
-
1
def overlaps?(other)
-
cover?(other.first) || other.cover?(first)
-
end
-
end
-
1
class Regexp #:nodoc:
-
1
def multiline?
-
options & MULTILINE == MULTILINE
-
end
-
end
-
1
require 'active_support/core_ext/string/conversions'
-
1
require 'active_support/core_ext/string/filters'
-
1
require 'active_support/core_ext/string/multibyte'
-
1
require 'active_support/core_ext/string/starts_ends_with'
-
1
require 'active_support/core_ext/string/inflections'
-
1
require 'active_support/core_ext/string/access'
-
1
require 'active_support/core_ext/string/behavior'
-
1
require 'active_support/core_ext/string/output_safety'
-
1
require 'active_support/core_ext/string/exclude'
-
1
require 'active_support/core_ext/string/strip'
-
1
require 'active_support/core_ext/string/inquiry'
-
1
require 'active_support/core_ext/string/indent'
-
1
require 'active_support/core_ext/string/zones'
-
1
class String
-
# If you pass a single Fixnum, returns a substring of one character at that
-
# position. The first character of the string is at position 0, the next at
-
# position 1, and so on. If a range is supplied, a substring containing
-
# characters at offsets given by the range is returned. In both cases, if an
-
# offset is negative, it is counted from the end of the string. Returns nil
-
# if the initial offset falls outside the string. Returns an empty string if
-
# the beginning of the range is greater than the end of the string.
-
#
-
# str = "hello"
-
# str.at(0) # => "h"
-
# str.at(1..3) # => "ell"
-
# str.at(-2) # => "l"
-
# str.at(-2..-1) # => "lo"
-
# str.at(5) # => nil
-
# str.at(5..-1) # => ""
-
#
-
# If a Regexp is given, the matching portion of the string is returned.
-
# If a String is given, that given string is returned if it occurs in
-
# the string. In both cases, nil is returned if there is no match.
-
#
-
# str = "hello"
-
# str.at(/lo/) # => "lo"
-
# str.at(/ol/) # => nil
-
# str.at("lo") # => "lo"
-
# str.at("ol") # => nil
-
1
def at(position)
-
self[position]
-
end
-
-
# Returns a substring from the given position to the end of the string.
-
# If the position is negative, it is counted from the end of the string.
-
#
-
# str = "hello"
-
# str.from(0) # => "hello"
-
# str.from(3) # => "lo"
-
# str.from(-2) # => "lo"
-
#
-
# You can mix it with +to+ method and do fun things like:
-
#
-
# str = "hello"
-
# str.from(0).to(-1) # => "hello"
-
# str.from(1).to(-2) # => "ell"
-
1
def from(position)
-
self[position..-1]
-
end
-
-
# Returns a substring from the beginning of the string to the given position.
-
# If the position is negative, it is counted from the end of the string.
-
#
-
# str = "hello"
-
# str.to(0) # => "h"
-
# str.to(3) # => "hell"
-
# str.to(-2) # => "hell"
-
#
-
# You can mix it with +from+ method and do fun things like:
-
#
-
# str = "hello"
-
# str.from(0).to(-1) # => "hello"
-
# str.from(1).to(-2) # => "ell"
-
1
def to(position)
-
self[0..position]
-
end
-
-
# Returns the first character. If a limit is supplied, returns a substring
-
# from the beginning of the string until it reaches the limit value. If the
-
# given limit is greater than or equal to the string length, returns self.
-
#
-
# str = "hello"
-
# str.first # => "h"
-
# str.first(1) # => "h"
-
# str.first(2) # => "he"
-
# str.first(0) # => ""
-
# str.first(6) # => "hello"
-
1
def first(limit = 1)
-
if limit == 0
-
''
-
elsif limit >= size
-
self
-
else
-
to(limit - 1)
-
end
-
end
-
-
# Returns the last character of the string. If a limit is supplied, returns a substring
-
# from the end of the string until it reaches the limit value (counting backwards). If
-
# the given limit is greater than or equal to the string length, returns self.
-
#
-
# str = "hello"
-
# str.last # => "o"
-
# str.last(1) # => "o"
-
# str.last(2) # => "lo"
-
# str.last(0) # => ""
-
# str.last(6) # => "hello"
-
1
def last(limit = 1)
-
if limit == 0
-
''
-
elsif limit >= size
-
self
-
else
-
from(-limit)
-
end
-
end
-
end
-
1
class String
-
# Enable more predictable duck-typing on String-like classes. See <tt>Object#acts_like?</tt>.
-
1
def acts_like_string?
-
true
-
end
-
end
-
1
require 'date'
-
1
require 'active_support/core_ext/time/calculations'
-
-
1
class String
-
# Converts a string to a Time value.
-
# The +form+ can be either :utc or :local (default :local).
-
#
-
# The time is parsed using Time.parse method.
-
# If +form+ is :local, then the time is in the system timezone.
-
# If the date part is missing then the current date is used and if
-
# the time part is missing then it is assumed to be 00:00:00.
-
#
-
# "13-12-2012".to_time # => 2012-12-13 00:00:00 +0100
-
# "06:12".to_time # => 2012-12-13 06:12:00 +0100
-
# "2012-12-13 06:12".to_time # => 2012-12-13 06:12:00 +0100
-
# "2012-12-13T06:12".to_time # => 2012-12-13 06:12:00 +0100
-
# "2012-12-13T06:12".to_time(:utc) # => 2012-12-13 05:12:00 UTC
-
# "12/13/2012".to_time # => ArgumentError: argument out of range
-
1
def to_time(form = :local)
-
parts = Date._parse(self, false)
-
return if parts.empty?
-
-
now = Time.now
-
time = Time.new(
-
parts.fetch(:year, now.year),
-
parts.fetch(:mon, now.month),
-
parts.fetch(:mday, now.day),
-
parts.fetch(:hour, 0),
-
parts.fetch(:min, 0),
-
parts.fetch(:sec, 0) + parts.fetch(:sec_fraction, 0),
-
parts.fetch(:offset, form == :utc ? 0 : nil)
-
)
-
-
form == :utc ? time.utc : time.getlocal
-
end
-
-
# Converts a string to a Date value.
-
#
-
# "1-1-2012".to_date # => Sun, 01 Jan 2012
-
# "01/01/2012".to_date # => Sun, 01 Jan 2012
-
# "2012-12-13".to_date # => Thu, 13 Dec 2012
-
# "12/13/2012".to_date # => ArgumentError: invalid date
-
1
def to_date
-
::Date.parse(self, false) unless blank?
-
end
-
-
# Converts a string to a DateTime value.
-
#
-
# "1-1-2012".to_datetime # => Sun, 01 Jan 2012 00:00:00 +0000
-
# "01/01/2012 23:59:59".to_datetime # => Sun, 01 Jan 2012 23:59:59 +0000
-
# "2012-12-13 12:50".to_datetime # => Thu, 13 Dec 2012 12:50:00 +0000
-
# "12/13/2012".to_datetime # => ArgumentError: invalid date
-
1
def to_datetime
-
::DateTime.parse(self, false) unless blank?
-
end
-
end
-
1
class String
-
# The inverse of <tt>String#include?</tt>. Returns true if the string
-
# does not include the other string.
-
#
-
# "hello".exclude? "lo" # => false
-
# "hello".exclude? "ol" # => true
-
# "hello".exclude? ?h # => false
-
1
def exclude?(string)
-
!include?(string)
-
end
-
end
-
1
class String
-
# Returns the string, first removing all whitespace on both ends of
-
# the string, and then changing remaining consecutive whitespace
-
# groups into one space each.
-
#
-
# Note that it handles both ASCII and Unicode whitespace like mongolian vowel separator (U+180E).
-
#
-
# %{ Multi-line
-
# string }.squish # => "Multi-line string"
-
# " foo bar \n \t boo".squish # => "foo bar boo"
-
1
def squish
-
dup.squish!
-
end
-
-
# Performs a destructive squish. See String#squish.
-
1
def squish!
-
gsub!(/\A[[:space:]]+/, '')
-
gsub!(/[[:space:]]+\z/, '')
-
gsub!(/[[:space:]]+/, ' ')
-
self
-
end
-
-
# Returns a new string with all occurrences of the pattern removed. Short-hand for String#gsub(pattern, '').
-
1
def remove(pattern)
-
gsub pattern, ''
-
end
-
-
# Alters the string by removing all occurrences of the pattern. Short-hand for String#gsub!(pattern, '').
-
1
def remove!(pattern)
-
gsub! pattern, ''
-
end
-
-
# Truncates a given +text+ after a given <tt>length</tt> if +text+ is longer than <tt>length</tt>:
-
#
-
# 'Once upon a time in a world far far away'.truncate(27)
-
# # => "Once upon a time in a wo..."
-
#
-
# Pass a string or regexp <tt>:separator</tt> to truncate +text+ at a natural break:
-
#
-
# 'Once upon a time in a world far far away'.truncate(27, separator: ' ')
-
# # => "Once upon a time in a..."
-
#
-
# 'Once upon a time in a world far far away'.truncate(27, separator: /\s/)
-
# # => "Once upon a time in a..."
-
#
-
# The last characters will be replaced with the <tt>:omission</tt> string (defaults to "...")
-
# for a total length not exceeding <tt>length</tt>:
-
#
-
# 'And they found that many people were sleeping better.'.truncate(25, omission: '... (continued)')
-
# # => "And they f... (continued)"
-
1
def truncate(truncate_at, options = {})
-
return dup unless length > truncate_at
-
-
omission = options[:omission] || '...'
-
length_with_room_for_omission = truncate_at - omission.length
-
stop = \
-
if options[:separator]
-
rindex(options[:separator], length_with_room_for_omission) || length_with_room_for_omission
-
else
-
length_with_room_for_omission
-
end
-
-
"#{self[0, stop]}#{omission}"
-
end
-
end
-
1
class String
-
# Same as +indent+, except it indents the receiver in-place.
-
#
-
# Returns the indented string, or +nil+ if there was nothing to indent.
-
1
def indent!(amount, indent_string=nil, indent_empty_lines=false)
-
indent_string = indent_string || self[/^[ \t]/] || ' '
-
re = indent_empty_lines ? /^/ : /^(?!$)/
-
gsub!(re, indent_string * amount)
-
end
-
-
# Indents the lines in the receiver:
-
#
-
# <<EOS.indent(2)
-
# def some_method
-
# some_code
-
# end
-
# EOS
-
# # =>
-
# def some_method
-
# some_code
-
# end
-
#
-
# The second argument, +indent_string+, specifies which indent string to
-
# use. The default is +nil+, which tells the method to make a guess by
-
# peeking at the first indented line, and fallback to a space if there is
-
# none.
-
#
-
# " foo".indent(2) # => " foo"
-
# "foo\n\t\tbar".indent(2) # => "\t\tfoo\n\t\t\t\tbar"
-
# "foo".indent(2, "\t") # => "\t\tfoo"
-
#
-
# While +indent_string+ is typically one space or tab, it may be any string.
-
#
-
# The third argument, +indent_empty_lines+, is a flag that says whether
-
# empty lines should be indented. Default is false.
-
#
-
# "foo\n\nbar".indent(2) # => " foo\n\n bar"
-
# "foo\n\nbar".indent(2, nil, true) # => " foo\n \n bar"
-
#
-
1
def indent(amount, indent_string=nil, indent_empty_lines=false)
-
dup.tap {|_| _.indent!(amount, indent_string, indent_empty_lines)}
-
end
-
end
-
1
require 'active_support/inflector/methods'
-
1
require 'active_support/inflector/transliterate'
-
-
# String inflections define new methods on the String class to transform names for different purposes.
-
# For instance, you can figure out the name of a table from the name of a class.
-
#
-
# 'ScaleScore'.tableize # => "scale_scores"
-
#
-
1
class String
-
# Returns the plural form of the word in the string.
-
#
-
# If the optional parameter +count+ is specified,
-
# the singular form will be returned if <tt>count == 1</tt>.
-
# For any other value of +count+ the plural will be returned.
-
#
-
# If the optional parameter +locale+ is specified,
-
# the word will be pluralized as a word of that language.
-
# By default, this parameter is set to <tt>:en</tt>.
-
# You must define your own inflection rules for languages other than English.
-
#
-
# 'post'.pluralize # => "posts"
-
# 'octopus'.pluralize # => "octopi"
-
# 'sheep'.pluralize # => "sheep"
-
# 'words'.pluralize # => "words"
-
# 'the blue mailman'.pluralize # => "the blue mailmen"
-
# 'CamelOctopus'.pluralize # => "CamelOctopi"
-
# 'apple'.pluralize(1) # => "apple"
-
# 'apple'.pluralize(2) # => "apples"
-
# 'ley'.pluralize(:es) # => "leyes"
-
# 'ley'.pluralize(1, :es) # => "ley"
-
1
def pluralize(count = nil, locale = :en)
-
18
locale = count if count.is_a?(Symbol)
-
18
if count == 1
-
self
-
else
-
18
ActiveSupport::Inflector.pluralize(self, locale)
-
end
-
end
-
-
# The reverse of +pluralize+, returns the singular form of a word in a string.
-
#
-
# If the optional parameter +locale+ is specified,
-
# the word will be singularized as a word of that language.
-
# By default, this parameter is set to <tt>:en</tt>.
-
# You must define your own inflection rules for languages other than English.
-
#
-
# 'posts'.singularize # => "post"
-
# 'octopi'.singularize # => "octopus"
-
# 'sheep'.singularize # => "sheep"
-
# 'word'.singularize # => "word"
-
# 'the blue mailmen'.singularize # => "the blue mailman"
-
# 'CamelOctopi'.singularize # => "CamelOctopus"
-
# 'leyes'.singularize(:es) # => "ley"
-
1
def singularize(locale = :en)
-
37
ActiveSupport::Inflector.singularize(self, locale)
-
end
-
-
# +constantize+ tries to find a declared constant with the name specified
-
# in the string. It raises a NameError when the name is not in CamelCase
-
# or is not initialized. See ActiveSupport::Inflector.constantize
-
#
-
# 'Module'.constantize # => Module
-
# 'Class'.constantize # => Class
-
# 'blargle'.constantize # => NameError: wrong constant name blargle
-
1
def constantize
-
7
ActiveSupport::Inflector.constantize(self)
-
end
-
-
# +safe_constantize+ tries to find a declared constant with the name specified
-
# in the string. It returns nil when the name is not in CamelCase
-
# or is not initialized. See ActiveSupport::Inflector.safe_constantize
-
#
-
# 'Module'.safe_constantize # => Module
-
# 'Class'.safe_constantize # => Class
-
# 'blargle'.safe_constantize # => nil
-
1
def safe_constantize
-
ActiveSupport::Inflector.safe_constantize(self)
-
end
-
-
# By default, +camelize+ converts strings to UpperCamelCase. If the argument to camelize
-
# is set to <tt>:lower</tt> then camelize produces lowerCamelCase.
-
#
-
# +camelize+ will also convert '/' to '::' which is useful for converting paths to namespaces.
-
#
-
# 'active_record'.camelize # => "ActiveRecord"
-
# 'active_record'.camelize(:lower) # => "activeRecord"
-
# 'active_record/errors'.camelize # => "ActiveRecord::Errors"
-
# 'active_record/errors'.camelize(:lower) # => "activeRecord::Errors"
-
1
def camelize(first_letter = :upper)
-
32
case first_letter
-
when :upper
-
32
ActiveSupport::Inflector.camelize(self, true)
-
when :lower
-
ActiveSupport::Inflector.camelize(self, false)
-
end
-
end
-
1
alias_method :camelcase, :camelize
-
-
# Capitalizes all the words and replaces some characters in the string to create
-
# a nicer looking title. +titleize+ is meant for creating pretty output. It is not
-
# used in the Rails internals.
-
#
-
# +titleize+ is also aliased as +titlecase+.
-
#
-
# 'man from the boondocks'.titleize # => "Man From The Boondocks"
-
# 'x-men: the last stand'.titleize # => "X Men: The Last Stand"
-
1
def titleize
-
ActiveSupport::Inflector.titleize(self)
-
end
-
1
alias_method :titlecase, :titleize
-
-
# The reverse of +camelize+. Makes an underscored, lowercase form from the expression in the string.
-
#
-
# +underscore+ will also change '::' to '/' to convert namespaces to paths.
-
#
-
# 'ActiveModel'.underscore # => "active_model"
-
# 'ActiveModel::Errors'.underscore # => "active_model/errors"
-
1
def underscore
-
68
ActiveSupport::Inflector.underscore(self)
-
end
-
-
# Replaces underscores with dashes in the string.
-
#
-
# 'puni_puni'.dasherize # => "puni-puni"
-
1
def dasherize
-
58
ActiveSupport::Inflector.dasherize(self)
-
end
-
-
# Removes the module part from the constant expression in the string.
-
#
-
# 'ActiveRecord::CoreExtensions::String::Inflections'.demodulize # => "Inflections"
-
# 'Inflections'.demodulize # => "Inflections"
-
#
-
# See also +deconstantize+.
-
1
def demodulize
-
4
ActiveSupport::Inflector.demodulize(self)
-
end
-
-
# Removes the rightmost segment from the constant expression in the string.
-
#
-
# 'Net::HTTP'.deconstantize # => "Net"
-
# '::Net::HTTP'.deconstantize # => "::Net"
-
# 'String'.deconstantize # => ""
-
# '::String'.deconstantize # => ""
-
# ''.deconstantize # => ""
-
#
-
# See also +demodulize+.
-
1
def deconstantize
-
ActiveSupport::Inflector.deconstantize(self)
-
end
-
-
# Replaces special characters in a string so that it may be used as part of a 'pretty' URL.
-
#
-
# class Person
-
# def to_param
-
# "#{id}-#{name.parameterize}"
-
# end
-
# end
-
#
-
# @person = Person.find(1)
-
# # => #<Person id: 1, name: "Donald E. Knuth">
-
#
-
# <%= link_to(@person.name, person_path) %>
-
# # => <a href="/person/1-donald-e-knuth">Donald E. Knuth</a>
-
1
def parameterize(sep = '-')
-
ActiveSupport::Inflector.parameterize(self, sep)
-
end
-
-
# Creates the name of a table like Rails does for models to table names. This method
-
# uses the +pluralize+ method on the last word in the string.
-
#
-
# 'RawScaledScorer'.tableize # => "raw_scaled_scorers"
-
# 'egg_and_ham'.tableize # => "egg_and_hams"
-
# 'fancyCategory'.tableize # => "fancy_categories"
-
1
def tableize
-
ActiveSupport::Inflector.tableize(self)
-
end
-
-
# Create a class name from a plural table name like Rails does for table names to models.
-
# Note that this returns a string and not a class. (To convert to an actual class
-
# follow +classify+ with +constantize+.)
-
#
-
# 'egg_and_hams'.classify # => "EggAndHam"
-
# 'posts'.classify # => "Post"
-
1
def classify
-
1
ActiveSupport::Inflector.classify(self)
-
end
-
-
# Capitalizes the first word, turns underscores into spaces, and strips a
-
# trailing '_id' if present.
-
# Like +titleize+, this is meant for creating pretty output.
-
#
-
# The capitalization of the first word can be turned off by setting the
-
# optional parameter +capitalize+ to false.
-
# By default, this parameter is true.
-
#
-
# 'employee_salary'.humanize # => "Employee salary"
-
# 'author_id'.humanize # => "Author"
-
# 'author_id'.humanize(capitalize: false) # => "author"
-
1
def humanize(options = {})
-
6
ActiveSupport::Inflector.humanize(self, options)
-
end
-
-
# Creates a foreign key name from a class name.
-
# +separate_class_name_and_id_with_underscore+ sets whether
-
# the method should put '_' between the name and 'id'.
-
#
-
# 'Message'.foreign_key # => "message_id"
-
# 'Message'.foreign_key(false) # => "messageid"
-
# 'Admin::Post'.foreign_key # => "post_id"
-
1
def foreign_key(separate_class_name_and_id_with_underscore = true)
-
3
ActiveSupport::Inflector.foreign_key(self, separate_class_name_and_id_with_underscore)
-
end
-
end
-
1
require 'active_support/string_inquirer'
-
-
1
class String
-
# Wraps the current string in the <tt>ActiveSupport::StringInquirer</tt> class,
-
# which gives you a prettier way to test for equality.
-
#
-
# env = 'production'.inquiry
-
# env.production? # => true
-
# env.development? # => false
-
1
def inquiry
-
ActiveSupport::StringInquirer.new(self)
-
end
-
end
-
# encoding: utf-8
-
1
require 'active_support/multibyte'
-
-
1
class String
-
# == Multibyte proxy
-
#
-
# +mb_chars+ is a multibyte safe proxy for string methods.
-
#
-
# It creates and returns an instance of the ActiveSupport::Multibyte::Chars class which
-
# encapsulates the original string. A Unicode safe version of all the String methods are defined on this proxy
-
# class. If the proxy class doesn't respond to a certain method, it's forwarded to the encapsulated string.
-
#
-
# name = 'Claus Müller'
-
# name.reverse # => "rell??M sualC"
-
# name.length # => 13
-
#
-
# name.mb_chars.reverse.to_s # => "rellüM sualC"
-
# name.mb_chars.length # => 12
-
#
-
# == Method chaining
-
#
-
# All the methods on the Chars proxy which normally return a string will return a Chars object. This allows
-
# method chaining on the result of any of these methods.
-
#
-
# name.mb_chars.reverse.length # => 12
-
#
-
# == Interoperability and configuration
-
#
-
# The Chars object tries to be as interchangeable with String objects as possible: sorting and comparing between
-
# String and Char work like expected. The bang! methods change the internal string representation in the Chars
-
# object. Interoperability problems can be resolved easily with a +to_s+ call.
-
#
-
# For more information about the methods defined on the Chars proxy see ActiveSupport::Multibyte::Chars. For
-
# information about how to change the default Multibyte behavior see ActiveSupport::Multibyte.
-
1
def mb_chars
-
ActiveSupport::Multibyte.proxy_class.new(self)
-
end
-
-
1
def is_utf8?
-
case encoding
-
when Encoding::UTF_8
-
valid_encoding?
-
when Encoding::ASCII_8BIT, Encoding::US_ASCII
-
dup.force_encoding(Encoding::UTF_8).valid_encoding?
-
else
-
false
-
end
-
end
-
end
-
1
require 'erb'
-
1
require 'active_support/core_ext/kernel/singleton_class'
-
-
1
class ERB
-
1
module Util
-
1
HTML_ESCAPE = { '&' => '&', '>' => '>', '<' => '<', '"' => '"', "'" => ''' }
-
1
JSON_ESCAPE = { '&' => '\u0026', '>' => '\u003e', '<' => '\u003c', "\u2028" => '\u2028', "\u2029" => '\u2029' }
-
1
HTML_ESCAPE_REGEXP = /[&"'><]/
-
1
HTML_ESCAPE_ONCE_REGEXP = /["><']|&(?!([a-zA-Z]+|(#\d+)|(#[xX][\dA-Fa-f]+));)/
-
1
JSON_ESCAPE_REGEXP = /[\u2028\u2029&><]/u
-
-
# A utility method for escaping HTML tag characters.
-
# This method is also aliased as <tt>h</tt>.
-
#
-
# In your ERB templates, use this method to escape any unsafe content. For example:
-
# <%=h @person.name %>
-
#
-
# puts html_escape('is a > 0 & a < 10?')
-
# # => is a > 0 & a < 10?
-
1
def html_escape(s)
-
2
s = s.to_s
-
2
if s.html_safe?
-
s
-
else
-
2
s.gsub(HTML_ESCAPE_REGEXP, HTML_ESCAPE).html_safe
-
end
-
end
-
-
# Aliasing twice issues a warning "discarding old...". Remove first to avoid it.
-
1
remove_method(:h)
-
1
alias h html_escape
-
-
1
module_function :h
-
-
1
singleton_class.send(:remove_method, :html_escape)
-
1
module_function :html_escape
-
-
# A utility method for escaping HTML without affecting existing escaped entities.
-
#
-
# html_escape_once('1 < 2 & 3')
-
# # => "1 < 2 & 3"
-
#
-
# html_escape_once('<< Accept & Checkout')
-
# # => "<< Accept & Checkout"
-
1
def html_escape_once(s)
-
result = s.to_s.gsub(HTML_ESCAPE_ONCE_REGEXP, HTML_ESCAPE)
-
s.html_safe? ? result.html_safe : result
-
end
-
-
1
module_function :html_escape_once
-
-
# A utility method for escaping HTML entities in JSON strings. Specifically, the
-
# &, > and < characters are replaced with their equivalent unicode escaped form -
-
# \u0026, \u003e, and \u003c. The Unicode sequences \u2028 and \u2029 are also
-
# escaped as they are treated as newline characters in some JavaScript engines.
-
# These sequences have identical meaning as the original characters inside the
-
# context of a JSON string, so assuming the input is a valid and well-formed
-
# JSON value, the output will have equivalent meaning when parsed:
-
#
-
# json = JSON.generate({ name: "</script><script>alert('PWNED!!!')</script>"})
-
# # => "{\"name\":\"</script><script>alert('PWNED!!!')</script>\"}"
-
#
-
# json_escape(json)
-
# # => "{\"name\":\"\\u003C/script\\u003E\\u003Cscript\\u003Ealert('PWNED!!!')\\u003C/script\\u003E\"}"
-
#
-
# JSON.parse(json) == JSON.parse(json_escape(json))
-
# # => true
-
#
-
# The intended use case for this method is to escape JSON strings before including
-
# them inside a script tag to avoid XSS vulnerability:
-
#
-
# <script>
-
# var currentUser = <%= raw json_escape(current_user.to_json) %>;
-
# </script>
-
#
-
# It is necessary to +raw+ the result of +json_escape+, so that quotation marks
-
# don't get converted to <tt>"</tt> entities. +json_escape+ doesn't
-
# automatically flag the result as HTML safe, since the raw value is unsafe to
-
# use inside HTML attributes.
-
#
-
# If you need to output JSON elsewhere in your HTML, you can just do something
-
# like this, as any unsafe characters (including quotation marks) will be
-
# automatically escaped for you:
-
#
-
# <div data-user-info="<%= current_user.to_json %>">...</div>
-
#
-
# WARNING: this helper only works with valid JSON. Using this on non-JSON values
-
# will open up serious XSS vulnerabilities. For example, if you replace the
-
# +current_user.to_json+ in the example above with user input instead, the browser
-
# will happily eval() that string as JavaScript.
-
#
-
# The escaping performed in this method is identical to those performed in the
-
# Active Support JSON encoder when +ActiveSupport.escape_html_entities_in_json+ is
-
# set to true. Because this transformation is idempotent, this helper can be
-
# applied even if +ActiveSupport.escape_html_entities_in_json+ is already true.
-
#
-
# Therefore, when you are unsure if +ActiveSupport.escape_html_entities_in_json+
-
# is enabled, or if you are unsure where your JSON string originated from, it
-
# is recommended that you always apply this helper (other libraries, such as the
-
# JSON gem, do not provide this kind of protection by default; also some gems
-
# might override +to_json+ to bypass Active Support's encoder).
-
1
def json_escape(s)
-
result = s.to_s.gsub(JSON_ESCAPE_REGEXP, JSON_ESCAPE)
-
s.html_safe? ? result.html_safe : result
-
end
-
-
1
module_function :json_escape
-
end
-
end
-
-
1
class Object
-
1
def html_safe?
-
2
false
-
end
-
end
-
-
1
class Numeric
-
1
def html_safe?
-
true
-
end
-
end
-
-
1
module ActiveSupport #:nodoc:
-
1
class SafeBuffer < String
-
1
UNSAFE_STRING_METHODS = %w(
-
capitalize chomp chop delete downcase gsub lstrip next reverse rstrip
-
slice squeeze strip sub succ swapcase tr tr_s upcase prepend
-
)
-
-
1
alias_method :original_concat, :concat
-
1
private :original_concat
-
-
1
class SafeConcatError < StandardError
-
1
def initialize
-
super 'Could not concatenate to the buffer because it is not html safe.'
-
end
-
end
-
-
1
def [](*args)
-
if args.size < 2
-
super
-
else
-
if html_safe?
-
new_safe_buffer = super
-
new_safe_buffer.instance_eval { @html_safe = true }
-
new_safe_buffer
-
else
-
to_str[*args]
-
end
-
end
-
end
-
-
1
def safe_concat(value)
-
raise SafeConcatError unless html_safe?
-
original_concat(value)
-
end
-
-
1
def initialize(*)
-
2
@html_safe = true
-
2
super
-
end
-
-
1
def initialize_copy(other)
-
super
-
@html_safe = other.html_safe?
-
end
-
-
1
def clone_empty
-
self[0, 0]
-
end
-
-
1
def concat(value)
-
if !html_safe? || value.html_safe?
-
super(value)
-
else
-
super(ERB::Util.h(value))
-
end
-
end
-
1
alias << concat
-
-
1
def +(other)
-
dup.concat(other)
-
end
-
-
1
def %(args)
-
case args
-
when Hash
-
escaped_args = Hash[args.map { |k,arg| [k, html_escape_interpolated_argument(arg)] }]
-
else
-
escaped_args = Array(args).map { |arg| html_escape_interpolated_argument(arg) }
-
end
-
-
self.class.new(super(escaped_args))
-
end
-
-
1
def html_safe?
-
defined?(@html_safe) && @html_safe
-
end
-
-
1
def to_s
-
self
-
end
-
-
1
def to_param
-
to_str
-
end
-
-
1
def encode_with(coder)
-
coder.represent_scalar nil, to_str
-
end
-
-
1
UNSAFE_STRING_METHODS.each do |unsafe_method|
-
20
if unsafe_method.respond_to?(unsafe_method)
-
20
class_eval <<-EOT, __FILE__, __LINE__ + 1
-
def #{unsafe_method}(*args, &block) # def capitalize(*args, &block)
-
to_str.#{unsafe_method}(*args, &block) # to_str.capitalize(*args, &block)
-
end # end
-
-
def #{unsafe_method}!(*args) # def capitalize!(*args)
-
@html_safe = false # @html_safe = false
-
super # super
-
end # end
-
EOT
-
end
-
end
-
-
1
private
-
-
1
def html_escape_interpolated_argument(arg)
-
(!html_safe? || arg.html_safe?) ? arg : ERB::Util.h(arg)
-
end
-
end
-
end
-
-
1
class String
-
1
def html_safe
-
2
ActiveSupport::SafeBuffer.new(self)
-
end
-
end
-
1
class String
-
1
alias_method :starts_with?, :start_with?
-
1
alias_method :ends_with?, :end_with?
-
end
-
1
require 'active_support/core_ext/object/try'
-
-
1
class String
-
# Strips indentation in heredocs.
-
#
-
# For example in
-
#
-
# if options[:usage]
-
# puts <<-USAGE.strip_heredoc
-
# This command does such and such.
-
#
-
# Supported options are:
-
# -h This message
-
# ...
-
# USAGE
-
# end
-
#
-
# the user would see the usage message aligned against the left margin.
-
#
-
# Technically, it looks for the least indented line in the whole string, and removes
-
# that amount of leading whitespace.
-
1
def strip_heredoc
-
indent = scan(/^[ \t]*(?=\S)/).min.try(:size) || 0
-
gsub(/^[ \t]{#{indent}}/, '')
-
end
-
end
-
1
require 'active_support/core_ext/string/conversions'
-
1
require 'active_support/core_ext/time/zones'
-
-
1
class String
-
# Converts String to a TimeWithZone in the current zone if Time.zone or Time.zone_default
-
# is set, otherwise converts String to a Time via String#to_time
-
1
def in_time_zone(zone = ::Time.zone)
-
if zone
-
::Time.find_zone!(zone).parse(self)
-
else
-
to_time
-
end
-
end
-
end
-
# Backport of Struct#to_h from Ruby 2.0
-
class Struct # :nodoc:
-
def to_h
-
Hash[members.zip(values)]
-
end
-
1
end unless Struct.instance_methods.include?(:to_h)
-
class Thread
-
LOCK = Mutex.new # :nodoc:
-
-
# Returns the value of a thread local variable that has been set. Note that
-
# these are different than fiber local values.
-
#
-
# Thread local values are carried along with threads, and do not respect
-
# fibers. For example:
-
#
-
# Thread.new {
-
# Thread.current.thread_variable_set("foo", "bar") # set a thread local
-
# Thread.current["foo"] = "bar" # set a fiber local
-
#
-
# Fiber.new {
-
# Fiber.yield [
-
# Thread.current.thread_variable_get("foo"), # get the thread local
-
# Thread.current["foo"], # get the fiber local
-
# ]
-
# }.resume
-
# }.join.value # => ['bar', nil]
-
#
-
# The value <tt>"bar"</tt> is returned for the thread local, where +nil+ is returned
-
# for the fiber local. The fiber is executed in the same thread, so the
-
# thread local values are available.
-
def thread_variable_get(key)
-
_locals[key.to_sym]
-
end
-
-
# Sets a thread local with +key+ to +value+. Note that these are local to
-
# threads, and not to fibers. Please see Thread#thread_variable_get for
-
# more information.
-
def thread_variable_set(key, value)
-
_locals[key.to_sym] = value
-
end
-
-
# Returns an array of the names of the thread-local variables (as Symbols).
-
#
-
# thr = Thread.new do
-
# Thread.current.thread_variable_set(:cat, 'meow')
-
# Thread.current.thread_variable_set("dog", 'woof')
-
# end
-
# thr.join # => #<Thread:0x401b3f10 dead>
-
# thr.thread_variables # => [:dog, :cat]
-
#
-
# Note that these are not fiber local variables. Please see Thread#thread_variable_get
-
# for more details.
-
def thread_variables
-
_locals.keys
-
end
-
-
# Returns <tt>true</tt> if the given string (or symbol) exists as a
-
# thread-local variable.
-
#
-
# me = Thread.current
-
# me.thread_variable_set(:oliver, "a")
-
# me.thread_variable?(:oliver) # => true
-
# me.thread_variable?(:stanley) # => false
-
#
-
# Note that these are not fiber local variables. Please see Thread#thread_variable_get
-
# for more details.
-
def thread_variable?(key)
-
_locals.has_key?(key.to_sym)
-
end
-
-
def freeze
-
_locals.freeze
-
super
-
end
-
-
private
-
-
def _locals
-
if defined?(@_locals)
-
@_locals
-
else
-
LOCK.synchronize { @_locals ||= {} }
-
end
-
end
-
1
end unless Thread.instance_methods.include?(:thread_variable_set)
-
1
require 'active_support/core_ext/time/acts_like'
-
1
require 'active_support/core_ext/time/calculations'
-
1
require 'active_support/core_ext/time/conversions'
-
1
require 'active_support/core_ext/time/marshal'
-
1
require 'active_support/core_ext/time/zones'
-
1
require 'active_support/core_ext/object/acts_like'
-
-
1
class Time
-
# Duck-types as a Time-like class. See Object#acts_like?.
-
1
def acts_like_time?
-
true
-
end
-
end
-
1
require 'active_support/duration'
-
1
require 'active_support/core_ext/time/conversions'
-
1
require 'active_support/time_with_zone'
-
1
require 'active_support/core_ext/time/zones'
-
1
require 'active_support/core_ext/date_and_time/calculations'
-
-
1
class Time
-
1
include DateAndTime::Calculations
-
-
1
COMMON_YEAR_DAYS_IN_MONTH = [nil, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
-
-
1
class << self
-
# Overriding case equality method so that it returns true for ActiveSupport::TimeWithZone instances
-
1
def ===(other)
-
12
super || (self == Time && other.is_a?(ActiveSupport::TimeWithZone))
-
end
-
-
# Return the number of days in the given month.
-
# If no year is specified, it will use the current year.
-
1
def days_in_month(month, year = now.year)
-
if month == 2 && ::Date.gregorian_leap?(year)
-
29
-
else
-
COMMON_YEAR_DAYS_IN_MONTH[month]
-
end
-
end
-
-
# Returns <tt>Time.zone.now</tt> when <tt>Time.zone</tt> or <tt>config.time_zone</tt> are set, otherwise just returns <tt>Time.now</tt>.
-
1
def current
-
::Time.zone ? ::Time.zone.now : ::Time.now
-
end
-
-
# Layers additional behavior on Time.at so that ActiveSupport::TimeWithZone and DateTime
-
# instances can be used when called with a single argument
-
1
def at_with_coercion(*args)
-
return at_without_coercion(*args) if args.size != 1
-
-
# Time.at can be called with a time or numerical value
-
time_or_number = args.first
-
-
if time_or_number.is_a?(ActiveSupport::TimeWithZone) || time_or_number.is_a?(DateTime)
-
at_without_coercion(time_or_number.to_f).getlocal
-
else
-
at_without_coercion(time_or_number)
-
end
-
end
-
1
alias_method :at_without_coercion, :at
-
1
alias_method :at, :at_with_coercion
-
end
-
-
# Seconds since midnight: Time.now.seconds_since_midnight
-
1
def seconds_since_midnight
-
to_i - change(:hour => 0).to_i + (usec / 1.0e+6)
-
end
-
-
# Returns the number of seconds until 23:59:59.
-
#
-
# Time.new(2012, 8, 29, 0, 0, 0).seconds_until_end_of_day # => 86399
-
# Time.new(2012, 8, 29, 12, 34, 56).seconds_until_end_of_day # => 41103
-
# Time.new(2012, 8, 29, 23, 59, 59).seconds_until_end_of_day # => 0
-
1
def seconds_until_end_of_day
-
end_of_day.to_i - to_i
-
end
-
-
# Returns a new Time where one or more of the elements have been changed according
-
# to the +options+ parameter. The time options (<tt>:hour</tt>, <tt>:min</tt>,
-
# <tt>:sec</tt>, <tt>:usec</tt>) reset cascadingly, so if only the hour is passed,
-
# then minute, sec, and usec is set to 0. If the hour and minute is passed, then
-
# sec and usec is set to 0. The +options+ parameter takes a hash with any of these
-
# keys: <tt>:year</tt>, <tt>:month</tt>, <tt>:day</tt>, <tt>:hour</tt>, <tt>:min</tt>,
-
# <tt>:sec</tt>, <tt>:usec</tt>.
-
#
-
# Time.new(2012, 8, 29, 22, 35, 0).change(day: 1) # => Time.new(2012, 8, 1, 22, 35, 0)
-
# Time.new(2012, 8, 29, 22, 35, 0).change(year: 1981, day: 1) # => Time.new(1981, 8, 1, 22, 35, 0)
-
# Time.new(2012, 8, 29, 22, 35, 0).change(year: 1981, hour: 0) # => Time.new(1981, 8, 29, 0, 0, 0)
-
1
def change(options)
-
new_year = options.fetch(:year, year)
-
new_month = options.fetch(:month, month)
-
new_day = options.fetch(:day, day)
-
new_hour = options.fetch(:hour, hour)
-
new_min = options.fetch(:min, options[:hour] ? 0 : min)
-
new_sec = options.fetch(:sec, (options[:hour] || options[:min]) ? 0 : sec)
-
new_usec = options.fetch(:usec, (options[:hour] || options[:min] || options[:sec]) ? 0 : Rational(nsec, 1000))
-
-
if utc?
-
::Time.utc(new_year, new_month, new_day, new_hour, new_min, new_sec, new_usec)
-
elsif zone
-
::Time.local(new_year, new_month, new_day, new_hour, new_min, new_sec, new_usec)
-
else
-
::Time.new(new_year, new_month, new_day, new_hour, new_min, new_sec + (new_usec.to_r / 1000000), utc_offset)
-
end
-
end
-
-
# Uses Date to provide precise Time calculations for years, months, and days
-
# according to the proleptic Gregorian calendar. The +options+ parameter
-
# takes a hash with any of these keys: <tt>:years</tt>, <tt>:months</tt>,
-
# <tt>:weeks</tt>, <tt>:days</tt>, <tt>:hours</tt>, <tt>:minutes</tt>,
-
# <tt>:seconds</tt>.
-
1
def advance(options)
-
unless options[:weeks].nil?
-
options[:weeks], partial_weeks = options[:weeks].divmod(1)
-
options[:days] = options.fetch(:days, 0) + 7 * partial_weeks
-
end
-
-
unless options[:days].nil?
-
options[:days], partial_days = options[:days].divmod(1)
-
options[:hours] = options.fetch(:hours, 0) + 24 * partial_days
-
end
-
-
d = to_date.advance(options)
-
d = d.gregorian if d.julian?
-
time_advanced_by_date = change(:year => d.year, :month => d.month, :day => d.day)
-
seconds_to_advance = \
-
options.fetch(:seconds, 0) +
-
options.fetch(:minutes, 0) * 60 +
-
options.fetch(:hours, 0) * 3600
-
-
if seconds_to_advance.zero?
-
time_advanced_by_date
-
else
-
time_advanced_by_date.since(seconds_to_advance)
-
end
-
end
-
-
# Returns a new Time representing the time a number of seconds ago, this is basically a wrapper around the Numeric extension
-
1
def ago(seconds)
-
since(-seconds)
-
end
-
-
# Returns a new Time representing the time a number of seconds since the instance time
-
1
def since(seconds)
-
self + seconds
-
rescue
-
to_datetime.since(seconds)
-
end
-
1
alias :in :since
-
-
# Returns a new Time representing the start of the day (0:00)
-
1
def beginning_of_day
-
#(self - seconds_since_midnight).change(usec: 0)
-
change(:hour => 0)
-
end
-
1
alias :midnight :beginning_of_day
-
1
alias :at_midnight :beginning_of_day
-
1
alias :at_beginning_of_day :beginning_of_day
-
-
# Returns a new Time representing the middle of the day (12:00)
-
1
def middle_of_day
-
change(:hour => 12)
-
end
-
1
alias :midday :middle_of_day
-
1
alias :noon :middle_of_day
-
1
alias :at_midday :middle_of_day
-
1
alias :at_noon :middle_of_day
-
1
alias :at_middle_of_day :middle_of_day
-
-
# Returns a new Time representing the end of the day, 23:59:59.999999 (.999999999 in ruby1.9)
-
1
def end_of_day
-
change(
-
:hour => 23,
-
:min => 59,
-
:sec => 59,
-
:usec => Rational(999999999, 1000)
-
)
-
end
-
1
alias :at_end_of_day :end_of_day
-
-
# Returns a new Time representing the start of the hour (x:00)
-
1
def beginning_of_hour
-
change(:min => 0)
-
end
-
1
alias :at_beginning_of_hour :beginning_of_hour
-
-
# Returns a new Time representing the end of the hour, x:59:59.999999 (.999999999 in ruby1.9)
-
1
def end_of_hour
-
change(
-
:min => 59,
-
:sec => 59,
-
:usec => Rational(999999999, 1000)
-
)
-
end
-
1
alias :at_end_of_hour :end_of_hour
-
-
# Returns a new Time representing the start of the minute (x:xx:00)
-
1
def beginning_of_minute
-
change(:sec => 0)
-
end
-
1
alias :at_beginning_of_minute :beginning_of_minute
-
-
# Returns a new Time representing the end of the minute, x:xx:59.999999 (.999999999 in ruby1.9)
-
1
def end_of_minute
-
change(
-
:sec => 59,
-
:usec => Rational(999999999, 1000)
-
)
-
end
-
1
alias :at_end_of_minute :end_of_minute
-
-
# Returns a Range representing the whole day of the current time.
-
1
def all_day
-
beginning_of_day..end_of_day
-
end
-
-
1
def plus_with_duration(other) #:nodoc:
-
if ActiveSupport::Duration === other
-
other.since(self)
-
else
-
plus_without_duration(other)
-
end
-
end
-
1
alias_method :plus_without_duration, :+
-
1
alias_method :+, :plus_with_duration
-
-
1
def minus_with_duration(other) #:nodoc:
-
69
if ActiveSupport::Duration === other
-
other.until(self)
-
else
-
69
minus_without_duration(other)
-
end
-
end
-
1
alias_method :minus_without_duration, :-
-
1
alias_method :-, :minus_with_duration
-
-
# Time#- can also be used to determine the number of seconds between two Time instances.
-
# We're layering on additional behavior so that ActiveSupport::TimeWithZone instances
-
# are coerced into values that Time#- will recognize
-
1
def minus_with_coercion(other)
-
69
other = other.comparable_time if other.respond_to?(:comparable_time)
-
69
other.is_a?(DateTime) ? to_f - other.to_f : minus_without_coercion(other)
-
end
-
1
alias_method :minus_without_coercion, :-
-
1
alias_method :-, :minus_with_coercion
-
-
# Layers additional behavior on Time#<=> so that DateTime and ActiveSupport::TimeWithZone instances
-
# can be chronologically compared with a Time
-
1
def compare_with_coercion(other)
-
# we're avoiding Time#to_datetime cause it's expensive
-
77
if other.is_a?(Time)
-
77
compare_without_coercion(other.to_time)
-
else
-
to_datetime <=> other
-
end
-
end
-
1
alias_method :compare_without_coercion, :<=>
-
1
alias_method :<=>, :compare_with_coercion
-
-
# Layers additional behavior on Time#eql? so that ActiveSupport::TimeWithZone instances
-
# can be eql? to an equivalent Time
-
1
def eql_with_coercion(other)
-
# if other is an ActiveSupport::TimeWithZone, coerce a Time instance from it so we can do eql? comparison
-
other = other.comparable_time if other.respond_to?(:comparable_time)
-
eql_without_coercion(other)
-
end
-
1
alias_method :eql_without_coercion, :eql?
-
1
alias_method :eql?, :eql_with_coercion
-
-
end
-
1
require 'active_support/inflector/methods'
-
1
require 'active_support/values/time_zone'
-
-
1
class Time
-
1
DATE_FORMATS = {
-
:db => '%Y-%m-%d %H:%M:%S',
-
:number => '%Y%m%d%H%M%S',
-
:nsec => '%Y%m%d%H%M%S%9N',
-
:time => '%H:%M',
-
:short => '%d %b %H:%M',
-
:long => '%B %d, %Y %H:%M',
-
:long_ordinal => lambda { |time|
-
day_format = ActiveSupport::Inflector.ordinalize(time.day)
-
time.strftime("%B #{day_format}, %Y %H:%M")
-
},
-
:rfc822 => lambda { |time|
-
offset_format = time.formatted_offset(false)
-
time.strftime("%a, %d %b %Y %H:%M:%S #{offset_format}")
-
},
-
:iso8601 => lambda { |time| time.iso8601 }
-
}
-
-
# Converts to a formatted string. See DATE_FORMATS for builtin formats.
-
#
-
# This method is aliased to <tt>to_s</tt>.
-
#
-
# time = Time.now # => Thu Jan 18 06:10:17 CST 2007
-
#
-
# time.to_formatted_s(:time) # => "06:10"
-
# time.to_s(:time) # => "06:10"
-
#
-
# time.to_formatted_s(:db) # => "2007-01-18 06:10:17"
-
# time.to_formatted_s(:number) # => "20070118061017"
-
# time.to_formatted_s(:short) # => "18 Jan 06:10"
-
# time.to_formatted_s(:long) # => "January 18, 2007 06:10"
-
# time.to_formatted_s(:long_ordinal) # => "January 18th, 2007 06:10"
-
# time.to_formatted_s(:rfc822) # => "Thu, 18 Jan 2007 06:10:17 -0600"
-
# time.to_formatted_s(:iso8601) # => "2007-01-18T06:10:17-06:00"
-
#
-
# == Adding your own time formats to +to_formatted_s+
-
# You can add your own formats to the Time::DATE_FORMATS hash.
-
# Use the format name as the hash key and either a strftime string
-
# or Proc instance that takes a time argument as the value.
-
#
-
# # config/initializers/time_formats.rb
-
# Time::DATE_FORMATS[:month_and_year] = '%B %Y'
-
# Time::DATE_FORMATS[:short_ordinal] = ->(time) { time.strftime("%B #{time.day.ordinalize}") }
-
1
def to_formatted_s(format = :default)
-
12
if formatter = DATE_FORMATS[format]
-
12
formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter)
-
else
-
to_default_s
-
end
-
end
-
1
alias_method :to_default_s, :to_s
-
1
alias_method :to_s, :to_formatted_s
-
-
# Returns the UTC offset as an +HH:MM formatted string.
-
#
-
# Time.local(2000).formatted_offset # => "-06:00"
-
# Time.local(2000).formatted_offset(false) # => "-0600"
-
1
def formatted_offset(colon = true, alternate_utc_string = nil)
-
utc? && alternate_utc_string || ActiveSupport::TimeZone.seconds_to_utc_offset(utc_offset, colon)
-
end
-
end
-
# Ruby 1.9.2 adds utc_offset and zone to Time, but marshaling only
-
# preserves utc_offset. Preserve zone also, even though it may not
-
# work in some edge cases.
-
1
if Time.local(2010).zone != Marshal.load(Marshal.dump(Time.local(2010))).zone
-
class Time
-
class << self
-
alias_method :_load_without_zone, :_load
-
def _load(marshaled_time)
-
time = _load_without_zone(marshaled_time)
-
time.instance_eval do
-
if zone = defined?(@_zone) && remove_instance_variable('@_zone')
-
ary = to_a
-
ary[0] += subsec if ary[0] == sec
-
ary[-1] = zone
-
utc? ? Time.utc(*ary) : Time.local(*ary)
-
else
-
self
-
end
-
end
-
end
-
end
-
-
alias_method :_dump_without_zone, :_dump
-
def _dump(*args)
-
obj = dup
-
obj.instance_variable_set('@_zone', zone)
-
obj.send :_dump_without_zone, *args
-
end
-
end
-
end
-
1
require 'active_support/time_with_zone'
-
1
require 'active_support/core_ext/date_and_time/zones'
-
-
1
class Time
-
1
include DateAndTime::Zones
-
1
class << self
-
1
attr_accessor :zone_default
-
-
# Returns the TimeZone for the current request, if this has been set (via Time.zone=).
-
# If <tt>Time.zone</tt> has not been set for the current request, returns the TimeZone specified in <tt>config.time_zone</tt>.
-
1
def zone
-
12
Thread.current[:time_zone] || zone_default
-
end
-
-
# Sets <tt>Time.zone</tt> to a TimeZone object for the current request/thread.
-
#
-
# This method accepts any of the following:
-
#
-
# * A Rails TimeZone object.
-
# * An identifier for a Rails TimeZone object (e.g., "Eastern Time (US & Canada)", <tt>-5.hours</tt>).
-
# * A TZInfo::Timezone object.
-
# * An identifier for a TZInfo::Timezone object (e.g., "America/New_York").
-
#
-
# Here's an example of how you might set <tt>Time.zone</tt> on a per request basis and reset it when the request is done.
-
# <tt>current_user.time_zone</tt> just needs to return a string identifying the user's preferred time zone:
-
#
-
# class ApplicationController < ActionController::Base
-
# around_filter :set_time_zone
-
#
-
# def set_time_zone
-
# if logged_in?
-
# Time.use_zone(current_user.time_zone) { yield }
-
# else
-
# yield
-
# end
-
# end
-
# end
-
1
def zone=(time_zone)
-
Thread.current[:time_zone] = find_zone!(time_zone)
-
end
-
-
# Allows override of <tt>Time.zone</tt> locally inside supplied block; resets <tt>Time.zone</tt> to existing value when done.
-
1
def use_zone(time_zone)
-
new_zone = find_zone!(time_zone)
-
begin
-
old_zone, ::Time.zone = ::Time.zone, new_zone
-
yield
-
ensure
-
::Time.zone = old_zone
-
end
-
end
-
-
# Returns a TimeZone instance or nil, or raises an ArgumentError for invalid timezones.
-
1
def find_zone!(time_zone)
-
13
if !time_zone || time_zone.is_a?(ActiveSupport::TimeZone)
-
12
time_zone
-
else
-
# lookup timezone based on identifier (unless we've been passed a TZInfo::Timezone)
-
1
unless time_zone.respond_to?(:period_for_local)
-
1
time_zone = ActiveSupport::TimeZone[time_zone] || TZInfo::Timezone.get(time_zone)
-
end
-
-
# Return if a TimeZone instance, or wrap in a TimeZone instance if a TZInfo::Timezone
-
1
if time_zone.is_a?(ActiveSupport::TimeZone)
-
1
time_zone
-
else
-
ActiveSupport::TimeZone.create(time_zone.name, nil, time_zone)
-
end
-
end
-
rescue TZInfo::InvalidTimezoneIdentifier
-
raise ArgumentError, "Invalid Timezone: #{time_zone}"
-
end
-
-
1
def find_zone(time_zone)
-
find_zone!(time_zone) rescue nil
-
end
-
end
-
end
-
# encoding: utf-8
-
-
1
require 'uri'
-
1
str = "\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E" # Ni-ho-nn-go in UTF-8, means Japanese.
-
1
parser = URI::Parser.new
-
-
1
unless str == parser.unescape(parser.escape(str))
-
1
URI::Parser.class_eval do
-
1
remove_method :unescape
-
1
def unescape(str, escaped = /%[a-fA-F\d]{2}/)
-
# TODO: Are we actually sure that ASCII == UTF-8?
-
# YK: My initial experiments say yes, but let's be sure please
-
enc = str.encoding
-
enc = Encoding::UTF_8 if enc == Encoding::US_ASCII
-
str.gsub(escaped) { [$&[1, 2].hex].pack('C') }.force_encoding(enc)
-
end
-
end
-
end
-
-
1
module URI
-
1
class << self
-
1
def parser
-
59
@parser ||= URI::Parser.new
-
end
-
end
-
end
-
1
require 'set'
-
1
require 'thread'
-
1
require 'thread_safe'
-
1
require 'pathname'
-
1
require 'active_support/core_ext/module/aliasing'
-
1
require 'active_support/core_ext/module/attribute_accessors'
-
1
require 'active_support/core_ext/module/introspection'
-
1
require 'active_support/core_ext/module/anonymous'
-
1
require 'active_support/core_ext/module/qualified_const'
-
1
require 'active_support/core_ext/object/blank'
-
1
require 'active_support/core_ext/kernel/reporting'
-
1
require 'active_support/core_ext/load_error'
-
1
require 'active_support/core_ext/name_error'
-
1
require 'active_support/core_ext/string/starts_ends_with'
-
1
require 'active_support/inflector'
-
-
1
module ActiveSupport #:nodoc:
-
1
module Dependencies #:nodoc:
-
1
extend self
-
-
# Should we turn on Ruby warnings on the first load of dependent files?
-
1
mattr_accessor :warnings_on_first_load
-
1
self.warnings_on_first_load = false
-
-
# All files ever loaded.
-
1
mattr_accessor :history
-
1
self.history = Set.new
-
-
# All files currently loaded.
-
1
mattr_accessor :loaded
-
1
self.loaded = Set.new
-
-
# Should we load files or require them?
-
1
mattr_accessor :mechanism
-
1
self.mechanism = ENV['NO_RELOAD'] ? :require : :load
-
-
# The set of directories from which we may automatically load files. Files
-
# under these directories will be reloaded on each request in development mode,
-
# unless the directory also appears in autoload_once_paths.
-
1
mattr_accessor :autoload_paths
-
1
self.autoload_paths = []
-
-
# The set of directories from which automatically loaded constants are loaded
-
# only once. All directories in this set must also be present in +autoload_paths+.
-
1
mattr_accessor :autoload_once_paths
-
1
self.autoload_once_paths = []
-
-
# An array of qualified constant names that have been loaded. Adding a name
-
# to this array will cause it to be unloaded the next time Dependencies are
-
# cleared.
-
1
mattr_accessor :autoloaded_constants
-
1
self.autoloaded_constants = []
-
-
# An array of constant names that need to be unloaded on every request. Used
-
# to allow arbitrary constants to be marked for unloading.
-
1
mattr_accessor :explicitly_unloadable_constants
-
1
self.explicitly_unloadable_constants = []
-
-
# The logger is used for generating information on the action run-time
-
# (including benchmarking) if available. Can be set to nil for no logging.
-
# Compatible with both Ruby's own Logger and Log4r loggers.
-
1
mattr_accessor :logger
-
-
# Set to +true+ to enable logging of const_missing and file loads.
-
1
mattr_accessor :log_activity
-
1
self.log_activity = false
-
-
# The WatchStack keeps a stack of the modules being watched as files are
-
# loaded. If a file in the process of being loaded (parent.rb) triggers the
-
# load of another file (child.rb) the stack will ensure that child.rb
-
# handles the new constants.
-
#
-
# If child.rb is being autoloaded, its constants will be added to
-
# autoloaded_constants. If it was being `require`d, they will be discarded.
-
#
-
# This is handled by walking back up the watch stack and adding the constants
-
# found by child.rb to the list of original constants in parent.rb.
-
1
class WatchStack
-
1
include Enumerable
-
-
# @watching is a stack of lists of constants being watched. For instance,
-
# if parent.rb is autoloaded, the stack will look like [[Object]]. If
-
# parent.rb then requires namespace/child.rb, the stack will look like
-
# [[Object], [Namespace]].
-
-
1
def initialize
-
1
@watching = []
-
1
@stack = Hash.new { |h,k| h[k] = [] }
-
end
-
-
1
def each(&block)
-
@stack.each(&block)
-
end
-
-
1
def watching?
-
151
!@watching.empty?
-
end
-
-
# Returns a list of new constants found since the last call to
-
# <tt>watch_namespaces</tt>.
-
1
def new_constants
-
constants = []
-
-
# Grab the list of namespaces that we're looking for new constants under
-
@watching.last.each do |namespace|
-
# Retrieve the constants that were present under the namespace when watch_namespaces
-
# was originally called
-
original_constants = @stack[namespace].last
-
-
mod = Inflector.constantize(namespace) if Dependencies.qualified_const_defined?(namespace)
-
next unless mod.is_a?(Module)
-
-
# Get a list of the constants that were added
-
new_constants = mod.local_constants - original_constants
-
-
# self[namespace] returns an Array of the constants that are being evaluated
-
# for that namespace. For instance, if parent.rb requires child.rb, the first
-
# element of self[Object] will be an Array of the constants that were present
-
# before parent.rb was required. The second element will be an Array of the
-
# constants that were present before child.rb was required.
-
@stack[namespace].each do |namespace_constants|
-
namespace_constants.concat(new_constants)
-
end
-
-
# Normalize the list of new constants, and add them to the list we will return
-
new_constants.each do |suffix|
-
constants << ([namespace, suffix] - ["Object"]).join("::")
-
end
-
end
-
constants
-
ensure
-
# A call to new_constants is always called after a call to watch_namespaces
-
pop_modules(@watching.pop)
-
end
-
-
# Add a set of modules to the watch stack, remembering the initial
-
# constants.
-
1
def watch_namespaces(namespaces)
-
@watching << namespaces.map do |namespace|
-
module_name = Dependencies.to_constant_name(namespace)
-
original_constants = Dependencies.qualified_const_defined?(module_name) ?
-
Inflector.constantize(module_name).local_constants : []
-
-
@stack[module_name] << original_constants
-
module_name
-
end
-
end
-
-
1
private
-
1
def pop_modules(modules)
-
modules.each { |mod| @stack[mod].pop }
-
end
-
end
-
-
# An internal stack used to record which constants are loaded by any block.
-
1
mattr_accessor :constant_watch_stack
-
1
self.constant_watch_stack = WatchStack.new
-
-
# Module includes this module.
-
1
module ModuleConstMissing #:nodoc:
-
1
def self.append_features(base)
-
1
base.class_eval do
-
# Emulate #exclude via an ivar
-
1
return if defined?(@_const_missing) && @_const_missing
-
1
@_const_missing = instance_method(:const_missing)
-
1
remove_method(:const_missing)
-
end
-
1
super
-
end
-
-
1
def self.exclude_from(base)
-
base.class_eval do
-
define_method :const_missing, @_const_missing
-
@_const_missing = nil
-
end
-
end
-
-
1
def const_missing(const_name)
-
16
from_mod = anonymous? ? guess_for_anonymous(const_name) : self
-
16
Dependencies.load_missing_constant(from_mod, const_name)
-
end
-
-
# Dependencies assumes the name of the module reflects the nesting (unless
-
# it can be proven that is not the case), and the path to the file that
-
# defines the constant. Anonymous modules cannot follow these conventions
-
# and we assume therefore the user wants to refer to a top-level constant.
-
1
def guess_for_anonymous(const_name)
-
if Object.const_defined?(const_name)
-
raise NameError.new "#{const_name} cannot be autoloaded from an anonymous class or module", const_name
-
else
-
Object
-
end
-
end
-
-
1
def unloadable(const_desc = self)
-
super(const_desc)
-
end
-
end
-
-
# Object includes this module.
-
1
module Loadable #:nodoc:
-
1
def self.exclude_from(base)
-
base.class_eval { define_method(:load, Kernel.instance_method(:load)) }
-
end
-
-
1
def require_or_load(file_name)
-
Dependencies.require_or_load(file_name)
-
end
-
-
# Interprets a file using <tt>mechanism</tt> and marks its defined
-
# constants as autoloaded. <tt>file_name</tt> can be either a string or
-
# respond to <tt>to_path</tt>.
-
#
-
# Use this method in code that absolutely needs a certain constant to be
-
# defined at that point. A typical use case is to make constant name
-
# resolution deterministic for constants with the same relative name in
-
# different namespaces whose evaluation would depend on load order
-
# otherwise.
-
1
def require_dependency(file_name, message = "No such file to load -- %s")
-
9
file_name = file_name.to_path if file_name.respond_to?(:to_path)
-
9
unless file_name.is_a?(String)
-
raise ArgumentError, "the file name must either be a String or implement #to_path -- you passed #{file_name.inspect}"
-
end
-
-
9
Dependencies.depend_on(file_name, message)
-
end
-
-
1
def load_dependency(file)
-
762
if Dependencies.load? && ActiveSupport::Dependencies.constant_watch_stack.watching?
-
Dependencies.new_constants_in(Object) { yield }
-
else
-
762
yield
-
end
-
rescue Exception => exception # errors from loading file
-
6
exception.blame_file! file if exception.respond_to? :blame_file!
-
6
raise
-
end
-
-
1
def load(file, wrap = false)
-
10
result = false
-
20
load_dependency(file) { result = super }
-
10
result
-
end
-
-
1
def require(file)
-
752
result = false
-
1504
load_dependency(file) { result = super }
-
746
result
-
end
-
-
# Mark the given constant as unloadable. Unloadable constants are removed
-
# each time dependencies are cleared.
-
#
-
# Note that marking a constant for unloading need only be done once. Setup
-
# or init scripts may list each unloadable constant that may need unloading;
-
# each constant will be removed for every subsequent clear, as opposed to
-
# for the first clear.
-
#
-
# The provided constant descriptor may be a (non-anonymous) module or class,
-
# or a qualified constant name as a string or symbol.
-
#
-
# Returns +true+ if the constant was not previously marked for unloading,
-
# +false+ otherwise.
-
1
def unloadable(const_desc)
-
Dependencies.mark_for_unload const_desc
-
end
-
end
-
-
# Exception file-blaming.
-
1
module Blamable #:nodoc:
-
1
def blame_file!(file)
-
6
(@blamed_files ||= []).unshift file
-
end
-
-
1
def blamed_files
-
2
@blamed_files ||= []
-
end
-
-
1
def describe_blame
-
return nil if blamed_files.empty?
-
"This error occurred while loading the following files:\n #{blamed_files.join "\n "}"
-
end
-
-
1
def copy_blame!(exc)
-
2
@blamed_files = exc.blamed_files.clone
-
2
self
-
end
-
end
-
-
1
def hook!
-
2
Object.class_eval { include Loadable }
-
2
Module.class_eval { include ModuleConstMissing }
-
2
Exception.class_eval { include Blamable }
-
end
-
-
1
def unhook!
-
ModuleConstMissing.exclude_from(Module)
-
Loadable.exclude_from(Object)
-
end
-
-
1
def load?
-
773
mechanism == :load
-
end
-
-
1
def depend_on(file_name, message = "No such file to load -- %s.rb")
-
9
path = search_for_file(file_name)
-
9
require_or_load(path || file_name)
-
rescue LoadError => load_error
-
2
if file_name = load_error.message[/ -- (.*?)(\.rb)?$/, 1]
-
2
load_error.message.replace(message % file_name)
-
2
load_error.copy_blame!(load_error)
-
end
-
2
raise
-
end
-
-
1
def clear
-
log_call
-
loaded.clear
-
remove_unloadable_constants!
-
end
-
-
1
def require_or_load(file_name, const_path = nil)
-
15
log_call file_name, const_path
-
15
file_name = $` if file_name =~ /\.rb\z/
-
15
expanded = File.expand_path(file_name)
-
15
return if loaded.include?(expanded)
-
-
# Record that we've seen this file *before* loading it to avoid an
-
# infinite loop with mutual dependencies.
-
11
loaded << expanded
-
-
11
begin
-
11
if load?
-
log "loading #{file_name}"
-
-
# Enable warnings if this file has not been loaded before and
-
# warnings_on_first_load is set.
-
load_args = ["#{file_name}.rb"]
-
load_args << const_path unless const_path.nil?
-
-
if !warnings_on_first_load or history.include?(expanded)
-
result = load_file(*load_args)
-
else
-
enable_warnings { result = load_file(*load_args) }
-
end
-
else
-
11
log "requiring #{file_name}"
-
11
result = require file_name
-
end
-
rescue Exception
-
2
loaded.delete expanded
-
2
raise
-
end
-
-
# Record history *after* loading so first load gets warnings.
-
9
history << expanded
-
9
result
-
end
-
-
# Is the provided constant path defined?
-
1
def qualified_const_defined?(path)
-
16
Object.qualified_const_defined?(path.sub(/^::/, ''), false)
-
end
-
-
# Given +path+, a filesystem path to a ruby file, return an array of
-
# constant paths which would cause Dependencies to attempt to load this
-
# file.
-
1
def loadable_constants_for_path(path, bases = autoload_paths)
-
path = $` if path =~ /\.rb\z/
-
expanded_path = File.expand_path(path)
-
paths = []
-
-
bases.each do |root|
-
expanded_root = File.expand_path(root)
-
next unless %r{\A#{Regexp.escape(expanded_root)}(/|\\)} =~ expanded_path
-
-
nesting = expanded_path[(expanded_root.size)..-1]
-
nesting = nesting[1..-1] if nesting && nesting[0] == ?/
-
next if nesting.blank?
-
-
paths << nesting.camelize
-
end
-
-
paths.uniq!
-
paths
-
end
-
-
# Search for a file in autoload_paths matching the provided suffix.
-
1
def search_for_file(path_suffix)
-
25
path_suffix = path_suffix.sub(/(\.rb)?$/, ".rb")
-
-
25
autoload_paths.each do |root|
-
141
path = File.join(root, path_suffix)
-
141
return path if File.file? path
-
end
-
nil # Gee, I sure wish we had first_match ;-)
-
end
-
-
# Does the provided path_suffix correspond to an autoloadable module?
-
# Instead of returning a boolean, the autoload base for this module is
-
# returned.
-
1
def autoloadable_module?(path_suffix)
-
10
autoload_paths.each do |load_path|
-
80
return load_path if File.directory? File.join(load_path, path_suffix)
-
end
-
nil
-
end
-
-
1
def load_once_path?(path)
-
# to_s works around a ruby1.9 issue where String#starts_with?(Pathname)
-
# will raise a TypeError: no implicit conversion of Pathname into String
-
autoload_once_paths.any? { |base| path.starts_with? base.to_s }
-
end
-
-
# Attempt to autoload the provided module name by searching for a directory
-
# matching the expected path suffix. If found, the module is created and
-
# assigned to +into+'s constants with the name +const_name+. Provided that
-
# the directory was loaded from a reloadable base path, it is added to the
-
# set of constants that are to be unloaded.
-
1
def autoload_module!(into, const_name, qualified_name, path_suffix)
-
10
return nil unless base_path = autoloadable_module?(path_suffix)
-
mod = Module.new
-
into.const_set const_name, mod
-
autoloaded_constants << qualified_name unless autoload_once_paths.include?(base_path)
-
mod
-
end
-
-
# Load the file at the provided path. +const_paths+ is a set of qualified
-
# constant names. When loading the file, Dependencies will watch for the
-
# addition of these constants. Each that is defined will be marked as
-
# autoloaded, and will be removed when Dependencies.clear is next called.
-
#
-
# If the second parameter is left off, then Dependencies will construct a
-
# set of names that the file at +path+ may define. See
-
# +loadable_constants_for_path+ for more details.
-
1
def load_file(path, const_paths = loadable_constants_for_path(path))
-
log_call path, const_paths
-
const_paths = [const_paths].compact unless const_paths.is_a? Array
-
parent_paths = const_paths.collect { |const_path| const_path[/.*(?=::)/] || ::Object }
-
-
result = nil
-
newly_defined_paths = new_constants_in(*parent_paths) do
-
result = Kernel.load path
-
end
-
-
autoloaded_constants.concat newly_defined_paths unless load_once_path?(path)
-
autoloaded_constants.uniq!
-
log "loading #{path} defined #{newly_defined_paths * ', '}" unless newly_defined_paths.empty?
-
result
-
end
-
-
# Returns the constant path for the provided parent and constant name.
-
1
def qualified_name_for(mod, name)
-
16
mod_name = to_constant_name mod
-
16
mod_name == "Object" ? name.to_s : "#{mod_name}::#{name}"
-
end
-
-
# Load the constant named +const_name+ which is missing from +from_mod+. If
-
# it is not possible to load the constant into from_mod, try its parent
-
# module using +const_missing+.
-
1
def load_missing_constant(from_mod, const_name)
-
16
log_call from_mod, const_name
-
-
16
unless qualified_const_defined?(from_mod.name) && Inflector.constantize(from_mod.name).equal?(from_mod)
-
raise ArgumentError, "A copy of #{from_mod} has been removed from the module tree but is still active!"
-
end
-
-
16
qualified_name = qualified_name_for from_mod, const_name
-
16
path_suffix = qualified_name.underscore
-
-
16
file_path = search_for_file(path_suffix)
-
-
16
if file_path
-
6
expanded = File.expand_path(file_path)
-
6
expanded.sub!(/\.rb\z/, '')
-
-
6
if loaded.include?(expanded)
-
raise "Circular dependency detected while autoloading constant #{qualified_name}"
-
else
-
6
require_or_load(expanded, qualified_name)
-
6
raise LoadError, "Unable to autoload constant #{qualified_name}, expected #{file_path} to define it" unless from_mod.const_defined?(const_name, false)
-
6
return from_mod.const_get(const_name)
-
end
-
elsif mod = autoload_module!(from_mod, const_name, qualified_name, path_suffix)
-
return mod
-
10
elsif (parent = from_mod.parent) && parent != from_mod &&
-
10
! from_mod.parents.any? { |p| p.const_defined?(const_name, false) }
-
# If our parents do not have a constant named +const_name+ then we are free
-
# to attempt to load upwards. If they do have such a constant, then this
-
# const_missing must be due to from_mod::const_name, which should not
-
# return constants from from_mod's parents.
-
3
begin
-
# Since Ruby does not pass the nesting at the point the unknown
-
# constant triggered the callback we cannot fully emulate constant
-
# name lookup and need to make a trade-off: we are going to assume
-
# that the nesting in the body of Foo::Bar is [Foo::Bar, Foo] even
-
# though it might not be. Counterexamples are
-
#
-
# class Foo::Bar
-
# Module.nesting # => [Foo::Bar]
-
# end
-
#
-
# or
-
#
-
# module M::N
-
# module S::T
-
# Module.nesting # => [S::T, M::N]
-
# end
-
# end
-
#
-
# for example.
-
3
return parent.const_missing(const_name)
-
rescue NameError => e
-
raise unless e.missing_name? qualified_name_for(parent, const_name)
-
end
-
end
-
-
7
name_error = NameError.new("uninitialized constant #{qualified_name}", const_name)
-
742
name_error.set_backtrace(caller.reject {|l| l.starts_with? __FILE__ })
-
7
raise name_error
-
end
-
-
# Remove the constants that have been autoloaded, and those that have been
-
# marked for unloading. Before each constant is removed a callback is sent
-
# to its class/module if it implements +before_remove_const+.
-
#
-
# The callback implementation should be restricted to cleaning up caches, etc.
-
# as the environment will be in an inconsistent state, e.g. other constants
-
# may have already been unloaded and not accessible.
-
1
def remove_unloadable_constants!
-
autoloaded_constants.each { |const| remove_constant const }
-
autoloaded_constants.clear
-
Reference.clear!
-
explicitly_unloadable_constants.each { |const| remove_constant const }
-
end
-
-
1
class ClassCache
-
1
def initialize
-
1
@store = ThreadSafe::Cache.new
-
end
-
-
1
def empty?
-
@store.empty?
-
end
-
-
1
def key?(key)
-
@store.key?(key)
-
end
-
-
1
def get(key)
-
18
key = key.name if key.respond_to?(:name)
-
18
@store[key] ||= Inflector.constantize(key)
-
end
-
1
alias :[] :get
-
-
1
def safe_get(key)
-
key = key.name if key.respond_to?(:name)
-
@store[key] ||= Inflector.safe_constantize(key)
-
end
-
-
1
def store(klass)
-
return self unless klass.respond_to?(:name)
-
raise(ArgumentError, 'anonymous classes cannot be cached') if klass.name.empty?
-
@store[klass.name] = klass
-
self
-
end
-
-
1
def clear!
-
@store.clear
-
end
-
end
-
-
1
Reference = ClassCache.new
-
-
# Store a reference to a class +klass+.
-
1
def reference(klass)
-
Reference.store klass
-
end
-
-
# Get the reference for class named +name+.
-
# Raises an exception if referenced class does not exist.
-
1
def constantize(name)
-
14
Reference.get(name)
-
end
-
-
# Get the reference for class named +name+ if one exists.
-
# Otherwise returns +nil+.
-
1
def safe_constantize(name)
-
Reference.safe_get(name)
-
end
-
-
# Determine if the given constant has been automatically loaded.
-
1
def autoloaded?(desc)
-
return false if desc.is_a?(Module) && desc.anonymous?
-
name = to_constant_name desc
-
return false unless qualified_const_defined? name
-
return autoloaded_constants.include?(name)
-
end
-
-
# Will the provided constant descriptor be unloaded?
-
1
def will_unload?(const_desc)
-
autoloaded?(const_desc) ||
-
explicitly_unloadable_constants.include?(to_constant_name(const_desc))
-
end
-
-
# Mark the provided constant name for unloading. This constant will be
-
# unloaded on each request, not just the next one.
-
1
def mark_for_unload(const_desc)
-
name = to_constant_name const_desc
-
if explicitly_unloadable_constants.include? name
-
false
-
else
-
explicitly_unloadable_constants << name
-
true
-
end
-
end
-
-
# Run the provided block and detect the new constants that were loaded during
-
# its execution. Constants may only be regarded as 'new' once -- so if the
-
# block calls +new_constants_in+ again, then the constants defined within the
-
# inner call will not be reported in this one.
-
#
-
# If the provided block does not run to completion, and instead raises an
-
# exception, any new constants are regarded as being only partially defined
-
# and will be removed immediately.
-
1
def new_constants_in(*descs)
-
log_call(*descs)
-
-
constant_watch_stack.watch_namespaces(descs)
-
aborting = true
-
-
begin
-
yield # Now yield to the code that is to define new constants.
-
aborting = false
-
ensure
-
new_constants = constant_watch_stack.new_constants
-
-
log "New constants: #{new_constants * ', '}"
-
return new_constants unless aborting
-
-
log "Error during loading, removing partially loaded constants "
-
new_constants.each { |c| remove_constant(c) }.clear
-
end
-
-
[]
-
end
-
-
# Convert the provided const desc to a qualified constant name (as a string).
-
# A module, class, symbol, or string may be provided.
-
1
def to_constant_name(desc) #:nodoc:
-
16
case desc
-
when String then desc.sub(/^::/, '')
-
when Symbol then desc.to_s
-
when Module
-
desc.name ||
-
16
raise(ArgumentError, "Anonymous modules have no name to be referenced by")
-
else raise TypeError, "Not a valid constant descriptor: #{desc.inspect}"
-
end
-
end
-
-
1
def remove_constant(const) #:nodoc:
-
# Normalize ::Foo, ::Object::Foo, Object::Foo, Object::Object::Foo, etc. as Foo.
-
normalized = const.to_s.sub(/\A::/, '')
-
normalized.sub!(/\A(Object::)+/, '')
-
-
constants = normalized.split('::')
-
to_remove = constants.pop
-
-
# Remove the file path from the loaded list.
-
file_path = search_for_file(const.underscore)
-
if file_path
-
expanded = File.expand_path(file_path)
-
expanded.sub!(/\.rb\z/, '')
-
self.loaded.delete(expanded)
-
end
-
-
if constants.empty?
-
parent = Object
-
else
-
# This method is robust to non-reachable constants.
-
#
-
# Non-reachable constants may be passed if some of the parents were
-
# autoloaded and already removed. It is easier to do a sanity check
-
# here than require the caller to be clever. We check the parent
-
# rather than the very const argument because we do not want to
-
# trigger Kernel#autoloads, see the comment below.
-
parent_name = constants.join('::')
-
return unless qualified_const_defined?(parent_name)
-
parent = constantize(parent_name)
-
end
-
-
log "removing constant #{const}"
-
-
# In an autoloaded user.rb like this
-
#
-
# autoload :Foo, 'foo'
-
#
-
# class User < ActiveRecord::Base
-
# end
-
#
-
# we correctly register "Foo" as being autoloaded. But if the app does
-
# not use the "Foo" constant we need to be careful not to trigger
-
# loading "foo.rb" ourselves. While #const_defined? and #const_get? do
-
# require the file, #autoload? and #remove_const don't.
-
#
-
# We are going to remove the constant nonetheless ---which exists as
-
# far as Ruby is concerned--- because if the user removes the macro
-
# call from a class or module that were not autoloaded, as in the
-
# example above with Object, accessing to that constant must err.
-
unless parent.autoload?(to_remove)
-
begin
-
constantized = parent.const_get(to_remove, false)
-
rescue NameError
-
log "the constant #{const} is not reachable anymore, skipping"
-
return
-
else
-
constantized.before_remove_const if constantized.respond_to?(:before_remove_const)
-
end
-
end
-
-
begin
-
parent.instance_eval { remove_const to_remove }
-
rescue NameError
-
log "the constant #{const} is not reachable anymore, skipping"
-
end
-
end
-
-
1
protected
-
1
def log_call(*args)
-
31
if log_activity?
-
arg_str = args.collect { |arg| arg.inspect } * ', '
-
/in `([a-z_\?\!]+)'/ =~ caller(1).first
-
selector = $1 || '<unknown>'
-
log "called #{selector}(#{arg_str})"
-
end
-
end
-
-
1
def log(msg)
-
11
logger.debug "Dependencies: #{msg}" if log_activity?
-
end
-
-
1
def log_activity?
-
42
logger && log_activity
-
end
-
end
-
end
-
-
1
ActiveSupport::Dependencies.hook!
-
1
require "active_support/inflector/methods"
-
-
1
module ActiveSupport
-
# Autoload and eager load conveniences for your library.
-
#
-
# This module allows you to define autoloads based on
-
# Rails conventions (i.e. no need to define the path
-
# it is automatically guessed based on the filename)
-
# and also define a set of constants that needs to be
-
# eager loaded:
-
#
-
# module MyLib
-
# extend ActiveSupport::Autoload
-
#
-
# autoload :Model
-
#
-
# eager_autoload do
-
# autoload :Cache
-
# end
-
# end
-
#
-
# Then your library can be eager loaded by simply calling:
-
#
-
# MyLib.eager_load!
-
1
module Autoload
-
1
def self.extended(base) # :nodoc:
-
25
base.class_eval do
-
25
@_autoloads = {}
-
25
@_under_path = nil
-
25
@_at_path = nil
-
25
@_eager_autoload = false
-
end
-
end
-
-
1
def autoload(const_name, path = @_at_path)
-
343
unless path
-
255
full = [name, @_under_path, const_name.to_s].compact.join("::")
-
255
path = Inflector.underscore(full)
-
end
-
-
343
if @_eager_autoload
-
132
@_autoloads[const_name] = path
-
end
-
-
343
super const_name, path
-
end
-
-
1
def autoload_under(path)
-
7
@_under_path, old_path = path, @_under_path
-
7
yield
-
ensure
-
7
@_under_path = old_path
-
end
-
-
1
def autoload_at(path)
-
7
@_at_path, old_path = path, @_at_path
-
7
yield
-
ensure
-
7
@_at_path = old_path
-
end
-
-
1
def eager_autoload
-
17
old_eager, @_eager_autoload = @_eager_autoload, true
-
17
yield
-
ensure
-
17
@_eager_autoload = old_eager
-
end
-
-
1
def eager_load!
-
@_autoloads.values.each { |file| require file }
-
end
-
-
1
def autoloads
-
@_autoloads
-
end
-
end
-
end
-
1
require 'singleton'
-
-
1
module ActiveSupport
-
# \Deprecation specifies the API used by Rails to deprecate methods, instance
-
# variables, objects and constants.
-
1
class Deprecation
-
# active_support.rb sets an autoload for ActiveSupport::Deprecation.
-
#
-
# If these requires were at the top of the file the constant would not be
-
# defined by the time their files were loaded. Since some of them reopen
-
# ActiveSupport::Deprecation its autoload would be triggered, resulting in
-
# a circular require warning for active_support/deprecation.rb.
-
#
-
# So, we define the constant first, and load dependencies later.
-
1
require 'active_support/deprecation/instance_delegator'
-
1
require 'active_support/deprecation/behaviors'
-
1
require 'active_support/deprecation/reporting'
-
1
require 'active_support/deprecation/method_wrappers'
-
1
require 'active_support/deprecation/proxy_wrappers'
-
1
require 'active_support/core_ext/module/deprecation'
-
-
1
include Singleton
-
1
include InstanceDelegator
-
1
include Behavior
-
1
include Reporting
-
1
include MethodWrapper
-
-
# The version number in which the deprecated behavior will be removed, by default.
-
1
attr_accessor :deprecation_horizon
-
-
# It accepts two parameters on initialization. The first is a version of library
-
# and the second is a library name
-
#
-
# ActiveSupport::Deprecation.new('2.0', 'MyLibrary')
-
1
def initialize(deprecation_horizon = '4.2', gem_name = 'Rails')
-
1
self.gem_name = gem_name
-
1
self.deprecation_horizon = deprecation_horizon
-
# By default, warnings are not silenced and debugging is off.
-
1
self.silenced = false
-
1
self.debug = false
-
end
-
end
-
end
-
1
require "active_support/notifications"
-
-
1
module ActiveSupport
-
1
class DeprecationException < StandardError
-
end
-
-
1
class Deprecation
-
# Default warning behaviors per Rails.env.
-
1
DEFAULT_BEHAVIORS = {
-
raise: ->(message, callstack) {
-
e = DeprecationException.new(message)
-
e.set_backtrace(callstack)
-
raise e
-
},
-
-
stderr: ->(message, callstack) {
-
$stderr.puts(message)
-
$stderr.puts callstack.join("\n ") if debug
-
},
-
-
log: ->(message, callstack) {
-
logger =
-
if defined?(Rails) && Rails.logger
-
Rails.logger
-
else
-
require 'active_support/logger'
-
ActiveSupport::Logger.new($stderr)
-
end
-
logger.warn message
-
logger.debug callstack.join("\n ") if debug
-
},
-
-
notify: ->(message, callstack) {
-
ActiveSupport::Notifications.instrument("deprecation.rails",
-
:message => message, :callstack => callstack)
-
},
-
-
silence: ->(message, callstack) {},
-
}
-
-
1
module Behavior
-
# Whether to print a backtrace along with the warning.
-
1
attr_accessor :debug
-
-
# Returns the current behavior or if one isn't set, defaults to +:stderr+.
-
1
def behavior
-
@behavior ||= [DEFAULT_BEHAVIORS[:stderr]]
-
end
-
-
# Sets the behavior to the specified value. Can be a single value, array,
-
# or an object that responds to +call+.
-
#
-
# Available behaviors:
-
#
-
# [+raise+] Raise <tt>ActiveSupport::DeprecationException</tt>.
-
# [+stderr+] Log all deprecation warnings to +$stderr+.
-
# [+log+] Log all deprecation warnings to +Rails.logger+.
-
# [+notify+] Use +ActiveSupport::Notifications+ to notify +deprecation.rails+.
-
# [+silence+] Do nothing.
-
#
-
# Setting behaviors only affects deprecations that happen after boot time.
-
# Deprecation warnings raised by gems are not affected by this setting
-
# because they happen before Rails boots up.
-
#
-
# ActiveSupport::Deprecation.behavior = :stderr
-
# ActiveSupport::Deprecation.behavior = [:stderr, :log]
-
# ActiveSupport::Deprecation.behavior = MyCustomHandler
-
# ActiveSupport::Deprecation.behavior = ->(message, callstack) {
-
# # custom stuff
-
# }
-
1
def behavior=(behavior)
-
2
@behavior = Array(behavior).map { |b| DEFAULT_BEHAVIORS[b] || b }
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/kernel/singleton_class'
-
1
require 'active_support/core_ext/module/delegation'
-
-
1
module ActiveSupport
-
1
class Deprecation
-
1
module InstanceDelegator # :nodoc:
-
1
def self.included(base)
-
1
base.extend(ClassMethods)
-
1
base.public_class_method :new
-
end
-
-
1
module ClassMethods # :nodoc:
-
1
def include(included_module)
-
15
included_module.instance_methods.each { |m| method_added(m) }
-
3
super
-
end
-
-
1
def method_added(method_name)
-
15
singleton_class.delegate(method_name, to: :instance)
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/module/aliasing'
-
1
require 'active_support/core_ext/array/extract_options'
-
-
1
module ActiveSupport
-
1
class Deprecation
-
1
module MethodWrapper
-
# Declare that a method has been deprecated.
-
#
-
# module Fred
-
# extend self
-
#
-
# def foo; end
-
# def bar; end
-
# def baz; end
-
# end
-
#
-
# ActiveSupport::Deprecation.deprecate_methods(Fred, :foo, bar: :qux, baz: 'use Bar#baz instead')
-
# # => [:foo, :bar, :baz]
-
#
-
# Fred.foo
-
# # => "DEPRECATION WARNING: foo is deprecated and will be removed from Rails 4.1."
-
#
-
# Fred.bar
-
# # => "DEPRECATION WARNING: bar is deprecated and will be removed from Rails 4.1 (use qux instead)."
-
#
-
# Fred.baz
-
# # => "DEPRECATION WARNING: baz is deprecated and will be removed from Rails 4.1 (use Bar#baz instead)."
-
1
def deprecate_methods(target_module, *method_names)
-
options = method_names.extract_options!
-
deprecator = options.delete(:deprecator) || ActiveSupport::Deprecation.instance
-
method_names += options.keys
-
-
method_names.each do |method_name|
-
target_module.alias_method_chain(method_name, :deprecation) do |target, punctuation|
-
target_module.send(:define_method, "#{target}_with_deprecation#{punctuation}") do |*args, &block|
-
deprecator.deprecation_warning(method_name, options[method_name])
-
send(:"#{target}_without_deprecation#{punctuation}", *args, &block)
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/inflector/methods'
-
-
1
module ActiveSupport
-
1
class Deprecation
-
1
class DeprecationProxy #:nodoc:
-
1
def self.new(*args, &block)
-
2
object = args.first
-
-
2
return object unless object
-
2
super
-
end
-
-
76
instance_methods.each { |m| undef_method m unless m =~ /^__|^object_id$/ }
-
-
# Don't give a deprecation warning on inspect since test/unit and error
-
# logs rely on it for diagnostics.
-
1
def inspect
-
target.inspect
-
end
-
-
1
private
-
1
def method_missing(called, *args, &block)
-
warn caller, called, args
-
target.__send__(called, *args, &block)
-
end
-
end
-
-
# This DeprecatedObjectProxy transforms object to deprecated object.
-
#
-
# @old_object = DeprecatedObjectProxy.new(Object.new, "Don't use this object anymore!")
-
# @old_object = DeprecatedObjectProxy.new(Object.new, "Don't use this object anymore!", deprecator_instance)
-
#
-
# When someone executes any method except +inspect+ on proxy object this will
-
# trigger +warn+ method on +deprecator_instance+.
-
#
-
# Default deprecator is <tt>ActiveSupport::Deprecation</tt>
-
1
class DeprecatedObjectProxy < DeprecationProxy
-
1
def initialize(object, message, deprecator = ActiveSupport::Deprecation.instance)
-
@object = object
-
@message = message
-
@deprecator = deprecator
-
end
-
-
1
private
-
1
def target
-
@object
-
end
-
-
1
def warn(callstack, called, args)
-
@deprecator.warn(@message, callstack)
-
end
-
end
-
-
# This DeprecatedInstanceVariableProxy transforms instance variable to
-
# deprecated instance variable.
-
#
-
# class Example
-
# def initialize(deprecator)
-
# @request = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new(self, :request, :@request, deprecator)
-
# @_request = :a_request
-
# end
-
#
-
# def request
-
# @_request
-
# end
-
#
-
# def old_request
-
# @request
-
# end
-
# end
-
#
-
# When someone execute any method on @request variable this will trigger
-
# +warn+ method on +deprecator_instance+ and will fetch <tt>@_request</tt>
-
# variable via +request+ method and execute the same method on non-proxy
-
# instance variable.
-
#
-
# Default deprecator is <tt>ActiveSupport::Deprecation</tt>.
-
1
class DeprecatedInstanceVariableProxy < DeprecationProxy
-
1
def initialize(instance, method, var = "@#{method}", deprecator = ActiveSupport::Deprecation.instance)
-
@instance = instance
-
@method = method
-
@var = var
-
@deprecator = deprecator
-
end
-
-
1
private
-
1
def target
-
@instance.__send__(@method)
-
end
-
-
1
def warn(callstack, called, args)
-
@deprecator.warn("#{@var} is deprecated! Call #{@method}.#{called} instead of #{@var}.#{called}. Args: #{args.inspect}", callstack)
-
end
-
end
-
-
# This DeprecatedConstantProxy transforms constant to deprecated constant.
-
#
-
# OLD_CONST = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('OLD_CONST', 'NEW_CONST')
-
# OLD_CONST = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('OLD_CONST', 'NEW_CONST', deprecator_instance)
-
#
-
# When someone use old constant this will trigger +warn+ method on
-
# +deprecator_instance+.
-
#
-
# Default deprecator is <tt>ActiveSupport::Deprecation</tt>.
-
1
class DeprecatedConstantProxy < DeprecationProxy
-
1
def initialize(old_const, new_const, deprecator = ActiveSupport::Deprecation.instance)
-
2
@old_const = old_const
-
2
@new_const = new_const
-
2
@deprecator = deprecator
-
end
-
-
1
def class
-
target.class
-
end
-
-
1
private
-
1
def target
-
ActiveSupport::Inflector.constantize(@new_const.to_s)
-
end
-
-
1
def warn(callstack, called, args)
-
@deprecator.warn("#{@old_const} is deprecated! Use #{@new_const} instead.", callstack)
-
end
-
end
-
end
-
end
-
1
module ActiveSupport
-
1
class Deprecation
-
1
module Reporting
-
# Whether to print a message (silent mode)
-
1
attr_accessor :silenced
-
# Name of gem where method is deprecated
-
1
attr_accessor :gem_name
-
-
# Outputs a deprecation warning to the output configured by
-
# <tt>ActiveSupport::Deprecation.behavior</tt>.
-
#
-
# ActiveSupport::Deprecation.warn('something broke!')
-
# # => "DEPRECATION WARNING: something broke! (called from your_code.rb:1)"
-
1
def warn(message = nil, callstack = nil)
-
return if silenced
-
-
callstack ||= caller(2)
-
deprecation_message(callstack, message).tap do |m|
-
behavior.each { |b| b.call(m, callstack) }
-
end
-
end
-
-
# Silence deprecation warnings within the block.
-
#
-
# ActiveSupport::Deprecation.warn('something broke!')
-
# # => "DEPRECATION WARNING: something broke! (called from your_code.rb:1)"
-
#
-
# ActiveSupport::Deprecation.silence do
-
# ActiveSupport::Deprecation.warn('something broke!')
-
# end
-
# # => nil
-
1
def silence
-
old_silenced, @silenced = @silenced, true
-
yield
-
ensure
-
@silenced = old_silenced
-
end
-
-
1
def deprecation_warning(deprecated_method_name, message = nil, caller_backtrace = nil)
-
caller_backtrace ||= caller(2)
-
deprecated_method_warning(deprecated_method_name, message).tap do |msg|
-
warn(msg, caller_backtrace)
-
end
-
end
-
-
1
private
-
# Outputs a deprecation warning message
-
#
-
# ActiveSupport::Deprecation.deprecated_method_warning(:method_name)
-
# # => "method_name is deprecated and will be removed from Rails #{deprecation_horizon}"
-
# ActiveSupport::Deprecation.deprecated_method_warning(:method_name, :another_method)
-
# # => "method_name is deprecated and will be removed from Rails #{deprecation_horizon} (use another_method instead)"
-
# ActiveSupport::Deprecation.deprecated_method_warning(:method_name, "Optional message")
-
# # => "method_name is deprecated and will be removed from Rails #{deprecation_horizon} (Optional message)"
-
1
def deprecated_method_warning(method_name, message = nil)
-
warning = "#{method_name} is deprecated and will be removed from #{gem_name} #{deprecation_horizon}"
-
case message
-
when Symbol then "#{warning} (use #{message} instead)"
-
when String then "#{warning} (#{message})"
-
else warning
-
end
-
end
-
-
1
def deprecation_message(callstack, message = nil)
-
message ||= "You are using deprecated behavior which will be removed from the next major or minor release."
-
message += '.' unless message =~ /\.$/
-
"DEPRECATION WARNING: #{message} #{deprecation_caller_message(callstack)}"
-
end
-
-
1
def deprecation_caller_message(callstack)
-
file, line, method = extract_callstack(callstack)
-
if file
-
if line && method
-
"(called from #{method} at #{file}:#{line})"
-
else
-
"(called from #{file}:#{line})"
-
end
-
end
-
end
-
-
1
def extract_callstack(callstack)
-
rails_gem_root = File.expand_path("../../../../..", __FILE__) + "/"
-
offending_line = callstack.find { |line| !line.start_with?(rails_gem_root) } || callstack.first
-
if offending_line
-
if md = offending_line.match(/^(.+?):(\d+)(?::in `(.*?)')?/)
-
md.captures
-
else
-
offending_line
-
end
-
end
-
end
-
end
-
end
-
end
-
1
module ActiveSupport
-
# This module provides an internal implementation to track descendants
-
# which is faster than iterating through ObjectSpace.
-
1
module DescendantsTracker
-
1
@@direct_descendants = {}
-
-
1
class << self
-
1
def direct_descendants(klass)
-
@@direct_descendants[klass] || []
-
end
-
-
1
def descendants(klass)
-
67
arr = []
-
67
accumulate_descendants(klass, arr)
-
67
arr
-
end
-
-
1
def clear
-
if defined? ActiveSupport::Dependencies
-
@@direct_descendants.each do |klass, descendants|
-
if ActiveSupport::Dependencies.autoloaded?(klass)
-
@@direct_descendants.delete(klass)
-
else
-
descendants.reject! { |v| ActiveSupport::Dependencies.autoloaded?(v) }
-
end
-
end
-
else
-
@@direct_descendants.clear
-
end
-
end
-
-
# This is the only method that is not thread safe, but is only ever called
-
# during the eager loading phase.
-
1
def store_inherited(klass, descendant)
-
16
(@@direct_descendants[klass] ||= []) << descendant
-
end
-
-
1
private
-
1
def accumulate_descendants(klass, acc)
-
67
if direct_descendants = @@direct_descendants[klass]
-
acc.concat(direct_descendants)
-
direct_descendants.each { |direct_descendant| accumulate_descendants(direct_descendant, acc) }
-
end
-
end
-
end
-
-
1
def inherited(base)
-
16
DescendantsTracker.store_inherited(self, base)
-
16
super
-
end
-
-
1
def direct_descendants
-
DescendantsTracker.direct_descendants(self)
-
end
-
-
1
def descendants
-
DescendantsTracker.descendants(self)
-
end
-
end
-
end
-
1
require 'active_support/proxy_object'
-
1
require 'active_support/core_ext/array/conversions'
-
1
require 'active_support/core_ext/object/acts_like'
-
-
1
module ActiveSupport
-
# Provides accurate date and time measurements using Date#advance and
-
# Time#advance, respectively. It mainly supports the methods on Numeric.
-
#
-
# 1.month.ago # equivalent to Time.now.advance(months: -1)
-
1
class Duration < ProxyObject
-
1
attr_accessor :value, :parts
-
-
1
def initialize(value, parts) #:nodoc:
-
@value, @parts = value, parts
-
end
-
-
# Adds another Duration or a Numeric to this Duration. Numeric values
-
# are treated as seconds.
-
1
def +(other)
-
if Duration === other
-
Duration.new(value + other.value, @parts + other.parts)
-
else
-
Duration.new(value + other, @parts + [[:seconds, other]])
-
end
-
end
-
-
# Subtracts another Duration or a Numeric from this Duration. Numeric
-
# values are treated as seconds.
-
1
def -(other)
-
self + (-other)
-
end
-
-
1
def -@ #:nodoc:
-
Duration.new(-value, parts.map { |type,number| [type, -number] })
-
end
-
-
1
def is_a?(klass) #:nodoc:
-
Duration == klass || value.is_a?(klass)
-
end
-
1
alias :kind_of? :is_a?
-
-
# Returns +true+ if +other+ is also a Duration instance with the
-
# same +value+, or if <tt>other == value</tt>.
-
1
def ==(other)
-
if Duration === other
-
other.value == value
-
else
-
other == value
-
end
-
end
-
-
1
def eql?(other)
-
other.is_a?(Duration) && self == other
-
end
-
-
1
def self.===(other) #:nodoc:
-
69
other.is_a?(Duration)
-
rescue ::NoMethodError
-
false
-
end
-
-
# Calculates a new Time or Date that is as far in the future
-
# as this Duration represents.
-
1
def since(time = ::Time.current)
-
sum(1, time)
-
end
-
1
alias :from_now :since
-
-
# Calculates a new Time or Date that is as far in the past
-
# as this Duration represents.
-
1
def ago(time = ::Time.current)
-
sum(-1, time)
-
end
-
1
alias :until :ago
-
-
1
def inspect #:nodoc:
-
parts.
-
reduce(::Hash.new(0)) { |h,(l,r)| h[l] += r; h }.
-
sort_by {|unit, _ | [:years, :months, :days, :minutes, :seconds].index(unit)}.
-
map {|unit, val| "#{val} #{val == 1 ? unit.to_s.chop : unit.to_s}"}.
-
to_sentence(:locale => :en)
-
end
-
-
1
def as_json(options = nil) #:nodoc:
-
to_i
-
end
-
-
1
protected
-
-
1
def sum(sign, time = ::Time.current) #:nodoc:
-
parts.inject(time) do |t,(type,number)|
-
if t.acts_like?(:time) || t.acts_like?(:date)
-
if type == :seconds
-
t.since(sign * number)
-
else
-
t.advance(type => sign * number)
-
end
-
else
-
raise ::ArgumentError, "expected a time or date, got #{time.inspect}"
-
end
-
end
-
end
-
-
1
private
-
-
# We define it as a workaround to Ruby 2.0.0-p353 bug.
-
# For more information, check rails/rails#13055.
-
# It should be dropped once a new Ruby patch-level
-
# release after 2.0.0-p353 happens.
-
1
def ===(other) #:nodoc:
-
value === other
-
end
-
-
1
def method_missing(method, *args, &block) #:nodoc:
-
value.send(method, *args, &block)
-
end
-
end
-
end
-
1
module ActiveSupport
-
# FileUpdateChecker specifies the API used by Rails to watch files
-
# and control reloading. The API depends on four methods:
-
#
-
# * +initialize+ which expects two parameters and one block as
-
# described below.
-
#
-
# * +updated?+ which returns a boolean if there were updates in
-
# the filesystem or not.
-
#
-
# * +execute+ which executes the given block on initialization
-
# and updates the latest watched files and timestamp.
-
#
-
# * +execute_if_updated+ which just executes the block if it was updated.
-
#
-
# After initialization, a call to +execute_if_updated+ must execute
-
# the block only if there was really a change in the filesystem.
-
#
-
# This class is used by Rails to reload the I18n framework whenever
-
# they are changed upon a new request.
-
#
-
# i18n_reloader = ActiveSupport::FileUpdateChecker.new(paths) do
-
# I18n.reload!
-
# end
-
#
-
# ActionDispatch::Reloader.to_prepare do
-
# i18n_reloader.execute_if_updated
-
# end
-
1
class FileUpdateChecker
-
# It accepts two parameters on initialization. The first is an array
-
# of files and the second is an optional hash of directories. The hash must
-
# have directories as keys and the value is an array of extensions to be
-
# watched under that directory.
-
#
-
# This method must also receive a block that will be called once a path
-
# changes. The array of files and list of directories cannot be changed
-
# after FileUpdateChecker has been initialized.
-
1
def initialize(files, dirs={}, &block)
-
3
@files = files.freeze
-
3
@glob = compile_glob(dirs)
-
3
@block = block
-
-
3
@watched = nil
-
3
@updated_at = nil
-
-
3
@last_watched = watched
-
3
@last_update_at = updated_at(@last_watched)
-
end
-
-
# Check if any of the entries were updated. If so, the watched and/or
-
# updated_at values are cached until the block is executed via +execute+
-
# or +execute_if_updated+.
-
1
def updated?
-
1
current_watched = watched
-
1
if @last_watched.size != current_watched.size
-
@watched = current_watched
-
true
-
else
-
1
current_updated_at = updated_at(current_watched)
-
1
if @last_update_at < current_updated_at
-
@watched = current_watched
-
@updated_at = current_updated_at
-
true
-
else
-
1
false
-
end
-
end
-
end
-
-
# Executes the given block and updates the latest watched files and
-
# timestamp.
-
1
def execute
-
2
@last_watched = watched
-
2
@last_update_at = updated_at(@last_watched)
-
2
@block.call
-
ensure
-
2
@watched = nil
-
2
@updated_at = nil
-
end
-
-
# Execute the block given if updated.
-
1
def execute_if_updated
-
1
if updated?
-
execute
-
true
-
else
-
1
false
-
end
-
end
-
-
1
private
-
-
1
def watched
-
@watched || begin
-
21
all = @files.select { |f| File.exist?(f) }
-
6
all.concat(Dir[@glob]) if @glob
-
6
all
-
6
end
-
end
-
-
1
def updated_at(paths)
-
6
@updated_at || max_mtime(paths) || Time.at(0)
-
end
-
-
# This method returns the maximum mtime of the files in +paths+, or +nil+
-
# if the array is empty.
-
#
-
# Files with a mtime in the future are ignored. Such abnormal situation
-
# can happen for example if the user changes the clock by hand. It is
-
# healthy to consider this edge case because with mtimes in the future
-
# reloading is not triggered.
-
1
def max_mtime(paths)
-
6
time_now = Time.now
-
88
paths.map {|path| File.mtime(path)}.reject {|mtime| time_now < mtime}.max
-
end
-
-
1
def compile_glob(hash)
-
3
hash.freeze # Freeze so changes aren't accidentally pushed
-
3
return if hash.empty?
-
-
1
globs = hash.map do |key, value|
-
8
"#{escape(key)}/**/*#{compile_ext(value)}"
-
end
-
1
"{#{globs.join(",")}}"
-
end
-
-
1
def escape(key)
-
8
key.gsub(',','\,')
-
end
-
-
1
def compile_ext(array)
-
8
array = Array(array)
-
8
return if array.empty?
-
8
".{#{array.join(",")}}"
-
end
-
end
-
end
-
1
module ActiveSupport
-
# Returns the version of the currently loaded ActiveSupport as a <tt>Gem::Version</tt>
-
1
def self.gem_version
-
Gem::Version.new VERSION::STRING
-
end
-
-
1
module VERSION
-
1
MAJOR = 4
-
1
MINOR = 1
-
1
TINY = 6
-
1
PRE = nil
-
-
1
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
-
end
-
end
-
1
require 'active_support/core_ext/hash/keys'
-
-
1
module ActiveSupport
-
# Implements a hash where keys <tt>:foo</tt> and <tt>"foo"</tt> are considered
-
# to be the same.
-
#
-
# rgb = ActiveSupport::HashWithIndifferentAccess.new
-
#
-
# rgb[:black] = '#000000'
-
# rgb[:black] # => '#000000'
-
# rgb['black'] # => '#000000'
-
#
-
# rgb['white'] = '#FFFFFF'
-
# rgb[:white] # => '#FFFFFF'
-
# rgb['white'] # => '#FFFFFF'
-
#
-
# Internally symbols are mapped to strings when used as keys in the entire
-
# writing interface (calling <tt>[]=</tt>, <tt>merge</tt>, etc). This
-
# mapping belongs to the public interface. For example, given:
-
#
-
# hash = ActiveSupport::HashWithIndifferentAccess.new(a: 1)
-
#
-
# You are guaranteed that the key is returned as a string:
-
#
-
# hash.keys # => ["a"]
-
#
-
# Technically other types of keys are accepted:
-
#
-
# hash = ActiveSupport::HashWithIndifferentAccess.new(a: 1)
-
# hash[0] = 0
-
# hash # => {"a"=>1, 0=>0}
-
#
-
# but this class is intended for use cases where strings or symbols are the
-
# expected keys and it is convenient to understand both as the same. For
-
# example the +params+ hash in Ruby on Rails.
-
#
-
# Note that core extensions define <tt>Hash#with_indifferent_access</tt>:
-
#
-
# rgb = { black: '#000000', white: '#FFFFFF' }.with_indifferent_access
-
#
-
# which may be handy.
-
1
class HashWithIndifferentAccess < Hash
-
# Returns +true+ so that <tt>Array#extract_options!</tt> finds members of
-
# this class.
-
1
def extractable_options?
-
true
-
end
-
-
1
def with_indifferent_access
-
4
dup
-
end
-
-
1
def nested_under_indifferent_access
-
2
self
-
end
-
-
1
def initialize(constructor = {})
-
65
if constructor.is_a?(Hash)
-
63
super()
-
63
update(constructor)
-
else
-
2
super(constructor)
-
end
-
end
-
-
1
def default(key = nil)
-
45
if key.is_a?(Symbol) && include?(key = key.to_s)
-
19
self[key]
-
else
-
26
super
-
end
-
end
-
-
1
def self.new_from_hash_copying_default(hash)
-
11
hash = hash.to_hash
-
11
new(hash).tap do |new_hash|
-
11
new_hash.default = hash.default
-
end
-
end
-
-
1
def self.[](*args)
-
8
new.merge!(Hash[*args])
-
end
-
-
1
alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
-
1
alias_method :regular_update, :update unless method_defined?(:regular_update)
-
-
# Assigns a new value to the hash:
-
#
-
# hash = ActiveSupport::HashWithIndifferentAccess.new
-
# hash[:key] = 'value'
-
#
-
# This value can be later fetched using either +:key+ or +'key'+.
-
1
def []=(key, value)
-
42
regular_writer(convert_key(key), convert_value(value, for: :assignment))
-
end
-
-
1
alias_method :store, :[]=
-
-
# Updates the receiver in-place, merging in the hash passed as argument:
-
#
-
# hash_1 = ActiveSupport::HashWithIndifferentAccess.new
-
# hash_1[:key] = 'value'
-
#
-
# hash_2 = ActiveSupport::HashWithIndifferentAccess.new
-
# hash_2[:key] = 'New Value!'
-
#
-
# hash_1.update(hash_2) # => {"key"=>"New Value!"}
-
#
-
# The argument can be either an
-
# <tt>ActiveSupport::HashWithIndifferentAccess</tt> or a regular +Hash+.
-
# In either case the merge respects the semantics of indifferent access.
-
#
-
# If the argument is a regular hash with keys +:key+ and +"key"+ only one
-
# of the values end up in the receiver, but which one is unspecified.
-
#
-
# When given a block, the value for duplicated keys will be determined
-
# by the result of invoking the block with the duplicated key, the value
-
# in the receiver, and the value in +other_hash+. The rules for duplicated
-
# keys follow the semantics of indifferent access:
-
#
-
# hash_1[:key] = 10
-
# hash_2['key'] = 12
-
# hash_1.update(hash_2) { |key, old, new| old + new } # => {"key"=>22}
-
1
def update(other_hash)
-
79
if other_hash.is_a? HashWithIndifferentAccess
-
26
super(other_hash)
-
else
-
53
other_hash.to_hash.each_pair do |key, value|
-
44
if block_given? && key?(key)
-
value = yield(convert_key(key), self[key], value)
-
end
-
44
regular_writer(convert_key(key), convert_value(value))
-
end
-
53
self
-
end
-
end
-
-
1
alias_method :merge!, :update
-
-
# Checks the hash for a key matching the argument passed in:
-
#
-
# hash = ActiveSupport::HashWithIndifferentAccess.new
-
# hash['key'] = 'value'
-
# hash.key?(:key) # => true
-
# hash.key?('key') # => true
-
1
def key?(key)
-
97
super(convert_key(key))
-
end
-
-
1
alias_method :include?, :key?
-
1
alias_method :has_key?, :key?
-
1
alias_method :member?, :key?
-
-
# Same as <tt>Hash#fetch</tt> where the key passed as argument can be
-
# either a string or a symbol:
-
#
-
# counters = ActiveSupport::HashWithIndifferentAccess.new
-
# counters[:foo] = 1
-
#
-
# counters.fetch('foo') # => 1
-
# counters.fetch(:bar, 0) # => 0
-
# counters.fetch(:bar) { |key| 0 } # => 0
-
# counters.fetch(:zoo) # => KeyError: key not found: "zoo"
-
1
def fetch(key, *extras)
-
super(convert_key(key), *extras)
-
end
-
-
# Returns an array of the values at the specified indices:
-
#
-
# hash = ActiveSupport::HashWithIndifferentAccess.new
-
# hash[:a] = 'x'
-
# hash[:b] = 'y'
-
# hash.values_at('a', 'b') # => ["x", "y"]
-
1
def values_at(*indices)
-
indices.collect { |key| self[convert_key(key)] }
-
end
-
-
# Returns an exact copy of the hash.
-
1
def dup
-
22
self.class.new(self).tap do |new_hash|
-
22
new_hash.default = default
-
end
-
end
-
-
# This method has the same semantics of +update+, except it does not
-
# modify the receiver but rather returns a new hash with indifferent
-
# access with the result of the merge.
-
1
def merge(hash, &block)
-
4
self.dup.update(hash, &block)
-
end
-
-
# Like +merge+ but the other way around: Merges the receiver into the
-
# argument and returns a new hash with indifferent access as result:
-
#
-
# hash = ActiveSupport::HashWithIndifferentAccess.new
-
# hash['a'] = nil
-
# hash.reverse_merge(a: 0, b: 1) # => {"a"=>nil, "b"=>1}
-
1
def reverse_merge(other_hash)
-
super(self.class.new_from_hash_copying_default(other_hash))
-
end
-
-
# Same semantics as +reverse_merge+ but modifies the receiver in-place.
-
1
def reverse_merge!(other_hash)
-
replace(reverse_merge( other_hash ))
-
end
-
-
# Replaces the contents of this hash with other_hash.
-
#
-
# h = { "a" => 100, "b" => 200 }
-
# h.replace({ "c" => 300, "d" => 400 }) # => {"c"=>300, "d"=>400}
-
1
def replace(other_hash)
-
super(self.class.new_from_hash_copying_default(other_hash))
-
end
-
-
# Removes the specified key from the hash.
-
1
def delete(key)
-
24
super(convert_key(key))
-
end
-
-
1
def stringify_keys!; self end
-
1
def deep_stringify_keys!; self end
-
11
def stringify_keys; dup end
-
1
def deep_stringify_keys; dup end
-
1
undef :symbolize_keys!
-
1
undef :deep_symbolize_keys!
-
1
def symbolize_keys; to_hash.symbolize_keys! end
-
1
def deep_symbolize_keys; to_hash.deep_symbolize_keys! end
-
1
def to_options!; self end
-
-
1
def select(*args, &block)
-
dup.tap { |hash| hash.select!(*args, &block) }
-
end
-
-
1
def reject(*args, &block)
-
dup.tap { |hash| hash.reject!(*args, &block) }
-
end
-
-
# Convert to a regular hash with string keys.
-
1
def to_hash
-
_new_hash= {}
-
each do |key, value|
-
_new_hash[convert_key(key)] = convert_value(value, for: :to_hash)
-
end
-
Hash.new(default).merge!(_new_hash)
-
end
-
-
1
protected
-
1
def convert_key(key)
-
207
key.kind_of?(Symbol) ? key.to_s : key
-
end
-
-
1
def convert_value(value, options = {})
-
146
if value.is_a? Hash
-
4
if options[:for] == :to_hash
-
value.to_hash
-
else
-
4
value.nested_under_indifferent_access
-
end
-
142
elsif value.is_a?(Array)
-
30
unless options[:for] == :assignment
-
30
value = value.dup
-
end
-
90
value.map! { |e| convert_value(e, options) }
-
else
-
112
value
-
end
-
end
-
end
-
end
-
-
1
HashWithIndifferentAccess = ActiveSupport::HashWithIndifferentAccess
-
1
require 'active_support/core_ext/hash/deep_merge'
-
1
require 'active_support/core_ext/hash/except'
-
1
require 'active_support/core_ext/hash/slice'
-
1
begin
-
1
require 'i18n'
-
rescue LoadError => e
-
$stderr.puts "The i18n gem is not available. Please add it to your Gemfile and run bundle install"
-
raise e
-
end
-
1
require 'active_support/lazy_load_hooks'
-
-
1
ActiveSupport.run_load_hooks(:i18n)
-
1
I18n.load_path << "#{File.dirname(__FILE__)}/locale/en.yml"
-
1
require "active_support"
-
1
require "active_support/file_update_checker"
-
1
require "active_support/core_ext/array/wrap"
-
-
1
module I18n
-
1
class Railtie < Rails::Railtie
-
1
config.i18n = ActiveSupport::OrderedOptions.new
-
1
config.i18n.railties_load_path = []
-
1
config.i18n.load_path = []
-
1
config.i18n.fallbacks = ActiveSupport::OrderedOptions.new
-
# Enforce I18n to check the available locales when setting a locale.
-
1
config.i18n.enforce_available_locales = true
-
-
# Set the i18n configuration after initialization since a lot of
-
# configuration is still usually done in application initializers.
-
1
config.after_initialize do |app|
-
1
I18n::Railtie.initialize_i18n(app)
-
end
-
-
# Trigger i18n config before any eager loading has happened
-
# so it's ready if any classes require it when eager loaded.
-
1
config.before_eager_load do |app|
-
I18n::Railtie.initialize_i18n(app)
-
end
-
-
1
protected
-
-
1
@i18n_inited = false
-
-
# Setup i18n configuration.
-
1
def self.initialize_i18n(app)
-
1
return if @i18n_inited
-
-
1
fallbacks = app.config.i18n.delete(:fallbacks)
-
-
# Avoid issues with setting the default_locale by disabling available locales
-
# check while configuring.
-
1
enforce_available_locales = app.config.i18n.delete(:enforce_available_locales)
-
1
enforce_available_locales = I18n.enforce_available_locales unless I18n.enforce_available_locales.nil?
-
1
I18n.enforce_available_locales = false
-
-
1
app.config.i18n.each do |setting, value|
-
2
case setting
-
when :railties_load_path
-
1
app.config.i18n.load_path.unshift(*value)
-
when :load_path
-
1
I18n.load_path += value
-
else
-
I18n.send("#{setting}=", value)
-
end
-
end
-
-
1
init_fallbacks(fallbacks) if fallbacks && validate_fallbacks(fallbacks)
-
-
# Restore available locales check so it will take place from now on.
-
1
I18n.enforce_available_locales = enforce_available_locales
-
-
2
reloader = ActiveSupport::FileUpdateChecker.new(I18n.load_path.dup){ I18n.reload! }
-
1
app.reloaders << reloader
-
1
ActionDispatch::Reloader.to_prepare { reloader.execute_if_updated }
-
1
reloader.execute
-
-
1
@i18n_inited = true
-
end
-
-
1
def self.include_fallbacks_module
-
I18n.backend.class.send(:include, I18n::Backend::Fallbacks)
-
end
-
-
1
def self.init_fallbacks(fallbacks)
-
include_fallbacks_module
-
-
args = case fallbacks
-
when ActiveSupport::OrderedOptions
-
[*(fallbacks[:defaults] || []) << fallbacks[:map]].compact
-
when Hash, Array
-
Array.wrap(fallbacks)
-
else # TrueClass
-
[]
-
end
-
-
I18n.fallbacks = I18n::Locale::Fallbacks.new(*args)
-
end
-
-
1
def self.validate_fallbacks(fallbacks)
-
1
case fallbacks
-
when ActiveSupport::OrderedOptions
-
1
!fallbacks.empty?
-
when TrueClass, Array, Hash
-
true
-
else
-
raise "Unexpected fallback type #{fallbacks.inspect}"
-
end
-
end
-
end
-
end
-
1
require 'active_support/inflector/inflections'
-
-
#--
-
# Defines the standard inflection rules. These are the starting point for
-
# new projects and are not considered complete. The current set of inflection
-
# rules is frozen. This means, we do not change them to become more complete.
-
# This is a safety measure to keep existing applications from breaking.
-
#++
-
1
module ActiveSupport
-
1
Inflector.inflections(:en) do |inflect|
-
1
inflect.plural(/$/, 's')
-
1
inflect.plural(/s$/i, 's')
-
1
inflect.plural(/^(ax|test)is$/i, '\1es')
-
1
inflect.plural(/(octop|vir)us$/i, '\1i')
-
1
inflect.plural(/(octop|vir)i$/i, '\1i')
-
1
inflect.plural(/(alias|status)$/i, '\1es')
-
1
inflect.plural(/(bu)s$/i, '\1ses')
-
1
inflect.plural(/(buffal|tomat)o$/i, '\1oes')
-
1
inflect.plural(/([ti])um$/i, '\1a')
-
1
inflect.plural(/([ti])a$/i, '\1a')
-
1
inflect.plural(/sis$/i, 'ses')
-
1
inflect.plural(/(?:([^f])fe|([lr])f)$/i, '\1\2ves')
-
1
inflect.plural(/(hive)$/i, '\1s')
-
1
inflect.plural(/([^aeiouy]|qu)y$/i, '\1ies')
-
1
inflect.plural(/(x|ch|ss|sh)$/i, '\1es')
-
1
inflect.plural(/(matr|vert|ind)(?:ix|ex)$/i, '\1ices')
-
1
inflect.plural(/^(m|l)ouse$/i, '\1ice')
-
1
inflect.plural(/^(m|l)ice$/i, '\1ice')
-
1
inflect.plural(/^(ox)$/i, '\1en')
-
1
inflect.plural(/^(oxen)$/i, '\1')
-
1
inflect.plural(/(quiz)$/i, '\1zes')
-
-
1
inflect.singular(/s$/i, '')
-
1
inflect.singular(/(ss)$/i, '\1')
-
1
inflect.singular(/(n)ews$/i, '\1ews')
-
1
inflect.singular(/([ti])a$/i, '\1um')
-
1
inflect.singular(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)(sis|ses)$/i, '\1sis')
-
1
inflect.singular(/(^analy)(sis|ses)$/i, '\1sis')
-
1
inflect.singular(/([^f])ves$/i, '\1fe')
-
1
inflect.singular(/(hive)s$/i, '\1')
-
1
inflect.singular(/(tive)s$/i, '\1')
-
1
inflect.singular(/([lr])ves$/i, '\1f')
-
1
inflect.singular(/([^aeiouy]|qu)ies$/i, '\1y')
-
1
inflect.singular(/(s)eries$/i, '\1eries')
-
1
inflect.singular(/(m)ovies$/i, '\1ovie')
-
1
inflect.singular(/(x|ch|ss|sh)es$/i, '\1')
-
1
inflect.singular(/^(m|l)ice$/i, '\1ouse')
-
1
inflect.singular(/(bus)(es)?$/i, '\1')
-
1
inflect.singular(/(o)es$/i, '\1')
-
1
inflect.singular(/(shoe)s$/i, '\1')
-
1
inflect.singular(/(cris|test)(is|es)$/i, '\1is')
-
1
inflect.singular(/^(a)x[ie]s$/i, '\1xis')
-
1
inflect.singular(/(octop|vir)(us|i)$/i, '\1us')
-
1
inflect.singular(/(alias|status)(es)?$/i, '\1')
-
1
inflect.singular(/^(ox)en/i, '\1')
-
1
inflect.singular(/(vert|ind)ices$/i, '\1ex')
-
1
inflect.singular(/(matr)ices$/i, '\1ix')
-
1
inflect.singular(/(quiz)zes$/i, '\1')
-
1
inflect.singular(/(database)s$/i, '\1')
-
-
1
inflect.irregular('person', 'people')
-
1
inflect.irregular('man', 'men')
-
1
inflect.irregular('child', 'children')
-
1
inflect.irregular('sex', 'sexes')
-
1
inflect.irregular('move', 'moves')
-
1
inflect.irregular('zombie', 'zombies')
-
-
1
inflect.uncountable(%w(equipment information rice money species series fish sheep jeans police))
-
end
-
end
-
# in case active_support/inflector is required without the rest of active_support
-
1
require 'active_support/inflector/inflections'
-
1
require 'active_support/inflector/transliterate'
-
1
require 'active_support/inflector/methods'
-
-
1
require 'active_support/inflections'
-
1
require 'active_support/core_ext/string/inflections'
-
1
require 'thread_safe'
-
1
require 'active_support/core_ext/array/prepend_and_append'
-
1
require 'active_support/i18n'
-
-
1
module ActiveSupport
-
1
module Inflector
-
1
extend self
-
-
# A singleton instance of this class is yielded by Inflector.inflections,
-
# which can then be used to specify additional inflection rules. If passed
-
# an optional locale, rules for other languages can be specified. The
-
# default locale is <tt>:en</tt>. Only rules for English are provided.
-
#
-
# ActiveSupport::Inflector.inflections(:en) do |inflect|
-
# inflect.plural /^(ox)$/i, '\1\2en'
-
# inflect.singular /^(ox)en/i, '\1'
-
#
-
# inflect.irregular 'octopus', 'octopi'
-
#
-
# inflect.uncountable 'equipment'
-
# end
-
#
-
# New rules are added at the top. So in the example above, the irregular
-
# rule for octopus will now be the first of the pluralization and
-
# singularization rules that is runs. This guarantees that your rules run
-
# before any of the rules that may already have been loaded.
-
1
class Inflections
-
1
@__instance__ = ThreadSafe::Cache.new
-
-
1
def self.instance(locale = :en)
-
551
@__instance__[locale] ||= new
-
end
-
-
1
attr_reader :plurals, :singulars, :uncountables, :humans, :acronyms, :acronym_regex
-
-
1
def initialize
-
1
@plurals, @singulars, @uncountables, @humans, @acronyms, @acronym_regex = [], [], [], [], {}, /(?=a)b/
-
end
-
-
# Private, for the test suite.
-
1
def initialize_dup(orig) # :nodoc:
-
%w(plurals singulars uncountables humans acronyms acronym_regex).each do |scope|
-
instance_variable_set("@#{scope}", orig.send(scope).dup)
-
end
-
end
-
-
# Specifies a new acronym. An acronym must be specified as it will appear
-
# in a camelized string. An underscore string that contains the acronym
-
# will retain the acronym when passed to +camelize+, +humanize+, or
-
# +titleize+. A camelized string that contains the acronym will maintain
-
# the acronym when titleized or humanized, and will convert the acronym
-
# into a non-delimited single lowercase word when passed to +underscore+.
-
#
-
# acronym 'HTML'
-
# titleize 'html' # => 'HTML'
-
# camelize 'html' # => 'HTML'
-
# underscore 'MyHTML' # => 'my_html'
-
#
-
# The acronym, however, must occur as a delimited unit and not be part of
-
# another word for conversions to recognize it:
-
#
-
# acronym 'HTTP'
-
# camelize 'my_http_delimited' # => 'MyHTTPDelimited'
-
# camelize 'https' # => 'Https', not 'HTTPs'
-
# underscore 'HTTPS' # => 'http_s', not 'https'
-
#
-
# acronym 'HTTPS'
-
# camelize 'https' # => 'HTTPS'
-
# underscore 'HTTPS' # => 'https'
-
#
-
# Note: Acronyms that are passed to +pluralize+ will no longer be
-
# recognized, since the acronym will not occur as a delimited unit in the
-
# pluralized result. To work around this, you must specify the pluralized
-
# form as an acronym as well:
-
#
-
# acronym 'API'
-
# camelize(pluralize('api')) # => 'Apis'
-
#
-
# acronym 'APIs'
-
# camelize(pluralize('api')) # => 'APIs'
-
#
-
# +acronym+ may be used to specify any word that contains an acronym or
-
# otherwise needs to maintain a non-standard capitalization. The only
-
# restriction is that the word must begin with a capital letter.
-
#
-
# acronym 'RESTful'
-
# underscore 'RESTful' # => 'restful'
-
# underscore 'RESTfulController' # => 'restful_controller'
-
# titleize 'RESTfulController' # => 'RESTful Controller'
-
# camelize 'restful' # => 'RESTful'
-
# camelize 'restful_controller' # => 'RESTfulController'
-
#
-
# acronym 'McDonald'
-
# underscore 'McDonald' # => 'mcdonald'
-
# camelize 'mcdonald' # => 'McDonald'
-
1
def acronym(word)
-
@acronyms[word.downcase] = word
-
@acronym_regex = /#{@acronyms.values.join("|")}/
-
end
-
-
# Specifies a new pluralization rule and its replacement. The rule can
-
# either be a string or a regular expression. The replacement should
-
# always be a string that may include references to the matched data from
-
# the rule.
-
1
def plural(rule, replacement)
-
33
@uncountables.delete(rule) if rule.is_a?(String)
-
33
@uncountables.delete(replacement)
-
33
@plurals.prepend([rule, replacement])
-
end
-
-
# Specifies a new singularization rule and its replacement. The rule can
-
# either be a string or a regular expression. The replacement should
-
# always be a string that may include references to the matched data from
-
# the rule.
-
1
def singular(rule, replacement)
-
39
@uncountables.delete(rule) if rule.is_a?(String)
-
39
@uncountables.delete(replacement)
-
39
@singulars.prepend([rule, replacement])
-
end
-
-
# Specifies a new irregular that applies to both pluralization and
-
# singularization at the same time. This can only be used for strings, not
-
# regular expressions. You simply pass the irregular in singular and
-
# plural form.
-
#
-
# irregular 'octopus', 'octopi'
-
# irregular 'person', 'people'
-
1
def irregular(singular, plural)
-
6
@uncountables.delete(singular)
-
6
@uncountables.delete(plural)
-
-
6
s0 = singular[0]
-
6
srest = singular[1..-1]
-
-
6
p0 = plural[0]
-
6
prest = plural[1..-1]
-
-
6
if s0.upcase == p0.upcase
-
6
plural(/(#{s0})#{srest}$/i, '\1' + prest)
-
6
plural(/(#{p0})#{prest}$/i, '\1' + prest)
-
-
6
singular(/(#{s0})#{srest}$/i, '\1' + srest)
-
6
singular(/(#{p0})#{prest}$/i, '\1' + srest)
-
else
-
plural(/#{s0.upcase}(?i)#{srest}$/, p0.upcase + prest)
-
plural(/#{s0.downcase}(?i)#{srest}$/, p0.downcase + prest)
-
plural(/#{p0.upcase}(?i)#{prest}$/, p0.upcase + prest)
-
plural(/#{p0.downcase}(?i)#{prest}$/, p0.downcase + prest)
-
-
singular(/#{s0.upcase}(?i)#{srest}$/, s0.upcase + srest)
-
singular(/#{s0.downcase}(?i)#{srest}$/, s0.downcase + srest)
-
singular(/#{p0.upcase}(?i)#{prest}$/, s0.upcase + srest)
-
singular(/#{p0.downcase}(?i)#{prest}$/, s0.downcase + srest)
-
end
-
end
-
-
# Add uncountable words that shouldn't be attempted inflected.
-
#
-
# uncountable 'money'
-
# uncountable 'money', 'information'
-
# uncountable %w( money information rice )
-
1
def uncountable(*words)
-
1
(@uncountables << words).flatten!
-
end
-
-
# Specifies a humanized form of a string by a regular expression rule or
-
# by a string mapping. When using a regular expression based replacement,
-
# the normal humanize formatting is called after the replacement. When a
-
# string is used, the human form should be specified as desired (example:
-
# 'The name', not 'the_name').
-
#
-
# human /_cnt$/i, '\1_count'
-
# human 'legacy_col_person_name', 'Name'
-
1
def human(rule, replacement)
-
@humans.prepend([rule, replacement])
-
end
-
-
# Clears the loaded inflections within a given scope (default is
-
# <tt>:all</tt>). Give the scope as a symbol of the inflection type, the
-
# options are: <tt>:plurals</tt>, <tt>:singulars</tt>, <tt>:uncountables</tt>,
-
# <tt>:humans</tt>.
-
#
-
# clear :all
-
# clear :plurals
-
1
def clear(scope = :all)
-
case scope
-
when :all
-
@plurals, @singulars, @uncountables, @humans = [], [], [], []
-
else
-
instance_variable_set "@#{scope}", []
-
end
-
end
-
end
-
-
# Yields a singleton instance of Inflector::Inflections so you can specify
-
# additional inflector rules. If passed an optional locale, rules for other
-
# languages can be specified. If not specified, defaults to <tt>:en</tt>.
-
# Only rules for English are provided.
-
#
-
# ActiveSupport::Inflector.inflections(:en) do |inflect|
-
# inflect.uncountable 'rails'
-
# end
-
1
def inflections(locale = :en)
-
551
if block_given?
-
1
yield Inflections.instance(locale)
-
else
-
550
Inflections.instance(locale)
-
end
-
end
-
end
-
end
-
# encoding: utf-8
-
-
1
require 'active_support/inflections'
-
-
1
module ActiveSupport
-
# The Inflector transforms words from singular to plural, class names to table
-
# names, modularized class names to ones without, and class names to foreign
-
# keys. The default inflections for pluralization, singularization, and
-
# uncountable words are kept in inflections.rb.
-
#
-
# The Rails core team has stated patches for the inflections library will not
-
# be accepted in order to avoid breaking legacy applications which may be
-
# relying on errant inflections. If you discover an incorrect inflection and
-
# require it for your application or wish to define rules for languages other
-
# than English, please correct or add them yourself (explained below).
-
1
module Inflector
-
1
extend self
-
-
# Returns the plural form of the word in the string.
-
#
-
# If passed an optional +locale+ parameter, the word will be
-
# pluralized using rules defined for that language. By default,
-
# this parameter is set to <tt>:en</tt>.
-
#
-
# 'post'.pluralize # => "posts"
-
# 'octopus'.pluralize # => "octopi"
-
# 'sheep'.pluralize # => "sheep"
-
# 'words'.pluralize # => "words"
-
# 'CamelOctopus'.pluralize # => "CamelOctopi"
-
# 'ley'.pluralize(:es) # => "leyes"
-
1
def pluralize(word, locale = :en)
-
18
apply_inflections(word, inflections(locale).plurals)
-
end
-
-
# The reverse of +pluralize+, returns the singular form of a word in a
-
# string.
-
#
-
# If passed an optional +locale+ parameter, the word will be
-
# singularized using rules defined for that language. By default,
-
# this parameter is set to <tt>:en</tt>.
-
#
-
# 'posts'.singularize # => "post"
-
# 'octopi'.singularize # => "octopus"
-
# 'sheep'.singularize # => "sheep"
-
# 'word'.singularize # => "word"
-
# 'CamelOctopi'.singularize # => "CamelOctopus"
-
# 'leyes'.singularize(:es) # => "ley"
-
1
def singularize(word, locale = :en)
-
38
apply_inflections(word, inflections(locale).singulars)
-
end
-
-
# By default, +camelize+ converts strings to UpperCamelCase. If the argument
-
# to +camelize+ is set to <tt>:lower</tt> then +camelize+ produces
-
# lowerCamelCase.
-
#
-
# +camelize+ will also convert '/' to '::' which is useful for converting
-
# paths to namespaces.
-
#
-
# 'active_model'.camelize # => "ActiveModel"
-
# 'active_model'.camelize(:lower) # => "activeModel"
-
# 'active_model/errors'.camelize # => "ActiveModel::Errors"
-
# 'active_model/errors'.camelize(:lower) # => "activeModel::Errors"
-
#
-
# As a rule of thumb you can think of +camelize+ as the inverse of
-
# +underscore+, though there are cases where that does not hold:
-
#
-
# 'SSLError'.underscore.camelize # => "SslError"
-
1
def camelize(term, uppercase_first_letter = true)
-
33
string = term.to_s
-
33
if uppercase_first_letter
-
66
string = string.sub(/^[a-z\d]*/) { inflections.acronyms[$&] || $&.capitalize }
-
else
-
string = string.sub(/^(?:#{inflections.acronym_regex}(?=\b|[A-Z_])|\w)/) { $&.downcase }
-
end
-
77
string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{inflections.acronyms[$2] || $2.capitalize}" }
-
33
string.gsub!('/', '::')
-
33
string
-
end
-
-
# Makes an underscored, lowercase form from the expression in the string.
-
#
-
# Changes '::' to '/' to convert namespaces to paths.
-
#
-
# 'ActiveModel'.underscore # => "active_model"
-
# 'ActiveModel::Errors'.underscore # => "active_model/errors"
-
#
-
# As a rule of thumb you can think of +underscore+ as the inverse of
-
# +camelize+, though there are cases where that does not hold:
-
#
-
# 'SSLError'.underscore.camelize # => "SslError"
-
1
def underscore(camel_cased_word)
-
330
word = camel_cased_word.to_s.gsub('::', '/')
-
330
word.gsub!(/(?:([A-Za-z\d])|^)(#{inflections.acronym_regex})(?=\b|[^a-z])/) { "#{$1}#{$1 && '_'}#{$2.downcase}" }
-
330
word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
-
330
word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
-
330
word.tr!("-", "_")
-
330
word.downcase!
-
330
word
-
end
-
-
# Capitalizes the first word, turns underscores into spaces, and strips a
-
# trailing '_id' if present.
-
# Like +titleize+, this is meant for creating pretty output.
-
#
-
# The capitalization of the first word can be turned off by setting the
-
# optional parameter +capitalize+ to false.
-
# By default, this parameter is true.
-
#
-
# humanize('employee_salary') # => "Employee salary"
-
# humanize('author_id') # => "Author"
-
# humanize('author_id', capitalize: false) # => "author"
-
1
def humanize(lower_case_and_underscored_word, options = {})
-
6
result = lower_case_and_underscored_word.to_s.dup
-
6
inflections.humans.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
-
6
result.gsub!(/_id$/, "")
-
6
result.tr!('_', ' ')
-
6
result.gsub!(/([a-z\d]*)/i) { |match|
-
25
"#{inflections.acronyms[match] || match.downcase}"
-
}
-
12
result.gsub!(/^\w/) { |match| match.upcase } if options.fetch(:capitalize, true)
-
6
result
-
end
-
-
# Capitalizes all the words and replaces some characters in the string to
-
# create a nicer looking title. +titleize+ is meant for creating pretty
-
# output. It is not used in the Rails internals.
-
#
-
# +titleize+ is also aliased as +titlecase+.
-
#
-
# 'man from the boondocks'.titleize # => "Man From The Boondocks"
-
# 'x-men: the last stand'.titleize # => "X Men: The Last Stand"
-
# 'TheManWithoutAPast'.titleize # => "The Man Without A Past"
-
# 'raiders_of_the_lost_ark'.titleize # => "Raiders Of The Lost Ark"
-
1
def titleize(word)
-
humanize(underscore(word)).gsub(/\b(?<!['’`])[a-z]/) { $&.capitalize }
-
end
-
-
# Create the name of a table like Rails does for models to table names. This
-
# method uses the +pluralize+ method on the last word in the string.
-
#
-
# 'RawScaledScorer'.tableize # => "raw_scaled_scorers"
-
# 'egg_and_ham'.tableize # => "egg_and_hams"
-
# 'fancyCategory'.tableize # => "fancy_categories"
-
1
def tableize(class_name)
-
pluralize(underscore(class_name))
-
end
-
-
# Create a class name from a plural table name like Rails does for table
-
# names to models. Note that this returns a string and not a Class (To
-
# convert to an actual class follow +classify+ with +constantize+).
-
#
-
# 'egg_and_hams'.classify # => "EggAndHam"
-
# 'posts'.classify # => "Post"
-
#
-
# Singular names are not handled correctly:
-
#
-
# 'calculus'.classify # => "Calculu"
-
1
def classify(table_name)
-
# strip out any leading schema name
-
1
camelize(singularize(table_name.to_s.sub(/.*\./, '')))
-
end
-
-
# Replaces underscores with dashes in the string.
-
#
-
# 'puni_puni'.dasherize # => "puni-puni"
-
1
def dasherize(underscored_word)
-
58
underscored_word.tr('_', '-')
-
end
-
-
# Removes the module part from the expression in the string.
-
#
-
# 'ActiveRecord::CoreExtensions::String::Inflections'.demodulize # => "Inflections"
-
# 'Inflections'.demodulize # => "Inflections"
-
#
-
# See also +deconstantize+.
-
1
def demodulize(path)
-
7
path = path.to_s
-
7
if i = path.rindex('::')
-
path[(i+2)..-1]
-
else
-
7
path
-
end
-
end
-
-
# Removes the rightmost segment from the constant expression in the string.
-
#
-
# 'Net::HTTP'.deconstantize # => "Net"
-
# '::Net::HTTP'.deconstantize # => "::Net"
-
# 'String'.deconstantize # => ""
-
# '::String'.deconstantize # => ""
-
# ''.deconstantize # => ""
-
#
-
# See also +demodulize+.
-
1
def deconstantize(path)
-
path.to_s[0, path.rindex('::') || 0] # implementation based on the one in facets' Module#spacename
-
end
-
-
# Creates a foreign key name from a class name.
-
# +separate_class_name_and_id_with_underscore+ sets whether
-
# the method should put '_' between the name and 'id'.
-
#
-
# 'Message'.foreign_key # => "message_id"
-
# 'Message'.foreign_key(false) # => "messageid"
-
# 'Admin::Post'.foreign_key # => "post_id"
-
1
def foreign_key(class_name, separate_class_name_and_id_with_underscore = true)
-
3
underscore(demodulize(class_name)) + (separate_class_name_and_id_with_underscore ? "_id" : "id")
-
end
-
-
# Tries to find a constant with the name specified in the argument string.
-
#
-
# 'Module'.constantize # => Module
-
# 'Test::Unit'.constantize # => Test::Unit
-
#
-
# The name is assumed to be the one of a top-level constant, no matter
-
# whether it starts with "::" or not. No lexical context is taken into
-
# account:
-
#
-
# C = 'outside'
-
# module M
-
# C = 'inside'
-
# C # => 'inside'
-
# 'C'.constantize # => 'outside', same as ::C
-
# end
-
#
-
# NameError is raised when the name is not in CamelCase or the constant is
-
# unknown.
-
1
def constantize(camel_cased_word)
-
40
names = camel_cased_word.split('::')
-
-
# Trigger a builtin NameError exception including the ill-formed constant in the message.
-
40
Object.const_get(camel_cased_word) if names.empty?
-
-
# Remove the first blank element in case of '::ClassName' notation.
-
40
names.shift if names.size > 1 && names.first.empty?
-
-
40
names.inject(Object) do |constant, name|
-
52
if constant == Object
-
40
constant.const_get(name)
-
else
-
12
candidate = constant.const_get(name)
-
12
next candidate if constant.const_defined?(name, false)
-
7
next candidate unless Object.const_defined?(name)
-
-
# Go down the ancestors to check it it's owned
-
# directly before we reach Object or the end of ancestors.
-
7
constant = constant.ancestors.inject do |const, ancestor|
-
371
break const if ancestor == Object
-
364
break ancestor if ancestor.const_defined?(name, false)
-
364
const
-
end
-
-
# owner is in Object, so raise
-
7
constant.const_get(name, false)
-
end
-
end
-
end
-
-
# Tries to find a constant with the name specified in the argument string.
-
#
-
# 'Module'.safe_constantize # => Module
-
# 'Test::Unit'.safe_constantize # => Test::Unit
-
#
-
# The name is assumed to be the one of a top-level constant, no matter
-
# whether it starts with "::" or not. No lexical context is taken into
-
# account:
-
#
-
# C = 'outside'
-
# module M
-
# C = 'inside'
-
# C # => 'inside'
-
# 'C'.safe_constantize # => 'outside', same as ::C
-
# end
-
#
-
# +nil+ is returned when the name is not in CamelCase or the constant (or
-
# part of it) is unknown.
-
#
-
# 'blargle'.safe_constantize # => nil
-
# 'UnknownModule'.safe_constantize # => nil
-
# 'UnknownModule::Foo::Bar'.safe_constantize # => nil
-
1
def safe_constantize(camel_cased_word)
-
constantize(camel_cased_word)
-
rescue NameError => e
-
raise unless e.message =~ /(uninitialized constant|wrong constant name) #{const_regexp(camel_cased_word)}$/ ||
-
e.name.to_s == camel_cased_word.to_s
-
rescue ArgumentError => e
-
raise unless e.message =~ /not missing constant #{const_regexp(camel_cased_word)}\!$/
-
end
-
-
# Returns the suffix that should be added to a number to denote the position
-
# in an ordered sequence such as 1st, 2nd, 3rd, 4th.
-
#
-
# ordinal(1) # => "st"
-
# ordinal(2) # => "nd"
-
# ordinal(1002) # => "nd"
-
# ordinal(1003) # => "rd"
-
# ordinal(-11) # => "th"
-
# ordinal(-1021) # => "st"
-
1
def ordinal(number)
-
abs_number = number.to_i.abs
-
-
if (11..13).include?(abs_number % 100)
-
"th"
-
else
-
case abs_number % 10
-
when 1; "st"
-
when 2; "nd"
-
when 3; "rd"
-
else "th"
-
end
-
end
-
end
-
-
# Turns a number into an ordinal string used to denote the position in an
-
# ordered sequence such as 1st, 2nd, 3rd, 4th.
-
#
-
# ordinalize(1) # => "1st"
-
# ordinalize(2) # => "2nd"
-
# ordinalize(1002) # => "1002nd"
-
# ordinalize(1003) # => "1003rd"
-
# ordinalize(-11) # => "-11th"
-
# ordinalize(-1021) # => "-1021st"
-
1
def ordinalize(number)
-
"#{number}#{ordinal(number)}"
-
end
-
-
1
private
-
-
# Mount a regular expression that will match part by part of the constant.
-
#
-
# const_regexp("Foo::Bar::Baz") # => /Foo(::Bar(::Baz)?)?/
-
# const_regexp("::") # => /::/
-
1
def const_regexp(camel_cased_word) #:nodoc:
-
parts = camel_cased_word.split("::")
-
-
return Regexp.escape(camel_cased_word) if parts.blank?
-
-
last = parts.pop
-
-
parts.reverse.inject(last) do |acc, part|
-
part.empty? ? acc : "#{part}(::#{acc})?"
-
end
-
end
-
-
# Applies inflection rules for +singularize+ and +pluralize+.
-
#
-
# apply_inflections('post', inflections.plurals) # => "posts"
-
# apply_inflections('posts', inflections.singulars) # => "post"
-
1
def apply_inflections(word, rules)
-
56
result = word.to_s.dup
-
-
56
if word.empty? || inflections.uncountables.include?(result.downcase[/\b\w+\Z/])
-
result
-
else
-
2121
rules.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
-
56
result
-
end
-
end
-
end
-
end
-
# encoding: utf-8
-
1
require 'active_support/core_ext/string/multibyte'
-
1
require 'active_support/i18n'
-
-
1
module ActiveSupport
-
1
module Inflector
-
-
# Replaces non-ASCII characters with an ASCII approximation, or if none
-
# exists, a replacement character which defaults to "?".
-
#
-
# transliterate('Ærøskøbing')
-
# # => "AEroskobing"
-
#
-
# Default approximations are provided for Western/Latin characters,
-
# e.g, "ø", "ñ", "é", "ß", etc.
-
#
-
# This method is I18n aware, so you can set up custom approximations for a
-
# locale. This can be useful, for example, to transliterate German's "ü"
-
# and "ö" to "ue" and "oe", or to add support for transliterating Russian
-
# to ASCII.
-
#
-
# In order to make your custom transliterations available, you must set
-
# them as the <tt>i18n.transliterate.rule</tt> i18n key:
-
#
-
# # Store the transliterations in locales/de.yml
-
# i18n:
-
# transliterate:
-
# rule:
-
# ü: "ue"
-
# ö: "oe"
-
#
-
# # Or set them using Ruby
-
# I18n.backend.store_translations(:de, i18n: {
-
# transliterate: {
-
# rule: {
-
# 'ü' => 'ue',
-
# 'ö' => 'oe'
-
# }
-
# }
-
# })
-
#
-
# The value for <tt>i18n.transliterate.rule</tt> can be a simple Hash that
-
# maps characters to ASCII approximations as shown above, or, for more
-
# complex requirements, a Proc:
-
#
-
# I18n.backend.store_translations(:de, i18n: {
-
# transliterate: {
-
# rule: ->(string) { MyTransliterator.transliterate(string) }
-
# }
-
# })
-
#
-
# Now you can have different transliterations for each locale:
-
#
-
# I18n.locale = :en
-
# transliterate('Jürgen')
-
# # => "Jurgen"
-
#
-
# I18n.locale = :de
-
# transliterate('Jürgen')
-
# # => "Juergen"
-
1
def transliterate(string, replacement = "?")
-
I18n.transliterate(ActiveSupport::Multibyte::Unicode.normalize(
-
ActiveSupport::Multibyte::Unicode.tidy_bytes(string), :c),
-
:replacement => replacement)
-
end
-
-
# Replaces special characters in a string so that it may be used as part of
-
# a 'pretty' URL.
-
#
-
# class Person
-
# def to_param
-
# "#{id}-#{name.parameterize}"
-
# end
-
# end
-
#
-
# @person = Person.find(1)
-
# # => #<Person id: 1, name: "Donald E. Knuth">
-
#
-
# <%= link_to(@person.name, person_path(@person)) %>
-
# # => <a href="/person/1-donald-e-knuth">Donald E. Knuth</a>
-
1
def parameterize(string, sep = '-')
-
# replace accented chars with their ascii equivalents
-
parameterized_string = transliterate(string)
-
# Turn unwanted chars into the separator
-
parameterized_string.gsub!(/[^a-z0-9\-_]+/i, sep)
-
unless sep.nil? || sep.empty?
-
re_sep = Regexp.escape(sep)
-
# No more than one of the separator in a row.
-
parameterized_string.gsub!(/#{re_sep}{2,}/, sep)
-
# Remove leading/trailing separator.
-
parameterized_string.gsub!(/^#{re_sep}|#{re_sep}$/i, '')
-
end
-
parameterized_string.downcase
-
end
-
-
end
-
end
-
1
require 'active_support/json/decoding'
-
1
require 'active_support/json/encoding'
-
1
require 'active_support/core_ext/module/attribute_accessors'
-
1
require 'active_support/core_ext/module/delegation'
-
1
require 'json'
-
-
1
module ActiveSupport
-
# Look for and parse json strings that look like ISO 8601 times.
-
1
mattr_accessor :parse_json_times
-
-
1
module JSON
-
# matches YAML-formatted dates
-
1
DATE_REGEX = /^(?:\d{4}-\d{2}-\d{2}|\d{4}-\d{1,2}-\d{1,2}[T \t]+\d{1,2}:\d{2}:\d{2}(\.[0-9]*)?(([ \t]*)Z|[-+]\d{2}?(:\d{2})?))$/
-
-
1
class << self
-
# Parses a JSON string (JavaScript Object Notation) into a hash.
-
# See www.json.org for more info.
-
#
-
# ActiveSupport::JSON.decode("{\"team\":\"rails\",\"players\":\"36\"}")
-
# => {"team" => "rails", "players" => "36"}
-
1
def decode(json, options = {})
-
if options.present?
-
raise ArgumentError, "In Rails 4.1, ActiveSupport::JSON.decode no longer " \
-
"accepts an options hash for MultiJSON. MultiJSON reached its end of life " \
-
"and has been removed."
-
end
-
-
data = ::JSON.parse(json, quirks_mode: true)
-
-
if ActiveSupport.parse_json_times
-
convert_dates_from(data)
-
else
-
data
-
end
-
end
-
-
# Returns the class of the error that will be raised when there is an
-
# error in decoding JSON. Using this method means you won't directly
-
# depend on the ActiveSupport's JSON implementation, in case it changes
-
# in the future.
-
#
-
# begin
-
# obj = ActiveSupport::JSON.decode(some_string)
-
# rescue ActiveSupport::JSON.parse_error
-
# Rails.logger.warn("Attempted to decode invalid JSON: #{some_string}")
-
# end
-
1
def parse_error
-
::JSON::ParserError
-
end
-
-
1
private
-
-
1
def convert_dates_from(data)
-
case data
-
when nil
-
nil
-
when DATE_REGEX
-
begin
-
DateTime.parse(data)
-
rescue ArgumentError
-
data
-
end
-
when Array
-
data.map! { |d| convert_dates_from(d) }
-
when Hash
-
data.each do |key, value|
-
data[key] = convert_dates_from(value)
-
end
-
else
-
data
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/object/json'
-
1
require 'active_support/core_ext/module/delegation'
-
-
1
module ActiveSupport
-
1
class << self
-
1
delegate :use_standard_json_time_format, :use_standard_json_time_format=,
-
:time_precision, :time_precision=,
-
:escape_html_entities_in_json, :escape_html_entities_in_json=,
-
:encode_big_decimal_as_string, :encode_big_decimal_as_string=,
-
:json_encoder, :json_encoder=,
-
:to => :'ActiveSupport::JSON::Encoding'
-
end
-
-
1
module JSON
-
# Dumps objects in JSON (JavaScript Object Notation).
-
# See www.json.org for more info.
-
#
-
# ActiveSupport::JSON.encode({ team: 'rails', players: '36' })
-
# # => "{\"team\":\"rails\",\"players\":\"36\"}"
-
1
def self.encode(value, options = nil)
-
Encoding.json_encoder.new(options).encode(value)
-
end
-
-
1
module Encoding #:nodoc:
-
1
class JSONGemEncoder #:nodoc:
-
1
attr_reader :options
-
-
1
def initialize(options = nil)
-
@options = options || {}
-
end
-
-
# Encode the given object into a JSON string
-
1
def encode(value)
-
stringify jsonify value.as_json(options.dup)
-
end
-
-
1
private
-
# Rails does more escaping than the JSON gem natively does (we
-
# escape \u2028 and \u2029 and optionally >, <, & to work around
-
# certain browser problems).
-
1
ESCAPED_CHARS = {
-
"\u2028" => '\u2028',
-
"\u2029" => '\u2029',
-
'>' => '\u003e',
-
'<' => '\u003c',
-
'&' => '\u0026',
-
}
-
-
1
ESCAPE_REGEX_WITH_HTML_ENTITIES = /[\u2028\u2029><&]/u
-
1
ESCAPE_REGEX_WITHOUT_HTML_ENTITIES = /[\u2028\u2029]/u
-
-
# This class wraps all the strings we see and does the extra escaping
-
1
class EscapedString < String #:nodoc:
-
1
def to_json(*)
-
if Encoding.escape_html_entities_in_json
-
super.gsub ESCAPE_REGEX_WITH_HTML_ENTITIES, ESCAPED_CHARS
-
else
-
super.gsub ESCAPE_REGEX_WITHOUT_HTML_ENTITIES, ESCAPED_CHARS
-
end
-
end
-
end
-
-
# Mark these as private so we don't leak encoding-specific constructs
-
1
private_constant :ESCAPED_CHARS, :ESCAPE_REGEX_WITH_HTML_ENTITIES,
-
:ESCAPE_REGEX_WITHOUT_HTML_ENTITIES, :EscapedString
-
-
# Convert an object into a "JSON-ready" representation composed of
-
# primitives like Hash, Array, String, Numeric, and true/false/nil.
-
# Recursively calls #as_json to the object to recursively build a
-
# fully JSON-ready object.
-
#
-
# This allows developers to implement #as_json without having to
-
# worry about what base types of objects they are allowed to return
-
# or having to remember to call #as_json recursively.
-
#
-
# Note: the +options+ hash passed to +object.to_json+ is only passed
-
# to +object.as_json+, not any of this method's recursive +#as_json+
-
# calls.
-
1
def jsonify(value)
-
case value
-
when String
-
EscapedString.new(value)
-
when Numeric, NilClass, TrueClass, FalseClass
-
value
-
when Hash
-
Hash[value.map { |k, v| [jsonify(k), jsonify(v)] }]
-
when Array
-
value.map { |v| jsonify(v) }
-
else
-
jsonify value.as_json
-
end
-
end
-
-
# Encode a "jsonified" Ruby data structure using the JSON gem
-
1
def stringify(jsonified)
-
::JSON.generate(jsonified, quirks_mode: true, max_nesting: false)
-
end
-
end
-
-
1
class << self
-
# If true, use ISO 8601 format for dates and times. Otherwise, fall back
-
# to the Active Support legacy format.
-
1
attr_accessor :use_standard_json_time_format
-
-
# If true, encode >, <, & as escaped unicode sequences (e.g. > as \u003e)
-
# as a safety measure.
-
1
attr_accessor :escape_html_entities_in_json
-
-
# Sets the precision of encoded time values.
-
# Defaults to 3 (equivalent to millisecond precision)
-
1
attr_accessor :time_precision
-
-
# Sets the encoder used by Rails to encode Ruby objects into JSON strings
-
# in +Object#to_json+ and +ActiveSupport::JSON.encode+.
-
1
attr_accessor :json_encoder
-
-
1
def encode_big_decimal_as_string=(as_string)
-
message = \
-
"The JSON encoder in Rails 4.1 no longer supports encoding BigDecimals as JSON numbers. Instead, " \
-
"the new encoder will always encode them as strings.\n\n" \
-
"You are seeing this error because you have 'active_support.encode_big_decimal_as_string' in " \
-
"your configuration file. If you have been setting this to true, you can safely remove it from " \
-
"your configuration. Otherwise, you should add the 'activesupport-json_encoder' gem to your " \
-
"Gemfile in order to restore this functionality."
-
-
raise NotImplementedError, message
-
end
-
-
1
def encode_big_decimal_as_string
-
message = \
-
"The JSON encoder in Rails 4.1 no longer supports encoding BigDecimals as JSON numbers. Instead, " \
-
"the new encoder will always encode them as strings.\n\n" \
-
"You are seeing this error because you are trying to check the value of the related configuration, " \
-
"'active_support.encode_big_decimal_as_string'. If your application depends on this option, you should " \
-
"add the 'activesupport-json_encoder' gem to your Gemfile. For now, this option will always be true. " \
-
"In the future, it will be removed from Rails, so you should stop checking its value."
-
-
ActiveSupport::Deprecation.warn message
-
-
true
-
end
-
-
# Deprecate CircularReferenceError
-
1
def const_missing(name)
-
if name == :CircularReferenceError
-
message = "The JSON encoder in Rails 4.1 no longer offers protection from circular references. " \
-
"You are seeing this warning because you are rescuing from (or otherwise referencing) " \
-
"ActiveSupport::Encoding::CircularReferenceError. In the future, this error will be " \
-
"removed from Rails. You should remove these rescue blocks from your code and ensure " \
-
"that your data structures are free of circular references so they can be properly " \
-
"serialized into JSON.\n\n" \
-
"For example, the following Hash contains a circular reference to itself:\n" \
-
" h = {}\n" \
-
" h['circular'] = h\n" \
-
"In this case, calling h.to_json would not work properly."
-
-
ActiveSupport::Deprecation.warn message
-
-
SystemStackError
-
else
-
super
-
end
-
end
-
end
-
-
1
self.use_standard_json_time_format = true
-
1
self.escape_html_entities_in_json = true
-
1
self.json_encoder = JSONGemEncoder
-
1
self.time_precision = 3
-
end
-
end
-
end
-
1
require 'thread_safe'
-
1
require 'openssl'
-
-
1
module ActiveSupport
-
# KeyGenerator is a simple wrapper around OpenSSL's implementation of PBKDF2
-
# It can be used to derive a number of keys for various purposes from a given secret.
-
# This lets Rails applications have a single secure secret, but avoid reusing that
-
# key in multiple incompatible contexts.
-
1
class KeyGenerator
-
1
def initialize(secret, options = {})
-
1
@secret = secret
-
# The default iterations are higher than required for our key derivation uses
-
# on the off chance someone uses this for password storage
-
1
@iterations = options[:iterations] || 2**16
-
end
-
-
# Returns a derived key suitable for use. The default key_size is chosen
-
# to be compatible with the default settings of ActiveSupport::MessageVerifier.
-
# i.e. OpenSSL::Digest::SHA1#block_length
-
1
def generate_key(salt, key_size=64)
-
OpenSSL::PKCS5.pbkdf2_hmac_sha1(@secret, salt, @iterations, key_size)
-
end
-
end
-
-
# CachingKeyGenerator is a wrapper around KeyGenerator which allows users to avoid
-
# re-executing the key generation process when it's called using the same salt and
-
# key_size
-
1
class CachingKeyGenerator
-
1
def initialize(key_generator)
-
1
@key_generator = key_generator
-
1
@cache_keys = ThreadSafe::Cache.new
-
end
-
-
# Returns a derived key suitable for use. The default key_size is chosen
-
# to be compatible with the default settings of ActiveSupport::MessageVerifier.
-
# i.e. OpenSSL::Digest::SHA1#block_length
-
1
def generate_key(salt, key_size=64)
-
@cache_keys["#{salt}#{key_size}"] ||= @key_generator.generate_key(salt, key_size)
-
end
-
end
-
-
1
class LegacyKeyGenerator # :nodoc:
-
1
SECRET_MIN_LENGTH = 30 # Characters
-
-
1
def initialize(secret)
-
ensure_secret_secure(secret)
-
@secret = secret
-
end
-
-
1
def generate_key(salt)
-
@secret
-
end
-
-
1
private
-
-
# To prevent users from using something insecure like "Password" we make sure that the
-
# secret they've provided is at least 30 characters in length.
-
1
def ensure_secret_secure(secret)
-
if secret.blank?
-
raise ArgumentError, "A secret is required to generate an integrity hash " \
-
"for cookie session data. Set a secret_key_base of at least " \
-
"#{SECRET_MIN_LENGTH} characters in config/secrets.yml."
-
end
-
-
if secret.length < SECRET_MIN_LENGTH
-
raise ArgumentError, "Secret should be something secure, " \
-
"like \"#{SecureRandom.hex(16)}\". The value you " \
-
"provided, \"#{secret}\", is shorter than the minimum length " \
-
"of #{SECRET_MIN_LENGTH} characters."
-
end
-
end
-
end
-
end
-
1
module ActiveSupport
-
# lazy_load_hooks allows Rails to lazily load a lot of components and thus
-
# making the app boot faster. Because of this feature now there is no need to
-
# require <tt>ActiveRecord::Base</tt> at boot time purely to apply
-
# configuration. Instead a hook is registered that applies configuration once
-
# <tt>ActiveRecord::Base</tt> is loaded. Here <tt>ActiveRecord::Base</tt> is
-
# used as example but this feature can be applied elsewhere too.
-
#
-
# Here is an example where +on_load+ method is called to register a hook.
-
#
-
# initializer 'active_record.initialize_timezone' do
-
# ActiveSupport.on_load(:active_record) do
-
# self.time_zone_aware_attributes = true
-
# self.default_timezone = :utc
-
# end
-
# end
-
#
-
# When the entirety of +activerecord/lib/active_record/base.rb+ has been
-
# evaluated then +run_load_hooks+ is invoked. The very last line of
-
# +activerecord/lib/active_record/base.rb+ is:
-
#
-
# ActiveSupport.run_load_hooks(:active_record, ActiveRecord::Base)
-
10
@load_hooks = Hash.new { |h,k| h[k] = [] }
-
10
@loaded = Hash.new { |h,k| h[k] = [] }
-
-
1
def self.on_load(name, options = {}, &block)
-
40
@loaded[name].each do |base|
-
4
execute_hook(base, options, block)
-
end
-
-
40
@load_hooks[name] << [block, options]
-
end
-
-
1
def self.execute_hook(base, options, block)
-
39
if options[:yield]
-
8
block.call(base)
-
else
-
31
base.instance_eval(&block)
-
end
-
end
-
-
1
def self.run_load_hooks(name, base = Object)
-
8
@loaded[name] << base
-
8
@load_hooks[name].each do |hook, options|
-
35
execute_hook(base, options, hook)
-
end
-
end
-
end
-
1
require 'active_support/core_ext/module/attribute_accessors'
-
1
require 'active_support/core_ext/class/attribute'
-
1
require 'active_support/subscriber'
-
-
1
module ActiveSupport
-
# ActiveSupport::LogSubscriber is an object set to consume
-
# ActiveSupport::Notifications with the sole purpose of logging them.
-
# The log subscriber dispatches notifications to a registered object based
-
# on its given namespace.
-
#
-
# An example would be Active Record log subscriber responsible for logging
-
# queries:
-
#
-
# module ActiveRecord
-
# class LogSubscriber < ActiveSupport::LogSubscriber
-
# def sql(event)
-
# "#{event.payload[:name]} (#{event.duration}) #{event.payload[:sql]}"
-
# end
-
# end
-
# end
-
#
-
# And it's finally registered as:
-
#
-
# ActiveRecord::LogSubscriber.attach_to :active_record
-
#
-
# Since we need to know all instance methods before attaching the log
-
# subscriber, the line above should be called after your
-
# <tt>ActiveRecord::LogSubscriber</tt> definition.
-
#
-
# After configured, whenever a "sql.active_record" notification is published,
-
# it will properly dispatch the event (ActiveSupport::Notifications::Event) to
-
# the sql method.
-
#
-
# Log subscriber also has some helpers to deal with logging and automatically
-
# flushes all logs when the request finishes (via action_dispatch.callback
-
# notification) in a Rails environment.
-
1
class LogSubscriber < Subscriber
-
# Embed in a String to clear all previous ANSI sequences.
-
1
CLEAR = "\e[0m"
-
1
BOLD = "\e[1m"
-
-
# Colors
-
1
BLACK = "\e[30m"
-
1
RED = "\e[31m"
-
1
GREEN = "\e[32m"
-
1
YELLOW = "\e[33m"
-
1
BLUE = "\e[34m"
-
1
MAGENTA = "\e[35m"
-
1
CYAN = "\e[36m"
-
1
WHITE = "\e[37m"
-
-
1
mattr_accessor :colorize_logging
-
1
self.colorize_logging = true
-
-
1
class << self
-
1
def logger
-
@logger ||= if defined?(Rails) && Rails.respond_to?(:logger)
-
Rails.logger
-
end
-
end
-
-
1
attr_writer :logger
-
-
1
def log_subscribers
-
subscribers
-
end
-
-
# Flush all log_subscribers' logger.
-
1
def flush_all!
-
logger.flush if logger.respond_to?(:flush)
-
end
-
end
-
-
1
def logger
-
LogSubscriber.logger
-
end
-
-
1
def start(name, id, payload)
-
65
super if logger
-
end
-
-
1
def finish(name, id, payload)
-
65
super if logger
-
rescue Exception => e
-
logger.error "Could not log #{name.inspect} event. #{e.class}: #{e.message} #{e.backtrace}"
-
end
-
-
1
protected
-
-
1
%w(info debug warn error fatal unknown).each do |level|
-
6
class_eval <<-METHOD, __FILE__, __LINE__ + 1
-
def #{level}(progname = nil, &block)
-
logger.#{level}(progname, &block) if logger
-
end
-
METHOD
-
end
-
-
# Set color by using a string or one of the defined constants. If a third
-
# option is set to +true+, it also adds bold to the string. This is based
-
# on the Highline implementation and will automatically append CLEAR to the
-
# end of the returned String.
-
1
def color(text, color, bold=false)
-
50
return text unless colorize_logging
-
50
color = self.class.const_get(color.upcase) if color.is_a?(Symbol)
-
50
bold = bold ? BOLD : ""
-
50
"#{bold}#{color}#{text}#{CLEAR}"
-
end
-
end
-
end
-
1
require 'active_support/core_ext/module/attribute_accessors'
-
1
require 'active_support/logger_silence'
-
1
require 'logger'
-
-
1
module ActiveSupport
-
1
class Logger < ::Logger
-
1
include LoggerSilence
-
-
# Broadcasts logs to multiple loggers.
-
1
def self.broadcast(logger) # :nodoc:
-
Module.new do
-
define_method(:add) do |*args, &block|
-
logger.add(*args, &block)
-
super(*args, &block)
-
end
-
-
define_method(:<<) do |x|
-
logger << x
-
super(x)
-
end
-
-
define_method(:close) do
-
logger.close
-
super()
-
end
-
-
define_method(:progname=) do |name|
-
logger.progname = name
-
super(name)
-
end
-
-
define_method(:formatter=) do |formatter|
-
logger.formatter = formatter
-
super(formatter)
-
end
-
-
define_method(:level=) do |level|
-
logger.level = level
-
super(level)
-
end
-
end
-
end
-
-
1
def initialize(*args)
-
1
super
-
1
@formatter = SimpleFormatter.new
-
end
-
-
# Simple formatter which only displays the message.
-
1
class SimpleFormatter < ::Logger::Formatter
-
# This method is invoked when a log event occurs
-
1
def call(severity, timestamp, progname, msg)
-
46
"#{String === msg ? msg : msg.inspect}\n"
-
end
-
end
-
end
-
end
-
1
require 'active_support/concern'
-
-
1
module LoggerSilence
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
cattr_accessor :silencer
-
1
self.silencer = true
-
end
-
-
# Silences the logger for the duration of the block.
-
1
def silence(temporary_level = Logger::ERROR)
-
if silencer
-
begin
-
old_logger_level, self.level = level, temporary_level
-
yield self
-
ensure
-
self.level = old_logger_level
-
end
-
else
-
yield self
-
end
-
end
-
end
-
1
require 'base64'
-
1
require 'active_support/core_ext/object/blank'
-
-
1
module ActiveSupport
-
# +MessageVerifier+ makes it easy to generate and verify messages which are
-
# signed to prevent tampering.
-
#
-
# This is useful for cases like remember-me tokens and auto-unsubscribe links
-
# where the session store isn't suitable or available.
-
#
-
# Remember Me:
-
# cookies[:remember_me] = @verifier.generate([@user.id, 2.weeks.from_now])
-
#
-
# In the authentication filter:
-
#
-
# id, time = @verifier.verify(cookies[:remember_me])
-
# if time < Time.now
-
# self.current_user = User.find(id)
-
# end
-
#
-
# By default it uses Marshal to serialize the message. If you want to use
-
# another serialization method, you can set the serializer in the options
-
# hash upon initialization:
-
#
-
# @verifier = ActiveSupport::MessageVerifier.new('s3Krit', serializer: YAML)
-
1
class MessageVerifier
-
1
class InvalidSignature < StandardError; end
-
-
1
def initialize(secret, options = {})
-
@secret = secret
-
@digest = options[:digest] || 'SHA1'
-
@serializer = options[:serializer] || Marshal
-
end
-
-
1
def verify(signed_message)
-
raise InvalidSignature if signed_message.blank?
-
-
data, digest = signed_message.split("--")
-
if data.present? && digest.present? && secure_compare(digest, generate_digest(data))
-
begin
-
@serializer.load(::Base64.strict_decode64(data))
-
rescue ArgumentError => argument_error
-
raise InvalidSignature if argument_error.message =~ %r{invalid base64}
-
raise
-
end
-
else
-
raise InvalidSignature
-
end
-
end
-
-
1
def generate(value)
-
data = ::Base64.strict_encode64(@serializer.dump(value))
-
"#{data}--#{generate_digest(data)}"
-
end
-
-
1
private
-
# constant-time comparison algorithm to prevent timing attacks
-
1
def secure_compare(a, b)
-
return false unless a.bytesize == b.bytesize
-
-
l = a.unpack "C#{a.bytesize}"
-
-
res = 0
-
b.each_byte { |byte| res |= byte ^ l.shift }
-
res == 0
-
end
-
-
1
def generate_digest(data)
-
require 'openssl' unless defined?(OpenSSL)
-
OpenSSL::HMAC.hexdigest(OpenSSL::Digest.const_get(@digest).new, @secret, data)
-
end
-
end
-
end
-
1
module ActiveSupport #:nodoc:
-
1
module Multibyte
-
1
autoload :Chars, 'active_support/multibyte/chars'
-
1
autoload :Unicode, 'active_support/multibyte/unicode'
-
-
# The proxy class returned when calling mb_chars. You can use this accessor
-
# to configure your own proxy class so you can support other encodings. See
-
# the ActiveSupport::Multibyte::Chars implementation for an example how to
-
# do this.
-
#
-
# ActiveSupport::Multibyte.proxy_class = CharsForUTF32
-
1
def self.proxy_class=(klass)
-
@proxy_class = klass
-
end
-
-
# Returns the current proxy class.
-
1
def self.proxy_class
-
@proxy_class ||= ActiveSupport::Multibyte::Chars
-
end
-
end
-
end
-
# encoding: utf-8
-
1
require 'active_support/json'
-
1
require 'active_support/core_ext/string/access'
-
1
require 'active_support/core_ext/string/behavior'
-
1
require 'active_support/core_ext/module/delegation'
-
-
1
module ActiveSupport #:nodoc:
-
1
module Multibyte #:nodoc:
-
# Chars enables you to work transparently with UTF-8 encoding in the Ruby
-
# String class without having extensive knowledge about the encoding. A
-
# Chars object accepts a string upon initialization and proxies String
-
# methods in an encoding safe manner. All the normal String methods are also
-
# implemented on the proxy.
-
#
-
# String methods are proxied through the Chars object, and can be accessed
-
# through the +mb_chars+ method. Methods which would normally return a
-
# String object now return a Chars object so methods can be chained.
-
#
-
# 'The Perfect String '.mb_chars.downcase.strip.normalize # => "the perfect string"
-
#
-
# Chars objects are perfectly interchangeable with String objects as long as
-
# no explicit class checks are made. If certain methods do explicitly check
-
# the class, call +to_s+ before you pass chars objects to them.
-
#
-
# bad.explicit_checking_method 'T'.mb_chars.downcase.to_s
-
#
-
# The default Chars implementation assumes that the encoding of the string
-
# is UTF-8, if you want to handle different encodings you can write your own
-
# multibyte string handler and configure it through
-
# ActiveSupport::Multibyte.proxy_class.
-
#
-
# class CharsForUTF32
-
# def size
-
# @wrapped_string.size / 4
-
# end
-
#
-
# def self.accepts?(string)
-
# string.length % 4 == 0
-
# end
-
# end
-
#
-
# ActiveSupport::Multibyte.proxy_class = CharsForUTF32
-
1
class Chars
-
1
include Comparable
-
1
attr_reader :wrapped_string
-
1
alias to_s wrapped_string
-
1
alias to_str wrapped_string
-
-
1
delegate :<=>, :=~, :acts_like_string?, :to => :wrapped_string
-
-
# Creates a new Chars instance by wrapping _string_.
-
1
def initialize(string)
-
@wrapped_string = string
-
@wrapped_string.force_encoding(Encoding::UTF_8) unless @wrapped_string.frozen?
-
end
-
-
# Forward all undefined methods to the wrapped string.
-
1
def method_missing(method, *args, &block)
-
result = @wrapped_string.__send__(method, *args, &block)
-
if method.to_s =~ /!$/
-
self if result
-
else
-
result.kind_of?(String) ? chars(result) : result
-
end
-
end
-
-
# Returns +true+ if _obj_ responds to the given method. Private methods
-
# are included in the search only if the optional second parameter
-
# evaluates to +true+.
-
1
def respond_to_missing?(method, include_private)
-
@wrapped_string.respond_to?(method, include_private)
-
end
-
-
# Returns +true+ when the proxy class can handle the string. Returns
-
# +false+ otherwise.
-
1
def self.consumes?(string)
-
string.encoding == Encoding::UTF_8
-
end
-
-
# Works just like <tt>String#split</tt>, with the exception that the items
-
# in the resulting list are Chars instances instead of String. This makes
-
# chaining methods easier.
-
#
-
# 'Café périferôl'.mb_chars.split(/é/).map { |part| part.upcase.to_s } # => ["CAF", " P", "RIFERÔL"]
-
1
def split(*args)
-
@wrapped_string.split(*args).map { |i| self.class.new(i) }
-
end
-
-
# Works like like <tt>String#slice!</tt>, but returns an instance of
-
# Chars, or nil if the string was not modified.
-
1
def slice!(*args)
-
chars(@wrapped_string.slice!(*args))
-
end
-
-
# Reverses all characters in the string.
-
#
-
# 'Café'.mb_chars.reverse.to_s # => 'éfaC'
-
1
def reverse
-
chars(Unicode.unpack_graphemes(@wrapped_string).reverse.flatten.pack('U*'))
-
end
-
-
# Limits the byte size of the string to a number of bytes without breaking
-
# characters. Usable when the storage for a string is limited for some
-
# reason.
-
#
-
# 'こんにちは'.mb_chars.limit(7).to_s # => "こん"
-
1
def limit(limit)
-
slice(0...translate_offset(limit))
-
end
-
-
# Converts characters in the string to uppercase.
-
#
-
# 'Laurent, où sont les tests ?'.mb_chars.upcase.to_s # => "LAURENT, OÙ SONT LES TESTS ?"
-
1
def upcase
-
chars Unicode.upcase(@wrapped_string)
-
end
-
-
# Converts characters in the string to lowercase.
-
#
-
# 'VĚDA A VÝZKUM'.mb_chars.downcase.to_s # => "věda a výzkum"
-
1
def downcase
-
chars Unicode.downcase(@wrapped_string)
-
end
-
-
# Converts characters in the string to the opposite case.
-
#
-
# 'El Cañón".mb_chars.swapcase.to_s # => "eL cAÑÓN"
-
1
def swapcase
-
chars Unicode.swapcase(@wrapped_string)
-
end
-
-
# Converts the first character to uppercase and the remainder to lowercase.
-
#
-
# 'über'.mb_chars.capitalize.to_s # => "Über"
-
1
def capitalize
-
(slice(0) || chars('')).upcase + (slice(1..-1) || chars('')).downcase
-
end
-
-
# Capitalizes the first letter of every word, when possible.
-
#
-
# "ÉL QUE SE ENTERÓ".mb_chars.titleize # => "Él Que Se Enteró"
-
# "日本語".mb_chars.titleize # => "日本語"
-
1
def titleize
-
chars(downcase.to_s.gsub(/\b('?\S)/u) { Unicode.upcase($1)})
-
end
-
1
alias_method :titlecase, :titleize
-
-
# Returns the KC normalization of the string by default. NFKC is
-
# considered the best normalization form for passing strings to databases
-
# and validations.
-
#
-
# * <tt>form</tt> - The form you want to normalize in. Should be one of the following:
-
# <tt>:c</tt>, <tt>:kc</tt>, <tt>:d</tt>, or <tt>:kd</tt>. Default is
-
# ActiveSupport::Multibyte::Unicode.default_normalization_form
-
1
def normalize(form = nil)
-
chars(Unicode.normalize(@wrapped_string, form))
-
end
-
-
# Performs canonical decomposition on all the characters.
-
#
-
# 'é'.length # => 2
-
# 'é'.mb_chars.decompose.to_s.length # => 3
-
1
def decompose
-
chars(Unicode.decompose(:canonical, @wrapped_string.codepoints.to_a).pack('U*'))
-
end
-
-
# Performs composition on all the characters.
-
#
-
# 'é'.length # => 3
-
# 'é'.mb_chars.compose.to_s.length # => 2
-
1
def compose
-
chars(Unicode.compose(@wrapped_string.codepoints.to_a).pack('U*'))
-
end
-
-
# Returns the number of grapheme clusters in the string.
-
#
-
# 'क्षि'.mb_chars.length # => 4
-
# 'क्षि'.mb_chars.grapheme_length # => 3
-
1
def grapheme_length
-
Unicode.unpack_graphemes(@wrapped_string).length
-
end
-
-
# Replaces all ISO-8859-1 or CP1252 characters by their UTF-8 equivalent
-
# resulting in a valid UTF-8 string.
-
#
-
# Passing +true+ will forcibly tidy all bytes, assuming that the string's
-
# encoding is entirely CP1252 or ISO-8859-1.
-
1
def tidy_bytes(force = false)
-
chars(Unicode.tidy_bytes(@wrapped_string, force))
-
end
-
-
1
def as_json(options = nil) #:nodoc:
-
to_s.as_json(options)
-
end
-
-
1
%w(capitalize downcase reverse tidy_bytes upcase).each do |method|
-
5
define_method("#{method}!") do |*args|
-
@wrapped_string = send(method, *args).to_s
-
self
-
end
-
end
-
-
1
protected
-
-
1
def translate_offset(byte_offset) #:nodoc:
-
return nil if byte_offset.nil?
-
return 0 if @wrapped_string == ''
-
-
begin
-
@wrapped_string.byteslice(0...byte_offset).unpack('U*').length
-
rescue ArgumentError
-
byte_offset -= 1
-
retry
-
end
-
end
-
-
1
def chars(string) #:nodoc:
-
self.class.new(string)
-
end
-
end
-
end
-
end
-
1
require 'active_support/notifications/instrumenter'
-
1
require 'active_support/notifications/fanout'
-
1
require 'active_support/per_thread_registry'
-
-
1
module ActiveSupport
-
# = Notifications
-
#
-
# <tt>ActiveSupport::Notifications</tt> provides an instrumentation API for
-
# Ruby.
-
#
-
# == Instrumenters
-
#
-
# To instrument an event you just need to do:
-
#
-
# ActiveSupport::Notifications.instrument('render', extra: :information) do
-
# render text: 'Foo'
-
# end
-
#
-
# That executes the block first and notifies all subscribers once done.
-
#
-
# In the example above +render+ is the name of the event, and the rest is called
-
# the _payload_. The payload is a mechanism that allows instrumenters to pass
-
# extra information to subscribers. Payloads consist of a hash whose contents
-
# are arbitrary and generally depend on the event.
-
#
-
# == Subscribers
-
#
-
# You can consume those events and the information they provide by registering
-
# a subscriber.
-
#
-
# ActiveSupport::Notifications.subscribe('render') do |name, start, finish, id, payload|
-
# name # => String, name of the event (such as 'render' from above)
-
# start # => Time, when the instrumented block started execution
-
# finish # => Time, when the instrumented block ended execution
-
# id # => String, unique ID for this notification
-
# payload # => Hash, the payload
-
# end
-
#
-
# For instance, let's store all "render" events in an array:
-
#
-
# events = []
-
#
-
# ActiveSupport::Notifications.subscribe('render') do |*args|
-
# events << ActiveSupport::Notifications::Event.new(*args)
-
# end
-
#
-
# That code returns right away, you are just subscribing to "render" events.
-
# The block is saved and will be called whenever someone instruments "render":
-
#
-
# ActiveSupport::Notifications.instrument('render', extra: :information) do
-
# render text: 'Foo'
-
# end
-
#
-
# event = events.first
-
# event.name # => "render"
-
# event.duration # => 10 (in milliseconds)
-
# event.payload # => { extra: :information }
-
#
-
# The block in the <tt>subscribe</tt> call gets the name of the event, start
-
# timestamp, end timestamp, a string with a unique identifier for that event
-
# (something like "535801666f04d0298cd6"), and a hash with the payload, in
-
# that order.
-
#
-
# If an exception happens during that particular instrumentation the payload will
-
# have a key <tt>:exception</tt> with an array of two elements as value: a string with
-
# the name of the exception class, and the exception message.
-
#
-
# As the previous example depicts, the class <tt>ActiveSupport::Notifications::Event</tt>
-
# is able to take the arguments as they come and provide an object-oriented
-
# interface to that data.
-
#
-
# It is also possible to pass an object as the second parameter passed to the
-
# <tt>subscribe</tt> method instead of a block:
-
#
-
# module ActionController
-
# class PageRequest
-
# def call(name, started, finished, unique_id, payload)
-
# Rails.logger.debug ['notification:', name, started, finished, unique_id, payload].join(' ')
-
# end
-
# end
-
# end
-
#
-
# ActiveSupport::Notifications.subscribe('process_action.action_controller', ActionController::PageRequest.new)
-
#
-
# resulting in the following output within the logs including a hash with the payload:
-
#
-
# notification: process_action.action_controller 2012-04-13 01:08:35 +0300 2012-04-13 01:08:35 +0300 af358ed7fab884532ec7 {
-
# controller: "Devise::SessionsController",
-
# action: "new",
-
# params: {"action"=>"new", "controller"=>"devise/sessions"},
-
# format: :html,
-
# method: "GET",
-
# path: "/login/sign_in",
-
# status: 200,
-
# view_runtime: 279.3080806732178,
-
# db_runtime: 40.053
-
# }
-
#
-
# You can also subscribe to all events whose name matches a certain regexp:
-
#
-
# ActiveSupport::Notifications.subscribe(/render/) do |*args|
-
# ...
-
# end
-
#
-
# and even pass no argument to <tt>subscribe</tt>, in which case you are subscribing
-
# to all events.
-
#
-
# == Temporary Subscriptions
-
#
-
# Sometimes you do not want to subscribe to an event for the entire life of
-
# the application. There are two ways to unsubscribe.
-
#
-
# WARNING: The instrumentation framework is designed for long-running subscribers,
-
# use this feature sparingly because it wipes some internal caches and that has
-
# a negative impact on performance.
-
#
-
# === Subscribe While a Block Runs
-
#
-
# You can subscribe to some event temporarily while some block runs. For
-
# example, in
-
#
-
# callback = lambda {|*args| ... }
-
# ActiveSupport::Notifications.subscribed(callback, "sql.active_record") do
-
# ...
-
# end
-
#
-
# the callback will be called for all "sql.active_record" events instrumented
-
# during the execution of the block. The callback is unsubscribed automatically
-
# after that.
-
#
-
# === Manual Unsubscription
-
#
-
# The +subscribe+ method returns a subscriber object:
-
#
-
# subscriber = ActiveSupport::Notifications.subscribe("render") do |*args|
-
# ...
-
# end
-
#
-
# To prevent that block from being called anymore, just unsubscribe passing
-
# that reference:
-
#
-
# ActiveSupport::Notifications.unsubscribe(subscriber)
-
#
-
# == Default Queue
-
#
-
# Notifications ships with a queue implementation that consumes and publishes events
-
# to all log subscribers. You can use any queue implementation you want.
-
#
-
1
module Notifications
-
1
class << self
-
1
attr_accessor :notifier
-
-
1
def publish(name, *args)
-
notifier.publish(name, *args)
-
end
-
-
1
def instrument(name, payload = {})
-
29
if notifier.listening?(name)
-
32
instrumenter.instrument(name, payload) { yield payload if block_given? }
-
else
-
13
yield payload if block_given?
-
end
-
end
-
-
1
def subscribe(*args, &block)
-
40
notifier.subscribe(*args, &block)
-
end
-
-
1
def subscribed(callback, *args, &block)
-
subscriber = subscribe(*args, &callback)
-
yield
-
ensure
-
unsubscribe(subscriber)
-
end
-
-
1
def unsubscribe(args)
-
8
notifier.unsubscribe(args)
-
end
-
-
1
def instrumenter
-
17
InstrumentationRegistry.instance.instrumenter_for(notifier)
-
end
-
end
-
-
# This class is a registry which holds all of the +Instrumenter+ objects
-
# in a particular thread local. To access the +Instrumenter+ object for a
-
# particular +notifier+, you can call the following method:
-
#
-
# InstrumentationRegistry.instrumenter_for(notifier)
-
#
-
# The instrumenters for multiple notifiers are held in a single instance of
-
# this class.
-
1
class InstrumentationRegistry # :nodoc:
-
1
extend ActiveSupport::PerThreadRegistry
-
-
1
def initialize
-
1
@registry = {}
-
end
-
-
1
def instrumenter_for(notifier)
-
17
@registry[notifier] ||= Instrumenter.new(notifier)
-
end
-
end
-
-
1
self.notifier = Fanout.new
-
end
-
end
-
1
require 'mutex_m'
-
1
require 'thread_safe'
-
-
1
module ActiveSupport
-
1
module Notifications
-
# This is a default queue implementation that ships with Notifications.
-
# It just pushes events to all registered log subscribers.
-
#
-
# This class is thread safe. All methods are reentrant.
-
1
class Fanout
-
1
include Mutex_m
-
-
1
def initialize
-
1
@subscribers = []
-
1
@listeners_for = ThreadSafe::Cache.new
-
1
super
-
end
-
-
1
def subscribe(pattern = nil, block = Proc.new)
-
40
subscriber = Subscribers.new pattern, block
-
40
synchronize do
-
40
@subscribers << subscriber
-
40
@listeners_for.clear
-
end
-
40
subscriber
-
end
-
-
1
def unsubscribe(subscriber)
-
8
synchronize do
-
245
@subscribers.reject! { |s| s.matches?(subscriber) }
-
8
@listeners_for.clear
-
end
-
end
-
-
1
def start(name, id, payload)
-
199
listeners_for(name).each { |s| s.start(name, id, payload) }
-
end
-
-
1
def finish(name, id, payload)
-
199
listeners_for(name).each { |s| s.finish(name, id, payload) }
-
end
-
-
1
def publish(name, *args)
-
listeners_for(name).each { |s| s.publish(name, *args) }
-
end
-
-
1
def listeners_for(name)
-
# this is correctly done double-checked locking (ThreadSafe::Cache's lookups have volatile semantics)
-
@listeners_for[name] || synchronize do
-
# use synchronisation when accessing @subscribers
-
741
@listeners_for[name] ||= @subscribers.select { |s| s.subscribed_to?(name) }
-
169
end
-
end
-
-
1
def listening?(name)
-
29
listeners_for(name).any?
-
end
-
-
# This is a sync queue, so there is no waiting.
-
1
def wait
-
end
-
-
1
module Subscribers # :nodoc:
-
1
def self.new(pattern, listener)
-
40
if listener.respond_to?(:start) and listener.respond_to?(:finish)
-
28
subscriber = Evented.new pattern, listener
-
else
-
12
subscriber = Timed.new pattern, listener
-
end
-
-
40
unless pattern
-
AllMessages.new(subscriber)
-
else
-
40
subscriber
-
end
-
end
-
-
1
class Evented #:nodoc:
-
1
def initialize(pattern, delegate)
-
40
@pattern = pattern
-
40
@delegate = delegate
-
40
@can_publish = delegate.respond_to?(:publish)
-
end
-
-
1
def publish(name, *args)
-
if @can_publish
-
@delegate.publish name, *args
-
end
-
end
-
-
1
def start(name, id, payload)
-
119
@delegate.start name, id, payload
-
end
-
-
1
def finish(name, id, payload)
-
119
@delegate.finish name, id, payload
-
end
-
-
1
def subscribed_to?(name)
-
716
@pattern === name.to_s
-
end
-
-
1
def matches?(subscriber_or_name)
-
self === subscriber_or_name ||
-
237
@pattern && @pattern === subscriber_or_name
-
end
-
end
-
-
1
class Timed < Evented
-
1
def publish(name, *args)
-
@delegate.call name, *args
-
end
-
-
1
def start(name, id, payload)
-
10
timestack = Thread.current[:_timestack] ||= []
-
10
timestack.push Time.now
-
end
-
-
1
def finish(name, id, payload)
-
10
timestack = Thread.current[:_timestack]
-
10
started = timestack.pop
-
10
@delegate.call(name, started, Time.now, id, payload)
-
end
-
end
-
-
1
class AllMessages # :nodoc:
-
1
def initialize(delegate)
-
@delegate = delegate
-
end
-
-
1
def start(name, id, payload)
-
@delegate.start name, id, payload
-
end
-
-
1
def finish(name, id, payload)
-
@delegate.finish name, id, payload
-
end
-
-
1
def publish(name, *args)
-
@delegate.publish name, *args
-
end
-
-
1
def subscribed_to?(name)
-
true
-
end
-
-
1
alias :matches? :===
-
end
-
end
-
end
-
end
-
end
-
1
require 'securerandom'
-
-
1
module ActiveSupport
-
1
module Notifications
-
# Instrumenters are stored in a thread local.
-
1
class Instrumenter
-
1
attr_reader :id
-
-
1
def initialize(notifier)
-
1
@id = unique_id
-
1
@notifier = notifier
-
end
-
-
# Instrument the given block by measuring the time taken to execute it
-
# and publish it. Notice that events get sent even if an error occurs
-
# in the passed-in block.
-
1
def instrument(name, payload={})
-
70
start name, payload
-
70
begin
-
70
yield payload
-
rescue Exception => e
-
payload[:exception] = [e.class.name, e.message]
-
raise e
-
ensure
-
70
finish name, payload
-
70
end
-
end
-
-
# Send a start notification with +name+ and +payload+.
-
1
def start(name, payload)
-
70
@notifier.start name, @id, payload
-
end
-
-
# Send a finish notification with +name+ and +payload+.
-
1
def finish(name, payload)
-
70
@notifier.finish name, @id, payload
-
end
-
-
1
private
-
-
1
def unique_id
-
1
SecureRandom.hex(10)
-
end
-
end
-
-
1
class Event
-
1
attr_reader :name, :time, :transaction_id, :payload, :children
-
1
attr_accessor :end
-
-
1
def initialize(name, start, ending, transaction_id, payload)
-
65
@name = name
-
65
@payload = payload.dup
-
65
@time = start
-
65
@transaction_id = transaction_id
-
65
@end = ending
-
65
@children = []
-
65
@duration = nil
-
end
-
-
1
def duration
-
92
@duration ||= 1000.0 * (self.end - time)
-
end
-
-
1
def <<(event)
-
2
@children << event
-
end
-
-
1
def parent_of?(event)
-
@children.include? event
-
end
-
end
-
end
-
end
-
1
module ActiveSupport
-
1
module NumberHelper
-
1
extend ActiveSupport::Autoload
-
-
1
eager_autoload do
-
1
autoload :NumberConverter
-
1
autoload :NumberToRoundedConverter
-
1
autoload :NumberToDelimitedConverter
-
1
autoload :NumberToHumanConverter
-
1
autoload :NumberToHumanSizeConverter
-
1
autoload :NumberToPhoneConverter
-
1
autoload :NumberToCurrencyConverter
-
1
autoload :NumberToPercentageConverter
-
end
-
-
1
extend self
-
-
# Formats a +number+ into a US phone number (e.g., (555)
-
# 123-9876). You can customize the format in the +options+ hash.
-
#
-
# ==== Options
-
#
-
# * <tt>:area_code</tt> - Adds parentheses around the area code.
-
# * <tt>:delimiter</tt> - Specifies the delimiter to use
-
# (defaults to "-").
-
# * <tt>:extension</tt> - Specifies an extension to add to the
-
# end of the generated number.
-
# * <tt>:country_code</tt> - Sets the country code for the phone
-
# number.
-
# ==== Examples
-
#
-
# number_to_phone(5551234) # => 555-1234
-
# number_to_phone('5551234') # => 555-1234
-
# number_to_phone(1235551234) # => 123-555-1234
-
# number_to_phone(1235551234, area_code: true) # => (123) 555-1234
-
# number_to_phone(1235551234, delimiter: ' ') # => 123 555 1234
-
# number_to_phone(1235551234, area_code: true, extension: 555) # => (123) 555-1234 x 555
-
# number_to_phone(1235551234, country_code: 1) # => +1-123-555-1234
-
# number_to_phone('123a456') # => 123a456
-
#
-
# number_to_phone(1235551234, country_code: 1, extension: 1343, delimiter: '.')
-
# # => +1.123.555.1234 x 1343
-
1
def number_to_phone(number, options = {})
-
NumberToPhoneConverter.convert(number, options)
-
end
-
-
# Formats a +number+ into a currency string (e.g., $13.65). You
-
# can customize the format in the +options+ hash.
-
#
-
# ==== Options
-
#
-
# * <tt>:locale</tt> - Sets the locale to be used for formatting
-
# (defaults to current locale).
-
# * <tt>:precision</tt> - Sets the level of precision (defaults
-
# to 2).
-
# * <tt>:unit</tt> - Sets the denomination of the currency
-
# (defaults to "$").
-
# * <tt>:separator</tt> - Sets the separator between the units
-
# (defaults to ".").
-
# * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
-
# to ",").
-
# * <tt>:format</tt> - Sets the format for non-negative numbers
-
# (defaults to "%u%n"). Fields are <tt>%u</tt> for the
-
# currency, and <tt>%n</tt> for the number.
-
# * <tt>:negative_format</tt> - Sets the format for negative
-
# numbers (defaults to prepending an hyphen to the formatted
-
# number given by <tt>:format</tt>). Accepts the same fields
-
# than <tt>:format</tt>, except <tt>%n</tt> is here the
-
# absolute value of the number.
-
#
-
# ==== Examples
-
#
-
# number_to_currency(1234567890.50) # => $1,234,567,890.50
-
# number_to_currency(1234567890.506) # => $1,234,567,890.51
-
# number_to_currency(1234567890.506, precision: 3) # => $1,234,567,890.506
-
# number_to_currency(1234567890.506, locale: :fr) # => 1 234 567 890,51 €
-
# number_to_currency('123a456') # => $123a456
-
#
-
# number_to_currency(-1234567890.50, negative_format: '(%u%n)')
-
# # => ($1,234,567,890.50)
-
# number_to_currency(1234567890.50, unit: '£', separator: ',', delimiter: '')
-
# # => £1234567890,50
-
# number_to_currency(1234567890.50, unit: '£', separator: ',', delimiter: '', format: '%n %u')
-
# # => 1234567890,50 £
-
1
def number_to_currency(number, options = {})
-
NumberToCurrencyConverter.convert(number, options)
-
end
-
-
# Formats a +number+ as a percentage string (e.g., 65%). You can
-
# customize the format in the +options+ hash.
-
#
-
# ==== Options
-
#
-
# * <tt>:locale</tt> - Sets the locale to be used for formatting
-
# (defaults to current locale).
-
# * <tt>:precision</tt> - Sets the precision of the number
-
# (defaults to 3).
-
# * <tt>:significant</tt> - If +true+, precision will be the #
-
# of significant_digits. If +false+, the # of fractional
-
# digits (defaults to +false+).
-
# * <tt>:separator</tt> - Sets the separator between the
-
# fractional and integer digits (defaults to ".").
-
# * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
-
# to "").
-
# * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
-
# insignificant zeros after the decimal separator (defaults to
-
# +false+).
-
# * <tt>:format</tt> - Specifies the format of the percentage
-
# string The number field is <tt>%n</tt> (defaults to "%n%").
-
#
-
# ==== Examples
-
#
-
# number_to_percentage(100) # => 100.000%
-
# number_to_percentage('98') # => 98.000%
-
# number_to_percentage(100, precision: 0) # => 100%
-
# number_to_percentage(1000, delimiter: '.', separator: ',') # => 1.000,000%
-
# number_to_percentage(302.24398923423, precision: 5) # => 302.24399%
-
# number_to_percentage(1000, locale: :fr) # => 1 000,000%
-
# number_to_percentage('98a') # => 98a%
-
# number_to_percentage(100, format: '%n %') # => 100 %
-
1
def number_to_percentage(number, options = {})
-
NumberToPercentageConverter.convert(number, options)
-
end
-
-
# Formats a +number+ with grouped thousands using +delimiter+
-
# (e.g., 12,324). You can customize the format in the +options+
-
# hash.
-
#
-
# ==== Options
-
#
-
# * <tt>:locale</tt> - Sets the locale to be used for formatting
-
# (defaults to current locale).
-
# * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
-
# to ",").
-
# * <tt>:separator</tt> - Sets the separator between the
-
# fractional and integer digits (defaults to ".").
-
#
-
# ==== Examples
-
#
-
# number_to_delimited(12345678) # => 12,345,678
-
# number_to_delimited('123456') # => 123,456
-
# number_to_delimited(12345678.05) # => 12,345,678.05
-
# number_to_delimited(12345678, delimiter: '.') # => 12.345.678
-
# number_to_delimited(12345678, delimiter: ',') # => 12,345,678
-
# number_to_delimited(12345678.05, separator: ' ') # => 12,345,678 05
-
# number_to_delimited(12345678.05, locale: :fr) # => 12 345 678,05
-
# number_to_delimited('112a') # => 112a
-
# number_to_delimited(98765432.98, delimiter: ' ', separator: ',')
-
# # => 98 765 432,98
-
1
def number_to_delimited(number, options = {})
-
NumberToDelimitedConverter.convert(number, options)
-
end
-
-
# Formats a +number+ with the specified level of
-
# <tt>:precision</tt> (e.g., 112.32 has a precision of 2 if
-
# +:significant+ is +false+, and 5 if +:significant+ is +true+).
-
# You can customize the format in the +options+ hash.
-
#
-
# ==== Options
-
#
-
# * <tt>:locale</tt> - Sets the locale to be used for formatting
-
# (defaults to current locale).
-
# * <tt>:precision</tt> - Sets the precision of the number
-
# (defaults to 3).
-
# * <tt>:significant</tt> - If +true+, precision will be the #
-
# of significant_digits. If +false+, the # of fractional
-
# digits (defaults to +false+).
-
# * <tt>:separator</tt> - Sets the separator between the
-
# fractional and integer digits (defaults to ".").
-
# * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
-
# to "").
-
# * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
-
# insignificant zeros after the decimal separator (defaults to
-
# +false+).
-
#
-
# ==== Examples
-
#
-
# number_to_rounded(111.2345) # => 111.235
-
# number_to_rounded(111.2345, precision: 2) # => 111.23
-
# number_to_rounded(13, precision: 5) # => 13.00000
-
# number_to_rounded(389.32314, precision: 0) # => 389
-
# number_to_rounded(111.2345, significant: true) # => 111
-
# number_to_rounded(111.2345, precision: 1, significant: true) # => 100
-
# number_to_rounded(13, precision: 5, significant: true) # => 13.000
-
# number_to_rounded(111.234, locale: :fr) # => 111,234
-
#
-
# number_to_rounded(13, precision: 5, significant: true, strip_insignificant_zeros: true)
-
# # => 13
-
#
-
# number_to_rounded(389.32314, precision: 4, significant: true) # => 389.3
-
# number_to_rounded(1111.2345, precision: 2, separator: ',', delimiter: '.')
-
# # => 1.111,23
-
1
def number_to_rounded(number, options = {})
-
NumberToRoundedConverter.convert(number, options)
-
end
-
-
# Formats the bytes in +number+ into a more understandable
-
# representation (e.g., giving it 1500 yields 1.5 KB). This
-
# method is useful for reporting file sizes to users. You can
-
# customize the format in the +options+ hash.
-
#
-
# See <tt>number_to_human</tt> if you want to pretty-print a
-
# generic number.
-
#
-
# ==== Options
-
#
-
# * <tt>:locale</tt> - Sets the locale to be used for formatting
-
# (defaults to current locale).
-
# * <tt>:precision</tt> - Sets the precision of the number
-
# (defaults to 3).
-
# * <tt>:significant</tt> - If +true+, precision will be the #
-
# of significant_digits. If +false+, the # of fractional
-
# digits (defaults to +true+)
-
# * <tt>:separator</tt> - Sets the separator between the
-
# fractional and integer digits (defaults to ".").
-
# * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
-
# to "").
-
# * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
-
# insignificant zeros after the decimal separator (defaults to
-
# +true+)
-
# * <tt>:prefix</tt> - If +:si+ formats the number using the SI
-
# prefix (defaults to :binary)
-
#
-
# ==== Examples
-
#
-
# number_to_human_size(123) # => 123 Bytes
-
# number_to_human_size(1234) # => 1.21 KB
-
# number_to_human_size(12345) # => 12.1 KB
-
# number_to_human_size(1234567) # => 1.18 MB
-
# number_to_human_size(1234567890) # => 1.15 GB
-
# number_to_human_size(1234567890123) # => 1.12 TB
-
# number_to_human_size(1234567, precision: 2) # => 1.2 MB
-
# number_to_human_size(483989, precision: 2) # => 470 KB
-
# number_to_human_size(1234567, precision: 2, separator: ',') # => 1,2 MB
-
#
-
# Non-significant zeros after the fractional separator are stripped out by
-
# default (set <tt>:strip_insignificant_zeros</tt> to +false+ to change that):
-
#
-
# number_to_human_size(1234567890123, precision: 5) # => "1.1229 TB"
-
# number_to_human_size(524288000, precision: 5) # => "500 MB"
-
1
def number_to_human_size(number, options = {})
-
NumberToHumanSizeConverter.convert(number, options)
-
end
-
-
# Pretty prints (formats and approximates) a number in a way it
-
# is more readable by humans (eg.: 1200000000 becomes "1.2
-
# Billion"). This is useful for numbers that can get very large
-
# (and too hard to read).
-
#
-
# See <tt>number_to_human_size</tt> if you want to print a file
-
# size.
-
#
-
# You can also define your own unit-quantifier names if you want
-
# to use other decimal units (eg.: 1500 becomes "1.5
-
# kilometers", 0.150 becomes "150 milliliters", etc). You may
-
# define a wide range of unit quantifiers, even fractional ones
-
# (centi, deci, mili, etc).
-
#
-
# ==== Options
-
#
-
# * <tt>:locale</tt> - Sets the locale to be used for formatting
-
# (defaults to current locale).
-
# * <tt>:precision</tt> - Sets the precision of the number
-
# (defaults to 3).
-
# * <tt>:significant</tt> - If +true+, precision will be the #
-
# of significant_digits. If +false+, the # of fractional
-
# digits (defaults to +true+)
-
# * <tt>:separator</tt> - Sets the separator between the
-
# fractional and integer digits (defaults to ".").
-
# * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults
-
# to "").
-
# * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
-
# insignificant zeros after the decimal separator (defaults to
-
# +true+)
-
# * <tt>:units</tt> - A Hash of unit quantifier names. Or a
-
# string containing an i18n scope where to find this hash. It
-
# might have the following keys:
-
# * *integers*: <tt>:unit</tt>, <tt>:ten</tt>,
-
# *<tt>:hundred</tt>, <tt>:thousand</tt>, <tt>:million</tt>,
-
# *<tt>:billion</tt>, <tt>:trillion</tt>,
-
# *<tt>:quadrillion</tt>
-
# * *fractionals*: <tt>:deci</tt>, <tt>:centi</tt>,
-
# *<tt>:mili</tt>, <tt>:micro</tt>, <tt>:nano</tt>,
-
# *<tt>:pico</tt>, <tt>:femto</tt>
-
# * <tt>:format</tt> - Sets the format of the output string
-
# (defaults to "%n %u"). The field types are:
-
# * %u - The quantifier (ex.: 'thousand')
-
# * %n - The number
-
#
-
# ==== Examples
-
#
-
# number_to_human(123) # => "123"
-
# number_to_human(1234) # => "1.23 Thousand"
-
# number_to_human(12345) # => "12.3 Thousand"
-
# number_to_human(1234567) # => "1.23 Million"
-
# number_to_human(1234567890) # => "1.23 Billion"
-
# number_to_human(1234567890123) # => "1.23 Trillion"
-
# number_to_human(1234567890123456) # => "1.23 Quadrillion"
-
# number_to_human(1234567890123456789) # => "1230 Quadrillion"
-
# number_to_human(489939, precision: 2) # => "490 Thousand"
-
# number_to_human(489939, precision: 4) # => "489.9 Thousand"
-
# number_to_human(1234567, precision: 4,
-
# significant: false) # => "1.2346 Million"
-
# number_to_human(1234567, precision: 1,
-
# separator: ',',
-
# significant: false) # => "1,2 Million"
-
#
-
# Non-significant zeros after the decimal separator are stripped
-
# out by default (set <tt>:strip_insignificant_zeros</tt> to
-
# +false+ to change that):
-
#
-
# number_to_human(12345012345, significant_digits: 6) # => "12.345 Billion"
-
# number_to_human(500000000, precision: 5) # => "500 Million"
-
#
-
# ==== Custom Unit Quantifiers
-
#
-
# You can also use your own custom unit quantifiers:
-
# number_to_human(500000, units: { unit: 'ml', thousand: 'lt' }) # => "500 lt"
-
#
-
# If in your I18n locale you have:
-
#
-
# distance:
-
# centi:
-
# one: "centimeter"
-
# other: "centimeters"
-
# unit:
-
# one: "meter"
-
# other: "meters"
-
# thousand:
-
# one: "kilometer"
-
# other: "kilometers"
-
# billion: "gazillion-distance"
-
#
-
# Then you could do:
-
#
-
# number_to_human(543934, units: :distance) # => "544 kilometers"
-
# number_to_human(54393498, units: :distance) # => "54400 kilometers"
-
# number_to_human(54393498000, units: :distance) # => "54.4 gazillion-distance"
-
# number_to_human(343, units: :distance, precision: 1) # => "300 meters"
-
# number_to_human(1, units: :distance) # => "1 meter"
-
# number_to_human(0.34, units: :distance) # => "34 centimeters"
-
1
def number_to_human(number, options = {})
-
NumberToHumanConverter.convert(number, options)
-
end
-
end
-
end
-
1
require 'active_support/core_ext/hash/deep_merge'
-
-
1
module ActiveSupport
-
1
class OptionMerger #:nodoc:
-
1
instance_methods.each do |method|
-
99
undef_method(method) if method !~ /^(__|instance_eval|class|object_id)/
-
end
-
-
1
def initialize(context, options)
-
@context, @options = context, options
-
end
-
-
1
private
-
1
def method_missing(method, *arguments, &block)
-
if arguments.first.is_a?(Proc)
-
proc = arguments.pop
-
arguments << lambda { |*args| @options.deep_merge(proc.call(*args)) }
-
else
-
arguments << (arguments.last.respond_to?(:to_hash) ? @options.deep_merge(arguments.pop) : @options.dup)
-
end
-
-
@context.__send__(method, *arguments, &block)
-
end
-
end
-
end
-
1
module ActiveSupport
-
# Usually key value pairs are handled something like this:
-
#
-
# h = {}
-
# h[:boy] = 'John'
-
# h[:girl] = 'Mary'
-
# h[:boy] # => 'John'
-
# h[:girl] # => 'Mary'
-
#
-
# Using +OrderedOptions+, the above code could be reduced to:
-
#
-
# h = ActiveSupport::OrderedOptions.new
-
# h.boy = 'John'
-
# h.girl = 'Mary'
-
# h.boy # => 'John'
-
# h.girl # => 'Mary'
-
1
class OrderedOptions < Hash
-
1
alias_method :_get, :[] # preserve the original #[] method
-
1
protected :_get # make it protected
-
-
1
def []=(key, value)
-
101
super(key.to_sym, value)
-
end
-
-
1
def [](key)
-
101
super(key.to_sym)
-
end
-
-
1
def method_missing(name, *args)
-
202
name_string = name.to_s
-
202
if name_string.chomp!('=')
-
101
self[name_string] = args.first
-
else
-
101
self[name]
-
end
-
end
-
-
1
def respond_to_missing?(name, include_private)
-
true
-
end
-
end
-
-
# +InheritableOptions+ provides a constructor to build an +OrderedOptions+
-
# hash inherited from another hash.
-
#
-
# Use this if you already have some hash and you want to create a new one based on it.
-
#
-
# h = ActiveSupport::InheritableOptions.new({ girl: 'Mary', boy: 'John' })
-
# h.girl # => 'Mary'
-
# h.boy # => 'John'
-
1
class InheritableOptions < OrderedOptions
-
1
def initialize(parent = nil)
-
14
if parent.kind_of?(OrderedOptions)
-
# use the faster _get when dealing with OrderedOptions
-
45
super() { |h,k| parent._get(k) }
-
3
elsif parent
-
super() { |h,k| parent[k] }
-
else
-
3
super()
-
end
-
end
-
-
1
def inheritable_copy
-
11
self.class.new(self)
-
end
-
end
-
end
-
1
module ActiveSupport
-
# This module is used to encapsulate access to thread local variables.
-
#
-
# Instead of polluting the thread locals namespace:
-
#
-
# Thread.current[:connection_handler]
-
#
-
# you define a class that extends this module:
-
#
-
# module ActiveRecord
-
# class RuntimeRegistry
-
# extend ActiveSupport::PerThreadRegistry
-
#
-
# attr_accessor :connection_handler
-
# end
-
# end
-
#
-
# and invoke the declared instance accessors as class methods. So
-
#
-
# ActiveRecord::RuntimeRegistry.connection_handler = connection_handler
-
#
-
# sets a connection handler local to the current thread, and
-
#
-
# ActiveRecord::RuntimeRegistry.connection_handler
-
#
-
# returns a connection handler local to the current thread.
-
#
-
# This feature is accomplished by instantiating the class and storing the
-
# instance as a thread local keyed by the class name. In the example above
-
# a key "ActiveRecord::RuntimeRegistry" is stored in <tt>Thread.current</tt>.
-
# The class methods proxy to said thread local instance.
-
#
-
# If the class has an initializer, it must accept no arguments.
-
1
module PerThreadRegistry
-
1
def self.extended(object)
-
6
object.instance_variable_set '@per_thread_registry_key', object.name.freeze
-
end
-
-
1
def instance
-
738
Thread.current[@per_thread_registry_key] ||= new
-
end
-
-
1
protected
-
1
def method_missing(name, *args, &block) # :nodoc:
-
# Caches the method definition as a singleton method of the receiver.
-
2
define_singleton_method(name) do |*a, &b|
-
91
instance.public_send(name, *a, &b)
-
end
-
-
2
send(name, *args, &block)
-
end
-
end
-
end
-
1
module ActiveSupport
-
# A class with no predefined methods that behaves similarly to Builder's
-
# BlankSlate. Used for proxy classes.
-
1
class ProxyObject < ::BasicObject
-
1
undef_method :==
-
1
undef_method :equal?
-
-
# Let ActiveSupport::ProxyObject at least raise exceptions.
-
1
def raise(*args)
-
::Object.send(:raise, *args)
-
end
-
end
-
end
-
# This is private interface.
-
#
-
# Rails components cherry pick from Active Support as needed, but there are a
-
# few features that are used for sure some way or another and it is not worth
-
# to put individual requires absolutely everywhere. Think blank? for example.
-
#
-
# This file is loaded by every Rails component except Active Support itself,
-
# but it does not belong to the Rails public interface. It is internal to
-
# Rails and can change anytime.
-
-
# Defines Object#blank? and Object#present?.
-
1
require 'active_support/core_ext/object/blank'
-
-
# Rails own autoload, eager_load, etc.
-
1
require 'active_support/dependencies/autoload'
-
-
# Support for ClassMethods and the included macro.
-
1
require 'active_support/concern'
-
-
# Defines Class#class_attribute.
-
1
require 'active_support/core_ext/class/attribute'
-
-
# Defines Module#delegate.
-
1
require 'active_support/core_ext/module/delegation'
-
-
# Defines ActiveSupport::Deprecation.
-
1
require 'active_support/deprecation'
-
1
require "active_support"
-
1
require "active_support/i18n_railtie"
-
-
1
module ActiveSupport
-
1
class Railtie < Rails::Railtie # :nodoc:
-
1
config.active_support = ActiveSupport::OrderedOptions.new
-
-
1
config.eager_load_namespaces << ActiveSupport
-
-
1
initializer "active_support.deprecation_behavior" do |app|
-
1
if deprecation = app.config.active_support.deprecation
-
1
ActiveSupport::Deprecation.behavior = deprecation
-
end
-
end
-
-
# Sets the default value for Time.zone
-
# If assigned value cannot be matched to a TimeZone, an exception will be raised.
-
1
initializer "active_support.initialize_time_zone" do |app|
-
1
require 'active_support/core_ext/time/zones'
-
1
zone_default = Time.find_zone!(app.config.time_zone)
-
-
1
unless zone_default
-
raise 'Value assigned to config.time_zone not recognized. ' \
-
'Run "rake -D time" for a list of tasks for finding appropriate time zone names.'
-
end
-
-
1
Time.zone_default = zone_default
-
end
-
-
# Sets the default week start
-
# If assigned value is not a valid day symbol (e.g. :sunday, :monday, ...), an exception will be raised.
-
1
initializer "active_support.initialize_beginning_of_week" do |app|
-
1
require 'active_support/core_ext/date/calculations'
-
1
beginning_of_week_default = Date.find_beginning_of_week!(app.config.beginning_of_week)
-
-
1
Date.beginning_of_week_default = beginning_of_week_default
-
end
-
-
1
initializer "active_support.set_configs" do |app|
-
1
app.config.active_support.each do |k, v|
-
1
k = "#{k}="
-
1
ActiveSupport.send(k, v) if ActiveSupport.respond_to? k
-
end
-
end
-
end
-
end
-
1
require 'active_support/concern'
-
1
require 'active_support/core_ext/class/attribute'
-
1
require 'active_support/core_ext/string/inflections'
-
1
require 'active_support/core_ext/array/extract_options'
-
-
1
module ActiveSupport
-
# Rescuable module adds support for easier exception handling.
-
1
module Rescuable
-
1
extend Concern
-
-
1
included do
-
1
class_attribute :rescue_handlers
-
1
self.rescue_handlers = []
-
end
-
-
1
module ClassMethods
-
# Rescue exceptions raised in controller actions.
-
#
-
# <tt>rescue_from</tt> receives a series of exception classes or class
-
# names, and a trailing <tt>:with</tt> option with the name of a method
-
# or a Proc object to be called to handle them. Alternatively a block can
-
# be given.
-
#
-
# Handlers that take one argument will be called with the exception, so
-
# that the exception can be inspected when dealing with it.
-
#
-
# Handlers are inherited. They are searched from right to left, from
-
# bottom to top, and up the hierarchy. The handler of the first class for
-
# which <tt>exception.is_a?(klass)</tt> holds true is the one invoked, if
-
# any.
-
#
-
# class ApplicationController < ActionController::Base
-
# rescue_from User::NotAuthorized, with: :deny_access # self defined exception
-
# rescue_from ActiveRecord::RecordInvalid, with: :show_errors
-
#
-
# rescue_from 'MyAppError::Base' do |exception|
-
# render xml: exception, status: 500
-
# end
-
#
-
# protected
-
# def deny_access
-
# ...
-
# end
-
#
-
# def show_errors(exception)
-
# exception.record.new_record? ? ...
-
# end
-
# end
-
#
-
# Exceptions raised inside exception handlers are not propagated up.
-
1
def rescue_from(*klasses, &block)
-
options = klasses.extract_options!
-
-
unless options.has_key?(:with)
-
if block_given?
-
options[:with] = block
-
else
-
raise ArgumentError, "Need a handler. Supply an options hash that has a :with key as the last argument."
-
end
-
end
-
-
klasses.each do |klass|
-
key = if klass.is_a?(Class) && klass <= Exception
-
klass.name
-
elsif klass.is_a?(String)
-
klass
-
else
-
raise ArgumentError, "#{klass} is neither an Exception nor a String"
-
end
-
-
# put the new handler at the end because the list is read in reverse
-
self.rescue_handlers += [[key, options[:with]]]
-
end
-
end
-
end
-
-
# Tries to rescue the exception by looking up and calling a registered handler.
-
1
def rescue_with_handler(exception)
-
if handler = handler_for_rescue(exception)
-
handler.arity != 0 ? handler.call(exception) : handler.call
-
true # don't rely on the return value of the handler
-
end
-
end
-
-
1
def handler_for_rescue(exception)
-
# We go from right to left because pairs are pushed onto rescue_handlers
-
# as rescue_from declarations are found.
-
_, rescuer = self.class.rescue_handlers.reverse.detect do |klass_name, handler|
-
# The purpose of allowing strings in rescue_from is to support the
-
# declaration of handler associations for exception classes whose
-
# definition is yet unknown.
-
#
-
# Since this loop needs the constants it would be inconsistent to
-
# assume they should exist at this point. An early raised exception
-
# could trigger some other handler and the array could include
-
# precisely a string whose corresponding constant has not yet been
-
# seen. This is why we are tolerant to unknown constants.
-
#
-
# Note that this tolerance only matters if the exception was given as
-
# a string, otherwise a NameError will be raised by the interpreter
-
# itself when rescue_from CONSTANT is executed.
-
klass = self.class.const_get(klass_name) rescue nil
-
klass ||= klass_name.constantize rescue nil
-
exception.is_a?(klass) if klass
-
end
-
-
case rescuer
-
when Symbol
-
method(rescuer)
-
when Proc
-
if rescuer.arity == 0
-
Proc.new { instance_exec(&rescuer) }
-
else
-
Proc.new { |_exception| instance_exec(_exception, &rescuer) }
-
end
-
end
-
end
-
end
-
end
-
1
module ActiveSupport
-
# Wrapping a string in this class gives you a prettier way to test
-
# for equality. The value returned by <tt>Rails.env</tt> is wrapped
-
# in a StringInquirer object so instead of calling this:
-
#
-
# Rails.env == 'production'
-
#
-
# you can call this:
-
#
-
# Rails.env.production?
-
1
class StringInquirer < String
-
1
private
-
-
1
def respond_to_missing?(method_name, include_private = false)
-
method_name[-1] == '?'
-
end
-
-
1
def method_missing(method_name, *arguments)
-
8
if method_name[-1] == '?'
-
8
self == method_name[0..-2]
-
else
-
super
-
end
-
end
-
end
-
end
-
1
require 'active_support/per_thread_registry'
-
-
1
module ActiveSupport
-
# ActiveSupport::Subscriber is an object set to consume
-
# ActiveSupport::Notifications. The subscriber dispatches notifications to
-
# a registered object based on its given namespace.
-
#
-
# An example would be Active Record subscriber responsible for collecting
-
# statistics about queries:
-
#
-
# module ActiveRecord
-
# class StatsSubscriber < ActiveSupport::Subscriber
-
# def sql(event)
-
# Statsd.timing("sql.#{event.payload[:name]}", event.duration)
-
# end
-
# end
-
# end
-
#
-
# And it's finally registered as:
-
#
-
# ActiveRecord::StatsSubscriber.attach_to :active_record
-
#
-
# Since we need to know all instance methods before attaching the log
-
# subscriber, the line above should be called after your subscriber definition.
-
#
-
# After configured, whenever a "sql.active_record" notification is published,
-
# it will properly dispatch the event (ActiveSupport::Notifications::Event) to
-
# the +sql+ method.
-
1
class Subscriber
-
1
class << self
-
-
# Attach the subscriber to a namespace.
-
1
def attach_to(namespace, subscriber=new, notifier=ActiveSupport::Notifications)
-
4
@namespace = namespace
-
4
@subscriber = subscriber
-
4
@notifier = notifier
-
-
4
subscribers << subscriber
-
-
# Add event subscribers for all existing methods on the class.
-
4
subscriber.public_methods(false).each do |event|
-
27
add_event_subscriber(event)
-
end
-
end
-
-
# Adds event subscribers for all new methods added to the class.
-
1
def method_added(event)
-
# Only public methods are added as subscribers, and only if a notifier
-
# has been set up. This means that subscribers will only be set up for
-
# classes that call #attach_to.
-
56
if public_method_defined?(event) && notifier
-
add_event_subscriber(event)
-
end
-
end
-
-
1
def subscribers
-
4
@@subscribers ||= []
-
end
-
-
1
protected
-
-
1
attr_reader :subscriber, :notifier, :namespace
-
-
1
def add_event_subscriber(event)
-
27
return if %w{ start finish }.include?(event.to_s)
-
-
27
pattern = "#{event}.#{namespace}"
-
-
# don't add multiple subscribers (eg. if methods are redefined)
-
27
return if subscriber.patterns.include?(pattern)
-
-
27
subscriber.patterns << pattern
-
27
notifier.subscribe(pattern, subscriber)
-
end
-
end
-
-
1
attr_reader :patterns # :nodoc:
-
-
1
def initialize
-
4
@queue_key = [self.class.name, object_id].join "-"
-
4
@patterns = []
-
4
super
-
end
-
-
1
def start(name, id, payload)
-
65
e = ActiveSupport::Notifications::Event.new(name, Time.now, nil, id, payload)
-
65
parent = event_stack.last
-
65
parent << e if parent
-
-
65
event_stack.push e
-
end
-
-
1
def finish(name, id, payload)
-
65
finished = Time.now
-
65
event = event_stack.pop
-
65
event.end = finished
-
65
event.payload.merge!(payload)
-
-
65
method = name.split('.').first
-
65
send(method, event)
-
end
-
-
1
private
-
-
1
def event_stack
-
195
SubscriberQueueRegistry.instance.get_queue(@queue_key)
-
end
-
end
-
-
# This is a registry for all the event stacks kept for subscribers.
-
#
-
# See the documentation of <tt>ActiveSupport::PerThreadRegistry</tt>
-
# for further details.
-
1
class SubscriberQueueRegistry # :nodoc:
-
1
extend PerThreadRegistry
-
-
1
def initialize
-
1
@registry = {}
-
end
-
-
1
def get_queue(queue_key)
-
195
@registry[queue_key] ||= []
-
end
-
end
-
end
-
1
require 'active_support/core_ext/module/delegation'
-
1
require 'active_support/core_ext/object/blank'
-
1
require 'logger'
-
1
require 'active_support/logger'
-
-
1
module ActiveSupport
-
# Wraps any standard Logger object to provide tagging capabilities.
-
#
-
# logger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT))
-
# logger.tagged('BCX') { logger.info 'Stuff' } # Logs "[BCX] Stuff"
-
# logger.tagged('BCX', "Jason") { logger.info 'Stuff' } # Logs "[BCX] [Jason] Stuff"
-
# logger.tagged('BCX') { logger.tagged('Jason') { logger.info 'Stuff' } } # Logs "[BCX] [Jason] Stuff"
-
#
-
# This is used by the default Rails.logger as configured by Railties to make
-
# it easy to stamp log lines with subdomains, request ids, and anything else
-
# to aid debugging of multi-user production applications.
-
1
module TaggedLogging
-
1
module Formatter # :nodoc:
-
# This method is invoked when a log event occurs.
-
1
def call(severity, timestamp, progname, msg)
-
46
super(severity, timestamp, progname, "#{tags_text}#{msg}")
-
end
-
-
1
def tagged(*tags)
-
new_tags = push_tags(*tags)
-
yield self
-
ensure
-
pop_tags(new_tags.size)
-
end
-
-
1
def push_tags(*tags)
-
tags.flatten.reject(&:blank?).tap do |new_tags|
-
current_tags.concat new_tags
-
end
-
end
-
-
1
def pop_tags(size = 1)
-
current_tags.pop size
-
end
-
-
1
def clear_tags!
-
current_tags.clear
-
end
-
-
1
def current_tags
-
46
Thread.current[:activesupport_tagged_logging_tags] ||= []
-
end
-
-
1
private
-
1
def tags_text
-
46
tags = current_tags
-
46
if tags.any?
-
tags.collect { |tag| "[#{tag}] " }.join
-
end
-
end
-
end
-
-
1
def self.new(logger)
-
# Ensure we set a default formatter so we aren't extending nil!
-
1
logger.formatter ||= ActiveSupport::Logger::SimpleFormatter.new
-
1
logger.formatter.extend Formatter
-
1
logger.extend(self)
-
end
-
-
1
delegate :push_tags, :pop_tags, :clear_tags!, to: :formatter
-
-
1
def tagged(*tags)
-
formatter.tagged(*tags) { yield self }
-
end
-
-
1
def flush
-
clear_tags!
-
super if defined?(super)
-
end
-
end
-
end
-
1
gem 'minitest' # make sure we get the gem, not stdlib
-
1
require 'minitest'
-
1
require 'active_support/testing/tagged_logging'
-
1
require 'active_support/testing/setup_and_teardown'
-
1
require 'active_support/testing/assertions'
-
1
require 'active_support/testing/deprecation'
-
1
require 'active_support/testing/declarative'
-
1
require 'active_support/testing/isolation'
-
1
require 'active_support/testing/constant_lookup'
-
1
require 'active_support/testing/time_helpers'
-
1
require 'active_support/core_ext/kernel/reporting'
-
1
require 'active_support/deprecation'
-
-
1
begin
-
2
silence_warnings { require 'mocha/setup' }
-
rescue LoadError
-
end
-
-
1
module ActiveSupport
-
1
class TestCase < ::Minitest::Test
-
1
Assertion = Minitest::Assertion
-
-
1
alias_method :method_name, :name
-
-
1
$tags = {}
-
1
def self.for_tag(tag)
-
yield if $tags[tag]
-
end
-
-
# FIXME: we have tests that depend on run order, we should fix that and
-
# remove this method call.
-
1
self.i_suck_and_my_tests_are_order_dependent!
-
-
1
include ActiveSupport::Testing::TaggedLogging
-
1
include ActiveSupport::Testing::SetupAndTeardown
-
1
include ActiveSupport::Testing::Assertions
-
1
include ActiveSupport::Testing::Deprecation
-
1
include ActiveSupport::Testing::TimeHelpers
-
1
extend ActiveSupport::Testing::Declarative
-
-
# test/unit backwards compatibility methods
-
1
alias :assert_raise :assert_raises
-
1
alias :assert_not_empty :refute_empty
-
1
alias :assert_not_equal :refute_equal
-
1
alias :assert_not_in_delta :refute_in_delta
-
1
alias :assert_not_in_epsilon :refute_in_epsilon
-
1
alias :assert_not_includes :refute_includes
-
1
alias :assert_not_instance_of :refute_instance_of
-
1
alias :assert_not_kind_of :refute_kind_of
-
1
alias :assert_no_match :refute_match
-
1
alias :assert_not_nil :refute_nil
-
1
alias :assert_not_operator :refute_operator
-
1
alias :assert_not_predicate :refute_predicate
-
1
alias :assert_not_respond_to :refute_respond_to
-
1
alias :assert_not_same :refute_same
-
-
# Fails if the block raises an exception.
-
#
-
# assert_nothing_raised do
-
# ...
-
# end
-
1
def assert_nothing_raised(*args)
-
yield
-
end
-
end
-
end
-
1
require 'active_support/core_ext/object/blank'
-
-
1
module ActiveSupport
-
1
module Testing
-
1
module Assertions
-
# Assert that an expression is not truthy. Passes if <tt>object</tt> is
-
# +nil+ or +false+. "Truthy" means "considered true in a conditional"
-
# like <tt>if foo</tt>.
-
#
-
# assert_not nil # => true
-
# assert_not false # => true
-
# assert_not 'foo' # => 'foo' is not nil or false
-
#
-
# An error message can be specified.
-
#
-
# assert_not foo, 'foo should be false'
-
1
def assert_not(object, message = nil)
-
message ||= "Expected #{mu_pp(object)} to be nil or false"
-
assert !object, message
-
end
-
-
# Test numeric difference between the return value of an expression as a
-
# result of what is evaluated in the yielded block.
-
#
-
# assert_difference 'Article.count' do
-
# post :create, article: {...}
-
# end
-
#
-
# An arbitrary expression is passed in and evaluated.
-
#
-
# assert_difference 'assigns(:article).comments(:reload).size' do
-
# post :create, comment: {...}
-
# end
-
#
-
# An arbitrary positive or negative difference can be specified.
-
# The default is <tt>1</tt>.
-
#
-
# assert_difference 'Article.count', -1 do
-
# post :delete, id: ...
-
# end
-
#
-
# An array of expressions can also be passed in and evaluated.
-
#
-
# assert_difference [ 'Article.count', 'Post.count' ], 2 do
-
# post :create, article: {...}
-
# end
-
#
-
# A lambda or a list of lambdas can be passed in and evaluated:
-
#
-
# assert_difference ->{ Article.count }, 2 do
-
# post :create, article: {...}
-
# end
-
#
-
# assert_difference [->{ Article.count }, ->{ Post.count }], 2 do
-
# post :create, article: {...}
-
# end
-
#
-
# An error message can be specified.
-
#
-
# assert_difference 'Article.count', -1, 'An Article should be destroyed' do
-
# post :delete, id: ...
-
# end
-
1
def assert_difference(expression, difference = 1, message = nil, &block)
-
expressions = Array(expression)
-
-
exps = expressions.map { |e|
-
e.respond_to?(:call) ? e : lambda { eval(e, block.binding) }
-
}
-
before = exps.map { |e| e.call }
-
-
yield
-
-
expressions.zip(exps).each_with_index do |(code, e), i|
-
error = "#{code.inspect} didn't change by #{difference}"
-
error = "#{message}.\n#{error}" if message
-
assert_equal(before[i] + difference, e.call, error)
-
end
-
end
-
-
# Assertion that the numeric result of evaluating an expression is not
-
# changed before and after invoking the passed in block.
-
#
-
# assert_no_difference 'Article.count' do
-
# post :create, article: invalid_attributes
-
# end
-
#
-
# An error message can be specified.
-
#
-
# assert_no_difference 'Article.count', 'An Article should not be created' do
-
# post :create, article: invalid_attributes
-
# end
-
1
def assert_no_difference(expression, message = nil, &block)
-
assert_difference expression, 0, message, &block
-
end
-
end
-
end
-
end
-
1
require "active_support/concern"
-
1
require "active_support/inflector"
-
-
1
module ActiveSupport
-
1
module Testing
-
# Resolves a constant from a minitest spec name.
-
#
-
# Given the following spec-style test:
-
#
-
# describe WidgetsController, :index do
-
# describe "authenticated user" do
-
# describe "returns widgets" do
-
# it "has a controller that exists" do
-
# assert_kind_of WidgetsController, @controller
-
# end
-
# end
-
# end
-
# end
-
#
-
# The test will have the following name:
-
#
-
# "WidgetsController::index::authenticated user::returns widgets"
-
#
-
# The constant WidgetsController can be resolved from the name.
-
# The following code will resolve the constant:
-
#
-
# controller = determine_constant_from_test_name(name) do |constant|
-
# Class === constant && constant < ::ActionController::Metal
-
# end
-
1
module ConstantLookup
-
1
extend ::ActiveSupport::Concern
-
-
1
module ClassMethods # :nodoc:
-
1
def determine_constant_from_test_name(test_name)
-
names = test_name.split "::"
-
while names.size > 0 do
-
names.last.sub!(/Test$/, "")
-
begin
-
constant = names.join("::").constantize
-
break(constant) if yield(constant)
-
rescue NoMethodError # subclass of NameError
-
raise
-
rescue NameError
-
# Constant wasn't found, move on
-
ensure
-
names.pop
-
end
-
end
-
end
-
end
-
-
end
-
end
-
end
-
1
module ActiveSupport
-
1
module Testing
-
1
module Declarative
-
-
1
def self.extended(klass) #:nodoc:
-
1
klass.class_eval do
-
-
1
unless method_defined?(:describe)
-
1
def self.describe(text)
-
if block_given?
-
super
-
else
-
message = "`describe` without a block is deprecated, please switch to: `def self.name; #{text.inspect}; end`\n"
-
ActiveSupport::Deprecation.warn message
-
-
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
-
def self.name
-
"#{text}"
-
end
-
RUBY_EVAL
-
end
-
end
-
end
-
-
end
-
end
-
-
1
unless defined?(Spec)
-
# Helper to define a test method using a String. Under the hood, it replaces
-
# spaces with underscores and defines the test method.
-
#
-
# test "verify something" do
-
# ...
-
# end
-
1
def test(name, &block)
-
test_name = "test_#{name.gsub(/\s+/,'_')}".to_sym
-
defined = instance_method(test_name) rescue false
-
raise "#{test_name} is already defined in #{self}" if defined
-
if block_given?
-
define_method(test_name, &block)
-
else
-
define_method(test_name) do
-
flunk "No implementation provided for #{name}"
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/deprecation'
-
-
1
module ActiveSupport
-
1
module Testing
-
1
module Deprecation #:nodoc:
-
1
def assert_deprecated(match = nil, &block)
-
result, warnings = collect_deprecations(&block)
-
assert !warnings.empty?, "Expected a deprecation warning within the block but received none"
-
if match
-
match = Regexp.new(Regexp.escape(match)) unless match.is_a?(Regexp)
-
assert warnings.any? { |w| w =~ match }, "No deprecation warning matched #{match}: #{warnings.join(', ')}"
-
end
-
result
-
end
-
-
1
def assert_not_deprecated(&block)
-
result, deprecations = collect_deprecations(&block)
-
assert deprecations.empty?, "Expected no deprecation warning within the block but received #{deprecations.size}: \n #{deprecations * "\n "}"
-
result
-
end
-
-
1
def collect_deprecations
-
old_behavior = ActiveSupport::Deprecation.behavior
-
deprecations = []
-
ActiveSupport::Deprecation.behavior = Proc.new do |message, callstack|
-
deprecations << message
-
end
-
result = yield
-
[result, deprecations]
-
ensure
-
ActiveSupport::Deprecation.behavior = old_behavior
-
end
-
end
-
end
-
end
-
1
require 'rbconfig'
-
-
1
module ActiveSupport
-
1
module Testing
-
1
module Isolation
-
1
require 'thread'
-
-
1
def self.included(klass) #:nodoc:
-
klass.class_eval do
-
parallelize_me!
-
end
-
end
-
-
1
def self.forking_env?
-
1
!ENV["NO_FORK"] && ((RbConfig::CONFIG['host_os'] !~ /mswin|mingw/) && (RUBY_PLATFORM !~ /java/))
-
end
-
-
1
@@class_setup_mutex = Mutex.new
-
-
1
def _run_class_setup # class setup method should only happen in parent
-
@@class_setup_mutex.synchronize do
-
unless defined?(@@ran_class_setup) || ENV['ISOLATION_TEST']
-
self.class.setup if self.class.respond_to?(:setup)
-
@@ran_class_setup = true
-
end
-
end
-
end
-
-
1
def run
-
serialized = run_in_isolation do
-
super
-
end
-
-
Marshal.load(serialized)
-
end
-
-
1
module Forking
-
1
def run_in_isolation(&blk)
-
read, write = IO.pipe
-
read.binmode
-
write.binmode
-
-
pid = fork do
-
read.close
-
yield
-
write.puts [Marshal.dump(self.dup)].pack("m")
-
exit!
-
end
-
-
write.close
-
result = read.read
-
Process.wait2(pid)
-
return result.unpack("m")[0]
-
end
-
end
-
-
1
module Subprocess
-
1
ORIG_ARGV = ARGV.dup unless defined?(ORIG_ARGV)
-
-
# Crazy H4X to get this working in windows / jruby with
-
# no forking.
-
1
def run_in_isolation(&blk)
-
require "tempfile"
-
-
if ENV["ISOLATION_TEST"]
-
yield
-
File.open(ENV["ISOLATION_OUTPUT"], "w") do |file|
-
file.puts [Marshal.dump(self.dup)].pack("m")
-
end
-
exit!
-
else
-
Tempfile.open("isolation") do |tmpfile|
-
ENV["ISOLATION_TEST"] = self.class.name
-
ENV["ISOLATION_OUTPUT"] = tmpfile.path
-
-
load_paths = $-I.map {|p| "-I\"#{File.expand_path(p)}\"" }.join(" ")
-
`#{Gem.ruby} #{load_paths} #{$0} #{ORIG_ARGV.join(" ")}`
-
-
ENV.delete("ISOLATION_TEST")
-
ENV.delete("ISOLATION_OUTPUT")
-
-
return tmpfile.read.unpack("m")[0]
-
end
-
end
-
end
-
end
-
-
1
include forking_env? ? Forking : Subprocess
-
end
-
end
-
end
-
1
require 'active_support/concern'
-
1
require 'active_support/callbacks'
-
-
1
module ActiveSupport
-
1
module Testing
-
# Adds support for +setup+ and +teardown+ callbacks.
-
# These callbacks serve as a replacement to overwriting the
-
# <tt>#setup</tt> and <tt>#teardown</tt> methods of your TestCase.
-
#
-
# class ExampleTest < ActiveSupport::TestCase
-
# setup do
-
# # ...
-
# end
-
#
-
# teardown do
-
# # ...
-
# end
-
# end
-
1
module SetupAndTeardown
-
1
extend ActiveSupport::Concern
-
-
1
included do
-
1
include ActiveSupport::Callbacks
-
1
define_callbacks :setup, :teardown
-
end
-
-
1
module ClassMethods
-
# Add a callback, which runs before <tt>TestCase#setup</tt>.
-
1
def setup(*args, &block)
-
7
set_callback(:setup, :before, *args, &block)
-
end
-
-
# Add a callback, which runs after <tt>TestCase#teardown</tt>.
-
1
def teardown(*args, &block)
-
3
set_callback(:teardown, :after, *args, &block)
-
end
-
end
-
-
1
def before_setup # :nodoc:
-
super
-
run_callbacks :setup
-
end
-
-
1
def after_teardown # :nodoc:
-
run_callbacks :teardown
-
super
-
end
-
end
-
end
-
end
-
1
module ActiveSupport
-
1
module Testing
-
# Logs a "PostsControllerTest: test name" heading before each test to
-
# make test.log easier to search and follow along with.
-
1
module TaggedLogging #:nodoc:
-
1
attr_writer :tagged_logger
-
-
1
def before_setup
-
if tagged_logger
-
heading = "#{self.class}: #{name}"
-
divider = '-' * heading.size
-
tagged_logger.info divider
-
tagged_logger.info heading
-
tagged_logger.info divider
-
end
-
super
-
end
-
-
1
private
-
1
def tagged_logger
-
@tagged_logger ||= (defined?(Rails.logger) && Rails.logger)
-
end
-
end
-
end
-
end
-
1
module ActiveSupport
-
1
module Testing
-
1
class SimpleStubs # :nodoc:
-
1
Stub = Struct.new(:object, :method_name, :original_method)
-
-
1
def initialize
-
@stubs = {}
-
end
-
-
1
def stub_object(object, method_name, return_value)
-
key = [object.object_id, method_name]
-
-
if stub = @stubs[key]
-
unstub_object(stub)
-
end
-
-
new_name = "__simple_stub__#{method_name}"
-
-
@stubs[key] = Stub.new(object, method_name, new_name)
-
-
object.singleton_class.send :alias_method, new_name, method_name
-
object.define_singleton_method(method_name) { return_value }
-
end
-
-
1
def unstub_all!
-
@stubs.each_value do |stub|
-
unstub_object(stub)
-
end
-
@stubs = {}
-
end
-
-
1
private
-
-
1
def unstub_object(stub)
-
singleton_class = stub.object.singleton_class
-
singleton_class.send :undef_method, stub.method_name
-
singleton_class.send :alias_method, stub.method_name, stub.original_method
-
singleton_class.send :undef_method, stub.original_method
-
end
-
end
-
-
# Containing helpers that helps you test passage of time.
-
1
module TimeHelpers
-
# Changes current time to the time in the future or in the past by a given time difference by
-
# stubbing +Time.now+ and +Date.today+.
-
#
-
# Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
-
# travel 1.day
-
# Time.current # => Sun, 10 Nov 2013 15:34:49 EST -05:00
-
# Date.current # => Sun, 10 Nov 2013
-
#
-
# This method also accepts a block, which will return the current time back to its original
-
# state at the end of the block:
-
#
-
# Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
-
# travel 1.day do
-
# User.create.created_at # => Sun, 10 Nov 2013 15:34:49 EST -05:00
-
# end
-
# Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
-
1
def travel(duration, &block)
-
travel_to Time.now + duration, &block
-
end
-
-
# Changes current time to the given time by stubbing +Time.now+ and
-
# +Date.today+ to return the time or date passed into this method.
-
#
-
# Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
-
# travel_to Time.new(2004, 11, 24, 01, 04, 44)
-
# Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00
-
# Date.current # => Wed, 24 Nov 2004
-
#
-
# Dates are taken as their timestamp at the beginning of the day in the
-
# application time zone. <tt>Time.current</tt> returns said timestamp,
-
# and <tt>Time.now</tt> its equivalent in the system time zone. Similarly,
-
# <tt>Date.current</tt> returns a date equal to the argument, and
-
# <tt>Date.today</tt> the date according to <tt>Time.now</tt>, which may
-
# be different. (Note that you rarely want to deal with <tt>Time.now</tt>,
-
# or <tt>Date.today</tt>, in order to honor the application time zone
-
# please always use <tt>Time.current</tt> and <tt>Date.current</tt>.)
-
#
-
# This method also accepts a block, which will return the current time back to its original
-
# state at the end of the block:
-
#
-
# Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
-
# travel_to Time.new(2004, 11, 24, 01, 04, 44) do
-
# Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00
-
# end
-
# Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
-
1
def travel_to(date_or_time, &block)
-
if date_or_time.is_a?(Date) && !date_or_time.is_a?(DateTime)
-
now = date_or_time.midnight.to_time
-
else
-
now = date_or_time.to_time
-
end
-
-
simple_stubs.stub_object(Time, :now, now)
-
simple_stubs.stub_object(Date, :today, now.to_date)
-
-
if block_given?
-
begin
-
block.call
-
ensure
-
travel_back
-
end
-
end
-
end
-
-
# Returns the current time back to its original state, by removing the stubs added by
-
# `travel` and `travel_to`.
-
#
-
# Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
-
# travel_to Time.new(2004, 11, 24, 01, 04, 44)
-
# Time.current # => Wed, 24 Nov 2004 01:04:44 EST -05:00
-
# travel_back
-
# Time.current # => Sat, 09 Nov 2013 15:34:49 EST -05:00
-
1
def travel_back
-
simple_stubs.unstub_all!
-
end
-
-
1
private
-
-
1
def simple_stubs
-
@simple_stubs ||= SimpleStubs.new
-
end
-
end
-
end
-
end
-
1
require 'active_support'
-
-
1
module ActiveSupport
-
1
autoload :Duration, 'active_support/duration'
-
1
autoload :TimeWithZone, 'active_support/time_with_zone'
-
1
autoload :TimeZone, 'active_support/values/time_zone'
-
end
-
-
1
require 'date'
-
1
require 'time'
-
-
1
require 'active_support/core_ext/time'
-
1
require 'active_support/core_ext/date'
-
1
require 'active_support/core_ext/date_time'
-
-
1
require 'active_support/core_ext/integer/time'
-
1
require 'active_support/core_ext/numeric/time'
-
-
1
require 'active_support/core_ext/string/conversions'
-
1
require 'active_support/core_ext/string/zones'
-
1
require 'active_support/values/time_zone'
-
1
require 'active_support/core_ext/object/acts_like'
-
-
1
module ActiveSupport
-
# A Time-like class that can represent a time in any time zone. Necessary
-
# because standard Ruby Time instances are limited to UTC and the
-
# system's <tt>ENV['TZ']</tt> zone.
-
#
-
# You shouldn't ever need to create a TimeWithZone instance directly via +new+.
-
# Instead use methods +local+, +parse+, +at+ and +now+ on TimeZone instances,
-
# and +in_time_zone+ on Time and DateTime instances.
-
#
-
# Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
-
# Time.zone.local(2007, 2, 10, 15, 30, 45) # => Sat, 10 Feb 2007 15:30:45 EST -05:00
-
# Time.zone.parse('2007-02-10 15:30:45') # => Sat, 10 Feb 2007 15:30:45 EST -05:00
-
# Time.zone.at(1170361845) # => Sat, 10 Feb 2007 15:30:45 EST -05:00
-
# Time.zone.now # => Sun, 18 May 2008 13:07:55 EDT -04:00
-
# Time.utc(2007, 2, 10, 20, 30, 45).in_time_zone # => Sat, 10 Feb 2007 15:30:45 EST -05:00
-
#
-
# See Time and TimeZone for further documentation of these methods.
-
#
-
# TimeWithZone instances implement the same API as Ruby Time instances, so
-
# that Time and TimeWithZone instances are interchangeable.
-
#
-
# t = Time.zone.now # => Sun, 18 May 2008 13:27:25 EDT -04:00
-
# t.hour # => 13
-
# t.dst? # => true
-
# t.utc_offset # => -14400
-
# t.zone # => "EDT"
-
# t.to_s(:rfc822) # => "Sun, 18 May 2008 13:27:25 -0400"
-
# t + 1.day # => Mon, 19 May 2008 13:27:25 EDT -04:00
-
# t.beginning_of_year # => Tue, 01 Jan 2008 00:00:00 EST -05:00
-
# t > Time.utc(1999) # => true
-
# t.is_a?(Time) # => true
-
# t.is_a?(ActiveSupport::TimeWithZone) # => true
-
1
class TimeWithZone
-
-
# Report class name as 'Time' to thwart type checking.
-
1
def self.name
-
'Time'
-
end
-
-
1
include Comparable
-
1
attr_reader :time_zone
-
-
1
def initialize(utc_time, time_zone, local_time = nil, period = nil)
-
12
@utc, @time_zone, @time = utc_time, time_zone, local_time
-
12
@period = @utc ? period : get_period_and_ensure_valid_local_time(period)
-
end
-
-
# Returns a Time or DateTime instance that represents the time in +time_zone+.
-
1
def time
-
36
@time ||= period.to_local(@utc)
-
end
-
-
# Returns a Time or DateTime instance that represents the time in UTC.
-
1
def utc
-
12
@utc ||= period.to_utc(@time)
-
end
-
1
alias_method :comparable_time, :utc
-
1
alias_method :getgm, :utc
-
1
alias_method :getutc, :utc
-
1
alias_method :gmtime, :utc
-
-
# Returns the underlying TZInfo::TimezonePeriod.
-
1
def period
-
12
@period ||= time_zone.period_for_utc(@utc)
-
end
-
-
# Returns the simultaneous time in <tt>Time.zone</tt>, or the specified zone.
-
1
def in_time_zone(new_zone = ::Time.zone)
-
return self if time_zone == new_zone
-
utc.in_time_zone(new_zone)
-
end
-
-
# Returns a <tt>Time.local()</tt> instance of the simultaneous time in your
-
# system's <tt>ENV['TZ']</tt> zone.
-
1
def localtime
-
utc.respond_to?(:getlocal) ? utc.getlocal : utc.to_time.getlocal
-
end
-
1
alias_method :getlocal, :localtime
-
-
# Returns true if the current time is within Daylight Savings Time for the
-
# specified time zone.
-
#
-
# Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
-
# Time.zone.parse("2012-5-30").dst? # => true
-
# Time.zone.parse("2012-11-30").dst? # => false
-
1
def dst?
-
period.dst?
-
end
-
1
alias_method :isdst, :dst?
-
-
# Returns true if the current time zone is set to UTC.
-
#
-
# Time.zone = 'UTC' # => 'UTC'
-
# Time.zone.now.utc? # => true
-
# Time.zone = 'Eastern Time (US & Canada)' # => 'Eastern Time (US & Canada)'
-
# Time.zone.now.utc? # => false
-
1
def utc?
-
time_zone.name == 'UTC'
-
end
-
1
alias_method :gmt?, :utc?
-
-
# Returns the offset from current time to UTC time in seconds.
-
1
def utc_offset
-
period.utc_total_offset
-
end
-
1
alias_method :gmt_offset, :utc_offset
-
1
alias_method :gmtoff, :utc_offset
-
-
# Returns a formatted string of the offset from UTC, or an alternative
-
# string if the time zone is already UTC.
-
#
-
# Time.zone = 'Eastern Time (US & Canada)' # => "Eastern Time (US & Canada)"
-
# Time.zone.now.formatted_offset(true) # => "-05:00"
-
# Time.zone.now.formatted_offset(false) # => "-0500"
-
# Time.zone = 'UTC' # => "UTC"
-
# Time.zone.now.formatted_offset(true, "0") # => "0"
-
1
def formatted_offset(colon = true, alternate_utc_string = nil)
-
utc? && alternate_utc_string || TimeZone.seconds_to_utc_offset(utc_offset, colon)
-
end
-
-
# Time uses +zone+ to display the time zone abbreviation, so we're
-
# duck-typing it.
-
1
def zone
-
period.zone_identifier.to_s
-
end
-
-
1
def inspect
-
"#{time.strftime('%a, %d %b %Y %H:%M:%S')} #{zone} #{formatted_offset}"
-
end
-
-
1
def xmlschema(fraction_digits = 0)
-
fraction = if fraction_digits.to_i > 0
-
(".%06i" % time.usec)[0, fraction_digits.to_i + 1]
-
end
-
-
"#{time.strftime("%Y-%m-%dT%H:%M:%S")}#{fraction}#{formatted_offset(true, 'Z')}"
-
end
-
1
alias_method :iso8601, :xmlschema
-
-
# Coerces time to a string for JSON encoding. The default format is ISO 8601.
-
# You can get %Y/%m/%d %H:%M:%S +offset style by setting
-
# <tt>ActiveSupport::JSON::Encoding.use_standard_json_time_format</tt>
-
# to +false+.
-
#
-
# # With ActiveSupport::JSON::Encoding.use_standard_json_time_format = true
-
# Time.utc(2005,2,1,15,15,10).in_time_zone("Hawaii").to_json
-
# # => "2005-02-01T05:15:10.000-10:00"
-
#
-
# # With ActiveSupport::JSON::Encoding.use_standard_json_time_format = false
-
# Time.utc(2005,2,1,15,15,10).in_time_zone("Hawaii").to_json
-
# # => "2005/02/01 05:15:10 -1000"
-
1
def as_json(options = nil)
-
if ActiveSupport::JSON::Encoding.use_standard_json_time_format
-
xmlschema(ActiveSupport::JSON::Encoding.time_precision)
-
else
-
%(#{time.strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)})
-
end
-
end
-
-
1
def encode_with(coder)
-
if coder.respond_to?(:represent_object)
-
coder.represent_object(nil, utc)
-
else
-
coder.represent_scalar(nil, utc.strftime("%Y-%m-%d %H:%M:%S.%9NZ"))
-
end
-
end
-
-
# Returns a string of the object's date and time in the format used by
-
# HTTP requests.
-
#
-
# Time.zone.now.httpdate # => "Tue, 01 Jan 2013 04:39:43 GMT"
-
1
def httpdate
-
utc.httpdate
-
end
-
-
# Returns a string of the object's date and time in the RFC 2822 standard
-
# format.
-
#
-
# Time.zone.now.rfc2822 # => "Tue, 01 Jan 2013 04:51:39 +0000"
-
1
def rfc2822
-
to_s(:rfc822)
-
end
-
1
alias_method :rfc822, :rfc2822
-
-
# <tt>:db</tt> format outputs time in UTC; all others output time in local.
-
# Uses TimeWithZone's +strftime+, so <tt>%Z</tt> and <tt>%z</tt> work correctly.
-
1
def to_s(format = :default)
-
if format == :db
-
utc.to_s(format)
-
elsif formatter = ::Time::DATE_FORMATS[format]
-
formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter)
-
else
-
"#{time.strftime("%Y-%m-%d %H:%M:%S")} #{formatted_offset(false, 'UTC')}" # mimicking Ruby 1.9 Time#to_s format
-
end
-
end
-
1
alias_method :to_formatted_s, :to_s
-
-
# Replaces <tt>%Z</tt> and <tt>%z</tt> directives with +zone+ and
-
# +formatted_offset+, respectively, before passing to Time#strftime, so
-
# that zone information is correct
-
1
def strftime(format)
-
format = format.gsub('%Z', zone)
-
.gsub('%z', formatted_offset(false))
-
.gsub('%:z', formatted_offset(true))
-
.gsub('%::z', formatted_offset(true) + ":00")
-
time.strftime(format)
-
end
-
-
# Use the time in UTC for comparisons.
-
1
def <=>(other)
-
utc <=> other
-
end
-
-
# Returns true if the current object's time is within the specified
-
# +min+ and +max+ time.
-
1
def between?(min, max)
-
utc.between?(min, max)
-
end
-
-
# Returns true if the current object's time is in the past.
-
1
def past?
-
utc.past?
-
end
-
-
# Returns true if the current object's time falls within
-
# the current day.
-
1
def today?
-
time.today?
-
end
-
-
# Returns true if the current object's time is in the future.
-
1
def future?
-
utc.future?
-
end
-
-
1
def eql?(other)
-
utc.eql?(other)
-
end
-
-
1
def hash
-
utc.hash
-
end
-
-
1
def +(other)
-
# If we're adding a Duration of variable length (i.e., years, months, days), move forward from #time,
-
# otherwise move forward from #utc, for accuracy when moving across DST boundaries
-
if duration_of_variable_length?(other)
-
method_missing(:+, other)
-
else
-
result = utc.acts_like?(:date) ? utc.since(other) : utc + other rescue utc.since(other)
-
result.in_time_zone(time_zone)
-
end
-
end
-
-
1
def -(other)
-
# If we're subtracting a Duration of variable length (i.e., years, months, days), move backwards from #time,
-
# otherwise move backwards #utc, for accuracy when moving across DST boundaries
-
if other.acts_like?(:time)
-
utc.to_f - other.to_f
-
elsif duration_of_variable_length?(other)
-
method_missing(:-, other)
-
else
-
result = utc.acts_like?(:date) ? utc.ago(other) : utc - other rescue utc.ago(other)
-
result.in_time_zone(time_zone)
-
end
-
end
-
-
1
def since(other)
-
# If we're adding a Duration of variable length (i.e., years, months, days), move forward from #time,
-
# otherwise move forward from #utc, for accuracy when moving across DST boundaries
-
if duration_of_variable_length?(other)
-
method_missing(:since, other)
-
else
-
utc.since(other).in_time_zone(time_zone)
-
end
-
end
-
-
1
def ago(other)
-
since(-other)
-
end
-
-
1
def advance(options)
-
# If we're advancing a value of variable length (i.e., years, weeks, months, days), advance from #time,
-
# otherwise advance from #utc, for accuracy when moving across DST boundaries
-
if options.values_at(:years, :weeks, :months, :days).any?
-
method_missing(:advance, options)
-
else
-
utc.advance(options).in_time_zone(time_zone)
-
end
-
end
-
-
1
%w(year mon month day mday wday yday hour min sec usec nsec to_date).each do |method_name|
-
13
class_eval <<-EOV, __FILE__, __LINE__ + 1
-
def #{method_name} # def month
-
time.#{method_name} # time.month
-
end # end
-
EOV
-
end
-
-
1
def to_a
-
[time.sec, time.min, time.hour, time.day, time.mon, time.year, time.wday, time.yday, dst?, zone]
-
end
-
-
1
def to_f
-
utc.to_f
-
end
-
-
1
def to_i
-
utc.to_i
-
end
-
1
alias_method :tv_sec, :to_i
-
-
1
def to_r
-
utc.to_r
-
end
-
-
# Return an instance of Time in the system timezone.
-
1
def to_time
-
utc.to_time
-
end
-
-
1
def to_datetime
-
utc.to_datetime.new_offset(Rational(utc_offset, 86_400))
-
end
-
-
# So that +self+ <tt>acts_like?(:time)</tt>.
-
1
def acts_like_time?
-
true
-
end
-
-
# Say we're a Time to thwart type checking.
-
1
def is_a?(klass)
-
36
klass == ::Time || super
-
end
-
1
alias_method :kind_of?, :is_a?
-
-
1
def freeze
-
period; utc; time # preload instance variables before freezing
-
super
-
end
-
-
1
def marshal_dump
-
[utc, time_zone.name, time]
-
end
-
-
1
def marshal_load(variables)
-
initialize(variables[0].utc, ::Time.find_zone(variables[1]), variables[2].utc)
-
end
-
-
# Ensure proxy class responds to all methods that underlying time instance
-
# responds to.
-
1
def respond_to_missing?(sym, include_priv)
-
# consistently respond false to acts_like?(:date), regardless of whether #time is a Time or DateTime
-
12
return false if sym.to_sym == :acts_like_date?
-
12
time.respond_to?(sym, include_priv)
-
end
-
-
# Send the missing method to +time+ instance, and wrap result in a new
-
# TimeWithZone with the existing +time_zone+.
-
1
def method_missing(sym, *args, &block)
-
wrap_with_time_zone time.__send__(sym, *args, &block)
-
rescue NoMethodError => e
-
raise e, e.message.sub(time.inspect, self.inspect), e.backtrace
-
end
-
-
1
private
-
1
def get_period_and_ensure_valid_local_time(period)
-
# we don't want a Time.local instance enforcing its own DST rules as well,
-
# so transfer time values to a utc constructor if necessary
-
@time = transfer_time_values_to_utc_constructor(@time) unless @time.utc?
-
begin
-
period || @time_zone.period_for_local(@time)
-
rescue ::TZInfo::PeriodNotFound
-
# time is in the "spring forward" hour gap, so we're moving the time forward one hour and trying again
-
@time += 1.hour
-
retry
-
end
-
end
-
-
1
def transfer_time_values_to_utc_constructor(time)
-
::Time.utc(time.year, time.month, time.day, time.hour, time.min, time.sec, Rational(time.nsec, 1000))
-
end
-
-
1
def duration_of_variable_length?(obj)
-
ActiveSupport::Duration === obj && obj.parts.any? {|p| [:years, :months, :days].include?(p[0]) }
-
end
-
-
1
def wrap_with_time_zone(time)
-
if time.acts_like?(:time)
-
periods = time_zone.periods_for_local(time)
-
self.class.new(nil, time_zone, time, periods.include?(period) ? period : nil)
-
elsif time.is_a?(Range)
-
wrap_with_time_zone(time.begin)..wrap_with_time_zone(time.end)
-
else
-
time
-
end
-
end
-
end
-
end
-
1
require 'tzinfo'
-
1
require 'thread_safe'
-
1
require 'active_support/core_ext/object/blank'
-
1
require 'active_support/core_ext/object/try'
-
-
1
module ActiveSupport
-
# The TimeZone class serves as a wrapper around TZInfo::Timezone instances.
-
# It allows us to do the following:
-
#
-
# * Limit the set of zones provided by TZInfo to a meaningful subset of 146
-
# zones.
-
# * Retrieve and display zones with a friendlier name
-
# (e.g., "Eastern Time (US & Canada)" instead of "America/New_York").
-
# * Lazily load TZInfo::Timezone instances only when they're needed.
-
# * Create ActiveSupport::TimeWithZone instances via TimeZone's +local+,
-
# +parse+, +at+ and +now+ methods.
-
#
-
# If you set <tt>config.time_zone</tt> in the Rails Application, you can
-
# access this TimeZone object via <tt>Time.zone</tt>:
-
#
-
# # application.rb:
-
# class Application < Rails::Application
-
# config.time_zone = 'Eastern Time (US & Canada)'
-
# end
-
#
-
# Time.zone # => #<TimeZone:0x514834...>
-
# Time.zone.name # => "Eastern Time (US & Canada)"
-
# Time.zone.now # => Sun, 18 May 2008 14:30:44 EDT -04:00
-
#
-
# The version of TZInfo bundled with Active Support only includes the
-
# definitions necessary to support the zones defined by the TimeZone class.
-
# If you need to use zones that aren't defined by TimeZone, you'll need to
-
# install the TZInfo gem (if a recent version of the gem is installed locally,
-
# this will be used instead of the bundled version.)
-
1
class TimeZone
-
# Keys are Rails TimeZone names, values are TZInfo identifiers.
-
1
MAPPING = {
-
"International Date Line West" => "Pacific/Midway",
-
"Midway Island" => "Pacific/Midway",
-
"American Samoa" => "Pacific/Pago_Pago",
-
"Hawaii" => "Pacific/Honolulu",
-
"Alaska" => "America/Juneau",
-
"Pacific Time (US & Canada)" => "America/Los_Angeles",
-
"Tijuana" => "America/Tijuana",
-
"Mountain Time (US & Canada)" => "America/Denver",
-
"Arizona" => "America/Phoenix",
-
"Chihuahua" => "America/Chihuahua",
-
"Mazatlan" => "America/Mazatlan",
-
"Central Time (US & Canada)" => "America/Chicago",
-
"Saskatchewan" => "America/Regina",
-
"Guadalajara" => "America/Mexico_City",
-
"Mexico City" => "America/Mexico_City",
-
"Monterrey" => "America/Monterrey",
-
"Central America" => "America/Guatemala",
-
"Eastern Time (US & Canada)" => "America/New_York",
-
"Indiana (East)" => "America/Indiana/Indianapolis",
-
"Bogota" => "America/Bogota",
-
"Lima" => "America/Lima",
-
"Quito" => "America/Lima",
-
"Atlantic Time (Canada)" => "America/Halifax",
-
"Caracas" => "America/Caracas",
-
"La Paz" => "America/La_Paz",
-
"Santiago" => "America/Santiago",
-
"Newfoundland" => "America/St_Johns",
-
"Brasilia" => "America/Sao_Paulo",
-
"Buenos Aires" => "America/Argentina/Buenos_Aires",
-
"Montevideo" => "America/Montevideo",
-
"Georgetown" => "America/Guyana",
-
"Greenland" => "America/Godthab",
-
"Mid-Atlantic" => "Atlantic/South_Georgia",
-
"Azores" => "Atlantic/Azores",
-
"Cape Verde Is." => "Atlantic/Cape_Verde",
-
"Dublin" => "Europe/Dublin",
-
"Edinburgh" => "Europe/London",
-
"Lisbon" => "Europe/Lisbon",
-
"London" => "Europe/London",
-
"Casablanca" => "Africa/Casablanca",
-
"Monrovia" => "Africa/Monrovia",
-
"UTC" => "Etc/UTC",
-
"Belgrade" => "Europe/Belgrade",
-
"Bratislava" => "Europe/Bratislava",
-
"Budapest" => "Europe/Budapest",
-
"Ljubljana" => "Europe/Ljubljana",
-
"Prague" => "Europe/Prague",
-
"Sarajevo" => "Europe/Sarajevo",
-
"Skopje" => "Europe/Skopje",
-
"Warsaw" => "Europe/Warsaw",
-
"Zagreb" => "Europe/Zagreb",
-
"Brussels" => "Europe/Brussels",
-
"Copenhagen" => "Europe/Copenhagen",
-
"Madrid" => "Europe/Madrid",
-
"Paris" => "Europe/Paris",
-
"Amsterdam" => "Europe/Amsterdam",
-
"Berlin" => "Europe/Berlin",
-
"Bern" => "Europe/Berlin",
-
"Rome" => "Europe/Rome",
-
"Stockholm" => "Europe/Stockholm",
-
"Vienna" => "Europe/Vienna",
-
"West Central Africa" => "Africa/Algiers",
-
"Bucharest" => "Europe/Bucharest",
-
"Cairo" => "Africa/Cairo",
-
"Helsinki" => "Europe/Helsinki",
-
"Kyiv" => "Europe/Kiev",
-
"Riga" => "Europe/Riga",
-
"Sofia" => "Europe/Sofia",
-
"Tallinn" => "Europe/Tallinn",
-
"Vilnius" => "Europe/Vilnius",
-
"Athens" => "Europe/Athens",
-
"Istanbul" => "Europe/Istanbul",
-
"Minsk" => "Europe/Minsk",
-
"Jerusalem" => "Asia/Jerusalem",
-
"Harare" => "Africa/Harare",
-
"Pretoria" => "Africa/Johannesburg",
-
"Moscow" => "Europe/Moscow",
-
"St. Petersburg" => "Europe/Moscow",
-
"Volgograd" => "Europe/Moscow",
-
"Kuwait" => "Asia/Kuwait",
-
"Riyadh" => "Asia/Riyadh",
-
"Nairobi" => "Africa/Nairobi",
-
"Baghdad" => "Asia/Baghdad",
-
"Tehran" => "Asia/Tehran",
-
"Abu Dhabi" => "Asia/Muscat",
-
"Muscat" => "Asia/Muscat",
-
"Baku" => "Asia/Baku",
-
"Tbilisi" => "Asia/Tbilisi",
-
"Yerevan" => "Asia/Yerevan",
-
"Kabul" => "Asia/Kabul",
-
"Ekaterinburg" => "Asia/Yekaterinburg",
-
"Islamabad" => "Asia/Karachi",
-
"Karachi" => "Asia/Karachi",
-
"Tashkent" => "Asia/Tashkent",
-
"Chennai" => "Asia/Kolkata",
-
"Kolkata" => "Asia/Kolkata",
-
"Mumbai" => "Asia/Kolkata",
-
"New Delhi" => "Asia/Kolkata",
-
"Kathmandu" => "Asia/Kathmandu",
-
"Astana" => "Asia/Dhaka",
-
"Dhaka" => "Asia/Dhaka",
-
"Sri Jayawardenepura" => "Asia/Colombo",
-
"Almaty" => "Asia/Almaty",
-
"Novosibirsk" => "Asia/Novosibirsk",
-
"Rangoon" => "Asia/Rangoon",
-
"Bangkok" => "Asia/Bangkok",
-
"Hanoi" => "Asia/Bangkok",
-
"Jakarta" => "Asia/Jakarta",
-
"Krasnoyarsk" => "Asia/Krasnoyarsk",
-
"Beijing" => "Asia/Shanghai",
-
"Chongqing" => "Asia/Chongqing",
-
"Hong Kong" => "Asia/Hong_Kong",
-
"Urumqi" => "Asia/Urumqi",
-
"Kuala Lumpur" => "Asia/Kuala_Lumpur",
-
"Singapore" => "Asia/Singapore",
-
"Taipei" => "Asia/Taipei",
-
"Perth" => "Australia/Perth",
-
"Irkutsk" => "Asia/Irkutsk",
-
"Ulaanbaatar" => "Asia/Ulaanbaatar",
-
"Seoul" => "Asia/Seoul",
-
"Osaka" => "Asia/Tokyo",
-
"Sapporo" => "Asia/Tokyo",
-
"Tokyo" => "Asia/Tokyo",
-
"Yakutsk" => "Asia/Yakutsk",
-
"Darwin" => "Australia/Darwin",
-
"Adelaide" => "Australia/Adelaide",
-
"Canberra" => "Australia/Melbourne",
-
"Melbourne" => "Australia/Melbourne",
-
"Sydney" => "Australia/Sydney",
-
"Brisbane" => "Australia/Brisbane",
-
"Hobart" => "Australia/Hobart",
-
"Vladivostok" => "Asia/Vladivostok",
-
"Guam" => "Pacific/Guam",
-
"Port Moresby" => "Pacific/Port_Moresby",
-
"Magadan" => "Asia/Magadan",
-
"Solomon Is." => "Pacific/Guadalcanal",
-
"New Caledonia" => "Pacific/Noumea",
-
"Fiji" => "Pacific/Fiji",
-
"Kamchatka" => "Asia/Kamchatka",
-
"Marshall Is." => "Pacific/Majuro",
-
"Auckland" => "Pacific/Auckland",
-
"Wellington" => "Pacific/Auckland",
-
"Nuku'alofa" => "Pacific/Tongatapu",
-
"Tokelau Is." => "Pacific/Fakaofo",
-
"Chatham Is." => "Pacific/Chatham",
-
"Samoa" => "Pacific/Apia"
-
}
-
-
1
UTC_OFFSET_WITH_COLON = '%s%02d:%02d'
-
1
UTC_OFFSET_WITHOUT_COLON = UTC_OFFSET_WITH_COLON.sub(':', '')
-
-
1
@lazy_zones_map = ThreadSafe::Cache.new
-
-
# Assumes self represents an offset from UTC in seconds (as returned from
-
# Time#utc_offset) and turns this into an +HH:MM formatted string.
-
#
-
# TimeZone.seconds_to_utc_offset(-21_600) # => "-06:00"
-
1
def self.seconds_to_utc_offset(seconds, colon = true)
-
format = colon ? UTC_OFFSET_WITH_COLON : UTC_OFFSET_WITHOUT_COLON
-
sign = (seconds < 0 ? '-' : '+')
-
hours = seconds.abs / 3600
-
minutes = (seconds.abs % 3600) / 60
-
format % [sign, hours, minutes]
-
end
-
-
1
include Comparable
-
1
attr_reader :name
-
1
attr_reader :tzinfo
-
-
# Create a new TimeZone object with the given name and offset. The
-
# offset is the number of seconds that this time zone is offset from UTC
-
# (GMT). Seconds were chosen as the offset unit because that is the unit
-
# that Ruby uses to represent time zone offsets (see Time#utc_offset).
-
1
def initialize(name, utc_offset = nil, tzinfo = nil)
-
1
@name = name
-
1
@utc_offset = utc_offset
-
1
@tzinfo = tzinfo || TimeZone.find_tzinfo(name)
-
1
@current_period = nil
-
end
-
-
# Returns the offset of this time zone from UTC in seconds.
-
1
def utc_offset
-
1
if @utc_offset
-
@utc_offset
-
else
-
1
@current_period ||= tzinfo.try(:current_period)
-
1
@current_period.try(:utc_offset)
-
end
-
end
-
-
# Returns the offset of this time zone as a formatted string, of the
-
# format "+HH:MM".
-
1
def formatted_offset(colon=true, alternate_utc_string = nil)
-
utc_offset == 0 && alternate_utc_string || self.class.seconds_to_utc_offset(utc_offset, colon)
-
end
-
-
# Compare this time zone to the parameter. The two are compared first on
-
# their offsets, and then by name.
-
1
def <=>(zone)
-
return unless zone.respond_to? :utc_offset
-
result = (utc_offset <=> zone.utc_offset)
-
result = (name <=> zone.name) if result == 0
-
result
-
end
-
-
# Compare #name and TZInfo identifier to a supplied regexp, returning +true+
-
# if a match is found.
-
1
def =~(re)
-
re === name || re === MAPPING[name]
-
end
-
-
# Returns a textual representation of this time zone.
-
1
def to_s
-
"(GMT#{formatted_offset}) #{name}"
-
end
-
-
# Method for creating new ActiveSupport::TimeWithZone instance in time zone
-
# of +self+ from given values.
-
#
-
# Time.zone = 'Hawaii' # => "Hawaii"
-
# Time.zone.local(2007, 2, 1, 15, 30, 45) # => Thu, 01 Feb 2007 15:30:45 HST -10:00
-
1
def local(*args)
-
time = Time.utc(*args)
-
ActiveSupport::TimeWithZone.new(nil, self, time)
-
end
-
-
# Method for creating new ActiveSupport::TimeWithZone instance in time zone
-
# of +self+ from number of seconds since the Unix epoch.
-
#
-
# Time.zone = 'Hawaii' # => "Hawaii"
-
# Time.utc(2000).to_f # => 946684800.0
-
# Time.zone.at(946684800.0) # => Fri, 31 Dec 1999 14:00:00 HST -10:00
-
1
def at(secs)
-
Time.at(secs).utc.in_time_zone(self)
-
end
-
-
# Method for creating new ActiveSupport::TimeWithZone instance in time zone
-
# of +self+ from parsed string.
-
#
-
# Time.zone = 'Hawaii' # => "Hawaii"
-
# Time.zone.parse('1999-12-31 14:00:00') # => Fri, 31 Dec 1999 14:00:00 HST -10:00
-
#
-
# If upper components are missing from the string, they are supplied from
-
# TimeZone#now:
-
#
-
# Time.zone.now # => Fri, 31 Dec 1999 14:00:00 HST -10:00
-
# Time.zone.parse('22:30:00') # => Fri, 31 Dec 1999 22:30:00 HST -10:00
-
1
def parse(str, now=now())
-
parts = Date._parse(str, false)
-
return if parts.empty?
-
-
time = Time.new(
-
parts.fetch(:year, now.year),
-
parts.fetch(:mon, now.month),
-
parts.fetch(:mday, now.day),
-
parts.fetch(:hour, 0),
-
parts.fetch(:min, 0),
-
parts.fetch(:sec, 0) + parts.fetch(:sec_fraction, 0),
-
parts.fetch(:offset, 0)
-
)
-
-
if parts[:offset]
-
TimeWithZone.new(time.utc, self)
-
else
-
TimeWithZone.new(nil, self, time)
-
end
-
end
-
-
# Returns an ActiveSupport::TimeWithZone instance representing the current
-
# time in the time zone represented by +self+.
-
#
-
# Time.zone = 'Hawaii' # => "Hawaii"
-
# Time.zone.now # => Wed, 23 Jan 2008 20:24:27 HST -10:00
-
1
def now
-
time_now.utc.in_time_zone(self)
-
end
-
-
# Return the current date in this time zone.
-
1
def today
-
tzinfo.now.to_date
-
end
-
-
# Returns the next date in this time zone.
-
1
def tomorrow
-
today + 1
-
end
-
-
# Returns the previous date in this time zone.
-
1
def yesterday
-
today - 1
-
end
-
-
# Adjust the given time to the simultaneous time in the time zone
-
# represented by +self+. Returns a Time.utc() instance -- if you want an
-
# ActiveSupport::TimeWithZone instance, use Time#in_time_zone() instead.
-
1
def utc_to_local(time)
-
tzinfo.utc_to_local(time)
-
end
-
-
# Adjust the given time to the simultaneous time in UTC. Returns a
-
# Time.utc() instance.
-
1
def local_to_utc(time, dst=true)
-
tzinfo.local_to_utc(time, dst)
-
end
-
-
# Available so that TimeZone instances respond like TZInfo::Timezone
-
# instances.
-
1
def period_for_utc(time)
-
12
tzinfo.period_for_utc(time)
-
end
-
-
# Available so that TimeZone instances respond like TZInfo::Timezone
-
# instances.
-
1
def period_for_local(time, dst=true)
-
tzinfo.period_for_local(time, dst)
-
end
-
-
1
def periods_for_local(time) #:nodoc:
-
tzinfo.periods_for_local(time)
-
end
-
-
1
def self.find_tzinfo(name)
-
1
TZInfo::TimezoneProxy.new(MAPPING[name] || name)
-
end
-
-
1
class << self
-
1
alias_method :create, :new
-
-
# Returns a TimeZone instance with the given name, or +nil+ if no
-
# such TimeZone instance exists. (This exists to support the use of
-
# this class with the +composed_of+ macro.)
-
1
def new(name)
-
self[name]
-
end
-
-
# Returns an array of all TimeZone objects. There are multiple
-
# TimeZone objects per time zone, in many cases, to make it easier
-
# for users to find their own time zone.
-
1
def all
-
@zones ||= zones_map.values.sort
-
end
-
-
1
def zones_map
-
@zones_map ||= begin
-
MAPPING.each_key {|place| self[place]} # load all the zones
-
@lazy_zones_map
-
end
-
end
-
-
# Locate a specific time zone object. If the argument is a string, it
-
# is interpreted to mean the name of the timezone to locate. If it is a
-
# numeric value it is either the hour offset, or the second offset, of the
-
# timezone to find. (The first one with that offset will be returned.)
-
# Returns +nil+ if no such time zone is known to the system.
-
1
def [](arg)
-
1
case arg
-
when String
-
1
begin
-
2
@lazy_zones_map[arg] ||= create(arg).tap { |tz| tz.utc_offset }
-
rescue TZInfo::InvalidTimezoneIdentifier
-
nil
-
end
-
when Numeric, ActiveSupport::Duration
-
arg *= 3600 if arg.abs <= 13
-
all.find { |z| z.utc_offset == arg.to_i }
-
else
-
raise ArgumentError, "invalid argument to TimeZone[]: #{arg.inspect}"
-
end
-
end
-
-
# A convenience method for returning a collection of TimeZone objects
-
# for time zones in the USA.
-
1
def us_zones
-
@us_zones ||= all.find_all { |z| z.name =~ /US|Arizona|Indiana|Hawaii|Alaska/ }
-
end
-
end
-
-
1
private
-
-
1
def time_now
-
Time.now
-
end
-
end
-
end
-
1
require_relative 'gem_version'
-
-
1
module ActiveSupport
-
# Returns the version of the currently loaded ActiveSupport as a <tt>Gem::Version</tt>
-
1
def self.version
-
gem_version
-
end
-
end
-
1
require 'time'
-
1
require 'base64'
-
1
require 'bigdecimal'
-
1
require 'active_support/core_ext/module/delegation'
-
1
require 'active_support/core_ext/string/inflections'
-
1
require 'active_support/core_ext/date_time/calculations'
-
-
1
module ActiveSupport
-
# = XmlMini
-
#
-
# To use the much faster libxml parser:
-
# gem 'libxml-ruby', '=0.9.7'
-
# XmlMini.backend = 'LibXML'
-
1
module XmlMini
-
1
extend self
-
-
# This module decorates files deserialized using Hash.from_xml with
-
# the <tt>original_filename</tt> and <tt>content_type</tt> methods.
-
1
module FileLike #:nodoc:
-
1
attr_writer :original_filename, :content_type
-
-
1
def original_filename
-
@original_filename || 'untitled'
-
end
-
-
1
def content_type
-
@content_type || 'application/octet-stream'
-
end
-
end
-
-
DEFAULT_ENCODINGS = {
-
"binary" => "base64"
-
1
} unless defined?(DEFAULT_ENCODINGS)
-
-
TYPE_NAMES = {
-
"Symbol" => "symbol",
-
"Fixnum" => "integer",
-
"Bignum" => "integer",
-
"BigDecimal" => "decimal",
-
"Float" => "float",
-
"TrueClass" => "boolean",
-
"FalseClass" => "boolean",
-
"Date" => "date",
-
"DateTime" => "dateTime",
-
"Time" => "dateTime",
-
"Array" => "array",
-
"Hash" => "hash"
-
1
} unless defined?(TYPE_NAMES)
-
-
FORMATTING = {
-
"symbol" => Proc.new { |symbol| symbol.to_s },
-
"date" => Proc.new { |date| date.to_s(:db) },
-
"dateTime" => Proc.new { |time| time.xmlschema },
-
"binary" => Proc.new { |binary| ::Base64.encode64(binary) },
-
"yaml" => Proc.new { |yaml| yaml.to_yaml }
-
1
} unless defined?(FORMATTING)
-
-
# TODO use regexp instead of Date.parse
-
1
unless defined?(PARSING)
-
1
PARSING = {
-
"symbol" => Proc.new { |symbol| symbol.to_s.to_sym },
-
"date" => Proc.new { |date| ::Date.parse(date) },
-
"datetime" => Proc.new { |time| Time.xmlschema(time).utc rescue ::DateTime.parse(time).utc },
-
"integer" => Proc.new { |integer| integer.to_i },
-
"float" => Proc.new { |float| float.to_f },
-
"decimal" => Proc.new { |number| BigDecimal(number) },
-
"boolean" => Proc.new { |boolean| %w(1 true).include?(boolean.to_s.strip) },
-
"string" => Proc.new { |string| string.to_s },
-
"yaml" => Proc.new { |yaml| YAML::load(yaml) rescue yaml },
-
"base64Binary" => Proc.new { |bin| ::Base64.decode64(bin) },
-
"binary" => Proc.new { |bin, entity| _parse_binary(bin, entity) },
-
"file" => Proc.new { |file, entity| _parse_file(file, entity) }
-
}
-
-
1
PARSING.update(
-
"double" => PARSING["float"],
-
"dateTime" => PARSING["datetime"]
-
)
-
end
-
-
1
delegate :parse, :to => :backend
-
-
1
def backend
-
current_thread_backend || @backend
-
end
-
-
1
def backend=(name)
-
1
backend = name && cast_backend_name_to_module(name)
-
1
self.current_thread_backend = backend if current_thread_backend
-
1
@backend = backend
-
end
-
-
1
def with_backend(name)
-
old_backend = current_thread_backend
-
self.current_thread_backend = name && cast_backend_name_to_module(name)
-
yield
-
ensure
-
self.current_thread_backend = old_backend
-
end
-
-
1
def to_tag(key, value, options)
-
type_name = options.delete(:type)
-
merged_options = options.merge(:root => key, :skip_instruct => true)
-
-
if value.is_a?(::Method) || value.is_a?(::Proc)
-
if value.arity == 1
-
value.call(merged_options)
-
else
-
value.call(merged_options, key.to_s.singularize)
-
end
-
elsif value.respond_to?(:to_xml)
-
value.to_xml(merged_options)
-
else
-
type_name ||= TYPE_NAMES[value.class.name]
-
type_name ||= value.class.name if value && !value.respond_to?(:to_str)
-
type_name = type_name.to_s if type_name
-
type_name = "dateTime" if type_name == "datetime"
-
-
key = rename_key(key.to_s, options)
-
-
attributes = options[:skip_types] || type_name.nil? ? { } : { :type => type_name }
-
attributes[:nil] = true if value.nil?
-
-
encoding = options[:encoding] || DEFAULT_ENCODINGS[type_name]
-
attributes[:encoding] = encoding if encoding
-
-
formatted_value = FORMATTING[type_name] && !value.nil? ?
-
FORMATTING[type_name].call(value) : value
-
-
options[:builder].tag!(key, formatted_value, attributes)
-
end
-
end
-
-
1
def rename_key(key, options = {})
-
camelize = options[:camelize]
-
dasherize = !options.has_key?(:dasherize) || options[:dasherize]
-
if camelize
-
key = true == camelize ? key.camelize : key.camelize(camelize)
-
end
-
key = _dasherize(key) if dasherize
-
key
-
end
-
-
1
protected
-
-
1
def _dasherize(key)
-
# $2 must be a non-greedy regex for this to work
-
left, middle, right = /\A(_*)(.*?)(_*)\Z/.match(key.strip)[1,3]
-
"#{left}#{middle.tr('_ ', '--')}#{right}"
-
end
-
-
# TODO: Add support for other encodings
-
1
def _parse_binary(bin, entity) #:nodoc:
-
case entity['encoding']
-
when 'base64'
-
::Base64.decode64(bin)
-
else
-
bin
-
end
-
end
-
-
1
def _parse_file(file, entity)
-
f = StringIO.new(::Base64.decode64(file))
-
f.extend(FileLike)
-
f.original_filename = entity['name']
-
f.content_type = entity['content_type']
-
f
-
end
-
-
1
private
-
-
1
def current_thread_backend
-
1
Thread.current[:xml_mini_backend]
-
end
-
-
1
def current_thread_backend=(name)
-
Thread.current[:xml_mini_backend] = name && cast_backend_name_to_module(name)
-
end
-
-
1
def cast_backend_name_to_module(name)
-
1
if name.is_a?(Module)
-
name
-
else
-
1
require "active_support/xml_mini/#{name.downcase}"
-
1
ActiveSupport.const_get("XmlMini_#{name}")
-
end
-
end
-
end
-
-
1
XmlMini.backend = 'REXML'
-
end
-
1
require 'active_support/core_ext/kernel/reporting'
-
1
require 'active_support/core_ext/object/blank'
-
1
require 'stringio'
-
-
1
module ActiveSupport
-
1
module XmlMini_REXML #:nodoc:
-
1
extend self
-
-
1
CONTENT_KEY = '__content__'.freeze
-
-
# Parse an XML Document string or IO into a simple hash.
-
#
-
# Same as XmlSimple::xml_in but doesn't shoot itself in the foot,
-
# and uses the defaults from Active Support.
-
#
-
# data::
-
# XML Document string or IO to parse
-
1
def parse(data)
-
if !data.respond_to?(:read)
-
data = StringIO.new(data || '')
-
end
-
-
char = data.getc
-
if char.nil?
-
{}
-
else
-
data.ungetc(char)
-
silence_warnings { require 'rexml/document' } unless defined?(REXML::Document)
-
doc = REXML::Document.new(data)
-
-
if doc.root
-
merge_element!({}, doc.root)
-
else
-
raise REXML::ParseException,
-
"The document #{doc.to_s.inspect} does not have a valid root"
-
end
-
end
-
end
-
-
1
private
-
# Convert an XML element and merge into the hash
-
#
-
# hash::
-
# Hash to merge the converted element into.
-
# element::
-
# XML element to merge into hash
-
1
def merge_element!(hash, element)
-
merge!(hash, element.name, collapse(element))
-
end
-
-
# Actually converts an XML document element into a data structure.
-
#
-
# element::
-
# The document element to be collapsed.
-
1
def collapse(element)
-
hash = get_attributes(element)
-
-
if element.has_elements?
-
element.each_element {|child| merge_element!(hash, child) }
-
merge_texts!(hash, element) unless empty_content?(element)
-
hash
-
else
-
merge_texts!(hash, element)
-
end
-
end
-
-
# Merge all the texts of an element into the hash
-
#
-
# hash::
-
# Hash to add the converted element to.
-
# element::
-
# XML element whose texts are to me merged into the hash
-
1
def merge_texts!(hash, element)
-
unless element.has_text?
-
hash
-
else
-
# must use value to prevent double-escaping
-
texts = ''
-
element.texts.each { |t| texts << t.value }
-
merge!(hash, CONTENT_KEY, texts)
-
end
-
end
-
-
# Adds a new key/value pair to an existing Hash. If the key to be added
-
# already exists and the existing value associated with key is not
-
# an Array, it will be wrapped in an Array. Then the new value is
-
# appended to that Array.
-
#
-
# hash::
-
# Hash to add key/value pair to.
-
# key::
-
# Key to be added.
-
# value::
-
# Value to be associated with key.
-
1
def merge!(hash, key, value)
-
if hash.has_key?(key)
-
if hash[key].instance_of?(Array)
-
hash[key] << value
-
else
-
hash[key] = [hash[key], value]
-
end
-
elsif value.instance_of?(Array)
-
hash[key] = [value]
-
else
-
hash[key] = value
-
end
-
hash
-
end
-
-
# Converts the attributes array of an XML element into a hash.
-
# Returns an empty Hash if node has no attributes.
-
#
-
# element::
-
# XML element to extract attributes from.
-
1
def get_attributes(element)
-
attributes = {}
-
element.attributes.each { |n,v| attributes[n] = v }
-
attributes
-
end
-
-
# Determines if a document element has text content
-
#
-
# element::
-
# XML element to be checked.
-
1
def empty_content?(element)
-
element.texts.join.blank?
-
end
-
end
-
end
-
# encoding:utf-8
-
#--
-
# Copyright (C) 2006-2013 Bob Aman
-
#
-
# Licensed under the Apache License, Version 2.0 (the "License");
-
# you may not use this file except in compliance with the License.
-
# You may obtain a copy of the License at
-
#
-
# http://www.apache.org/licenses/LICENSE-2.0
-
#
-
# Unless required by applicable law or agreed to in writing, software
-
# distributed under the License is distributed on an "AS IS" BASIS,
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-
# See the License for the specific language governing permissions and
-
# limitations under the License.
-
#++
-
-
-
1
begin
-
1
require "addressable/idna/native"
-
rescue LoadError
-
# libidn or the idn gem was not available, fall back on a pure-Ruby
-
# implementation...
-
1
require "addressable/idna/pure"
-
end
-
# encoding:utf-8
-
#--
-
# Copyright (C) 2006-2013 Bob Aman
-
#
-
# Licensed under the Apache License, Version 2.0 (the "License");
-
# you may not use this file except in compliance with the License.
-
# You may obtain a copy of the License at
-
#
-
# http://www.apache.org/licenses/LICENSE-2.0
-
#
-
# Unless required by applicable law or agreed to in writing, software
-
# distributed under the License is distributed on an "AS IS" BASIS,
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-
# See the License for the specific language governing permissions and
-
# limitations under the License.
-
#++
-
-
-
1
require "idn"
-
-
module Addressable
-
module IDNA
-
def self.punycode_encode(value)
-
IDN::Punycode.encode(value)
-
end
-
-
def self.punycode_decode(value)
-
IDN::Punycode.decode(value)
-
end
-
-
def self.unicode_normalize_kc(value)
-
IDN::Stringprep.nfkc_normalize(value)
-
end
-
-
def self.to_ascii(value)
-
IDN::Idna.toASCII(value)
-
end
-
-
def self.to_unicode(value)
-
IDN::Idna.toUnicode(value)
-
end
-
end
-
end
-
# encoding:utf-8
-
#--
-
# Copyright (C) 2006-2013 Bob Aman
-
#
-
# Licensed under the Apache License, Version 2.0 (the "License");
-
# you may not use this file except in compliance with the License.
-
# You may obtain a copy of the License at
-
#
-
# http://www.apache.org/licenses/LICENSE-2.0
-
#
-
# Unless required by applicable law or agreed to in writing, software
-
# distributed under the License is distributed on an "AS IS" BASIS,
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-
# See the License for the specific language governing permissions and
-
# limitations under the License.
-
#++
-
-
-
1
module Addressable
-
1
module IDNA
-
# This module is loosely based on idn_actionmailer by Mick Staugaard,
-
# the unicode library by Yoshida Masato, and the punycode implementation
-
# by Kazuhiro Nishiyama. Most of the code was copied verbatim, but
-
# some reformatting was done, and some translation from C was done.
-
#
-
# Without their code to work from as a base, we'd all still be relying
-
# on the presence of libidn. Which nobody ever seems to have installed.
-
#
-
# Original sources:
-
# http://github.com/staugaard/idn_actionmailer
-
# http://www.yoshidam.net/Ruby.html#unicode
-
# http://rubyforge.org/frs/?group_id=2550
-
-
-
1
UNICODE_TABLE = File.expand_path(
-
File.join(File.dirname(__FILE__), '../../..', 'data/unicode.data')
-
)
-
-
1
ACE_PREFIX = "xn--"
-
-
1
UTF8_REGEX = /\A(?:
-
[\x09\x0A\x0D\x20-\x7E] # ASCII
-
| [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
-
| \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
-
| [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
-
| \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
-
| \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
-
| [\xF1-\xF3][\x80-\xBF]{3} # planes 4nil5
-
| \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
-
)*\z/mnx
-
-
1
UTF8_REGEX_MULTIBYTE = /(?:
-
[\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
-
| \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
-
| [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
-
| \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
-
| \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
-
| [\xF1-\xF3][\x80-\xBF]{3} # planes 4nil5
-
| \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
-
)/mnx
-
-
# :startdoc:
-
-
# Converts from a Unicode internationalized domain name to an ASCII
-
# domain name as described in RFC 3490.
-
1
def self.to_ascii(input)
-
input = input.dup
-
if input.respond_to?(:force_encoding)
-
input.force_encoding(Encoding::ASCII_8BIT)
-
end
-
if input =~ UTF8_REGEX && input =~ UTF8_REGEX_MULTIBYTE
-
parts = unicode_downcase(input).split('.')
-
parts.map! do |part|
-
if part.respond_to?(:force_encoding)
-
part.force_encoding(Encoding::ASCII_8BIT)
-
end
-
if part =~ UTF8_REGEX && part =~ UTF8_REGEX_MULTIBYTE
-
ACE_PREFIX + punycode_encode(unicode_normalize_kc(part))
-
else
-
part
-
end
-
end
-
parts.join('.')
-
else
-
input
-
end
-
end
-
-
# Converts from an ASCII domain name to a Unicode internationalized
-
# domain name as described in RFC 3490.
-
1
def self.to_unicode(input)
-
parts = input.split('.')
-
parts.map! do |part|
-
if part =~ /^#{ACE_PREFIX}/
-
punycode_decode(part[/^#{ACE_PREFIX}(.+)/, 1])
-
else
-
part
-
end
-
end
-
output = parts.join('.')
-
if output.respond_to?(:force_encoding)
-
output.force_encoding(Encoding::UTF_8)
-
end
-
output
-
end
-
-
# Unicode normalization form KC.
-
1
def self.unicode_normalize_kc(input)
-
input = input.to_s unless input.is_a?(String)
-
unpacked = input.unpack("U*")
-
unpacked =
-
unicode_compose(unicode_sort_canonical(unicode_decompose(unpacked)))
-
return unpacked.pack("U*")
-
end
-
-
##
-
# Unicode aware downcase method.
-
#
-
# @api private
-
# @param [String] input
-
# The input string.
-
# @return [String] The downcased result.
-
1
def self.unicode_downcase(input)
-
unpacked = input.unpack("U*")
-
unpacked.map! { |codepoint| lookup_unicode_lowercase(codepoint) }
-
return unpacked.pack("U*")
-
end
-
2
(class <<self; private :unicode_downcase; end)
-
-
1
def self.unicode_compose(unpacked)
-
unpacked_result = []
-
length = unpacked.length
-
-
return unpacked if length == 0
-
-
starter = unpacked[0]
-
starter_cc = lookup_unicode_combining_class(starter)
-
starter_cc = 256 if starter_cc != 0
-
for i in 1...length
-
ch = unpacked[i]
-
cc = lookup_unicode_combining_class(ch)
-
-
if (starter_cc == 0 &&
-
(composite = unicode_compose_pair(starter, ch)) != nil)
-
starter = composite
-
startercc = lookup_unicode_combining_class(composite)
-
else
-
unpacked_result << starter
-
starter = ch
-
startercc = cc
-
end
-
end
-
unpacked_result << starter
-
return unpacked_result
-
end
-
2
(class <<self; private :unicode_compose; end)
-
-
1
def self.unicode_compose_pair(ch_one, ch_two)
-
if ch_one >= HANGUL_LBASE && ch_one < HANGUL_LBASE + HANGUL_LCOUNT &&
-
ch_two >= HANGUL_VBASE && ch_two < HANGUL_VBASE + HANGUL_VCOUNT
-
# Hangul L + V
-
return HANGUL_SBASE + (
-
(ch_one - HANGUL_LBASE) * HANGUL_VCOUNT + (ch_two - HANGUL_VBASE)
-
) * HANGUL_TCOUNT
-
elsif ch_one >= HANGUL_SBASE &&
-
ch_one < HANGUL_SBASE + HANGUL_SCOUNT &&
-
(ch_one - HANGUL_SBASE) % HANGUL_TCOUNT == 0 &&
-
ch_two >= HANGUL_TBASE && ch_two < HANGUL_TBASE + HANGUL_TCOUNT
-
# Hangul LV + T
-
return ch_one + (ch_two - HANGUL_TBASE)
-
end
-
-
p = []
-
ucs4_to_utf8 = lambda do |ch|
-
# For some reason, rcov likes to drop BUS errors here.
-
if ch < 128
-
p << ch
-
elsif ch < 2048
-
p << (ch >> 6 | 192)
-
p << (ch & 63 | 128)
-
elsif ch < 0x10000
-
p << (ch >> 12 | 224)
-
p << (ch >> 6 & 63 | 128)
-
p << (ch & 63 | 128)
-
elsif ch < 0x200000
-
p << (ch >> 18 | 240)
-
p << (ch >> 12 & 63 | 128)
-
p << (ch >> 6 & 63 | 128)
-
p << (ch & 63 | 128)
-
elsif ch < 0x4000000
-
p << (ch >> 24 | 248)
-
p << (ch >> 18 & 63 | 128)
-
p << (ch >> 12 & 63 | 128)
-
p << (ch >> 6 & 63 | 128)
-
p << (ch & 63 | 128)
-
elsif ch < 0x80000000
-
p << (ch >> 30 | 252)
-
p << (ch >> 24 & 63 | 128)
-
p << (ch >> 18 & 63 | 128)
-
p << (ch >> 12 & 63 | 128)
-
p << (ch >> 6 & 63 | 128)
-
p << (ch & 63 | 128)
-
end
-
end
-
-
ucs4_to_utf8.call(ch_one)
-
ucs4_to_utf8.call(ch_two)
-
-
return lookup_unicode_composition(p)
-
end
-
2
(class <<self; private :unicode_compose_pair; end)
-
-
1
def self.unicode_sort_canonical(unpacked)
-
unpacked = unpacked.dup
-
i = 1
-
length = unpacked.length
-
-
return unpacked if length < 2
-
-
while i < length
-
last = unpacked[i-1]
-
ch = unpacked[i]
-
last_cc = lookup_unicode_combining_class(last)
-
cc = lookup_unicode_combining_class(ch)
-
if cc != 0 && last_cc != 0 && last_cc > cc
-
unpacked[i] = last
-
unpacked[i-1] = ch
-
i -= 1 if i > 1
-
else
-
i += 1
-
end
-
end
-
return unpacked
-
end
-
2
(class <<self; private :unicode_sort_canonical; end)
-
-
1
def self.unicode_decompose(unpacked)
-
unpacked_result = []
-
for cp in unpacked
-
if cp >= HANGUL_SBASE && cp < HANGUL_SBASE + HANGUL_SCOUNT
-
l, v, t = unicode_decompose_hangul(cp)
-
unpacked_result << l
-
unpacked_result << v if v
-
unpacked_result << t if t
-
else
-
dc = lookup_unicode_compatibility(cp)
-
unless dc
-
unpacked_result << cp
-
else
-
unpacked_result.concat(unicode_decompose(dc.unpack("U*")))
-
end
-
end
-
end
-
return unpacked_result
-
end
-
2
(class <<self; private :unicode_decompose; end)
-
-
1
def self.unicode_decompose_hangul(codepoint)
-
sindex = codepoint - HANGUL_SBASE;
-
if sindex < 0 || sindex >= HANGUL_SCOUNT
-
l = codepoint
-
v = t = nil
-
return l, v, t
-
end
-
l = HANGUL_LBASE + sindex / HANGUL_NCOUNT
-
v = HANGUL_VBASE + (sindex % HANGUL_NCOUNT) / HANGUL_TCOUNT
-
t = HANGUL_TBASE + sindex % HANGUL_TCOUNT
-
if t == HANGUL_TBASE
-
t = nil
-
end
-
return l, v, t
-
end
-
2
(class <<self; private :unicode_decompose_hangul; end)
-
-
1
def self.lookup_unicode_combining_class(codepoint)
-
codepoint_data = UNICODE_DATA[codepoint]
-
(codepoint_data ?
-
(codepoint_data[UNICODE_DATA_COMBINING_CLASS] || 0) :
-
0)
-
end
-
2
(class <<self; private :lookup_unicode_combining_class; end)
-
-
1
def self.lookup_unicode_compatibility(codepoint)
-
codepoint_data = UNICODE_DATA[codepoint]
-
(codepoint_data ?
-
codepoint_data[UNICODE_DATA_COMPATIBILITY] : nil)
-
end
-
2
(class <<self; private :lookup_unicode_compatibility; end)
-
-
1
def self.lookup_unicode_lowercase(codepoint)
-
codepoint_data = UNICODE_DATA[codepoint]
-
(codepoint_data ?
-
(codepoint_data[UNICODE_DATA_LOWERCASE] || codepoint) :
-
codepoint)
-
end
-
2
(class <<self; private :lookup_unicode_lowercase; end)
-
-
1
def self.lookup_unicode_composition(unpacked)
-
return COMPOSITION_TABLE[unpacked]
-
end
-
2
(class <<self; private :lookup_unicode_composition; end)
-
-
1
HANGUL_SBASE = 0xac00
-
1
HANGUL_LBASE = 0x1100
-
1
HANGUL_LCOUNT = 19
-
1
HANGUL_VBASE = 0x1161
-
1
HANGUL_VCOUNT = 21
-
1
HANGUL_TBASE = 0x11a7
-
1
HANGUL_TCOUNT = 28
-
1
HANGUL_NCOUNT = HANGUL_VCOUNT * HANGUL_TCOUNT # 588
-
1
HANGUL_SCOUNT = HANGUL_LCOUNT * HANGUL_NCOUNT # 11172
-
-
1
UNICODE_DATA_COMBINING_CLASS = 0
-
1
UNICODE_DATA_EXCLUSION = 1
-
1
UNICODE_DATA_CANONICAL = 2
-
1
UNICODE_DATA_COMPATIBILITY = 3
-
1
UNICODE_DATA_UPPERCASE = 4
-
1
UNICODE_DATA_LOWERCASE = 5
-
1
UNICODE_DATA_TITLECASE = 6
-
-
1
begin
-
1
if defined?(FakeFS)
-
fakefs_state = FakeFS.activated?
-
FakeFS.deactivate!
-
end
-
# This is a sparse Unicode table. Codepoints without entries are
-
# assumed to have the value: [0, 0, nil, nil, nil, nil, nil]
-
1
UNICODE_DATA = File.open(UNICODE_TABLE, "rb") do |file|
-
1
Marshal.load(file.read)
-
end
-
ensure
-
1
if defined?(FakeFS)
-
FakeFS.activate! if fakefs_state
-
end
-
end
-
-
1
COMPOSITION_TABLE = {}
-
1
for codepoint, data in UNICODE_DATA
-
4233
canonical = data[UNICODE_DATA_CANONICAL]
-
4233
exclusion = data[UNICODE_DATA_EXCLUSION]
-
-
4233
if canonical && exclusion == 0
-
918
COMPOSITION_TABLE[canonical.unpack("C*")] = codepoint
-
end
-
end
-
-
1
UNICODE_MAX_LENGTH = 256
-
1
ACE_MAX_LENGTH = 256
-
-
1
PUNYCODE_BASE = 36
-
1
PUNYCODE_TMIN = 1
-
1
PUNYCODE_TMAX = 26
-
1
PUNYCODE_SKEW = 38
-
1
PUNYCODE_DAMP = 700
-
1
PUNYCODE_INITIAL_BIAS = 72
-
1
PUNYCODE_INITIAL_N = 0x80
-
1
PUNYCODE_DELIMITER = 0x2D
-
-
1
PUNYCODE_MAXINT = 1 << 64
-
-
1
PUNYCODE_PRINT_ASCII =
-
"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" +
-
"\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" +
-
" !\"\#$%&'()*+,-./" +
-
"0123456789:;<=>?" +
-
"@ABCDEFGHIJKLMNO" +
-
"PQRSTUVWXYZ[\\]^_" +
-
"`abcdefghijklmno" +
-
"pqrstuvwxyz{|}~\n"
-
-
# Input is invalid.
-
1
class PunycodeBadInput < StandardError; end
-
# Output would exceed the space provided.
-
1
class PunycodeBigOutput < StandardError; end
-
# Input needs wider integers to process.
-
1
class PunycodeOverflow < StandardError; end
-
-
1
def self.punycode_encode(unicode)
-
input = unicode.unpack("U*")
-
output = [0] * (ACE_MAX_LENGTH + 1)
-
input_length = input.size
-
output_length = [ACE_MAX_LENGTH]
-
-
# Initialize the state
-
n = PUNYCODE_INITIAL_N
-
delta = out = 0
-
max_out = output_length[0]
-
bias = PUNYCODE_INITIAL_BIAS
-
-
# Handle the basic code points:
-
input_length.times do |j|
-
if punycode_basic?(input[j])
-
if max_out - out < 2
-
raise PunycodeBigOutput,
-
"Output would exceed the space provided."
-
end
-
output[out] = input[j]
-
out += 1
-
end
-
end
-
-
h = b = out
-
-
# h is the number of code points that have been handled, b is the
-
# number of basic code points, and out is the number of characters
-
# that have been output.
-
-
if b > 0
-
output[out] = PUNYCODE_DELIMITER
-
out += 1
-
end
-
-
# Main encoding loop:
-
-
while h < input_length
-
# All non-basic code points < n have been
-
# handled already. Find the next larger one:
-
-
m = PUNYCODE_MAXINT
-
input_length.times do |j|
-
m = input[j] if (n...m) === input[j]
-
end
-
-
# Increase delta enough to advance the decoder's
-
# <n,i> state to <m,0>, but guard against overflow:
-
-
if m - n > (PUNYCODE_MAXINT - delta) / (h + 1)
-
raise PunycodeOverflow, "Input needs wider integers to process."
-
end
-
delta += (m - n) * (h + 1)
-
n = m
-
-
input_length.times do |j|
-
# Punycode does not need to check whether input[j] is basic:
-
if input[j] < n
-
delta += 1
-
if delta == 0
-
raise PunycodeOverflow,
-
"Input needs wider integers to process."
-
end
-
end
-
-
if input[j] == n
-
# Represent delta as a generalized variable-length integer:
-
-
q = delta; k = PUNYCODE_BASE
-
while true
-
if out >= max_out
-
raise PunycodeBigOutput,
-
"Output would exceed the space provided."
-
end
-
t = (
-
if k <= bias
-
PUNYCODE_TMIN
-
elsif k >= bias + PUNYCODE_TMAX
-
PUNYCODE_TMAX
-
else
-
k - bias
-
end
-
)
-
break if q < t
-
output[out] =
-
punycode_encode_digit(t + (q - t) % (PUNYCODE_BASE - t))
-
out += 1
-
q = (q - t) / (PUNYCODE_BASE - t)
-
k += PUNYCODE_BASE
-
end
-
-
output[out] = punycode_encode_digit(q)
-
out += 1
-
bias = punycode_adapt(delta, h + 1, h == b)
-
delta = 0
-
h += 1
-
end
-
end
-
-
delta += 1
-
n += 1
-
end
-
-
output_length[0] = out
-
-
outlen = out
-
outlen.times do |j|
-
c = output[j]
-
unless c >= 0 && c <= 127
-
raise Exception, "Invalid output char."
-
end
-
unless PUNYCODE_PRINT_ASCII[c]
-
raise PunycodeBadInput, "Input is invalid."
-
end
-
end
-
-
output[0..outlen].map { |x| x.chr }.join("").sub(/\0+\z/, "")
-
end
-
2
(class <<self; private :punycode_encode; end)
-
-
1
def self.punycode_decode(punycode)
-
input = []
-
output = []
-
-
if ACE_MAX_LENGTH * 2 < punycode.size
-
raise PunycodeBigOutput, "Output would exceed the space provided."
-
end
-
punycode.each_byte do |c|
-
unless c >= 0 && c <= 127
-
raise PunycodeBadInput, "Input is invalid."
-
end
-
input.push(c)
-
end
-
-
input_length = input.length
-
output_length = [UNICODE_MAX_LENGTH]
-
-
# Initialize the state
-
n = PUNYCODE_INITIAL_N
-
-
out = i = 0
-
max_out = output_length[0]
-
bias = PUNYCODE_INITIAL_BIAS
-
-
# Handle the basic code points: Let b be the number of input code
-
# points before the last delimiter, or 0 if there is none, then
-
# copy the first b code points to the output.
-
-
b = 0
-
input_length.times do |j|
-
b = j if punycode_delimiter?(input[j])
-
end
-
if b > max_out
-
raise PunycodeBigOutput, "Output would exceed the space provided."
-
end
-
-
b.times do |j|
-
unless punycode_basic?(input[j])
-
raise PunycodeBadInput, "Input is invalid."
-
end
-
output[out] = input[j]
-
out+=1
-
end
-
-
# Main decoding loop: Start just after the last delimiter if any
-
# basic code points were copied; start at the beginning otherwise.
-
-
in_ = b > 0 ? b + 1 : 0
-
while in_ < input_length
-
-
# in_ is the index of the next character to be consumed, and
-
# out is the number of code points in the output array.
-
-
# Decode a generalized variable-length integer into delta,
-
# which gets added to i. The overflow checking is easier
-
# if we increase i as we go, then subtract off its starting
-
# value at the end to obtain delta.
-
-
oldi = i; w = 1; k = PUNYCODE_BASE
-
while true
-
if in_ >= input_length
-
raise PunycodeBadInput, "Input is invalid."
-
end
-
digit = punycode_decode_digit(input[in_])
-
in_+=1
-
if digit >= PUNYCODE_BASE
-
raise PunycodeBadInput, "Input is invalid."
-
end
-
if digit > (PUNYCODE_MAXINT - i) / w
-
raise PunycodeOverflow, "Input needs wider integers to process."
-
end
-
i += digit * w
-
t = (
-
if k <= bias
-
PUNYCODE_TMIN
-
elsif k >= bias + PUNYCODE_TMAX
-
PUNYCODE_TMAX
-
else
-
k - bias
-
end
-
)
-
break if digit < t
-
if w > PUNYCODE_MAXINT / (PUNYCODE_BASE - t)
-
raise PunycodeOverflow, "Input needs wider integers to process."
-
end
-
w *= PUNYCODE_BASE - t
-
k += PUNYCODE_BASE
-
end
-
-
bias = punycode_adapt(i - oldi, out + 1, oldi == 0)
-
-
# I was supposed to wrap around from out + 1 to 0,
-
# incrementing n each time, so we'll fix that now:
-
-
if i / (out + 1) > PUNYCODE_MAXINT - n
-
raise PunycodeOverflow, "Input needs wider integers to process."
-
end
-
n += i / (out + 1)
-
i %= out + 1
-
-
# Insert n at position i of the output:
-
-
# not needed for Punycode:
-
# raise PUNYCODE_INVALID_INPUT if decode_digit(n) <= base
-
if out >= max_out
-
raise PunycodeBigOutput, "Output would exceed the space provided."
-
end
-
-
#memmove(output + i + 1, output + i, (out - i) * sizeof *output)
-
output[i + 1, out - i] = output[i, out - i]
-
output[i] = n
-
i += 1
-
-
out += 1
-
end
-
-
output_length[0] = out
-
-
output.pack("U*")
-
end
-
2
(class <<self; private :punycode_decode; end)
-
-
1
def self.punycode_basic?(codepoint)
-
codepoint < 0x80
-
end
-
2
(class <<self; private :punycode_basic?; end)
-
-
1
def self.punycode_delimiter?(codepoint)
-
codepoint == PUNYCODE_DELIMITER
-
end
-
2
(class <<self; private :punycode_delimiter?; end)
-
-
1
def self.punycode_encode_digit(d)
-
d + 22 + 75 * ((d < 26) ? 1 : 0)
-
end
-
2
(class <<self; private :punycode_encode_digit; end)
-
-
# Returns the numeric value of a basic codepoint
-
# (for use in representing integers) in the range 0 to
-
# base - 1, or PUNYCODE_BASE if codepoint does not represent a value.
-
1
def self.punycode_decode_digit(codepoint)
-
if codepoint - 48 < 10
-
codepoint - 22
-
elsif codepoint - 65 < 26
-
codepoint - 65
-
elsif codepoint - 97 < 26
-
codepoint - 97
-
else
-
PUNYCODE_BASE
-
end
-
end
-
2
(class <<self; private :punycode_decode_digit; end)
-
-
# Bias adaptation method
-
1
def self.punycode_adapt(delta, numpoints, firsttime)
-
delta = firsttime ? delta / PUNYCODE_DAMP : delta >> 1
-
# delta >> 1 is a faster way of doing delta / 2
-
delta += delta / numpoints
-
difference = PUNYCODE_BASE - PUNYCODE_TMIN
-
-
k = 0
-
while delta > (difference * PUNYCODE_TMAX) / 2
-
delta /= difference
-
k += PUNYCODE_BASE
-
end
-
-
k + (difference + 1) * delta / (delta + PUNYCODE_SKEW)
-
end
-
2
(class <<self; private :punycode_adapt; end)
-
end
-
# :startdoc:
-
end
-
# encoding:utf-8
-
#--
-
# Copyright (C) 2006-2013 Bob Aman
-
#
-
# Licensed under the Apache License, Version 2.0 (the "License");
-
# you may not use this file except in compliance with the License.
-
# You may obtain a copy of the License at
-
#
-
# http://www.apache.org/licenses/LICENSE-2.0
-
#
-
# Unless required by applicable law or agreed to in writing, software
-
# distributed under the License is distributed on an "AS IS" BASIS,
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-
# See the License for the specific language governing permissions and
-
# limitations under the License.
-
#++
-
-
-
1
require "addressable/version"
-
1
require "addressable/uri"
-
-
1
module Addressable
-
##
-
# This is an implementation of a URI template based on
-
# RFC 6570 (http://tools.ietf.org/html/rfc6570).
-
1
class Template
-
# Constants used throughout the template code.
-
1
anything =
-
Addressable::URI::CharacterClasses::RESERVED +
-
Addressable::URI::CharacterClasses::UNRESERVED
-
-
-
1
variable_char_class =
-
Addressable::URI::CharacterClasses::ALPHA +
-
Addressable::URI::CharacterClasses::DIGIT + '_'
-
-
1
var_char =
-
"(?:(?:[#{variable_char_class}]|%[a-fA-F0-9][a-fA-F0-9])+)"
-
1
RESERVED =
-
"(?:[#{anything}]|%[a-fA-F0-9][a-fA-F0-9])"
-
1
UNRESERVED =
-
"(?:[#{
-
Addressable::URI::CharacterClasses::UNRESERVED
-
}]|%[a-fA-F0-9][a-fA-F0-9])"
-
1
variable =
-
"(?:#{var_char}(?:\\.?#{var_char})*)"
-
1
varspec =
-
"(?:(#{variable})(\\*|:\\d+)?)"
-
1
VARNAME =
-
/^#{variable}$/
-
1
VARSPEC =
-
/^#{varspec}$/
-
1
VARIABLE_LIST =
-
/^#{varspec}(?:,#{varspec})*$/
-
1
operator =
-
"+#./;?&=,!@|"
-
1
EXPRESSION =
-
/\{([#{operator}])?(#{varspec}(?:,#{varspec})*)\}/
-
-
-
1
LEADERS = {
-
'?' => '?',
-
'/' => '/',
-
'#' => '#',
-
'.' => '.',
-
';' => ';',
-
'&' => '&'
-
}
-
1
JOINERS = {
-
'?' => '&',
-
'.' => '.',
-
';' => ';',
-
'&' => '&',
-
'/' => '/'
-
}
-
-
##
-
# Raised if an invalid template value is supplied.
-
1
class InvalidTemplateValueError < StandardError
-
end
-
-
##
-
# Raised if an invalid template operator is used in a pattern.
-
1
class InvalidTemplateOperatorError < StandardError
-
end
-
-
##
-
# Raised if an invalid template operator is used in a pattern.
-
1
class TemplateOperatorAbortedError < StandardError
-
end
-
-
##
-
# This class represents the data that is extracted when a Template
-
# is matched against a URI.
-
1
class MatchData
-
##
-
# Creates a new MatchData object.
-
# MatchData objects should never be instantiated directly.
-
#
-
# @param [Addressable::URI] uri
-
# The URI that the template was matched against.
-
1
def initialize(uri, template, mapping)
-
@uri = uri.dup.freeze
-
@template = template
-
@mapping = mapping.dup.freeze
-
end
-
-
##
-
# @return [Addressable::URI]
-
# The URI that the Template was matched against.
-
1
attr_reader :uri
-
-
##
-
# @return [Addressable::Template]
-
# The Template used for the match.
-
1
attr_reader :template
-
-
##
-
# @return [Hash]
-
# The mapping that resulted from the match.
-
# Note that this mapping does not include keys or values for
-
# variables that appear in the Template, but are not present
-
# in the URI.
-
1
attr_reader :mapping
-
-
##
-
# @return [Array]
-
# The list of variables that were present in the Template.
-
# Note that this list will include variables which do not appear
-
# in the mapping because they were not present in URI.
-
1
def variables
-
self.template.variables
-
end
-
1
alias_method :keys, :variables
-
1
alias_method :names, :variables
-
-
##
-
# @return [Array]
-
# The list of values that were captured by the Template.
-
# Note that this list will include nils for any variables which
-
# were in the Template, but did not appear in the URI.
-
1
def values
-
@values ||= self.variables.inject([]) do |accu, key|
-
accu << self.mapping[key]
-
accu
-
end
-
end
-
1
alias_method :captures, :values
-
-
##
-
# Accesses captured values by name or by index.
-
#
-
# @param [String, Symbol, Fixnum] key
-
# Capture index or name. Note that when accessing by with index
-
# of 0, the full URI will be returned. The intention is to mimic
-
# the ::MatchData#[] behavior.
-
#
-
# @param [#to_int, nil] len
-
# If provided, an array of values will be returend with the given
-
# parameter used as length.
-
#
-
# @return [Array, String, nil]
-
# The captured value corresponding to the index or name. If the
-
# value was not provided or the key is unknown, nil will be
-
# returned.
-
#
-
# If the second parameter is provided, an array of that length will
-
# be returned instead.
-
1
def [](key, len = nil)
-
if len
-
to_a[key, len]
-
elsif String === key or Symbol === key
-
mapping[key.to_s]
-
else
-
to_a[key]
-
end
-
end
-
-
##
-
# @return [Array]
-
# Array with the matched URI as first element followed by the captured
-
# values.
-
1
def to_a
-
[to_s, *values]
-
end
-
-
##
-
# @return [String]
-
# The matched URI as String.
-
1
def to_s
-
uri.to_s
-
end
-
1
alias_method :string, :to_s
-
-
# Returns multiple captured values at once.
-
#
-
# @param [String, Symbol, Fixnum] *indexes
-
# Indices of the captures to be returned
-
#
-
# @return [Array]
-
# Values corresponding to given indices.
-
#
-
# @see Addressable::Template::MatchData#[]
-
1
def values_at(*indexes)
-
indexes.map { |i| self[i] }
-
end
-
-
##
-
# Returns a <tt>String</tt> representation of the MatchData's state.
-
#
-
# @return [String] The MatchData's state, as a <tt>String</tt>.
-
1
def inspect
-
sprintf("#<%s:%#0x RESULT:%s>",
-
self.class.to_s, self.object_id, self.mapping.inspect)
-
end
-
-
##
-
# Dummy method for code expecting a ::MatchData instance
-
#
-
# @return [String] An empty string.
-
1
def pre_match
-
""
-
end
-
1
alias_method :post_match, :pre_match
-
end
-
-
##
-
# Creates a new <tt>Addressable::Template</tt> object.
-
#
-
# @param [#to_str] pattern The URI Template pattern.
-
#
-
# @return [Addressable::Template] The initialized Template object.
-
1
def initialize(pattern)
-
if !pattern.respond_to?(:to_str)
-
raise TypeError, "Can't convert #{pattern.class} into String."
-
end
-
@pattern = pattern.to_str.freeze
-
end
-
-
##
-
# @return [String] The Template object's pattern.
-
1
attr_reader :pattern
-
-
##
-
# Returns a <tt>String</tt> representation of the Template object's state.
-
#
-
# @return [String] The Template object's state, as a <tt>String</tt>.
-
1
def inspect
-
sprintf("#<%s:%#0x PATTERN:%s>",
-
self.class.to_s, self.object_id, self.pattern)
-
end
-
-
##
-
# Returns <code>true</code> if the Template objects are equal. This method
-
# does NOT normalize either Template before doing the comparison.
-
#
-
# @param [Object] template The Template to compare.
-
#
-
# @return [TrueClass, FalseClass]
-
# <code>true</code> if the Templates are equivalent, <code>false</code>
-
# otherwise.
-
1
def ==(template)
-
return false unless template.kind_of?(Template)
-
return self.pattern == template.pattern
-
end
-
-
##
-
# Addressable::Template makes no distinction between `==` and `eql?`.
-
#
-
# @see #==
-
1
alias_method :eql?, :==
-
-
##
-
# Extracts a mapping from the URI using a URI Template pattern.
-
#
-
# @param [Addressable::URI, #to_str] uri
-
# The URI to extract from.
-
#
-
# @param [#restore, #match] processor
-
# A template processor object may optionally be supplied.
-
#
-
# The object should respond to either the <tt>restore</tt> or
-
# <tt>match</tt> messages or both. The <tt>restore</tt> method should
-
# take two parameters: `[String] name` and `[String] value`.
-
# The <tt>restore</tt> method should reverse any transformations that
-
# have been performed on the value to ensure a valid URI.
-
# The <tt>match</tt> method should take a single
-
# parameter: `[String] name`. The <tt>match</tt> method should return
-
# a <tt>String</tt> containing a regular expression capture group for
-
# matching on that particular variable. The default value is `".*?"`.
-
# The <tt>match</tt> method has no effect on multivariate operator
-
# expansions.
-
#
-
# @return [Hash, NilClass]
-
# The <tt>Hash</tt> mapping that was extracted from the URI, or
-
# <tt>nil</tt> if the URI didn't match the template.
-
#
-
# @example
-
# class ExampleProcessor
-
# def self.restore(name, value)
-
# return value.gsub(/\+/, " ") if name == "query"
-
# return value
-
# end
-
#
-
# def self.match(name)
-
# return ".*?" if name == "first"
-
# return ".*"
-
# end
-
# end
-
#
-
# uri = Addressable::URI.parse(
-
# "http://example.com/search/an+example+search+query/"
-
# )
-
# Addressable::Template.new(
-
# "http://example.com/search/{query}/"
-
# ).extract(uri, ExampleProcessor)
-
# #=> {"query" => "an example search query"}
-
#
-
# uri = Addressable::URI.parse("http://example.com/a/b/c/")
-
# Addressable::Template.new(
-
# "http://example.com/{first}/{second}/"
-
# ).extract(uri, ExampleProcessor)
-
# #=> {"first" => "a", "second" => "b/c"}
-
#
-
# uri = Addressable::URI.parse("http://example.com/a/b/c/")
-
# Addressable::Template.new(
-
# "http://example.com/{first}/{-list|/|second}/"
-
# ).extract(uri)
-
# #=> {"first" => "a", "second" => ["b", "c"]}
-
1
def extract(uri, processor=nil)
-
match_data = self.match(uri, processor)
-
return (match_data ? match_data.mapping : nil)
-
end
-
-
##
-
# Extracts match data from the URI using a URI Template pattern.
-
#
-
# @param [Addressable::URI, #to_str] uri
-
# The URI to extract from.
-
#
-
# @param [#restore, #match] processor
-
# A template processor object may optionally be supplied.
-
#
-
# The object should respond to either the <tt>restore</tt> or
-
# <tt>match</tt> messages or both. The <tt>restore</tt> method should
-
# take two parameters: `[String] name` and `[String] value`.
-
# The <tt>restore</tt> method should reverse any transformations that
-
# have been performed on the value to ensure a valid URI.
-
# The <tt>match</tt> method should take a single
-
# parameter: `[String] name`. The <tt>match</tt> method should return
-
# a <tt>String</tt> containing a regular expression capture group for
-
# matching on that particular variable. The default value is `".*?"`.
-
# The <tt>match</tt> method has no effect on multivariate operator
-
# expansions.
-
#
-
# @return [Hash, NilClass]
-
# The <tt>Hash</tt> mapping that was extracted from the URI, or
-
# <tt>nil</tt> if the URI didn't match the template.
-
#
-
# @example
-
# class ExampleProcessor
-
# def self.restore(name, value)
-
# return value.gsub(/\+/, " ") if name == "query"
-
# return value
-
# end
-
#
-
# def self.match(name)
-
# return ".*?" if name == "first"
-
# return ".*"
-
# end
-
# end
-
#
-
# uri = Addressable::URI.parse(
-
# "http://example.com/search/an+example+search+query/"
-
# )
-
# match = Addressable::Template.new(
-
# "http://example.com/search/{query}/"
-
# ).match(uri, ExampleProcessor)
-
# match.variables
-
# #=> ["query"]
-
# match.captures
-
# #=> ["an example search query"]
-
#
-
# uri = Addressable::URI.parse("http://example.com/a/b/c/")
-
# match = Addressable::Template.new(
-
# "http://example.com/{first}/{+second}/"
-
# ).match(uri, ExampleProcessor)
-
# match.variables
-
# #=> ["first", "second"]
-
# match.captures
-
# #=> ["a", "b/c"]
-
#
-
# uri = Addressable::URI.parse("http://example.com/a/b/c/")
-
# match = Addressable::Template.new(
-
# "http://example.com/{first}{/second*}/"
-
# ).match(uri)
-
# match.variables
-
# #=> ["first", "second"]
-
# match.captures
-
# #=> ["a", ["b", "c"]]
-
1
def match(uri, processor=nil)
-
uri = Addressable::URI.parse(uri)
-
mapping = {}
-
-
# First, we need to process the pattern, and extract the values.
-
expansions, expansion_regexp =
-
parse_template_pattern(pattern, processor)
-
-
return nil unless uri.to_str.match(expansion_regexp)
-
unparsed_values = uri.to_str.scan(expansion_regexp).flatten
-
-
if uri.to_str == pattern
-
return Addressable::Template::MatchData.new(uri, self, mapping)
-
elsif expansions.size > 0
-
index = 0
-
expansions.each do |expansion|
-
_, operator, varlist = *expansion.match(EXPRESSION)
-
varlist.split(',').each do |varspec|
-
_, name, modifier = *varspec.match(VARSPEC)
-
mapping[name] ||= nil
-
case operator
-
when nil, '+', '#', '/', '.'
-
unparsed_value = unparsed_values[index]
-
name = varspec[VARSPEC, 1]
-
value = unparsed_value
-
value = value.split(JOINERS[operator]) if value && modifier == '*'
-
when ';', '?', '&'
-
if modifier == '*'
-
if unparsed_values[index]
-
value = unparsed_values[index].split(JOINERS[operator])
-
value = value.inject({}) do |acc, v|
-
key, val = v.split('=')
-
val = "" if val.nil?
-
acc[key] = val
-
acc
-
end
-
end
-
else
-
if (unparsed_values[index])
-
name, value = unparsed_values[index].split('=')
-
value = "" if value.nil?
-
end
-
end
-
end
-
if processor != nil && processor.respond_to?(:restore)
-
value = processor.restore(name, value)
-
end
-
if processor == nil
-
if value.is_a?(Hash)
-
value = value.inject({}){|acc, (k, v)|
-
acc[Addressable::URI.unencode_component(k)] =
-
Addressable::URI.unencode_component(v)
-
acc
-
}
-
elsif value.is_a?(Array)
-
value = value.map{|v| Addressable::URI.unencode_component(v) }
-
else
-
value = Addressable::URI.unencode_component(value)
-
end
-
end
-
if !mapping.has_key?(name) || mapping[name].nil?
-
# Doesn't exist, set to value (even if value is nil)
-
mapping[name] = value
-
end
-
index = index + 1
-
end
-
end
-
return Addressable::Template::MatchData.new(uri, self, mapping)
-
else
-
return nil
-
end
-
end
-
-
##
-
# Expands a URI template into another URI template.
-
#
-
# @param [Hash] mapping The mapping that corresponds to the pattern.
-
# @param [#validate, #transform] processor
-
# An optional processor object may be supplied.
-
#
-
# The object should respond to either the <tt>validate</tt> or
-
# <tt>transform</tt> messages or both. Both the <tt>validate</tt> and
-
# <tt>transform</tt> methods should take two parameters: <tt>name</tt> and
-
# <tt>value</tt>. The <tt>validate</tt> method should return <tt>true</tt>
-
# or <tt>false</tt>; <tt>true</tt> if the value of the variable is valid,
-
# <tt>false</tt> otherwise. An <tt>InvalidTemplateValueError</tt>
-
# exception will be raised if the value is invalid. The <tt>transform</tt>
-
# method should return the transformed variable value as a <tt>String</tt>.
-
# If a <tt>transform</tt> method is used, the value will not be percent
-
# encoded automatically. Unicode normalization will be performed both
-
# before and after sending the value to the transform method.
-
#
-
# @return [Addressable::Template] The partially expanded URI template.
-
#
-
# @example
-
# Addressable::Template.new(
-
# "http://example.com/{one}/{two}/"
-
# ).partial_expand({"one" => "1"}).pattern
-
# #=> "http://example.com/1/{two}/"
-
#
-
# Addressable::Template.new(
-
# "http://example.com/{?one,two}/"
-
# ).partial_expand({"one" => "1"}).pattern
-
# #=> "http://example.com/?one=1{&two}/"
-
#
-
# Addressable::Template.new(
-
# "http://example.com/{?one,two,three}/"
-
# ).partial_expand({"one" => "1", "three" => 3}).pattern
-
# #=> "http://example.com/?one=1{&two}&three=3"
-
1
def partial_expand(mapping, processor=nil)
-
result = self.pattern.dup
-
mapping = normalize_keys(mapping)
-
result.gsub!( EXPRESSION ) do |capture|
-
transform_partial_capture(mapping, capture, processor)
-
end
-
return Addressable::Template.new(result)
-
end
-
-
##
-
# Expands a URI template into a full URI.
-
#
-
# @param [Hash] mapping The mapping that corresponds to the pattern.
-
# @param [#validate, #transform] processor
-
# An optional processor object may be supplied.
-
#
-
# The object should respond to either the <tt>validate</tt> or
-
# <tt>transform</tt> messages or both. Both the <tt>validate</tt> and
-
# <tt>transform</tt> methods should take two parameters: <tt>name</tt> and
-
# <tt>value</tt>. The <tt>validate</tt> method should return <tt>true</tt>
-
# or <tt>false</tt>; <tt>true</tt> if the value of the variable is valid,
-
# <tt>false</tt> otherwise. An <tt>InvalidTemplateValueError</tt>
-
# exception will be raised if the value is invalid. The <tt>transform</tt>
-
# method should return the transformed variable value as a <tt>String</tt>.
-
# If a <tt>transform</tt> method is used, the value will not be percent
-
# encoded automatically. Unicode normalization will be performed both
-
# before and after sending the value to the transform method.
-
#
-
# @return [Addressable::URI] The expanded URI template.
-
#
-
# @example
-
# class ExampleProcessor
-
# def self.validate(name, value)
-
# return !!(value =~ /^[\w ]+$/) if name == "query"
-
# return true
-
# end
-
#
-
# def self.transform(name, value)
-
# return value.gsub(/ /, "+") if name == "query"
-
# return value
-
# end
-
# end
-
#
-
# Addressable::Template.new(
-
# "http://example.com/search/{query}/"
-
# ).expand(
-
# {"query" => "an example search query"},
-
# ExampleProcessor
-
# ).to_str
-
# #=> "http://example.com/search/an+example+search+query/"
-
#
-
# Addressable::Template.new(
-
# "http://example.com/search/{query}/"
-
# ).expand(
-
# {"query" => "an example search query"}
-
# ).to_str
-
# #=> "http://example.com/search/an%20example%20search%20query/"
-
#
-
# Addressable::Template.new(
-
# "http://example.com/search/{query}/"
-
# ).expand(
-
# {"query" => "bogus!"},
-
# ExampleProcessor
-
# ).to_str
-
# #=> Addressable::Template::InvalidTemplateValueError
-
1
def expand(mapping, processor=nil)
-
result = self.pattern.dup
-
mapping = normalize_keys(mapping)
-
result.gsub!( EXPRESSION ) do |capture|
-
transform_capture(mapping, capture, processor)
-
end
-
return Addressable::URI.parse(result)
-
end
-
-
##
-
# Returns an Array of variables used within the template pattern.
-
# The variables are listed in the Array in the order they appear within
-
# the pattern. Multiple occurrences of a variable within a pattern are
-
# not represented in this Array.
-
#
-
# @return [Array] The variables present in the template's pattern.
-
1
def variables
-
@variables ||= ordered_variable_defaults.map { |var, val| var }.uniq
-
end
-
1
alias_method :keys, :variables
-
-
##
-
# Returns a mapping of variables to their default values specified
-
# in the template. Variables without defaults are not returned.
-
#
-
# @return [Hash] Mapping of template variables to their defaults
-
1
def variable_defaults
-
@variable_defaults ||=
-
Hash[*ordered_variable_defaults.reject { |k, v| v.nil? }.flatten]
-
end
-
-
1
private
-
1
def ordered_variable_defaults
-
@ordered_variable_defaults ||= (
-
expansions, _ = parse_template_pattern(pattern)
-
expansions.map do |capture|
-
_, _, varlist = *capture.match(EXPRESSION)
-
varlist.split(',').map do |varspec|
-
varspec[VARSPEC, 1]
-
end
-
end.flatten
-
)
-
end
-
-
-
##
-
# Loops through each capture and expands any values available in mapping
-
#
-
# @param [Hash] mapping
-
# Set of keys to expand
-
# @param [String] capture
-
# The expression to expand
-
# @param [#validate, #transform] processor
-
# An optional processor object may be supplied.
-
#
-
# The object should respond to either the <tt>validate</tt> or
-
# <tt>transform</tt> messages or both. Both the <tt>validate</tt> and
-
# <tt>transform</tt> methods should take two parameters: <tt>name</tt> and
-
# <tt>value</tt>. The <tt>validate</tt> method should return <tt>true</tt>
-
# or <tt>false</tt>; <tt>true</tt> if the value of the variable is valid,
-
# <tt>false</tt> otherwise. An <tt>InvalidTemplateValueError</tt> exception
-
# will be raised if the value is invalid. The <tt>transform</tt> method
-
# should return the transformed variable value as a <tt>String</tt>. If a
-
# <tt>transform</tt> method is used, the value will not be percent encoded
-
# automatically. Unicode normalization will be performed both before and
-
# after sending the value to the transform method.
-
#
-
# @return [String] The expanded expression
-
1
def transform_partial_capture(mapping, capture, processor = nil)
-
_, operator, varlist = *capture.match(EXPRESSION)
-
is_first = true
-
varlist.split(',').inject('') do |acc, varspec|
-
_, name, _ = *varspec.match(VARSPEC)
-
value = mapping[name]
-
if value
-
operator = '&' if !is_first && operator == '?'
-
acc << transform_capture(mapping, "{#{operator}#{varspec}}", processor)
-
else
-
operator = '&' if !is_first && operator == '?'
-
acc << "{#{operator}#{varspec}}"
-
end
-
is_first = false
-
acc
-
end
-
end
-
-
##
-
# Transforms a mapped value so that values can be substituted into the
-
# template.
-
#
-
# @param [Hash] mapping The mapping to replace captures
-
# @param [String] capture
-
# The expression to replace
-
# @param [#validate, #transform] processor
-
# An optional processor object may be supplied.
-
#
-
# The object should respond to either the <tt>validate</tt> or
-
# <tt>transform</tt> messages or both. Both the <tt>validate</tt> and
-
# <tt>transform</tt> methods should take two parameters: <tt>name</tt> and
-
# <tt>value</tt>. The <tt>validate</tt> method should return <tt>true</tt>
-
# or <tt>false</tt>; <tt>true</tt> if the value of the variable is valid,
-
# <tt>false</tt> otherwise. An <tt>InvalidTemplateValueError</tt> exception
-
# will be raised if the value is invalid. The <tt>transform</tt> method
-
# should return the transformed variable value as a <tt>String</tt>. If a
-
# <tt>transform</tt> method is used, the value will not be percent encoded
-
# automatically. Unicode normalization will be performed both before and
-
# after sending the value to the transform method.
-
#
-
# @return [String] The expanded expression
-
1
def transform_capture(mapping, capture, processor=nil)
-
_, operator, varlist = *capture.match(EXPRESSION)
-
return_value = varlist.split(',').inject([]) do |acc, varspec|
-
_, name, modifier = *varspec.match(VARSPEC)
-
value = mapping[name]
-
unless value == nil || value == {}
-
allow_reserved = %w(+ #).include?(operator)
-
# Common primitives where the .to_s output is well-defined
-
if Numeric === value || Symbol === value ||
-
value == true || value == false
-
value = value.to_s
-
end
-
length = modifier.gsub(':', '').to_i if modifier =~ /^:\d+/
-
-
unless (Hash === value) ||
-
value.respond_to?(:to_ary) || value.respond_to?(:to_str)
-
raise TypeError,
-
"Can't convert #{value.class} into String or Array."
-
end
-
-
value = normalize_value(value)
-
-
if processor == nil || !processor.respond_to?(:transform)
-
# Handle percent escaping
-
if allow_reserved
-
encode_map =
-
Addressable::URI::CharacterClasses::RESERVED +
-
Addressable::URI::CharacterClasses::UNRESERVED
-
else
-
encode_map = Addressable::URI::CharacterClasses::UNRESERVED
-
end
-
if value.kind_of?(Array)
-
transformed_value = value.map do |val|
-
if length
-
Addressable::URI.encode_component(val[0...length], encode_map)
-
else
-
Addressable::URI.encode_component(val, encode_map)
-
end
-
end
-
unless modifier == "*"
-
transformed_value = transformed_value.join(',')
-
end
-
elsif value.kind_of?(Hash)
-
transformed_value = value.map do |key, val|
-
if modifier == "*"
-
"#{
-
Addressable::URI.encode_component( key, encode_map)
-
}=#{
-
Addressable::URI.encode_component( val, encode_map)
-
}"
-
else
-
"#{
-
Addressable::URI.encode_component( key, encode_map)
-
},#{
-
Addressable::URI.encode_component( val, encode_map)
-
}"
-
end
-
end
-
unless modifier == "*"
-
transformed_value = transformed_value.join(',')
-
end
-
else
-
if length
-
transformed_value = Addressable::URI.encode_component(
-
value[0...length], encode_map)
-
else
-
transformed_value = Addressable::URI.encode_component(
-
value, encode_map)
-
end
-
end
-
end
-
-
# Process, if we've got a processor
-
if processor != nil
-
if processor.respond_to?(:validate)
-
if !processor.validate(name, value)
-
display_value = value.kind_of?(Array) ? value.inspect : value
-
raise InvalidTemplateValueError,
-
"#{name}=#{display_value} is an invalid template value."
-
end
-
end
-
if processor.respond_to?(:transform)
-
transformed_value = processor.transform(name, value)
-
transformed_value = normalize_value(transformed_value)
-
end
-
end
-
acc << [name, transformed_value]
-
end
-
acc
-
end
-
return "" if return_value.empty?
-
join_values(operator, return_value)
-
end
-
-
##
-
# Takes a set of values, and joins them together based on the
-
# operator.
-
#
-
# @param [String, Nil] operator One of the operators from the set
-
# (?,&,+,#,;,/,.), or nil if there wasn't one.
-
# @param [Array] return_value
-
# The set of return values (as [variable_name, value] tuples) that will
-
# be joined together.
-
#
-
# @return [String] The transformed mapped value
-
1
def join_values(operator, return_value)
-
leader = LEADERS.fetch(operator, '')
-
joiner = JOINERS.fetch(operator, ',')
-
case operator
-
when '&', '?'
-
leader + return_value.map{|k,v|
-
if v.is_a?(Array) && v.first =~ /=/
-
v.join(joiner)
-
elsif v.is_a?(Array)
-
v.map{|inner_value| "#{k}=#{inner_value}"}.join(joiner)
-
else
-
"#{k}=#{v}"
-
end
-
}.join(joiner)
-
when ';'
-
return_value.map{|k,v|
-
if v.is_a?(Array) && v.first =~ /=/
-
';' + v.join(";")
-
elsif v.is_a?(Array)
-
';' + v.map{|inner_value| "#{k}=#{inner_value}"}.join(";")
-
else
-
v && v != '' ? ";#{k}=#{v}" : ";#{k}"
-
end
-
}.join
-
else
-
leader + return_value.map{|k,v| v}.join(joiner)
-
end
-
end
-
-
##
-
# Takes a set of values, and joins them together based on the
-
# operator.
-
#
-
# @param [Hash, Array, String] value
-
# Normalizes keys and values with IDNA#unicode_normalize_kc
-
#
-
# @return [Hash, Array, String] The normalized values
-
1
def normalize_value(value)
-
unless value.is_a?(Hash)
-
value = value.respond_to?(:to_ary) ? value.to_ary : value.to_str
-
end
-
-
# Handle unicode normalization
-
if value.kind_of?(Array)
-
value.map! { |val| Addressable::IDNA.unicode_normalize_kc(val) }
-
elsif value.kind_of?(Hash)
-
value = value.inject({}) { |acc, (k, v)|
-
acc[Addressable::IDNA.unicode_normalize_kc(k)] =
-
Addressable::IDNA.unicode_normalize_kc(v)
-
acc
-
}
-
else
-
value = Addressable::IDNA.unicode_normalize_kc(value)
-
end
-
value
-
end
-
-
##
-
# Generates a hash with string keys
-
#
-
# @param [Hash] mapping A mapping hash to normalize
-
#
-
# @return [Hash]
-
# A hash with stringified keys
-
1
def normalize_keys(mapping)
-
return mapping.inject({}) do |accu, pair|
-
name, value = pair
-
if Symbol === name
-
name = name.to_s
-
elsif name.respond_to?(:to_str)
-
name = name.to_str
-
else
-
raise TypeError,
-
"Can't convert #{name.class} into String."
-
end
-
accu[name] = value
-
accu
-
end
-
end
-
-
##
-
# Generates the <tt>Regexp</tt> that parses a template pattern.
-
#
-
# @param [String] pattern The URI template pattern.
-
# @param [#match] processor The template processor to use.
-
#
-
# @return [Regexp]
-
# A regular expression which may be used to parse a template pattern.
-
1
def parse_template_pattern(pattern, processor=nil)
-
# Escape the pattern. The two gsubs restore the escaped curly braces
-
# back to their original form. Basically, escape everything that isn't
-
# within an expansion.
-
escaped_pattern = Regexp.escape(
-
pattern
-
).gsub(/\\\{(.*?)\\\}/) do |escaped|
-
escaped.gsub(/\\(.)/, "\\1")
-
end
-
-
expansions = []
-
-
# Create a regular expression that captures the values of the
-
# variables in the URI.
-
regexp_string = escaped_pattern.gsub( EXPRESSION ) do |expansion|
-
-
expansions << expansion
-
_, operator, varlist = *expansion.match(EXPRESSION)
-
leader = Regexp.escape(LEADERS.fetch(operator, ''))
-
joiner = Regexp.escape(JOINERS.fetch(operator, ','))
-
combined = varlist.split(',').map do |varspec|
-
_, name, modifier = *varspec.match(VARSPEC)
-
-
result = processor && processor.respond_to?(:match) ? processor.match(name) : nil
-
if result
-
"(#{ result })"
-
else
-
group = case operator
-
when '+'
-
"#{ RESERVED }*?"
-
when '#'
-
"#{ RESERVED }*?"
-
when '/'
-
"#{ UNRESERVED }*?"
-
when '.'
-
"#{ UNRESERVED.gsub('\.', '') }*?"
-
when ';'
-
"#{ UNRESERVED }*=?#{ UNRESERVED }*?"
-
when '?'
-
"#{ UNRESERVED }*=#{ UNRESERVED }*?"
-
when '&'
-
"#{ UNRESERVED }*=#{ UNRESERVED }*?"
-
else
-
"#{ UNRESERVED }*?"
-
end
-
if modifier == '*'
-
"(#{group}(?:#{joiner}?#{group})*)?"
-
else
-
"(#{group})?"
-
end
-
end
-
end.join("#{joiner}?")
-
"(?:|#{leader}#{combined})"
-
end
-
-
# Ensure that the regular expression matches the whole URI.
-
regexp_string = "^#{regexp_string}$"
-
return expansions, Regexp.new(regexp_string)
-
end
-
-
end
-
end
-
# encoding:utf-8
-
#--
-
# Copyright (C) 2006-2013 Bob Aman
-
#
-
# Licensed under the Apache License, Version 2.0 (the "License");
-
# you may not use this file except in compliance with the License.
-
# You may obtain a copy of the License at
-
#
-
# http://www.apache.org/licenses/LICENSE-2.0
-
#
-
# Unless required by applicable law or agreed to in writing, software
-
# distributed under the License is distributed on an "AS IS" BASIS,
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-
# See the License for the specific language governing permissions and
-
# limitations under the License.
-
#++
-
-
-
1
require "addressable/version"
-
1
require "addressable/idna"
-
-
##
-
# Addressable is a library for processing links and URIs.
-
1
module Addressable
-
##
-
# This is an implementation of a URI parser based on
-
# <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986</a>,
-
# <a href="http://www.ietf.org/rfc/rfc3987.txt">RFC 3987</a>.
-
1
class URI
-
##
-
# Raised if something other than a uri is supplied.
-
1
class InvalidURIError < StandardError
-
end
-
-
##
-
# Container for the character classes specified in
-
# <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986</a>.
-
1
module CharacterClasses
-
1
ALPHA = "a-zA-Z"
-
1
DIGIT = "0-9"
-
1
GEN_DELIMS = "\\:\\/\\?\\#\\[\\]\\@"
-
1
SUB_DELIMS = "\\!\\$\\&\\'\\(\\)\\*\\+\\,\\;\\="
-
1
RESERVED = GEN_DELIMS + SUB_DELIMS
-
1
UNRESERVED = ALPHA + DIGIT + "\\-\\.\\_\\~"
-
1
PCHAR = UNRESERVED + SUB_DELIMS + "\\:\\@"
-
1
SCHEME = ALPHA + DIGIT + "\\-\\+\\."
-
1
AUTHORITY = PCHAR
-
1
PATH = PCHAR + "\\/"
-
1
QUERY = PCHAR + "\\/\\?"
-
1
FRAGMENT = PCHAR + "\\/\\?"
-
end
-
-
1
SLASH = '/'
-
1
EMPTY_STR = ''
-
-
1
URIREGEX = /^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/
-
-
1
PORT_MAPPING = {
-
"http" => 80,
-
"https" => 443,
-
"ftp" => 21,
-
"tftp" => 69,
-
"sftp" => 22,
-
"ssh" => 22,
-
"svn+ssh" => 22,
-
"telnet" => 23,
-
"nntp" => 119,
-
"gopher" => 70,
-
"wais" => 210,
-
"ldap" => 389,
-
"prospero" => 1525
-
}
-
-
##
-
# Returns a URI object based on the parsed string.
-
#
-
# @param [String, Addressable::URI, #to_str] uri
-
# The URI string to parse.
-
# No parsing is performed if the object is already an
-
# <code>Addressable::URI</code>.
-
#
-
# @return [Addressable::URI] The parsed URI.
-
1
def self.parse(uri)
-
# If we were given nil, return nil.
-
return nil unless uri
-
# If a URI object is passed, just return itself.
-
return uri.dup if uri.kind_of?(self)
-
-
# If a URI object of the Ruby standard library variety is passed,
-
# convert it to a string, then parse the string.
-
# We do the check this way because we don't want to accidentally
-
# cause a missing constant exception to be thrown.
-
if uri.class.name =~ /^URI\b/
-
uri = uri.to_s
-
end
-
-
# Otherwise, convert to a String
-
begin
-
uri = uri.to_str
-
rescue TypeError, NoMethodError
-
raise TypeError, "Can't convert #{uri.class} into String."
-
end if not uri.is_a? String
-
-
# This Regexp supplied as an example in RFC 3986, and it works great.
-
scan = uri.scan(URIREGEX)
-
fragments = scan[0]
-
scheme = fragments[1]
-
authority = fragments[3]
-
path = fragments[4]
-
query = fragments[6]
-
fragment = fragments[8]
-
user = nil
-
password = nil
-
host = nil
-
port = nil
-
if authority != nil
-
# The Regexp above doesn't split apart the authority.
-
userinfo = authority[/^([^\[\]]*)@/, 1]
-
if userinfo != nil
-
user = userinfo.strip[/^([^:]*):?/, 1]
-
password = userinfo.strip[/:(.*)$/, 1]
-
end
-
host = authority.gsub(
-
/^([^\[\]]*)@/, EMPTY_STR
-
).gsub(
-
/:([^:@\[\]]*?)$/, EMPTY_STR
-
)
-
port = authority[/:([^:@\[\]]*?)$/, 1]
-
end
-
if port == EMPTY_STR
-
port = nil
-
end
-
-
return new(
-
:scheme => scheme,
-
:user => user,
-
:password => password,
-
:host => host,
-
:port => port,
-
:path => path,
-
:query => query,
-
:fragment => fragment
-
)
-
end
-
-
##
-
# Converts an input to a URI. The input does not have to be a valid
-
# URI — the method will use heuristics to guess what URI was intended.
-
# This is not standards-compliant, merely user-friendly.
-
#
-
# @param [String, Addressable::URI, #to_str] uri
-
# The URI string to parse.
-
# No parsing is performed if the object is already an
-
# <code>Addressable::URI</code>.
-
# @param [Hash] hints
-
# A <code>Hash</code> of hints to the heuristic parser.
-
# Defaults to <code>{:scheme => "http"}</code>.
-
#
-
# @return [Addressable::URI] The parsed URI.
-
1
def self.heuristic_parse(uri, hints={})
-
# If we were given nil, return nil.
-
return nil unless uri
-
# If a URI object is passed, just return itself.
-
return uri.dup if uri.kind_of?(self)
-
-
# If a URI object of the Ruby standard library variety is passed,
-
# convert it to a string, then parse the string.
-
# We do the check this way because we don't want to accidentally
-
# cause a missing constant exception to be thrown.
-
if uri.class.name =~ /^URI\b/
-
uri = uri.to_s
-
end
-
-
if !uri.respond_to?(:to_str)
-
raise TypeError, "Can't convert #{uri.class} into String."
-
end
-
# Otherwise, convert to a String
-
uri = uri.to_str.dup
-
hints = {
-
:scheme => "http"
-
}.merge(hints)
-
case uri
-
when /^http:\/+/
-
uri.gsub!(/^http:\/+/, "http://")
-
when /^https:\/+/
-
uri.gsub!(/^https:\/+/, "https://")
-
when /^feed:\/+http:\/+/
-
uri.gsub!(/^feed:\/+http:\/+/, "feed:http://")
-
when /^feed:\/+/
-
uri.gsub!(/^feed:\/+/, "feed://")
-
when /^file:\/+/
-
uri.gsub!(/^file:\/+/, "file:///")
-
when /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/
-
uri.gsub!(/^/, hints[:scheme] + "://")
-
end
-
parsed = self.parse(uri)
-
if parsed.scheme =~ /^[^\/?#\.]+\.[^\/?#]+$/
-
parsed = self.parse(hints[:scheme] + "://" + uri)
-
end
-
if parsed.path.include?(".")
-
new_host = parsed.path[/^([^\/]+\.[^\/]*)/, 1]
-
if new_host
-
parsed.defer_validation do
-
new_path = parsed.path.gsub(
-
Regexp.new("^" + Regexp.escape(new_host)), EMPTY_STR)
-
parsed.host = new_host
-
parsed.path = new_path
-
parsed.scheme = hints[:scheme] unless parsed.scheme
-
end
-
end
-
end
-
return parsed
-
end
-
-
##
-
# Converts a path to a file scheme URI. If the path supplied is
-
# relative, it will be returned as a relative URI. If the path supplied
-
# is actually a non-file URI, it will parse the URI as if it had been
-
# parsed with <code>Addressable::URI.parse</code>. Handles all of the
-
# various Microsoft-specific formats for specifying paths.
-
#
-
# @param [String, Addressable::URI, #to_str] path
-
# Typically a <code>String</code> path to a file or directory, but
-
# will return a sensible return value if an absolute URI is supplied
-
# instead.
-
#
-
# @return [Addressable::URI]
-
# The parsed file scheme URI or the original URI if some other URI
-
# scheme was provided.
-
#
-
# @example
-
# base = Addressable::URI.convert_path("/absolute/path/")
-
# uri = Addressable::URI.convert_path("relative/path")
-
# (base + uri).to_s
-
# #=> "file:///absolute/path/relative/path"
-
#
-
# Addressable::URI.convert_path(
-
# "c:\\windows\\My Documents 100%20\\foo.txt"
-
# ).to_s
-
# #=> "file:///c:/windows/My%20Documents%20100%20/foo.txt"
-
#
-
# Addressable::URI.convert_path("http://example.com/").to_s
-
# #=> "http://example.com/"
-
1
def self.convert_path(path)
-
# If we were given nil, return nil.
-
return nil unless path
-
# If a URI object is passed, just return itself.
-
return path if path.kind_of?(self)
-
if !path.respond_to?(:to_str)
-
raise TypeError, "Can't convert #{path.class} into String."
-
end
-
# Otherwise, convert to a String
-
path = path.to_str.strip
-
-
path.gsub!(/^file:\/?\/?/, EMPTY_STR) if path =~ /^file:\/?\/?/
-
path = SLASH + path if path =~ /^([a-zA-Z])[\|:]/
-
uri = self.parse(path)
-
-
if uri.scheme == nil
-
# Adjust windows-style uris
-
uri.path.gsub!(/^\/?([a-zA-Z])[\|:][\\\/]/) do
-
"/#{$1.downcase}:/"
-
end
-
uri.path.gsub!(/\\/, SLASH)
-
if File.exists?(uri.path) &&
-
File.stat(uri.path).directory?
-
uri.path.gsub!(/\/$/, EMPTY_STR)
-
uri.path = uri.path + '/'
-
end
-
-
# If the path is absolute, set the scheme and host.
-
if uri.path =~ /^\//
-
uri.scheme = "file"
-
uri.host = EMPTY_STR
-
end
-
uri.normalize!
-
end
-
-
return uri
-
end
-
-
##
-
# Joins several URIs together.
-
#
-
# @param [String, Addressable::URI, #to_str] *uris
-
# The URIs to join.
-
#
-
# @return [Addressable::URI] The joined URI.
-
#
-
# @example
-
# base = "http://example.com/"
-
# uri = Addressable::URI.parse("relative/path")
-
# Addressable::URI.join(base, uri)
-
# #=> #<Addressable::URI:0xcab390 URI:http://example.com/relative/path>
-
1
def self.join(*uris)
-
uri_objects = uris.collect do |uri|
-
if !uri.respond_to?(:to_str)
-
raise TypeError, "Can't convert #{uri.class} into String."
-
end
-
uri.kind_of?(self) ? uri : self.parse(uri.to_str)
-
end
-
result = uri_objects.shift.dup
-
for uri in uri_objects
-
result.join!(uri)
-
end
-
return result
-
end
-
-
##
-
# Percent encodes a URI component.
-
#
-
# @param [String, #to_str] component The URI component to encode.
-
#
-
# @param [String, Regexp] character_class
-
# The characters which are not percent encoded. If a <code>String</code>
-
# is passed, the <code>String</code> must be formatted as a regular
-
# expression character class. (Do not include the surrounding square
-
# brackets.) For example, <code>"b-zB-Z0-9"</code> would cause
-
# everything but the letters 'b' through 'z' and the numbers '0' through
-
# '9' to be percent encoded. If a <code>Regexp</code> is passed, the
-
# value <code>/[^b-zB-Z0-9]/</code> would have the same effect. A set of
-
# useful <code>String</code> values may be found in the
-
# <code>Addressable::URI::CharacterClasses</code> module. The default
-
# value is the reserved plus unreserved character classes specified in
-
# <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986</a>.
-
#
-
# @param [Regexp] upcase_encoded
-
# A string of characters that may already be percent encoded, and whose
-
# encodings should be upcased. This allows normalization of percent
-
# encodings for characters not included in the
-
# <code>character_class</code>.
-
#
-
# @return [String] The encoded component.
-
#
-
# @example
-
# Addressable::URI.encode_component("simple/example", "b-zB-Z0-9")
-
# => "simple%2Fex%61mple"
-
# Addressable::URI.encode_component("simple/example", /[^b-zB-Z0-9]/)
-
# => "simple%2Fex%61mple"
-
# Addressable::URI.encode_component(
-
# "simple/example", Addressable::URI::CharacterClasses::UNRESERVED
-
# )
-
# => "simple%2Fexample"
-
1
def self.encode_component(component, character_class=
-
CharacterClasses::RESERVED + CharacterClasses::UNRESERVED,
-
upcase_encoded='')
-
return nil if component.nil?
-
-
begin
-
if component.kind_of?(Symbol) ||
-
component.kind_of?(Numeric) ||
-
component.kind_of?(TrueClass) ||
-
component.kind_of?(FalseClass)
-
component = component.to_s
-
else
-
component = component.to_str
-
end
-
rescue TypeError, NoMethodError
-
raise TypeError, "Can't convert #{component.class} into String."
-
end if !component.is_a? String
-
-
if ![String, Regexp].include?(character_class.class)
-
raise TypeError,
-
"Expected String or Regexp, got #{character_class.inspect}"
-
end
-
if character_class.kind_of?(String)
-
character_class = /[^#{character_class}]/
-
end
-
if component.respond_to?(:force_encoding)
-
# We can't perform regexps on invalid UTF sequences, but
-
# here we need to, so switch to ASCII.
-
component = component.dup
-
component.force_encoding(Encoding::ASCII_8BIT)
-
end
-
# Avoiding gsub! because there are edge cases with frozen strings
-
component = component.gsub(character_class) do |sequence|
-
(sequence.unpack('C*').map { |c| "%" + ("%02x" % c).upcase }).join
-
end
-
if upcase_encoded.length > 0
-
component = component.gsub(/%(#{upcase_encoded.chars.map do |char|
-
char.unpack('C*').map { |c| '%02x' % c }.join
-
end.join('|')})/i) { |s| s.upcase }
-
end
-
return component
-
end
-
-
1
class << self
-
1
alias_method :encode_component, :encode_component
-
end
-
-
##
-
# Unencodes any percent encoded characters within a URI component.
-
# This method may be used for unencoding either components or full URIs,
-
# however, it is recommended to use the <code>unencode_component</code>
-
# alias when unencoding components.
-
#
-
# @param [String, Addressable::URI, #to_str] uri
-
# The URI or component to unencode.
-
#
-
# @param [Class] return_type
-
# The type of object to return.
-
# This value may only be set to <code>String</code> or
-
# <code>Addressable::URI</code>. All other values are invalid. Defaults
-
# to <code>String</code>.
-
#
-
# @param [String] leave_encoded
-
# A string of characters to leave encoded. If a percent encoded character
-
# in this list is encountered then it will remain percent encoded.
-
#
-
# @return [String, Addressable::URI]
-
# The unencoded component or URI.
-
# The return type is determined by the <code>return_type</code>
-
# parameter.
-
1
def self.unencode(uri, return_type=String, leave_encoded='')
-
return nil if uri.nil?
-
-
begin
-
uri = uri.to_str
-
rescue NoMethodError, TypeError
-
raise TypeError, "Can't convert #{uri.class} into String."
-
end if !uri.is_a? String
-
if ![String, ::Addressable::URI].include?(return_type)
-
raise TypeError,
-
"Expected Class (String or Addressable::URI), " +
-
"got #{return_type.inspect}"
-
end
-
uri = uri.dup
-
# Seriously, only use UTF-8. I'm really not kidding!
-
uri.force_encoding("utf-8") if uri.respond_to?(:force_encoding)
-
leave_encoded.force_encoding("utf-8") if leave_encoded.respond_to?(:force_encoding)
-
result = uri.gsub(/%[0-9a-f]{2}/iu) do |sequence|
-
c = sequence[1..3].to_i(16).chr
-
c.force_encoding("utf-8") if c.respond_to?(:force_encoding)
-
leave_encoded.include?(c) ? sequence : c
-
end
-
result.force_encoding("utf-8") if result.respond_to?(:force_encoding)
-
if return_type == String
-
return result
-
elsif return_type == ::Addressable::URI
-
return ::Addressable::URI.parse(result)
-
end
-
end
-
-
1
class << self
-
1
alias_method :unescape, :unencode
-
1
alias_method :unencode_component, :unencode
-
1
alias_method :unescape_component, :unencode
-
end
-
-
-
##
-
# Normalizes the encoding of a URI component.
-
#
-
# @param [String, #to_str] component The URI component to encode.
-
#
-
# @param [String, Regexp] character_class
-
# The characters which are not percent encoded. If a <code>String</code>
-
# is passed, the <code>String</code> must be formatted as a regular
-
# expression character class. (Do not include the surrounding square
-
# brackets.) For example, <code>"b-zB-Z0-9"</code> would cause
-
# everything but the letters 'b' through 'z' and the numbers '0'
-
# through '9' to be percent encoded. If a <code>Regexp</code> is passed,
-
# the value <code>/[^b-zB-Z0-9]/</code> would have the same effect. A
-
# set of useful <code>String</code> values may be found in the
-
# <code>Addressable::URI::CharacterClasses</code> module. The default
-
# value is the reserved plus unreserved character classes specified in
-
# <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986</a>.
-
#
-
# @param [String] leave_encoded
-
# When <code>character_class</code> is a <code>String</code> then
-
# <code>leave_encoded</code> is a string of characters that should remain
-
# percent encoded while normalizing the component; if they appear percent
-
# encoded in the original component, then they will be upcased ("%2f"
-
# normalized to "%2F") but otherwise left alone.
-
#
-
# @return [String] The normalized component.
-
#
-
# @example
-
# Addressable::URI.normalize_component("simpl%65/%65xampl%65", "b-zB-Z")
-
# => "simple%2Fex%61mple"
-
# Addressable::URI.normalize_component(
-
# "simpl%65/%65xampl%65", /[^b-zB-Z]/
-
# )
-
# => "simple%2Fex%61mple"
-
# Addressable::URI.normalize_component(
-
# "simpl%65/%65xampl%65",
-
# Addressable::URI::CharacterClasses::UNRESERVED
-
# )
-
# => "simple%2Fexample"
-
# Addressable::URI.normalize_component(
-
# "one%20two%2fthree%26four",
-
# "0-9a-zA-Z &/",
-
# "/"
-
# )
-
# => "one two%2Fthree&four"
-
1
def self.normalize_component(component, character_class=
-
CharacterClasses::RESERVED + CharacterClasses::UNRESERVED,
-
leave_encoded='')
-
return nil if component.nil?
-
-
begin
-
component = component.to_str
-
rescue NoMethodError, TypeError
-
raise TypeError, "Can't convert #{component.class} into String."
-
end if !component.is_a? String
-
-
if ![String, Regexp].include?(character_class.class)
-
raise TypeError,
-
"Expected String or Regexp, got #{character_class.inspect}"
-
end
-
if character_class.kind_of?(String)
-
leave_re = if leave_encoded.length > 0
-
character_class = "#{character_class}%" unless character_class.include?('%')
-
-
"|%(?!#{leave_encoded.chars.map do |char|
-
seq = char.unpack('C*').map { |c| '%02x' % c }.join
-
[seq.upcase, seq.downcase]
-
end.flatten.join('|')})"
-
end
-
-
character_class = /[^#{character_class}]#{leave_re}/
-
end
-
if component.respond_to?(:force_encoding)
-
# We can't perform regexps on invalid UTF sequences, but
-
# here we need to, so switch to ASCII.
-
component = component.dup
-
component.force_encoding(Encoding::ASCII_8BIT)
-
end
-
unencoded = self.unencode_component(component, String, leave_encoded)
-
begin
-
encoded = self.encode_component(
-
Addressable::IDNA.unicode_normalize_kc(unencoded),
-
character_class,
-
leave_encoded
-
)
-
rescue ArgumentError
-
encoded = self.encode_component(unencoded)
-
end
-
if encoded.respond_to?(:force_encoding)
-
encoded.force_encoding(Encoding::UTF_8)
-
end
-
return encoded
-
end
-
-
##
-
# Percent encodes any special characters in the URI.
-
#
-
# @param [String, Addressable::URI, #to_str] uri
-
# The URI to encode.
-
#
-
# @param [Class] return_type
-
# The type of object to return.
-
# This value may only be set to <code>String</code> or
-
# <code>Addressable::URI</code>. All other values are invalid. Defaults
-
# to <code>String</code>.
-
#
-
# @return [String, Addressable::URI]
-
# The encoded URI.
-
# The return type is determined by the <code>return_type</code>
-
# parameter.
-
1
def self.encode(uri, return_type=String)
-
return nil if uri.nil?
-
-
begin
-
uri = uri.to_str
-
rescue NoMethodError, TypeError
-
raise TypeError, "Can't convert #{uri.class} into String."
-
end if !uri.is_a? String
-
-
if ![String, ::Addressable::URI].include?(return_type)
-
raise TypeError,
-
"Expected Class (String or Addressable::URI), " +
-
"got #{return_type.inspect}"
-
end
-
uri_object = uri.kind_of?(self) ? uri : self.parse(uri)
-
encoded_uri = Addressable::URI.new(
-
:scheme => self.encode_component(uri_object.scheme,
-
Addressable::URI::CharacterClasses::SCHEME),
-
:authority => self.encode_component(uri_object.authority,
-
Addressable::URI::CharacterClasses::AUTHORITY),
-
:path => self.encode_component(uri_object.path,
-
Addressable::URI::CharacterClasses::PATH),
-
:query => self.encode_component(uri_object.query,
-
Addressable::URI::CharacterClasses::QUERY),
-
:fragment => self.encode_component(uri_object.fragment,
-
Addressable::URI::CharacterClasses::FRAGMENT)
-
)
-
if return_type == String
-
return encoded_uri.to_s
-
elsif return_type == ::Addressable::URI
-
return encoded_uri
-
end
-
end
-
-
1
class << self
-
1
alias_method :escape, :encode
-
end
-
-
##
-
# Normalizes the encoding of a URI. Characters within a hostname are
-
# not percent encoded to allow for internationalized domain names.
-
#
-
# @param [String, Addressable::URI, #to_str] uri
-
# The URI to encode.
-
#
-
# @param [Class] return_type
-
# The type of object to return.
-
# This value may only be set to <code>String</code> or
-
# <code>Addressable::URI</code>. All other values are invalid. Defaults
-
# to <code>String</code>.
-
#
-
# @return [String, Addressable::URI]
-
# The encoded URI.
-
# The return type is determined by the <code>return_type</code>
-
# parameter.
-
1
def self.normalized_encode(uri, return_type=String)
-
begin
-
uri = uri.to_str
-
rescue NoMethodError, TypeError
-
raise TypeError, "Can't convert #{uri.class} into String."
-
end if !uri.is_a? String
-
-
if ![String, ::Addressable::URI].include?(return_type)
-
raise TypeError,
-
"Expected Class (String or Addressable::URI), " +
-
"got #{return_type.inspect}"
-
end
-
uri_object = uri.kind_of?(self) ? uri : self.parse(uri)
-
components = {
-
:scheme => self.unencode_component(uri_object.scheme),
-
:user => self.unencode_component(uri_object.user),
-
:password => self.unencode_component(uri_object.password),
-
:host => self.unencode_component(uri_object.host),
-
:port => (uri_object.port.nil? ? nil : uri_object.port.to_s),
-
:path => self.unencode_component(uri_object.path),
-
:query => self.unencode_component(uri_object.query),
-
:fragment => self.unencode_component(uri_object.fragment)
-
}
-
components.each do |key, value|
-
if value != nil
-
begin
-
components[key] =
-
Addressable::IDNA.unicode_normalize_kc(value.to_str)
-
rescue ArgumentError
-
# Likely a malformed UTF-8 character, skip unicode normalization
-
components[key] = value.to_str
-
end
-
end
-
end
-
encoded_uri = Addressable::URI.new(
-
:scheme => self.encode_component(components[:scheme],
-
Addressable::URI::CharacterClasses::SCHEME),
-
:user => self.encode_component(components[:user],
-
Addressable::URI::CharacterClasses::UNRESERVED),
-
:password => self.encode_component(components[:password],
-
Addressable::URI::CharacterClasses::UNRESERVED),
-
:host => components[:host],
-
:port => components[:port],
-
:path => self.encode_component(components[:path],
-
Addressable::URI::CharacterClasses::PATH),
-
:query => self.encode_component(components[:query],
-
Addressable::URI::CharacterClasses::QUERY),
-
:fragment => self.encode_component(components[:fragment],
-
Addressable::URI::CharacterClasses::FRAGMENT)
-
)
-
if return_type == String
-
return encoded_uri.to_s
-
elsif return_type == ::Addressable::URI
-
return encoded_uri
-
end
-
end
-
-
##
-
# Encodes a set of key/value pairs according to the rules for the
-
# <code>application/x-www-form-urlencoded</code> MIME type.
-
#
-
# @param [#to_hash, #to_ary] form_values
-
# The form values to encode.
-
#
-
# @param [TrueClass, FalseClass] sort
-
# Sort the key/value pairs prior to encoding.
-
# Defaults to <code>false</code>.
-
#
-
# @return [String]
-
# The encoded value.
-
1
def self.form_encode(form_values, sort=false)
-
if form_values.respond_to?(:to_hash)
-
form_values = form_values.to_hash.to_a
-
elsif form_values.respond_to?(:to_ary)
-
form_values = form_values.to_ary
-
else
-
raise TypeError, "Can't convert #{form_values.class} into Array."
-
end
-
-
form_values = form_values.inject([]) do |accu, (key, value)|
-
if value.kind_of?(Array)
-
value.each do |v|
-
accu << [key.to_s, v.to_s]
-
end
-
else
-
accu << [key.to_s, value.to_s]
-
end
-
accu
-
end
-
-
if sort
-
# Useful for OAuth and optimizing caching systems
-
form_values = form_values.sort
-
end
-
escaped_form_values = form_values.map do |(key, value)|
-
# Line breaks are CRLF pairs
-
[
-
self.encode_component(
-
key.gsub(/(\r\n|\n|\r)/, "\r\n"),
-
CharacterClasses::UNRESERVED
-
).gsub("%20", "+"),
-
self.encode_component(
-
value.gsub(/(\r\n|\n|\r)/, "\r\n"),
-
CharacterClasses::UNRESERVED
-
).gsub("%20", "+")
-
]
-
end
-
return (escaped_form_values.map do |(key, value)|
-
"#{key}=#{value}"
-
end).join("&")
-
end
-
-
##
-
# Decodes a <code>String</code> according to the rules for the
-
# <code>application/x-www-form-urlencoded</code> MIME type.
-
#
-
# @param [String, #to_str] encoded_value
-
# The form values to decode.
-
#
-
# @return [Array]
-
# The decoded values.
-
# This is not a <code>Hash</code> because of the possibility for
-
# duplicate keys.
-
1
def self.form_unencode(encoded_value)
-
if !encoded_value.respond_to?(:to_str)
-
raise TypeError, "Can't convert #{encoded_value.class} into String."
-
end
-
encoded_value = encoded_value.to_str
-
split_values = encoded_value.split("&").map do |pair|
-
pair.split("=", 2)
-
end
-
return split_values.map do |(key, value)|
-
[
-
key ? self.unencode_component(
-
key.gsub("+", "%20")).gsub(/(\r\n|\n|\r)/, "\n") : nil,
-
value ? (self.unencode_component(
-
value.gsub("+", "%20")).gsub(/(\r\n|\n|\r)/, "\n")) : nil
-
]
-
end
-
end
-
-
##
-
# Creates a new uri object from component parts.
-
#
-
# @option [String, #to_str] scheme The scheme component.
-
# @option [String, #to_str] user The user component.
-
# @option [String, #to_str] password The password component.
-
# @option [String, #to_str] userinfo
-
# The userinfo component. If this is supplied, the user and password
-
# components must be omitted.
-
# @option [String, #to_str] host The host component.
-
# @option [String, #to_str] port The port component.
-
# @option [String, #to_str] authority
-
# The authority component. If this is supplied, the user, password,
-
# userinfo, host, and port components must be omitted.
-
# @option [String, #to_str] path The path component.
-
# @option [String, #to_str] query The query component.
-
# @option [String, #to_str] fragment The fragment component.
-
#
-
# @return [Addressable::URI] The constructed URI object.
-
1
def initialize(options={})
-
if options.has_key?(:authority)
-
if (options.keys & [:userinfo, :user, :password, :host, :port]).any?
-
raise ArgumentError,
-
"Cannot specify both an authority and any of the components " +
-
"within the authority."
-
end
-
end
-
if options.has_key?(:userinfo)
-
if (options.keys & [:user, :password]).any?
-
raise ArgumentError,
-
"Cannot specify both a userinfo and either the user or password."
-
end
-
end
-
-
self.defer_validation do
-
# Bunch of crazy logic required because of the composite components
-
# like userinfo and authority.
-
self.scheme = options[:scheme] if options[:scheme]
-
self.user = options[:user] if options[:user]
-
self.password = options[:password] if options[:password]
-
self.userinfo = options[:userinfo] if options[:userinfo]
-
self.host = options[:host] if options[:host]
-
self.port = options[:port] if options[:port]
-
self.authority = options[:authority] if options[:authority]
-
self.path = options[:path] if options[:path]
-
self.query = options[:query] if options[:query]
-
self.query_values = options[:query_values] if options[:query_values]
-
self.fragment = options[:fragment] if options[:fragment]
-
end
-
end
-
-
##
-
# Freeze URI, initializing instance variables.
-
#
-
# @return [Addressable::URI] The frozen URI object.
-
1
def freeze
-
self.normalized_scheme
-
self.normalized_user
-
self.normalized_password
-
self.normalized_userinfo
-
self.normalized_host
-
self.normalized_port
-
self.normalized_authority
-
self.normalized_site
-
self.normalized_path
-
self.normalized_query
-
self.normalized_fragment
-
self.hash
-
super
-
end
-
-
##
-
# The scheme component for this URI.
-
#
-
# @return [String] The scheme component.
-
1
def scheme
-
return instance_variable_defined?(:@scheme) ? @scheme : nil
-
end
-
-
##
-
# The scheme component for this URI, normalized.
-
#
-
# @return [String] The scheme component, normalized.
-
1
def normalized_scheme
-
self.scheme && @normalized_scheme ||= (begin
-
if self.scheme =~ /^\s*ssh\+svn\s*$/i
-
"svn+ssh"
-
else
-
Addressable::URI.normalize_component(
-
self.scheme.strip.downcase,
-
Addressable::URI::CharacterClasses::SCHEME
-
)
-
end
-
end)
-
end
-
-
##
-
# Sets the scheme component for this URI.
-
#
-
# @param [String, #to_str] new_scheme The new scheme component.
-
1
def scheme=(new_scheme)
-
if new_scheme && !new_scheme.respond_to?(:to_str)
-
raise TypeError, "Can't convert #{new_scheme.class} into String."
-
elsif new_scheme
-
new_scheme = new_scheme.to_str
-
end
-
if new_scheme && new_scheme !~ /[a-z][a-z0-9\.\+\-]*/i
-
raise InvalidURIError, "Invalid scheme format."
-
end
-
@scheme = new_scheme
-
@scheme = nil if @scheme.to_s.strip.empty?
-
-
# Reset dependant values
-
@normalized_scheme = nil
-
@uri_string = nil
-
@hash = nil
-
-
# Ensure we haven't created an invalid URI
-
validate()
-
end
-
-
##
-
# The user component for this URI.
-
#
-
# @return [String] The user component.
-
1
def user
-
return instance_variable_defined?(:@user) ? @user : nil
-
end
-
-
##
-
# The user component for this URI, normalized.
-
#
-
# @return [String] The user component, normalized.
-
1
def normalized_user
-
self.user && @normalized_user ||= (begin
-
if normalized_scheme =~ /https?/ && self.user.strip.empty? &&
-
(!self.password || self.password.strip.empty?)
-
nil
-
else
-
Addressable::URI.normalize_component(
-
self.user.strip,
-
Addressable::URI::CharacterClasses::UNRESERVED
-
)
-
end
-
end)
-
end
-
-
##
-
# Sets the user component for this URI.
-
#
-
# @param [String, #to_str] new_user The new user component.
-
1
def user=(new_user)
-
if new_user && !new_user.respond_to?(:to_str)
-
raise TypeError, "Can't convert #{new_user.class} into String."
-
end
-
@user = new_user ? new_user.to_str : nil
-
-
# You can't have a nil user with a non-nil password
-
if password != nil
-
@user = EMPTY_STR if @user.nil?
-
end
-
-
# Reset dependant values
-
@userinfo = nil
-
@normalized_userinfo = nil
-
@authority = nil
-
@normalized_user = nil
-
@uri_string = nil
-
@hash = nil
-
-
# Ensure we haven't created an invalid URI
-
validate()
-
end
-
-
##
-
# The password component for this URI.
-
#
-
# @return [String] The password component.
-
1
def password
-
return instance_variable_defined?(:@password) ? @password : nil
-
end
-
-
##
-
# The password component for this URI, normalized.
-
#
-
# @return [String] The password component, normalized.
-
1
def normalized_password
-
self.password && @normalized_password ||= (begin
-
if self.normalized_scheme =~ /https?/ && self.password.strip.empty? &&
-
(!self.user || self.user.strip.empty?)
-
nil
-
else
-
Addressable::URI.normalize_component(
-
self.password.strip,
-
Addressable::URI::CharacterClasses::UNRESERVED
-
)
-
end
-
end)
-
end
-
-
##
-
# Sets the password component for this URI.
-
#
-
# @param [String, #to_str] new_password The new password component.
-
1
def password=(new_password)
-
if new_password && !new_password.respond_to?(:to_str)
-
raise TypeError, "Can't convert #{new_password.class} into String."
-
end
-
@password = new_password ? new_password.to_str : nil
-
-
# You can't have a nil user with a non-nil password
-
@password ||= nil
-
@user ||= nil
-
if @password != nil
-
@user = EMPTY_STR if @user.nil?
-
end
-
-
# Reset dependant values
-
@userinfo = nil
-
@normalized_userinfo = nil
-
@authority = nil
-
@normalized_password = nil
-
@uri_string = nil
-
@hash = nil
-
-
# Ensure we haven't created an invalid URI
-
validate()
-
end
-
-
##
-
# The userinfo component for this URI.
-
# Combines the user and password components.
-
#
-
# @return [String] The userinfo component.
-
1
def userinfo
-
current_user = self.user
-
current_password = self.password
-
(current_user || current_password) && @userinfo ||= (begin
-
if current_user && current_password
-
"#{current_user}:#{current_password}"
-
elsif current_user && !current_password
-
"#{current_user}"
-
end
-
end)
-
end
-
-
##
-
# The userinfo component for this URI, normalized.
-
#
-
# @return [String] The userinfo component, normalized.
-
1
def normalized_userinfo
-
self.userinfo && @normalized_userinfo ||= (begin
-
current_user = self.normalized_user
-
current_password = self.normalized_password
-
if !current_user && !current_password
-
nil
-
elsif current_user && current_password
-
"#{current_user}:#{current_password}"
-
elsif current_user && !current_password
-
"#{current_user}"
-
end
-
end)
-
end
-
-
##
-
# Sets the userinfo component for this URI.
-
#
-
# @param [String, #to_str] new_userinfo The new userinfo component.
-
1
def userinfo=(new_userinfo)
-
if new_userinfo && !new_userinfo.respond_to?(:to_str)
-
raise TypeError, "Can't convert #{new_userinfo.class} into String."
-
end
-
new_user, new_password = if new_userinfo
-
[
-
new_userinfo.to_str.strip[/^(.*):/, 1],
-
new_userinfo.to_str.strip[/:(.*)$/, 1]
-
]
-
else
-
[nil, nil]
-
end
-
-
# Password assigned first to ensure validity in case of nil
-
self.password = new_password
-
self.user = new_user
-
-
# Reset dependant values
-
@authority = nil
-
@uri_string = nil
-
@hash = nil
-
-
# Ensure we haven't created an invalid URI
-
validate()
-
end
-
-
##
-
# The host component for this URI.
-
#
-
# @return [String] The host component.
-
1
def host
-
return instance_variable_defined?(:@host) ? @host : nil
-
end
-
-
##
-
# The host component for this URI, normalized.
-
#
-
# @return [String] The host component, normalized.
-
1
def normalized_host
-
self.host && @normalized_host ||= (begin
-
if !self.host.strip.empty?
-
result = ::Addressable::IDNA.to_ascii(
-
URI.unencode_component(self.host.strip.downcase)
-
)
-
if result =~ /[^\.]\.$/
-
# Single trailing dots are unnecessary.
-
result = result[0...-1]
-
end
-
result
-
else
-
EMPTY_STR
-
end
-
end)
-
end
-
-
##
-
# Sets the host component for this URI.
-
#
-
# @param [String, #to_str] new_host The new host component.
-
1
def host=(new_host)
-
if new_host && !new_host.respond_to?(:to_str)
-
raise TypeError, "Can't convert #{new_host.class} into String."
-
end
-
@host = new_host ? new_host.to_str : nil
-
-
unreserved = CharacterClasses::UNRESERVED
-
sub_delims = CharacterClasses::SUB_DELIMS
-
if @host != nil && (@host =~ /[<>{}\/\?\#\@]/ ||
-
(@host[/^\[(.*)\]$/, 1] != nil && @host[/^\[(.*)\]$/, 1] !~
-
Regexp.new("^[#{unreserved}#{sub_delims}:]*$")))
-
raise InvalidURIError, "Invalid character in host: '#{@host.to_s}'"
-
end
-
-
# Reset dependant values
-
@authority = nil
-
@normalized_host = nil
-
@uri_string = nil
-
@hash = nil
-
-
# Ensure we haven't created an invalid URI
-
validate()
-
end
-
-
##
-
# This method is same as URI::Generic#host except
-
# brackets for IPv6 (and 'IPvFuture') addresses are removed.
-
#
-
# @see Addressable::URI#host
-
#
-
# @return [String] The hostname for this URI.
-
1
def hostname
-
v = self.host
-
/\A\[(.*)\]\z/ =~ v ? $1 : v
-
end
-
-
##
-
# This method is same as URI::Generic#host= except
-
# the argument can be a bare IPv6 address (or 'IPvFuture').
-
#
-
# @see Addressable::URI#host=
-
#
-
# @param [String, #to_str] new_hostname The new hostname for this URI.
-
1
def hostname=(new_hostname)
-
if new_hostname && !new_hostname.respond_to?(:to_str)
-
raise TypeError, "Can't convert #{new_hostname.class} into String."
-
end
-
v = new_hostname ? new_hostname.to_str : nil
-
v = "[#{v}]" if /\A\[.*\]\z/ !~ v && /:/ =~ v
-
self.host = v
-
end
-
-
##
-
# The authority component for this URI.
-
# Combines the user, password, host, and port components.
-
#
-
# @return [String] The authority component.
-
1
def authority
-
self.host && @authority ||= (begin
-
authority = ""
-
if self.userinfo != nil
-
authority << "#{self.userinfo}@"
-
end
-
authority << self.host
-
if self.port != nil
-
authority << ":#{self.port}"
-
end
-
authority
-
end)
-
end
-
-
##
-
# The authority component for this URI, normalized.
-
#
-
# @return [String] The authority component, normalized.
-
1
def normalized_authority
-
self.authority && @normalized_authority ||= (begin
-
authority = ""
-
if self.normalized_userinfo != nil
-
authority << "#{self.normalized_userinfo}@"
-
end
-
authority << self.normalized_host
-
if self.normalized_port != nil
-
authority << ":#{self.normalized_port}"
-
end
-
authority
-
end)
-
end
-
-
##
-
# Sets the authority component for this URI.
-
#
-
# @param [String, #to_str] new_authority The new authority component.
-
1
def authority=(new_authority)
-
if new_authority
-
if !new_authority.respond_to?(:to_str)
-
raise TypeError, "Can't convert #{new_authority.class} into String."
-
end
-
new_authority = new_authority.to_str
-
new_userinfo = new_authority[/^([^\[\]]*)@/, 1]
-
if new_userinfo
-
new_user = new_userinfo.strip[/^([^:]*):?/, 1]
-
new_password = new_userinfo.strip[/:(.*)$/, 1]
-
end
-
new_host = new_authority.gsub(
-
/^([^\[\]]*)@/, EMPTY_STR
-
).gsub(
-
/:([^:@\[\]]*?)$/, EMPTY_STR
-
)
-
new_port =
-
new_authority[/:([^:@\[\]]*?)$/, 1]
-
end
-
-
# Password assigned first to ensure validity in case of nil
-
self.password = defined?(new_password) ? new_password : nil
-
self.user = defined?(new_user) ? new_user : nil
-
self.host = defined?(new_host) ? new_host : nil
-
self.port = defined?(new_port) ? new_port : nil
-
-
# Reset dependant values
-
@userinfo = nil
-
@normalized_userinfo = nil
-
@uri_string = nil
-
@hash = nil
-
-
# Ensure we haven't created an invalid URI
-
validate()
-
end
-
-
##
-
# The origin for this URI, serialized to ASCII, as per
-
# RFC 6454, section 6.2.
-
#
-
# @return [String] The serialized origin.
-
1
def origin
-
return (if self.scheme && self.authority
-
if self.normalized_port
-
(
-
"#{self.normalized_scheme}://#{self.normalized_host}" +
-
":#{self.normalized_port}"
-
)
-
else
-
"#{self.normalized_scheme}://#{self.normalized_host}"
-
end
-
else
-
"null"
-
end)
-
end
-
-
# Returns an array of known ip-based schemes. These schemes typically
-
# use a similar URI form:
-
# <code>//<user>:<password>@<host>:<port>/<url-path></code>
-
1
def self.ip_based_schemes
-
return self.port_mapping.keys
-
end
-
-
# Returns a hash of common IP-based schemes and their default port
-
# numbers. Adding new schemes to this hash, as necessary, will allow
-
# for better URI normalization.
-
1
def self.port_mapping
-
PORT_MAPPING
-
end
-
-
##
-
# The port component for this URI.
-
# This is the port number actually given in the URI. This does not
-
# infer port numbers from default values.
-
#
-
# @return [Integer] The port component.
-
1
def port
-
return instance_variable_defined?(:@port) ? @port : nil
-
end
-
-
##
-
# The port component for this URI, normalized.
-
#
-
# @return [Integer] The port component, normalized.
-
1
def normalized_port
-
if URI.port_mapping[self.normalized_scheme] == self.port
-
nil
-
else
-
self.port
-
end
-
end
-
-
##
-
# Sets the port component for this URI.
-
#
-
# @param [String, Integer, #to_s] new_port The new port component.
-
1
def port=(new_port)
-
if new_port != nil && new_port.respond_to?(:to_str)
-
new_port = Addressable::URI.unencode_component(new_port.to_str)
-
end
-
if new_port != nil && !(new_port.to_s =~ /^\d+$/)
-
raise InvalidURIError,
-
"Invalid port number: #{new_port.inspect}"
-
end
-
-
@port = new_port.to_s.to_i
-
@port = nil if @port == 0
-
-
# Reset dependant values
-
@authority = nil
-
@normalized_port = nil
-
@uri_string = nil
-
@hash = nil
-
-
# Ensure we haven't created an invalid URI
-
validate()
-
end
-
-
##
-
# The inferred port component for this URI.
-
# This method will normalize to the default port for the URI's scheme if
-
# the port isn't explicitly specified in the URI.
-
#
-
# @return [Integer] The inferred port component.
-
1
def inferred_port
-
if self.port.to_i == 0
-
self.default_port
-
else
-
self.port.to_i
-
end
-
end
-
-
##
-
# The default port for this URI's scheme.
-
# This method will always returns the default port for the URI's scheme
-
# regardless of the presence of an explicit port in the URI.
-
#
-
# @return [Integer] The default port.
-
1
def default_port
-
URI.port_mapping[self.scheme.strip.downcase] if self.scheme
-
end
-
-
##
-
# The combination of components that represent a site.
-
# Combines the scheme, user, password, host, and port components.
-
# Primarily useful for HTTP and HTTPS.
-
#
-
# For example, <code>"http://example.com/path?query"</code> would have a
-
# <code>site</code> value of <code>"http://example.com"</code>.
-
#
-
# @return [String] The components that identify a site.
-
1
def site
-
(self.scheme || self.authority) && @site ||= (begin
-
site_string = ""
-
site_string << "#{self.scheme}:" if self.scheme != nil
-
site_string << "//#{self.authority}" if self.authority != nil
-
site_string
-
end)
-
end
-
-
##
-
# The normalized combination of components that represent a site.
-
# Combines the scheme, user, password, host, and port components.
-
# Primarily useful for HTTP and HTTPS.
-
#
-
# For example, <code>"http://example.com/path?query"</code> would have a
-
# <code>site</code> value of <code>"http://example.com"</code>.
-
#
-
# @return [String] The normalized components that identify a site.
-
1
def normalized_site
-
self.site && @normalized_site ||= (begin
-
site_string = ""
-
if self.normalized_scheme != nil
-
site_string << "#{self.normalized_scheme}:"
-
end
-
if self.normalized_authority != nil
-
site_string << "//#{self.normalized_authority}"
-
end
-
site_string
-
end)
-
end
-
-
##
-
# Sets the site value for this URI.
-
#
-
# @param [String, #to_str] new_site The new site value.
-
1
def site=(new_site)
-
if new_site
-
if !new_site.respond_to?(:to_str)
-
raise TypeError, "Can't convert #{new_site.class} into String."
-
end
-
new_site = new_site.to_str
-
# These two regular expressions derived from the primary parsing
-
# expression
-
self.scheme = new_site[/^(?:([^:\/?#]+):)?(?:\/\/(?:[^\/?#]*))?$/, 1]
-
self.authority = new_site[
-
/^(?:(?:[^:\/?#]+):)?(?:\/\/([^\/?#]*))?$/, 1
-
]
-
else
-
self.scheme = nil
-
self.authority = nil
-
end
-
end
-
-
##
-
# The path component for this URI.
-
#
-
# @return [String] The path component.
-
1
def path
-
return instance_variable_defined?(:@path) ? @path : EMPTY_STR
-
end
-
-
1
NORMPATH = /^(?!\/)[^\/:]*:.*$/
-
##
-
# The path component for this URI, normalized.
-
#
-
# @return [String] The path component, normalized.
-
1
def normalized_path
-
@normalized_path ||= (begin
-
path = self.path.to_s
-
if self.scheme == nil && path =~ NORMPATH
-
# Relative paths with colons in the first segment are ambiguous.
-
path = path.sub(":", "%2F")
-
end
-
# String#split(delimeter, -1) uses the more strict splitting behavior
-
# found by default in Python.
-
result = (path.strip.split(SLASH, -1).map do |segment|
-
Addressable::URI.normalize_component(
-
segment,
-
Addressable::URI::CharacterClasses::PCHAR
-
)
-
end).join(SLASH)
-
-
result = URI.normalize_path(result)
-
if result.empty? &&
-
["http", "https", "ftp", "tftp"].include?(self.normalized_scheme)
-
result = SLASH
-
end
-
result
-
end)
-
end
-
-
##
-
# Sets the path component for this URI.
-
#
-
# @param [String, #to_str] new_path The new path component.
-
1
def path=(new_path)
-
if new_path && !new_path.respond_to?(:to_str)
-
raise TypeError, "Can't convert #{new_path.class} into String."
-
end
-
@path = (new_path || EMPTY_STR).to_str
-
if !@path.empty? && @path[0..0] != SLASH && host != nil
-
@path = "/#{@path}"
-
end
-
-
# Reset dependant values
-
@normalized_path = nil
-
@uri_string = nil
-
@hash = nil
-
end
-
-
##
-
# The basename, if any, of the file in the path component.
-
#
-
# @return [String] The path's basename.
-
1
def basename
-
# Path cannot be nil
-
return File.basename(self.path).gsub(/;[^\/]*$/, EMPTY_STR)
-
end
-
-
##
-
# The extname, if any, of the file in the path component.
-
# Empty string if there is no extension.
-
#
-
# @return [String] The path's extname.
-
1
def extname
-
return nil unless self.path
-
return File.extname(self.basename)
-
end
-
-
##
-
# The query component for this URI.
-
#
-
# @return [String] The query component.
-
1
def query
-
return instance_variable_defined?(:@query) ? @query : nil
-
end
-
-
##
-
# The query component for this URI, normalized.
-
#
-
# @return [String] The query component, normalized.
-
1
def normalized_query(*flags)
-
modified_query_class = Addressable::URI::CharacterClasses::QUERY.dup
-
# Make sure possible key-value pair delimiters are escaped.
-
modified_query_class.sub!("\\&", "").sub!("\\;", "")
-
pairs = (self.query || "").split("&", -1)
-
pairs.sort! if flags.include?(:sorted)
-
component = (pairs.map do |pair|
-
Addressable::URI.normalize_component(pair, modified_query_class, "+")
-
end).join("&")
-
component == "" ? nil : component
-
end
-
-
##
-
# Sets the query component for this URI.
-
#
-
# @param [String, #to_str] new_query The new query component.
-
1
def query=(new_query)
-
if new_query && !new_query.respond_to?(:to_str)
-
raise TypeError, "Can't convert #{new_query.class} into String."
-
end
-
@query = new_query ? new_query.to_str : nil
-
-
# Reset dependant values
-
@normalized_query = nil
-
@uri_string = nil
-
@hash = nil
-
end
-
-
##
-
# Converts the query component to a Hash value.
-
#
-
# @param [Class] return_type The return type desired. Value must be either
-
# `Hash` or `Array`.
-
#
-
# @return [Hash, Array] The query string parsed as a Hash or Array object.
-
#
-
# @example
-
# Addressable::URI.parse("?one=1&two=2&three=3").query_values
-
# #=> {"one" => "1", "two" => "2", "three" => "3"}
-
# Addressable::URI.parse("?one=two&one=three").query_values(Array)
-
# #=> [["one", "two"], ["one", "three"]]
-
# Addressable::URI.parse("?one=two&one=three").query_values(Hash)
-
# #=> {"one" => "three"}
-
1
def query_values(return_type=Hash)
-
empty_accumulator = Array == return_type ? [] : {}
-
if return_type != Hash && return_type != Array
-
raise ArgumentError, "Invalid return type. Must be Hash or Array."
-
end
-
return nil if self.query == nil
-
split_query = (self.query.split("&").map do |pair|
-
pair.split("=", 2) if pair && !pair.empty?
-
end).compact
-
return split_query.inject(empty_accumulator.dup) do |accu, pair|
-
# I'd rather use key/value identifiers instead of array lookups,
-
# but in this case I really want to maintain the exact pair structure,
-
# so it's best to make all changes in-place.
-
pair[0] = URI.unencode_component(pair[0])
-
if pair[1].respond_to?(:to_str)
-
# I loathe the fact that I have to do this. Stupid HTML 4.01.
-
# Treating '+' as a space was just an unbelievably bad idea.
-
# There was nothing wrong with '%20'!
-
# If it ain't broke, don't fix it!
-
pair[1] = URI.unencode_component(pair[1].to_str.gsub(/\+/, " "))
-
end
-
if return_type == Hash
-
accu[pair[0]] = pair[1]
-
else
-
accu << pair
-
end
-
accu
-
end
-
end
-
-
##
-
# Sets the query component for this URI from a Hash object.
-
# An empty Hash or Array will result in an empty query string.
-
#
-
# @param [Hash, #to_hash, Array] new_query_values The new query values.
-
#
-
# @example
-
# uri.query_values = {:a => "a", :b => ["c", "d", "e"]}
-
# uri.query
-
# # => "a=a&b=c&b=d&b=e"
-
# uri.query_values = [['a', 'a'], ['b', 'c'], ['b', 'd'], ['b', 'e']]
-
# uri.query
-
# # => "a=a&b=c&b=d&b=e"
-
# uri.query_values = [['a', 'a'], ['b', ['c', 'd', 'e']]]
-
# uri.query
-
# # => "a=a&b=c&b=d&b=e"
-
# uri.query_values = [['flag'], ['key', 'value']]
-
# uri.query
-
# # => "flag&key=value"
-
1
def query_values=(new_query_values)
-
if new_query_values == nil
-
self.query = nil
-
return nil
-
end
-
-
if !new_query_values.is_a?(Array)
-
if !new_query_values.respond_to?(:to_hash)
-
raise TypeError,
-
"Can't convert #{new_query_values.class} into Hash."
-
end
-
new_query_values = new_query_values.to_hash
-
new_query_values = new_query_values.map do |key, value|
-
key = key.to_s if key.kind_of?(Symbol)
-
[key, value]
-
end
-
# Useful default for OAuth and caching.
-
# Only to be used for non-Array inputs. Arrays should preserve order.
-
new_query_values.sort!
-
end
-
-
# new_query_values have form [['key1', 'value1'], ['key2', 'value2']]
-
buffer = ""
-
new_query_values.each do |key, value|
-
encoded_key = URI.encode_component(
-
key, CharacterClasses::UNRESERVED
-
)
-
if value == nil
-
buffer << "#{encoded_key}&"
-
elsif value.kind_of?(Array)
-
value.each do |sub_value|
-
encoded_value = URI.encode_component(
-
sub_value, CharacterClasses::UNRESERVED
-
)
-
buffer << "#{encoded_key}=#{encoded_value}&"
-
end
-
else
-
encoded_value = URI.encode_component(
-
value, CharacterClasses::UNRESERVED
-
)
-
buffer << "#{encoded_key}=#{encoded_value}&"
-
end
-
end
-
self.query = buffer.chop
-
end
-
-
##
-
# The HTTP request URI for this URI. This is the path and the
-
# query string.
-
#
-
# @return [String] The request URI required for an HTTP request.
-
1
def request_uri
-
return nil if self.absolute? && self.scheme !~ /^https?$/
-
return (
-
(!self.path.empty? ? self.path : SLASH) +
-
(self.query ? "?#{self.query}" : EMPTY_STR)
-
)
-
end
-
-
##
-
# Sets the HTTP request URI for this URI.
-
#
-
# @param [String, #to_str] new_request_uri The new HTTP request URI.
-
1
def request_uri=(new_request_uri)
-
if !new_request_uri.respond_to?(:to_str)
-
raise TypeError, "Can't convert #{new_request_uri.class} into String."
-
end
-
if self.absolute? && self.scheme !~ /^https?$/
-
raise InvalidURIError,
-
"Cannot set an HTTP request URI for a non-HTTP URI."
-
end
-
new_request_uri = new_request_uri.to_str
-
path_component = new_request_uri[/^([^\?]*)\?(?:.*)$/, 1]
-
query_component = new_request_uri[/^(?:[^\?]*)\?(.*)$/, 1]
-
path_component = path_component.to_s
-
path_component = (!path_component.empty? ? path_component : SLASH)
-
self.path = path_component
-
self.query = query_component
-
-
# Reset dependant values
-
@uri_string = nil
-
@hash = nil
-
end
-
-
##
-
# The fragment component for this URI.
-
#
-
# @return [String] The fragment component.
-
1
def fragment
-
return instance_variable_defined?(:@fragment) ? @fragment : nil
-
end
-
-
##
-
# The fragment component for this URI, normalized.
-
#
-
# @return [String] The fragment component, normalized.
-
1
def normalized_fragment
-
self.fragment && @normalized_fragment ||= (begin
-
component = Addressable::URI.normalize_component(
-
self.fragment,
-
Addressable::URI::CharacterClasses::FRAGMENT
-
)
-
component == "" ? nil : component
-
end)
-
end
-
-
##
-
# Sets the fragment component for this URI.
-
#
-
# @param [String, #to_str] new_fragment The new fragment component.
-
1
def fragment=(new_fragment)
-
if new_fragment && !new_fragment.respond_to?(:to_str)
-
raise TypeError, "Can't convert #{new_fragment.class} into String."
-
end
-
@fragment = new_fragment ? new_fragment.to_str : nil
-
-
# Reset dependant values
-
@normalized_fragment = nil
-
@uri_string = nil
-
@hash = nil
-
-
# Ensure we haven't created an invalid URI
-
validate()
-
end
-
-
##
-
# Determines if the scheme indicates an IP-based protocol.
-
#
-
# @return [TrueClass, FalseClass]
-
# <code>true</code> if the scheme indicates an IP-based protocol.
-
# <code>false</code> otherwise.
-
1
def ip_based?
-
if self.scheme
-
return URI.ip_based_schemes.include?(
-
self.scheme.strip.downcase)
-
end
-
return false
-
end
-
-
##
-
# Determines if the URI is relative.
-
#
-
# @return [TrueClass, FalseClass]
-
# <code>true</code> if the URI is relative. <code>false</code>
-
# otherwise.
-
1
def relative?
-
return self.scheme.nil?
-
end
-
-
##
-
# Determines if the URI is absolute.
-
#
-
# @return [TrueClass, FalseClass]
-
# <code>true</code> if the URI is absolute. <code>false</code>
-
# otherwise.
-
1
def absolute?
-
return !relative?
-
end
-
-
##
-
# Joins two URIs together.
-
#
-
# @param [String, Addressable::URI, #to_str] The URI to join with.
-
#
-
# @return [Addressable::URI] The joined URI.
-
1
def join(uri)
-
if !uri.respond_to?(:to_str)
-
raise TypeError, "Can't convert #{uri.class} into String."
-
end
-
if !uri.kind_of?(URI)
-
# Otherwise, convert to a String, then parse.
-
uri = URI.parse(uri.to_str)
-
end
-
if uri.to_s.empty?
-
return self.dup
-
end
-
-
joined_scheme = nil
-
joined_user = nil
-
joined_password = nil
-
joined_host = nil
-
joined_port = nil
-
joined_path = nil
-
joined_query = nil
-
joined_fragment = nil
-
-
# Section 5.2.2 of RFC 3986
-
if uri.scheme != nil
-
joined_scheme = uri.scheme
-
joined_user = uri.user
-
joined_password = uri.password
-
joined_host = uri.host
-
joined_port = uri.port
-
joined_path = URI.normalize_path(uri.path)
-
joined_query = uri.query
-
else
-
if uri.authority != nil
-
joined_user = uri.user
-
joined_password = uri.password
-
joined_host = uri.host
-
joined_port = uri.port
-
joined_path = URI.normalize_path(uri.path)
-
joined_query = uri.query
-
else
-
if uri.path == nil || uri.path.empty?
-
joined_path = self.path
-
if uri.query != nil
-
joined_query = uri.query
-
else
-
joined_query = self.query
-
end
-
else
-
if uri.path[0..0] == SLASH
-
joined_path = URI.normalize_path(uri.path)
-
else
-
base_path = self.path.dup
-
base_path = EMPTY_STR if base_path == nil
-
base_path = URI.normalize_path(base_path)
-
-
# Section 5.2.3 of RFC 3986
-
#
-
# Removes the right-most path segment from the base path.
-
if base_path =~ /\//
-
base_path.gsub!(/\/[^\/]+$/, SLASH)
-
else
-
base_path = EMPTY_STR
-
end
-
-
# If the base path is empty and an authority segment has been
-
# defined, use a base path of SLASH
-
if base_path.empty? && self.authority != nil
-
base_path = SLASH
-
end
-
-
joined_path = URI.normalize_path(base_path + uri.path)
-
end
-
joined_query = uri.query
-
end
-
joined_user = self.user
-
joined_password = self.password
-
joined_host = self.host
-
joined_port = self.port
-
end
-
joined_scheme = self.scheme
-
end
-
joined_fragment = uri.fragment
-
-
return self.class.new(
-
:scheme => joined_scheme,
-
:user => joined_user,
-
:password => joined_password,
-
:host => joined_host,
-
:port => joined_port,
-
:path => joined_path,
-
:query => joined_query,
-
:fragment => joined_fragment
-
)
-
end
-
1
alias_method :+, :join
-
-
##
-
# Destructive form of <code>join</code>.
-
#
-
# @param [String, Addressable::URI, #to_str] The URI to join with.
-
#
-
# @return [Addressable::URI] The joined URI.
-
#
-
# @see Addressable::URI#join
-
1
def join!(uri)
-
replace_self(self.join(uri))
-
end
-
-
##
-
# Merges a URI with a <code>Hash</code> of components.
-
# This method has different behavior from <code>join</code>. Any
-
# components present in the <code>hash</code> parameter will override the
-
# original components. The path component is not treated specially.
-
#
-
# @param [Hash, Addressable::URI, #to_hash] The components to merge with.
-
#
-
# @return [Addressable::URI] The merged URI.
-
#
-
# @see Hash#merge
-
1
def merge(hash)
-
if !hash.respond_to?(:to_hash)
-
raise TypeError, "Can't convert #{hash.class} into Hash."
-
end
-
hash = hash.to_hash
-
-
if hash.has_key?(:authority)
-
if (hash.keys & [:userinfo, :user, :password, :host, :port]).any?
-
raise ArgumentError,
-
"Cannot specify both an authority and any of the components " +
-
"within the authority."
-
end
-
end
-
if hash.has_key?(:userinfo)
-
if (hash.keys & [:user, :password]).any?
-
raise ArgumentError,
-
"Cannot specify both a userinfo and either the user or password."
-
end
-
end
-
-
uri = self.class.new
-
uri.defer_validation do
-
# Bunch of crazy logic required because of the composite components
-
# like userinfo and authority.
-
uri.scheme =
-
hash.has_key?(:scheme) ? hash[:scheme] : self.scheme
-
if hash.has_key?(:authority)
-
uri.authority =
-
hash.has_key?(:authority) ? hash[:authority] : self.authority
-
end
-
if hash.has_key?(:userinfo)
-
uri.userinfo =
-
hash.has_key?(:userinfo) ? hash[:userinfo] : self.userinfo
-
end
-
if !hash.has_key?(:userinfo) && !hash.has_key?(:authority)
-
uri.user =
-
hash.has_key?(:user) ? hash[:user] : self.user
-
uri.password =
-
hash.has_key?(:password) ? hash[:password] : self.password
-
end
-
if !hash.has_key?(:authority)
-
uri.host =
-
hash.has_key?(:host) ? hash[:host] : self.host
-
uri.port =
-
hash.has_key?(:port) ? hash[:port] : self.port
-
end
-
uri.path =
-
hash.has_key?(:path) ? hash[:path] : self.path
-
uri.query =
-
hash.has_key?(:query) ? hash[:query] : self.query
-
uri.fragment =
-
hash.has_key?(:fragment) ? hash[:fragment] : self.fragment
-
end
-
-
return uri
-
end
-
-
##
-
# Destructive form of <code>merge</code>.
-
#
-
# @param [Hash, Addressable::URI, #to_hash] The components to merge with.
-
#
-
# @return [Addressable::URI] The merged URI.
-
#
-
# @see Addressable::URI#merge
-
1
def merge!(uri)
-
replace_self(self.merge(uri))
-
end
-
-
##
-
# Returns the shortest normalized relative form of this URI that uses the
-
# supplied URI as a base for resolution. Returns an absolute URI if
-
# necessary. This is effectively the opposite of <code>route_to</code>.
-
#
-
# @param [String, Addressable::URI, #to_str] uri The URI to route from.
-
#
-
# @return [Addressable::URI]
-
# The normalized relative URI that is equivalent to the original URI.
-
1
def route_from(uri)
-
uri = URI.parse(uri).normalize
-
normalized_self = self.normalize
-
if normalized_self.relative?
-
raise ArgumentError, "Expected absolute URI, got: #{self.to_s}"
-
end
-
if uri.relative?
-
raise ArgumentError, "Expected absolute URI, got: #{uri.to_s}"
-
end
-
if normalized_self == uri
-
return Addressable::URI.parse("##{normalized_self.fragment}")
-
end
-
components = normalized_self.to_hash
-
if normalized_self.scheme == uri.scheme
-
components[:scheme] = nil
-
if normalized_self.authority == uri.authority
-
components[:user] = nil
-
components[:password] = nil
-
components[:host] = nil
-
components[:port] = nil
-
if normalized_self.path == uri.path
-
components[:path] = nil
-
if normalized_self.query == uri.query
-
components[:query] = nil
-
end
-
else
-
if uri.path != SLASH and components[:path]
-
self_splitted_path = split_path(components[:path])
-
uri_splitted_path = split_path(uri.path)
-
self_dir = self_splitted_path.shift
-
uri_dir = uri_splitted_path.shift
-
while !self_splitted_path.empty? && !uri_splitted_path.empty? and self_dir == uri_dir
-
self_dir = self_splitted_path.shift
-
uri_dir = uri_splitted_path.shift
-
end
-
components[:path] = (uri_splitted_path.fill('..') + [self_dir] + self_splitted_path).join(SLASH)
-
end
-
end
-
end
-
end
-
# Avoid network-path references.
-
if components[:host] != nil
-
components[:scheme] = normalized_self.scheme
-
end
-
return Addressable::URI.new(
-
:scheme => components[:scheme],
-
:user => components[:user],
-
:password => components[:password],
-
:host => components[:host],
-
:port => components[:port],
-
:path => components[:path],
-
:query => components[:query],
-
:fragment => components[:fragment]
-
)
-
end
-
-
##
-
# Returns the shortest normalized relative form of the supplied URI that
-
# uses this URI as a base for resolution. Returns an absolute URI if
-
# necessary. This is effectively the opposite of <code>route_from</code>.
-
#
-
# @param [String, Addressable::URI, #to_str] uri The URI to route to.
-
#
-
# @return [Addressable::URI]
-
# The normalized relative URI that is equivalent to the supplied URI.
-
1
def route_to(uri)
-
return URI.parse(uri).route_from(self)
-
end
-
-
##
-
# Returns a normalized URI object.
-
#
-
# NOTE: This method does not attempt to fully conform to specifications.
-
# It exists largely to correct other people's failures to read the
-
# specifications, and also to deal with caching issues since several
-
# different URIs may represent the same resource and should not be
-
# cached multiple times.
-
#
-
# @return [Addressable::URI] The normalized URI.
-
1
def normalize
-
# This is a special exception for the frequently misused feed
-
# URI scheme.
-
if normalized_scheme == "feed"
-
if self.to_s =~ /^feed:\/*http:\/*/
-
return URI.parse(
-
self.to_s[/^feed:\/*(http:\/*.*)/, 1]
-
).normalize
-
end
-
end
-
-
return self.class.new(
-
:scheme => normalized_scheme,
-
:authority => normalized_authority,
-
:path => normalized_path,
-
:query => normalized_query,
-
:fragment => normalized_fragment
-
)
-
end
-
-
##
-
# Destructively normalizes this URI object.
-
#
-
# @return [Addressable::URI] The normalized URI.
-
#
-
# @see Addressable::URI#normalize
-
1
def normalize!
-
replace_self(self.normalize)
-
end
-
-
##
-
# Creates a URI suitable for display to users. If semantic attacks are
-
# likely, the application should try to detect these and warn the user.
-
# See <a href="http://www.ietf.org/rfc/rfc3986.txt">RFC 3986</a>,
-
# section 7.6 for more information.
-
#
-
# @return [Addressable::URI] A URI suitable for display purposes.
-
1
def display_uri
-
display_uri = self.normalize
-
display_uri.host = ::Addressable::IDNA.to_unicode(display_uri.host)
-
return display_uri
-
end
-
-
##
-
# Returns <code>true</code> if the URI objects are equal. This method
-
# normalizes both URIs before doing the comparison, and allows comparison
-
# against <code>Strings</code>.
-
#
-
# @param [Object] uri The URI to compare.
-
#
-
# @return [TrueClass, FalseClass]
-
# <code>true</code> if the URIs are equivalent, <code>false</code>
-
# otherwise.
-
1
def ===(uri)
-
if uri.respond_to?(:normalize)
-
uri_string = uri.normalize.to_s
-
else
-
begin
-
uri_string = ::Addressable::URI.parse(uri).normalize.to_s
-
rescue InvalidURIError, TypeError
-
return false
-
end
-
end
-
return self.normalize.to_s == uri_string
-
end
-
-
##
-
# Returns <code>true</code> if the URI objects are equal. This method
-
# normalizes both URIs before doing the comparison.
-
#
-
# @param [Object] uri The URI to compare.
-
#
-
# @return [TrueClass, FalseClass]
-
# <code>true</code> if the URIs are equivalent, <code>false</code>
-
# otherwise.
-
1
def ==(uri)
-
return false unless uri.kind_of?(URI)
-
return self.normalize.to_s == uri.normalize.to_s
-
end
-
-
##
-
# Returns <code>true</code> if the URI objects are equal. This method
-
# does NOT normalize either URI before doing the comparison.
-
#
-
# @param [Object] uri The URI to compare.
-
#
-
# @return [TrueClass, FalseClass]
-
# <code>true</code> if the URIs are equivalent, <code>false</code>
-
# otherwise.
-
1
def eql?(uri)
-
return false unless uri.kind_of?(URI)
-
return self.to_s == uri.to_s
-
end
-
-
##
-
# A hash value that will make a URI equivalent to its normalized
-
# form.
-
#
-
# @return [Integer] A hash of the URI.
-
1
def hash
-
return @hash ||= (self.to_s.hash * -1)
-
end
-
-
##
-
# Clones the URI object.
-
#
-
# @return [Addressable::URI] The cloned URI.
-
1
def dup
-
duplicated_uri = self.class.new(
-
:scheme => self.scheme ? self.scheme.dup : nil,
-
:user => self.user ? self.user.dup : nil,
-
:password => self.password ? self.password.dup : nil,
-
:host => self.host ? self.host.dup : nil,
-
:port => self.port,
-
:path => self.path ? self.path.dup : nil,
-
:query => self.query ? self.query.dup : nil,
-
:fragment => self.fragment ? self.fragment.dup : nil
-
)
-
return duplicated_uri
-
end
-
-
##
-
# Omits components from a URI.
-
#
-
# @param [Symbol] *components The components to be omitted.
-
#
-
# @return [Addressable::URI] The URI with components omitted.
-
#
-
# @example
-
# uri = Addressable::URI.parse("http://example.com/path?query")
-
# #=> #<Addressable::URI:0xcc5e7a URI:http://example.com/path?query>
-
# uri.omit(:scheme, :authority)
-
# #=> #<Addressable::URI:0xcc4d86 URI:/path?query>
-
1
def omit(*components)
-
invalid_components = components - [
-
:scheme, :user, :password, :userinfo, :host, :port, :authority,
-
:path, :query, :fragment
-
]
-
unless invalid_components.empty?
-
raise ArgumentError,
-
"Invalid component names: #{invalid_components.inspect}."
-
end
-
duplicated_uri = self.dup
-
duplicated_uri.defer_validation do
-
components.each do |component|
-
duplicated_uri.send((component.to_s + "=").to_sym, nil)
-
end
-
duplicated_uri.user = duplicated_uri.normalized_user
-
end
-
duplicated_uri
-
end
-
-
##
-
# Destructive form of omit.
-
#
-
# @param [Symbol] *components The components to be omitted.
-
#
-
# @return [Addressable::URI] The URI with components omitted.
-
#
-
# @see Addressable::URI#omit
-
1
def omit!(*components)
-
replace_self(self.omit(*components))
-
end
-
-
##
-
# Determines if the URI is an empty string.
-
#
-
# @return [TrueClass, FalseClass]
-
# Returns <code>true</code> if empty, <code>false</code> otherwise.
-
1
def empty?
-
return self.to_s.empty?
-
end
-
-
##
-
# Converts the URI to a <code>String</code>.
-
#
-
# @return [String] The URI's <code>String</code> representation.
-
1
def to_s
-
if self.scheme == nil && self.path != nil && !self.path.empty? &&
-
self.path =~ NORMPATH
-
raise InvalidURIError,
-
"Cannot assemble URI string with ambiguous path: '#{self.path}'"
-
end
-
@uri_string ||= (begin
-
uri_string = ""
-
uri_string << "#{self.scheme}:" if self.scheme != nil
-
uri_string << "//#{self.authority}" if self.authority != nil
-
uri_string << self.path.to_s
-
uri_string << "?#{self.query}" if self.query != nil
-
uri_string << "##{self.fragment}" if self.fragment != nil
-
if uri_string.respond_to?(:force_encoding)
-
uri_string.force_encoding(Encoding::UTF_8)
-
end
-
uri_string
-
end)
-
end
-
-
##
-
# URI's are glorified <code>Strings</code>. Allow implicit conversion.
-
1
alias_method :to_str, :to_s
-
-
##
-
# Returns a Hash of the URI components.
-
#
-
# @return [Hash] The URI as a <code>Hash</code> of components.
-
1
def to_hash
-
return {
-
:scheme => self.scheme,
-
:user => self.user,
-
:password => self.password,
-
:host => self.host,
-
:port => self.port,
-
:path => self.path,
-
:query => self.query,
-
:fragment => self.fragment
-
}
-
end
-
-
##
-
# Returns a <code>String</code> representation of the URI object's state.
-
#
-
# @return [String] The URI object's state, as a <code>String</code>.
-
1
def inspect
-
sprintf("#<%s:%#0x URI:%s>", URI.to_s, self.object_id, self.to_s)
-
end
-
-
##
-
# This method allows you to make several changes to a URI simultaneously,
-
# which separately would cause validation errors, but in conjunction,
-
# are valid. The URI will be revalidated as soon as the entire block has
-
# been executed.
-
#
-
# @param [Proc] block
-
# A set of operations to perform on a given URI.
-
1
def defer_validation(&block)
-
raise LocalJumpError, "No block given." unless block
-
@validation_deferred = true
-
block.call()
-
@validation_deferred = false
-
validate
-
return nil
-
end
-
-
1
private
-
1
SELF_REF = '.'
-
1
PARENT = '..'
-
-
1
RULE_2A = /\/\.\/|\/\.$/
-
1
RULE_2B_2C = /\/([^\/]*)\/\.\.\/|\/([^\/]*)\/\.\.$/
-
1
RULE_2D = /^\.\.?\/?/
-
1
RULE_PREFIXED_PARENT = /^\/\.\.?\/|^(\/\.\.?)+\/?$/
-
-
##
-
# Resolves paths to their simplest form.
-
#
-
# @param [String] path The path to normalize.
-
#
-
# @return [String] The normalized path.
-
1
def self.normalize_path(path)
-
# Section 5.2.4 of RFC 3986
-
-
return nil if path.nil?
-
normalized_path = path.dup
-
begin
-
mod = nil
-
mod ||= normalized_path.gsub!(RULE_2A, SLASH)
-
-
pair = normalized_path.match(RULE_2B_2C)
-
parent, current = pair[1], pair[2] if pair
-
if pair && ((parent != SELF_REF && parent != PARENT) ||
-
(current != SELF_REF && current != PARENT))
-
mod ||= normalized_path.gsub!(
-
Regexp.new(
-
"/#{Regexp.escape(parent.to_s)}/\\.\\./|" +
-
"(/#{Regexp.escape(current.to_s)}/\\.\\.$)"
-
), SLASH
-
)
-
end
-
-
mod ||= normalized_path.gsub!(RULE_2D, EMPTY_STR)
-
# Non-standard, removes prefixed dotted segments from path.
-
mod ||= normalized_path.gsub!(RULE_PREFIXED_PARENT, SLASH)
-
end until mod.nil?
-
-
return normalized_path
-
end
-
-
##
-
# Ensures that the URI is valid.
-
1
def validate
-
return if !!@validation_deferred
-
if self.scheme != nil && self.ip_based? &&
-
(self.host == nil || self.host.empty?) &&
-
(self.path == nil || self.path.empty?)
-
raise InvalidURIError,
-
"Absolute URI missing hierarchical segment: '#{self.to_s}'"
-
end
-
if self.host == nil
-
if self.port != nil ||
-
self.user != nil ||
-
self.password != nil
-
raise InvalidURIError, "Hostname not supplied: '#{self.to_s}'"
-
end
-
end
-
if self.path != nil && !self.path.empty? && self.path[0..0] != SLASH &&
-
self.authority != nil
-
raise InvalidURIError,
-
"Cannot have a relative path with an authority set: '#{self.to_s}'"
-
end
-
return nil
-
end
-
-
##
-
# Replaces the internal state of self with the specified URI's state.
-
# Used in destructive operations to avoid massive code repetition.
-
#
-
# @param [Addressable::URI] uri The URI to replace <code>self</code> with.
-
#
-
# @return [Addressable::URI] <code>self</code>.
-
1
def replace_self(uri)
-
# Reset dependant values
-
instance_variables.each do |var|
-
instance_variable_set(var, nil)
-
end
-
-
@scheme = uri.scheme
-
@user = uri.user
-
@password = uri.password
-
@host = uri.host
-
@port = uri.port
-
@path = uri.path
-
@query = uri.query
-
@fragment = uri.fragment
-
return self
-
end
-
-
##
-
# Splits path string with "/"(slash).
-
# It is considered that there is empty string after last slash when
-
# path ends with slash.
-
#
-
# @param [String] path The path to split.
-
#
-
# @return [Array<String>] An array of parts of path.
-
1
def split_path(path)
-
splitted = path.split(SLASH)
-
splitted << EMPTY_STR if path.end_with? SLASH
-
splitted
-
end
-
end
-
end
-
# encoding:utf-8
-
#--
-
# Copyright (C) 2006-2013 Bob Aman
-
#
-
# Licensed under the Apache License, Version 2.0 (the "License");
-
# you may not use this file except in compliance with the License.
-
# You may obtain a copy of the License at
-
#
-
# http://www.apache.org/licenses/LICENSE-2.0
-
#
-
# Unless required by applicable law or agreed to in writing, software
-
# distributed under the License is distributed on an "AS IS" BASIS,
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-
# See the License for the specific language governing permissions and
-
# limitations under the License.
-
#++
-
-
-
# Used to prevent the class/module from being loaded more than once
-
1
if !defined?(Addressable::VERSION)
-
1
module Addressable
-
1
module VERSION
-
1
MAJOR = 2
-
1
MINOR = 3
-
1
TINY = 6
-
-
1
STRING = [MAJOR, MINOR, TINY].join('.')
-
end
-
end
-
end
-
1
require 'arel/crud'
-
1
require 'arel/factory_methods'
-
-
1
require 'arel/expressions'
-
1
require 'arel/predications'
-
1
require 'arel/window_predications'
-
1
require 'arel/math'
-
1
require 'arel/alias_predication'
-
1
require 'arel/order_predications'
-
1
require 'arel/table'
-
1
require 'arel/attributes'
-
1
require 'arel/compatibility/wheres'
-
-
#### these are deprecated
-
1
require 'arel/expression'
-
####
-
-
1
require 'arel/visitors'
-
-
1
require 'arel/tree_manager'
-
1
require 'arel/insert_manager'
-
1
require 'arel/select_manager'
-
1
require 'arel/update_manager'
-
1
require 'arel/delete_manager'
-
1
require 'arel/nodes'
-
-
-
#### these are deprecated
-
1
require 'arel/deprecated'
-
1
require 'arel/sql/engine'
-
1
require 'arel/sql_literal'
-
####
-
-
1
module Arel
-
1
VERSION = '5.0.1'
-
-
1
def self.sql raw_sql
-
15
Arel::Nodes::SqlLiteral.new raw_sql
-
end
-
-
1
def self.star
-
13
sql '*'
-
end
-
## Convenience Alias
-
1
Node = Arel::Nodes::Node
-
end
-
1
module Arel
-
1
module AliasPredication
-
1
def as other
-
Nodes::As.new self, Nodes::SqlLiteral.new(other)
-
end
-
end
-
end
-
1
require 'arel/attributes/attribute'
-
-
1
module Arel
-
1
module Attributes
-
###
-
# Factory method to wrap a raw database +column+ to an Arel Attribute.
-
1
def self.for column
-
case column.type
-
when :string, :text, :binary then String
-
when :integer then Integer
-
when :float then Float
-
when :decimal then Decimal
-
when :date, :datetime, :timestamp, :time then Time
-
when :boolean then Boolean
-
else
-
Undefined
-
end
-
end
-
end
-
end
-
1
module Arel
-
1
module Attributes
-
1
class Attribute < Struct.new :relation, :name
-
1
include Arel::Expressions
-
1
include Arel::Predications
-
1
include Arel::AliasPredication
-
1
include Arel::OrderPredications
-
1
include Arel::Math
-
-
###
-
# Create a node for lowering this attribute
-
1
def lower
-
relation.lower self
-
end
-
end
-
-
1
class String < Attribute; end
-
1
class Time < Attribute; end
-
1
class Boolean < Attribute; end
-
1
class Decimal < Attribute; end
-
1
class Float < Attribute; end
-
1
class Integer < Attribute; end
-
1
class Undefined < Attribute; end
-
end
-
-
1
Attribute = Attributes::Attribute
-
end
-
1
module Arel
-
1
module Compatibility # :nodoc:
-
1
class Wheres # :nodoc:
-
1
include Enumerable
-
-
1
module Value # :nodoc:
-
1
attr_accessor :visitor
-
1
def value
-
visitor.accept self
-
end
-
-
1
def name
-
super.to_sym
-
end
-
end
-
-
1
def initialize engine, collection
-
@engine = engine
-
@collection = collection
-
end
-
-
1
def each
-
to_sql = Visitors::ToSql.new @engine
-
-
@collection.each { |c|
-
c.extend(Value)
-
c.visitor = to_sql
-
yield c
-
}
-
end
-
end
-
end
-
end
-
1
module Arel
-
###
-
# FIXME hopefully we can remove this
-
1
module Crud
-
1
def compile_update values, pk
-
um = UpdateManager.new @engine
-
-
if Nodes::SqlLiteral === values
-
relation = @ctx.from
-
else
-
relation = values.first.first.relation
-
end
-
um.key = pk
-
um.table relation
-
um.set values
-
um.take @ast.limit.expr if @ast.limit
-
um.order(*@ast.orders)
-
um.wheres = @ctx.wheres
-
um
-
end
-
-
1
def compile_insert values
-
im = create_insert
-
im.insert values
-
im
-
end
-
-
1
def create_insert
-
6
InsertManager.new @engine
-
end
-
-
1
def compile_delete
-
dm = DeleteManager.new @engine
-
dm.wheres = @ctx.wheres
-
dm.from @ctx.froms
-
dm
-
end
-
-
end
-
end
-
1
module Arel
-
1
class DeleteManager < Arel::TreeManager
-
1
def initialize engine
-
super
-
@ast = Nodes::DeleteStatement.new
-
@ctx = @ast
-
end
-
-
1
def from relation
-
@ast.relation = relation
-
self
-
end
-
-
1
def wheres= list
-
@ast.wheres = list
-
end
-
end
-
end
-
1
module Arel
-
1
InnerJoin = Nodes::InnerJoin
-
1
OuterJoin = Nodes::OuterJoin
-
end
-
1
module Arel
-
1
module Expression
-
1
include Arel::OrderPredications
-
end
-
end
-
1
module Arel
-
1
module Expressions
-
1
def count distinct = false
-
2
Nodes::Count.new [self], distinct
-
end
-
-
1
def sum
-
Nodes::Sum.new [self], Nodes::SqlLiteral.new('sum_id')
-
end
-
-
1
def maximum
-
Nodes::Max.new [self], Nodes::SqlLiteral.new('max_id')
-
end
-
-
1
def minimum
-
Nodes::Min.new [self], Nodes::SqlLiteral.new('min_id')
-
end
-
-
1
def average
-
Nodes::Avg.new [self], Nodes::SqlLiteral.new('avg_id')
-
end
-
-
1
def extract field
-
Nodes::Extract.new [self], field
-
end
-
end
-
end
-
1
module Arel
-
###
-
# Methods for creating various nodes
-
1
module FactoryMethods
-
1
def create_true
-
Arel::Nodes::True.new
-
end
-
-
1
def create_false
-
Arel::Nodes::False.new
-
end
-
-
1
def create_table_alias relation, name
-
Nodes::TableAlias.new(relation, name)
-
end
-
-
1
def create_join to, constraint = nil, klass = Nodes::InnerJoin
-
4
klass.new(to, constraint)
-
end
-
-
1
def create_string_join to
-
create_join to, nil, Nodes::StringJoin
-
end
-
-
1
def create_and clauses
-
Nodes::And.new clauses
-
end
-
-
1
def create_on expr
-
4
Nodes::On.new expr
-
end
-
-
1
def grouping expr
-
Nodes::Grouping.new expr
-
end
-
-
###
-
# Create a LOWER() function
-
1
def lower column
-
Nodes::NamedFunction.new 'LOWER', [column]
-
end
-
end
-
end
-
1
module Arel
-
1
class InsertManager < Arel::TreeManager
-
1
def initialize engine
-
6
super
-
6
@ast = Nodes::InsertStatement.new
-
end
-
-
1
def into table
-
6
@ast.relation = table
-
6
self
-
end
-
-
1
def columns; @ast.columns end
-
1
def values= val; @ast.values = val; end
-
-
1
def insert fields
-
6
return if fields.empty?
-
-
6
if String === fields
-
@ast.values = SqlLiteral.new(fields)
-
else
-
6
@ast.relation ||= fields.first.first.relation
-
-
6
values = []
-
-
6
fields.each do |column, value|
-
24
@ast.columns << column
-
24
values << value
-
end
-
6
@ast.values = create_values values, @ast.columns
-
end
-
end
-
-
1
def create_values values, columns
-
6
Nodes::Values.new values, columns
-
end
-
end
-
end
-
1
module Arel
-
1
module Math
-
1
def *(other)
-
Arel::Nodes::Multiplication.new(self, other)
-
end
-
-
1
def +(other)
-
Arel::Nodes::Grouping.new(Arel::Nodes::Addition.new(self, other))
-
end
-
-
1
def -(other)
-
Arel::Nodes::Grouping.new(Arel::Nodes::Subtraction.new(self, other))
-
end
-
-
1
def /(other)
-
Arel::Nodes::Division.new(self, other)
-
end
-
end
-
end
-
# node
-
1
require 'arel/nodes/node'
-
1
require 'arel/nodes/select_statement'
-
1
require 'arel/nodes/select_core'
-
1
require 'arel/nodes/insert_statement'
-
1
require 'arel/nodes/update_statement'
-
-
# terminal
-
-
1
require 'arel/nodes/terminal'
-
1
require 'arel/nodes/true'
-
1
require 'arel/nodes/false'
-
-
# unary
-
1
require 'arel/nodes/unary'
-
1
require 'arel/nodes/grouping'
-
1
require 'arel/nodes/ascending'
-
1
require 'arel/nodes/descending'
-
1
require 'arel/nodes/unqualified_column'
-
1
require 'arel/nodes/with'
-
-
# binary
-
1
require 'arel/nodes/binary'
-
1
require 'arel/nodes/equality'
-
1
require 'arel/nodes/in' # Why is this subclassed from equality?
-
1
require 'arel/nodes/join_source'
-
1
require 'arel/nodes/delete_statement'
-
1
require 'arel/nodes/table_alias'
-
1
require 'arel/nodes/infix_operation'
-
1
require 'arel/nodes/over'
-
-
# nary
-
1
require 'arel/nodes/and'
-
-
# function
-
# FIXME: Function + Alias can be rewritten as a Function and Alias node.
-
# We should make Function a Unary node and deprecate the use of "aliaz"
-
1
require 'arel/nodes/function'
-
1
require 'arel/nodes/count'
-
1
require 'arel/nodes/extract'
-
1
require 'arel/nodes/values'
-
1
require 'arel/nodes/named_function'
-
-
# windows
-
1
require 'arel/nodes/window'
-
-
# joins
-
1
require 'arel/nodes/inner_join'
-
1
require 'arel/nodes/outer_join'
-
1
require 'arel/nodes/string_join'
-
-
1
require 'arel/nodes/sql_literal'
-
1
module Arel
-
1
module Nodes
-
1
class And < Arel::Nodes::Node
-
1
attr_reader :children
-
-
1
def initialize children, right = nil
-
4
super()
-
4
unless Array === children
-
warn "(#{caller.first}) AND nodes should be created with a list"
-
children = [children, right]
-
end
-
4
@children = children
-
end
-
-
1
def left
-
children.first
-
end
-
-
1
def right
-
children[1]
-
end
-
-
1
def hash
-
children.hash
-
end
-
-
1
def eql? other
-
self.class == other.class &&
-
self.children == other.children
-
end
-
1
alias :== :eql?
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class Ascending < Ordering
-
-
1
def reverse
-
Descending.new(expr)
-
end
-
-
1
def direction
-
:asc
-
end
-
-
1
def ascending?
-
true
-
end
-
-
1
def descending?
-
false
-
end
-
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class Binary < Arel::Nodes::Node
-
1
attr_accessor :left, :right
-
-
1
def initialize left, right
-
59
super()
-
59
@left = left
-
59
@right = right
-
end
-
-
1
def initialize_copy other
-
super
-
@left = @left.clone if @left
-
@right = @right.clone if @right
-
end
-
-
1
def hash
-
4
[@left, @right].hash
-
end
-
-
1
def eql? other
-
self.class == other.class &&
-
16
self.left == other.left &&
-
self.right == other.right
-
end
-
1
alias :== :eql?
-
end
-
-
%w{
-
As
-
Assignment
-
Between
-
DoesNotMatch
-
GreaterThan
-
GreaterThanOrEqual
-
Join
-
LessThan
-
LessThanOrEqual
-
Matches
-
NotEqual
-
NotIn
-
Or
-
Union
-
UnionAll
-
Intersect
-
Except
-
1
}.each do |name|
-
17
const_set name, Class.new(Binary)
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class Count < Arel::Nodes::Function
-
1
def initialize expr, distinct = false, aliaz = nil
-
2
super(expr, aliaz)
-
2
@distinct = distinct
-
end
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class DeleteStatement < Arel::Nodes::Binary
-
1
alias :relation :left
-
1
alias :relation= :left=
-
1
alias :wheres :right
-
1
alias :wheres= :right=
-
-
1
def initialize relation = nil, wheres = []
-
super
-
end
-
-
1
def initialize_copy other
-
super
-
@right = @right.clone
-
end
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class Descending < Ordering
-
-
1
def reverse
-
Ascending.new(expr)
-
end
-
-
1
def direction
-
:desc
-
end
-
-
1
def ascending?
-
false
-
end
-
-
1
def descending?
-
true
-
end
-
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class Equality < Arel::Nodes::Binary
-
1
def operator; :== end
-
1
alias :operand1 :left
-
1
alias :operand2 :right
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
-
1
class Extract < Arel::Nodes::Unary
-
1
include Arel::Expression
-
1
include Arel::Predications
-
-
1
attr_accessor :field
-
1
attr_accessor :alias
-
-
1
def initialize expr, field, aliaz = nil
-
super(expr)
-
@field = field
-
@alias = aliaz && SqlLiteral.new(aliaz)
-
end
-
-
1
def as aliaz
-
self.alias = SqlLiteral.new(aliaz)
-
self
-
end
-
-
1
def hash
-
super ^ [@field, @alias].hash
-
end
-
-
1
def eql? other
-
super &&
-
self.field == other.field &&
-
self.alias == other.alias
-
end
-
1
alias :== :eql?
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class False < Arel::Nodes::Node
-
1
def hash
-
self.class.hash
-
end
-
-
1
def eql? other
-
self.class == other.class
-
end
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class Function < Arel::Nodes::Node
-
1
include Arel::Expression
-
1
include Arel::Predications
-
1
include Arel::WindowPredications
-
1
attr_accessor :expressions, :alias, :distinct
-
-
1
def initialize expr, aliaz = nil
-
2
super()
-
2
@expressions = expr
-
2
@alias = aliaz && SqlLiteral.new(aliaz)
-
2
@distinct = false
-
end
-
-
1
def as aliaz
-
self.alias = SqlLiteral.new(aliaz)
-
self
-
end
-
-
1
def hash
-
[@expressions, @alias, @distinct].hash
-
end
-
-
1
def eql? other
-
self.class == other.class &&
-
self.expressions == other.expressions &&
-
self.alias == other.alias &&
-
self.distinct == other.distinct
-
end
-
end
-
-
%w{
-
Sum
-
Exists
-
Max
-
Min
-
Avg
-
1
}.each do |name|
-
5
const_set(name, Class.new(Function))
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class Grouping < Unary
-
1
include Arel::Predications
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class In < Equality
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
-
1
class InfixOperation < Binary
-
1
include Arel::Expressions
-
1
include Arel::Predications
-
1
include Arel::OrderPredications
-
1
include Arel::AliasPredication
-
1
include Arel::Math
-
-
1
attr_reader :operator
-
-
1
def initialize operator, left, right
-
super(left, right)
-
@operator = operator
-
end
-
end
-
-
1
class Multiplication < InfixOperation
-
1
def initialize left, right
-
super(:*, left, right)
-
end
-
end
-
-
1
class Division < InfixOperation
-
1
def initialize left, right
-
super(:/, left, right)
-
end
-
end
-
-
1
class Addition < InfixOperation
-
1
def initialize left, right
-
super(:+, left, right)
-
end
-
end
-
-
1
class Subtraction < InfixOperation
-
1
def initialize left, right
-
super(:-, left, right)
-
end
-
end
-
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class InnerJoin < Arel::Nodes::Join
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class InsertStatement < Arel::Nodes::Node
-
1
attr_accessor :relation, :columns, :values
-
-
1
def initialize
-
6
super()
-
6
@relation = nil
-
6
@columns = []
-
6
@values = nil
-
end
-
-
1
def initialize_copy other
-
super
-
@columns = @columns.clone
-
@values = @values.clone if @values
-
end
-
-
1
def hash
-
[@relation, @columns, @values].hash
-
end
-
-
1
def eql? other
-
self.class == other.class &&
-
self.relation == other.relation &&
-
self.columns == other.columns &&
-
self.values == other.values
-
end
-
1
alias :== :eql?
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
###
-
# Class that represents a join source
-
#
-
# http://www.sqlite.org/syntaxdiagrams.html#join-source
-
-
1
class JoinSource < Arel::Nodes::Binary
-
1
def initialize single_source, joinop = []
-
15
super
-
end
-
-
1
def empty?
-
7
!left && right.empty?
-
end
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class NamedFunction < Arel::Nodes::Function
-
1
attr_accessor :name
-
-
1
def initialize name, expr, aliaz = nil
-
super(expr, aliaz)
-
@name = name
-
end
-
-
1
def hash
-
super ^ @name.hash
-
end
-
-
1
def eql? other
-
super && self.name == other.name
-
end
-
1
alias :== :eql?
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
###
-
# Abstract base class for all AST nodes
-
1
class Node
-
1
include Arel::FactoryMethods
-
1
include Enumerable
-
-
1
if $DEBUG
-
def _caller
-
@caller
-
end
-
-
def initialize
-
@caller = caller.dup
-
end
-
end
-
-
###
-
# Factory method to create a Nodes::Not node that has the recipient of
-
# the caller as a child.
-
1
def not
-
Nodes::Not.new self
-
end
-
-
###
-
# Factory method to create a Nodes::Grouping node that has an Nodes::Or
-
# node as a child.
-
1
def or right
-
Nodes::Grouping.new Nodes::Or.new(self, right)
-
end
-
-
###
-
# Factory method to create an Nodes::And node.
-
1
def and right
-
Nodes::And.new [self, right]
-
end
-
-
# FIXME: this method should go away. I don't like people calling
-
# to_sql on non-head nodes. This forces us to walk the AST until we
-
# can find a node that has a "relation" member.
-
#
-
# Maybe we should just use `Table.engine`? :'(
-
1
def to_sql engine = Table.engine
-
engine.connection.visitor.accept self
-
end
-
-
# Iterate through AST, nodes will be yielded depth-first
-
1
def each &block
-
2
return enum_for(:each) unless block_given?
-
-
2
::Arel::Visitors::DepthFirst.new(block).accept self
-
end
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class OuterJoin < Arel::Nodes::Join
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
-
1
class Over < Binary
-
1
include Arel::AliasPredication
-
-
1
def initialize(left, right = nil)
-
super(left, right)
-
end
-
-
1
def operator; 'OVER' end
-
end
-
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class SelectCore < Arel::Nodes::Node
-
1
attr_accessor :top, :projections, :wheres, :groups, :windows
-
1
attr_accessor :having, :source, :set_quantifier
-
-
1
def initialize
-
15
super()
-
15
@source = JoinSource.new nil
-
15
@top = nil
-
-
# http://savage.net.au/SQL/sql-92.bnf.html#set%20quantifier
-
15
@set_quantifier = nil
-
15
@projections = []
-
15
@wheres = []
-
15
@groups = []
-
15
@having = nil
-
15
@windows = []
-
end
-
-
1
def from
-
@source.left
-
end
-
-
1
def from= value
-
@source.left = value
-
end
-
-
1
alias :froms= :from=
-
1
alias :froms :from
-
-
1
def initialize_copy other
-
super
-
@source = @source.clone if @source
-
@projections = @projections.clone
-
@wheres = @wheres.clone
-
@groups = @groups.clone
-
@having = @having.clone if @having
-
@windows = @windows.clone
-
end
-
-
1
def hash
-
[
-
@source, @top, @set_quantifier, @projections,
-
@wheres, @groups, @having, @windows
-
].hash
-
end
-
-
1
def eql? other
-
self.class == other.class &&
-
self.source == other.source &&
-
self.top == other.top &&
-
self.set_quantifier == other.set_quantifier &&
-
self.projections == other.projections &&
-
self.wheres == other.wheres &&
-
self.groups == other.groups &&
-
self.having == other.having &&
-
self.windows == other.windows
-
end
-
1
alias :== :eql?
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class SelectStatement < Arel::Nodes::Node
-
1
attr_reader :cores
-
1
attr_accessor :limit, :orders, :lock, :offset, :with
-
-
1
def initialize cores = [SelectCore.new]
-
15
super()
-
15
@cores = cores
-
15
@orders = []
-
15
@limit = nil
-
15
@lock = nil
-
15
@offset = nil
-
15
@with = nil
-
end
-
-
1
def initialize_copy other
-
super
-
@cores = @cores.map { |x| x.clone }
-
@orders = @orders.map { |x| x.clone }
-
end
-
-
1
def hash
-
[@cores, @orders, @limit, @lock, @offset, @with].hash
-
end
-
-
1
def eql? other
-
self.class == other.class &&
-
self.cores == other.cores &&
-
self.orders == other.orders &&
-
self.limit == other.limit &&
-
self.lock == other.lock &&
-
self.offset == other.offset &&
-
self.with == other.with
-
end
-
1
alias :== :eql?
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class SqlLiteral < String
-
1
include Arel::Expressions
-
1
include Arel::Predications
-
1
include Arel::AliasPredication
-
1
include Arel::OrderPredications
-
-
1
def encode_with(coder)
-
coder.scalar = self.to_s
-
end
-
end
-
-
1
class BindParam < SqlLiteral
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class StringJoin < Arel::Nodes::Join
-
1
def initialize left, right = nil
-
super
-
end
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class TableAlias < Arel::Nodes::Binary
-
1
alias :name :right
-
1
alias :relation :left
-
1
alias :table_alias :name
-
-
1
def [] name
-
Attribute.new(self, name)
-
end
-
-
1
def table_name
-
relation.respond_to?(:name) ? relation.name : name
-
end
-
-
1
def engine
-
relation.engine
-
end
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class Distinct < Arel::Nodes::Node
-
1
def hash
-
self.class.hash
-
end
-
-
1
def eql? other
-
self.class == other.class
-
end
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class True < Arel::Nodes::Node
-
1
def hash
-
self.class.hash
-
end
-
-
1
def eql? other
-
self.class == other.class
-
end
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class Unary < Arel::Nodes::Node
-
1
attr_accessor :expr
-
1
alias :value :expr
-
-
1
def initialize expr
-
4
super()
-
4
@expr = expr
-
end
-
-
1
def hash
-
@expr.hash
-
end
-
-
1
def eql? other
-
self.class == other.class &&
-
self.expr == other.expr
-
end
-
1
alias :== :eql?
-
end
-
-
%w{
-
Bin
-
Group
-
Having
-
Limit
-
Not
-
Offset
-
On
-
Ordering
-
Top
-
Lock
-
DistinctOn
-
1
}.each do |name|
-
11
const_set(name, Class.new(Unary))
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class UnqualifiedColumn < Arel::Nodes::Unary
-
1
alias :attribute :expr
-
1
alias :attribute= :expr=
-
-
1
def relation
-
@expr.relation
-
end
-
-
1
def column
-
@expr.column
-
end
-
-
1
def name
-
@expr.name
-
end
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class UpdateStatement < Arel::Nodes::Node
-
1
attr_accessor :relation, :wheres, :values, :orders, :limit
-
1
attr_accessor :key
-
-
1
def initialize
-
@relation = nil
-
@wheres = []
-
@values = []
-
@orders = []
-
@limit = nil
-
@key = nil
-
end
-
-
1
def initialize_copy other
-
super
-
@wheres = @wheres.clone
-
@values = @values.clone
-
end
-
-
1
def hash
-
[@relation, @wheres, @values, @orders, @limit, @key].hash
-
end
-
-
1
def eql? other
-
self.class == other.class &&
-
self.relation == other.relation &&
-
self.wheres == other.wheres &&
-
self.values == other.values &&
-
self.orders == other.orders &&
-
self.limit == other.limit &&
-
self.key == other.key
-
end
-
1
alias :== :eql?
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class Values < Arel::Nodes::Binary
-
1
alias :expressions :left
-
1
alias :expressions= :left=
-
1
alias :columns :right
-
1
alias :columns= :right=
-
-
1
def initialize exprs, columns = []
-
6
super
-
end
-
end
-
end
-
end
-
1
module Arel
-
1
module Nodes
-
1
class With < Arel::Nodes::Unary
-
1
alias children expr
-
end
-
-
1
class WithRecursive < With; end
-
end
-
end
-
-
1
module Arel
-
1
module OrderPredications
-
-
1
def asc
-
Nodes::Ascending.new self
-
end
-
-
1
def desc
-
Nodes::Descending.new self
-
end
-
-
end
-
end
-
1
module Arel
-
1
module Predications
-
1
def not_eq other
-
Nodes::NotEqual.new self, other
-
end
-
-
1
def not_eq_any others
-
grouping_any :not_eq, others
-
end
-
-
1
def not_eq_all others
-
grouping_all :not_eq, others
-
end
-
-
1
def eq other
-
12
Nodes::Equality.new self, other
-
end
-
-
1
def eq_any others
-
grouping_any :eq, others
-
end
-
-
1
def eq_all others
-
grouping_all :eq, others
-
end
-
-
1
def in other
-
case other
-
when Arel::SelectManager
-
Arel::Nodes::In.new(self, other.ast)
-
when Range
-
if other.begin == -Float::INFINITY && other.end == Float::INFINITY
-
Nodes::NotIn.new self, []
-
elsif other.end == Float::INFINITY
-
Nodes::GreaterThanOrEqual.new(self, other.begin)
-
elsif other.begin == -Float::INFINITY && other.exclude_end?
-
Nodes::LessThan.new(self, other.end)
-
elsif other.begin == -Float::INFINITY
-
Nodes::LessThanOrEqual.new(self, other.end)
-
elsif other.exclude_end?
-
left = Nodes::GreaterThanOrEqual.new(self, other.begin)
-
right = Nodes::LessThan.new(self, other.end)
-
Nodes::And.new [left, right]
-
else
-
Nodes::Between.new(self, Nodes::And.new([other.begin, other.end]))
-
end
-
else
-
Nodes::In.new self, other
-
end
-
end
-
-
1
def in_any others
-
grouping_any :in, others
-
end
-
-
1
def in_all others
-
grouping_all :in, others
-
end
-
-
1
def not_in other
-
case other
-
when Arel::SelectManager
-
Arel::Nodes::NotIn.new(self, other.ast)
-
when Range
-
if other.begin == -Float::INFINITY && other.end == Float::INFINITY
-
Nodes::In.new self, []
-
elsif other.end == Float::INFINITY
-
Nodes::LessThan.new(self, other.begin)
-
elsif other.begin == -Float::INFINITY && other.exclude_end?
-
Nodes::GreaterThanOrEqual.new(self, other.end)
-
elsif other.begin == -Float::INFINITY
-
Nodes::GreaterThan.new(self, other.end)
-
elsif other.exclude_end?
-
left = Nodes::LessThan.new(self, other.begin)
-
right = Nodes::GreaterThanOrEqual.new(self, other.end)
-
Nodes::Or.new left, right
-
else
-
left = Nodes::LessThan.new(self, other.begin)
-
right = Nodes::GreaterThan.new(self, other.end)
-
Nodes::Or.new left, right
-
end
-
else
-
Nodes::NotIn.new self, other
-
end
-
end
-
-
1
def not_in_any others
-
grouping_any :not_in, others
-
end
-
-
1
def not_in_all others
-
grouping_all :not_in, others
-
end
-
-
1
def matches other
-
Nodes::Matches.new self, other
-
end
-
-
1
def matches_any others
-
grouping_any :matches, others
-
end
-
-
1
def matches_all others
-
grouping_all :matches, others
-
end
-
-
1
def does_not_match other
-
Nodes::DoesNotMatch.new self, other
-
end
-
-
1
def does_not_match_any others
-
grouping_any :does_not_match, others
-
end
-
-
1
def does_not_match_all others
-
grouping_all :does_not_match, others
-
end
-
-
1
def gteq right
-
Nodes::GreaterThanOrEqual.new self, right
-
end
-
-
1
def gteq_any others
-
grouping_any :gteq, others
-
end
-
-
1
def gteq_all others
-
grouping_all :gteq, others
-
end
-
-
1
def gt right
-
Nodes::GreaterThan.new self, right
-
end
-
-
1
def gt_any others
-
grouping_any :gt, others
-
end
-
-
1
def gt_all others
-
grouping_all :gt, others
-
end
-
-
1
def lt right
-
Nodes::LessThan.new self, right
-
end
-
-
1
def lt_any others
-
grouping_any :lt, others
-
end
-
-
1
def lt_all others
-
grouping_all :lt, others
-
end
-
-
1
def lteq right
-
Nodes::LessThanOrEqual.new self, right
-
end
-
-
1
def lteq_any others
-
grouping_any :lteq, others
-
end
-
-
1
def lteq_all others
-
grouping_all :lteq, others
-
end
-
-
1
private
-
-
1
def grouping_any method_id, others
-
nodes = others.map {|expr| send(method_id, expr)}
-
Nodes::Grouping.new nodes.inject { |memo,node|
-
Nodes::Or.new(memo, node)
-
}
-
end
-
-
1
def grouping_all method_id, others
-
Nodes::Grouping.new Nodes::And.new(others.map {|expr| send(method_id, expr)})
-
end
-
end
-
end
-
1
module Arel
-
1
class SelectManager < Arel::TreeManager
-
1
include Arel::Crud
-
-
1
STRING_OR_SYMBOL_CLASS = [Symbol, String]
-
-
1
def initialize engine, table = nil
-
15
super(engine)
-
15
@ast = Nodes::SelectStatement.new
-
15
@ctx = @ast.cores.last
-
15
from table
-
end
-
-
1
def initialize_copy other
-
super
-
@ctx = @ast.cores.last
-
end
-
-
1
def limit
-
@ast.limit && @ast.limit.expr
-
end
-
1
alias :taken :limit
-
-
1
def constraints
-
@ctx.wheres
-
end
-
-
1
def offset
-
@ast.offset && @ast.offset.expr
-
end
-
-
1
def skip amount
-
if amount
-
@ast.offset = Nodes::Offset.new(amount)
-
else
-
@ast.offset = nil
-
end
-
self
-
end
-
1
alias :offset= :skip
-
-
###
-
# Produces an Arel::Nodes::Exists node
-
1
def exists
-
Arel::Nodes::Exists.new @ast
-
end
-
-
1
def as other
-
create_table_alias grouping(@ast), Nodes::SqlLiteral.new(other)
-
end
-
-
1
def lock locking = Arel.sql('FOR UPDATE')
-
case locking
-
when true
-
locking = Arel.sql('FOR UPDATE')
-
when Arel::Nodes::SqlLiteral
-
when String
-
locking = Arel.sql locking
-
end
-
-
@ast.lock = Nodes::Lock.new(locking)
-
self
-
end
-
-
1
def locked
-
@ast.lock
-
end
-
-
1
def on *exprs
-
@ctx.source.right.last.right = Nodes::On.new(collapse(exprs))
-
self
-
end
-
-
1
def group *columns
-
columns.each do |column|
-
# FIXME: backwards compat
-
column = Nodes::SqlLiteral.new(column) if String === column
-
column = Nodes::SqlLiteral.new(column.to_s) if Symbol === column
-
-
@ctx.groups.push Nodes::Group.new column
-
end
-
self
-
end
-
-
1
def from table
-
15
table = Nodes::SqlLiteral.new(table) if String === table
-
# FIXME: this is a hack to support
-
# test_with_two_tables_in_from_without_getting_double_quoted
-
# from the AR tests.
-
-
15
case table
-
when Nodes::Join
-
@ctx.source.right << table
-
else
-
15
@ctx.source.left = table
-
end
-
-
15
self
-
end
-
-
1
def froms
-
@ast.cores.map { |x| x.from }.compact
-
end
-
-
1
def join relation, klass = Nodes::InnerJoin
-
return self unless relation
-
-
case relation
-
when String, Nodes::SqlLiteral
-
raise if relation.blank?
-
klass = Nodes::StringJoin
-
end
-
-
@ctx.source.right << create_join(relation, nil, klass)
-
self
-
end
-
-
1
def having *exprs
-
@ctx.having = Nodes::Having.new(collapse(exprs, @ctx.having))
-
self
-
end
-
-
1
def window name
-
window = Nodes::NamedWindow.new(name)
-
@ctx.windows.push window
-
window
-
end
-
-
1
def project *projections
-
# FIXME: converting these to SQLLiterals is probably not good, but
-
# rails tests require it.
-
15
@ctx.projections.concat projections.map { |x|
-
15
STRING_OR_SYMBOL_CLASS.include?(x.class) ? SqlLiteral.new(x.to_s) : x
-
}
-
15
self
-
end
-
-
1
def projections
-
@ctx.projections
-
end
-
-
1
def projections= projections
-
@ctx.projections = projections
-
end
-
-
1
def distinct(value = true)
-
15
if value
-
@ctx.set_quantifier = Arel::Nodes::Distinct.new
-
else
-
15
@ctx.set_quantifier = nil
-
end
-
end
-
-
1
def order *expr
-
# FIXME: We SHOULD NOT be converting these to SqlLiteral automatically
-
@ast.orders.concat expr.map { |x|
-
STRING_OR_SYMBOL_CLASS.include?(x.class) ? Nodes::SqlLiteral.new(x.to_s) : x
-
}
-
self
-
end
-
-
1
def orders
-
@ast.orders
-
end
-
-
1
def where_sql
-
return if @ctx.wheres.empty?
-
-
viz = Visitors::WhereSql.new @engine.connection
-
Nodes::SqlLiteral.new viz.accept @ctx
-
end
-
-
1
def union operation, other = nil
-
if other
-
node_class = Nodes.const_get("Union#{operation.to_s.capitalize}")
-
else
-
other = operation
-
node_class = Nodes::Union
-
end
-
-
node_class.new self.ast, other.ast
-
end
-
-
1
def intersect other
-
Nodes::Intersect.new ast, other.ast
-
end
-
-
1
def except other
-
Nodes::Except.new ast, other.ast
-
end
-
1
alias :minus :except
-
-
1
def with *subqueries
-
if subqueries.first.is_a? Symbol
-
node_class = Nodes.const_get("With#{subqueries.shift.to_s.capitalize}")
-
else
-
node_class = Nodes::With
-
end
-
@ast.with = node_class.new(subqueries.flatten)
-
-
self
-
end
-
-
1
def take limit
-
if limit
-
@ast.limit = Nodes::Limit.new(limit)
-
@ctx.top = Nodes::Top.new(limit)
-
else
-
@ast.limit = nil
-
@ctx.top = nil
-
end
-
self
-
end
-
1
alias limit= take
-
-
1
def join_sql
-
return nil if @ctx.source.right.empty?
-
-
sql = visitor.dup.extend(Visitors::JoinSql).accept @ctx
-
Nodes::SqlLiteral.new sql
-
end
-
-
1
def order_clauses
-
visitor = Visitors::OrderClauses.new(@engine.connection)
-
visitor.accept(@ast).map { |x|
-
Nodes::SqlLiteral.new x
-
}
-
end
-
-
1
def join_sources
-
2
@ctx.source.right
-
end
-
-
1
def source
-
@ctx.source
-
end
-
-
1
def joins manager
-
if $VERBOSE
-
warn "joins is deprecated and will be removed in 4.0.0"
-
warn "please remove your call to joins from #{caller.first}"
-
end
-
manager.join_sql
-
end
-
-
1
class Row < Struct.new(:data) # :nodoc:
-
1
def id
-
data['id']
-
end
-
-
1
def method_missing(name, *args)
-
name = name.to_s
-
return data[name] if data.key?(name)
-
super
-
end
-
end
-
-
1
def to_a # :nodoc:
-
warn "to_a is deprecated. Please remove it from #{caller[0]}"
-
# FIXME: I think `select` should be made public...
-
@engine.connection.send(:select, to_sql, 'AREL').map { |x| Row.new(x) }
-
end
-
-
1
private
-
1
def collapse exprs, existing = nil
-
exprs = exprs.unshift(existing.expr) if existing
-
exprs = exprs.compact.map { |expr|
-
if String === expr
-
# FIXME: Don't do this automatically
-
Arel.sql(expr)
-
else
-
expr
-
end
-
}
-
-
if exprs.length == 1
-
exprs.first
-
else
-
create_and exprs
-
end
-
end
-
end
-
end
-
1
module Arel
-
1
module Sql
-
1
class Engine
-
1
def self.new thing
-
#warn "#{caller.first} -- Engine will be removed"
-
thing
-
end
-
end
-
end
-
end
-
1
module Arel
-
1
class SqlLiteral < Nodes::SqlLiteral
-
end
-
end
-
1
module Arel
-
1
class Table
-
1
include Arel::Crud
-
1
include Arel::FactoryMethods
-
-
1
@engine = nil
-
2
class << self; attr_accessor :engine; end
-
-
1
attr_accessor :name, :engine, :aliases, :table_alias
-
-
# TableAlias and Table both have a #table_name which is the name of the underlying table
-
1
alias :table_name :name
-
-
1
def initialize name, engine = Table.engine
-
19
@name = name.to_s
-
19
@engine = engine
-
19
@columns = nil
-
19
@aliases = []
-
19
@table_alias = nil
-
19
@primary_key = nil
-
-
19
if Hash === engine
-
@engine = engine[:engine] || Table.engine
-
-
# Sometime AR sends an :as parameter to table, to let the table know
-
# that it is an Alias. We may want to override new, and return a
-
# TableAlias node?
-
@table_alias = engine[:as] unless engine[:as].to_s == @name
-
end
-
end
-
-
1
def primary_key
-
if $VERBOSE
-
warn <<-eowarn
-
primary_key (#{caller.first}) is deprecated and will be removed in Arel 4.0.0
-
eowarn
-
end
-
@primary_key ||= begin
-
primary_key_name = @engine.connection.primary_key(name)
-
# some tables might be without primary key
-
primary_key_name && self[primary_key_name]
-
end
-
end
-
-
1
def alias name = "#{self.name}_2"
-
Nodes::TableAlias.new(self, name).tap do |node|
-
@aliases << node
-
end
-
end
-
-
1
def from table
-
SelectManager.new(@engine, table)
-
end
-
-
1
def joins manager
-
if $VERBOSE
-
warn "joins is deprecated and will be removed in 4.0.0"
-
warn "please remove your call to joins from #{caller.first}"
-
end
-
nil
-
end
-
-
1
def join relation, klass = Nodes::InnerJoin
-
return from(self) unless relation
-
-
case relation
-
when String, Nodes::SqlLiteral
-
raise if relation.blank?
-
klass = Nodes::StringJoin
-
end
-
-
from(self).join(relation, klass)
-
end
-
-
1
def group *columns
-
from(self).group(*columns)
-
end
-
-
1
def order *expr
-
from(self).order(*expr)
-
end
-
-
1
def where condition
-
from(self).where condition
-
end
-
-
1
def project *things
-
from(self).project(*things)
-
end
-
-
1
def take amount
-
from(self).take amount
-
end
-
-
1
def skip amount
-
from(self).skip amount
-
end
-
-
1
def having expr
-
from(self).having expr
-
end
-
-
1
def [] name
-
53
::Arel::Attribute.new self, name
-
end
-
-
1
def select_manager
-
SelectManager.new(@engine)
-
end
-
-
1
def insert_manager
-
InsertManager.new(@engine)
-
end
-
-
1
def hash
-
# Perf note: aliases, table alias and engine is excluded from the hash
-
# aliases can have a loop back to this table breaking hashes in parent
-
# relations, for the vast majority of cases @name is unique to a query
-
28
@name.hash
-
end
-
-
1
def eql? other
-
self.class == other.class &&
-
self.name == other.name &&
-
self.engine == other.engine &&
-
self.aliases == other.aliases &&
-
self.table_alias == other.table_alias
-
end
-
1
alias :== :eql?
-
-
1
private
-
-
1
def attributes_for columns
-
return nil unless columns
-
-
columns.map do |column|
-
Attributes.for(column).new self, column.name.to_sym
-
end
-
end
-
-
end
-
end
-
1
module Arel
-
1
class TreeManager
-
1
include Arel::FactoryMethods
-
-
1
attr_reader :ast, :engine
-
-
1
attr_accessor :bind_values
-
-
1
def initialize engine
-
21
@engine = engine
-
21
@ctx = nil
-
21
@bind_values = []
-
end
-
-
1
def to_dot
-
Visitors::Dot.new.accept @ast
-
end
-
-
1
def visitor
-
engine.connection.visitor
-
end
-
-
1
def to_sql
-
visitor.accept @ast
-
end
-
-
1
def initialize_copy other
-
super
-
@ast = @ast.clone
-
end
-
-
1
def where expr
-
4
if Arel::TreeManager === expr
-
expr = expr.ast
-
end
-
4
@ctx.wheres << expr
-
4
self
-
end
-
end
-
end
-
1
module Arel
-
1
class UpdateManager < Arel::TreeManager
-
1
def initialize engine
-
super
-
@ast = Nodes::UpdateStatement.new
-
@ctx = @ast
-
end
-
-
1
def take limit
-
@ast.limit = Nodes::Limit.new(limit) if limit
-
self
-
end
-
-
1
def key= key
-
@ast.key = key
-
end
-
-
1
def key
-
@ast.key
-
end
-
-
1
def order *expr
-
@ast.orders = expr
-
self
-
end
-
-
###
-
# UPDATE +table+
-
1
def table table
-
@ast.relation = table
-
self
-
end
-
-
1
def wheres= exprs
-
@ast.wheres = exprs
-
end
-
-
1
def where expr
-
@ast.wheres << expr
-
self
-
end
-
-
1
def set values
-
if String === values
-
@ast.values = [values]
-
else
-
@ast.values = values.map { |column,value|
-
Nodes::Assignment.new(
-
Nodes::UnqualifiedColumn.new(column),
-
value
-
)
-
}
-
end
-
self
-
end
-
end
-
end
-
1
require 'arel/visitors/visitor'
-
1
require 'arel/visitors/depth_first'
-
1
require 'arel/visitors/to_sql'
-
1
require 'arel/visitors/sqlite'
-
1
require 'arel/visitors/postgresql'
-
1
require 'arel/visitors/mysql'
-
1
require 'arel/visitors/mssql'
-
1
require 'arel/visitors/oracle'
-
1
require 'arel/visitors/join_sql'
-
1
require 'arel/visitors/where_sql'
-
1
require 'arel/visitors/order_clauses'
-
1
require 'arel/visitors/dot'
-
1
require 'arel/visitors/ibm_db'
-
1
require 'arel/visitors/informix'
-
-
1
module Arel
-
1
module Visitors
-
1
VISITORS = {
-
'postgresql' => Arel::Visitors::PostgreSQL,
-
'mysql' => Arel::Visitors::MySQL,
-
'mysql2' => Arel::Visitors::MySQL,
-
'mssql' => Arel::Visitors::MSSQL,
-
'sqlserver' => Arel::Visitors::MSSQL,
-
'oracle_enhanced' => Arel::Visitors::Oracle,
-
'sqlite' => Arel::Visitors::SQLite,
-
'sqlite3' => Arel::Visitors::SQLite,
-
'ibm_db' => Arel::Visitors::IBM_DB,
-
'informix' => Arel::Visitors::Informix,
-
}
-
-
1
ENGINE_VISITORS = Hash.new do |hash, engine|
-
pool = engine.connection_pool
-
adapter = pool.spec.config[:adapter]
-
hash[engine] = (VISITORS[adapter] || Visitors::ToSql).new(engine)
-
end
-
-
1
def self.visitor_for engine
-
ENGINE_VISITORS[engine]
-
end
-
2
class << self; alias :for :visitor_for; end
-
end
-
end
-
1
module Arel
-
1
module Visitors
-
1
module BindVisitor
-
1
def initialize target
-
@block = nil
-
super
-
end
-
-
1
def accept node, &block
-
@block = block if block_given?
-
super
-
end
-
-
1
private
-
-
1
def visit_Arel_Nodes_Assignment o, a
-
if o.right.is_a? Arel::Nodes::BindParam
-
"#{visit o.left, a} = #{visit o.right, a}"
-
else
-
super
-
end
-
end
-
-
1
def visit_Arel_Nodes_BindParam o, a
-
if @block
-
@block.call
-
else
-
super
-
end
-
end
-
-
end
-
end
-
end
-
1
module Arel
-
1
module Visitors
-
1
class DepthFirst < Arel::Visitors::Visitor
-
1
def initialize block = nil
-
2
@block = block || Proc.new
-
end
-
-
1
private
-
-
1
def visit o, a = nil
-
40
super
-
40
@block.call o
-
end
-
-
1
def unary o, a
-
visit o.expr, a
-
end
-
1
alias :visit_Arel_Nodes_Group :unary
-
1
alias :visit_Arel_Nodes_Grouping :unary
-
1
alias :visit_Arel_Nodes_Having :unary
-
1
alias :visit_Arel_Nodes_Limit :unary
-
1
alias :visit_Arel_Nodes_Not :unary
-
1
alias :visit_Arel_Nodes_Offset :unary
-
1
alias :visit_Arel_Nodes_On :unary
-
1
alias :visit_Arel_Nodes_Ordering :unary
-
1
alias :visit_Arel_Nodes_Ascending :unary
-
1
alias :visit_Arel_Nodes_Descending :unary
-
1
alias :visit_Arel_Nodes_Top :unary
-
1
alias :visit_Arel_Nodes_UnqualifiedColumn :unary
-
-
1
def function o, a
-
visit o.expressions, a
-
visit o.alias, a
-
visit o.distinct, a
-
end
-
1
alias :visit_Arel_Nodes_Avg :function
-
1
alias :visit_Arel_Nodes_Exists :function
-
1
alias :visit_Arel_Nodes_Max :function
-
1
alias :visit_Arel_Nodes_Min :function
-
1
alias :visit_Arel_Nodes_Sum :function
-
-
1
def visit_Arel_Nodes_NamedFunction o, a
-
visit o.name, a
-
visit o.expressions, a
-
visit o.distinct, a
-
visit o.alias, a
-
end
-
-
1
def visit_Arel_Nodes_Count o, a
-
visit o.expressions, a
-
visit o.alias, a
-
visit o.distinct, a
-
end
-
-
1
def nary o, a
-
o.children.each { |child| visit child, a }
-
end
-
1
alias :visit_Arel_Nodes_And :nary
-
-
1
def binary o, a
-
2
visit o.left, a
-
2
visit o.right, a
-
end
-
1
alias :visit_Arel_Nodes_As :binary
-
1
alias :visit_Arel_Nodes_Assignment :binary
-
1
alias :visit_Arel_Nodes_Between :binary
-
1
alias :visit_Arel_Nodes_DeleteStatement :binary
-
1
alias :visit_Arel_Nodes_DoesNotMatch :binary
-
1
alias :visit_Arel_Nodes_Equality :binary
-
1
alias :visit_Arel_Nodes_GreaterThan :binary
-
1
alias :visit_Arel_Nodes_GreaterThanOrEqual :binary
-
1
alias :visit_Arel_Nodes_In :binary
-
1
alias :visit_Arel_Nodes_InfixOperation :binary
-
1
alias :visit_Arel_Nodes_JoinSource :binary
-
1
alias :visit_Arel_Nodes_InnerJoin :binary
-
1
alias :visit_Arel_Nodes_LessThan :binary
-
1
alias :visit_Arel_Nodes_LessThanOrEqual :binary
-
1
alias :visit_Arel_Nodes_Matches :binary
-
1
alias :visit_Arel_Nodes_NotEqual :binary
-
1
alias :visit_Arel_Nodes_NotIn :binary
-
1
alias :visit_Arel_Nodes_Or :binary
-
1
alias :visit_Arel_Nodes_OuterJoin :binary
-
1
alias :visit_Arel_Nodes_TableAlias :binary
-
1
alias :visit_Arel_Nodes_Values :binary
-
-
1
def visit_Arel_Nodes_StringJoin o, a
-
visit o.left, a
-
end
-
-
1
def visit_Arel_Attribute o, a
-
2
visit o.relation, a
-
2
visit o.name, a
-
end
-
1
alias :visit_Arel_Attributes_Integer :visit_Arel_Attribute
-
1
alias :visit_Arel_Attributes_Float :visit_Arel_Attribute
-
1
alias :visit_Arel_Attributes_String :visit_Arel_Attribute
-
1
alias :visit_Arel_Attributes_Time :visit_Arel_Attribute
-
1
alias :visit_Arel_Attributes_Boolean :visit_Arel_Attribute
-
1
alias :visit_Arel_Attributes_Attribute :visit_Arel_Attribute
-
1
alias :visit_Arel_Attributes_Decimal :visit_Arel_Attribute
-
-
1
def visit_Arel_Table o, a
-
4
visit o.name, a
-
end
-
-
1
def terminal o, a
-
end
-
1
alias :visit_ActiveSupport_Multibyte_Chars :terminal
-
1
alias :visit_ActiveSupport_StringInquirer :terminal
-
1
alias :visit_Arel_Nodes_Lock :terminal
-
1
alias :visit_Arel_Nodes_Node :terminal
-
1
alias :visit_Arel_Nodes_SqlLiteral :terminal
-
1
alias :visit_Arel_Nodes_BindParam :terminal
-
1
alias :visit_Arel_Nodes_Window :terminal
-
1
alias :visit_Arel_SqlLiteral :terminal
-
1
alias :visit_BigDecimal :terminal
-
1
alias :visit_Bignum :terminal
-
1
alias :visit_Class :terminal
-
1
alias :visit_Date :terminal
-
1
alias :visit_DateTime :terminal
-
1
alias :visit_FalseClass :terminal
-
1
alias :visit_Fixnum :terminal
-
1
alias :visit_Float :terminal
-
1
alias :visit_NilClass :terminal
-
1
alias :visit_String :terminal
-
1
alias :visit_Symbol :terminal
-
1
alias :visit_Time :terminal
-
1
alias :visit_TrueClass :terminal
-
-
1
def visit_Arel_Nodes_InsertStatement o, a
-
visit o.relation, a
-
visit o.columns, a
-
visit o.values, a
-
end
-
-
1
def visit_Arel_Nodes_SelectCore o, a
-
2
visit o.projections, a
-
2
visit o.source, a
-
2
visit o.wheres, a
-
2
visit o.groups, a
-
2
visit o.windows, a
-
2
visit o.having, a
-
end
-
-
1
def visit_Arel_Nodes_SelectStatement o, a
-
2
visit o.cores, a
-
2
visit o.orders, a
-
2
visit o.limit, a
-
2
visit o.lock, a
-
2
visit o.offset, a
-
end
-
-
1
def visit_Arel_Nodes_UpdateStatement o, a
-
visit o.relation, a
-
visit o.values, a
-
visit o.wheres, a
-
visit o.orders, a
-
visit o.limit, a
-
end
-
-
1
def visit_Array o, a
-
18
o.each { |i| visit i, a }
-
end
-
-
1
def visit_Hash o, a
-
o.each { |k,v| visit(k, a); visit(v, a) }
-
end
-
end
-
end
-
end
-
1
module Arel
-
1
module Visitors
-
1
class Dot < Arel::Visitors::Visitor
-
1
class Node # :nodoc:
-
1
attr_accessor :name, :id, :fields
-
-
1
def initialize name, id, fields = []
-
@name = name
-
@id = id
-
@fields = fields
-
end
-
end
-
-
1
class Edge < Struct.new :name, :from, :to # :nodoc:
-
end
-
-
1
def initialize
-
@nodes = []
-
@edges = []
-
@node_stack = []
-
@edge_stack = []
-
@seen = {}
-
end
-
-
1
def accept object
-
super
-
to_dot
-
end
-
-
1
private
-
1
def visit_Arel_Nodes_Ordering o, a
-
visit_edge o, a, "expr"
-
end
-
-
1
def visit_Arel_Nodes_TableAlias o, a
-
visit_edge o, a, "name"
-
visit_edge o, a, "relation"
-
end
-
-
1
def visit_Arel_Nodes_Count o, a
-
visit_edge o, a, "expressions"
-
visit_edge o, a, "distinct"
-
end
-
-
1
def visit_Arel_Nodes_Values o, a
-
visit_edge o, a, "expressions"
-
end
-
-
1
def visit_Arel_Nodes_StringJoin o, a
-
visit_edge o, a, "left"
-
end
-
-
1
def visit_Arel_Nodes_InnerJoin o, a
-
visit_edge o, a, "left"
-
visit_edge o, a, "right"
-
end
-
1
alias :visit_Arel_Nodes_OuterJoin :visit_Arel_Nodes_InnerJoin
-
-
1
def visit_Arel_Nodes_DeleteStatement o, a
-
visit_edge o, a, "relation"
-
visit_edge o, a, "wheres"
-
end
-
-
1
def unary o, a
-
visit_edge o, a, "expr"
-
end
-
1
alias :visit_Arel_Nodes_Group :unary
-
1
alias :visit_Arel_Nodes_Grouping :unary
-
1
alias :visit_Arel_Nodes_Having :unary
-
1
alias :visit_Arel_Nodes_Limit :unary
-
1
alias :visit_Arel_Nodes_Not :unary
-
1
alias :visit_Arel_Nodes_Offset :unary
-
1
alias :visit_Arel_Nodes_On :unary
-
1
alias :visit_Arel_Nodes_Top :unary
-
1
alias :visit_Arel_Nodes_UnqualifiedColumn :unary
-
1
alias :visit_Arel_Nodes_Preceding :unary
-
1
alias :visit_Arel_Nodes_Following :unary
-
1
alias :visit_Arel_Nodes_Rows :unary
-
1
alias :visit_Arel_Nodes_Range :unary
-
-
1
def window o, a
-
visit_edge o, a, "orders"
-
visit_edge o, a, "framing"
-
end
-
1
alias :visit_Arel_Nodes_Window :window
-
-
1
def named_window o, a
-
visit_edge o, a, "orders"
-
visit_edge o, a, "framing"
-
visit_edge o, a, "name"
-
end
-
1
alias :visit_Arel_Nodes_NamedWindow :named_window
-
-
1
def function o, a
-
visit_edge o, a, "expressions"
-
visit_edge o, a, "distinct"
-
visit_edge o, a, "alias"
-
end
-
1
alias :visit_Arel_Nodes_Exists :function
-
1
alias :visit_Arel_Nodes_Min :function
-
1
alias :visit_Arel_Nodes_Max :function
-
1
alias :visit_Arel_Nodes_Avg :function
-
1
alias :visit_Arel_Nodes_Sum :function
-
-
1
def extract o, a
-
visit_edge o, a, "expressions"
-
visit_edge o, a, "alias"
-
end
-
1
alias :visit_Arel_Nodes_Extract :extract
-
-
1
def visit_Arel_Nodes_NamedFunction o, a
-
visit_edge o, a, "name"
-
visit_edge o, a, "expressions"
-
visit_edge o, a, "distinct"
-
visit_edge o, a, "alias"
-
end
-
-
1
def visit_Arel_Nodes_InsertStatement o, a
-
visit_edge o, a, "relation"
-
visit_edge o, a, "columns"
-
visit_edge o, a, "values"
-
end
-
-
1
def visit_Arel_Nodes_SelectCore o, a
-
visit_edge o, a, "source"
-
visit_edge o, a, "projections"
-
visit_edge o, a, "wheres"
-
visit_edge o, a, "windows"
-
end
-
-
1
def visit_Arel_Nodes_SelectStatement o, a
-
visit_edge o, a, "cores"
-
visit_edge o, a, "limit"
-
visit_edge o, a, "orders"
-
visit_edge o, a, "offset"
-
end
-
-
1
def visit_Arel_Nodes_UpdateStatement o, a
-
visit_edge o, a, "relation"
-
visit_edge o, a, "wheres"
-
visit_edge o, a, "values"
-
end
-
-
1
def visit_Arel_Table o, a
-
visit_edge o, a, "name"
-
end
-
-
1
def visit_Arel_Attribute o, a
-
visit_edge o, a, "relation"
-
visit_edge o, a, "name"
-
end
-
1
alias :visit_Arel_Attributes_Integer :visit_Arel_Attribute
-
1
alias :visit_Arel_Attributes_Float :visit_Arel_Attribute
-
1
alias :visit_Arel_Attributes_String :visit_Arel_Attribute
-
1
alias :visit_Arel_Attributes_Time :visit_Arel_Attribute
-
1
alias :visit_Arel_Attributes_Boolean :visit_Arel_Attribute
-
1
alias :visit_Arel_Attributes_Attribute :visit_Arel_Attribute
-
-
1
def nary o, a
-
o.children.each_with_index do |x,i|
-
edge(i) { visit x, a }
-
end
-
end
-
1
alias :visit_Arel_Nodes_And :nary
-
-
1
def binary o, a
-
visit_edge o, a, "left"
-
visit_edge o, a, "right"
-
end
-
1
alias :visit_Arel_Nodes_As :binary
-
1
alias :visit_Arel_Nodes_Assignment :binary
-
1
alias :visit_Arel_Nodes_Between :binary
-
1
alias :visit_Arel_Nodes_DoesNotMatch :binary
-
1
alias :visit_Arel_Nodes_Equality :binary
-
1
alias :visit_Arel_Nodes_GreaterThan :binary
-
1
alias :visit_Arel_Nodes_GreaterThanOrEqual :binary
-
1
alias :visit_Arel_Nodes_In :binary
-
1
alias :visit_Arel_Nodes_JoinSource :binary
-
1
alias :visit_Arel_Nodes_LessThan :binary
-
1
alias :visit_Arel_Nodes_LessThanOrEqual :binary
-
1
alias :visit_Arel_Nodes_Matches :binary
-
1
alias :visit_Arel_Nodes_NotEqual :binary
-
1
alias :visit_Arel_Nodes_NotIn :binary
-
1
alias :visit_Arel_Nodes_Or :binary
-
1
alias :visit_Arel_Nodes_Over :binary
-
-
1
def visit_String o, a
-
@node_stack.last.fields << o
-
end
-
1
alias :visit_Time :visit_String
-
1
alias :visit_Date :visit_String
-
1
alias :visit_DateTime :visit_String
-
1
alias :visit_NilClass :visit_String
-
1
alias :visit_TrueClass :visit_String
-
1
alias :visit_FalseClass :visit_String
-
1
alias :visit_Arel_SqlLiteral :visit_String
-
1
alias :visit_Arel_Nodes_BindParam :visit_String
-
1
alias :visit_Fixnum :visit_String
-
1
alias :visit_BigDecimal :visit_String
-
1
alias :visit_Float :visit_String
-
1
alias :visit_Symbol :visit_String
-
1
alias :visit_Arel_Nodes_SqlLiteral :visit_String
-
-
1
def visit_Hash o, a
-
o.each_with_index do |pair, i|
-
edge("pair_#{i}") { visit pair, a }
-
end
-
end
-
-
1
def visit_Array o, a
-
o.each_with_index do |x,i|
-
edge(i) { visit x, a }
-
end
-
end
-
-
1
def visit_edge o, a, method
-
edge(method) { visit o.send(method), a }
-
end
-
-
1
def visit o, a = nil
-
if node = @seen[o.object_id]
-
@edge_stack.last.to = node
-
return
-
end
-
-
node = Node.new(o.class.name, o.object_id)
-
@seen[node.id] = node
-
@nodes << node
-
with_node node do
-
super
-
end
-
end
-
-
1
def edge name
-
edge = Edge.new(name, @node_stack.last)
-
@edge_stack.push edge
-
@edges << edge
-
yield
-
@edge_stack.pop
-
end
-
-
1
def with_node node
-
if edge = @edge_stack.last
-
edge.to = node
-
end
-
-
@node_stack.push node
-
yield
-
@node_stack.pop
-
end
-
-
1
def quote string
-
string.to_s.gsub('"', '\"')
-
end
-
-
1
def to_dot
-
"digraph \"Arel\" {\nnode [width=0.375,height=0.25,shape=record];\n" +
-
@nodes.map { |node|
-
label = "<f0>#{node.name}"
-
-
node.fields.each_with_index do |field, i|
-
label << "|<f#{i + 1}>#{quote field}"
-
end
-
-
"#{node.id} [label=\"#{label}\"];"
-
}.join("\n") + "\n" + @edges.map { |edge|
-
"#{edge.from.id} -> #{edge.to.id} [label=\"#{edge.name}\"];"
-
}.join("\n") + "\n}"
-
end
-
end
-
end
-
end
-
1
module Arel
-
1
module Visitors
-
1
class IBM_DB < Arel::Visitors::ToSql
-
1
private
-
-
1
def visit_Arel_Nodes_Limit o, a
-
"FETCH FIRST #{visit o.expr, a} ROWS ONLY"
-
end
-
-
end
-
end
-
end
-
1
module Arel
-
1
module Visitors
-
1
class Informix < Arel::Visitors::ToSql
-
1
private
-
1
def visit_Arel_Nodes_SelectStatement o, a
-
[
-
"SELECT",
-
(visit(o.offset, a) if o.offset),
-
(visit(o.limit, a) if o.limit),
-
o.cores.map { |x| visit_Arel_Nodes_SelectCore x, a }.join,
-
("ORDER BY #{o.orders.map { |x| visit x, a }.join(', ')}" unless o.orders.empty?),
-
(visit(o.lock, a) if o.lock),
-
].compact.join ' '
-
end
-
1
def visit_Arel_Nodes_SelectCore o, a
-
[
-
"#{o.projections.map { |x| visit x, a }.join ', '}",
-
("FROM #{visit(o.source, a)}" if o.source && !o.source.empty?),
-
("WHERE #{o.wheres.map { |x| visit x, a }.join ' AND ' }" unless o.wheres.empty?),
-
("GROUP BY #{o.groups.map { |x| visit x, a }.join ', ' }" unless o.groups.empty?),
-
(visit(o.having, a) if o.having),
-
].compact.join ' '
-
end
-
1
def visit_Arel_Nodes_Offset o, a
-
"SKIP #{visit o.expr, a}"
-
end
-
1
def visit_Arel_Nodes_Limit o, a
-
"LIMIT #{visit o.expr, a}"
-
end
-
end
-
end
-
end
-
-
1
module Arel
-
1
module Visitors
-
###
-
# This class produces SQL for JOIN clauses but omits the "single-source"
-
# part of the Join grammar:
-
#
-
# http://www.sqlite.org/syntaxdiagrams.html#join-source
-
#
-
# This visitor is used in SelectManager#join_sql and is for backwards
-
# compatibility with Arel V1.0
-
1
module JoinSql
-
1
private
-
-
1
def visit_Arel_Nodes_SelectCore o, a
-
o.source.right.map { |j| visit j, a }.join ' '
-
end
-
end
-
end
-
end
-
1
module Arel
-
1
module Visitors
-
1
class MSSQL < Arel::Visitors::ToSql
-
1
private
-
-
# `top` wouldn't really work here. I.e. User.select("distinct first_name").limit(10) would generate
-
# "select top 10 distinct first_name from users", which is invalid query! it should be
-
# "select distinct top 10 first_name from users"
-
1
def visit_Arel_Nodes_Top o, a
-
""
-
end
-
-
1
def visit_Arel_Nodes_SelectStatement o, a
-
if !o.limit && !o.offset
-
return super o, a
-
end
-
-
select_order_by = "ORDER BY #{o.orders.map { |x| visit x, a }.join(', ')}" unless o.orders.empty?
-
-
is_select_count = false
-
sql = o.cores.map { |x|
-
core_order_by = select_order_by || determine_order_by(x, a)
-
if select_count? x
-
x.projections = [row_num_literal(core_order_by)]
-
is_select_count = true
-
else
-
x.projections << row_num_literal(core_order_by)
-
end
-
-
visit_Arel_Nodes_SelectCore x, a
-
}.join
-
-
sql = "SELECT _t.* FROM (#{sql}) as _t WHERE #{get_offset_limit_clause(o)}"
-
# fixme count distinct wouldn't work with limit or offset
-
sql = "SELECT COUNT(1) as count_id FROM (#{sql}) AS subquery" if is_select_count
-
sql
-
end
-
-
1
def get_offset_limit_clause o
-
first_row = o.offset ? o.offset.expr.to_i + 1 : 1
-
last_row = o.limit ? o.limit.expr.to_i - 1 + first_row : nil
-
if last_row
-
" _row_num BETWEEN #{first_row} AND #{last_row}"
-
else
-
" _row_num >= #{first_row}"
-
end
-
end
-
-
1
def determine_order_by x, a
-
unless x.groups.empty?
-
"ORDER BY #{x.groups.map { |g| visit g, a }.join ', ' }"
-
else
-
"ORDER BY #{find_left_table_pk(x.froms, a)}"
-
end
-
end
-
-
1
def row_num_literal order_by
-
Nodes::SqlLiteral.new("ROW_NUMBER() OVER (#{order_by}) as _row_num")
-
end
-
-
1
def select_count? x
-
x.projections.length == 1 && Arel::Nodes::Count === x.projections.first
-
end
-
-
# FIXME raise exception of there is no pk?
-
# FIXME!! Table.primary_key will be deprecated. What is the replacement??
-
1
def find_left_table_pk o, a
-
return visit o.primary_key, a if o.instance_of? Arel::Table
-
find_left_table_pk o.left, a if o.kind_of? Arel::Nodes::Join
-
end
-
end
-
end
-
end
-
1
module Arel
-
1
module Visitors
-
1
class MySQL < Arel::Visitors::ToSql
-
1
private
-
1
def visit_Arel_Nodes_Union o, a, suppress_parens = false
-
left_result = case o.left
-
when Arel::Nodes::Union
-
visit_Arel_Nodes_Union o.left, a, true
-
else
-
visit o.left, a
-
end
-
-
right_result = case o.right
-
when Arel::Nodes::Union
-
visit_Arel_Nodes_Union o.right, a, true
-
else
-
visit o.right, a
-
end
-
-
if suppress_parens
-
"#{left_result} UNION #{right_result}"
-
else
-
"( #{left_result} UNION #{right_result} )"
-
end
-
end
-
-
1
def visit_Arel_Nodes_Bin o, a
-
"BINARY #{visit o.expr, a}"
-
end
-
-
###
-
# :'(
-
# http://dev.mysql.com/doc/refman/5.0/en/select.html#id3482214
-
1
def visit_Arel_Nodes_SelectStatement o, a
-
o.limit = Arel::Nodes::Limit.new(18446744073709551615) if o.offset && !o.limit
-
super
-
end
-
-
1
def visit_Arel_Nodes_SelectCore o, a
-
o.froms ||= Arel.sql('DUAL')
-
super
-
end
-
-
1
def visit_Arel_Nodes_UpdateStatement o, a
-
[
-
"UPDATE #{visit o.relation, a}",
-
("SET #{o.values.map { |value| visit value, a }.join ', '}" unless o.values.empty?),
-
("WHERE #{o.wheres.map { |x| visit x, a }.join ' AND '}" unless o.wheres.empty?),
-
("ORDER BY #{o.orders.map { |x| visit x, a }.join(', ')}" unless o.orders.empty?),
-
(visit(o.limit, a) if o.limit),
-
].compact.join ' '
-
end
-
-
end
-
end
-
end
-
1
module Arel
-
1
module Visitors
-
1
class Oracle < Arel::Visitors::ToSql
-
1
private
-
-
1
def visit_Arel_Nodes_SelectStatement o, a
-
o = order_hacks(o, a)
-
-
# if need to select first records without ORDER BY and GROUP BY and without DISTINCT
-
# then can use simple ROWNUM in WHERE clause
-
if o.limit && o.orders.empty? && !o.offset && o.cores.first.set_quantifier.class.to_s !~ /Distinct/
-
o.cores.last.wheres.push Nodes::LessThanOrEqual.new(
-
Nodes::SqlLiteral.new('ROWNUM'), o.limit.expr
-
)
-
return super
-
end
-
-
if o.limit && o.offset
-
o = o.dup
-
limit = o.limit.expr.to_i
-
offset = o.offset
-
o.offset = nil
-
sql = super(o, a)
-
return <<-eosql
-
SELECT * FROM (
-
SELECT raw_sql_.*, rownum raw_rnum_
-
FROM (#{sql}) raw_sql_
-
WHERE rownum <= #{offset.expr.to_i + limit}
-
)
-
WHERE #{visit offset, a}
-
eosql
-
end
-
-
if o.limit
-
o = o.dup
-
limit = o.limit.expr
-
return "SELECT * FROM (#{super(o, a)}) WHERE ROWNUM <= #{visit limit, a}"
-
end
-
-
if o.offset
-
o = o.dup
-
offset = o.offset
-
o.offset = nil
-
sql = super(o, a)
-
return <<-eosql
-
SELECT * FROM (
-
SELECT raw_sql_.*, rownum raw_rnum_
-
FROM (#{sql}) raw_sql_
-
)
-
WHERE #{visit offset, a}
-
eosql
-
end
-
-
super
-
end
-
-
1
def visit_Arel_Nodes_Limit o, a
-
end
-
-
1
def visit_Arel_Nodes_Offset o, a
-
"raw_rnum_ > #{visit o.expr, a}"
-
end
-
-
1
def visit_Arel_Nodes_Except o, a
-
"( #{visit o.left, a} MINUS #{visit o.right, a} )"
-
end
-
-
1
def visit_Arel_Nodes_UpdateStatement o, a
-
# Oracle does not allow ORDER BY/LIMIT in UPDATEs.
-
if o.orders.any? && o.limit.nil?
-
# However, there is no harm in silently eating the ORDER BY clause if no LIMIT has been provided,
-
# otherwise let the user deal with the error
-
o = o.dup
-
o.orders = []
-
end
-
-
super
-
end
-
-
###
-
# Hacks for the order clauses specific to Oracle
-
1
def order_hacks o, a
-
return o if o.orders.empty?
-
return o unless o.cores.any? do |core|
-
core.projections.any? do |projection|
-
/FIRST_VALUE/ === projection
-
end
-
end
-
# Previous version with join and split broke ORDER BY clause
-
# if it contained functions with several arguments (separated by ',').
-
#
-
# orders = o.orders.map { |x| visit x, a }.join(', ').split(',')
-
orders = o.orders.map do |x|
-
string = visit x, a
-
if string.include?(',')
-
split_order_string(string)
-
else
-
string
-
end
-
end.flatten
-
o.orders = []
-
orders.each_with_index do |order, i|
-
o.orders <<
-
Nodes::SqlLiteral.new("alias_#{i}__#{' DESC' if /\bdesc$/i === order}")
-
end
-
o
-
end
-
-
# Split string by commas but count opening and closing brackets
-
# and ignore commas inside brackets.
-
1
def split_order_string(string)
-
array = []
-
i = 0
-
string.split(',').each do |part|
-
if array[i]
-
array[i] << ',' << part
-
else
-
# to ensure that array[i] will be String and not Arel::Nodes::SqlLiteral
-
array[i] = '' << part
-
end
-
i += 1 if array[i].count('(') == array[i].count(')')
-
end
-
array
-
end
-
-
end
-
end
-
end
-
1
module Arel
-
1
module Visitors
-
1
class OrderClauses < Arel::Visitors::ToSql
-
1
private
-
-
1
def visit_Arel_Nodes_SelectStatement o, a
-
o.orders.map { |x| visit x, a }
-
end
-
end
-
end
-
end
-
1
module Arel
-
1
module Visitors
-
1
class PostgreSQL < Arel::Visitors::ToSql
-
1
private
-
-
1
def visit_Arel_Nodes_Matches o, a
-
a = o.left if Arel::Attributes::Attribute === o.left
-
"#{visit o.left, a} ILIKE #{visit o.right, a}"
-
end
-
-
1
def visit_Arel_Nodes_DoesNotMatch o, a
-
a = o.left if Arel::Attributes::Attribute === o.left
-
"#{visit o.left, a} NOT ILIKE #{visit o.right, a}"
-
end
-
-
1
def visit_Arel_Nodes_DistinctOn o, a
-
"DISTINCT ON ( #{visit o.expr, a} )"
-
end
-
end
-
end
-
end
-
1
module Arel
-
1
module Visitors
-
1
class SQLite < Arel::Visitors::ToSql
-
1
private
-
-
# Locks are not supported in SQLite
-
1
def visit_Arel_Nodes_Lock o, a
-
end
-
-
1
def visit_Arel_Nodes_SelectStatement o, a
-
o.limit = Arel::Nodes::Limit.new(-1) if o.offset && !o.limit
-
super
-
end
-
end
-
end
-
end
-
1
require 'bigdecimal'
-
1
require 'date'
-
-
1
module Arel
-
1
module Visitors
-
1
class ToSql < Arel::Visitors::Visitor
-
##
-
# This is some roflscale crazy stuff. I'm roflscaling this because
-
# building SQL queries is a hotspot. I will explain the roflscale so that
-
# others will not rm this code.
-
#
-
# In YARV, string literals in a method body will get duped when the byte
-
# code is executed. Let's take a look:
-
#
-
# > puts RubyVM::InstructionSequence.new('def foo; "bar"; end').disasm
-
#
-
# == disasm: <RubyVM::InstructionSequence:foo@<compiled>>=====
-
# 0000 trace 8
-
# 0002 trace 1
-
# 0004 putstring "bar"
-
# 0006 trace 16
-
# 0008 leave
-
#
-
# The `putstring` bytecode will dup the string and push it on the stack.
-
# In many cases in our SQL visitor, that string is never mutated, so there
-
# is no need to dup the literal.
-
#
-
# If we change to a constant lookup, the string will not be duped, and we
-
# can reduce the objects in our system:
-
#
-
# > puts RubyVM::InstructionSequence.new('BAR = "bar"; def foo; BAR; end').disasm
-
#
-
# == disasm: <RubyVM::InstructionSequence:foo@<compiled>>========
-
# 0000 trace 8
-
# 0002 trace 1
-
# 0004 getinlinecache 11, <ic:0>
-
# 0007 getconstant :BAR
-
# 0009 setinlinecache <ic:0>
-
# 0011 trace 16
-
# 0013 leave
-
#
-
# `getconstant` should be a hash lookup, and no object is duped when the
-
# value of the constant is pushed on the stack. Hence the crazy
-
# constants below.
-
#
-
# `matches` and `doesNotMatch` operate case-insensitively via Visitor subclasses
-
# specialized for specific databases when necessary.
-
#
-
-
1
WHERE = ' WHERE ' # :nodoc:
-
1
SPACE = ' ' # :nodoc:
-
1
COMMA = ', ' # :nodoc:
-
1
GROUP_BY = ' GROUP BY ' # :nodoc:
-
1
ORDER_BY = ' ORDER BY ' # :nodoc:
-
1
WINDOW = ' WINDOW ' # :nodoc:
-
1
AND = ' AND ' # :nodoc:
-
-
1
DISTINCT = 'DISTINCT' # :nodoc:
-
-
1
def initialize connection
-
1
@connection = connection
-
1
@schema_cache = connection.schema_cache
-
1
@quoted_tables = {}
-
1
@quoted_columns = {}
-
end
-
-
1
private
-
-
1
def visit_Arel_Nodes_DeleteStatement o, a
-
[
-
"DELETE FROM #{visit o.relation}",
-
("WHERE #{o.wheres.map { |x| visit x }.join AND}" unless o.wheres.empty?)
-
].compact.join ' '
-
end
-
-
# FIXME: we should probably have a 2-pass visitor for this
-
1
def build_subselect key, o
-
stmt = Nodes::SelectStatement.new
-
core = stmt.cores.first
-
core.froms = o.relation
-
core.wheres = o.wheres
-
core.projections = [key]
-
stmt.limit = o.limit
-
stmt.orders = o.orders
-
stmt
-
end
-
-
1
def visit_Arel_Nodes_UpdateStatement o, a
-
if o.orders.empty? && o.limit.nil?
-
wheres = o.wheres
-
else
-
wheres = [Nodes::In.new(o.key, [build_subselect(o.key, o)])]
-
end
-
-
[
-
"UPDATE #{visit o.relation, a}",
-
("SET #{o.values.map { |value| visit value, a }.join ', '}" unless o.values.empty?),
-
("WHERE #{wheres.map { |x| visit x, a }.join ' AND '}" unless wheres.empty?),
-
].compact.join ' '
-
end
-
-
1
def visit_Arel_Nodes_InsertStatement o, a
-
[
-
"INSERT INTO #{visit o.relation, a}",
-
-
("(#{o.columns.map { |x|
-
24
quote_column_name x.name
-
6
}.join ', '})" unless o.columns.empty?),
-
-
6
(visit o.values, a if o.values),
-
6
].compact.join ' '
-
end
-
-
1
def visit_Arel_Nodes_Exists o, a
-
"EXISTS (#{visit o.expressions, a})#{
-
o.alias ? " AS #{visit o.alias, a}" : ''}"
-
end
-
-
1
def visit_Arel_Nodes_True o, a
-
"TRUE"
-
end
-
-
1
def visit_Arel_Nodes_False o, a
-
"FALSE"
-
end
-
-
1
def table_exists? name
-
@schema_cache.table_exists? name
-
end
-
-
1
def column_for attr
-
return unless attr
-
name = attr.name.to_s
-
table = attr.relation.table_name
-
-
return nil unless table_exists? table
-
-
column_cache(table)[name]
-
end
-
-
1
def column_cache(table)
-
@schema_cache.columns_hash(table)
-
end
-
-
1
def visit_Arel_Nodes_Values o, a
-
6
"VALUES (#{o.expressions.zip(o.columns).map { |value, attr|
-
24
if Nodes::SqlLiteral === value
-
24
visit value, a
-
else
-
quote(value, attr && column_for(attr))
-
end
-
}.join ', '})"
-
end
-
-
1
def visit_Arel_Nodes_SelectStatement o, a
-
7
str = ''
-
-
7
if o.with
-
str << visit(o.with, a)
-
str << SPACE
-
end
-
-
14
o.cores.each { |x| str << visit_Arel_Nodes_SelectCore(x, a) }
-
-
7
unless o.orders.empty?
-
str << SPACE
-
str << ORDER_BY
-
len = o.orders.length - 1
-
o.orders.each_with_index { |x, i|
-
str << visit(x, a)
-
str << COMMA unless len == i
-
}
-
end
-
-
7
str << " #{visit(o.limit, a)}" if o.limit
-
7
str << " #{visit(o.offset, a)}" if o.offset
-
7
str << " #{visit(o.lock, a)}" if o.lock
-
-
7
str.strip!
-
7
str
-
end
-
-
1
def visit_Arel_Nodes_SelectCore o, a
-
7
str = "SELECT"
-
-
7
str << " #{visit(o.top, a)}" if o.top
-
7
str << " #{visit(o.set_quantifier, a)}" if o.set_quantifier
-
-
7
unless o.projections.empty?
-
7
str << SPACE
-
7
len = o.projections.length - 1
-
7
o.projections.each_with_index do |x, i|
-
7
str << visit(x, a)
-
7
str << COMMA unless len == i
-
end
-
end
-
-
7
str << " FROM #{visit(o.source, a)}" if o.source && !o.source.empty?
-
-
7
unless o.wheres.empty?
-
4
str << WHERE
-
4
len = o.wheres.length - 1
-
4
o.wheres.each_with_index do |x, i|
-
4
str << visit(x, a)
-
4
str << AND unless len == i
-
end
-
end
-
-
7
unless o.groups.empty?
-
str << GROUP_BY
-
len = o.groups.length - 1
-
o.groups.each_with_index do |x, i|
-
str << visit(x, a)
-
str << COMMA unless len == i
-
end
-
end
-
-
7
str << " #{visit(o.having, a)}" if o.having
-
-
7
unless o.windows.empty?
-
str << WINDOW
-
len = o.windows.length - 1
-
o.windows.each_with_index do |x, i|
-
str << visit(x, a)
-
str << COMMA unless len == i
-
end
-
end
-
-
7
str
-
end
-
-
1
def visit_Arel_Nodes_Bin o, a
-
visit o.expr, a
-
end
-
-
1
def visit_Arel_Nodes_Distinct o, a
-
DISTINCT
-
end
-
-
1
def visit_Arel_Nodes_DistinctOn o, a
-
raise NotImplementedError, 'DISTINCT ON not implemented for this db'
-
end
-
-
1
def visit_Arel_Nodes_With o, a
-
"WITH #{o.children.map { |x| visit x, a }.join(', ')}"
-
end
-
-
1
def visit_Arel_Nodes_WithRecursive o, a
-
"WITH RECURSIVE #{o.children.map { |x| visit x, a }.join(', ')}"
-
end
-
-
1
def visit_Arel_Nodes_Union o, a
-
"( #{visit o.left, a} UNION #{visit o.right, a} )"
-
end
-
-
1
def visit_Arel_Nodes_UnionAll o, a
-
"( #{visit o.left, a} UNION ALL #{visit o.right, a} )"
-
end
-
-
1
def visit_Arel_Nodes_Intersect o, a
-
"( #{visit o.left, a} INTERSECT #{visit o.right, a} )"
-
end
-
-
1
def visit_Arel_Nodes_Except o, a
-
"( #{visit o.left, a} EXCEPT #{visit o.right, a} )"
-
end
-
-
1
def visit_Arel_Nodes_NamedWindow o, a
-
"#{quote_column_name o.name} AS #{visit_Arel_Nodes_Window o, a}"
-
end
-
-
1
def visit_Arel_Nodes_Window o, a
-
s = [
-
("ORDER BY #{o.orders.map { |x| visit(x, a) }.join(', ')}" unless o.orders.empty?),
-
(visit o.framing, a if o.framing)
-
].compact.join ' '
-
"(#{s})"
-
end
-
-
1
def visit_Arel_Nodes_Rows o, a
-
if o.expr
-
"ROWS #{visit o.expr, a}"
-
else
-
"ROWS"
-
end
-
end
-
-
1
def visit_Arel_Nodes_Range o, a
-
if o.expr
-
"RANGE #{visit o.expr, a}"
-
else
-
"RANGE"
-
end
-
end
-
-
1
def visit_Arel_Nodes_Preceding o, a
-
"#{o.expr ? visit(o.expr, a) : 'UNBOUNDED'} PRECEDING"
-
end
-
-
1
def visit_Arel_Nodes_Following o, a
-
"#{o.expr ? visit(o.expr, a) : 'UNBOUNDED'} FOLLOWING"
-
end
-
-
1
def visit_Arel_Nodes_CurrentRow o, a
-
"CURRENT ROW"
-
end
-
-
1
def visit_Arel_Nodes_Over o, a
-
case o.right
-
when nil
-
"#{visit o.left, a} OVER ()"
-
when Arel::Nodes::SqlLiteral
-
"#{visit o.left, a} OVER #{visit o.right, a}"
-
when String, Symbol
-
"#{visit o.left, a} OVER #{quote_column_name o.right.to_s}"
-
else
-
"#{visit o.left, a} OVER #{visit o.right, a}"
-
end
-
end
-
-
1
def visit_Arel_Nodes_Having o, a
-
"HAVING #{visit o.expr, a}"
-
end
-
-
1
def visit_Arel_Nodes_Offset o, a
-
"OFFSET #{visit o.expr, a}"
-
end
-
-
1
def visit_Arel_Nodes_Limit o, a
-
"LIMIT #{visit o.expr, a}"
-
end
-
-
# FIXME: this does nothing on most databases, but does on MSSQL
-
1
def visit_Arel_Nodes_Top o, a
-
""
-
end
-
-
1
def visit_Arel_Nodes_Lock o, a
-
visit o.expr, a
-
end
-
-
1
def visit_Arel_Nodes_Grouping o, a
-
"(#{visit o.expr, a})"
-
end
-
-
1
def visit_Arel_SelectManager o, a
-
"(#{o.to_sql.rstrip})"
-
end
-
-
1
def visit_Arel_Nodes_Ascending o, a
-
"#{visit o.expr, a} ASC"
-
end
-
-
1
def visit_Arel_Nodes_Descending o, a
-
"#{visit o.expr, a} DESC"
-
end
-
-
1
def visit_Arel_Nodes_Group o, a
-
visit o.expr, a
-
end
-
-
1
def visit_Arel_Nodes_NamedFunction o, a
-
"#{o.name}(#{o.distinct ? 'DISTINCT ' : ''}#{o.expressions.map { |x|
-
visit x, a
-
}.join(', ')})#{o.alias ? " AS #{visit o.alias, a}" : ''}"
-
end
-
-
1
def visit_Arel_Nodes_Extract o, a
-
"EXTRACT(#{o.field.to_s.upcase} FROM #{visit o.expr, a})#{o.alias ? " AS #{visit o.alias, a}" : ''}"
-
end
-
-
1
def visit_Arel_Nodes_Count o, a
-
2
"COUNT(#{o.distinct ? 'DISTINCT ' : ''}#{o.expressions.map { |x|
-
2
visit x, a
-
}.join(', ')})#{o.alias ? " AS #{visit o.alias, a}" : ''}"
-
end
-
-
1
def visit_Arel_Nodes_Sum o, a
-
"SUM(#{o.distinct ? 'DISTINCT ' : ''}#{o.expressions.map { |x|
-
visit x, a }.join(', ')})#{o.alias ? " AS #{visit o.alias, a}" : ''}"
-
end
-
-
1
def visit_Arel_Nodes_Max o, a
-
"MAX(#{o.distinct ? 'DISTINCT ' : ''}#{o.expressions.map { |x|
-
visit x, a }.join(', ')})#{o.alias ? " AS #{visit o.alias, a}" : ''}"
-
end
-
-
1
def visit_Arel_Nodes_Min o, a
-
"MIN(#{o.distinct ? 'DISTINCT ' : ''}#{o.expressions.map { |x|
-
visit x, a }.join(', ')})#{o.alias ? " AS #{visit o.alias, a}" : ''}"
-
end
-
-
1
def visit_Arel_Nodes_Avg o, a
-
"AVG(#{o.distinct ? 'DISTINCT ' : ''}#{o.expressions.map { |x|
-
visit x, a }.join(', ')})#{o.alias ? " AS #{visit o.alias, a}" : ''}"
-
end
-
-
1
def visit_Arel_Nodes_TableAlias o, a
-
"#{visit o.relation, a} #{quote_table_name o.name}"
-
end
-
-
1
def visit_Arel_Nodes_Between o, a
-
a = o.left if Arel::Attributes::Attribute === o.left
-
"#{visit o.left, a} BETWEEN #{visit o.right, a}"
-
end
-
-
1
def visit_Arel_Nodes_GreaterThanOrEqual o, a
-
a = o.left if Arel::Attributes::Attribute === o.left
-
"#{visit o.left, a} >= #{visit o.right, a}"
-
end
-
-
1
def visit_Arel_Nodes_GreaterThan o, a
-
a = o.left if Arel::Attributes::Attribute === o.left
-
"#{visit o.left, a} > #{visit o.right, a}"
-
end
-
-
1
def visit_Arel_Nodes_LessThanOrEqual o, a
-
a = o.left if Arel::Attributes::Attribute === o.left
-
"#{visit o.left, a} <= #{visit o.right, a}"
-
end
-
-
1
def visit_Arel_Nodes_LessThan o, a
-
a = o.left if Arel::Attributes::Attribute === o.left
-
"#{visit o.left, a} < #{visit o.right, a}"
-
end
-
-
1
def visit_Arel_Nodes_Matches o, a
-
a = o.left if Arel::Attributes::Attribute === o.left
-
"#{visit o.left, a} LIKE #{visit o.right, a}"
-
end
-
-
1
def visit_Arel_Nodes_DoesNotMatch o, a
-
a = o.left if Arel::Attributes::Attribute === o.left
-
"#{visit o.left, a} NOT LIKE #{visit o.right, a}"
-
end
-
-
1
def visit_Arel_Nodes_JoinSource o, a
-
[
-
7
(visit(o.left, a) if o.left),
-
2
o.right.map { |j| visit j, a }.join(' ')
-
7
].compact.join ' '
-
end
-
-
1
def visit_Arel_Nodes_StringJoin o, a
-
visit o.left, a
-
end
-
-
1
def visit_Arel_Nodes_OuterJoin o, a
-
"LEFT OUTER JOIN #{visit o.left, a} #{visit o.right, a}"
-
end
-
-
1
def visit_Arel_Nodes_InnerJoin o, a
-
2
s = "INNER JOIN #{visit o.left, a}"
-
2
if o.right
-
2
s << SPACE
-
2
s << visit(o.right, a)
-
end
-
2
s
-
end
-
-
1
def visit_Arel_Nodes_On o, a
-
2
"ON #{visit o.expr, a}"
-
end
-
-
1
def visit_Arel_Nodes_Not o, a
-
"NOT (#{visit o.expr, a})"
-
end
-
-
1
def visit_Arel_Table o, a
-
15
if o.table_alias
-
"#{quote_table_name o.name} #{quote_table_name o.table_alias}"
-
else
-
15
quote_table_name o.name
-
end
-
end
-
-
1
def visit_Arel_Nodes_In o, a
-
if Array === o.right && o.right.empty?
-
'1=0'
-
else
-
a = o.left if Arel::Attributes::Attribute === o.left
-
"#{visit o.left, a} IN (#{visit o.right, a})"
-
end
-
end
-
-
1
def visit_Arel_Nodes_NotIn o, a
-
if Array === o.right && o.right.empty?
-
'1=1'
-
else
-
a = o.left if Arel::Attributes::Attribute === o.left
-
"#{visit o.left, a} NOT IN (#{visit o.right, a})"
-
end
-
end
-
-
1
def visit_Arel_Nodes_And o, a
-
8
o.children.map { |x| visit x, a }.join ' AND '
-
end
-
-
1
def visit_Arel_Nodes_Or o, a
-
"#{visit o.left, a} OR #{visit o.right, a}"
-
end
-
-
1
def visit_Arel_Nodes_Assignment o, a
-
right = quote(o.right, column_for(o.left))
-
"#{visit o.left, a} = #{right}"
-
end
-
-
1
def visit_Arel_Nodes_Equality o, a
-
6
right = o.right
-
-
6
a = o.left if Arel::Attributes::Attribute === o.left
-
6
if right.nil?
-
"#{visit o.left, a} IS NULL"
-
else
-
6
"#{visit o.left, a} = #{visit right, a}"
-
end
-
end
-
-
1
def visit_Arel_Nodes_NotEqual o, a
-
right = o.right
-
-
a = o.left if Arel::Attributes::Attribute === o.left
-
if right.nil?
-
"#{visit o.left, a} IS NOT NULL"
-
else
-
"#{visit o.left, a} != #{visit right, a}"
-
end
-
end
-
-
1
def visit_Arel_Nodes_As o, a
-
"#{visit o.left, a} AS #{visit o.right, a}"
-
end
-
-
1
def visit_Arel_Nodes_UnqualifiedColumn o, a
-
"#{quote_column_name o.name}"
-
end
-
-
1
def visit_Arel_Attributes_Attribute o, a
-
13
join_name = o.relation.table_alias || o.relation.name
-
13
"#{quote_table_name join_name}.#{quote_column_name o.name}"
-
end
-
1
alias :visit_Arel_Attributes_Integer :visit_Arel_Attributes_Attribute
-
1
alias :visit_Arel_Attributes_Float :visit_Arel_Attributes_Attribute
-
1
alias :visit_Arel_Attributes_Decimal :visit_Arel_Attributes_Attribute
-
1
alias :visit_Arel_Attributes_String :visit_Arel_Attributes_Attribute
-
1
alias :visit_Arel_Attributes_Time :visit_Arel_Attributes_Attribute
-
1
alias :visit_Arel_Attributes_Boolean :visit_Arel_Attributes_Attribute
-
-
31
def literal o, a; o end
-
-
1
alias :visit_Arel_Nodes_BindParam :literal
-
1
alias :visit_Arel_Nodes_SqlLiteral :literal
-
1
alias :visit_Arel_SqlLiteral :literal # This is deprecated
-
1
alias :visit_Bignum :literal
-
1
alias :visit_Fixnum :literal
-
-
1
def quoted o, a
-
quote(o, column_for(a))
-
end
-
-
1
alias :visit_ActiveSupport_Multibyte_Chars :quoted
-
1
alias :visit_ActiveSupport_StringInquirer :quoted
-
1
alias :visit_BigDecimal :quoted
-
1
alias :visit_Class :quoted
-
1
alias :visit_Date :quoted
-
1
alias :visit_DateTime :quoted
-
1
alias :visit_FalseClass :quoted
-
1
alias :visit_Float :quoted
-
1
alias :visit_Hash :quoted
-
1
alias :visit_NilClass :quoted
-
1
alias :visit_String :quoted
-
1
alias :visit_Symbol :quoted
-
1
alias :visit_Time :quoted
-
1
alias :visit_TrueClass :quoted
-
-
1
def visit_Arel_Nodes_InfixOperation o, a
-
"#{visit o.left, a} #{o.operator} #{visit o.right, a}"
-
end
-
-
1
alias :visit_Arel_Nodes_Addition :visit_Arel_Nodes_InfixOperation
-
1
alias :visit_Arel_Nodes_Subtraction :visit_Arel_Nodes_InfixOperation
-
1
alias :visit_Arel_Nodes_Multiplication :visit_Arel_Nodes_InfixOperation
-
1
alias :visit_Arel_Nodes_Division :visit_Arel_Nodes_InfixOperation
-
-
1
def visit_Array o, a
-
o.map { |x| visit x, a }.join(', ')
-
end
-
-
1
def quote value, column = nil
-
return value if Arel::Nodes::SqlLiteral === value
-
@connection.quote value, column
-
end
-
-
1
def quote_table_name name
-
28
return name if Arel::Nodes::SqlLiteral === name
-
28
@quoted_tables[name] ||= @connection.quote_table_name(name)
-
end
-
-
1
def quote_column_name name
-
37
@quoted_columns[name] ||= Arel::Nodes::SqlLiteral === name ? name : @connection.quote_column_name(name)
-
end
-
end
-
end
-
end
-
1
module Arel
-
1
module Visitors
-
1
class Visitor
-
1
def accept object
-
15
visit object
-
end
-
-
1
private
-
-
1
DISPATCH = Hash.new do |hash, visitor_class|
-
2
hash[visitor_class] =
-
Hash.new do |method_hash, node_class|
-
22
method_hash[node_class] = "visit_#{(node_class.name || '').gsub('::', '_')}"
-
end
-
end
-
-
1
def dispatch
-
140
DISPATCH[self.class]
-
end
-
-
1
def visit object, attribute = nil
-
140
send dispatch[object.class], object, attribute
-
rescue NoMethodError => e
-
raise e if respond_to?(dispatch[object.class], true)
-
superklass = object.class.ancestors.find { |klass|
-
respond_to?(dispatch[klass], true)
-
}
-
raise(TypeError, "Cannot visit #{object.class}") unless superklass
-
dispatch[object.class] = dispatch[superklass]
-
retry
-
end
-
end
-
end
-
end
-
1
module Arel
-
1
module Visitors
-
1
class WhereSql < Arel::Visitors::ToSql
-
1
def visit_Arel_Nodes_SelectCore o, a
-
"WHERE #{o.wheres.map { |x| visit x, a }.join ' AND ' }"
-
end
-
end
-
end
-
end
-
1
module Arel
-
1
module WindowPredications
-
-
1
def over(expr = nil)
-
Nodes::Over.new(self, expr)
-
end
-
-
end
-
end
-
1
module Bootstrap
-
1
class << self
-
# Inspired by Kaminari
-
1
def load!
-
1
register_compass_extension if compass?
-
-
1
if rails?
-
1
register_rails_engine
-
end
-
-
1
configure_sass
-
end
-
-
# Paths
-
1
def gem_path
-
1
@gem_path ||= File.expand_path '..', File.dirname(__FILE__)
-
end
-
-
1
def stylesheets_path
-
1
File.join assets_path, 'stylesheets'
-
end
-
-
1
def fonts_path
-
File.join assets_path, 'fonts'
-
end
-
-
1
def javascripts_path
-
File.join assets_path, 'javascripts'
-
end
-
-
1
def assets_path
-
1
@assets_path ||= File.join gem_path, 'assets'
-
end
-
-
# Environment detection helpers
-
1
def asset_pipeline?
-
defined?(::Sprockets)
-
end
-
-
1
def compass?
-
1
defined?(::Compass)
-
end
-
-
1
def rails?
-
1
defined?(::Rails)
-
end
-
-
1
private
-
-
1
def configure_sass
-
1
require 'sass'
-
-
1
::Sass.load_paths << stylesheets_path
-
-
# bootstrap requires minimum precision of 10, see https://github.com/twbs/bootstrap-sass/issues/409
-
1
::Sass::Script::Number.precision = [10, ::Sass::Script::Number.precision].max
-
end
-
-
1
def register_compass_extension
-
::Compass::Frameworks.register(
-
'bootstrap',
-
:path => gem_path,
-
:stylesheets_directory => stylesheets_path,
-
:templates_directory => File.join(gem_path, 'templates')
-
)
-
end
-
-
1
def register_rails_engine
-
1
require 'bootstrap-sass/engine'
-
end
-
end
-
end
-
-
1
Bootstrap.load!
-
1
module Bootstrap
-
1
module Rails
-
1
class Engine < ::Rails::Engine
-
1
initializer 'bootstrap-sass.assets.precompile' do |app|
-
1
%w(stylesheets javascripts fonts images).each do |sub|
-
4
app.config.assets.paths << root.join('assets', sub)
-
end
-
1
app.config.assets.precompile << %r(bootstrap/glyphicons-halflings-regular\.(?:eot|svg|ttf|woff)$)
-
end
-
end
-
end
-
end
-
1
require 'byebug/core'
-
1
require 'byebug/attacher'
-
1
module Byebug
-
#
-
# Enters byebug right before (or right after if _before_ is false) return
-
# events occur. Before entering byebug the init script is read.
-
#
-
1
def self.attach(steps_out, before)
-
start
-
self.debugged_program = $PROGRAM_NAME
-
run_init_script(StringIO.new)
-
current_context.step_out(steps_out, before)
-
end
-
end
-
-
#
-
# Adds a `byebug` method to the Kernel module.
-
#
-
# Dropping a `byebug` call anywhere in your code, you get a debug prompt.
-
#
-
1
module Kernel
-
1
def byebug(steps_out = 1, before = true)
-
Byebug.attach(steps_out + 1, before)
-
end
-
-
1
alias_method :debugger, :byebug
-
end
-
1
module Byebug
-
#
-
# Implements breakpoints
-
#
-
1
class Breakpoint
-
#
-
# First breakpoint, in order of creation
-
#
-
1
def self.first
-
Byebug.breakpoints.first
-
end
-
-
#
-
# Last breakpoint, in order of creation
-
#
-
1
def self.last
-
Byebug.breakpoints.last
-
end
-
-
#
-
# Adds a new breakpoint
-
#
-
# @param [String] file
-
# @param [Fixnum] line
-
# @param [String] expr
-
#
-
1
def self.add(file, line, expr = nil)
-
breakpoint = Breakpoint.new(file, line, expr)
-
Byebug.breakpoints << breakpoint
-
breakpoint
-
end
-
-
#
-
# Removes a breakpoint
-
#
-
# @param [integer] breakpoint number
-
#
-
1
def self.remove(id)
-
Byebug.breakpoints.reject! { |b| b.id == id }
-
end
-
-
#
-
# True if there's no breakpoints
-
#
-
1
def self.none?
-
Byebug.breakpoints.empty?
-
end
-
-
#
-
# Prints all information associated to the breakpoint
-
#
-
1
def inspect
-
meths = %w(id pos source expr hit_condition hit_count hit_value enabled?)
-
values = meths.map do |field|
-
"#{field}: #{send(field)}"
-
end.join(', ')
-
"#<Byebug::Breakpoint #{values}>"
-
end
-
end
-
end
-
1
require 'columnize'
-
1
require 'forwardable'
-
1
require 'byebug/helper'
-
-
1
module Byebug
-
#
-
# Parent class of all byebug commands.
-
#
-
# Subclasses need to implement a `regexp` and an `execute` command.
-
#
-
1
class Command
-
1
Subcmd = Struct.new(:name, :min, :help)
-
-
1
def initialize(state)
-
@match, @state = nil, state
-
end
-
-
1
def match(input)
-
@match = regexp.match(input)
-
end
-
-
1
protected
-
-
1
extend Forwardable
-
1
def_delegators :@state, :errmsg, :puts
-
-
1
def confirm(msg)
-
@state.confirm(msg) == 'y'
-
end
-
-
1
def bb_eval(str, b = get_binding)
-
eval(str, b)
-
rescue StandardError, ScriptError => e
-
at = eval('Thread.current.backtrace_locations', b)
-
puts "#{at.shift}: #{e.class} Exception(#{e.message})"
-
at.each { |path| puts "\tfrom #{path}" }
-
nil
-
end
-
-
1
def bb_warning_eval(str, b = get_binding)
-
eval(str, b)
-
rescue StandardError, ScriptError => e
-
puts "#{e.class} Exception: #{e.message}"
-
nil
-
end
-
-
1
def get_binding(pos = @state.frame_pos)
-
@state.context ? @state.context.frame_binding(pos) : TOPLEVEL_BINDING
-
end
-
-
1
class << self
-
1
attr_accessor :allow_in_control
-
1
attr_writer :allow_in_post_mortem, :always_run
-
-
1
def allow_in_post_mortem
-
!defined?(@allow_in_post_mortem) ? true : false
-
end
-
-
1
def always_run
-
@always_run ||= 0
-
end
-
-
1
def help(args = nil)
-
if args && args[1]
-
output = format_subcmd(args[1])
-
else
-
output = description.gsub(/^ +/, '') + "\n"
-
output += format_subcmds if defined? self::Subcommands
-
end
-
output
-
end
-
-
1
def find(subcmds, str)
-
str.downcase!
-
subcmds.each do |subcmd|
-
if (str.size >= subcmd.min) && (subcmd.name[0..str.size - 1] == str)
-
return subcmd
-
end
-
end
-
-
nil
-
end
-
-
1
def format_subcmd(subcmd_name)
-
subcmd = find(self::Subcommands, subcmd_name)
-
return "Invalid \"#{names.join('|')}\" " \
-
"subcommand \"#{args[1]}\"." unless subcmd
-
-
"\n #{subcmd.help}.\n\n"
-
end
-
-
1
def format_subcmds
-
header = names.join('|')
-
s = " List of \"#{header}\" subcommands:\n --\n"
-
w = self::Subcommands.map(&:name).max_by(&:size).size
-
self::Subcommands.each do |subcmd|
-
s += format(" %s %-#{w}s -- %s\n", header, subcmd.name, subcmd.help)
-
end
-
s + "\n"
-
end
-
-
1
def commands
-
49
@commands ||= []
-
end
-
-
1
def inherited(klass)
-
49
commands << klass
-
end
-
-
1
def load_commands
-
1
Dir.glob(File.expand_path('../commands/*.rb', __FILE__)).each do |file|
-
32
require file
-
end
-
-
1
Byebug.constants.grep(/Functions$/).map do |name|
-
9
include Byebug.const_get(name)
-
end
-
end
-
end
-
end
-
-
1
Command.load_commands
-
end
-
1
module Byebug
-
#
-
# Implements breakpoint functionality
-
#
-
1
class BreakCommand < Command
-
1
self.allow_in_post_mortem = false
-
1
self.allow_in_control = true
-
-
1
POSITION_REGEXP = '(?:(\d+)|(.+?)[:.#]([^.:\s]+))'
-
-
1
def regexp
-
/^\s* b(?:reak)? (?:\s+ #{POSITION_REGEXP})? (?:\s+(.+))? \s*$/x
-
end
-
-
1
def execute
-
return puts(self.class.help) if self.class.names.include?(@match[0])
-
-
if @match[1]
-
line, _, _, expr = @match.captures
-
else
-
_, file, line, expr = @match.captures
-
end
-
-
if expr && file.nil? && line.nil?
-
return errmsg("Invalid breakpoint location: #{expr}")
-
elsif expr && expr !~ /^\s*if\s+(.+)/
-
return errmsg("Expecting \"if\" in breakpoint condition, got: #{expr}")
-
else
-
expr = $1
-
end
-
-
if file.nil? && !@state.context
-
return errmsg('We are not in a state that has an associated file')
-
end
-
-
file = @state.file if file.nil?
-
line = @state.line.to_s if line.nil?
-
-
if line =~ /^\d+$/
-
path = CommandProcessor.canonic_file(file)
-
return errmsg("No file named #{path}") unless File.exist?(file)
-
-
l, n = line.to_i, File.foreach(file).count
-
return errmsg("There are only #{n} lines in file #{path}") if l > n
-
-
autoreload = Setting[:autoreload]
-
possible_lines = LineCache.trace_line_numbers(file, autoreload)
-
unless possible_lines.member?(l)
-
return errmsg("Line #{l} is not a valid breakpoint in file #{path}")
-
end
-
-
b = Breakpoint.add(file, l, expr)
-
puts "Created breakpoint #{b.id} at #{path}:#{l}"
-
-
unless syntax_valid?(expr)
-
errmsg("Incorrect expression \"#{expr}\"; breakpoint disabled.")
-
b.enabled = false
-
end
-
-
else
-
kl = bb_warning_eval(file)
-
return errmsg("Unknown class #{file}") unless kl && kl.is_a?(Module)
-
-
class_name, method = kl.name, line.intern
-
b = Breakpoint.add(class_name, method, expr)
-
puts "Created breakpoint #{b.id} at #{class_name}::#{method}"
-
end
-
end
-
-
1
class << self
-
1
def names
-
%w(break)
-
end
-
-
1
def description
-
%{b[reak] file:line [if expr]
-
b[reak] class(.|#)method [if expr]
-
-
Set breakpoint to some position, (optionally) if expr == true}
-
end
-
end
-
end
-
end
-
1
module Byebug
-
#
-
# Implements exception catching.
-
#
-
# Enables the user to catch unhandled assertion when they happen.
-
#
-
1
class CatchCommand < Command
-
1
self.allow_in_control = true
-
-
1
def regexp
-
/^\s* cat(?:ch)? (?:\s+(\S+))? (?:\s+(off))? \s*$/x
-
end
-
-
1
def execute
-
excn = @match[1]
-
return info_catch unless excn
-
-
if !@match[2]
-
if 'off' == @match[1]
-
Byebug.catchpoints.clear if
-
confirm('Delete all catchpoints? (y or n) ')
-
else
-
puts "Warning #{@match[1]} is not known to be a Class" unless
-
bb_eval "#{@match[1]}.is_a?(Class)", get_binding
-
Byebug.add_catchpoint @match[1]
-
puts "Catching exception #{@match[1]}."
-
end
-
elsif @match[2] != 'off'
-
errmsg "Off expected. Got #{@match[2]}"
-
elsif Byebug.catchpoints.member?(@match[1])
-
Byebug.catchpoints.delete @match[1]
-
errmsg "Catch for exception #{match[1]} removed"
-
else
-
errmsg "Catch for exception #{@match[1]} not found"
-
end
-
end
-
-
1
class << self
-
1
def names
-
%w(catch)
-
end
-
-
1
def description
-
%(cat[ch][ (off|<exception>[ off])]
-
-
"catch" lists catchpoints.
-
"catch off" deletes all catchpoints.
-
"catch <exception>" enables handling <exception>.
-
"catch <exception> off" disables handling <exception>.)
-
end
-
end
-
end
-
end
-
1
module Byebug
-
#
-
# Implements conditions on breakpoints.
-
#
-
# Adds the ability to stop on breakpoints only under certain conditions.
-
#
-
1
class ConditionCommand < Command
-
1
self.allow_in_post_mortem = false
-
-
1
def regexp
-
/^\s* cond(?:ition)? (?:\s+(\d+)(?:\s+(.*))?)? \s*$/x
-
end
-
-
1
def execute
-
return puts(ConditionCommand.help) unless @match[1]
-
-
breakpoints = Byebug.breakpoints.sort_by { |b| b.id }
-
return errmsg('No breakpoints have been set') unless breakpoints.any?
-
-
pos, err = get_int(@match[1], 'Condition', 1)
-
return errmsg(err) if err
-
-
breakpoint = breakpoints.find { |b| b.id == pos }
-
return errmsg('Invalid breakpoint id. Use "info breakpoint" to find ' \
-
'out the correct id') unless breakpoint
-
-
return errmsg("Incorrect expression \"#{@match[2]}\", " \
-
'breakpoint not changed') unless syntax_valid?(@match[2])
-
-
breakpoint.expr = @match[2]
-
end
-
-
1
class << self
-
1
def names
-
%w(condition)
-
end
-
-
1
def description
-
%(cond[ition] <n>[ expr]
-
-
Specify breakpoint number <n> to break only if <expr> is true. <n> is
-
an integer and <expr> is an expression to be evaluated whenever
-
breakpoint <n> is reached. If no expression is specified, the
-
condition is removed.)
-
end
-
end
-
end
-
end
-
1
module Byebug
-
#
-
# Implements the continue command.
-
#
-
# Allows the user to continue execution until the next stopping point, a
-
# specific line number or until program termination.
-
#
-
1
class ContinueCommand < Command
-
1
def regexp
-
/^\s* c(?:ont(?:inue)?)? (?:\s+(\S+))? \s*$/x
-
end
-
-
1
def execute
-
if @match[1]
-
num, err = get_int(@match[1], 'Continue', 0, nil)
-
return errmsg(err) unless num
-
-
filename = File.expand_path(@state.file)
-
unless LineCache.trace_line_numbers(filename).member?(num)
-
return errmsg("Line #{num} is not a valid stopping point in file")
-
end
-
-
Breakpoint.add(filename, num)
-
end
-
-
@state.proceed
-
end
-
-
1
class << self
-
1
def names
-
%w(continue)
-
end
-
-
1
def description
-
%(c[ont[inue]][ <n>]
-
-
Run until program ends, hits a breakpoint or reaches line <n>.)
-
end
-
end
-
end
-
end
-
1
module Byebug
-
#
-
# Implements breakpoint deletion.
-
#
-
1
class DeleteCommand < Command
-
1
self.allow_in_post_mortem = false
-
1
self.allow_in_control = true
-
-
1
def regexp
-
/^\s* del(?:ete)? (?:\s+(.*))?$/x
-
end
-
-
1
def execute
-
unless @match[1]
-
Byebug.breakpoints.clear if confirm('Delete all breakpoints? (y/n) ')
-
-
return nil
-
end
-
-
@match[1].split(/[ \t]+/).each do |number|
-
pos, err = get_int(number, 'Delete', 1)
-
-
return errmsg(err) unless pos
-
-
unless Breakpoint.remove(pos)
-
return errmsg("No breakpoint number #{pos}")
-
end
-
end
-
end
-
-
1
class << self
-
1
def names
-
%w(delete)
-
end
-
-
1
def description
-
%(del[ete][ nnn...]
-
-
Without and argument, deletes all breakpoints. With integer
-
arguments, it deletes specific breakpoints.)
-
end
-
end
-
end
-
end
-
1
module Byebug
-
#
-
# Custom display utilities.
-
#
-
1
module DisplayFunctions
-
1
def display_expression(exp)
-
"#{exp} = #{bb_warning_eval(exp).inspect}"
-
end
-
-
1
def active_display_expressions?
-
@state.display.select { |d| d[0] }.size > 0
-
end
-
-
1
def print_display_expressions
-
n = 1
-
@state.display.each do |d|
-
puts "#{n}: #{display_expression(d[1])}" if d[0]
-
n += 1
-
end
-
end
-
end
-
-
#
-
# Implements the functionality of adding custom expressions to be displayed
-
# every time the debugger stops.
-
#
-
1
class AddDisplayCommand < Command
-
1
self.allow_in_post_mortem = false
-
-
1
def regexp
-
/^\s* disp(?:lay)? \s+ (.+) \s*$/x
-
end
-
-
1
def execute
-
exp = @match[1]
-
@state.display.push [true, exp]
-
puts "#{@state.display.size}: #{display_expression(exp)}"
-
end
-
-
1
class << self
-
1
def names
-
%w(display)
-
end
-
-
1
def description
-
%(disp[lay] <expression>
-
-
Add <expression> into display expression list.)
-
end
-
end
-
end
-
-
#
-
# Displays the value of enabled expressions.
-
#
-
1
class DisplayCommand < Command
-
1
self.allow_in_post_mortem = false
-
-
1
def self.always_run
-
2
-
end
-
-
1
def regexp
-
/^\s* disp(?:lay)? \s*$/x
-
end
-
-
1
def execute
-
print_display_expressions
-
end
-
-
1
class << self
-
1
def names
-
%w(display)
-
end
-
-
1
def description
-
%(disp[lay] Display expression list.)
-
end
-
end
-
end
-
end
-
1
module Byebug
-
#
-
# Edit a file from byebug's prompt.
-
#
-
1
class EditCommand < Command
-
1
self.allow_in_control = true
-
-
1
def regexp
-
/^\s* ed(?:it)? (?:\s+(\S+))? \s*$/x
-
end
-
-
1
def execute
-
if !@match[1]
-
unless @state.file
-
return errmsg "We are not in a state that has an associated file.\n"
-
end
-
file = @state.file
-
line = @state.line if @state.line
-
elsif (@pos_match = /([^:]+)[:]([0-9]+)/.match(@match[1]))
-
file, line = @pos_match.captures
-
elsif File.exist?(@match[1])
-
file = @match[1]
-
else
-
return errmsg "Invalid file[:line] number specification: #{@match[1]}\n"
-
end
-
-
editor = ENV['EDITOR'] || 'vim'
-
-
if File.readable?(file)
-
system("#{editor} +#{line} #{file}") if line
-
system("#{editor} #{file}") unless line
-
else
-
errmsg "File \"#{file}\" is not readable.\n"
-
end
-
end
-
-
1
class << self
-
1
def names
-
%w(edit)
-
end
-
-
1
def description
-
%(edit[ file:lineno] Edit specified files.
-
-
With no argument, edits file containing most recent line listed.
-
Editing targets can also be specified to start editing at a specific
-
line in a specific file.)
-
end
-
end
-
end
-
end
-
1
module Byebug
-
#
-
# Mixin to assist command parsing
-
#
-
1
module EnableDisableFunctions
-
1
def enable_disable_breakpoints(is_enable, args)
-
return errmsg('No breakpoints have been set') if Breakpoint.none?
-
-
all_breakpoints = Byebug.breakpoints.sort_by { |b| b.id }
-
if args.empty?
-
selected_breakpoints = all_breakpoints
-
else
-
selected_ids = []
-
args.each do |pos|
-
last_id = all_breakpoints.last.id
-
pos, err = get_int(pos, "#{is_enable} breakpoints", 1, last_id)
-
return errmsg(err) unless pos
-
-
selected_ids << pos
-
end
-
selected_breakpoints = all_breakpoints.select do
-
|b| selected_ids.include?(b.id)
-
end
-
end
-
-
selected_breakpoints.each do |b|
-
enabled = ('enable' == is_enable)
-
if enabled && !syntax_valid?(b.expr)
-
return errmsg("Expression \"#{b.expr}\" syntactically incorrect; " \
-
'breakpoint remains disabled.')
-
end
-
-
b.enabled = enabled
-
end
-
end
-
-
1
def enable_disable_display(is_enable, args)
-
if 0 == @state.display.size
-
return errmsg('No display expressions have been set')
-
end
-
-
args.each do |pos|
-
pos, err = get_int(pos, "#{is_enable} display", 1, @state.display.size)
-
return errmsg(err) unless err.nil?
-
-
@state.display[pos - 1][0] = ('enable' == is_enable)
-
end
-
end
-
end
-
-
#
-
# Enabling or disabling custom display expressions or breakpoints.
-
#
-
1
class EnableDisableCommand < Command
-
Subcommands = [
-
['breakpoints', 2, 'Enable/disable breakpoints. Give breakpoint ' \
-
'numbers (separated by spaces) as arguments or no ' \
-
'argument at all if you want to enable/disable ' \
-
'every breakpoint'],
-
['display', 2, 'Enable/disable some expressions to be displayed when ' \
-
' when program stops. Arguments are the code numbers ' \
-
'of the expressions to resume/stop displaying. Do ' \
-
'"info display" to see the current list of code ' \
-
'numbers']
-
].map do |name, min, help|
-
2
Subcmd.new(name, min, help)
-
1
end unless defined?(Subcommands)
-
-
1
def regexp
-
/^\s* (dis|en)(?:able)? (?:\s+(.+))? \s*$/x
-
end
-
-
1
def execute
-
cmd = @match[1] == 'dis' ? 'disable' : 'enable'
-
-
return errmsg("\"#{cmd}\" must be followed by \"display\", " \
-
"\"breakpoints\" or breakpoint ids") unless @match[2]
-
-
args = @match[2].split(/[ \t]+/)
-
param = args.shift
-
subcmd = Command.find(Subcommands, param)
-
if subcmd
-
send("#{cmd}_#{subcmd.name}", args)
-
else
-
send("#{cmd}_breakpoints", args.unshift(param))
-
end
-
end
-
-
1
def enable_breakpoints(args)
-
enable_disable_breakpoints('enable', args)
-
end
-
-
1
def enable_display(args)
-
enable_disable_display('enable', args)
-
end
-
-
1
def disable_breakpoints(args)
-
enable_disable_breakpoints('disable', args)
-
end
-
-
1
def disable_display(args)
-
enable_disable_display('disable', args)
-
end
-
-
1
class << self
-
1
def names
-
%w((en|dis)able)
-
end
-
-
1
def description
-
%{(en|dis)[able][[ (breakpoints|display)][ n1[ n2[ ...[ nn]]]]]
-
-
Enables or disables breakpoints or displays.
-
-
"enable" by itself enables all breakpoints, just like
-
"enable breakpoints". On the other side, "disable" or
-
"disable breakpoints" disable all breakpoints.
-
-
You can also specify a space separated list of breakpoint numbers to
-
enable or disable specific breakpoints. You can use either
-
"enable <id1> ... <idn>" or "enable breakpoints <id1> ... <idn>" and
-
the same with "disable".
-
-
If instead of "breakpoints" you specify "display", the command will
-
work exactly the same way, but displays will get enabled/disabled
-
instead of breakpoints.}
-
end
-
end
-
end
-
end
-
1
require 'English'
-
1
require 'pp'
-
-
1
module Byebug
-
#
-
# Utilities used by the eval command
-
#
-
1
module EvalFunctions
-
1
def run_with_binding
-
binding = get_binding
-
yield binding
-
end
-
end
-
-
#
-
# Evaluation of expressions from byebug's prompt.
-
#
-
1
class EvalCommand < Command
-
1
self.allow_in_control = true
-
-
1
def match(input)
-
@input = input
-
super
-
end
-
-
1
def regexp
-
/^\s* (p|e(?:val)?)\s+/x
-
end
-
-
1
def execute
-
expr = @match ? @match.post_match : @input
-
run_with_binding do |b|
-
if Setting[:stack_on_error]
-
puts "#{bb_eval(expr, b).inspect}"
-
else
-
puts "#{bb_warning_eval(expr, b).inspect}"
-
end
-
end
-
rescue
-
puts "#{$ERROR_INFO.class} Exception: #{$ERROR_INFO.message}"
-
end
-
-
1
class << self
-
1
def names
-
%w(p eval)
-
end
-
-
1
def description
-
%{(p|e[val]) <expression>
-
-
Evaluates <expression> and prints its value.
-
-
* NOTE - unknown input is automatically evaluated, to turn this off
-
use 'set noautoeval'.}
-
end
-
end
-
end
-
-
#
-
# Evaluation and pretty printing from byebug's prompt.
-
#
-
1
class PPCommand < Command
-
1
self.allow_in_control = true
-
-
1
def regexp
-
/^\s* pp \s+/x
-
end
-
-
1
def execute
-
out = StringIO.new
-
run_with_binding do |b|
-
if Setting[:stack_on_error]
-
PP.pp(bb_eval(@match.post_match, b), out)
-
else
-
PP.pp(bb_warning_eval(@match.post_match, b), out)
-
end
-
end
-
puts out.string
-
rescue
-
out.puts $ERROR_INFO.message
-
end
-
-
1
class << self
-
1
def names
-
%w(pp)
-
end
-
-
1
def description
-
%(pp <expression>
-
-
Evaluates <expression> and pretty-prints its value.)
-
end
-
end
-
end
-
-
#
-
# Evaluation, pretty printing and columnizing from byebug's prompt.
-
#
-
1
class PutLCommand < Command
-
1
include Columnize
-
1
self.allow_in_control = true
-
-
1
def regexp
-
/^\s* putl\s+/x
-
end
-
-
1
def execute
-
out = StringIO.new
-
run_with_binding do |b|
-
if Setting[:stack_on_error]
-
vals = bb_eval(@match.post_match, b)
-
else
-
vals = bb_warning_eval(@match.post_match, b)
-
end
-
if vals.is_a?(Array)
-
vals = vals.map { |item| item.to_s }
-
puts "#{columnize(vals, Setting[:width])}"
-
else
-
PP.pp(vals, out)
-
puts out.string
-
end
-
end
-
rescue
-
out.puts $ERROR_INFO.message
-
end
-
-
1
class << self
-
1
def names
-
%w(putl)
-
end
-
-
1
def description
-
%(putl <expression>
-
-
Evaluates <expression>, an array, and columnize its value.)
-
end
-
end
-
end
-
-
#
-
# Evaluation, pretty printing, columnizing and sorting from byebug's prompt
-
#
-
1
class PSCommand < Command
-
1
include Columnize
-
1
self.allow_in_control = true
-
-
1
def regexp
-
/^\s* ps\s+/x
-
end
-
-
1
def execute
-
out = StringIO.new
-
run_with_binding do |b|
-
if Setting[:stack_on_error]
-
vals = bb_eval(@match.post_match, b)
-
else
-
vals = bb_warning_eval(@match.post_match, b)
-
end
-
if vals.is_a?(Array)
-
vals = vals.map { |item| item.to_s }
-
puts "#{columnize(vals.sort!, Setting[:width])}"
-
else
-
PP.pp(vals, out)
-
puts out.string
-
end
-
end
-
rescue
-
out.puts $ERROR_INFO.message
-
end
-
-
1
class << self
-
1
def names
-
%w(ps)
-
end
-
-
1
def description
-
%(ps <expression>
-
-
Evaluates <expression>, an array, sort and columnize its value.)
-
end
-
end
-
end
-
end
-
1
module Byebug
-
#
-
# Implements the finish functionality.
-
#
-
# Allows the user to continue execution until certain frames are finished.
-
#
-
1
class FinishCommand < Command
-
1
self.allow_in_post_mortem = false
-
-
1
def regexp
-
/^\s* fin(?:ish)? (?:\s+(\S+))? \s*$/x
-
end
-
-
1
def execute
-
max_frames = Context.stack_size - @state.frame_pos
-
if @match[1]
-
n_frames, err = get_int(@match[1], 'finish', 0, max_frames - 1)
-
return errmsg(err) unless n_frames
-
else
-
n_frames = 1
-
end
-
-
force = n_frames == 0 ? true : false
-
@state.context.step_out(@state.frame_pos + n_frames, force)
-
@state.frame_pos = 0
-
@state.proceed
-
end
-
-
1
class << self
-
1
def names
-
%w(finish)
-
end
-
-
1
def description
-
%(fin[ish][ n_frames] Execute until frame returns.
-
-
If no number is given, we run until the current frame returns. If a
-
number of frames `n_frames` is given, then we run until `n_frames`
-
return from the current position.)
-
end
-
end
-
end
-
end
-
# encoding: utf-8
-
1
module Byebug
-
#
-
# Mixin to assist command parsing
-
#
-
1
module FrameFunctions
-
1
def c_frame?(frame_no)
-
@state.context.frame_binding(frame_no).nil?
-
end
-
-
1
def switch_to_frame(frame_no)
-
frame_no >= 0 ? frame_no : Context.stack_size + frame_no
-
end
-
-
1
def navigate_to_frame(jump_no)
-
return if jump_no == 0
-
total_jumps, current_jumps, new_pos = jump_no.abs, 0, @state.frame_pos
-
step = jump_no / total_jumps
-
loop do
-
new_pos += step
-
return new_pos if new_pos < 0 || new_pos >= Context.stack_size
-
-
next if c_frame?(new_pos)
-
-
current_jumps += 1
-
break if current_jumps == total_jumps
-
end
-
new_pos
-
end
-
-
1
def adjust_frame(frame_pos, absolute)
-
if absolute
-
abs_frame_pos = switch_to_frame(frame_pos)
-
return errmsg("Can't navigate to c-frame") if c_frame?(abs_frame_pos)
-
else
-
abs_frame_pos = navigate_to_frame(frame_pos)
-
end
-
-
if abs_frame_pos >= Context.stack_size
-
return errmsg("Can't navigate beyond the oldest frame")
-
elsif abs_frame_pos < 0
-
return errmsg("Can't navigate beyond the newest frame")
-
end
-
-
@state.frame_pos = abs_frame_pos
-
@state.file = @state.context.frame_file @state.frame_pos
-
@state.line = @state.context.frame_line @state.frame_pos
-
@state.prev_line = nil
-
ListCommand.new(@state).execute
-
end
-
-
1
def get_frame_class(style, pos)
-
frame_class = style == 'short' ? '' : "#{@state.context.frame_class pos}"
-
frame_class == '' ? '' : "#{frame_class}."
-
end
-
-
1
def get_frame_block_and_method(pos)
-
frame_deco_regexp = /((?:block(?: \(\d+ levels\))?|rescue) in )?(.+)/
-
frame_deco_method = "#{@state.context.frame_method pos}"
-
frame_block_and_method = frame_deco_regexp.match(frame_deco_method)[1..2]
-
frame_block_and_method.map { |x| x.nil? ? '' : x }
-
end
-
-
1
def get_frame_args(style, pos)
-
args = @state.context.frame_args pos
-
return '' if args.empty?
-
-
locals = @state.context.frame_locals pos if style == 'long'
-
my_args = args.map do |arg|
-
case arg[0]
-
when :block
-
prefix, default = '&', 'block'
-
when :rest
-
prefix, default = '*', 'args'
-
else
-
prefix, default = '', nil
-
end
-
-
klass = style == 'long' && arg[1] ? "##{locals[arg[1]].class}" : ''
-
"#{prefix}#{arg[1] || default}#{klass}"
-
end
-
-
"(#{my_args.join(', ')})"
-
end
-
-
1
def get_frame_call(prefix, pos)
-
frame_block, frame_method = get_frame_block_and_method(pos)
-
frame_class = get_frame_class(Setting[:callstyle], pos)
-
frame_args = get_frame_args(Setting[:callstyle], pos)
-
-
call_str = frame_block + frame_class + frame_method + frame_args
-
-
max_call_str_size = Setting[:width] - prefix.size
-
if call_str.size > max_call_str_size
-
call_str = call_str[0..max_call_str_size - 5] + '...)'
-
end
-
-
call_str
-
end
-
-
1
def print_backtrace
-
realsize = Context.stack_size
-
calcedsize = @state.context.calced_stack_size
-
if calcedsize != realsize
-
if Byebug.post_mortem?
-
stacksize = calcedsize
-
else
-
errmsg "Byebug's stacksize (#{calcedsize}) should be #{realsize}. " \
-
"This might be a bug in byebug or ruby's debugging API's\n"
-
stacksize = realsize
-
end
-
else
-
stacksize = calcedsize
-
end
-
(0...stacksize).each do |idx|
-
print_frame(idx)
-
end
-
end
-
-
1
require 'pathname'
-
-
1
def shortpath(fullpath)
-
components = Pathname(fullpath).each_filename.to_a
-
return File.join(components) if components.size <= 2
-
-
File.join('...', components[-3..-1])
-
end
-
-
1
def print_frame(pos, mark_current = true)
-
fullpath = @state.context.frame_file(pos)
-
file = Setting[:fullpath] ? fullpath : shortpath(fullpath)
-
line = @state.context.frame_line(pos)
-
-
if mark_current
-
frame_str = (pos == @state.frame_pos) ? '--> ' : ' '
-
else
-
frame_str = ''
-
end
-
frame_str += c_frame?(pos) ? ' ͱ-- ' : ''
-
-
frame_str += format('#%-2d ', pos)
-
frame_str += get_frame_call frame_str, pos
-
file_line = "at #{CommandProcessor.canonic_file(file)}:#{line}"
-
if frame_str.size + file_line.size + 1 > Setting[:width]
-
frame_str += "\n #{file_line}"
-
else
-
frame_str += " #{file_line}"
-
end
-
-
puts frame_str
-
end
-
end
-
-
#
-
# Show current backtrace.
-
#
-
1
class WhereCommand < Command
-
1
def regexp
-
/^\s* (?:w(?:here)?|bt|backtrace) \s*$/x
-
end
-
-
1
def execute
-
print_backtrace
-
end
-
-
1
class << self
-
1
def names
-
%w(where backtrace bt)
-
end
-
-
1
def description
-
%(w[here]|bt|backtrace Display stack frames.
-
-
Print the entire stack frame. Each frame is numbered; the most recent
-
frame is 0. A frame number can be referred to in the "frame" command;
-
"up" and "down" add or subtract respectively to frame numbers shown.
-
The position of the current frame is marked with -->. C-frames hang
-
from their most immediate Ruby frame to indicate that they are not
-
navigable.)
-
end
-
end
-
end
-
-
#
-
# Move the current frame up in the backtrace.
-
#
-
1
class UpCommand < Command
-
1
def regexp
-
/^\s* u(?:p)? (?:\s+(\S+))? \s*$/x
-
end
-
-
1
def execute
-
pos, err = parse_steps(@match[1], 'Up')
-
return errmsg(err) unless pos
-
-
adjust_frame(pos, false)
-
end
-
-
1
class << self
-
1
def names
-
%w(up)
-
end
-
-
1
def description
-
%(up[ count] Move to higher frame.)
-
end
-
end
-
end
-
-
#
-
# Move the current frame down in the backtrace.
-
#
-
1
class DownCommand < Command
-
1
def regexp
-
/^\s* down (?:\s+(\S+))? \s*$/x
-
end
-
-
1
def execute
-
pos, err = parse_steps(@match[1], 'Down')
-
return errmsg(err) unless pos
-
-
adjust_frame(-pos, false)
-
end
-
-
1
class << self
-
1
def names
-
%w(down)
-
end
-
-
1
def description
-
%(down[ count] Move to lower frame.)
-
end
-
end
-
end
-
-
#
-
# Move to specific frames in the backtrace.
-
#
-
1
class FrameCommand < Command
-
1
def regexp
-
/^\s* f(?:rame)? (?:\s+(\S+))? \s*$/x
-
end
-
-
1
def execute
-
return print_frame @state.frame_pos unless @match[1]
-
-
pos, err = get_int(@match[1], 'Frame')
-
return errmsg(err) unless pos
-
-
adjust_frame(pos, true)
-
end
-
-
1
class << self
-
1
def names
-
%w(frame)
-
end
-
-
1
def description
-
%(f[rame][ frame-number]
-
-
Move the current frame to the specified frame number, or the 0 if no
-
frame-number has been given.
-
-
A negative number indicates position from the other end, so
-
"frame -1" moves to the oldest frame, and "frame 0" moves to the
-
newest frame.
-
-
Without an argument, the command prints the current stack frame. Since
-
the current position is redisplayed, it may trigger a resyncronization
-
if there is a front end also watching over things.)
-
end
-
end
-
end
-
end
-
1
module Byebug
-
#
-
# Ask for help from byebug's prompt.
-
#
-
1
class HelpCommand < Command
-
1
include Columnize
-
-
1
self.allow_in_control = true
-
-
1
def regexp
-
/^\s* h(?:elp)? (?:\s+(.+))? \s*$/x
-
end
-
-
1
def execute
-
if @match[1]
-
args = @match[1].split
-
cmds = @state.commands.select { |cmd| cmd.names.include?(args[0]) }
-
if cmds.empty?
-
return errmsg("Undefined command: \"#{args[0]}\". Try \"help\"")
-
end
-
-
return puts(cmds.map { |cmd| cmd.help(args[1..-1]) }.join("\n"))
-
end
-
-
puts "byebug help v#{VERSION}" unless Setting[:testing]
-
puts "Type \"help <command-name>\" for help on a specific command\n"
-
puts 'Available commands:'
-
cmds = @state.commands.map { |cmd| cmd.names }.flatten.uniq.sort
-
puts columnize(cmds, Setting[:width])
-
end
-
-
1
class << self
-
1
def names
-
%w(help)
-
end
-
-
1
def description
-
%(h[elp][ <command>[ <subcommand>]]
-
-
"help" alone prints this help.
-
"help <command>" prints help on <command>.
-
"help <command> <subcommand> prints help on <subcommand>.)
-
end
-
end
-
end
-
end
-
1
module Byebug
-
#
-
# Show history of byebug commands.
-
#
-
1
class HistoryCommand < Command
-
1
def regexp
-
/^\s* hist(?:ory)? (?:\s+(?<num_cmds>.+))? \s*$/x
-
end
-
-
1
def execute
-
history = @state.interface.history
-
-
if @match[:num_cmds]
-
size, _ = get_int(@match[:num_cmds], 'history', 1, history.size)
-
return errmsg(err) unless size
-
end
-
-
puts history.to_s(size)
-
end
-
-
1
class << self
-
1
def names
-
%w(history)
-
end
-
-
1
def description
-
%(hist[ory] [num_cmds] Show byebug's command history.)
-
end
-
end
-
end
-
end
-
1
module Byebug
-
#
-
# Utility methods to assist the info command
-
#
-
1
module InfoFunctions
-
1
def info_catch(*_args)
-
return puts('No frame selected.') unless @state.context
-
-
if Byebug.catchpoints && !Byebug.catchpoints.empty?
-
Byebug.catchpoints.each do |exception, _hits|
-
puts("#{exception}: #{exception.is_a?(Class)}")
-
end
-
else
-
puts 'No exceptions set to be caught.'
-
end
-
end
-
-
1
def info_args(*args)
-
locals = @state.context.frame_locals
-
args = @state.context.frame_args
-
return if args == [[:rest]]
-
-
args.map do |_, name|
-
s = "#{name} = #{locals[name].inspect}"
-
s[Setting[:width] - 3..-1] = '...' if s.size > Setting[:width]
-
puts s
-
end
-
end
-
-
1
def info_breakpoint(brkpt)
-
expr = brkpt.expr.nil? ? '' : " if #{brkpt.expr}"
-
y_n = brkpt.enabled? ? 'y' : 'n'
-
interp = format('%-3d %-3s at %s:%s%s',
-
brkpt.id, y_n, brkpt.source, brkpt.pos, expr)
-
puts interp
-
hits = brkpt.hit_count
-
return unless hits > 0
-
-
s = (hits > 1) ? 's' : ''
-
puts "\tbreakpoint already hit #{hits} time#{s}"
-
end
-
-
1
def info_breakpoints(*args)
-
return puts('No breakpoints.') if Byebug.breakpoints.empty?
-
-
brkpts = Byebug.breakpoints.sort_by { |b| b.id }
-
unless args.empty?
-
indices = args.map { |a| a.to_i }
-
brkpts = brkpts.select { |b| indices.member?(b.id) }
-
return errmsg('No breakpoints found among list given') if brkpts.empty?
-
end
-
-
puts 'Num Enb What'
-
brkpts.each { |b| info_breakpoint(b) }
-
end
-
-
1
def info_display(*_args)
-
return puts('There are no auto-display expressions now.') unless
-
@state.display.find { |d| d[0] }
-
-
puts 'Auto-display expressions now in effect:'
-
puts 'Num Enb Expression'
-
n = 1
-
@state.display.each do |d|
-
puts(format('%3d: %s %s', n, d[0] ? 'y' : 'n', d[1]))
-
n += 1
-
end
-
end
-
-
1
def info_file_path(file)
-
s = "File #{file}"
-
path = File.expand_path(file)
-
s = "#{s} - #{path}" if path && path != file
-
puts s
-
end
-
-
1
def info_file_lines(file)
-
lines = File.foreach(file)
-
puts "\t#{lines.count} lines" if lines
-
end
-
-
1
def info_file_breakpoints(file)
-
breakpoints = LineCache.trace_line_numbers(file)
-
return unless breakpoints
-
-
puts "\tbreakpoint line numbers:"
-
puts columnize(breakpoints.to_a.sort, Setting[:width])
-
end
-
-
1
def info_file_mtime(file)
-
stat = File.stat(file)
-
puts "\t#{stat.mtime}" if stat
-
end
-
-
1
def info_file_sha1(file)
-
puts "\t#{Digest::SHA1.hexdigest(file)}"
-
end
-
-
1
def info_files(*_args)
-
files = SCRIPT_LINES__.keys
-
files.uniq.sort.each do |file|
-
info_file_path(file)
-
info_file_mtime(file)
-
end
-
end
-
-
1
def info_line(*_args)
-
puts "Line #{@state.line} of \"#{@state.file}\""
-
end
-
-
1
def print_hash(vars)
-
vars.keys.sort.each do |name|
-
begin
-
s = "#{name} = #{vars[name].inspect}"
-
rescue
-
begin
-
s = "#{name} = #{vars[name]}"
-
rescue
-
s = "#{name} = *Error in evaluation*"
-
end
-
end
-
s[Setting[:width] - 3..-1] = '...' if s.size > Setting[:width]
-
puts s
-
end
-
end
-
-
1
def info_stop_reason(stop_reason)
-
case stop_reason
-
when :step
-
puts "It stopped after stepping, next'ing or initial start."
-
when :breakpoint
-
puts 'It stopped at a breakpoint.'
-
when :catchpoint
-
puts 'It stopped at a catchpoint.'
-
else
-
puts "Unknown reason: #{@state.context.stop_reason}"
-
end
-
end
-
-
1
def info_program(*_args)
-
if @state.context.dead?
-
puts 'The program crashed.'
-
excpt = Byebug.last_exception
-
return puts("Exception: #{excpt.inspect}") if excpt
-
end
-
-
puts 'Program stopped. '
-
info_stop_reason @state.context.stop_reason
-
end
-
-
1
def info_variables(*_args)
-
locals = @state.context.frame_locals
-
locals[:self] = @state.context.frame_self(@state.frame_pos)
-
print_hash(locals)
-
-
obj = bb_eval('self')
-
var_list(obj.instance_variables, obj.instance_eval { binding })
-
var_class_self
-
end
-
end
-
-
#
-
# Show info about different aspects of the debugger.
-
#
-
1
class InfoCommand < Command
-
1
include Columnize
-
1
self.allow_in_control = true
-
-
Subcommands = [
-
['args', 1, 'Argument variables of current stack frame'],
-
['breakpoints', 1, 'Status of user-settable breakpoints',
-
'Without argument, list info about all breakpoints. With an integer ' \
-
'argument, list info on that breakpoint.'],
-
['catch', 3, 'Exceptions that can be caught in the current stack frame'],
-
['display', 2, 'Expressions to display when program stops'],
-
['file', 4, 'Info about a particular file read in',
-
'After the file name is supplied, you can list file attributes that ' \
-
'you wish to see. Attributes include: "all", "basic", "breakpoint", ' \
-
'"lines", "mtime", "path" and "sha1".'],
-
['files', 5, 'File names and timestamps of files read in'],
-
['line', 2, 'Line number and file name of current position in source ' \
-
'file.'],
-
['program', 2, 'Execution status of the program']
-
].map do |name, min, help|
-
8
Subcmd.new(name, min, help)
-
1
end unless defined?(Subcommands)
-
-
InfoFileSubcommands = [
-
['all', 1, 'All file information available - breakpoints, lines, ' \
-
'mtime, path and sha1'],
-
['basic', 2, 'basic information - path, number of lines'],
-
['breakpoints', 2, 'Show trace line numbers',
-
'These are the line number where a breakpoint can be set.'],
-
['lines', 1, 'Show number of lines in the file'],
-
['mtime', 1, 'Show modification time of file'],
-
['path', 4, 'Show full file path name for file'],
-
['sha1', 1, 'Show SHA1 hash of contents of the file']
-
].map do |name, min, help|
-
7
Subcmd.new(name, min, help)
-
1
end unless defined?(InfoFileSubcommands)
-
-
1
def info_file(*args)
-
return info_files unless args[0]
-
-
mode = args[1] || 'basic'
-
subcmd = Command.find(InfoFileSubcommands, mode)
-
return errmsg "Invalid parameter #{args[1]}\n" unless subcmd
-
-
if %w(all basic).member?(subcmd.name)
-
info_file_path(args[0])
-
info_file_lines(args[0])
-
if subcmd.name == 'all'
-
info_file_breakpoints(args[0])
-
info_file_mtime(args[0])
-
info_file_sha1(args[0])
-
end
-
else
-
puts("File #{args[0]}") if subcmd.name != 'path'
-
send("info_file_#{subcmd.name}", args[0])
-
end
-
end
-
-
1
def regexp
-
/^\s* i(?:nfo)? (?:\s+(.+))? \s*$/x
-
end
-
-
1
def execute
-
return puts(InfoCommand.help) unless @match[1]
-
-
args = @match[1].split(/[ \t]+/)
-
param = args.shift
-
subcmd = Command.find(Subcommands, param)
-
return errmsg "Unknown info command #{param}\n" unless subcmd
-
-
if @state.context
-
send("info_#{subcmd.name}", *args)
-
else
-
errmsg "info_#{subcmd.name} not available without a context.\n"
-
end
-
end
-
-
1
class << self
-
1
def names
-
%w(info)
-
end
-
-
1
def description
-
<<-EOD.gsub(/^ {8}/, '')
-
-
info[ subcommand]
-
-
Generic command for showing things about the program being debugged.
-
-
EOD
-
end
-
-
1
def help(subcmds = [])
-
return description + format_subcmds if subcmds.empty?
-
-
subcmd = subcmds.first
-
return format_subcmd(subcmd) unless 'file' == subcmd && subcmds[2]
-
-
subsubcmd = Command.find(InfoFileSubcommands, subcmds[2])
-
return "\nInvalid \"file\" attribute \"#{args[2]}\"." unless subsubcmd
-
-
subsubcmd.short_help
-
end
-
end
-
end
-
end
-
1
module Byebug
-
#
-
# Interrupting execution of current thread.
-
#
-
1
class InterruptCommand < Command
-
1
self.allow_in_control = true
-
1
self.allow_in_post_mortem = false
-
-
1
def regexp
-
/^\s*i(?:nterrupt)?\s*$/
-
end
-
-
1
def execute
-
context = Byebug.thread_context(Thread.main)
-
context.interrupt
-
end
-
-
1
class << self
-
1
def names
-
%w(interrupt)
-
end
-
-
1
def description
-
%(i|nterrupt Interrupts the program.)
-
end
-
end
-
end
-
end
-
1
require 'irb'
-
-
1
module Byebug
-
#
-
# Enter IRB from byebug's prompt
-
#
-
1
class IrbCommand < Command
-
1
def regexp
-
/^\s* irb \s*$/x
-
end
-
-
1
def execute
-
unless @state.interface.is_a?(LocalInterface)
-
return errmsg('Command is available only in local mode.')
-
end
-
-
IRB.start(__FILE__)
-
end
-
-
1
class << self
-
1
def names
-
%w(irb)
-
end
-
-
1
def description
-
%{irb Starts an Interactive Ruby (IRB) session.}
-
end
-
end
-
end
-
end
-
1
module Byebug
-
#
-
# Send custom signals to the debugged program.
-
#
-
1
class KillCommand < Command
-
1
self.allow_in_control = true
-
-
1
def regexp
-
/^\s* (?:kill) \s* (?:\s+(\S+))? \s*$/x
-
end
-
-
1
def execute
-
if @match[1]
-
signame = @match[1]
-
unless Signal.list.member?(signame)
-
errmsg("signal name #{signame} is not a signal I know about\n")
-
return false
-
end
-
@state.interface.close if 'KILL' == signame
-
else
-
return unless confirm('Really kill? (y/n) ')
-
signame = 'KILL'
-
end
-
-
Process.kill(signame, Process.pid)
-
end
-
-
1
class << self
-
1
def names
-
%w(kill)
-
end
-
-
1
def description
-
%{kill[ SIGNAL]
-
-
Send [signal] to Process.pid
-
Equivalent of Process.kill(Process.pid)}
-
end
-
end
-
end
-
end
-
1
module Byebug
-
#
-
# List parts of the source code.
-
#
-
1
class ListCommand < Command
-
1
def regexp
-
/^\s* l(?:ist)? (?:\s*([-=])|\s+(\S+))? \s*$/x
-
end
-
-
1
def execute
-
Byebug.source_reload if Setting[:autoreload]
-
-
lines = get_lines(@state.file)
-
return errmsg "No sourcefile available for #{@state.file}\n" unless lines
-
-
@match ||= match('list')
-
b, e = range(@match[2], lines.size)
-
return errmsg('Invalid line range') unless valid_range?(b, e, lines.size)
-
display_lines(b, e, lines)
-
-
@state.prev_line = b
-
end
-
-
1
class << self
-
1
def names
-
%w(list)
-
end
-
-
1
def description
-
%(l[ist][[-=]][ nn-mm]
-
-
Lists lines of code forward from current line or from the place where
-
code was last listed. If "list-" is specified, lists backwards
-
instead. If "list=" is specified, lists from current line regardless
-
of where code was last listed. A line range can also be specified to
-
list specific sections of code.)
-
end
-
end
-
-
1
private
-
-
#
-
# Line range to be printed by `list`.
-
#
-
# If <input> is set, range is parsed from it.
-
#
-
# Otherwise it's automatically chosen.
-
#
-
1
def range(input, max_line)
-
size = [Setting[:listsize], max_line].min
-
-
return set_range(size, max_line) unless input
-
-
parse_range(input, size, max_line)
-
end
-
-
1
def valid_range?(first, last, max)
-
first <= last && (1..max).include?(first) && (1..max).include?(last)
-
end
-
-
#
-
# Set line range to be printed by list
-
#
-
# @param size - number of lines to be printed
-
# @param max_line - max line number that can be printed
-
#
-
# @return first line number to list
-
# @return last line number to list
-
#
-
1
def set_range(size, max_line)
-
first = amend(lower(size, @match[1] || '+'), max_line - size + 1)
-
-
[first, move(first, size - 1)]
-
end
-
-
1
def parse_range(input, size, max_line)
-
first, err = get_int(input.split(/[-,]/)[0], 'List', 1, max_line)
-
return [-1, -1] if err
-
-
if input.split(/[-,]/)[1]
-
last, _ = get_int(input.split(/[-,]/)[1], 'List', 1, max_line)
-
return [-1, -1] unless last
-
-
last = amend(last, max_line)
-
else
-
first -= (size / 2)
-
end
-
-
[first, last || move(first, size - 1)]
-
end
-
-
1
def amend(line, max_line)
-
return 1 if line < 1
-
-
[max_line, line].min
-
end
-
-
1
def lower(size, direction = '+')
-
return @state.line - size / 2 if direction == '=' || !@state.prev_line
-
-
move(@state.prev_line, size, direction)
-
end
-
-
1
def move(line, size, direction = '+')
-
line.send(direction, size)
-
end
-
-
#
-
# Show file lines in <lines> from line number <min> to line number <max>.
-
#
-
1
def display_lines(min, max, lines)
-
puts "\n[#{min}, #{max}] in #{@state.file}"
-
-
(min..max).to_a.zip(lines[min - 1..max - 1]).map do |l|
-
mark = l[0] == @state.line ? '=> ' : ' '
-
puts format("#{mark}%#{max.to_s.size}d: %s", l[0], l[1])
-
end
-
end
-
end
-
end
-
1
module Byebug
-
#
-
# Show methods of specific classes/modules/objects.
-
#
-
1
class MethodCommand < Command
-
1
include Columnize
-
-
1
def regexp
-
/^\s* m(?:ethod)? \s+ (i(:?nstance)?\s+)?/x
-
end
-
-
1
def execute
-
obj = bb_eval(@match.post_match)
-
if @match[1]
-
puts "#{columnize(obj.methods.sort, Setting[:width])}"
-
elsif !obj.is_a?(Module)
-
puts "Should be Class/Module: #{@match.post_match}"
-
else
-
puts "#{columnize(obj.instance_methods(false).sort, Setting[:width])}"
-
end
-
end
-
-
1
class << self
-
1
def names
-
%w(method)
-
end
-
-
1
def description
-
%{m[ethod] (i[nstance][ <obj>]|<class|module>)
-
-
When invoked with "instance", shows instance methods of the object
-
specified as argument or of self no object was specified.
-
-
When invoked only with a class or module, shows class methods of the
-
class or module specified as argument.}
-
end
-
end
-
end
-
end
-
module Byebug
-
#
-
# Enter Pry from byebug's prompt
-
#
-
1
class PryCommand < Command
-
1
def regexp
-
/^\s* pry \s*$/x
-
end
-
-
1
def execute
-
unless @state.interface.is_a?(LocalInterface)
-
return errmsg('Command is available only in local mode.')
-
end
-
-
get_binding.pry
-
end
-
-
1
class << self
-
1
def names
-
%w(pry)
-
end
-
-
1
def description
-
%(pry Starts a Pry session.)
-
end
-
end
-
end
-
1
end if defined?(Pry)
-
1
module Byebug
-
#
-
# Exit from byebug.
-
#
-
1
class QuitCommand < Command
-
1
self.allow_in_control = true
-
-
1
def regexp
-
/^\s* (?:q(?:uit)?|exit) \s* (!|\s+unconditionally)? \s*$/x
-
end
-
-
1
def execute
-
return unless @match[1] || confirm('Really quit? (y/n) ')
-
-
@state.interface.close
-
exit! # exit -> exit!: No graceful way to stop...
-
end
-
-
1
class << self
-
1
def names
-
%w(quit exit)
-
end
-
-
1
def description
-
%(q[uit]|exit [!|unconditionally] Exits from byebug.
-
-
Normally we prompt before exiting. However if the parameter
-
"unconditionally" is given or command is suffixed with !, we exit
-
without asking further questions.)
-
end
-
end
-
end
-
end
-
1
module Byebug
-
#
-
# Reload source code to pick up latest changes.
-
#
-
1
class ReloadCommand < Command
-
1
self.allow_in_control = true
-
1
self.allow_in_post_mortem = false
-
-
1
def regexp
-
/^\s* r(?:eload)? \s*$/x
-
end
-
-
1
def execute
-
Byebug.source_reload
-
onoff = Setting[:autoreload] ? 'on' : 'off'
-
puts "Source code was reloaded. Automatic reloading is #{onoff}"
-
end
-
-
1
class << self
-
1
def names
-
%w(reload)
-
end
-
-
1
def description
-
%(r[eload] Forces source code reloading.)
-
end
-
end
-
end
-
end
-
1
module Byebug
-
#
-
# Restart debugged program from within byebug.
-
#
-
1
class RestartCommand < Command
-
1
self.allow_in_control = true
-
-
1
def regexp
-
/^\s* (?:restart|R) (?:\s+(?<args>.+))? \s*$/x
-
end
-
-
1
def execute
-
prog = Byebug.debugged_program
-
-
if defined?(BYEBUG_SCRIPT)
-
cmd = "#{BYEBUG_SCRIPT} #{prog}"
-
else
-
puts 'Byebug was not called from the outset...'
-
if File.executable?(prog)
-
cmd = prog
-
else
-
puts "Program #{prog} not executable... Wrapping it in a ruby call"
-
cmd = "ruby -rbyebug -I#{$LOAD_PATH.join(' -I')} #{prog}"
-
end
-
end
-
-
if @match[:args]
-
cmd += " #{@match[:args]}"
-
else
-
require 'shellwords'
-
cmd += " #{ARGV.compact.shelljoin}"
-
end
-
-
# An execv would be preferable to the "exec" below.
-
puts "Re exec'ing:\n\t#{cmd}"
-
exec cmd
-
rescue Errno::EOPNOTSUPP
-
puts 'Restart command is not available at this time.'
-
end
-
-
1
class << self
-
1
def names
-
%w(restart)
-
end
-
-
1
def description
-
%(restart|R [args]
-
-
Restart the program. This is a re-exec - all byebug state
-
is lost. If command arguments are passed those are used.)
-
end
-
end
-
end
-
end
-
1
module Byebug
-
#
-
# Default file where commands are saved
-
#
-
1
RESTART_FILE = '.byebug-save' unless defined?(RESTART_FILE)
-
-
#
-
# Save current settings to use them in another debug session.
-
#
-
1
class SaveCommand < Command
-
1
self.allow_in_control = true
-
-
1
def save_breakpoints(file)
-
Byebug.breakpoints.each do |b|
-
file.puts "break #{b.source}:#{b.pos}#{" if #{b.expr}" if b.expr}"
-
end
-
end
-
-
1
def save_catchpoints(file)
-
Byebug.catchpoints.keys.each do |c|
-
file.puts "catch #{c}"
-
end
-
end
-
-
1
def save_displays(file)
-
@state.display.each { |d| file.puts "display #{d[1]}" if d[0] }
-
end
-
-
1
def save_settings(file)
-
%w(autoeval autoirb autolist basename testing).each do |setting|
-
file.puts "set #{setting} #{Setting[setting.to_sym]}"
-
end
-
end
-
-
1
def regexp
-
/^\s* sa(?:ve)? (?:\s+(\S+))? \s*$/x
-
end
-
-
1
def execute
-
file = open(@match[1] || RESTART_FILE, 'w')
-
-
save_breakpoints(file)
-
save_catchpoints(file)
-
save_displays(file)
-
save_settings(file)
-
-
puts "Saved to '#{file.path}'"
-
file.close
-
end
-
-
1
class << self
-
1
def names
-
%w(save)
-
end
-
-
1
def description
-
%(save[ FILE]
-
-
Saves current byebug state to FILE as a script file. This includes
-
breakpoints, catchpoints, display expressions and some settings. If
-
no filename is given, we will fabricate one.
-
-
Use the "source" command in another debug session to restore them.)
-
end
-
end
-
end
-
end
-
1
module Byebug
-
#
-
# Change byebug settings.
-
#
-
1
class SetCommand < Command
-
1
self.allow_in_control = true
-
-
1
def regexp
-
/^\s* set (?:\s+(?<setting>\w+))? (?:\s+(?<value>\S+))? \s*$/x
-
end
-
-
1
def execute
-
key, value = @match[:setting], @match[:value]
-
return puts(SetCommand.help) if key.nil? && value.nil?
-
-
full_key = Setting.find(key)
-
return errmsg("Unknown setting :#{key}") unless full_key
-
-
if !Setting.boolean?(full_key) && value.nil?
-
value, err = nil, "You must specify a value for setting :#{key}"
-
elsif Setting.boolean?(full_key)
-
value, err = get_onoff(value, key =~ /^no/ ? false : true)
-
elsif Setting.integer?(full_key)
-
value, err = get_int(value, full_key, 1)
-
end
-
return errmsg(err) if value.nil?
-
-
Setting[full_key.to_sym] = value
-
-
puts Setting.settings[full_key.to_sym].to_s
-
end
-
-
1
def get_onoff(arg, default)
-
return default if arg.nil?
-
-
case arg
-
when '1', 'on', 'true'
-
true
-
when '0', 'off', 'false'
-
false
-
else
-
[nil, "Expecting 'on', 1, true, 'off', 0, false. Got: #{arg}.\n"]
-
end
-
end
-
-
1
class << self
-
1
def names
-
%w(set)
-
end
-
-
1
def description
-
<<-EOD.gsub(/^ /, '')
-
-
set <setting> <value>
-
-
Modifies parts of byebug environment.
-
-
Boolean values take "on", "off", "true", "false", "1" or "0". If you
-
don't specify a value, the boolean setting will be enabled.
-
Conversely, you can use "set no<setting> to disable them.
-
-
You can see these environment settings with the "show" command.
-
EOD
-
end
-
-
1
def help(subcmds = [])
-
Setting.help('set', subcmds.first)
-
end
-
end
-
end
-
end
-
1
module Byebug
-
#
-
# Show byebug settings.
-
#
-
1
class ShowCommand < Command
-
1
self.allow_in_control = true
-
-
1
def regexp
-
/^\s* show (?:\s+(?<setting>\w+))? \s*$/x
-
end
-
-
1
def execute
-
key = @match[:setting]
-
return puts(self.class.help) if key.nil?
-
-
full_key = Setting.find(key)
-
return errmsg("Unknown setting :#{key}") unless full_key
-
-
puts Setting.settings[full_key.to_sym].to_s
-
end
-
-
1
class << self
-
1
def names
-
%w(show)
-
end
-
-
1
def description
-
<<-EOD.gsub(/^ {8}/, '')
-
-
show <setting> <value>
-
-
Generic command for showing byebug settings. You can change them with
-
the "set" command.
-
-
EOD
-
end
-
-
1
def help(subcmds = [])
-
Setting.help('show', subcmds.first)
-
end
-
end
-
end
-
end
-
1
module Byebug
-
#
-
# Execute a file containing byebug commands.
-
#
-
# It can be used to restore a previously saved debugging session.
-
#
-
1
class SourceCommand < Command
-
1
self.allow_in_control = true
-
-
1
def regexp
-
/^\s* so(?:urce)? (?:\s+(\S+))? \s*$/x
-
end
-
-
1
def execute
-
return puts(self.class.help) if self.class.names.include?(@match[0])
-
-
file = File.expand_path(@match[1]).strip
-
return errmsg("File \"#{file}\" not found") unless File.exist?(file)
-
-
if @state && @state.interface
-
@state.interface.command_queue += File.open(file).readlines
-
else
-
Byebug.run_script(file, @state)
-
end
-
end
-
-
1
class << self
-
1
def names
-
%w(source)
-
end
-
-
1
def description
-
%(source <file>
-
-
Executes file <file> containing byebug commands.)
-
end
-
end
-
end
-
end
-
1
module Byebug
-
#
-
# Mixin to assist command parsing
-
#
-
1
module SteppingFunctions
-
1
def parse_force(str)
-
return Setting[:forcestep] unless str
-
-
case str
-
when '+' then
-
true
-
when '-' then
-
false
-
end
-
end
-
end
-
-
#
-
# Implements the next functionality.
-
#
-
# Allows the user the continue execution until the next instruction in the
-
# current frame.
-
#
-
1
class NextCommand < Command
-
1
self.allow_in_post_mortem = false
-
-
1
def regexp
-
/^\s* n(?:ext)?([+-])? (?:\s+(\S+))? \s*$/x
-
end
-
-
1
def execute
-
steps, err = parse_steps(@match[2], 'Next')
-
return errmsg(err) unless steps
-
-
@state.context.step_over(steps, @state.frame_pos, parse_force(@match[1]))
-
@state.proceed
-
end
-
-
1
class << self
-
1
def names
-
%w(next)
-
end
-
-
1
def description
-
%(n[ext][+-]?[ nnn]
-
-
Steps over once or nnn times.
-
'+' forces to move to another line.
-
'-' is the opposite of '+' and disables the :forcestep setting.)
-
end
-
end
-
end
-
-
#
-
# Implements the step functionality.
-
#
-
# Allows the user the continue execution until the next instruction, possibily
-
# in a different frame. Use step to step into method calls or blocks.
-
#
-
1
class StepCommand < Command
-
1
self.allow_in_post_mortem = false
-
-
1
def regexp
-
/^\s* s(?:tep)?([+-]) ?(?:\s+(\S+))? \s*$/x
-
end
-
-
1
def execute
-
steps, err = parse_steps(@match[2], 'Steps')
-
return errmsg(err) unless steps
-
-
@state.context.step_into(steps, parse_force(@match[1]))
-
@state.proceed
-
end
-
-
1
class << self
-
1
def names
-
%w(step)
-
end
-
-
1
def description
-
%{s[tep][+-]?[ nnn]
-
-
Steps (into methods) once or nnn times.
-
'+' forces to move to another line.
-
'-' is the opposite of '+' and disables the :forcestep setting.}
-
end
-
end
-
end
-
end
-
1
module Byebug
-
#
-
# Utilities to assist commands related to threads.
-
#
-
1
module ThreadFunctions
-
1
def display_context(context, should_show_top_frame = true)
-
args = thread_arguments(context, should_show_top_frame)
-
interp = format("%s%s%d %s\t%s",
-
args[:status_flag], args[:debug_flag], args[:id],
-
args[:thread], args[:file_line])
-
puts interp
-
end
-
-
1
def thread_arguments(context, should_show_top_frame = true)
-
status_flag = if context.suspended?
-
'$'
-
else
-
context.thread == Thread.current ? '+' : ' '
-
end
-
debug_flag = context.ignored? ? '!' : ' '
-
if should_show_top_frame
-
if context.thread == Thread.current && !context.dead?
-
file = context.frame_file(0)
-
line = context.frame_line(0)
-
else
-
if context.thread.backtrace_locations &&
-
context.thread.backtrace_locations[0]
-
file = context.thread.backtrace_locations[0].path
-
line = context.thread.backtrace_locations[0].lineno
-
end
-
end
-
file_line = "#{file}:#{line}"
-
end
-
{
-
status_flag: status_flag,
-
debug_flag: debug_flag,
-
id: context.thnum,
-
thread: context.thread.inspect,
-
file_line: file_line ? file_line : ''
-
}
-
end
-
-
1
def parse_thread_num(subcmd, arg)
-
return errmsg("\"#{subcmd}\" needs a thread number") if '' == arg
-
-
thread_num, err = get_int(arg, subcmd, 1)
-
return errmsg(err) unless thread_num
-
-
Byebug.contexts.find { |c| c.thnum == thnum }
-
end
-
-
1
def parse_thread_num_for_cmd(subcmd, arg)
-
c = parse_thread_num(subcmd, arg)
-
return unless c
-
-
case
-
when nil == c
-
errmsg 'No such thread'
-
when @state.context == c
-
errmsg "It's the current thread"
-
when c.ignored?
-
errmsg "Can't #{subcmd} thread #{arg}"
-
else
-
c
-
end
-
end
-
end
-
-
#
-
# List current threads.
-
#
-
1
class ThreadListCommand < Command
-
1
self.allow_in_control = true
-
-
1
def regexp
-
/^\s* th(?:read)? \s+ l(?:ist)? \s*$/x
-
end
-
-
1
def execute
-
Byebug.contexts.select { |c| Thread.list.include?(c.thread) }
-
.sort_by(&:thnum).each { |c| display_context(c) }
-
end
-
-
1
class << self
-
1
def names
-
%w(thread)
-
end
-
-
1
def description
-
%(th[read] l[ist] Lists all threads.)
-
end
-
end
-
end
-
-
#
-
# Show current thread.
-
#
-
1
class ThreadCurrentCommand < Command
-
1
def regexp
-
/^\s* th(?:read)? \s+ (?:cur(?:rent)?)? \s*$/x
-
end
-
-
1
def execute
-
display_context(@state.context)
-
end
-
-
1
class << self
-
1
def names
-
%w(thread)
-
end
-
-
1
def description
-
%(th[read] [cur[rent]] Shows current thread.)
-
end
-
end
-
end
-
-
#
-
# Stop execution of a thread.
-
#
-
1
class ThreadStopCommand < Command
-
1
self.allow_in_control = true
-
1
self.allow_in_post_mortem = false
-
-
1
def regexp
-
/^\s* th(?:read)? \s+ stop \s* (\S*) \s*$/x
-
end
-
-
1
def execute
-
c = parse_thread_num_for_cmd('thread stop', @match[1])
-
return unless c
-
-
c.suspend
-
display_context(c)
-
end
-
-
1
class << self
-
1
def names
-
%w(thread)
-
end
-
-
1
def description
-
%(th[read] stop <n> Stops thread <n>.)
-
end
-
end
-
end
-
-
#
-
# Resume execution of a thread.
-
#
-
1
class ThreadResumeCommand < Command
-
1
self.allow_in_control = true
-
1
self.allow_in_post_mortem = false
-
-
1
def regexp
-
/^\s* th(?:read)? \s+ resume \s* (\S*) \s*$/x
-
end
-
-
1
def execute
-
c = parse_thread_num_for_cmd('thread resume', @match[1])
-
return unless c
-
return errmsg('Already running') unless c.suspended?
-
c.resume
-
display_context(c)
-
end
-
-
1
class << self
-
1
def names
-
%w(thread)
-
end
-
-
1
def description
-
%(th[read] resume <n> Resumes thread <n>.)
-
end
-
end
-
end
-
-
#
-
# Switch execution to a different thread.
-
#
-
1
class ThreadSwitchCommand < Command
-
1
self.allow_in_control = true
-
1
self.allow_in_post_mortem = false
-
-
1
def regexp
-
/^\s* th(?:read)? \s+ (?:sw(?:itch)?\s+)? (\S+) \s*$/x
-
end
-
-
1
def execute
-
if @match[1] =~ /switch/
-
return errmsg('"thread switch" needs a thread number')
-
end
-
-
c = parse_thread_num_for_cmd('thread switch', @match[1])
-
return unless c
-
-
display_context(c)
-
c.step_into 1
-
c.thread.run
-
@state.proceed
-
end
-
-
1
class << self
-
1
def names
-
%w(thread)
-
end
-
-
1
def description
-
%(th[read] [sw[itch]] <nnn> Switches thread context to <n>.)
-
end
-
end
-
end
-
end
-
1
module Byebug
-
#
-
# Show information about every line that is executed.
-
#
-
1
class TraceCommand < Command
-
1
def regexp
-
/^\s* tr(?:acevar)? (?: \s+ (\S+))? # (variable-name)?
-
(?: \s+ (\S+))? # (stop | nostop)?
-
\s*$/x
-
end
-
-
1
def execute
-
var = @match[1]
-
if global_variables.include?("$#{var}".to_sym)
-
if @match[2] && @match[2] !~ /(:?no)?stop/
-
errmsg "expecting \"stop\" or \"nostop\"; got \"#{@match[2]}\""
-
else
-
dbg_cmd = if @match[2] && @match[2] !~ /nostop/
-
'byebug(1, false)'
-
else
-
''
-
end
-
end
-
eval("trace_var(:\"\$#{var}\") do |val|
-
puts \"traced global variable '#{var}' has value '\#{val}'\"
-
#{dbg_cmd}
-
end")
-
puts "Tracing global variable \"#{var}\"."
-
else
-
errmsg "'#{var}' is not a global variable."
-
end
-
end
-
-
1
class << self
-
1
def names
-
%w(trace)
-
end
-
-
1
def description
-
%(tr[acevar] <variable> [[no]stop]
-
-
Start tracing variable <variable>.
-
-
If "stop" is specified, execution will stop every time the variable
-
changes its value. If nothing or "nostop" is specified, execution
-
won't stop, changes will just be logged in byebug's output.)
-
end
-
end
-
end
-
end
-
1
module Byebug
-
#
-
# Remove expressions from display list.
-
#
-
1
class UndisplayCommand < Command
-
1
self.allow_in_post_mortem = false
-
-
1
def regexp
-
/^\s* undisp(?:lay)? (?:\s+(\S+))? \s*$/x
-
end
-
-
1
def execute
-
if @match[1]
-
pos, err = get_int(@match[1], 'Undisplay', 1, @state.display.size)
-
return errmsg(err) unless err.nil?
-
-
unless @state.display[pos - 1]
-
return errmsg("Display expression #{pos} is not defined.")
-
end
-
-
@state.display[pos - 1][0] = nil
-
else
-
return unless confirm('Clear all expressions? (y/n) ')
-
-
@state.display.each { |d| d[0] = false }
-
end
-
end
-
-
1
class << self
-
1
def names
-
%w(undisplay)
-
end
-
-
1
def description
-
%(undisp[lay][ nnn]
-
-
Cancel some expressions to be displayed when program stops. Arguments
-
are the code numbers of the expressions to stop displaying. No
-
argument means cancel all automatic-display expressions. "delete
-
display" has the same effect as this command. Do "info display" to see
-
the current list of code numbers.)
-
end
-
end
-
end
-
end
-
1
module Byebug
-
#
-
# Utilities for the var command.
-
#
-
1
module VarFunctions
-
1
def var_list(ary, b = get_binding)
-
ary.sort!
-
ary.each do |v|
-
begin
-
s = bb_eval(v.to_s, b).inspect
-
rescue
-
begin
-
s = bb_eval(v.to_s, b).to_s
-
rescue
-
s = '*Error in evaluation*'
-
end
-
end
-
s = "#{v} = #{s}"
-
s[Setting[:width] - 3..-1] = '...' if s.size > Setting[:width]
-
puts s
-
end
-
end
-
-
1
def var_class_self
-
obj = bb_eval('self')
-
var_list(obj.class.class_variables, get_binding)
-
end
-
-
1
def var_global
-
globals = global_variables.reject do |v|
-
[:$IGNORECASE, :$=, :$KCODE, :$-K, :$binding].include?(v)
-
end
-
-
var_list(globals)
-
end
-
-
1
def var_instance(where)
-
obj = bb_eval(where)
-
var_list(obj.instance_variables, obj.instance_eval { binding })
-
end
-
-
1
def var_local
-
_self = @state.context.frame_self(@state.frame_pos)
-
locals = @state.context.frame_locals
-
locals.keys.sort.each do |name|
-
puts format(' %s => %p', name, locals[name])
-
end
-
end
-
end
-
-
#
-
# Show all variables and its values.
-
#
-
1
class VarAllCommand < Command
-
1
def regexp
-
/^\s* v(?:ar)? \s+ a(?:ll)? \s*$/x
-
end
-
-
1
def execute
-
var_class_self
-
var_global
-
var_instance('self')
-
var_local
-
end
-
-
1
class << self
-
1
def names
-
%w(var)
-
end
-
-
1
def description
-
%(v[ar] a[ll]
-
-
Show local, global and instance & class variables of self.)
-
end
-
end
-
end
-
-
#
-
# Show class variables and its values.
-
#
-
1
class VarClassCommand < Command
-
1
def regexp
-
/^\s* v(?:ar)? \s+ cl(?:ass)? \s*/x
-
end
-
-
1
def execute
-
return errmsg "can't get class variables here.\n" unless @state.context
-
var_class_self
-
end
-
-
1
class << self
-
1
def names
-
%w(var)
-
end
-
-
1
def description
-
%(v[ar] cl[ass] Show class variables of self.)
-
end
-
end
-
end
-
-
#
-
# Show constants and its values.
-
#
-
1
class VarConstantCommand < Command
-
1
def regexp
-
/^\s* v(?:ar)? \s+ co(?:nst(?:ant)?)? \s+/x
-
end
-
-
1
def execute
-
obj = bb_eval(@match.post_match)
-
if obj.is_a? Module
-
constants = bb_eval("#{@match.post_match}.constants")
-
constants.sort!
-
constants.each do |c|
-
value = obj.const_get(c)
-
puts format(' %s => %p', c, value)
-
end
-
else
-
puts "Should be Class/Module: #{@match.post_match}"
-
end
-
end
-
-
1
class << self
-
1
def names
-
%w(var)
-
end
-
-
1
def description
-
%(v[ar] co[nst] <object> Show constants of <object>.)
-
end
-
end
-
end
-
-
#
-
# Show global variables and its values.
-
#
-
1
class VarGlobalCommand < Command
-
1
def regexp
-
/^\s* v(?:ar)? \s+ g(?:lobal)? \s*$/x
-
end
-
-
1
def execute
-
var_global
-
end
-
-
1
class << self
-
1
def names
-
%w(var)
-
end
-
-
1
def description
-
%(v[ar] g[lobal] Show global variables.)
-
end
-
end
-
end
-
-
#
-
# Show instance variables and its values.
-
#
-
1
class VarInstanceCommand < Command
-
1
def regexp
-
/^\s* v(?:ar)? \s+ ins(?:tance)? \s*/x
-
end
-
-
1
def execute
-
var_instance(@match.post_match.empty? ? 'self' : @match.post_match)
-
end
-
-
1
class << self
-
1
def names
-
%w(var)
-
end
-
-
1
def description
-
%(v[ar] i[nstance] <object>
-
-
Show instance variables of <object>.)
-
end
-
end
-
end
-
-
#
-
# Show local variables and its values.
-
#
-
1
class VarLocalCommand < Command
-
1
def regexp
-
/^\s* v(?:ar)? \s+ l(?:ocal)? \s*$/x
-
end
-
-
1
def execute
-
var_local
-
end
-
-
1
class << self
-
1
def names
-
%w(var)
-
end
-
-
1
def description
-
%(v[ar] l[ocal] Sow local variables.)
-
end
-
end
-
end
-
end
-
1
module Byebug
-
#
-
# Mantains context information for the debugger and it's the main
-
# communication point between the library and the C-extension through the
-
# at_breakpoint, at_catchpoint, at_tracing, at_line and at_return callbacks
-
#
-
1
class Context
-
1
class << self
-
1
def stack_size(byebug_frames = false)
-
backtrace = Thread.current.backtrace_locations(0)
-
return 0 unless backtrace
-
-
unless byebug_frames
-
backtrace = backtrace.drop_while { |l| !ignored(l.path) }
-
.drop_while { |l| ignored(l.path) }
-
.take_while { |l| !ignored(l.path) }
-
end
-
-
backtrace.size
-
end
-
-
1
def ignored(path)
-
IGNORED_FILES.include?(path)
-
end
-
1
private :ignored
-
end
-
-
1
def interrupt
-
step_into 1
-
end
-
-
1
def frame_locals(frame_no = 0)
-
bind = frame_binding frame_no
-
eval 'local_variables.inject({}){|h, v| h[v] = eval(v.to_s); h}', bind
-
end
-
-
1
def c_frame_args(frame_no)
-
myself = frame_self frame_no
-
return [] unless myself.to_s != 'main'
-
myself.send(:method, frame_method(frame_no)).parameters
-
end
-
-
1
def ruby_frame_args(bind)
-
return [] unless eval '__method__', bind
-
begin
-
eval 'self.method(__method__).parameters', bind
-
rescue NameError => e
-
puts "WARNING: Got exception #{e.class}: \"#{e.message}\" " \
-
'while retreving parameters from frame'
-
return []
-
end
-
end
-
-
1
def frame_args(frame_no = 0)
-
bind = frame_binding frame_no
-
if bind.nil?
-
c_frame_args frame_no
-
else
-
ruby_frame_args bind
-
end
-
end
-
-
1
def handler
-
Byebug.handler || fail('No interface loaded')
-
end
-
-
1
def at_breakpoint(brkpnt)
-
handler.at_breakpoint(self, brkpnt)
-
end
-
-
1
def at_catchpoint(excpt)
-
handler.at_catchpoint(self, excpt)
-
end
-
-
1
def at_tracing(file, line)
-
handler.at_tracing(self, file, line)
-
end
-
-
1
def at_line(file, line)
-
handler.at_line(self, file, line) unless IGNORED_FILES.include?(file)
-
end
-
-
1
def at_return(file, line)
-
handler.at_return(self, file, line)
-
end
-
end
-
end
-
1
require 'byebug/byebug'
-
1
require 'byebug/version'
-
1
require 'byebug/context'
-
1
require 'byebug/breakpoint'
-
1
require 'byebug/interface'
-
1
require 'byebug/processor'
-
1
require 'byebug/setting'
-
1
require 'byebug/remote'
-
-
1
require 'stringio'
-
1
require 'tracer'
-
1
require 'linecache19'
-
-
1
module Byebug
-
#
-
# List of files byebug will ignore while debugging
-
#
-
1
IGNORED_FILES = Dir.glob(File.expand_path('../**/*.rb', __FILE__))
-
-
#
-
# Configuration file used for startup commands. Default value is .byebugrc
-
#
-
1
INIT_FILE = '.byebugrc' unless defined?(INIT_FILE)
-
-
1
class << self
-
1
attr_accessor :handler, :debugged_program
-
-
1
extend Forwardable
-
1
def_delegators :handler, :errmsg, :puts
-
end
-
-
1
Byebug.handler = CommandProcessor.new
-
-
1
def self.source_reload
-
hsh = 'SCRIPT_LINES__'
-
Object.send(:remove_const, hsh) if Object.const_defined?(hsh)
-
Object.const_set(hsh, {})
-
end
-
-
#
-
# Byebug's interface is its handler's interface
-
#
-
1
def self.interface=(value)
-
handler.interface = value
-
end
-
-
#
-
# Runs normal byebug initialization scripts.
-
#
-
# Reads and executes the commands from init file (if any) in the current
-
# working directory. This is only done if the current directory is
-
# different from your home directory. Thus, you can have more than one init
-
# file, one generic in your home directory, and another, specific to the
-
# program you are debugging, in the directory where you invoke byebug.
-
#
-
1
def self.run_init_script(out = handler.interface)
-
cwd_script = File.expand_path(File.join('.', INIT_FILE))
-
run_script(cwd_script, out, true) if File.exist?(cwd_script)
-
-
home_script = File.expand_path(File.join(ENV['HOME'].to_s, INIT_FILE))
-
if File.exist?(home_script) && cwd_script != home_script
-
run_script(home_script, out, true)
-
end
-
end
-
-
#
-
# Runs a script file
-
#
-
1
def self.run_script(file, out = handler.interface, verbose = false)
-
interface = ScriptInterface.new(File.expand_path(file), out)
-
processor = ControlCommandProcessor.new(interface)
-
processor.process_commands(verbose)
-
end
-
end
-
-
#
-
# Extends the extension class to be able to pass information about the
-
# debugging environment from the c-extension to the user.
-
#
-
1
class Exception
-
1
attr_reader :__bb_file, :__bb_line, :__bb_binding, :__bb_context
-
end
-
1
module Byebug
-
#
-
# Miscelaneous Utilities
-
#
-
1
module MiscUtils
-
#
-
# Cross-platform way of finding an executable in the $PATH.
-
# Borrowed from: http://stackoverflow.com/questions/2108727
-
#
-
1
def which(cmd)
-
return File.expand_path(cmd) if File.exist?(cmd)
-
-
exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
-
ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
-
exts.each do |ext|
-
exe = File.join(path, "#{cmd}#{ext}")
-
return exe if File.executable?(exe) && !File.directory?(exe)
-
end
-
end
-
-
nil
-
end
-
end
-
-
#
-
# Utilities to assist command parsing
-
#
-
1
module ParseFunctions
-
#
-
# Parse 'str' of command 'cmd' as an integer between min and max. If either
-
# min or max is nil, that value has no bound.
-
#
-
1
def get_int(str, cmd, min = nil, max = nil)
-
if str !~ /\A[0-9]+\z/
-
return nil, "\"#{cmd}\" argument \"#{str}\" needs to be a number"
-
end
-
-
int = str.to_i
-
if min && int < min
-
return min, "\"#{cmd}\" argument \"#{str}\" needs to be at least #{min}"
-
elsif max && int > max
-
return max, "\"#{cmd}\" argument \"#{str}\" needs to be at most #{max}"
-
end
-
-
int
-
end
-
-
#
-
# Fills SCRIPT_LINES__ entry for <filename> if not already filled.
-
#
-
1
def lines(filename)
-
SCRIPT_LINES__[filename] ||= File.readlines(filename)
-
end
-
-
#
-
# Gets all lines in a source code file
-
#
-
1
def get_lines(filename)
-
return nil unless File.exist?(filename)
-
-
lines(filename)
-
end
-
-
#
-
# Gets a single line in a source code file
-
#
-
1
def get_line(filename, lineno)
-
lines = get_lines(filename)
-
return nil unless lines
-
-
lines[lineno - 1]
-
end
-
-
#
-
# Returns true if code is syntactically correct for Ruby.
-
#
-
1
def syntax_valid?(code)
-
eval("BEGIN {return true}\n#{code}", nil, '', 0)
-
rescue SyntaxError
-
false
-
end
-
-
#
-
# Returns the number of steps specified in <str> as an integer or 1 if <str>
-
# is empty.
-
#
-
1
def parse_steps(str, cmd)
-
return 1 unless str
-
-
steps, err = get_int(str, cmd, 1)
-
return nil, err unless steps
-
-
steps
-
end
-
end
-
end
-
1
require 'byebug/history'
-
-
#
-
# Namespace for all of byebug's code
-
#
-
1
module Byebug
-
#
-
# Main Interface class
-
#
-
# Contains common functionality to all implemented interfaces.
-
#
-
1
class Interface
-
1
attr_accessor :command_queue, :history
-
-
1
def initialize
-
1
@command_queue, @history = [], History.new
-
end
-
-
#
-
# Common routine for reporting byebug error messages.
-
# Derived classes may want to override this to capture output.
-
#
-
1
def errmsg(message)
-
print("*** #{message}\n")
-
end
-
-
1
protected
-
-
#
-
# Stores <cmd> in commands history.
-
#
-
1
def save_history(cmd)
-
@history.push(cmd) unless @history.ignore?(cmd)
-
end
-
end
-
-
1
require 'byebug/interfaces/local_interface'
-
1
require 'byebug/interfaces/script_interface'
-
1
require 'byebug/interfaces/remote_interface'
-
end
-
1
module Byebug
-
#
-
# Interface class for standard byebug use.
-
#
-
1
class LocalInterface < Interface
-
1
def read_command(prompt)
-
readline(prompt, true)
-
end
-
-
1
def confirm(prompt)
-
readline(prompt, false)
-
end
-
-
1
def puts(*args)
-
STDOUT.puts(*args)
-
end
-
-
1
def close
-
end
-
-
1
private
-
-
1
def readline(prompt, hist)
-
line = Readline.readline(prompt, false)
-
rescue Interrupt
-
puts('^C')
-
retry
-
ensure
-
save_history(line) if hist
-
end
-
end
-
end
-
1
require 'byebug/history'
-
-
1
module Byebug
-
#
-
# Interface class for remote use of byebug.
-
#
-
1
class RemoteInterface < Interface
-
1
def initialize(socket)
-
super()
-
@socket = socket
-
end
-
-
1
def close
-
@socket.close
-
rescue IOError
-
end
-
-
1
def confirm(prompt)
-
send_command "CONFIRM #{prompt}"
-
end
-
-
1
def read_command(prompt)
-
send_command "PROMPT #{prompt}"
-
end
-
-
1
def puts(message)
-
@socket.puts(message)
-
end
-
-
1
private
-
-
1
def send_command(msg)
-
@socket.puts msg
-
result = @socket.gets
-
fail IOError unless result
-
result.chomp
-
end
-
end
-
end
-
1
module Byebug
-
#
-
# Interface class for command execution from script files.
-
#
-
1
class ScriptInterface < Interface
-
1
def initialize(file, out, verbose = false)
-
super()
-
@file = file.respond_to?(:gets) ? file : open(file)
-
@out, @verbose = out, verbose
-
end
-
-
1
def read_command(_prompt)
-
while (result = @file.gets)
-
puts "# #{result}" if @verbose
-
next if result =~ /^\s*#/
-
next if result.strip.empty?
-
return result.chomp
-
end
-
end
-
-
1
def confirm(_prompt)
-
'y'
-
end
-
-
1
def puts(message)
-
@out.printf(message)
-
end
-
-
1
def close
-
@file.close
-
end
-
end
-
end
-
1
require 'forwardable'
-
-
1
module Byebug
-
1
class Processor
-
1
attr_accessor :interface
-
-
1
extend Forwardable
-
1
def_delegators :@interface, :errmsg, :puts
-
-
1
def initialize(interface)
-
1
@interface = interface
-
end
-
-
1
def without_exceptions
-
yield
-
rescue
-
nil
-
end
-
end
-
end
-
-
1
require 'byebug/command'
-
1
require 'byebug/processors/command_processor'
-
1
require 'byebug/processors/control_command_processor'
-
1
module Byebug
-
#
-
# Processes commands in regular mode
-
#
-
1
class CommandProcessor < Processor
-
1
attr_reader :display
-
-
1
def initialize(interface = LocalInterface.new)
-
1
super(interface)
-
-
1
@display = []
-
1
@mutex = Mutex.new
-
1
@last_cmd = nil # To allow empty (just <RET>) commands
-
1
@last_file = nil # Filename the last time we stopped
-
1
@last_line = nil # Line number the last time we stopped
-
1
@context_was_dead = false # Assume we haven't started.
-
end
-
-
1
def interface=(interface)
-
@mutex.synchronize do
-
@interface.close if @interface
-
@interface = interface
-
end
-
end
-
-
1
require 'pathname' # For cleanpath
-
-
#
-
# Regularize file name.
-
#
-
# This is also used as a common funnel place if basename is desired or if we
-
# are working remotely and want to change the basename. Or we are eliding
-
# filenames.
-
1
def self.canonic_file(filename)
-
return filename if ['(irb)', '-e'].include?(filename)
-
-
# For now we want resolved filenames
-
if Setting[:basename]
-
File.basename(filename)
-
else
-
Pathname.new(filename).cleanpath.to_s
-
end
-
end
-
-
1
def self.protect(mname)
-
5
alias_method "__#{mname}", mname
-
5
module_eval <<-END, __FILE__, __LINE__ + 1
-
def #{mname}(*args)
-
@mutex.synchronize do
-
return unless @interface
-
__#{mname}(*args)
-
end
-
rescue IOError, SystemCallError
-
@interface.close
-
rescue SignalException
-
raise
-
rescue
-
without_exceptions do
-
puts "INTERNAL ERROR!!! #\{$!\}"
-
puts $!.backtrace.map{|l| "\t#\{l\}"}.join("\n")
-
end
-
end
-
END
-
end
-
-
1
def at_breakpoint(_context, breakpoint)
-
n = Byebug.breakpoints.index(breakpoint) + 1
-
file = self.class.canonic_file(breakpoint.source)
-
line = breakpoint.pos
-
puts "Stopped by breakpoint #{n} at #{file}:#{line}"
-
end
-
1
protect :at_breakpoint
-
-
1
def at_catchpoint(context, excpt)
-
file = self.class.canonic_file(context.frame_file(0))
-
line = context.frame_line(0)
-
puts "Catchpoint at #{file}:#{line}: `#{excpt}' (#{excpt.class})"
-
end
-
1
protect :at_catchpoint
-
-
1
include ParseFunctions
-
-
1
def at_tracing(context, file, line)
-
if file != @last_file || line != @last_line || Setting[:tracing_plus]
-
path = self.class.canonic_file(file)
-
@last_file, @last_line = file, line
-
puts "Tracing: #{path}:#{line} #{get_line(file, line)}"
-
end
-
always_run(context, file, line, 2)
-
end
-
1
protect :at_tracing
-
-
1
def at_line(context, file, line)
-
Byebug.source_reload if Setting[:autoreload]
-
process_commands(context, file, line)
-
end
-
1
protect :at_line
-
-
1
def at_return(context, file, line)
-
process_commands(context, file, line)
-
end
-
1
protect :at_return
-
-
1
private
-
-
#
-
# Prompt shown before reading a command.
-
#
-
1
def prompt(context)
-
"(byebug#{context.dead? ? ':post-mortem' : ''}) "
-
end
-
-
#
-
# Run commands everytime.
-
#
-
# For example display commands or possibly the list or irb in an
-
# "autolist" or "autoirb".
-
#
-
# @return List of commands acceptable to run bound to the current state
-
#
-
1
def always_run(context, file, line, run_level)
-
cmds = Command.commands
-
-
state = State.new(cmds, context, @display, file, @interface, line)
-
-
# Change default when in irb or code included in command line
-
Setting[:autolist] = false if ['(irb)', '-e'].include?(file)
-
-
# Bind commands to the current state.
-
commands = cmds.map do |cmd_class|
-
cmd = cmd_class.new(state)
-
cmd.execute if cmd.class.always_run >= run_level
-
cmd
-
end
-
-
[state, commands]
-
end
-
-
#
-
# Splits a command line of the form "cmd1 ; cmd2 ; ... ; cmdN" into an
-
# array of commands: [cmd1, cmd2, ..., cmdN]
-
#
-
1
def split_commands(cmd_line)
-
cmd_line.split(/;/).each_with_object([]) do |v, m|
-
if m.empty?
-
m << v
-
else
-
if m.last[-1] == '\\'
-
m.last[-1, 1] = ''
-
m.last << ';' << v
-
else
-
m << v
-
end
-
end
-
end
-
end
-
-
#
-
# Handle byebug commands.
-
#
-
1
def process_commands(context, file, line)
-
state, commands = preloop(context, file, line)
-
-
repl(state, commands, context)
-
-
postloop
-
end
-
-
#
-
# Main byebug's REPL
-
#
-
1
def repl(state, commands, context)
-
until state.proceed?
-
input = if @interface.command_queue.empty?
-
@interface.read_command(prompt(context))
-
else
-
@interface.command_queue.shift
-
end
-
return unless input
-
-
if input == ''
-
next unless @last_cmd
-
input = @last_cmd
-
else
-
@last_cmd = input
-
end
-
-
split_commands(input).each do |cmd|
-
one_cmd(commands, context, cmd)
-
end
-
end
-
end
-
-
#
-
# Autoevals a single command
-
#
-
1
def one_unknown_cmd(commands, input)
-
unless Setting[:autoeval]
-
return errmsg("Unknown command: \"#{input}\". Try \"help\"")
-
end
-
-
commands.find { |c| c.is_a?(EvalCommand) }.execute
-
end
-
-
#
-
# Executes a single byebug command
-
#
-
1
def one_cmd(commands, context, input)
-
cmd = commands.find { |c| c.match(input) }
-
return one_unknown_cmd(commands, input) unless cmd
-
-
if context.dead? && !cmd.class.allow_in_post_mortem
-
return errmsg('Command unavailable in post mortem mode.')
-
end
-
-
cmd.execute
-
end
-
-
#
-
# Tasks to do before processor loop.
-
#
-
1
def preloop(context, file, line)
-
state, commands = always_run(context, file, line, 1)
-
-
if Setting[:testing]
-
Thread.current.thread_variable_set('state', state)
-
else
-
Thread.current.thread_variable_set('state', nil)
-
end
-
-
@context_was_dead = true if context.dead? && !@context_was_dead
-
if @context_was_dead
-
puts 'The program finished.'
-
@context_was_dead = false
-
end
-
-
puts(state.location) if Setting[:autolist] == 0
-
-
@interface.history.restore if Setting[:autosave]
-
-
[state, commands]
-
end
-
-
#
-
# Tasks to do after processor loop.
-
#
-
1
def postloop
-
Setting[:autosave] ? @interface.history.save : @interface.history.clear
-
end
-
-
1
class State
-
1
attr_accessor :commands, :context, :display, :file, :frame_pos,
-
:interface, :line, :prev_line
-
-
1
def initialize(commands, context, display, file, interface, line)
-
@commands, @context, @display = commands, context, display
-
@file, @frame_pos, @interface = file, 0, interface
-
@line, @prev_line, @proceed = line, nil, false
-
end
-
-
1
extend Forwardable
-
1
def_delegators :@interface, :errmsg, :puts, :confirm
-
-
1
def proceed?
-
@proceed
-
end
-
-
1
def proceed
-
@proceed = true
-
end
-
-
1
def location
-
path = self.class.canonic_file(@file)
-
loc = "#{path} @ #{@line}\n"
-
loc += "#{get_line(@file, @line)}\n" unless
-
['(irb)', '-e'].include? @file
-
loc
-
end
-
end
-
end
-
end
-
1
module Byebug
-
#
-
# Processes commands in 'control' mode, when there's no program running
-
#
-
1
class ControlCommandProcessor < Processor
-
1
def initialize(interface = LocalInterface.new)
-
super(interface)
-
@context_was_dead = false # Assume we haven't started.
-
end
-
-
1
def process_commands(verbose = false)
-
control_cmds = Command.commands.select do |cmd|
-
cmd.allow_in_control
-
end
-
state = State.new(@interface, control_cmds)
-
commands = control_cmds.map { |cmd| cmd.new(state) }
-
-
if @context_was_dead
-
puts 'The program finished.'
-
@context_was_dead = false
-
end
-
-
while (input = @interface.read_command(prompt(nil)))
-
puts("+#{input}") if verbose
-
-
cmd = commands.find { |c| c.match(input) }
-
return errmsg('Unknown command') unless cmd
-
-
cmd.execute
-
end
-
rescue IOError, SystemCallError
-
rescue
-
without_exceptions do
-
puts "INTERNAL ERROR!!! #{$ERROR_INFO}"
-
puts $ERROR_INFO.backtrace.map { |l| "\t#{l}" }.join("\n")
-
end
-
ensure
-
@interface.close
-
end
-
-
#
-
# Prompt shown before reading a command.
-
#
-
1
def prompt(_context)
-
'(byebug:ctrl) '
-
end
-
-
1
class State
-
1
attr_reader :commands, :interface
-
-
1
def initialize(interface, commands)
-
@interface = interface
-
@commands = commands
-
end
-
-
1
def proceed
-
end
-
-
1
extend Forwardable
-
1
def_delegators :@interface, :errmsg, :puts
-
-
1
def confirm(*_args)
-
'y'
-
end
-
-
1
def context
-
nil
-
end
-
-
1
def file
-
errmsg 'No filename given.'
-
end
-
end
-
end
-
end
-
1
require 'socket'
-
-
1
module Byebug
-
# Port number used for remote debugging
-
1
PORT = 8989 unless defined?(PORT)
-
-
1
class << self
-
# If in remote mode, wait for the remote connection
-
1
attr_accessor :wait_connection
-
-
# The actual port that the server is started at
-
1
attr_accessor :actual_port
-
1
attr_reader :actual_control_port
-
-
#
-
# Interrupts the current thread
-
#
-
1
def interrupt
-
current_context.interrupt
-
end
-
-
#
-
# Starts a remote byebug
-
#
-
1
def start_server(host = nil, port = PORT)
-
return if @thread
-
-
self.interface = nil
-
start
-
-
start_control(host, port == 0 ? 0 : port + 1)
-
-
yield if block_given?
-
-
mutex = Mutex.new
-
proceed = ConditionVariable.new
-
-
server = TCPServer.new(host, port)
-
self.actual_port = server.addr[1]
-
@thread = DebugThread.new do
-
while (session = server.accept)
-
self.interface = RemoteInterface.new(session)
-
mutex.synchronize { proceed.signal } if wait_connection
-
end
-
end
-
-
mutex.synchronize { proceed.wait(mutex) } if wait_connection
-
end
-
-
1
def start_control(host = nil, ctrl_port = PORT + 1)
-
return @actual_control_port if @control_thread
-
server = TCPServer.new(host, ctrl_port)
-
@actual_control_port = server.addr[1]
-
@control_thread = DebugThread.new do
-
while (session = server.accept)
-
interface = RemoteInterface.new(session)
-
ControlCommandProcessor.new(interface).process_commands
-
end
-
end
-
@actual_control_port
-
end
-
-
#
-
# Connects to the remote byebug
-
#
-
1
def start_client(host = 'localhost', port = PORT)
-
interface = LocalInterface.new
-
socket = TCPSocket.new(host, port)
-
puts 'Connected.'
-
-
catch(:exit) do
-
while (line = socket.gets)
-
case line
-
when /^PROMPT (.*)$/
-
input = interface.read_command(Regexp.last_match[1])
-
throw :exit unless input
-
socket.puts input
-
when /^CONFIRM (.*)$/
-
input = interface.confirm(Regexp.last_match[1])
-
throw :exit unless input
-
socket.puts input
-
else
-
puts line
-
end
-
end
-
end
-
socket.close
-
end
-
end
-
end
-
1
module Byebug
-
#
-
# Parent class for all byebug settings.
-
#
-
1
class Setting
-
1
attr_accessor :value
-
-
1
DEFAULT = false
-
-
1
def initialize
-
16
@value = self.class::DEFAULT
-
end
-
-
1
def self.settings
-
19
@settings ||= {}
-
end
-
-
1
def self.[](name)
-
settings[name].value
-
end
-
-
1
def self.[]=(name, value)
-
settings[name].value = value
-
end
-
-
1
def self.boolean?(name)
-
key = (name =~ /^no/ ? name[2..-1] : name).to_sym
-
settings[key].boolean?
-
end
-
-
1
def self.integer?(name)
-
settings[name.to_sym].integer?
-
end
-
-
1
def boolean?
-
[true, false].include?(value)
-
end
-
-
1
def integer?
-
Integer(value) ? true : false
-
rescue ArgumentError
-
false
-
end
-
-
1
def self.exists?(name)
-
key = (name =~ /^no/ ? name[2..-1] : name).to_sym
-
boolean?(key) ? settings.include?(key) : settings.include?(name.to_sym)
-
end
-
-
1
def self.load
-
1
Dir.glob(File.expand_path('../settings/*.rb', __FILE__)).each do |file|
-
19
require file
-
end
-
1
Byebug.constants.grep(/[a-z]Setting/).map do |name|
-
19
setting = Byebug.const_get(name).new
-
19
settings[setting.to_sym] = setting
-
end
-
end
-
-
1
def self.find(shortcut)
-
abbr = shortcut =~ /^no/ ? shortcut[2..-1] : shortcut
-
matches = settings.select do |key, value|
-
value.boolean? ? key =~ /#{abbr}/ : key =~ /#{shortcut}/
-
end
-
matches.size == 1 ? matches.keys.first : nil
-
end
-
-
1
def self.help_all
-
output = " List of settings supported in byebug:\n --\n"
-
width = settings.keys.max_by(&:size).size
-
settings.values.each do |sett|
-
output << format(" %-#{width}s -- %s\n", sett.to_sym, sett.banner)
-
end
-
output + "\n"
-
end
-
-
1
def self.help(cmd, subcmd)
-
if subcmd
-
camelized = subcmd.split('_').map { |w| w.capitalize }.join
-
setting = Byebug.const_get("#{camelized}Setting").new
-
<<-EOH.gsub(/^ {8}/, '')
-
-
#{cmd} #{setting.to_sym} <value>
-
-
#{setting.banner}.
-
-
EOH
-
else
-
command = Byebug.const_get("#{cmd.capitalize}Command")
-
command.description + help_all
-
end
-
end
-
-
1
def help
-
"\n #{banner}.\n\n"
-
end
-
-
1
def to_sym
-
19
name = self.class.name.gsub(/^Byebug::/, '').gsub(/Setting$/, '')
-
19
name.gsub(/(.)([A-Z])/, '\1_\2').downcase.to_sym
-
end
-
-
1
def to_s
-
"#{to_sym} is #{value ? 'on' : 'off'}\n"
-
end
-
end
-
-
1
Setting.load
-
end
-
1
module Byebug
-
#
-
# Setting for automatic evaluation of unknown commands.
-
#
-
1
class AutoevalSetting < Setting
-
1
DEFAULT = true
-
-
1
def banner
-
'Automatically evaluate unrecognized commands'
-
end
-
end
-
end
-
1
module Byebug
-
#
-
# Setting for automatically invoking IRB on every stop.
-
#
-
1
class AutoirbSetting < Setting
-
1
DEFAULT = 0
-
-
1
def initialize
-
1
IrbCommand.always_run = DEFAULT
-
end
-
-
1
def banner
-
'Invoke IRB on every stop'
-
end
-
-
1
def value=(v)
-
IrbCommand.always_run = v ? 1 : 0
-
end
-
-
1
def value
-
IrbCommand.always_run == 1
-
end
-
end
-
end
-
1
module Byebug
-
#
-
# Setting for automatically listing source code on every stop.
-
#
-
1
class AutolistSetting < Setting
-
1
DEFAULT = 1
-
-
1
def initialize
-
1
ListCommand.always_run = DEFAULT
-
end
-
-
1
def banner
-
'Invoke list command on every stop'
-
end
-
-
1
def value=(v)
-
ListCommand.always_run = v ? 1 : 0
-
end
-
-
1
def value
-
ListCommand.always_run == 1
-
end
-
end
-
end
-
1
module Byebug
-
#
-
# Setting for automatically reloading source code when it is changed.
-
#
-
1
class AutoreloadSetting < Setting
-
1
DEFAULT = true
-
-
1
def banner
-
'Automatically reload source code when it is changed'
-
end
-
end
-
end
-
1
module Byebug
-
#
-
# Setting for automatically saving previously entered commands to history
-
# when exiting the debugger.
-
#
-
1
class AutosaveSetting < Setting
-
1
DEFAULT = true
-
-
1
def banner
-
'Automatically save command history record on exit'
-
end
-
end
-
end
-
1
module Byebug
-
#
-
# Command to display short paths in file names.
-
#
-
# For example, when displaying source code information.
-
#
-
1
class BasenameSetting < Setting
-
1
def banner
-
'<file>:<line> information after every stop uses short paths'
-
end
-
end
-
end
-
1
module Byebug
-
#
-
# Setting to customize the verbosity level for stack frames.
-
#
-
1
class CallstyleSetting < Setting
-
1
DEFAULT = 'long'
-
-
1
def banner
-
'Set how you want method call parameters to be displayed'
-
end
-
-
1
def to_s
-
"Frame display callstyle is '#{value}'"
-
end
-
end
-
end
-
1
module Byebug
-
#
-
# Setting to force changing lines when executing step or next commands.
-
#
-
1
class ForcestepSetting < Setting
-
1
def banner
-
'Force next/step commands to always move to a new line'
-
end
-
-
1
def print
-
"forced-stepping is #{getter}"
-
end
-
end
-
end
-
1
module Byebug
-
#
-
# Setting to display full paths in backtraces.
-
#
-
1
class FullpathSetting < Setting
-
1
DEFAULT = true
-
-
1
def banner
-
'Display full file names in backtraces'
-
end
-
end
-
end
-
1
module Byebug
-
#
-
# Setting to customize the file where byebug's history is saved.
-
#
-
1
class HistfileSetting < Setting
-
1
DEFAULT = File.expand_path("#{ENV['HOME'] || '.'}/.byebug_hist")
-
-
1
def banner
-
'File where cmd history is saved to. Default: ~/.byebug_hist'
-
end
-
-
1
def to_s
-
"The command history file is #{value}\n"
-
end
-
end
-
end
-
1
module Byebug
-
#
-
# Setting to customize the number of byebug commands to be saved in history.
-
#
-
1
class HistsizeSetting < Setting
-
1
DEFAULT = 256
-
-
1
def banner
-
'Maximum number of commands that can be stored in byebug history'
-
end
-
-
1
def to_s
-
"Maximum size of byebug's command history is #{value}"
-
end
-
end
-
end
-
1
module Byebug
-
#
-
# Setting to enable/disable linetracing.
-
#
-
1
class LinetraceSetting < Setting
-
1
def banner
-
'Enable line execution tracing'
-
end
-
-
1
def value=(v)
-
Byebug.tracing = v
-
end
-
-
1
def value
-
Byebug.tracing?
-
end
-
end
-
end
-
1
module Byebug
-
#
-
# Setting to customize the number of source code lines to be displayed every
-
# time the "list" command is invoked.
-
#
-
1
class ListsizeSetting < Setting
-
1
DEFAULT = 10
-
-
1
def banner
-
'Set number of source lines to list by default'
-
end
-
-
1
def to_s
-
"Number of source lines to list is #{value}\n"
-
end
-
end
-
end
-
1
module Byebug
-
#
-
# Setting to enable/disable post_mortem mode, i.e., a debugger prompt after
-
# program termination by unhandled exception.
-
#
-
1
class PostMortemSetting < Setting
-
1
def initialize
-
1
Byebug.post_mortem = DEFAULT
-
end
-
-
1
def banner
-
'Enable/disable post-mortem mode'
-
end
-
-
1
def value=(v)
-
Byebug.post_mortem = v
-
end
-
-
1
def value
-
Byebug.post_mortem?
-
end
-
end
-
-
#
-
# Saves information about the unhandled exception and gives a byebug
-
# prompt back to the user before program termination.
-
#
-
1
def self.handle_post_mortem
-
context = Byebug.raised_exception.__bb_context
-
file = Byebug.raised_exception.__bb_file
-
line = Byebug.raised_exception.__bb_line
-
Byebug.handler.at_line(context, file, line)
-
end
-
-
2
at_exit { Byebug.handle_post_mortem if Byebug.post_mortem? }
-
end
-
1
module Byebug
-
#
-
# Setting to enable/disable the display of backtraces when evaluations raise
-
# errors.
-
#
-
1
class StackOnErrorSetting < Setting
-
1
def banner
-
'Display stack trace when `eval` raises an exception'
-
end
-
end
-
end
-
1
module Byebug
-
#
-
# Special setting to flag that byebug is being tested.
-
#
-
# FIXME: make this private.
-
#
-
1
class TestingSetting < Setting
-
1
def banner
-
'Used when testing byebug'
-
end
-
end
-
end
-
1
module Byebug
-
#
-
# Setting to allow consecutive repeated lines to be displayed when line
-
# tracing is enabled.
-
#
-
1
class TracingPlusSetting < Setting
-
1
def banner
-
'Set line execution tracing to always show different lines'
-
end
-
end
-
end
-
1
module Byebug
-
#
-
# Setting to show verbose output about TracePoint API events.
-
#
-
1
class VerboseSetting < Setting
-
1
def banner
-
'Enable verbose output of TracePoint API events'
-
end
-
-
1
def value=(v)
-
Byebug.verbose = v
-
end
-
-
1
def value
-
Byebug.verbose?
-
end
-
end
-
end
-
1
module Byebug
-
#
-
# Setting to customize the maximum width of byebug's output.
-
#
-
1
class WidthSetting < Setting
-
1
DEFAULT = 160
-
-
1
def banner
-
"Number of characters per line in byebug's output"
-
end
-
-
1
def to_s
-
"Maximum width of byebug's output is #{value}"
-
end
-
end
-
end
-
1
module Byebug
-
1
VERSION = '3.5.1'
-
end
-
1
require 'timeout'
-
1
require 'nokogiri'
-
1
require 'xpath'
-
-
1
module Capybara
-
1
class CapybaraError < StandardError; end
-
1
class DriverNotFoundError < CapybaraError; end
-
1
class FrozenInTime < CapybaraError; end
-
1
class ElementNotFound < CapybaraError; end
-
1
class ModalNotFound < CapybaraError; end
-
1
class Ambiguous < ElementNotFound; end
-
1
class ExpectationNotMet < ElementNotFound; end
-
1
class FileNotFound < CapybaraError; end
-
1
class UnselectNotAllowed < CapybaraError; end
-
1
class NotSupportedByDriverError < CapybaraError; end
-
1
class InfiniteRedirectError < CapybaraError; end
-
1
class ScopeError < CapybaraError; end
-
1
class WindowError < CapybaraError; end
-
1
class ReadOnlyElementError < CapybaraError; end
-
-
1
class << self
-
1
attr_accessor :asset_host, :app_host, :run_server, :default_host, :always_include_port
-
1
attr_accessor :server_port, :exact, :match, :exact_options, :visible_text_only
-
1
attr_accessor :default_selector, :default_wait_time, :ignore_hidden_elements
-
1
attr_accessor :save_and_open_page_path, :automatic_reload, :raise_server_errors
-
1
attr_writer :default_driver, :current_driver, :javascript_driver, :session_name, :server_host
-
1
attr_accessor :app
-
-
##
-
#
-
# Configure Capybara to suit your needs.
-
#
-
# Capybara.configure do |config|
-
# config.run_server = false
-
# config.app_host = 'http://www.google.com'
-
# end
-
#
-
# === Configurable options
-
#
-
# [app_host = String] The default host to use when giving a relative URL to visit
-
# [always_include_port = Boolean] Whether the Rack server's port should automatically be inserted into every visited URL (Default: false)
-
# [asset_host = String] Where dynamic assets are hosted - will be prepended to relative asset locations if present (Default: nil)
-
# [run_server = Boolean] Whether to start a Rack server for the given Rack app (Default: true)
-
# [default_selector = :css/:xpath] Methods which take a selector use the given type by default (Default: CSS)
-
# [default_wait_time = Integer] The number of seconds to wait for asynchronous processes to finish (Default: 2)
-
# [ignore_hidden_elements = Boolean] Whether to ignore hidden elements on the page (Default: true)
-
# [automatic_reload = Boolean] Whether to automatically reload elements as Capybara is waiting (Default: true)
-
# [save_and_open_page_path = String] Where to put pages saved through save_and_open_page (Default: Dir.pwd)
-
#
-
# === DSL Options
-
#
-
# when using capybara/dsl, the following options are also available:
-
#
-
# [default_driver = Symbol] The name of the driver to use by default. (Default: :rack_test)
-
# [javascript_driver = Symbol] The name of a driver to use for JavaScript enabled tests. (Default: :selenium)
-
#
-
1
def configure
-
1
yield self
-
end
-
-
##
-
#
-
# Register a new driver for Capybara.
-
#
-
# Capybara.register_driver :rack_test do |app|
-
# Capybara::Driver::RackTest.new(app)
-
# end
-
#
-
# @param [Symbol] name The name of the new driver
-
# @yield [app] This block takes a rack app and returns a Capybara driver
-
# @yieldparam [<Rack>] app The rack application that this driver runs agains. May be nil.
-
# @yieldreturn [Capybara::Driver::Base] A Capybara driver instance
-
#
-
1
def register_driver(name, &block)
-
3
drivers[name] = block
-
end
-
-
##
-
#
-
# Add a new selector to Capybara. Selectors can be used by various methods in Capybara
-
# to find certain elements on the page in a more convenient way. For example adding a
-
# selector to find certain table rows might look like this:
-
#
-
# Capybara.add_selector(:row) do
-
# xpath { |num| ".//tbody/tr[#{num}]" }
-
# end
-
#
-
# This makes it possible to use this selector in a variety of ways:
-
#
-
# find(:row, 3)
-
# page.find('table#myTable').find(:row, 3).text
-
# page.find('table#myTable').has_selector?(:row, 3)
-
# within(:row, 3) { expect(page).to have_content('$100.000') }
-
#
-
# Here is another example:
-
#
-
# Capybara.add_selector(:id) do
-
# xpath { |id| XPath.descendant[XPath.attr(:id) == id.to_s] }
-
# end
-
#
-
# Note that this particular selector already ships with Capybara.
-
#
-
# @param [Symbol] name The name of the selector to add
-
# @yield A block executed in the context of the new {Capybara::Selector}
-
#
-
1
def add_selector(name, &block)
-
15
Capybara::Selector.add(name, &block)
-
end
-
-
1
def drivers
-
3
@drivers ||= {}
-
end
-
-
##
-
#
-
# Register a proc that Capybara will call to run the Rack application.
-
#
-
# Capybara.server do |app, port|
-
# require 'rack/handler/mongrel'
-
# Rack::Handler::Mongrel.run(app, :Port => port)
-
# end
-
#
-
# By default, Capybara will try to run webrick.
-
#
-
# @yield [app, port] This block receives a rack app and port and should run a Rack handler
-
#
-
1
def server(&block)
-
1
if block_given?
-
1
@server = block
-
else
-
@server
-
end
-
end
-
-
##
-
#
-
# Wraps the given string, which should contain an HTML document or fragment
-
# in a {Capybara::Node::Simple} which exposes all {Capybara::Node::Matchers} and
-
# {Capybara::Node::Finders}. This allows you to query any string containing
-
# HTML in the exact same way you would query the current document in a Capybara
-
# session. For example:
-
#
-
# node = Capybara.string <<-HTML
-
# <ul>
-
# <li id="home">Home</li>
-
# <li id="projects">Projects</li>
-
# </ul>
-
# HTML
-
#
-
# node.find('#projects').text # => 'Projects'
-
# node.has_selector?('li#home', :text => 'Home')
-
# node.has_selector?(:projects)
-
# node.find('ul').find('li').text # => 'Home'
-
#
-
# @param [String] html An html fragment or document
-
# @return [Capybara::Node::Simple] A node which has Capybara's finders and matchers
-
#
-
1
def string(html)
-
Capybara::Node::Simple.new(html)
-
end
-
-
##
-
#
-
# Runs Capybara's default server for the given application and port
-
# under most circumstances you should not have to call this method
-
# manually.
-
#
-
# @param [Rack Application] app The rack application to run
-
# @param [Fixnum] port The port to run the application on
-
#
-
1
def run_default_server(app, port)
-
require 'rack/handler/webrick'
-
Rack::Handler::WEBrick.run(app, :Host => server_host, :Port => port, :AccessLog => [], :Logger => WEBrick::Log::new(nil, 0))
-
end
-
-
##
-
#
-
# @return [Symbol] The name of the driver to use by default
-
#
-
1
def default_driver
-
@default_driver || :rack_test
-
end
-
-
##
-
#
-
# @return [Symbol] The name of the driver currently in use
-
#
-
1
def current_driver
-
@current_driver || default_driver
-
end
-
1
alias_method :mode, :current_driver
-
-
##
-
#
-
# @return [Symbol] The name of the driver used when JavaScript is needed
-
#
-
1
def javascript_driver
-
@javascript_driver || :selenium
-
end
-
-
##
-
#
-
# Use the default driver as the current driver
-
#
-
1
def use_default_driver
-
@current_driver = nil
-
end
-
-
##
-
#
-
# Yield a block using a specific driver
-
#
-
1
def using_driver(driver)
-
previous_driver = Capybara.current_driver
-
Capybara.current_driver = driver
-
yield
-
ensure
-
@current_driver = previous_driver
-
end
-
-
##
-
#
-
# @return [String] The IP address bound by default server
-
#
-
1
def server_host
-
@server_host || '127.0.0.1'
-
end
-
-
##
-
#
-
# Yield a block using a specific wait time
-
#
-
1
def using_wait_time(seconds)
-
previous_wait_time = Capybara.default_wait_time
-
Capybara.default_wait_time = seconds
-
yield
-
ensure
-
Capybara.default_wait_time = previous_wait_time
-
end
-
-
##
-
#
-
# The current Capybara::Session based on what is set as Capybara.app and Capybara.current_driver
-
#
-
# @return [Capybara::Session] The currently used session
-
#
-
1
def current_session
-
session_pool["#{current_driver}:#{session_name}:#{app.object_id}"] ||= Capybara::Session.new(current_driver, app)
-
end
-
-
##
-
#
-
# Reset sessions, cleaning out the pool of sessions. This will remove any session information such
-
# as cookies.
-
#
-
1
def reset_sessions!
-
session_pool.each { |mode, session| session.reset! }
-
end
-
1
alias_method :reset!, :reset_sessions!
-
-
##
-
#
-
# The current session name.
-
#
-
# @return [Symbol] The name of the currently used session.
-
#
-
1
def session_name
-
@session_name ||= :default
-
end
-
-
##
-
#
-
# Yield a block using a specific session name.
-
#
-
1
def using_session(name)
-
self.session_name = name
-
yield
-
ensure
-
self.session_name = :default
-
end
-
-
##
-
#
-
# Parse raw html into a document using Nokogiri, and adjust textarea contents as defined by the spec.
-
#
-
# @param [String] html The raw html
-
# @return [Nokogiri::HTML::Document] HTML document
-
#
-
1
def HTML(html)
-
Nokogiri::HTML(html).tap do |document|
-
document.xpath('//textarea').each do |textarea|
-
textarea.content=textarea.content.sub(/\A\n/,'')
-
end
-
end
-
end
-
-
1
def included(base)
-
base.send(:include, Capybara::DSL)
-
warn "`include Capybara` is deprecated. Please use `include Capybara::DSL` instead."
-
end
-
-
1
def deprecate(method, alternate_method)
-
warn "DEPRECATED: ##{method} is deprecated, please use ##{alternate_method} instead"
-
end
-
-
1
private
-
-
1
def session_pool
-
@session_pool ||= {}
-
end
-
end
-
-
1
self.default_driver = nil
-
1
self.current_driver = nil
-
-
1
module Driver; end
-
1
module RackTest; end
-
1
module Selenium; end
-
-
1
require 'capybara/helpers'
-
1
require 'capybara/session'
-
1
require 'capybara/dsl'
-
1
require 'capybara/window'
-
1
require 'capybara/server'
-
1
require 'capybara/selector'
-
1
require 'capybara/result'
-
1
require 'capybara/version'
-
-
1
require 'capybara/queries/base_query'
-
1
require 'capybara/query'
-
1
require 'capybara/queries/text_query'
-
1
require 'capybara/queries/title_query'
-
-
1
require 'capybara/node/finders'
-
1
require 'capybara/node/matchers'
-
1
require 'capybara/node/actions'
-
1
require 'capybara/node/document_matchers'
-
1
require 'capybara/node/simple'
-
1
require 'capybara/node/base'
-
1
require 'capybara/node/element'
-
1
require 'capybara/node/document'
-
-
1
require 'capybara/driver/base'
-
1
require 'capybara/driver/node'
-
-
1
require 'capybara/rack_test/driver'
-
1
require 'capybara/rack_test/node'
-
1
require 'capybara/rack_test/form'
-
1
require 'capybara/rack_test/browser'
-
1
require 'capybara/rack_test/css_handlers.rb'
-
-
1
require 'capybara/selenium/node'
-
1
require 'capybara/selenium/driver'
-
end
-
-
1
Capybara.configure do |config|
-
1
config.always_include_port = false
-
1
config.run_server = true
-
1
config.server {|app, port| Capybara.run_default_server(app, port)}
-
1
config.default_selector = :css
-
1
config.default_wait_time = 2
-
1
config.ignore_hidden_elements = true
-
1
config.default_host = "http://www.example.com"
-
1
config.automatic_reload = true
-
1
config.match = :smart
-
1
config.exact = false
-
1
config.raise_server_errors = true
-
1
config.visible_text_only = false
-
end
-
-
1
Capybara.register_driver :rack_test do |app|
-
Capybara::RackTest::Driver.new(app)
-
end
-
-
1
Capybara.register_driver :selenium do |app|
-
Capybara::Selenium::Driver.new(app)
-
end
-
1
class Capybara::Driver::Base
-
1
def current_url
-
raise NotImplementedError
-
end
-
-
1
def visit(path)
-
raise NotImplementedError
-
end
-
-
1
def find_xpath(query)
-
raise NotImplementedError
-
end
-
-
1
def find_css(query)
-
raise NotImplementedError
-
end
-
-
1
def html
-
raise NotImplementedError
-
end
-
-
1
def go_back
-
raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#go_back'
-
end
-
-
1
def go_forward
-
raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#go_forward'
-
end
-
-
1
def execute_script(script)
-
raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#execute_script'
-
end
-
-
1
def evaluate_script(script)
-
raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#evaluate_script'
-
end
-
-
1
def save_screenshot(path, options={})
-
raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#save_screenshot'
-
end
-
-
1
def response_headers
-
raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#response_headers'
-
end
-
-
1
def status_code
-
raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#status_code'
-
end
-
-
1
def within_frame(frame_handle)
-
raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#within_frame'
-
end
-
-
1
def current_window_handle
-
raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#current_window_handle'
-
end
-
-
1
def window_size(handle)
-
raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#window_size'
-
end
-
-
1
def resize_window_to(handle, width, height)
-
raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#resize_window_to'
-
end
-
-
1
def maximize_window(handle)
-
raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#maximize_current_window'
-
end
-
-
1
def close_window(handle)
-
raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#close_window'
-
end
-
-
1
def window_handles
-
raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#window_handles'
-
end
-
-
1
def open_new_window
-
raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#open_new_window'
-
end
-
-
1
def switch_to_window(handle)
-
raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#switch_to_window'
-
end
-
-
1
def within_window(locator)
-
raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#within_window'
-
end
-
-
1
def no_such_window_error
-
raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#no_such_window_error'
-
end
-
-
-
##
-
#
-
# Execute the block, and then accept the modal opened.
-
# @param type [:alert, :confirm, :prompt]
-
# @option options [Numeric] :wait How long to wait for the modal to appear after executing the block.
-
# @option options [String, Regexp] :text Text to verify is in the message shown in the modal
-
# @option options [String] :with Text to fill in in the case of a prompt
-
# @return [String] the message shown in the modal
-
# @raise [Capybara::ModalNotFound] if modal dialog hasn't been found
-
#
-
1
def accept_modal(type, options={}, &blk)
-
raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#accept_modal'
-
end
-
-
##
-
#
-
# Execute the block, and then dismiss the modal opened.
-
# @param type [:alert, :confirm, :prompt]
-
# @option options [Numeric] :wait How long to wait for the modal to appear after executing the block.
-
# @option options [String, Regexp] :text Text to verify is in the message shown in the modal
-
# @return [String] the message shown in the modal
-
# @raise [Capybara::ModalNotFound] if modal dialog hasn't been found
-
#
-
1
def dismiss_modal(type, options={}, &blk)
-
raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#dismiss_modal'
-
end
-
-
1
def invalid_element_errors
-
[]
-
end
-
-
1
def wait?
-
false
-
end
-
-
1
def reset!
-
end
-
-
1
def needs_server?
-
false
-
end
-
end
-
1
module Capybara
-
1
module Driver
-
1
class Node
-
1
attr_reader :driver, :native
-
-
1
def initialize(driver, native)
-
@driver = driver
-
@native = native
-
end
-
-
1
def all_text
-
raise NotImplementedError
-
end
-
-
1
def visible_text
-
raise NotImplementedError
-
end
-
-
1
def [](name)
-
raise NotImplementedError
-
end
-
-
1
def value
-
raise NotImplementedError
-
end
-
-
# @param value String or Array. Array is only allowed if node has 'multiple' attribute
-
# @param options [Hash{}] Driver specific options for how to set a value on a node
-
1
def set(value, options={})
-
raise NotImplementedError
-
end
-
-
1
def select_option
-
raise NotImplementedError
-
end
-
-
1
def unselect_option
-
raise NotImplementedError
-
end
-
-
1
def click
-
raise NotImplementedError
-
end
-
-
1
def right_click
-
raise NotImplementedError
-
end
-
-
1
def double_click
-
raise NotImplementedError
-
end
-
-
1
def hover
-
raise NotImplementedError
-
end
-
-
1
def drag_to(element)
-
raise NotImplementedError
-
end
-
-
1
def tag_name
-
raise NotImplementedError
-
end
-
-
1
def visible?
-
raise NotImplementedError
-
end
-
-
1
def checked?
-
raise NotImplementedError
-
end
-
-
1
def selected?
-
raise NotImplementedError
-
end
-
-
1
def disabled?
-
raise NotImplementedError
-
end
-
-
1
def path
-
raise NotSupportedByDriverError, 'Capybara::Driver::Node#path'
-
end
-
-
1
def trigger(event)
-
raise NotSupportedByDriverError, 'Capybara::Driver::Node#trigger'
-
end
-
-
1
def inspect
-
%(#<#{self.class} tag="#{tag_name}" path="#{path}">)
-
rescue NotSupportedByDriverError
-
%(#<#{self.class} tag="#{tag_name}">)
-
end
-
-
1
def ==(other)
-
raise NotSupportedByDriverError, 'Capybara::Driver::Node#=='
-
end
-
end
-
end
-
end
-
1
require 'capybara'
-
-
1
module Capybara
-
1
module DSL
-
1
def self.included(base)
-
warn "including Capybara::DSL in the global scope is not recommended!" if base == Object
-
super
-
end
-
-
1
def self.extended(base)
-
1
warn "extending the main object with Capybara::DSL is not recommended!" if base == TOPLEVEL_BINDING.eval("self")
-
1
super
-
end
-
-
##
-
#
-
# Shortcut to working in a different session.
-
#
-
1
def using_session(name, &block)
-
Capybara.using_session(name, &block)
-
end
-
-
##
-
#
-
# Shortcut to using a different wait time.
-
#
-
1
def using_wait_time(seconds, &block)
-
Capybara.using_wait_time(seconds, &block)
-
end
-
-
##
-
#
-
# Shortcut to accessing the current session.
-
#
-
# class MyClass
-
# include Capybara::DSL
-
#
-
# def has_header?
-
# page.has_css?('h1')
-
# end
-
# end
-
#
-
# @return [Capybara::Session] The current session object
-
#
-
1
def page
-
Capybara.current_session
-
end
-
-
1
Session::DSL_METHODS.each do |method|
-
90
define_method method do |*args, &block|
-
page.send method, *args, &block
-
end
-
end
-
end
-
-
1
extend(Capybara::DSL)
-
end
-
# encoding: UTF-8
-
-
1
module Capybara
-
-
# @api private
-
1
module Helpers
-
1
extend self
-
-
##
-
#
-
# Normalizes whitespace space by stripping leading and trailing
-
# whitespace and replacing sequences of whitespace characters
-
# with a single space.
-
#
-
# @param [String] text Text to normalize
-
# @return [String] Normalized text
-
#
-
1
def normalize_whitespace(text)
-
text.to_s.gsub(/[[:space:]]+/, ' ').strip
-
end
-
-
##
-
#
-
# Escapes any characters that would have special meaning in a regexp
-
# if text is not a regexp
-
#
-
# @param [String] text Text to escape
-
# @return [String] Escaped text
-
#
-
1
def to_regexp(text)
-
text.is_a?(Regexp) ? text : Regexp.new(Regexp.escape(normalize_whitespace(text)))
-
end
-
-
##
-
#
-
# Injects a `<base>` tag into the given HTML code, pointing to
-
# `Capybara.asset_host`.
-
#
-
# @param [String] html HTML code to inject into
-
# @return [String] The modified HTML code
-
#
-
1
def inject_asset_host(html)
-
if Capybara.asset_host && Nokogiri::HTML(html).css("base").empty?
-
match = html.match(/<head[^<]*?>/)
-
html.clone.insert match.end(0), "<base href='#{Capybara.asset_host}' />"
-
else
-
html
-
end
-
end
-
-
##
-
#
-
# Checks if the given count matches the given count options.
-
# Defaults to true if no options are specified. If multiple
-
# options are provided, it tests that all conditions are met;
-
# however, if :count is supplied, all other options are ignored.
-
#
-
# @param [Integer] count The actual number. Should be coercible via Integer()
-
# @option [Range] between Count must be within the given range
-
# @option [Integer] count Count must be exactly this
-
# @option [Integer] maximum Count must be smaller than or equal to this value
-
# @option [Integer] minimum Count must be larger than or equal to this value
-
#
-
1
def matches_count?(count, options={})
-
return (Integer(options[:count]) == count) if options[:count]
-
return false if options[:maximum] && (Integer(options[:maximum]) < count)
-
return false if options[:minimum] && (Integer(options[:minimum]) > count)
-
return false if options[:between] && !(options[:between] === count)
-
return true
-
end
-
-
##
-
#
-
# Checks if a count of 0 is valid for the given options hash.
-
# Returns false if options hash does not specify any count options.
-
#
-
1
def expects_none?(options={})
-
if [:count, :maximum, :minimum, :between].any? { |k| options.has_key? k }
-
matches_count?(0,options)
-
else
-
false
-
end
-
end
-
-
##
-
#
-
# Generates a failure message given a description of the query and count
-
# options.
-
#
-
# @param [String] description Description of a query
-
# @option [Range] between Count should have been within the given range
-
# @option [Integer] count Count should have been exactly this
-
# @option [Integer] maximum Count should have been smaller than or equal to this value
-
# @option [Integer] minimum Count should have been larger than or equal to this value
-
#
-
1
def failure_message(description, options={})
-
message = "expected to find #{description}"
-
if options[:count]
-
message << " #{options[:count]} #{declension('time', 'times', options[:count])}"
-
elsif options[:between]
-
message << " between #{options[:between].first} and #{options[:between].last} times"
-
elsif options[:maximum]
-
message << " at most #{options[:maximum]} #{declension('time', 'times', options[:maximum])}"
-
elsif options[:minimum]
-
message << " at least #{options[:minimum]} #{declension('time', 'times', options[:minimum])}"
-
end
-
message
-
end
-
-
##
-
#
-
# A poor man's `pluralize`. Given two declensions, one singular and one
-
# plural, as well as a count, this will pick the correct declension. This
-
# way we can generate grammatically correct error message.
-
#
-
# @param [String] singular The singular form of the word
-
# @param [String] plural The plural form of the word
-
# @param [Integer] count The number of items
-
#
-
1
def declension(singular, plural, count)
-
if count == 1
-
singular
-
else
-
plural
-
end
-
end
-
end
-
end
-
1
module Capybara
-
1
module Node
-
1
module Actions
-
-
##
-
#
-
# Finds a button or link by id, text or value and clicks it. Also looks at image
-
# alt text inside the link.
-
#
-
# @param [String] locator Text, id or value of link or button
-
#
-
1
def click_link_or_button(locator, options={})
-
find(:link_or_button, locator, options).click
-
end
-
1
alias_method :click_on, :click_link_or_button
-
-
##
-
#
-
# Finds a link by id or text and clicks it. Also looks at image
-
# alt text inside the link.
-
#
-
# @param [String] locator Text or id of link
-
# @param options
-
# @option options [String] :href The value the href attribute must be
-
#
-
1
def click_link(locator, options={})
-
find(:link, locator, options).click
-
end
-
-
##
-
#
-
# Finds a button by id, text or value and clicks it.
-
#
-
# @param [String] locator Text, id or value of button
-
#
-
1
def click_button(locator, options={})
-
find(:button, locator, options).click
-
end
-
-
##
-
#
-
# Locate a text field or text area and fill it in with the given text
-
# The field can be found via its name, id or label text.
-
#
-
# page.fill_in 'Name', :with => 'Bob'
-
#
-
# @param [String] locator Which field to fill in
-
# @param [Hash] options
-
# @option options [String] :with The value to fill in - required
-
# @option options [Hash] :fill_options Driver specific options regarding how to fill fields
-
#
-
1
def fill_in(locator, options={})
-
raise "Must pass a hash containing 'with'" if not options.is_a?(Hash) or not options.has_key?(:with)
-
with = options.delete(:with)
-
fill_options = options.delete(:fill_options)
-
find(:fillable_field, locator, options).set(with, fill_options)
-
end
-
-
##
-
#
-
# Find a radio button and mark it as checked. The radio button can be found
-
# via name, id or label text.
-
#
-
# page.choose('Male')
-
#
-
# @param [String] locator Which radio button to choose
-
#
-
1
def choose(locator, options={})
-
find(:radio_button, locator, options).set(true)
-
end
-
-
##
-
#
-
# Find a check box and mark it as checked. The check box can be found
-
# via name, id or label text.
-
#
-
# page.check('German')
-
#
-
# @param [String] locator Which check box to check
-
#
-
1
def check(locator, options={})
-
find(:checkbox, locator, options).set(true)
-
end
-
-
##
-
#
-
# Find a check box and mark uncheck it. The check box can be found
-
# via name, id or label text.
-
#
-
# page.uncheck('German')
-
#
-
# @param [String] locator Which check box to uncheck
-
#
-
1
def uncheck(locator, options={})
-
find(:checkbox, locator, options).set(false)
-
end
-
-
##
-
#
-
# Find a select box on the page and select a particular option from it. If the select
-
# box is a multiple select, +select+ can be called multiple times to select more than
-
# one option. The select box can be found via its name, id or label text.
-
#
-
# page.select 'March', :from => 'Month'
-
#
-
# @param [String] value Which option to select
-
# @param [Hash{:from => String}] options The id, name or label of the select box
-
#
-
1
def select(value, options={})
-
if options.has_key?(:from)
-
from = options.delete(:from)
-
find(:select, from, options).find(:option, value, options).select_option
-
else
-
find(:option, value, options).select_option
-
end
-
end
-
-
##
-
#
-
# Find a select box on the page and unselect a particular option from it. If the select
-
# box is a multiple select, +unselect+ can be called multiple times to unselect more than
-
# one option. The select box can be found via its name, id or label text.
-
#
-
# page.unselect 'March', :from => 'Month'
-
#
-
# @param [String] value Which option to unselect
-
# @param [Hash{:from => String}] options The id, name or label of the select box
-
#
-
1
def unselect(value, options={})
-
if options.has_key?(:from)
-
from = options.delete(:from)
-
find(:select, from, options).find(:option, value, options).unselect_option
-
else
-
find(:option, value, options).unselect_option
-
end
-
end
-
-
##
-
#
-
# Find a file field on the page and attach a file given its path. The file field can
-
# be found via its name, id or label text.
-
#
-
# page.attach_file(locator, '/path/to/file.png')
-
#
-
# @param [String] locator Which field to attach the file to
-
# @param [String] path The path of the file that will be attached, or an array of paths
-
#
-
1
def attach_file(locator, path, options={})
-
Array(path).each do |p|
-
raise Capybara::FileNotFound, "cannot attach file, #{p} does not exist" unless File.exist?(p.to_s)
-
end
-
find(:file_field, locator, options).set(path)
-
end
-
end
-
end
-
end
-
1
module Capybara
-
1
module Node
-
-
##
-
#
-
# A {Capybara::Node::Base} represents either an element on a page through the subclass
-
# {Capybara::Node::Element} or a document through {Capybara::Node::Document}.
-
#
-
# Both types of Node share the same methods, used for interacting with the
-
# elements on the page. These methods are divided into three categories,
-
# finders, actions and matchers. These are found in the modules
-
# {Capybara::Node::Finders}, {Capybara::Node::Actions} and {Capybara::Node::Matchers}
-
# respectively.
-
#
-
# A {Capybara::Session} exposes all methods from {Capybara::Node::Document} directly:
-
#
-
# session = Capybara::Session.new(:rack_test, my_app)
-
# session.visit('/')
-
# session.fill_in('Foo', :with => 'Bar') # from Capybara::Node::Actions
-
# bar = session.find('#bar') # from Capybara::Node::Finders
-
# bar.select('Baz', :from => 'Quox') # from Capybara::Node::Actions
-
# session.has_css?('#foobar') # from Capybara::Node::Matchers
-
#
-
1
class Base
-
1
attr_reader :session, :base, :parent
-
-
1
include Capybara::Node::Finders
-
1
include Capybara::Node::Actions
-
1
include Capybara::Node::Matchers
-
-
1
def initialize(session, base)
-
@session = session
-
@base = base
-
end
-
-
# overridden in subclasses, e.g. Capybara::Node::Element
-
1
def reload
-
self
-
end
-
-
##
-
#
-
# This method is Capybara's primary defence against asynchronicity
-
# problems. It works by attempting to run a given block of code until it
-
# succeeds. The exact behaviour of this method depends on a number of
-
# factors. Basically there are certain exceptions which, when raised
-
# from the block, instead of bubbling up, are caught, and the block is
-
# re-run.
-
#
-
# Certain drivers, such as RackTest, have no support for asynchronous
-
# processes, these drivers run the block, and any error raised bubbles up
-
# immediately. This allows faster turn around in the case where an
-
# expectation fails.
-
#
-
# Only exceptions that are {Capybara::ElementNotFound} or any subclass
-
# thereof cause the block to be rerun. Drivers may specify additional
-
# exceptions which also cause reruns. This usually occurs when a node is
-
# manipulated which no longer exists on the page. For example, the
-
# Selenium driver specifies
-
# `Selenium::WebDriver::Error::ObsoleteElementError`.
-
#
-
# As long as any of these exceptions are thrown, the block is re-run,
-
# until a certain amount of time passes. The amount of time defaults to
-
# {Capybara.default_wait_time} and can be overridden through the `seconds`
-
# argument. This time is compared with the system time to see how much
-
# time has passed. If the return value of `Time.now` is stubbed out,
-
# Capybara will raise `Capybara::FrozenInTime`.
-
#
-
# @param [Integer] seconds Number of seconds to retry this block
-
# @param options [Hash]
-
# @option options [Array<Exception>] :errors (driver.invalid_element_errors +
-
# [Capybara::ElementNotFound]) exception types that cause the block to be rerun
-
# @return [Object] The result of the given block
-
# @raise [Capybara::FrozenInTime] If the return value of `Time.now` appears stuck
-
#
-
1
def synchronize(seconds=Capybara.default_wait_time, options = {})
-
start_time = Time.now
-
-
if session.synchronized
-
yield
-
else
-
session.synchronized = true
-
begin
-
yield
-
rescue => e
-
session.raise_server_error!
-
raise e unless driver.wait?
-
raise e unless catch_error?(e, options[:errors])
-
raise e if (Time.now - start_time) >= seconds
-
sleep(0.05)
-
raise Capybara::FrozenInTime, "time appears to be frozen, Capybara does not work with libraries which freeze time, consider using time travelling instead" if Time.now == start_time
-
reload if Capybara.automatic_reload
-
retry
-
ensure
-
session.synchronized = false
-
end
-
end
-
end
-
-
# @api private
-
1
def find_css(css)
-
base.find_css(css)
-
end
-
-
# @api private
-
1
def find_xpath(xpath)
-
base.find_xpath(xpath)
-
end
-
-
1
protected
-
-
1
def catch_error?(error, errors = nil)
-
errors ||= (driver.invalid_element_errors + [Capybara::ElementNotFound])
-
errors.any? do |type|
-
error.is_a?(type)
-
end
-
end
-
-
1
def driver
-
session.driver
-
end
-
end
-
end
-
end
-
1
module Capybara
-
1
module Node
-
-
##
-
#
-
# A {Capybara::Document} represents an HTML document. Any operation
-
# performed on it will be performed on the entire document.
-
#
-
# @see Capybara::Node
-
#
-
1
class Document < Base
-
1
include Capybara::Node::DocumentMatchers
-
-
1
def inspect
-
%(#<Capybara::Document>)
-
end
-
-
##
-
#
-
# @return [String] The text of the document
-
#
-
1
def text(type=nil)
-
find(:xpath, '/html').text(type)
-
end
-
-
1
def title
-
session.driver.title
-
end
-
end
-
end
-
end
-
1
module Capybara
-
1
module Node
-
1
module DocumentMatchers
-
##
-
# Asserts that the page has the given title.
-
#
-
# @!macro title_query_params
-
# @overload $0(string, options = {})
-
# @param string [String] The string that title should include
-
# @overload $0(regexp, options = {})
-
# @param regexp [Regexp] The regexp that title should match to
-
# @option options [Numeric] :wait (Capybara.default_wait_time) Time that Capybara will wait for title to eq/match given string/regexp argument
-
# @raise [Capybara::ExpectationNotMet] if the assertion hasn't succeeded during wait time
-
# @return [true]
-
#
-
1
def assert_title(title, options = {})
-
query = Capybara::Queries::TitleQuery.new(title, options)
-
synchronize(query.wait) do
-
unless query.resolves_for?(self)
-
raise Capybara::ExpectationNotMet, query.failure_message
-
end
-
end
-
return true
-
end
-
-
##
-
# Asserts that the page doesn't have the given title.
-
#
-
# @macro title_query_params
-
# @raise [Capybara::ExpectationNotMet] if the assertion hasn't succeeded during wait time
-
# @return [true]
-
#
-
1
def assert_no_title(title, options = {})
-
query = Capybara::Queries::TitleQuery.new(title, options)
-
synchronize(query.wait) do
-
if query.resolves_for?(self)
-
raise Capybara::ExpectationNotMet, query.negative_failure_message
-
end
-
end
-
return true
-
end
-
-
##
-
# Checks if the page has the given title.
-
#
-
# @macro title_query_params
-
# @return [Boolean]
-
#
-
1
def has_title?(title, options = {})
-
assert_title(title, options)
-
rescue Capybara::ExpectationNotMet
-
return false
-
end
-
-
##
-
# Checks if the page doesn't have the given title.
-
#
-
# @macro title_query_params
-
# @return [Boolean]
-
#
-
1
def has_no_title?(title, options = {})
-
assert_no_title(title, options)
-
rescue Capybara::ExpectationNotMet
-
return false
-
end
-
end
-
end
-
end
-
1
module Capybara
-
1
module Node
-
-
##
-
#
-
# A {Capybara::Element} represents a single element on the page. It is possible
-
# to interact with the contents of this element the same as with a document:
-
#
-
# session = Capybara::Session.new(:rack_test, my_app)
-
#
-
# bar = session.find('#bar') # from Capybara::Node::Finders
-
# bar.select('Baz', :from => 'Quox') # from Capybara::Node::Actions
-
#
-
# {Capybara::Element} also has access to HTML attributes and other properties of the
-
# element:
-
#
-
# bar.value
-
# bar.text
-
# bar[:title]
-
#
-
# @see Capybara::Node
-
#
-
1
class Element < Base
-
-
1
def initialize(session, base, parent, query)
-
super(session, base)
-
@parent = parent
-
@query = query
-
end
-
-
1
def allow_reload!
-
@allow_reload = true
-
end
-
-
##
-
#
-
# @return [Object] The native element from the driver, this allows access to driver specific methods
-
#
-
1
def native
-
synchronize { base.native }
-
end
-
-
##
-
#
-
# Retrieve the text of the element. If `Capybara.ignore_hidden_elements`
-
# is `true`, which it is by default, then this will return only text
-
# which is visible. The exact semantics of this may differ between
-
# drivers, but generally any text within elements with `display:none` is
-
# ignored. This behaviour can be overridden by passing `:all` to this
-
# method.
-
#
-
# @param [:all, :visible] type Whether to return only visible or all text
-
# @return [String] The text of the element
-
#
-
1
def text(type=nil)
-
type ||= :all unless Capybara.ignore_hidden_elements or Capybara.visible_text_only
-
synchronize do
-
if type == :all
-
base.all_text
-
else
-
base.visible_text
-
end
-
end
-
end
-
-
##
-
#
-
# Retrieve the given attribute
-
#
-
# element[:title] # => HTML title attribute
-
#
-
# @param [Symbol] attribute The attribute to retrieve
-
# @return [String] The value of the attribute
-
#
-
1
def [](attribute)
-
synchronize { base[attribute] }
-
end
-
-
##
-
#
-
# @return [String] The value of the form element
-
#
-
1
def value
-
synchronize { base.value }
-
end
-
-
##
-
#
-
# Set the value of the form element to the given value.
-
#
-
# @param [String] value The new value
-
# @param [Hash{}] options Driver specific options for how to set the value
-
#
-
1
def set(value, options={})
-
options ||= {}
-
-
driver_supports_options = (base.method(:set).arity != 1)
-
-
unless options.empty? || driver_supports_options
-
warn "Options passed to Capybara::Node#set but the driver doesn't support them"
-
end
-
-
synchronize do
-
if driver_supports_options
-
base.set(value, options)
-
else
-
base.set(value)
-
end
-
end
-
end
-
-
##
-
#
-
# Select this node if is an option element inside a select tag
-
#
-
1
def select_option
-
synchronize { base.select_option }
-
end
-
-
##
-
#
-
# Unselect this node if is an option element inside a multiple select tag
-
#
-
1
def unselect_option
-
synchronize { base.unselect_option }
-
end
-
-
##
-
#
-
# Click the Element
-
#
-
1
def click
-
synchronize { base.click }
-
end
-
-
##
-
#
-
# Right Click the Element
-
#
-
1
def right_click
-
synchronize { base.right_click }
-
end
-
-
##
-
#
-
# Double Click the Element
-
#
-
1
def double_click
-
synchronize { base.double_click }
-
end
-
-
##
-
#
-
# Hover on the Element
-
#
-
1
def hover
-
synchronize { base.hover }
-
end
-
-
##
-
#
-
# @return [String] The tag name of the element
-
#
-
1
def tag_name
-
synchronize { base.tag_name }
-
end
-
-
##
-
#
-
# Whether or not the element is visible. Not all drivers support CSS, so
-
# the result may be inaccurate.
-
#
-
# @return [Boolean] Whether the element is visible
-
#
-
1
def visible?
-
synchronize { base.visible? }
-
end
-
-
##
-
#
-
# Whether or not the element is checked.
-
#
-
# @return [Boolean] Whether the element is checked
-
#
-
1
def checked?
-
synchronize { base.checked? }
-
end
-
-
##
-
#
-
# Whether or not the element is selected.
-
#
-
# @return [Boolean] Whether the element is selected
-
#
-
1
def selected?
-
synchronize { base.selected? }
-
end
-
-
##
-
#
-
# Whether or not the element is disabled.
-
#
-
# @return [Boolean] Whether the element is disabled
-
#
-
1
def disabled?
-
synchronize { base.disabled? }
-
end
-
-
##
-
#
-
# An XPath expression describing where on the page the element can be found
-
#
-
# @return [String] An XPath expression
-
#
-
1
def path
-
synchronize { base.path }
-
end
-
-
##
-
#
-
# Trigger any event on the current element, for example mouseover or focus
-
# events. Does not work in Selenium.
-
#
-
# @param [String] event The name of the event to trigger
-
#
-
1
def trigger(event)
-
synchronize { base.trigger(event) }
-
end
-
-
##
-
#
-
# Drag the element to the given other element.
-
#
-
# source = page.find('#foo')
-
# target = page.find('#bar')
-
# source.drag_to(target)
-
#
-
# @param [Capybara::Element] node The element to drag to
-
#
-
1
def drag_to(node)
-
synchronize { base.drag_to(node.base) }
-
end
-
-
1
def reload
-
if @allow_reload
-
begin
-
reloaded = parent.reload.first(@query.name, @query.locator, @query.options)
-
@base = reloaded.base if reloaded
-
rescue => e
-
raise e unless catch_error?(e)
-
end
-
end
-
self
-
end
-
-
1
def inspect
-
%(#<Capybara::Element tag="#{tag_name}" path="#{path}">)
-
rescue NotSupportedByDriverError
-
%(#<Capybara::Element tag="#{tag_name}">)
-
end
-
end
-
end
-
end
-
1
module Capybara
-
1
module Node
-
1
module Finders
-
-
##
-
#
-
# Find an {Capybara::Element} based on the given arguments. +find+ will raise an error if the element
-
# is not found.
-
#
-
# If the driver is capable of executing JavaScript, +find+ will wait for a set amount of time
-
# and continuously retry finding the element until either the element is found or the time
-
# expires. The length of time +find+ will wait is controlled through {Capybara.default_wait_time}
-
# and defaults to 2 seconds.
-
#
-
# +find+ takes the same options as +all+.
-
#
-
# page.find('#foo').find('.bar')
-
# page.find(:xpath, '//div[contains(., "bar")]')
-
# page.find('li', :text => 'Quox').click_link('Delete')
-
#
-
# @param (see Capybara::Node::Finders#all)
-
# @option options [Boolean] match The matching strategy to use.
-
# @option options [false, Numeric] wait How long to wait for the element to appear.
-
#
-
# @return [Capybara::Element] The found element
-
# @raise [Capybara::ElementNotFound] If the element can't be found before time expires
-
#
-
1
def find(*args)
-
query = Capybara::Query.new(*args)
-
synchronize(query.wait) do
-
if query.match == :smart or query.match == :prefer_exact
-
result = query.resolve_for(self, true)
-
result = query.resolve_for(self, false) if result.size == 0 && !query.exact?
-
else
-
result = query.resolve_for(self)
-
end
-
if query.match == :one or query.match == :smart and result.size > 1
-
raise Capybara::Ambiguous.new("Ambiguous match, found #{result.size} elements matching #{query.description}")
-
end
-
if result.size == 0
-
raise Capybara::ElementNotFound.new("Unable to find #{query.description}")
-
end
-
result.first
-
end.tap(&:allow_reload!)
-
end
-
-
##
-
#
-
# Find a form field on the page. The field can be found by its name, id or label text.
-
#
-
# @param [String] locator Which field to find
-
# @return [Capybara::Element] The found element
-
#
-
1
def find_field(locator, options={})
-
find(:field, locator, options)
-
end
-
1
alias_method :field_labeled, :find_field
-
-
##
-
#
-
# Find a link on the page. The link can be found by its id or text.
-
#
-
# @param [String] locator Which link to find
-
# @return [Capybara::Element] The found element
-
#
-
1
def find_link(locator, options={})
-
find(:link, locator, options)
-
end
-
-
##
-
#
-
# Find a button on the page. The button can be found by its id, name or value.
-
#
-
# @param [String] locator Which button to find
-
# @return [Capybara::Element] The found element
-
#
-
1
def find_button(locator, options={})
-
find(:button, locator, options)
-
end
-
-
##
-
#
-
# Find a element on the page, given its id.
-
#
-
# @param [String] id Which element to find
-
# @return [Capybara::Element] The found element
-
#
-
1
def find_by_id(id, options={})
-
find(:id, id, options)
-
end
-
-
##
-
#
-
# Find all elements on the page matching the given selector
-
# and options.
-
#
-
# Both XPath and CSS expressions are supported, but Capybara
-
# does not try to automatically distinguish between them. The
-
# following statements are equivalent:
-
#
-
# page.all(:css, 'a#person_123')
-
# page.all(:xpath, '//a[@id="person_123"]')
-
#
-
#
-
# If the type of selector is left out, Capybara uses
-
# {Capybara.default_selector}. It's set to :css by default.
-
#
-
# page.all("a#person_123")
-
#
-
# Capybara.default_selector = :xpath
-
# page.all('//a[@id="person_123"]')
-
#
-
# The set of found elements can further be restricted by specifying
-
# options. It's possible to select elements by their text or visibility:
-
#
-
# page.all('a', :text => 'Home')
-
# page.all('#menu li', :visible => true)
-
#
-
# By default if no elements are found, an empty array is returned;
-
# however, expectations can be set on the number of elements to be
-
# found using:
-
#
-
# page.assert_selector('p#foo', :count => 4)
-
# page.assert_selector('p#foo', :maximum => 10)
-
# page.assert_selector('p#foo', :minimum => 1)
-
# page.assert_selector('p#foo', :between => 1..10)
-
#
-
# See {Capybara::Helpers#matches_count?} for additional information about
-
# count matching.
-
#
-
# @overload all([kind], locator, options)
-
# @param [:css, :xpath] kind The type of selector
-
# @param [String] locator The selector
-
# @option options [String, Regexp] text Only find elements which contain this text or match this regexp
-
# @option options [Boolean] visible Only find elements that are visible on the page. Setting this to false
-
# finds invisible _and_ visible elements.
-
# @option options [Integer] count Exact number of matches that are expected to be found
-
# @option options [Integer] maximum Maximum number of matches that are expected to be found
-
# @option options [Integer] minimum Minimum number of matches that are expected to be found
-
# @option options [Range] between Number of matches found must be within the given range
-
# @option options [Boolean] exact Control whether `is` expressions in the given XPath match exactly or partially
-
# @return [Capybara::Result] A collection of found elements
-
#
-
1
def all(*args)
-
query = Capybara::Query.new(*args)
-
synchronize(query.wait) do
-
result = query.resolve_for(self)
-
raise Capybara::ExpectationNotMet, result.failure_message unless result.matches_count?
-
result
-
end
-
end
-
-
##
-
#
-
# Find the first element on the page matching the given selector
-
# and options, or nil if no element matches.
-
#
-
# @overload first([kind], locator, options)
-
# @param [:css, :xpath] kind The type of selector
-
# @param [String] locator The selector
-
# @param [Hash] options Additional options; see {#all}
-
# @return [Capybara::Element] The found element or nil
-
#
-
1
def first(*args)
-
all(*args).first
-
end
-
end
-
end
-
end
-
1
module Capybara
-
1
module Node
-
1
module Matchers
-
-
##
-
#
-
# Checks if a given selector is on the page or current node.
-
#
-
# page.has_selector?('p#foo')
-
# page.has_selector?(:xpath, './/p[@id="foo"]')
-
# page.has_selector?(:foo)
-
#
-
# By default it will check if the expression occurs at least once,
-
# but a different number can be specified.
-
#
-
# page.has_selector?('p.foo', :count => 4)
-
#
-
# This will check if the expression occurs exactly 4 times.
-
#
-
# It also accepts all options that {Capybara::Node::Finders#all} accepts,
-
# such as :text and :visible.
-
#
-
# page.has_selector?('li', :text => 'Horse', :visible => true)
-
#
-
# has_selector? can also accept XPath expressions generated by the
-
# XPath gem:
-
#
-
# page.has_selector?(:xpath, XPath.descendant(:p))
-
#
-
# @param (see Capybara::Node::Finders#all)
-
# @param args
-
# @option args [Integer] :count (nil) Number of times the text should occur
-
# @option args [Integer] :minimum (nil) Minimum number of times the text should occur
-
# @option args [Integer] :maximum (nil) Maximum number of times the text should occur
-
# @option args [Range] :between (nil) Range of times that should contain number of times text occurs
-
# @return [Boolean] If the expression exists
-
#
-
1
def has_selector?(*args)
-
assert_selector(*args)
-
rescue Capybara::ExpectationNotMet
-
return false
-
end
-
-
##
-
#
-
# Checks if a given selector is not on the page or current node.
-
# Usage is identical to Capybara::Node::Matchers#has_selector?
-
#
-
# @param (see Capybara::Node::Finders#has_selector?)
-
# @return [Boolean]
-
#
-
1
def has_no_selector?(*args)
-
assert_no_selector(*args)
-
rescue Capybara::ExpectationNotMet
-
return false
-
end
-
-
##
-
#
-
# Asserts that a given selector is on the page or current node.
-
#
-
# page.assert_selector('p#foo')
-
# page.assert_selector(:xpath, './/p[@id="foo"]')
-
# page.assert_selector(:foo)
-
#
-
# By default it will check if the expression occurs at least once,
-
# but a different number can be specified.
-
#
-
# page.assert_selector('p#foo', :count => 4)
-
#
-
# This will check if the expression occurs exactly 4 times. See
-
# {Capybara::Node::Finders#all} for other available result size options.
-
#
-
# If a :count of 0 is specified, it will behave like {#assert_no_selector};
-
# however, use of that method is preferred over this one.
-
#
-
# It also accepts all options that {Capybara::Node::Finders#all} accepts,
-
# such as :text and :visible.
-
#
-
# page.assert_selector('li', :text => 'Horse', :visible => true)
-
#
-
# `assert_selector` can also accept XPath expressions generated by the
-
# XPath gem:
-
#
-
# page.assert_selector(:xpath, XPath.descendant(:p))
-
#
-
# @param (see Capybara::Node::Finders#all)
-
# @option options [Integer] :count (nil) Number of times the expression should occur
-
# @raise [Capybara::ExpectationNotMet] If the selector does not exist
-
#
-
1
def assert_selector(*args)
-
query = Capybara::Query.new(*args)
-
synchronize(query.wait) do
-
result = query.resolve_for(self)
-
matches_count = Capybara::Helpers.matches_count?(result.size, query.options)
-
unless matches_count && ((result.size > 0) || Capybara::Helpers.expects_none?(query.options))
-
raise Capybara::ExpectationNotMet, result.failure_message
-
end
-
end
-
return true
-
end
-
-
##
-
#
-
# Asserts that a given selector is not on the page or current node.
-
# Usage is identical to Capybara::Node::Matchers#assert_selector
-
#
-
# Query options such as :count, :minimum, :maximum, and :between are
-
# considered to be an integral part of the selector. This will return
-
# true, for example, if a page contains 4 anchors but the query expects 5:
-
#
-
# page.assert_no_selector('a', :minimum => 1) # Found, raises Capybara::ExpectationNotMet
-
# page.assert_no_selector('a', :count => 4) # Found, raises Capybara::ExpectationNotMet
-
# page.assert_no_selector('a', :count => 5) # Not Found, returns true
-
#
-
# @param (see Capybara::Node::Finders#assert_selector)
-
# @raise [Capybara::ExpectationNotMet] If the selector exists
-
#
-
1
def assert_no_selector(*args)
-
query = Capybara::Query.new(*args)
-
synchronize(query.wait) do
-
result = query.resolve_for(self)
-
matches_count = Capybara::Helpers.matches_count?(result.size, query.options)
-
if matches_count && ((result.size > 0) || Capybara::Helpers.expects_none?(query.options))
-
raise Capybara::ExpectationNotMet, result.negative_failure_message
-
end
-
end
-
return true
-
end
-
1
alias_method :refute_selector, :assert_no_selector
-
-
##
-
#
-
# Checks if a given XPath expression is on the page or current node.
-
#
-
# page.has_xpath?('.//p[@id="foo"]')
-
#
-
# By default it will check if the expression occurs at least once,
-
# but a different number can be specified.
-
#
-
# page.has_xpath?('.//p[@id="foo"]', :count => 4)
-
#
-
# This will check if the expression occurs exactly 4 times.
-
#
-
# It also accepts all options that {Capybara::Node::Finders#all} accepts,
-
# such as :text and :visible.
-
#
-
# page.has_xpath?('.//li', :text => 'Horse', :visible => true)
-
#
-
# has_xpath? can also accept XPath expressions generate by the
-
# XPath gem:
-
#
-
# xpath = XPath.generate { |x| x.descendant(:p) }
-
# page.has_xpath?(xpath)
-
#
-
# @param [String] path An XPath expression
-
# @param options (see Capybara::Node::Finders#all)
-
# @option options [Integer] :count (nil) Number of times the expression should occur
-
# @return [Boolean] If the expression exists
-
#
-
1
def has_xpath?(path, options={})
-
has_selector?(:xpath, path, options)
-
end
-
-
##
-
#
-
# Checks if a given XPath expression is not on the page or current node.
-
# Usage is identical to Capybara::Node::Matchers#has_xpath?
-
#
-
# @param (see Capybara::Node::Finders#has_xpath?)
-
# @return [Boolean]
-
#
-
1
def has_no_xpath?(path, options={})
-
has_no_selector?(:xpath, path, options)
-
end
-
-
##
-
#
-
# Checks if a given CSS selector is on the page or current node.
-
#
-
# page.has_css?('p#foo')
-
#
-
# By default it will check if the selector occurs at least once,
-
# but a different number can be specified.
-
#
-
# page.has_css?('p#foo', :count => 4)
-
#
-
# This will check if the selector occurs exactly 4 times.
-
#
-
# It also accepts all options that {Capybara::Node::Finders#all} accepts,
-
# such as :text and :visible.
-
#
-
# page.has_css?('li', :text => 'Horse', :visible => true)
-
#
-
# @param [String] path A CSS selector
-
# @param options (see Capybara::Node::Finders#all)
-
# @option options [Integer] :count (nil) Number of times the selector should occur
-
# @return [Boolean] If the selector exists
-
#
-
1
def has_css?(path, options={})
-
has_selector?(:css, path, options)
-
end
-
-
##
-
#
-
# Checks if a given CSS selector is not on the page or current node.
-
# Usage is identical to Capybara::Node::Matchers#has_css?
-
#
-
# @param (see Capybara::Node::Finders#has_css?)
-
# @return [Boolean]
-
#
-
1
def has_no_css?(path, options={})
-
has_no_selector?(:css, path, options)
-
end
-
-
##
-
#
-
# Checks if the page or current node has a link with the given
-
# text or id.
-
#
-
# @param [String] locator The text or id of a link to check for
-
# @param options
-
# @option options [String] :href The value the href attribute must be
-
# @return [Boolean] Whether it exists
-
#
-
1
def has_link?(locator, options={})
-
has_selector?(:link, locator, options)
-
end
-
-
##
-
#
-
# Checks if the page or current node has no link with the given
-
# text or id.
-
#
-
# @param (see Capybara::Node::Finders#has_link?)
-
# @return [Boolean] Whether it doesn't exist
-
#
-
1
def has_no_link?(locator, options={})
-
has_no_selector?(:link, locator, options)
-
end
-
-
##
-
#
-
# Checks if the page or current node has a button with the given
-
# text, value or id.
-
#
-
# @param [String] locator The text, value or id of a button to check for
-
# @return [Boolean] Whether it exists
-
#
-
1
def has_button?(locator, options={})
-
has_selector?(:button, locator, options)
-
end
-
-
##
-
#
-
# Checks if the page or current node has no button with the given
-
# text, value or id.
-
#
-
# @param [String] locator The text, value or id of a button to check for
-
# @return [Boolean] Whether it doesn't exist
-
#
-
1
def has_no_button?(locator, options={})
-
has_no_selector?(:button, locator, options)
-
end
-
-
##
-
#
-
# Checks if the page or current node has a form field with the given
-
# label, name or id.
-
#
-
# For text fields and other textual fields, such as textareas and
-
# HTML5 email/url/etc. fields, it's possible to specify a :with
-
# option to specify the text the field should contain:
-
#
-
# page.has_field?('Name', :with => 'Jonas')
-
#
-
# It is also possible to filter by the field type attribute:
-
#
-
# page.has_field?('Email', :type => 'email')
-
#
-
# Note: 'textarea' and 'select' are valid type values, matching the associated tag names.
-
#
-
# @param [String] locator The label, name or id of a field to check for
-
# @option options [String] :with The text content of the field
-
# @option options [String] :type The type attribute of the field
-
# @return [Boolean] Whether it exists
-
#
-
1
def has_field?(locator, options={})
-
has_selector?(:field, locator, options)
-
end
-
-
##
-
#
-
# Checks if the page or current node has no form field with the given
-
# label, name or id. See {Capybara::Node::Matchers#has_field?}.
-
#
-
# @param [String] locator The label, name or id of a field to check for
-
# @option options [String] :with The text content of the field
-
# @option options [String] :type The type attribute of the field
-
# @return [Boolean] Whether it doesn't exist
-
#
-
1
def has_no_field?(locator, options={})
-
has_no_selector?(:field, locator, options)
-
end
-
-
##
-
#
-
# Checks if the page or current node has a radio button or
-
# checkbox with the given label, value or id, that is currently
-
# checked.
-
#
-
# @param [String] locator The label, name or id of a checked field
-
# @return [Boolean] Whether it exists
-
#
-
1
def has_checked_field?(locator, options={})
-
has_selector?(:field, locator, options.merge(:checked => true))
-
end
-
-
##
-
#
-
# Checks if the page or current node has no radio button or
-
# checkbox with the given label, value or id, that is currently
-
# checked.
-
#
-
# @param [String] locator The label, name or id of a checked field
-
# @return [Boolean] Whether it doesn't exist
-
#
-
1
def has_no_checked_field?(locator, options={})
-
has_no_selector?(:field, locator, options.merge(:checked => true))
-
end
-
-
##
-
#
-
# Checks if the page or current node has a radio button or
-
# checkbox with the given label, value or id, that is currently
-
# unchecked.
-
#
-
# @param [String] locator The label, name or id of an unchecked field
-
# @return [Boolean] Whether it exists
-
#
-
1
def has_unchecked_field?(locator, options={})
-
has_selector?(:field, locator, options.merge(:unchecked => true))
-
end
-
-
##
-
#
-
# Checks if the page or current node has no radio button or
-
# checkbox with the given label, value or id, that is currently
-
# unchecked.
-
#
-
# @param [String] locator The label, name or id of an unchecked field
-
# @return [Boolean] Whether it doesn't exist
-
#
-
1
def has_no_unchecked_field?(locator, options={})
-
has_no_selector?(:field, locator, options.merge(:unchecked => true))
-
end
-
-
##
-
#
-
# Checks if the page or current node has a select field with the
-
# given label, name or id.
-
#
-
# It can be specified which option should currently be selected:
-
#
-
# page.has_select?('Language', :selected => 'German')
-
#
-
# For multiple select boxes, several options may be specified:
-
#
-
# page.has_select?('Language', :selected => ['English', 'German'])
-
#
-
# It's also possible to check if the exact set of options exists for
-
# this select box:
-
#
-
# page.has_select?('Language', :options => ['English', 'German', 'Spanish'])
-
#
-
# You can also check for a partial set of options:
-
#
-
# page.has_select?('Language', :with_options => ['English', 'German'])
-
#
-
# @param [String] locator The label, name or id of a select box
-
# @option options [Array] :options Options which should be contained in this select box
-
# @option options [Array] :with_options Partial set of options which should be contained in this select box
-
# @option options [String, Array] :selected Options which should be selected
-
# @return [Boolean] Whether it exists
-
#
-
1
def has_select?(locator, options={})
-
has_selector?(:select, locator, options)
-
end
-
-
##
-
#
-
# Checks if the page or current node has no select field with the
-
# given label, name or id. See {Capybara::Node::Matchers#has_select?}.
-
#
-
# @param (see Capybara::Node::Matchers#has_select?)
-
# @return [Boolean] Whether it doesn't exist
-
#
-
1
def has_no_select?(locator, options={})
-
has_no_selector?(:select, locator, options)
-
end
-
-
##
-
#
-
# Checks if the page or current node has a table with the given id
-
# or caption:
-
#
-
# page.has_table?('People')
-
#
-
# @param [String] locator The id or caption of a table
-
# @return [Boolean] Whether it exist
-
#
-
1
def has_table?(locator, options={})
-
has_selector?(:table, locator, options)
-
end
-
-
##
-
#
-
# Checks if the page or current node has no table with the given id
-
# or caption. See {Capybara::Node::Matchers#has_table?}.
-
#
-
# @param (see Capybara::Node::Matchers#has_table?)
-
# @return [Boolean] Whether it doesn't exist
-
#
-
1
def has_no_table?(locator, options={})
-
has_no_selector?(:table, locator, options)
-
end
-
-
##
-
# Asserts that the page or current node has the given text content,
-
# ignoring any HTML tags.
-
#
-
# @!macro text_query_params
-
# @overload $0(type, text, options = {})
-
# @param [:all, :visible] type Whether to check for only visible or all text
-
# @param [String, Regexp] text The string/regexp to check for. If it's a string, text is expected to include it. If it's a regexp, text is expected to match it.
-
# @option options [Integer] :count (nil) Number of times the text is expected to occur
-
# @option options [Integer] :minimum (nil) Minimum number of times the text is expected to occur
-
# @option options [Integer] :maximum (nil) Maximum number of times the text is expected to occur
-
# @option options [Range] :between (nil) Range of times that is expected to contain number of times text occurs
-
# @option options [Numeric] :wait (Capybara.default_wait_time) Time that Capybara will wait for text to eq/match given string/regexp argument
-
# @overload $0(text, options = {})
-
# @param [String, Regexp] text The string/regexp to check for. If it's a string, text is expected to include it. If it's a regexp, text is expected to match it.
-
# @option options [Integer] :count (nil) Number of times the text is expected to occur
-
# @option options [Integer] :minimum (nil) Minimum number of times the text is expected to occur
-
# @option options [Integer] :maximum (nil) Maximum number of times the text is expected to occur
-
# @option options [Range] :between (nil) Range of times that is expected to contain number of times text occurs
-
# @option options [Numeric] :wait (Capybara.default_wait_time) Time that Capybara will wait for text to eq/match given string/regexp argument
-
# @raise [Capybara::ExpectationNotMet] if the assertion hasn't succeeded during wait time
-
# @return [true]
-
#
-
1
def assert_text(*args)
-
query = Capybara::Queries::TextQuery.new(*args)
-
synchronize(query.wait) do
-
count = query.resolve_for(self)
-
matches_count = Capybara::Helpers.matches_count?(count, query.options)
-
unless matches_count && ((count > 0) || Capybara::Helpers.expects_none?(query.options))
-
raise Capybara::ExpectationNotMet, query.failure_message
-
end
-
end
-
return true
-
end
-
-
##
-
# Asserts that the page or current node doesn't have the given text content,
-
# ignoring any HTML tags.
-
#
-
# @macro text_query_params
-
# @raise [Capybara::ExpectationNotMet] if the assertion hasn't succeeded during wait time
-
# @return [true]
-
#
-
1
def assert_no_text(*args)
-
query = Capybara::Queries::TextQuery.new(*args)
-
synchronize(query.wait) do
-
count = query.resolve_for(self)
-
matches_count = Capybara::Helpers.matches_count?(count, query.options)
-
if matches_count && ((count > 0) || Capybara::Helpers.expects_none?(query.options))
-
raise Capybara::ExpectationNotMet, query.negative_failure_message
-
end
-
end
-
return true
-
end
-
-
##
-
# Checks if the page or current node has the given text content,
-
# ignoring any HTML tags.
-
#
-
# Whitespaces are normalized in both node's text and passed text parameter.
-
# Note that whitespace isn't normalized in passed regexp as normalizing whitespace
-
# in regexp isn't easy and doesn't seem to be worth it.
-
#
-
# By default it will check if the text occurs at least once,
-
# but a different number can be specified.
-
#
-
# page.has_text?('lorem ipsum', between: 2..4)
-
#
-
# This will check if the text occurs from 2 to 4 times.
-
#
-
# @macro text_query_params
-
# @return [Boolean] Whether it exists
-
#
-
1
def has_text?(*args)
-
assert_text(*args)
-
rescue Capybara::ExpectationNotMet
-
return false
-
end
-
1
alias_method :has_content?, :has_text?
-
-
##
-
# Checks if the page or current node does not have the given text
-
# content, ignoring any HTML tags and normalizing whitespace.
-
#
-
# @macro text_query_params
-
# @return [Boolean] Whether it doesn't exist
-
#
-
1
def has_no_text?(*args)
-
assert_no_text(*args)
-
rescue Capybara::ExpectationNotMet
-
return false
-
end
-
1
alias_method :has_no_content?, :has_no_text?
-
-
1
def ==(other)
-
self.eql?(other) || (other.respond_to?(:base) && base == other.base)
-
end
-
end
-
end
-
end
-
1
module Capybara
-
# @api private
-
1
module Queries
-
1
class BaseQuery
-
1
COUNT_KEYS = [:count, :minimum, :maximum, :between]
-
-
1
attr_reader :options
-
-
1
def wait
-
if @options.has_key?(:wait)
-
@options[:wait] || 0
-
else
-
Capybara.default_wait_time
-
end
-
end
-
-
1
private
-
-
1
def assert_valid_keys
-
invalid_keys = @options.keys - valid_keys
-
unless invalid_keys.empty?
-
invalid_names = invalid_keys.map(&:inspect).join(", ")
-
valid_names = valid_keys.map(&:inspect).join(", ")
-
raise ArgumentError, "invalid keys #{invalid_names}, should be one of #{valid_names}"
-
end
-
end
-
end
-
end
-
end
-
1
module Capybara
-
# @api private
-
1
module Queries
-
1
class TextQuery < BaseQuery
-
1
def initialize(*args)
-
@type = args.shift if args.first.is_a?(Symbol) || args.first.nil?
-
@expected_text, @options = args
-
unless @expected_text.is_a?(Regexp)
-
@expected_text = Capybara::Helpers.normalize_whitespace(@expected_text)
-
end
-
@search_regexp = Capybara::Helpers.to_regexp(@expected_text)
-
@options ||= {}
-
assert_valid_keys
-
-
# this is needed to not break existing tests that may use keys supported by `Query` but not supported by `TextQuery`
-
# can be removed in next minor version (> 2.4)
-
invalid_keys = @options.keys - (COUNT_KEYS + [:wait])
-
unless invalid_keys.empty?
-
invalid_names = invalid_keys.map(&:inspect).join(", ")
-
valid_names = valid_keys.map(&:inspect).join(", ")
-
warn "invalid keys #{invalid_names}, should be one of #{valid_names}"
-
end
-
end
-
-
1
def resolve_for(node)
-
@actual_text = Capybara::Helpers.normalize_whitespace(node.text(@type))
-
@count = @actual_text.scan(@search_regexp).size
-
end
-
-
1
def failure_message
-
description =
-
if @expected_text.is_a?(Regexp)
-
"text matching #{@expected_text.inspect}"
-
else
-
"text #{@expected_text.inspect}"
-
end
-
-
message = Capybara::Helpers.failure_message(description, @options)
-
unless (COUNT_KEYS & @options.keys).empty?
-
message << " but found #{@count} #{Capybara::Helpers.declension('time', 'times', @count)}"
-
end
-
message << " in #{@actual_text.inspect}"
-
end
-
-
1
def negative_failure_message
-
failure_message.sub(/(to find)/, 'not \1')
-
end
-
-
1
private
-
-
1
def valid_keys
-
Capybara::Query::VALID_KEYS # can be changed to COUNT_KEYS + [:wait] in next minor version (> 2.4)
-
end
-
end
-
end
-
end
-
1
module Capybara
-
# @api private
-
1
module Queries
-
1
class TitleQuery < BaseQuery
-
1
def initialize(expected_title, options = {})
-
@expected_title = expected_title
-
@options = options
-
unless @expected_title.is_a?(Regexp)
-
@expected_title = Capybara::Helpers.normalize_whitespace(@expected_title)
-
end
-
@search_regexp = Capybara::Helpers.to_regexp(@expected_title)
-
assert_valid_keys
-
end
-
-
1
def resolves_for?(node)
-
@actual_title = node.title
-
@actual_title.match(@search_regexp)
-
end
-
-
1
def failure_message
-
failure_message_helper
-
end
-
-
1
def negative_failure_message
-
failure_message_helper(' not')
-
end
-
-
1
private
-
-
1
def failure_message_helper(negated = '')
-
verb = (@expected_title.is_a?(Regexp))? 'match' : 'include'
-
"expected #{@actual_title.inspect}#{negated} to #{verb} #{@expected_title.inspect}"
-
end
-
-
1
def valid_keys
-
[:wait]
-
end
-
end
-
end
-
end
-
1
module Capybara
-
# @deprecated This class and its methods are not supposed to be used by users of Capybara's public API.
-
# It may be removed in future versions of Capybara.
-
1
class Query < Queries::BaseQuery
-
1
attr_accessor :selector, :locator, :options, :expression, :find, :negative
-
-
1
VALID_KEYS = [:text, :visible, :between, :count, :maximum, :minimum, :exact, :match, :wait]
-
1
VALID_MATCH = [:first, :smart, :prefer_exact, :one]
-
-
1
def initialize(*args)
-
@options = if args.last.is_a?(Hash) then args.pop.dup else {} end
-
-
if args[0].is_a?(Symbol)
-
@selector = Selector.all[args[0]]
-
@locator = args[1]
-
else
-
@selector = Selector.all.values.find { |s| s.match?(args[0]) }
-
@locator = args[0]
-
end
-
@selector ||= Selector.all[Capybara.default_selector]
-
-
# for compatibility with Capybara 2.0
-
if Capybara.exact_options and @selector == Selector.all[:option]
-
@options[:exact] = true
-
end
-
-
@expression = @selector.call(@locator)
-
assert_valid_keys
-
end
-
-
1
def name; selector.name; end
-
1
def label; selector.label or selector.name; end
-
-
1
def description
-
@description = "#{label} #{locator.inspect}"
-
@description << " with text #{options[:text].inspect}" if options[:text]
-
@description << selector.description(options)
-
@description
-
end
-
-
1
def matches_filters?(node)
-
if options[:text]
-
regexp = options[:text].is_a?(Regexp) ? options[:text] : Regexp.escape(options[:text].to_s)
-
return false if not node.text(visible).match(regexp)
-
end
-
case visible
-
when :visible then return false unless node.visible?
-
when :hidden then return false if node.visible?
-
end
-
selector.custom_filters.each do |name, filter|
-
if options.has_key?(name)
-
return false unless filter.matches?(node, options[name])
-
elsif filter.default?
-
return false unless filter.matches?(node, filter.default)
-
end
-
end
-
end
-
-
1
def visible
-
if options.has_key?(:visible)
-
case @options[:visible]
-
when true then :visible
-
when false then :all
-
else @options[:visible]
-
end
-
else
-
if Capybara.ignore_hidden_elements
-
:visible
-
else
-
:all
-
end
-
end
-
end
-
-
1
def exact?
-
if options.has_key?(:exact)
-
@options[:exact]
-
else
-
Capybara.exact
-
end
-
end
-
-
1
def match
-
if options.has_key?(:match)
-
@options[:match]
-
else
-
Capybara.match
-
end
-
end
-
-
1
def xpath(exact=nil)
-
exact = self.exact? if exact == nil
-
if @expression.respond_to?(:to_xpath) and exact
-
@expression.to_xpath(:exact)
-
else
-
@expression.to_s
-
end
-
end
-
-
1
def css
-
@expression
-
end
-
-
# @api private
-
1
def resolve_for(node, exact = nil)
-
node.synchronize do
-
children = if selector.format == :css
-
node.find_css(self.css)
-
else
-
node.find_xpath(self.xpath(exact))
-
end.map do |child|
-
if node.is_a?(Capybara::Node::Base)
-
Capybara::Node::Element.new(node.session, child, node, self)
-
else
-
Capybara::Node::Simple.new(child)
-
end
-
end
-
Capybara::Result.new(children, self)
-
end
-
end
-
-
1
private
-
-
1
def valid_keys
-
COUNT_KEYS + [:text, :visible, :exact, :match, :wait] + @selector.custom_filters.keys
-
end
-
-
1
def assert_valid_keys
-
super
-
unless VALID_MATCH.include?(match)
-
raise ArgumentError, "invalid option #{match.inspect} for :match, should be one of #{VALID_MATCH.map(&:inspect).join(", ")}"
-
end
-
end
-
end
-
end
-
1
class Capybara::RackTest::Browser
-
1
include ::Rack::Test::Methods
-
-
1
attr_reader :driver
-
1
attr_accessor :current_host
-
-
1
def initialize(driver)
-
@driver = driver
-
end
-
-
1
def app
-
driver.app
-
end
-
-
1
def options
-
driver.options
-
end
-
-
1
def visit(path, attributes = {})
-
reset_host!
-
process_and_follow_redirects(:get, path, attributes)
-
end
-
-
1
def submit(method, path, attributes)
-
path = request_path if not path or path.empty?
-
process_and_follow_redirects(method, path, attributes, {'HTTP_REFERER' => current_url})
-
end
-
-
1
def follow(method, path, attributes = {})
-
return if path.gsub(/^#{request_path}/, '').start_with?('#')
-
process_and_follow_redirects(method, path, attributes, {'HTTP_REFERER' => current_url})
-
end
-
-
1
def process_and_follow_redirects(method, path, attributes = {}, env = {})
-
process(method, path, attributes, env)
-
if driver.follow_redirects?
-
driver.redirect_limit.times do
-
process(:get, last_response["Location"], {}, env) if last_response.redirect?
-
end
-
raise Capybara::InfiniteRedirectError, "redirected more than #{driver.redirect_limit} times, check for infinite redirects." if last_response.redirect?
-
end
-
end
-
-
1
def process(method, path, attributes = {}, env = {})
-
new_uri = URI.parse(path)
-
method.downcase! unless method.is_a? Symbol
-
-
new_uri.path = request_path if path.start_with?("?")
-
new_uri.path = "/" if new_uri.path.empty?
-
new_uri.path = request_path.sub(%r(/[^/]*$), '/') + new_uri.path unless new_uri.path.start_with?('/')
-
new_uri.scheme ||= @current_scheme
-
new_uri.host ||= @current_host
-
new_uri.port ||= @current_port unless new_uri.default_port == @current_port
-
-
@current_scheme = new_uri.scheme
-
@current_host = new_uri.host
-
@current_port = new_uri.port
-
-
reset_cache!
-
send(method, new_uri.to_s, attributes, env.merge(options[:headers] || {}))
-
end
-
-
1
def current_url
-
last_request.url
-
rescue Rack::Test::Error
-
""
-
end
-
-
1
def reset_host!
-
uri = URI.parse(Capybara.app_host || Capybara.default_host)
-
@current_scheme = uri.scheme
-
@current_host = uri.host
-
@current_port = uri.port
-
end
-
-
1
def reset_cache!
-
@dom = nil
-
end
-
-
1
def dom
-
@dom ||= Capybara::HTML(html)
-
end
-
-
1
def find(format, selector)
-
if format==:css
-
dom.css(selector, Capybara::RackTest::CSSHandlers.new)
-
else
-
dom.xpath(selector)
-
end.map { |node| Capybara::RackTest::Node.new(self, node) }
-
end
-
-
1
def html
-
last_response.body
-
rescue Rack::Test::Error
-
""
-
end
-
-
1
def title
-
dom.xpath("//title").text
-
end
-
-
1
protected
-
-
1
def build_rack_mock_session
-
reset_host! unless current_host
-
Rack::MockSession.new(app, current_host)
-
end
-
-
1
def request_path
-
last_request.path
-
rescue Rack::Test::Error
-
"/"
-
end
-
end
-
1
class Capybara::RackTest::CSSHandlers < BasicObject
-
1
include ::Kernel
-
-
1
def disabled list
-
list.find_all { |node| node.has_attribute? 'disabled' }
-
end
-
1
def enabled list
-
list.find_all { |node| !node.has_attribute? 'disabled' }
-
end
-
end
-
1
require 'rack/test'
-
1
require 'rack/utils'
-
1
require 'mime/types'
-
1
require 'nokogiri'
-
1
require 'cgi'
-
-
1
class Capybara::RackTest::Driver < Capybara::Driver::Base
-
1
DEFAULT_OPTIONS = {
-
:respect_data_method => false,
-
:follow_redirects => true,
-
:redirect_limit => 5
-
}
-
1
attr_reader :app, :options
-
-
1
def initialize(app, options={})
-
raise ArgumentError, "rack-test requires a rack application, but none was given" unless app
-
@app = app
-
@options = DEFAULT_OPTIONS.merge(options)
-
end
-
-
1
def browser
-
@browser ||= Capybara::RackTest::Browser.new(self)
-
end
-
-
1
def follow_redirects?
-
@options[:follow_redirects]
-
end
-
-
1
def redirect_limit
-
@options[:redirect_limit]
-
end
-
-
1
def response
-
browser.last_response
-
end
-
-
1
def request
-
browser.last_request
-
end
-
-
1
def visit(path, attributes = {})
-
browser.visit(path, attributes)
-
end
-
-
1
def submit(method, path, attributes)
-
browser.submit(method, path, attributes)
-
end
-
-
1
def follow(method, path, attributes = {})
-
browser.follow(method, path, attributes)
-
end
-
-
1
def current_url
-
browser.current_url
-
end
-
-
1
def response_headers
-
response.headers
-
end
-
-
1
def status_code
-
response.status
-
end
-
-
1
def find_xpath(selector)
-
browser.find(:xpath, selector)
-
end
-
-
1
def find_css(selector)
-
browser.find(:css,selector)
-
end
-
-
1
def html
-
browser.html
-
end
-
-
1
def dom
-
browser.dom
-
end
-
-
1
def title
-
browser.title
-
end
-
-
1
def reset!
-
@browser = nil
-
end
-
-
1
def get(*args, &block); browser.get(*args, &block); end
-
1
def post(*args, &block); browser.post(*args, &block); end
-
1
def put(*args, &block); browser.put(*args, &block); end
-
1
def delete(*args, &block); browser.delete(*args, &block); end
-
1
def header(key, value); browser.header(key, value); end
-
end
-
1
class Capybara::RackTest::Form < Capybara::RackTest::Node
-
# This only needs to inherit from Rack::Test::UploadedFile because Rack::Test checks for
-
# the class specifically when determining whether to construct the request as multipart.
-
# That check should be based solely on the form element's 'enctype' attribute value,
-
# which should probably be provided to Rack::Test in its non-GET request methods.
-
1
class NilUploadedFile < Rack::Test::UploadedFile
-
1
def initialize
-
@empty_file = Tempfile.new("nil_uploaded_file")
-
@empty_file.close
-
end
-
-
1
def original_filename; ""; end
-
1
def content_type; "application/octet-stream"; end
-
1
def path; @empty_file.path; end
-
end
-
-
1
def params(button)
-
params = {}
-
-
form_element_types=[:input, :select, :textarea]
-
form_elements_xpath=XPath.generate do |x|
-
xpath=x.descendant(*form_element_types).where(~x.attr(:form))
-
xpath=xpath.union(x.anywhere(*form_element_types).where(x.attr(:form) == native[:id])) if native[:id]
-
xpath.where(~x.attr(:disabled))
-
end.to_s
-
-
native.xpath(form_elements_xpath).map do |field|
-
case field.name
-
when 'input'
-
if %w(radio checkbox).include? field['type']
-
if field['checked']
-
node=Capybara::RackTest::Node.new(self.driver, field)
-
merge_param!(params, field['name'].to_s, node.value.to_s)
-
end
-
elsif %w(submit image).include? field['type']
-
# TO DO identify the click button here (in document order, rather
-
# than leaving until the end of the params)
-
elsif field['type'] =='file'
-
if multipart?
-
file = \
-
if (value = field['value']).to_s.empty?
-
NilUploadedFile.new
-
else
-
content_type = MIME::Types.type_for(value).first.to_s
-
Rack::Test::UploadedFile.new(value, content_type)
-
end
-
merge_param!(params, field['name'].to_s, file)
-
else
-
merge_param!(params, field['name'].to_s, File.basename(field['value'].to_s))
-
end
-
else
-
merge_param!(params, field['name'].to_s, field['value'].to_s)
-
end
-
when 'select'
-
if field['multiple'] == 'multiple'
-
options = field.xpath(".//option[@selected]")
-
options.each do |option|
-
merge_param!(params, field['name'].to_s, (option['value'] || option.text).to_s)
-
end
-
else
-
option = field.xpath(".//option[@selected]").first
-
option ||= field.xpath('.//option').first
-
merge_param!(params, field['name'].to_s, (option['value'] || option.text).to_s) if option
-
end
-
when 'textarea'
-
merge_param!(params, field['name'].to_s, field.text.to_s.gsub(/\n/, "\r\n"))
-
end
-
end
-
merge_param!(params, button[:name], button[:value] || "") if button[:name]
-
params
-
end
-
-
1
def submit(button)
-
action = (button && button['formaction']) || native['action']
-
driver.submit(method, action.to_s, params(button))
-
end
-
-
1
def multipart?
-
self[:enctype] == "multipart/form-data"
-
end
-
-
1
private
-
-
1
def method
-
self[:method] =~ /post/i ? :post : :get
-
end
-
-
1
def merge_param!(params, key, value)
-
Rack::Utils.normalize_params(params, key, value)
-
end
-
end
-
1
require 'capybara'
-
1
require 'capybara/dsl'
-
-
1
Capybara.app = Rack::Builder.new do
-
# Work around an issue where rails allows concurrency in test mode even though eager_load
-
# is false which can cause an issue with constant loading
-
1
if Gem::Version.new(Rails.version) >= Gem::Version.new("4.0")
-
1
use Rack::Lock unless Rails.application.config.allow_concurrency || Rails.application.config.eager_load || Rails.application.middleware.include?(Rack::Lock)
-
end
-
-
1
map "/" do
-
1
if Gem::Version.new(Rails.version) >= Gem::Version.new("3.0")
-
1
run Rails.application
-
else # Rails 2
-
use Rails::Rack::Static
-
run ActionController::Dispatcher.new
-
end
-
end
-
end.to_app
-
-
1
Capybara.save_and_open_page_path = Rails.root.join('tmp/capybara')
-
-
# Override default rack_test driver to respect data-method attributes.
-
1
Capybara.register_driver :rack_test do |app|
-
Capybara::RackTest::Driver.new(app, :respect_data_method => true)
-
end
-
1
require 'forwardable'
-
-
1
module Capybara
-
-
##
-
# A {Capybara::Result} represents a collection of {Capybara::Element} on the page. It is possible to interact with this
-
# collection similar to an Array because it implements Enumerable and offers the following Array methods through delegation:
-
#
-
# * []
-
# * each()
-
# * at()
-
# * size()
-
# * count()
-
# * length()
-
# * first()
-
# * last()
-
# * empty?()
-
#
-
# @see Capybara::Element
-
#
-
1
class Result
-
1
include Enumerable
-
1
extend Forwardable
-
-
1
def initialize(elements, query)
-
@elements = elements
-
@result = elements.select { |node| query.matches_filters?(node) }
-
@rest = @elements - @result
-
@query = query
-
end
-
-
1
def_delegators :@result, :each, :[], :at, :size, :count, :length,
-
:first, :last, :values_at, :empty?, :inspect, :sample, :index
-
-
1
def matches_count?
-
Capybara::Helpers.matches_count?(@result.size, @query.options)
-
end
-
-
1
def failure_message
-
message = Capybara::Helpers.failure_message(@query.description, @query.options)
-
if count > 0
-
message << ", found #{count} #{Capybara::Helpers.declension("match", "matches", count)}: " << @result.map(&:text).map(&:inspect).join(", ")
-
else
-
message << " but there were no matches"
-
end
-
unless @rest.empty?
-
elements = @rest.map(&:text).map(&:inspect).join(", ")
-
message << ". Also found " << elements << ", which matched the selector but not all filters."
-
end
-
message
-
end
-
-
1
def negative_failure_message
-
failure_message.sub(/(to find)/, 'not \1')
-
end
-
end
-
end
-
1
require 'capybara'
-
1
require 'capybara/dsl'
-
1
require 'rspec/core'
-
1
require 'capybara/rspec/matchers'
-
1
require 'capybara/rspec/features'
-
-
1
RSpec.configure do |config|
-
1
config.include Capybara::DSL, :type => :feature
-
1
config.include Capybara::RSpecMatchers, :type => :feature
-
-
# A work-around to support accessing the current example that works in both
-
# RSpec 2 and RSpec 3.
-
1
fetch_current_example = RSpec.respond_to?(:current_example) ?
-
proc { RSpec.current_example } : proc { |context| context.example }
-
-
# The before and after blocks must run instantaneously, because Capybara
-
# might not actually be used in all examples where it's included.
-
1
config.after do
-
4
if self.class.include?(Capybara::DSL)
-
Capybara.reset_sessions!
-
Capybara.use_default_driver
-
end
-
end
-
1
config.before do
-
4
if self.class.include?(Capybara::DSL)
-
example = fetch_current_example.call(self)
-
Capybara.current_driver = Capybara.javascript_driver if example.metadata[:js]
-
Capybara.current_driver = example.metadata[:driver] if example.metadata[:driver]
-
end
-
end
-
end
-
-
1
if RSpec::Core::Version::STRING.to_f >= 3.0
-
1
RSpec.shared_context "Capybara Features", :capybara_feature => true do
-
instance_eval do
-
alias background before
-
alias given let
-
alias given! let!
-
end
-
end
-
-
1
RSpec.configure do |config|
-
1
config.alias_example_group_to :feature, :capybara_feature => true, :type => :feature
-
1
config.alias_example_to :scenario
-
1
config.alias_example_to :xscenario, :skip => "Temporarily disabled with xscenario"
-
# config.alias_example_to :fscenario, :focus => true
-
end
-
else
-
module Capybara
-
module Features
-
def self.included(base)
-
base.instance_eval do
-
alias :background :before
-
alias :scenario :it
-
alias :xscenario :xit
-
alias :given :let
-
alias :given! :let!
-
alias :feature :describe
-
end
-
end
-
end
-
end
-
-
-
def self.feature(*args, &block)
-
options = if args.last.is_a?(Hash) then args.pop else {} end
-
options[:capybara_feature] = true
-
options[:type] = :feature
-
options[:caller] ||= caller
-
args.push(options)
-
-
#call describe on RSpec in case user has expose_dsl_globally set to false
-
RSpec.describe(*args, &block)
-
end
-
-
RSpec.configuration.include Capybara::Features, :capybara_feature => true
-
end
-
1
module Capybara
-
1
module RSpecMatchers
-
1
class Matcher
-
1
include ::RSpec::Matchers::Composable if defined?(::RSpec::Expectations::Version) && RSpec::Expectations::Version::STRING.to_f >= 3.0
-
-
1
def wrap(actual)
-
if actual.respond_to?("has_selector?")
-
actual
-
else
-
Capybara.string(actual.to_s)
-
end
-
end
-
end
-
-
1
class HaveSelector < Matcher
-
1
attr_reader :failure_message, :failure_message_when_negated
-
-
1
def initialize(*args)
-
@args = args
-
end
-
-
1
def matches?(actual)
-
wrap(actual).assert_selector(*@args)
-
rescue Capybara::ExpectationNotMet => e
-
@failure_message = e.message
-
return false
-
end
-
-
1
def does_not_match?(actual)
-
wrap(actual).assert_no_selector(*@args)
-
rescue Capybara::ExpectationNotMet => e
-
@failure_message_when_negated = e.message
-
return false
-
end
-
-
1
def description
-
"have #{query.description}"
-
end
-
-
1
def query
-
@query ||= Capybara::Query.new(*@args)
-
end
-
-
# RSpec 2 compatibility:
-
1
alias_method :failure_message_for_should, :failure_message
-
1
alias_method :failure_message_for_should_not, :failure_message_when_negated
-
end
-
-
1
class HaveText < Matcher
-
1
attr_reader :type, :content, :options
-
-
1
attr_reader :failure_message, :failure_message_when_negated
-
-
1
def initialize(*args)
-
@args = args.dup
-
-
# are set just for backwards compatability
-
@type = args.shift if args.first.is_a?(Symbol)
-
@content = args.shift
-
@options = (args.first.is_a?(Hash))? args.first : {}
-
end
-
-
1
def matches?(actual)
-
wrap(actual).assert_text(*@args)
-
rescue Capybara::ExpectationNotMet => e
-
@failure_message = e.message
-
return false
-
end
-
-
1
def does_not_match?(actual)
-
wrap(actual).assert_no_text(*@args)
-
rescue Capybara::ExpectationNotMet => e
-
@failure_message_when_negated = e.message
-
return false
-
end
-
-
1
def description
-
"text #{format(content)}"
-
end
-
-
1
def format(content)
-
content = Capybara::Helpers.normalize_whitespace(content) unless content.is_a? Regexp
-
content.inspect
-
end
-
-
# RSpec 2 compatibility:
-
1
alias_method :failure_message_for_should, :failure_message
-
1
alias_method :failure_message_for_should_not, :failure_message_when_negated
-
end
-
-
1
class HaveTitle < Matcher
-
1
attr_reader :title
-
-
1
attr_reader :failure_message, :failure_message_when_negated
-
-
1
def initialize(*args)
-
@args = args
-
-
# are set just for backwards compatability
-
@title = args.first
-
end
-
-
1
def matches?(actual)
-
wrap(actual).assert_title(*@args)
-
rescue Capybara::ExpectationNotMet => e
-
@failure_message = e.message
-
return false
-
end
-
-
1
def does_not_match?(actual)
-
wrap(actual).assert_no_title(*@args)
-
rescue Capybara::ExpectationNotMet => e
-
@failure_message_when_negated = e.message
-
return false
-
end
-
-
1
def description
-
"have title #{title.inspect}"
-
end
-
-
# RSpec 2 compatibility:
-
1
alias_method :failure_message_for_should, :failure_message
-
1
alias_method :failure_message_for_should_not, :failure_message_when_negated
-
end
-
-
1
class BecomeClosed
-
1
def initialize(options)
-
@wait_time = Capybara::Query.new(options).wait
-
end
-
-
1
def matches?(window)
-
@window = window
-
start_time = Time.now
-
while window.exists?
-
return false if (Time.now - start_time) > @wait_time
-
sleep 0.05
-
end
-
true
-
end
-
-
1
def failure_message
-
"expected #{@window.inspect} to become closed after #{@wait_time} seconds"
-
end
-
-
1
def failure_message_when_negated
-
"expected #{@window.inspect} not to become closed after #{@wait_time} seconds"
-
end
-
-
# RSpec 2 compatibility:
-
1
alias_method :failure_message_for_should, :failure_message
-
1
alias_method :failure_message_for_should_not, :failure_message_when_negated
-
end
-
-
1
def have_selector(*args)
-
HaveSelector.new(*args)
-
end
-
-
1
def have_xpath(xpath, options={})
-
HaveSelector.new(:xpath, xpath, options)
-
end
-
-
1
def have_css(css, options={})
-
HaveSelector.new(:css, css, options)
-
end
-
-
1
def have_text(*args)
-
HaveText.new(*args)
-
end
-
1
alias_method :have_content, :have_text
-
-
1
def have_title(title, options = {})
-
HaveTitle.new(title, options)
-
end
-
-
1
def have_link(locator, options={})
-
HaveSelector.new(:link, locator, options)
-
end
-
-
1
def have_button(locator, options={})
-
HaveSelector.new(:button, locator, options)
-
end
-
-
1
def have_field(locator, options={})
-
HaveSelector.new(:field, locator, options)
-
end
-
-
1
def have_checked_field(locator, options={})
-
HaveSelector.new(:field, locator, options.merge(:checked => true))
-
end
-
-
1
def have_unchecked_field(locator, options={})
-
HaveSelector.new(:field, locator, options.merge(:unchecked => true))
-
end
-
-
1
def have_select(locator, options={})
-
HaveSelector.new(:select, locator, options)
-
end
-
-
1
def have_table(locator, options={})
-
HaveSelector.new(:table, locator, options)
-
end
-
-
##
-
# Wait for window to become closed.
-
# @example
-
# expect(window).to become_closed(wait: 0.8)
-
# @param options [Hash] optional param
-
# @option options [Numeric] :wait (Capybara.default_wait_time) wait time
-
1
def become_closed(options = {})
-
BecomeClosed.new(options)
-
end
-
end
-
end
-
1
module Capybara
-
1
class Selector
-
1
class Filter
-
1
def initialize(name, block, options={})
-
22
@name = name
-
22
@block = block
-
22
@options = options
-
22
@options[:valid_values] = [true,false] if options[:boolean]
-
end
-
-
1
def default?
-
@options.has_key?(:default)
-
end
-
-
1
def default
-
@options[:default]
-
end
-
-
1
def matches?(node, value)
-
if @options.has_key?(:valid_values) && !Array(@options[:valid_values]).include?(value)
-
warn "Invalid value #{value.inspect} passed to filter #{@name}"
-
end
-
@block.call(node, value)
-
end
-
end
-
-
1
attr_reader :name, :custom_filters, :format
-
-
1
class << self
-
1
def all
-
15
@selectors ||= {}
-
end
-
-
1
def add(name, &block)
-
15
all[name.to_sym] = Capybara::Selector.new(name.to_sym, &block)
-
end
-
-
1
def remove(name)
-
all.delete(name.to_sym)
-
end
-
end
-
-
1
def initialize(name, &block)
-
15
@name = name
-
15
@custom_filters = {}
-
15
@match = nil
-
15
@label = nil
-
15
@failure_message = nil
-
15
@description = nil
-
15
instance_eval(&block)
-
end
-
-
1
def xpath(&block)
-
14
@format = :xpath
-
14
@xpath = block if block
-
14
@xpath
-
end
-
-
# Same as xpath, but wrap in XPath.css().
-
1
def css(&block)
-
1
@format = :css
-
1
@css = block if block
-
1
@css
-
end
-
-
1
def match(&block)
-
@match = block if block
-
@match
-
end
-
-
1
def label(label=nil)
-
5
@label = label if label
-
5
@label
-
end
-
-
1
def description(options={})
-
(@description && @description.call(options)).to_s
-
end
-
-
1
def call(locator)
-
if @format==:css
-
@css.call(locator)
-
else
-
@xpath.call(locator)
-
end
-
end
-
-
1
def match?(locator)
-
@match and @match.call(locator)
-
end
-
-
1
def filter(name, options={}, &block)
-
22
@custom_filters[name] = Filter.new(name, block, options)
-
end
-
-
1
def describe &block
-
9
@description = block
-
end
-
end
-
end
-
-
1
Capybara.add_selector(:xpath) do
-
1
xpath { |xpath| xpath }
-
end
-
-
1
Capybara.add_selector(:css) do
-
1
css { |css| css }
-
end
-
-
1
Capybara.add_selector(:id) do
-
1
xpath { |id| XPath.descendant[XPath.attr(:id) == id.to_s] }
-
end
-
-
1
Capybara.add_selector(:field) do
-
1
xpath { |locator| XPath::HTML.field(locator) }
-
1
filter(:checked, boolean: true) { |node, value| not(value ^ node.checked?) }
-
1
filter(:unchecked, boolean: true) { |node, value| (value ^ node.checked?) }
-
1
filter(:disabled, default: false, boolean: true) { |node, value| not(value ^ node.disabled?) }
-
1
filter(:with) { |node, with| node.value == with.to_s }
-
1
filter(:type) do |node, type|
-
if ['textarea', 'select'].include?(type)
-
node.tag_name == type
-
else
-
node[:type] == type
-
end
-
end
-
1
describe do |options|
-
desc, states = "", []
-
desc << " of type #{options[:type].inspect}" if options[:type]
-
desc << " with value #{options[:with].to_s.inspect}" if options.has_key?(:with)
-
states << 'checked' if options[:checked] || (options.has_key?(:unchecked) && !options[:unchecked])
-
states << 'not checked' if options[:unchecked] || (options.has_key?(:checked) && !options[:checked])
-
states << 'disabled' if options[:disabled]
-
desc << " that is #{states.join(' and ')}" unless states.empty?
-
desc
-
end
-
end
-
-
1
Capybara.add_selector(:fieldset) do
-
1
xpath { |locator| XPath::HTML.fieldset(locator) }
-
end
-
-
1
Capybara.add_selector(:link_or_button) do
-
1
label "link or button"
-
1
xpath { |locator| XPath::HTML.link_or_button(locator) }
-
1
filter(:disabled, default: false, boolean: true) { |node, value| node.tag_name == "a" or not(value ^ node.disabled?) }
-
1
describe { |options| " that is disabled" if options[:disabled] }
-
end
-
-
1
Capybara.add_selector(:link) do
-
1
xpath { |locator| XPath::HTML.link(locator) }
-
1
filter(:href) do |node, href|
-
node.first(:xpath, XPath.axis(:self)[XPath.attr(:href).equals(href.to_s)])
-
end
-
1
describe { |options| " with href #{options[:href].inspect}" if options[:href] }
-
end
-
-
1
Capybara.add_selector(:button) do
-
1
xpath { |locator| XPath::HTML.button(locator) }
-
1
filter(:disabled, default: false, boolean: true) { |node, value| not(value ^ node.disabled?) }
-
1
describe { |options| " that is disabled" if options[:disabled] }
-
end
-
-
1
Capybara.add_selector(:fillable_field) do
-
1
label "field"
-
1
xpath { |locator| XPath::HTML.fillable_field(locator) }
-
1
filter(:disabled, default: false, boolean: true) { |node, value| not(value ^ node.disabled?) }
-
1
describe { |options| " that is disabled" if options[:disabled] }
-
end
-
-
1
Capybara.add_selector(:radio_button) do
-
1
label "radio button"
-
1
xpath { |locator| XPath::HTML.radio_button(locator) }
-
1
filter(:checked, boolean: true) { |node, value| not(value ^ node.checked?) }
-
1
filter(:unchecked, boolean: true) { |node, value| (value ^ node.checked?) }
-
1
filter(:option) { |node, value| node.value == value.to_s }
-
1
filter(:disabled, default: false, boolean: true) { |node, value| not(value ^ node.disabled?) }
-
1
describe do |options|
-
desc, states = "", []
-
desc << " with value #{options[:option].inspect}" if options[:option]
-
states << 'checked' if options[:checked] || (options.has_key?(:unchecked) && !options[:unchecked])
-
states << 'not checked' if options[:unchecked] || (options.has_key?(:checked) && !options[:checked])
-
states << 'disabled' if options[:disabled]
-
desc << " that is #{states.join(' and ')}" unless states.empty?
-
desc
-
end
-
end
-
-
1
Capybara.add_selector(:checkbox) do
-
1
xpath { |locator| XPath::HTML.checkbox(locator) }
-
1
filter(:checked, boolean: true) { |node, value| not(value ^ node.checked?) }
-
1
filter(:unchecked, boolean: true) { |node, value| (value ^ node.checked?) }
-
1
filter(:option) { |node, value| node.value == value.to_s }
-
1
filter(:disabled, default: false, boolean: true) { |node, value| not(value ^ node.disabled?) }
-
1
describe do |options|
-
desc, states = "", []
-
desc << " with value #{options[:option].inspect}" if options[:option]
-
states << 'checked' if options[:checked] || (options.has_key?(:unchecked) && !options[:unchecked])
-
states << 'not checked' if options[:unchecked] || (options.has_key?(:checked) && !options[:checked])
-
states << 'disabled' if options[:disabled]
-
desc << " that is #{states.join(' and ')}" unless states.empty?
-
desc
-
end
-
end
-
-
1
Capybara.add_selector(:select) do
-
1
label "select box"
-
1
xpath { |locator| XPath::HTML.select(locator) }
-
1
filter(:options) do |node, options|
-
actual = node.all(:xpath, './/option').map { |option| option.text }
-
options.sort == actual.sort
-
end
-
1
filter(:with_options) { |node, options| options.all? { |option| node.first(:option, option) } }
-
1
filter(:selected) do |node, selected|
-
actual = node.all(:xpath, './/option').select { |option| option.selected? }.map { |option| option.text }
-
[selected].flatten.sort == actual.sort
-
end
-
1
filter(:disabled, default: false, boolean: true) { |node, value| not(value ^ node.disabled?) }
-
1
describe do |options|
-
desc = ""
-
desc << " with options #{options[:options].inspect}" if options[:options]
-
desc << " with at least options #{options[:with_options].inspect}" if options[:with_options]
-
desc << " with #{options[:selected].inspect} selected" if options[:selected]
-
desc << " that is disabled" if options[:disabled]
-
desc
-
end
-
end
-
-
1
Capybara.add_selector(:option) do
-
1
xpath { |locator| XPath::HTML.option(locator) }
-
end
-
-
1
Capybara.add_selector(:file_field) do
-
1
label "file field"
-
1
xpath { |locator| XPath::HTML.file_field(locator) }
-
1
filter(:disabled, default: false, boolean: true) { |node, value| not(value ^ node.disabled?) }
-
1
describe { |options| " that is disabled" if options[:disabled] }
-
end
-
-
1
Capybara.add_selector(:table) do
-
1
xpath { |locator| XPath::HTML.table(locator) }
-
end
-
1
require "uri"
-
-
1
class Capybara::Selenium::Driver < Capybara::Driver::Base
-
1
DEFAULT_OPTIONS = {
-
:browser => :firefox
-
}
-
1
SPECIAL_OPTIONS = [:browser]
-
-
1
attr_reader :app, :options
-
-
1
def browser
-
unless @browser
-
@browser = Selenium::WebDriver.for(options[:browser], options.reject { |key,val| SPECIAL_OPTIONS.include?(key) })
-
-
main = Process.pid
-
at_exit do
-
# Store the exit status of the test run since it goes away after calling the at_exit proc...
-
@exit_status = $!.status if $!.is_a?(SystemExit)
-
quit if Process.pid == main
-
exit @exit_status if @exit_status # Force exit with stored status
-
end
-
end
-
@browser
-
end
-
-
1
def initialize(app, options={})
-
begin
-
require 'selenium-webdriver'
-
rescue LoadError => e
-
if e.message =~ /selenium-webdriver/
-
raise LoadError, "Capybara's selenium driver is unable to load `selenium-webdriver`, please install the gem and add `gem 'selenium-webdriver'` to your Gemfile if you are using bundler."
-
else
-
raise e
-
end
-
end
-
-
@app = app
-
@browser = nil
-
@exit_status = nil
-
@frame_handles = {}
-
@options = DEFAULT_OPTIONS.merge(options)
-
end
-
-
1
def visit(path)
-
browser.navigate.to(path)
-
end
-
-
1
def go_back
-
browser.navigate.back
-
end
-
-
1
def go_forward
-
browser.navigate.forward
-
end
-
-
1
def html
-
browser.page_source
-
end
-
-
1
def title
-
browser.title
-
end
-
-
1
def current_url
-
browser.current_url
-
end
-
-
1
def find_xpath(selector)
-
browser.find_elements(:xpath, selector).map { |node| Capybara::Selenium::Node.new(self, node) }
-
end
-
-
1
def find_css(selector)
-
browser.find_elements(:css, selector).map { |node| Capybara::Selenium::Node.new(self, node) }
-
end
-
-
1
def wait?; true; end
-
1
def needs_server?; true; end
-
-
1
def execute_script(script)
-
browser.execute_script script
-
end
-
-
1
def evaluate_script(script)
-
browser.execute_script "return #{script}"
-
end
-
-
1
def save_screenshot(path, options={})
-
browser.save_screenshot(path)
-
end
-
-
1
def reset!
-
# Use instance variable directly so we avoid starting the browser just to reset the session
-
if @browser
-
begin
-
begin @browser.manage.delete_all_cookies
-
rescue Selenium::WebDriver::Error::UnhandledError
-
# delete_all_cookies fails when we've previously gone
-
# to about:blank, so we rescue this error and do nothing
-
# instead.
-
end
-
@browser.navigate.to("about:blank")
-
rescue Selenium::WebDriver::Error::UnhandledAlertError
-
# This error is thrown if an unhandled alert is on the page
-
# Firefox appears to automatically dismiss this alert, chrome does not
-
# We'll try to accept it
-
begin
-
@browser.switch_to.alert.accept
-
rescue Selenium::WebDriver::Error::NoAlertPresentError
-
# The alert is now gone - nothing to do
-
end
-
# try cleaning up the browser again
-
retry
-
end
-
end
-
end
-
-
##
-
#
-
# Webdriver supports frame name, id, index(zero-based) or {Capybara::Element} to find iframe
-
#
-
# @overload within_frame(index)
-
# @param [Integer] index index of a frame
-
# @overload within_frame(name_or_id)
-
# @param [String] name_or_id name or id of a frame
-
# @overload within_frame(element)
-
# @param [Capybara::Node::Base] a_node frame element
-
#
-
1
def within_frame(frame_handle)
-
@frame_handles[browser.window_handle] ||= []
-
frame_handle = frame_handle.native if frame_handle.is_a?(Capybara::Node::Base)
-
@frame_handles[browser.window_handle] << frame_handle
-
a=browser.switch_to.frame(frame_handle)
-
yield
-
ensure
-
# There doesnt appear to be any way in Webdriver to move back to a parent frame
-
# other than going back to the root and then reiterating down
-
@frame_handles[browser.window_handle].pop
-
browser.switch_to.default_content
-
@frame_handles[browser.window_handle].each { |fh| browser.switch_to.frame(fh) }
-
end
-
-
1
def current_window_handle
-
browser.window_handle
-
end
-
-
1
def window_size(handle)
-
within_given_window(handle) do
-
size = browser.manage.window.size
-
[size.width, size.height]
-
end
-
end
-
-
1
def resize_window_to(handle, width, height)
-
within_given_window(handle) do
-
browser.manage.window.resize_to(width, height)
-
end
-
end
-
-
1
def maximize_window(handle)
-
within_given_window(handle) do
-
browser.manage.window.maximize
-
end
-
sleep 0.1 # work around for https://code.google.com/p/selenium/issues/detail?id=7405
-
end
-
-
1
def close_window(handle)
-
within_given_window(handle) do
-
browser.close
-
end
-
end
-
-
1
def window_handles
-
browser.window_handles
-
end
-
-
1
def open_new_window
-
browser.execute_script('window.open();')
-
end
-
-
1
def switch_to_window(handle)
-
browser.switch_to.window handle
-
end
-
-
# @api private
-
1
def find_window(locator)
-
handles = browser.window_handles
-
return locator if handles.include? locator
-
-
original_handle = browser.window_handle
-
handles.each do |handle|
-
switch_to_window(handle)
-
if (locator == browser.execute_script("return window.name") ||
-
browser.title.include?(locator) ||
-
browser.current_url.include?(locator))
-
switch_to_window(original_handle)
-
return handle
-
end
-
end
-
raise Capybara::ElementNotFound, "Could not find a window identified by #{locator}"
-
end
-
-
1
def within_window(locator)
-
handle = find_window(locator)
-
browser.switch_to.window(handle) { yield }
-
end
-
-
1
def accept_modal(type, options={}, &blk)
-
yield if block_given?
-
modal = find_modal(options)
-
modal.send_keys options[:with] if options[:with]
-
message = modal.text
-
modal.accept
-
message
-
end
-
-
1
def dismiss_modal(type, options={}, &blk)
-
yield if block_given?
-
modal = find_modal(options)
-
message = modal.text
-
modal.dismiss
-
message
-
end
-
-
1
def quit
-
@browser.quit if @browser
-
rescue Errno::ECONNREFUSED
-
# Browser must have already gone
-
ensure
-
@browser = nil
-
end
-
-
1
def invalid_element_errors
-
[Selenium::WebDriver::Error::StaleElementReferenceError, Selenium::WebDriver::Error::UnhandledError, Selenium::WebDriver::Error::ElementNotVisibleError]
-
end
-
-
1
def no_such_window_error
-
Selenium::WebDriver::Error::NoSuchWindowError
-
end
-
-
1
private
-
-
1
def within_given_window(handle)
-
original_handle = self.current_window_handle
-
if handle == original_handle
-
yield
-
else
-
switch_to_window(handle)
-
result = yield
-
switch_to_window(original_handle)
-
result
-
end
-
end
-
-
1
def find_modal(options={})
-
# Selenium has its own built in wait (2 seconds)for a modal to show up, so this wait is really the minimum time
-
# Actual wait time may be longer than specified
-
wait = Selenium::WebDriver::Wait.new(
-
timeout: (options[:wait] || Capybara.default_wait_time),
-
ignore: Selenium::WebDriver::Error::NoAlertPresentError)
-
begin
-
modal = wait.until do
-
alert = @browser.switch_to.alert
-
regexp = options[:text].is_a?(Regexp) ? options[:text] : Regexp.escape(options[:text].to_s)
-
alert.text.match(regexp) ? alert : nil
-
end
-
rescue Selenium::WebDriver::Error::TimeOutError
-
raise Capybara::ModalNotFound.new("Unable to find modal dialog#{" with #{options[:text]}" if options[:text]}")
-
end
-
end
-
-
end
-
1
class Capybara::Selenium::Node < Capybara::Driver::Node
-
1
def visible_text
-
# Selenium doesn't normalize Unicode whitespace.
-
Capybara::Helpers.normalize_whitespace(native.text)
-
end
-
-
1
def all_text
-
text = driver.browser.execute_script("return arguments[0].textContent", native)
-
Capybara::Helpers.normalize_whitespace(text)
-
end
-
-
1
def [](name)
-
native.attribute(name.to_s)
-
rescue Selenium::WebDriver::Error::WebDriverError
-
nil
-
end
-
-
1
def value
-
if tag_name == "select" and self[:multiple] and not self[:multiple] == "false"
-
native.find_elements(:xpath, ".//option").select { |n| n.selected? }.map { |n| n[:value] || n.text }
-
else
-
native[:value]
-
end
-
end
-
-
1
def set(value)
-
tag_name = self.tag_name
-
type = self[:type]
-
if (Array === value) && !self[:multiple]
-
raise ArgumentError.new "Value cannot be an Array when 'multiple' attribute is not present. Not a #{value.class}"
-
end
-
if tag_name == 'input' and type == 'radio'
-
click
-
elsif tag_name == 'input' and type == 'checkbox'
-
click if value ^ native.attribute('checked').to_s.eql?("true")
-
elsif tag_name == 'input' and type == 'file'
-
path_names = value.to_s.empty? ? [] : value
-
native.send_keys(*path_names)
-
elsif tag_name == 'textarea' or tag_name == 'input'
-
if self[:readonly]
-
warn "Attempt to set readonly element with value: #{value} \n *This will raise an exception in a future version of Capybara"
-
elsif value.to_s.empty?
-
native.clear
-
else
-
#script can change a readonly element which user input cannot, so dont execute if readonly
-
driver.browser.execute_script "arguments[0].value = ''", native
-
native.send_keys(value.to_s)
-
end
-
elsif native.attribute('isContentEditable')
-
#ensure we are focused on the element
-
script = <<-JS
-
var range = document.createRange();
-
range.selectNodeContents(arguments[0]);
-
window.getSelection().addRange(range);
-
JS
-
driver.browser.execute_script script, native
-
native.send_keys(value.to_s)
-
end
-
end
-
-
1
def select_option
-
native.click unless selected?
-
end
-
-
1
def unselect_option
-
if select_node['multiple'] != 'multiple' and select_node['multiple'] != 'true'
-
raise Capybara::UnselectNotAllowed, "Cannot unselect option from single select box."
-
end
-
native.click if selected?
-
end
-
-
1
def click
-
native.click
-
end
-
-
1
def right_click
-
driver.browser.action.context_click(native).perform
-
end
-
-
1
def double_click
-
driver.browser.action.double_click(native).perform
-
end
-
-
1
def hover
-
driver.browser.action.move_to(native).perform
-
end
-
-
1
def drag_to(element)
-
driver.browser.action.drag_and_drop(native, element.native).perform
-
end
-
-
1
def tag_name
-
native.tag_name.downcase
-
end
-
-
1
def visible?
-
displayed = native.displayed?
-
displayed and displayed != "false"
-
end
-
-
1
def selected?
-
selected = native.selected?
-
selected and selected != "false"
-
end
-
-
1
def disabled?
-
!native.enabled?
-
end
-
-
1
alias :checked? :selected?
-
-
1
def find_xpath(locator)
-
native.find_elements(:xpath, locator).map { |n| self.class.new(driver, n) }
-
end
-
-
1
def find_css(locator)
-
native.find_elements(:css, locator).map { |n| self.class.new(driver, n) }
-
end
-
-
1
def ==(other)
-
native == other.native
-
end
-
-
1
private
-
-
# a reference to the select node if this is an option node
-
1
def select_node
-
find_xpath('./ancestor::select').first
-
end
-
end
-
1
require 'uri'
-
1
require 'net/http'
-
1
require 'rack'
-
-
1
module Capybara
-
1
class Server
-
1
class Middleware
-
1
attr_accessor :error
-
-
1
def initialize(app)
-
@app = app
-
end
-
-
1
def call(env)
-
if env["PATH_INFO"] == "/__identify__"
-
[200, {}, [@app.object_id.to_s]]
-
else
-
begin
-
@app.call(env)
-
rescue StandardError => e
-
@error = e unless @error
-
raise e
-
end
-
end
-
end
-
end
-
-
1
class << self
-
1
def ports
-
@ports ||= {}
-
end
-
end
-
-
1
attr_reader :app, :port, :host
-
-
1
def initialize(app, port=Capybara.server_port, host=Capybara.server_host)
-
@app = app
-
@middleware = Middleware.new(@app)
-
@server_thread = nil # suppress warnings
-
@host, @port = host, port
-
@port ||= Capybara::Server.ports[@app.object_id]
-
@port ||= find_available_port
-
end
-
-
1
def reset_error!
-
@middleware.error = nil
-
end
-
-
1
def error
-
@middleware.error
-
end
-
-
1
def responsive?
-
return false if @server_thread && @server_thread.join(0)
-
-
res = Net::HTTP.start(host, @port) { |http| http.get('/__identify__') }
-
-
if res.is_a?(Net::HTTPSuccess) or res.is_a?(Net::HTTPRedirection)
-
return res.body == @app.object_id.to_s
-
end
-
rescue SystemCallError
-
return false
-
end
-
-
1
def boot
-
unless responsive?
-
Capybara::Server.ports[@app.object_id] = @port
-
-
@server_thread = Thread.new do
-
Capybara.server.call(@middleware, @port)
-
end
-
-
Timeout.timeout(60) { @server_thread.join(0.1) until responsive? }
-
end
-
rescue Timeout::Error
-
raise "Rack application timed out during boot"
-
else
-
self
-
end
-
-
1
private
-
-
1
def find_available_port
-
server = TCPServer.new('127.0.0.1', 0)
-
server.addr[1]
-
ensure
-
server.close if server
-
end
-
-
end
-
end
-
1
module Capybara
-
-
##
-
#
-
# The Session class represents a single user's interaction with the system. The Session can use
-
# any of the underlying drivers. A session can be initialized manually like this:
-
#
-
# session = Capybara::Session.new(:culerity, MyRackApp)
-
#
-
# The application given as the second argument is optional. When running Capybara against an external
-
# page, you might want to leave it out:
-
#
-
# session = Capybara::Session.new(:culerity)
-
# session.visit('http://www.google.com')
-
#
-
# Session provides a number of methods for controlling the navigation of the page, such as +visit+,
-
# +current_path, and so on. It also delegate a number of methods to a Capybara::Document, representing
-
# the current HTML document. This allows interaction:
-
#
-
# session.fill_in('q', :with => 'Capybara')
-
# session.click_button('Search')
-
# expect(session).to have_content('Capybara')
-
#
-
# When using capybara/dsl, the Session is initialized automatically for you.
-
#
-
1
class Session
-
1
NODE_METHODS = [
-
:all, :first, :attach_file, :text, :check, :choose,
-
:click_link_or_button, :click_button, :click_link, :field_labeled,
-
:fill_in, :find, :find_button, :find_by_id, :find_field, :find_link,
-
:has_content?, :has_text?, :has_css?, :has_no_content?, :has_no_text?,
-
:has_no_css?, :has_no_xpath?, :resolve, :has_xpath?, :select, :uncheck,
-
:has_link?, :has_no_link?, :has_button?, :has_no_button?, :has_field?,
-
:has_no_field?, :has_checked_field?, :has_unchecked_field?,
-
:has_no_table?, :has_table?, :unselect, :has_select?, :has_no_select?,
-
:has_selector?, :has_no_selector?, :click_on, :has_no_checked_field?,
-
:has_no_unchecked_field?, :query, :assert_selector, :assert_no_selector,
-
:refute_selector, :assert_text, :assert_no_text
-
]
-
# @api private
-
1
DOCUMENT_METHODS = [
-
:title, :assert_title, :assert_no_title, :has_title?, :has_no_title?
-
]
-
1
SESSION_METHODS = [
-
:body, :html, :source, :current_url, :current_host, :current_path,
-
:execute_script, :evaluate_script, :visit, :go_back, :go_forward,
-
:within, :within_fieldset, :within_table, :within_frame, :current_window,
-
:windows, :open_new_window, :switch_to_window, :within_window, :window_opened_by,
-
:save_page, :save_and_open_page, :save_screenshot,
-
:save_and_open_screenshot, :reset_session!, :response_headers,
-
:status_code, :current_scope
-
] + DOCUMENT_METHODS
-
1
MODAL_METHODS = [
-
:accept_alert, :accept_confirm, :dismiss_confirm, :accept_prompt,
-
:dismiss_prompt
-
]
-
1
DSL_METHODS = NODE_METHODS + SESSION_METHODS + MODAL_METHODS
-
-
1
attr_reader :mode, :app, :server
-
1
attr_accessor :synchronized
-
-
1
def initialize(mode, app=nil)
-
@mode = mode
-
@app = app
-
if Capybara.run_server and @app and driver.needs_server?
-
@server = Capybara::Server.new(@app).boot
-
else
-
@server = nil
-
end
-
@touched = false
-
end
-
-
1
def driver
-
@driver ||= begin
-
unless Capybara.drivers.has_key?(mode)
-
other_drivers = Capybara.drivers.keys.map { |key| key.inspect }
-
raise Capybara::DriverNotFoundError, "no driver called #{mode.inspect} was found, available drivers: #{other_drivers.join(', ')}"
-
end
-
Capybara.drivers[mode].call(app)
-
end
-
end
-
-
##
-
#
-
# Reset the session (i.e. remove cookies and navigate to blank page)
-
#
-
# This method does not:
-
#
-
# * accept modal dialogs if they are present (Selenium driver now does, others may not)
-
# * clear browser cache/HTML 5 local storage/IndexedDB/Web SQL database/etc.
-
# * modify state of the driver/underlying browser in any other way
-
#
-
# as doing so will result in performance downsides and it's not needed to do everything from the list above for most apps.
-
#
-
# If you want to do anything from the list above on a general basis you can:
-
#
-
# * write RSpec/Cucumber/etc. after hook
-
# * monkeypatch this method
-
# * use Ruby's `prepend` method
-
#
-
1
def reset!
-
if @touched
-
driver.reset!
-
assert_no_selector :xpath, "/html/body/*"
-
@touched = false
-
end
-
raise_server_error!
-
end
-
1
alias_method :cleanup!, :reset!
-
1
alias_method :reset_session!, :reset!
-
-
##
-
#
-
# Raise errors encountered in the server
-
#
-
1
def raise_server_error!
-
raise @server.error if Capybara.raise_server_errors and @server and @server.error
-
ensure
-
@server.reset_error! if @server
-
end
-
-
##
-
#
-
# Returns a hash of response headers. Not supported by all drivers (e.g. Selenium)
-
#
-
# @return [Hash{String => String}] A hash of response headers.
-
#
-
1
def response_headers
-
driver.response_headers
-
end
-
-
##
-
#
-
# Returns the current HTTP status code as an Integer. Not supported by all drivers (e.g. Selenium)
-
#
-
# @return [Integer] Current HTTP status code
-
#
-
1
def status_code
-
driver.status_code
-
end
-
-
##
-
#
-
# @return [String] A snapshot of the DOM of the current document, as it looks right now (potentially modified by JavaScript).
-
#
-
1
def html
-
driver.html
-
end
-
1
alias_method :body, :html
-
1
alias_method :source, :html
-
-
##
-
#
-
# @return [String] Path of the current page, without any domain information
-
#
-
1
def current_path
-
path = URI.parse(current_url).path
-
path if path and not path.empty?
-
end
-
-
##
-
#
-
# @return [String] Host of the current page
-
#
-
1
def current_host
-
uri = URI.parse(current_url)
-
"#{uri.scheme}://#{uri.host}" if uri.host
-
end
-
-
##
-
#
-
# @return [String] Fully qualified URL of the current page
-
#
-
1
def current_url
-
driver.current_url
-
end
-
-
##
-
#
-
# Navigate to the given URL. The URL can either be a relative URL or an absolute URL
-
# The behaviour of either depends on the driver.
-
#
-
# session.visit('/foo')
-
# session.visit('http://google.com')
-
#
-
# For drivers which can run against an external application, such as the selenium driver
-
# giving an absolute URL will navigate to that page. This allows testing applications
-
# running on remote servers. For these drivers, setting {Capybara.app_host} will make the
-
# remote server the default. For example:
-
#
-
# Capybara.app_host = 'http://google.com'
-
# session.visit('/') # visits the google homepage
-
#
-
# If {Capybara.always_include_port} is set to true and this session is running against
-
# a rack application, then the port that the rack application is running on will automatically
-
# be inserted into the URL. Supposing the app is running on port `4567`, doing something like:
-
#
-
# visit("http://google.com/test")
-
#
-
# Will actually navigate to `http://google.com:4567/test`.
-
#
-
# @param [#to_s] url The URL to navigate to. The parameter will be cast to a String.
-
#
-
1
def visit(url)
-
raise_server_error!
-
-
url = url.to_s
-
@touched = true
-
-
url_relative = URI.parse(url).scheme.nil?
-
-
if url_relative && Capybara.app_host
-
url = Capybara.app_host + url
-
url_relative = false
-
end
-
-
if @server
-
url = "http://#{@server.host}:#{@server.port}" + url if url_relative
-
-
if Capybara.always_include_port
-
uri = URI.parse(url)
-
uri.port = @server.port if uri.port == uri.default_port
-
url = uri.to_s
-
end
-
end
-
-
driver.visit(url)
-
end
-
-
##
-
#
-
# Move back a single entry in the browser's history.
-
#
-
1
def go_back
-
driver.go_back
-
end
-
-
##
-
#
-
# Move forward a single entry in the browser's history.
-
#
-
1
def go_forward
-
driver.go_forward
-
end
-
-
##
-
#
-
# Executes the given block within the context of a node. `within` takes the
-
# same options as `find`, as well as a block. For the duration of the
-
# block, any command to Capybara will be handled as though it were scoped
-
# to the given element.
-
#
-
# within(:xpath, '//div[@id="delivery-address"]') do
-
# fill_in('Street', :with => '12 Main Street')
-
# end
-
#
-
# Just as with `find`, if multiple elements match the selector given to
-
# `within`, an error will be raised, and just as with `find`, this
-
# behaviour can be controlled through the `:match` and `:exact` options.
-
#
-
# It is possible to omit the first parameter, in that case, the selector is
-
# assumed to be of the type set in Capybara.default_selector.
-
#
-
# within('div#delivery-address') do
-
# fill_in('Street', :with => '12 Main Street')
-
# end
-
#
-
# Note that a lot of uses of `within` can be replaced more succinctly with
-
# chaining:
-
#
-
# find('div#delivery-address').fill_in('Street', :with => '12 Main Street')
-
#
-
# @overload within(*find_args)
-
# @param (see Capybara::Node::Finders#all)
-
#
-
# @overload within(a_node)
-
# @param [Capybara::Node::Base] a_node The node in whose scope the block should be evaluated
-
#
-
# @raise [Capybara::ElementNotFound] If the scope can't be found before time expires
-
#
-
1
def within(*args)
-
new_scope = if args.first.is_a?(Capybara::Node::Base) then args.first else find(*args) end
-
begin
-
scopes.push(new_scope)
-
yield
-
ensure
-
scopes.pop
-
end
-
end
-
-
##
-
#
-
# Execute the given block within the a specific fieldset given the id or legend of that fieldset.
-
#
-
# @param [String] locator Id or legend of the fieldset
-
#
-
1
def within_fieldset(locator)
-
within :fieldset, locator do
-
yield
-
end
-
end
-
-
##
-
#
-
# Execute the given block within the a specific table given the id or caption of that table.
-
#
-
# @param [String] locator Id or caption of the table
-
#
-
1
def within_table(locator)
-
within :table, locator do
-
yield
-
end
-
end
-
-
##
-
#
-
# Execute the given block within the given iframe using given frame name or index.
-
# May be supported by not all drivers. Drivers that support it, may provide additional options.
-
#
-
# @overload within_frame(index)
-
# @param [Integer] index index of a frame
-
# @overload within_frame(name)
-
# @param [String] name name of a frame
-
#
-
1
def within_frame(frame_handle)
-
scopes.push(nil)
-
driver.within_frame(frame_handle) do
-
yield
-
end
-
ensure
-
scopes.pop
-
end
-
-
##
-
# @return [Capybara::Window] current window
-
#
-
1
def current_window
-
Window.new(self, driver.current_window_handle)
-
end
-
-
##
-
# Get all opened windows.
-
# The order of windows in returned array is not defined.
-
# The driver may sort windows by their creation time but it's not required.
-
#
-
# @return [Array<Capybara::Window>] an array of all windows
-
#
-
1
def windows
-
driver.window_handles.map do |handle|
-
Window.new(self, handle)
-
end
-
end
-
-
##
-
# Open new window.
-
# Current window doesn't change as the result of this call.
-
# It should be switched to explicitly.
-
#
-
# @return [Capybara::Window] window that has been opened
-
#
-
1
def open_new_window
-
window_opened_by do
-
driver.open_new_window
-
end
-
end
-
-
##
-
# @overload switch_to_window(&block)
-
# Switches to the first window for which given block returns a value other than false or nil.
-
# If window that matches block can't be found, the window will be switched back and `WindowError` will be raised.
-
# @example
-
# window = switch_to_window { title == 'Page title' }
-
# @raise [Capybara::WindowError] if no window matches given block
-
# @overload switch_to_window(window)
-
# @param window [Capybara::Window] window that should be switched to
-
# @raise [Capybara::Driver::Base#no_such_window_error] if unexistent (e.g. closed) window was passed
-
#
-
# @return [Capybara::Window] window that has been switched to
-
# @raise [Capybara::ScopeError] if this method is invoked inside `within`,
-
# `within_frame` or `within_window` methods
-
# @raise [ArgumentError] if both or neither arguments were provided
-
#
-
1
def switch_to_window(window = nil)
-
block_given = block_given?
-
if window && block_given
-
raise ArgumentError, "`switch_to_window` can take either a block or a window, not both"
-
elsif !window && !block_given
-
raise ArgumentError, "`switch_to_window`: either window or block should be provided"
-
elsif scopes.size > 1
-
raise Capybara::ScopeError, "`switch_to_window` is not supposed to be invoked from "\
-
"`within`'s, `within_frame`'s' or `within_window`'s' block."
-
end
-
-
if window
-
driver.switch_to_window(window.handle)
-
window
-
else
-
original_window_handle = driver.current_window_handle
-
begin
-
driver.window_handles.each do |handle|
-
driver.switch_to_window handle
-
if yield
-
return Window.new(self, handle)
-
end
-
end
-
rescue => e
-
driver.switch_to_window(original_window_handle)
-
raise e
-
else
-
driver.switch_to_window(original_window_handle)
-
raise Capybara::WindowError, "Could not find a window matching block/lambda"
-
end
-
end
-
end
-
-
##
-
# This method does the following:
-
#
-
# 1. Switches to the given window (it can be located by window instance/lambda/string).
-
# 2. Executes the given block (within window located at previous step).
-
# 3. Switches back (this step will be invoked even if exception will happen at second step)
-
#
-
# @overload within_window(window) { do_something }
-
# @param window [Capybara::Window] instance of `Capybara::Window` class
-
# that will be switched to
-
# @raise [driver#no_such_window_error] if unexistent (e.g. closed) window was passed
-
# @overload within_window(proc_or_lambda) { do_something }
-
# @param lambda [Proc] lambda. First window for which lambda
-
# returns a value other than false or nil will be switched to.
-
# @example
-
# within_window(->{ page.title == 'Page title' }) { click_button 'Submit' }
-
# @raise [Capybara::WindowError] if no window matching lambda was found
-
# @overload within_window(string) { do_something }
-
# @deprecated Pass window or lambda instead
-
# @param [String] handle, name, url or title of the window
-
#
-
# @raise [Capybara::ScopeError] if this method is invoked inside `within`,
-
# `within_frame` or `within_window` methods
-
# @return value returned by the block
-
#
-
1
def within_window(window_or_handle)
-
if window_or_handle.instance_of?(Capybara::Window)
-
original = current_window
-
switch_to_window(window_or_handle) unless original == window_or_handle
-
scopes << nil
-
begin
-
yield
-
ensure
-
@scopes.pop
-
switch_to_window(original) unless original == window_or_handle
-
end
-
elsif window_or_handle.is_a?(Proc)
-
original = current_window
-
switch_to_window { window_or_handle.call }
-
scopes << nil
-
begin
-
yield
-
ensure
-
@scopes.pop
-
switch_to_window(original)
-
end
-
else
-
offending_line = caller.first
-
file_line = offending_line.match(/^(.+?):(\d+)/)[0]
-
warn "DEPRECATION WARNING: Passing string argument to #within_window is deprecated. "\
-
"Pass window object or lambda. (called from #{file_line})"
-
begin
-
scopes << nil
-
driver.within_window(window_or_handle) { yield }
-
ensure
-
@scopes.pop
-
end
-
end
-
end
-
-
##
-
# Get the window that has been opened by the passed block.
-
# It will wait for it to be opened (in the same way as other Capybara methods wait).
-
# It's better to use this method than `windows.last`
-
# {https://dvcs.w3.org/hg/webdriver/raw-file/default/webdriver-spec.html#h_note_10 as order of windows isn't defined in some drivers}
-
#
-
# @param options [Hash]
-
# @option options [Numeric] :wait (Capybara.default_wait_time) wait time
-
# @return [Capybara::Window] the window that has been opened within a block
-
# @raise [Capybara::WindowError] if block passed to window hasn't opened window
-
# or opened more than one window
-
#
-
1
def window_opened_by(options = {}, &block)
-
old_handles = driver.window_handles
-
block.call
-
-
wait_time = Capybara::Query.new(options).wait
-
document.synchronize(wait_time, errors: [Capybara::WindowError]) do
-
opened_handles = (driver.window_handles - old_handles)
-
if opened_handles.size != 1
-
raise Capybara::WindowError, "block passed to #window_opened_by "\
-
"opened #{opened_handles.size} windows instead of 1"
-
end
-
Window.new(self, opened_handles.first)
-
end
-
end
-
-
##
-
#
-
# Execute the given script, not returning a result. This is useful for scripts that return
-
# complex objects, such as jQuery statements. +execute_script+ should be used over
-
# +evaluate_script+ whenever possible.
-
#
-
# @param [String] script A string of JavaScript to execute
-
#
-
1
def execute_script(script)
-
@touched = true
-
driver.execute_script(script)
-
end
-
-
##
-
#
-
# Evaluate the given JavaScript and return the result. Be careful when using this with
-
# scripts that return complex objects, such as jQuery statements. +execute_script+ might
-
# be a better alternative.
-
#
-
# @param [String] script A string of JavaScript to evaluate
-
# @return [Object] The result of the evaluated JavaScript (may be driver specific)
-
#
-
1
def evaluate_script(script)
-
@touched = true
-
driver.evaluate_script(script)
-
end
-
-
##
-
#
-
# Execute the block, accepting a alert.
-
#
-
# @!macro modal_params
-
# @overload $0(text, options = {}, &blk)
-
# @param text [String, Regexp] Text or regex to match against the text in the modal. If not provided any modal is matched
-
# @overload $0(options = {}, &blk)
-
# @option options [Numeric] :wait How long to wait for the modal to appear after executing the block.
-
# @return [String] the message shown in the modal
-
# @raise [Capybara::ModalNotFound] if modal dialog hasn't been found
-
#
-
1
def accept_alert(text_or_options=nil, options={}, &blk)
-
if text_or_options.is_a? Hash
-
options=text_or_options
-
else
-
options[:text]=text_or_options
-
end
-
-
driver.accept_modal(:alert, options, &blk)
-
end
-
-
##
-
#
-
# Execute the block, accepting a confirm.
-
#
-
# @macro modal_params
-
#
-
1
def accept_confirm(text_or_options=nil, options={}, &blk)
-
if text_or_options.is_a? Hash
-
options=text_or_options
-
else
-
options[:text]=text_or_options
-
end
-
-
driver.accept_modal(:confirm, options, &blk)
-
end
-
-
##
-
#
-
# Execute the block, dismissing a confirm.
-
#
-
# @macro modal_params
-
#
-
1
def dismiss_confirm(text_or_options=nil, options={}, &blk)
-
if text_or_options.is_a? Hash
-
options=text_or_options
-
else
-
options[:text]=text_or_options
-
end
-
-
driver.dismiss_modal(:confirm, options, &blk)
-
end
-
-
##
-
#
-
# Execute the block, accepting a prompt, optionally responding to the prompt.
-
#
-
# @macro modal_params
-
# @option options [String] :with Response to provide to the prompt
-
#
-
1
def accept_prompt(text_or_options=nil, options={}, &blk)
-
if text_or_options.is_a? Hash
-
options=text_or_options
-
else
-
options[:text]=text_or_options
-
end
-
-
driver.accept_modal(:prompt, options, &blk)
-
end
-
-
##
-
#
-
# Execute the block, dismissing a prompt.
-
#
-
# @macro modal_params
-
#
-
1
def dismiss_prompt(text_or_options=nil, options={}, &blk)
-
if text_or_options.is_a? Hash
-
options=text_or_options
-
else
-
options[:text]=text_or_options
-
end
-
-
driver.dismiss_modal(:prompt, options, &blk)
-
end
-
-
##
-
#
-
# Save a snapshot of the page.
-
#
-
# @param [String] path The path to where it should be saved [optional]
-
#
-
1
def save_page(path=nil)
-
path ||= default_path('html')
-
-
FileUtils.mkdir_p(File.dirname(path))
-
-
File.open(path,'wb') { |f| f.write(Capybara::Helpers.inject_asset_host(body)) }
-
path
-
end
-
-
##
-
#
-
# Save a snapshot of the page and open it in a browser for inspection
-
#
-
# @param [String] file_name The path to where it should be saved [optional]
-
#
-
1
def save_and_open_page(file_name=nil)
-
file_name = save_page(file_name)
-
open_file(file_name)
-
end
-
-
##
-
#
-
# Save a screenshot of page
-
#
-
# @param [String] path A string of image path
-
# @option [Hash] options Options for saving screenshot
-
1
def save_screenshot(path, options={})
-
path ||= default_path('png')
-
-
FileUtils.mkdir_p(File.dirname(path))
-
-
driver.save_screenshot(path, options)
-
path
-
end
-
-
##
-
#
-
# Save a screenshot of the page and open it for inspection
-
#
-
# @param [String] file_name The path to where it should be saved [optional]
-
#
-
1
def save_and_open_screenshot(file_name=nil)
-
file_name = save_screenshot(file_name)
-
open_file(file_name)
-
end
-
-
1
def document
-
@document ||= Capybara::Node::Document.new(self, driver)
-
end
-
-
1
NODE_METHODS.each do |method|
-
51
define_method method do |*args, &block|
-
@touched = true
-
current_scope.send(method, *args, &block)
-
end
-
end
-
-
1
DOCUMENT_METHODS.each do |method|
-
5
define_method method do |*args, &block|
-
document.send(method, *args, &block)
-
end
-
end
-
-
1
def inspect
-
%(#<Capybara::Session>)
-
end
-
-
1
def current_scope
-
scopes.last || document
-
end
-
-
1
private
-
-
1
def open_file(file_name)
-
begin
-
require "launchy"
-
Launchy.open(file_name)
-
rescue LoadError
-
warn "File saved to #{file_name}."
-
warn "Please install the launchy gem to open the file automatically."
-
end
-
end
-
-
1
def default_path(extension)
-
timestamp = Time.new.strftime("%Y%m%d%H%M%S")
-
path = "capybara-#{timestamp}#{rand(10**10)}.#{extension}"
-
File.expand_path(path, Capybara.save_and_open_page_path)
-
end
-
-
1
def scopes
-
@scopes ||= [nil]
-
end
-
end
-
end
-
1
module Capybara
-
1
VERSION = '2.4.3'
-
end
-
1
module Capybara
-
##
-
# The Window class represents a browser window.
-
#
-
# You can get an instance of the class by calling either of:
-
#
-
# * {Capybara::Session#windows}
-
# * {Capybara::Session#current_window}
-
# * {Capybara::Session#window_opened_by}
-
# * {Capybara::Session#switch_to_window}
-
#
-
# Note that some drivers (e.g. Selenium) support getting size of/resizing/closing only
-
# current window. So if you invoke such method for:
-
#
-
# * window that is current, Capybara will make 2 Selenium method invocations
-
# (get handle of current window + get size/resize/close).
-
# * window that is not current, Capybara will make 4 Selenium method invocations
-
# (get handle of current window + switch to given handle + get size/resize/close + switch to original handle)
-
#
-
1
class Window
-
# @return [String] a string that uniquely identifies window within session
-
1
attr_reader :handle
-
-
# @return [Capybara::Session] session that this window belongs to
-
1
attr_reader :session
-
-
# @api private
-
1
def initialize(session, handle)
-
@session = session
-
@driver = session.driver
-
@handle = handle
-
end
-
-
##
-
# @return [Boolean] whether the window is not closed
-
1
def exists?
-
@driver.window_handles.include?(@handle)
-
end
-
-
##
-
# @return [Boolean] whether the window is closed
-
1
def closed?
-
!exists?
-
end
-
-
##
-
# @return [Boolean] whether this window is the window in which commands are being executed
-
1
def current?
-
@driver.current_window_handle == @handle
-
rescue @driver.no_such_window_error
-
false
-
end
-
-
##
-
# Close window.
-
#
-
# If this method was called for window that is current, then after calling this method
-
# future invocations of other Capybara methods should raise
-
# `session.driver.no_such_window_error` until another window will be switched to.
-
#
-
# @!macro about_current
-
# If this method was called for window that is not current, then after calling this method
-
# current window shouldn remain the same as it was before calling this method.
-
#
-
1
def close
-
@driver.close_window(handle)
-
end
-
-
##
-
# Get window size.
-
#
-
# @macro about_current
-
# @return [Array<(Fixnum, Fixnum)>] an array with width and height
-
#
-
1
def size
-
@driver.window_size(handle)
-
end
-
-
##
-
# Resize window.
-
#
-
# @macro about_current
-
# @param width [String] the new window width in pixels
-
# @param height [String] the new window height in pixels
-
#
-
1
def resize_to(width, height)
-
@driver.resize_window_to(handle, width, height)
-
end
-
-
##
-
# Maximize window.
-
#
-
# If a particular driver (e.g. headless driver) doesn't have concept of maximizing it
-
# may not support this method.
-
#
-
# @macro about_current
-
#
-
1
def maximize
-
@driver.maximize_window(handle)
-
end
-
-
1
def eql?(other)
-
other.kind_of?(self.class) && @session == other.session && @handle == other.handle
-
end
-
1
alias_method :==, :eql?
-
-
1
def hash
-
@session.hash ^ @handle.hash
-
end
-
-
1
def inspect
-
"#<Window @handle=#{@handle.inspect}>"
-
end
-
-
1
private
-
-
1
def raise_unless_current(what)
-
unless current?
-
raise Capybara::WindowError, "#{what} not current window is not possible."
-
end
-
end
-
end
-
end
-
1
require 'childprocess/errors'
-
1
require 'childprocess/abstract_process'
-
1
require 'childprocess/abstract_io'
-
1
require "fcntl"
-
-
1
module ChildProcess
-
-
1
@posix_spawn = false
-
-
1
class << self
-
1
def new(*args)
-
case os
-
when :macosx, :linux, :solaris, :bsd, :cygwin, :aix
-
if posix_spawn?
-
Unix::PosixSpawnProcess.new(args)
-
elsif jruby?
-
JRuby::Process.new(args)
-
else
-
Unix::ForkExecProcess.new(args)
-
end
-
when :windows
-
Windows::Process.new(args)
-
else
-
raise Error, "unsupported platform #{platform_name.inspect}"
-
end
-
end
-
1
alias_method :build, :new
-
-
1
def platform
-
2
if RUBY_PLATFORM == "java"
-
:jruby
-
2
elsif defined?(RUBY_ENGINE) && RUBY_ENGINE == "ironruby"
-
:ironruby
-
else
-
2
os
-
end
-
end
-
-
1
def platform_name
-
@platform_name ||= "#{arch}-#{os}"
-
end
-
-
1
def unix?
-
1
!windows?
-
end
-
-
1
def linux?
-
os == :linux
-
end
-
-
1
def jruby?
-
2
platform == :jruby
-
end
-
-
1
def windows?
-
2
os == :windows
-
end
-
-
1
def posix_spawn?
-
enabled = @posix_spawn || %w[1 true].include?(ENV['CHILDPROCESS_POSIX_SPAWN'])
-
return false unless enabled
-
-
require 'ffi'
-
begin
-
require "childprocess/unix/platform/#{ChildProcess.platform_name}"
-
rescue LoadError
-
raise ChildProcess::MissingPlatformError
-
end
-
-
require "childprocess/unix/lib"
-
require 'childprocess/unix/posix_spawn_process'
-
-
true
-
rescue ChildProcess::MissingPlatformError => ex
-
warn_once ex.message
-
false
-
end
-
-
#
-
# Set this to true to enable experimental use of posix_spawn.
-
#
-
-
1
def posix_spawn=(bool)
-
@posix_spawn = bool
-
end
-
-
1
def os
-
@os ||= (
-
1
require "rbconfig"
-
1
host_os = RbConfig::CONFIG['host_os'].downcase
-
-
1
case host_os
-
when /linux/
-
:linux
-
when /darwin|mac os/
-
1
:macosx
-
when /mswin|msys|mingw32/
-
:windows
-
when /cygwin/
-
:cygwin
-
when /solaris|sunos/
-
:solaris
-
when /bsd/
-
:bsd
-
when /aix/
-
:aix
-
else
-
raise Error, "unknown os: #{host_os.inspect}"
-
end
-
4
)
-
end
-
-
1
def arch
-
@arch ||= (
-
host_cpu = RbConfig::CONFIG['host_cpu'].downcase
-
case host_cpu
-
when /i[3456]86/
-
# Darwin always reports i686, even when running in 64bit mod
-
if os == :macosx && 0xfee1deadbeef.is_a?(Fixnum)
-
"x86_64"
-
else
-
"i386"
-
end
-
when /amd64|x86_64/
-
"x86_64"
-
when /ppc|powerpc/
-
"powerpc"
-
else
-
host_cpu
-
end
-
)
-
end
-
-
#
-
# By default, a child process will inherit open file descriptors from the
-
# parent process. This helper provides a cross-platform way of making sure
-
# that doesn't happen for the given file/io.
-
#
-
-
1
def close_on_exec(file)
-
if file.respond_to?(:close_on_exec=)
-
file.close_on_exec = true
-
elsif file.respond_to?(:fcntl) && defined?(Fcntl::FD_CLOEXEC)
-
file.fcntl Fcntl::F_SETFD, Fcntl::FD_CLOEXEC
-
-
if jruby? && posix_spawn?
-
# on JRuby, the fcntl call above apparently isn't enough when
-
# we're launching the process through posix_spawn.
-
fileno = JRuby.posix_fileno_for(file)
-
Unix::Lib.fcntl fileno, Fcntl::F_SETFD, Fcntl::FD_CLOEXEC
-
end
-
elsif windows?
-
Windows::Lib.dont_inherit file
-
else
-
raise Error, "not sure how to set close-on-exec for #{file.inspect} on #{platform_name.inspect}"
-
end
-
end
-
-
1
private
-
-
1
def warn_once(msg)
-
@warnings ||= {}
-
-
unless @warnings[msg]
-
@warnings[msg] = true
-
$stderr.puts msg
-
end
-
end
-
-
end # class << self
-
end # ChildProcess
-
-
1
require 'jruby' if ChildProcess.jruby?
-
-
1
require 'childprocess/unix' if ChildProcess.unix?
-
1
require 'childprocess/windows' if ChildProcess.windows?
-
1
require 'childprocess/jruby' if ChildProcess.jruby?
-
1
module ChildProcess
-
1
class AbstractIO
-
1
attr_reader :stderr, :stdout, :stdin
-
-
1
def inherit!
-
@stdout = STDOUT
-
@stderr = STDERR
-
end
-
-
1
def stderr=(io)
-
check_type io
-
@stderr = io
-
end
-
-
1
def stdout=(io)
-
check_type io
-
@stdout = io
-
end
-
-
#
-
# @api private
-
#
-
-
1
def _stdin=(io)
-
check_type io
-
@stdin = io
-
end
-
-
1
private
-
-
1
def check_type(io)
-
raise SubclassResponsibility, "check_type"
-
end
-
-
end
-
end
-
1
module ChildProcess
-
1
class AbstractProcess
-
1
POLL_INTERVAL = 0.1
-
-
1
attr_reader :exit_code
-
-
#
-
# Set this to true if you do not care about when or if the process quits.
-
#
-
1
attr_accessor :detach
-
-
#
-
# Set this to true if you want to write to the process' stdin (process.io.stdin)
-
#
-
1
attr_accessor :duplex
-
-
#
-
# Modify the child's environment variables
-
#
-
1
attr_reader :environment
-
-
#
-
# Set the child's current working directory.
-
#
-
1
attr_accessor :cwd
-
-
#
-
#
-
# Set this to true to make the child process the leader of a new process group
-
#
-
# This can be used to make sure that all grandchildren are killed
-
# when the child process dies.
-
#
-
1
attr_accessor :leader
-
-
#
-
# Create a new process with the given args.
-
#
-
# @api private
-
# @see ChildProcess.build
-
#
-
-
1
def initialize(args)
-
unless args.all? { |e| e.kind_of?(String) }
-
raise ArgumentError, "all arguments must be String: #{args.inspect}"
-
end
-
-
@args = args
-
@started = false
-
@exit_code = nil
-
@io = nil
-
@cwd = nil
-
@detach = false
-
@duplex = false
-
@leader = false
-
@environment = {}
-
end
-
-
#
-
# Returns a ChildProcess::AbstractIO subclass to configure the child's IO streams.
-
#
-
-
1
def io
-
raise SubclassResponsibility, "io"
-
end
-
-
#
-
# @return [Fixnum] the pid of the process after it has started
-
#
-
-
1
def pid
-
raise SubclassResponsibility, "pid"
-
end
-
-
#
-
# Launch the child process
-
#
-
# @return [AbstractProcess] self
-
#
-
-
1
def start
-
launch_process
-
@started = true
-
-
self
-
end
-
-
#
-
# Forcibly terminate the process, using increasingly harsher methods if possible.
-
#
-
# @param [Fixnum] timeout (3) Seconds to wait before trying the next method.
-
#
-
-
1
def stop(timeout = 3)
-
raise SubclassResponsibility, "stop"
-
end
-
-
#
-
# Block until the process has been terminated.
-
#
-
# @return [Fixnum] The exit status of the process
-
#
-
-
1
def wait
-
raise SubclassResponsibility, "wait"
-
end
-
-
#
-
# Did the process exit?
-
#
-
# @return [Boolean]
-
#
-
-
1
def exited?
-
raise SubclassResponsibility, "exited?"
-
end
-
-
#
-
# Is this process running?
-
#
-
# @return [Boolean]
-
#
-
-
1
def alive?
-
started? && !exited?
-
end
-
-
#
-
# Returns true if the process has exited and the exit code was not 0.
-
#
-
# @return [Boolean]
-
#
-
-
1
def crashed?
-
exited? && @exit_code != 0
-
end
-
-
#
-
# Wait for the process to exit, raising a ChildProcess::TimeoutError if
-
# the timeout expires.
-
#
-
-
1
def poll_for_exit(timeout)
-
log "polling #{timeout} seconds for exit"
-
-
end_time = Time.now + timeout
-
until (ok = exited?) || Time.now > end_time
-
sleep POLL_INTERVAL
-
end
-
-
unless ok
-
raise TimeoutError, "process still alive after #{timeout} seconds"
-
end
-
end
-
-
1
private
-
-
1
def launch_process
-
raise SubclassResponsibility, "launch_process"
-
end
-
-
1
def started?
-
@started
-
end
-
-
1
def detach?
-
@detach
-
end
-
-
1
def duplex?
-
@duplex
-
end
-
-
1
def leader?
-
@leader
-
end
-
-
1
def log(*args)
-
$stderr.puts "#{self.inspect} : #{args.inspect}" if $DEBUG
-
end
-
-
1
def assert_started
-
raise Error, "process not started" unless started?
-
end
-
-
end # AbstractProcess
-
end # ChildProcess
-
1
module ChildProcess
-
1
class Error < StandardError
-
end
-
-
1
class TimeoutError < Error
-
end
-
-
1
class SubclassResponsibility < Error
-
end
-
-
1
class InvalidEnvironmentVariable < Error
-
end
-
-
1
class LaunchError < Error
-
end
-
-
1
class MissingPlatformError < Error
-
1
def initialize
-
message = "posix_spawn is not yet supported on #{ChildProcess.platform_name} (#{RUBY_PLATFORM}), falling back to default implementation. " +
-
"If you believe this is an error, please file a bug at http://github.com/jarib/childprocess/issues"
-
-
super(message)
-
end
-
-
end
-
end
-
1
module ChildProcess
-
1
module Unix
-
end
-
end
-
-
1
require "childprocess/unix/io"
-
1
require "childprocess/unix/process"
-
1
require "childprocess/unix/fork_exec_process"
-
# PosixSpawnProcess + ffi is required on demand.
-
1
module ChildProcess
-
1
module Unix
-
1
class ForkExecProcess < Process
-
1
private
-
-
1
def launch_process
-
if @io
-
stdout = @io.stdout
-
stderr = @io.stderr
-
end
-
-
# pipe used to detect exec() failure
-
exec_r, exec_w = ::IO.pipe
-
ChildProcess.close_on_exec exec_w
-
-
if duplex?
-
reader, writer = ::IO.pipe
-
end
-
-
@pid = Kernel.fork {
-
# Children of the forked process will inherit its process group
-
# This is to make sure that all grandchildren dies when this Process instance is killed
-
::Process.setpgid 0, 0 if leader?
-
-
if @cwd
-
Dir.chdir(@cwd)
-
end
-
-
exec_r.close
-
set_env
-
-
STDOUT.reopen(stdout || "/dev/null")
-
STDERR.reopen(stderr || "/dev/null")
-
-
if duplex?
-
STDIN.reopen(reader)
-
writer.close
-
end
-
-
executable, *args = @args
-
-
begin
-
Kernel.exec([executable, executable], *args)
-
rescue SystemCallError => ex
-
exec_w << ex.message
-
end
-
}
-
-
exec_w.close
-
-
if duplex?
-
io._stdin = writer
-
reader.close
-
end
-
-
# if we don't eventually get EOF, exec() failed
-
unless exec_r.eof?
-
raise LaunchError, exec_r.read || "executing command with #{@args.inspect} failed"
-
end
-
-
::Process.detach(@pid) if detach?
-
end
-
-
1
def set_env
-
@environment.each { |k, v| ENV[k.to_s] = v.nil? ? nil : v.to_s }
-
end
-
-
end # Process
-
end # Unix
-
end # ChildProcess
-
1
module ChildProcess
-
1
module Unix
-
1
class IO < AbstractIO
-
1
private
-
-
1
def check_type(io)
-
unless io.respond_to? :to_io
-
raise ArgumentError, "expected #{io.inspect} to respond to :to_io"
-
end
-
-
result = io.to_io
-
unless result && result.kind_of?(::IO)
-
raise TypeError, "expected IO, got #{result.inspect}:#{result.class}"
-
end
-
end
-
-
end # IO
-
end # Unix
-
end # ChildProcess
-
-
-
1
module ChildProcess
-
1
module Unix
-
1
class Process < AbstractProcess
-
1
attr_reader :pid
-
-
1
def io
-
@io ||= Unix::IO.new
-
end
-
-
1
def stop(timeout = 3)
-
assert_started
-
send_term
-
-
begin
-
return poll_for_exit(timeout)
-
rescue TimeoutError
-
# try next
-
end
-
-
send_kill
-
wait
-
rescue Errno::ECHILD, Errno::ESRCH
-
# handle race condition where process dies between timeout
-
# and send_kill
-
true
-
end
-
-
1
def exited?
-
return true if @exit_code
-
-
assert_started
-
pid, status = ::Process.waitpid2(_pid, ::Process::WNOHANG | ::Process::WUNTRACED)
-
pid = nil if pid == 0 # may happen on jruby
-
-
log(:pid => pid, :status => status)
-
-
if pid
-
set_exit_code(status)
-
end
-
-
!!pid
-
end
-
-
1
def wait
-
assert_started
-
-
if exited?
-
exit_code
-
else
-
_, status = ::Process.waitpid2 _pid
-
set_exit_code(status)
-
end
-
end
-
-
1
private
-
-
1
def send_term
-
send_signal 'TERM'
-
end
-
-
1
def send_kill
-
send_signal 'KILL'
-
end
-
-
1
def send_signal(sig)
-
assert_started
-
-
log "sending #{sig}"
-
::Process.kill sig, _pid
-
end
-
-
1
def set_exit_code(status)
-
@exit_code = status.exitstatus || status.termsig
-
end
-
-
1
def _pid
-
if leader?
-
-@pid # negative pid == process group
-
else
-
@pid
-
end
-
end
-
-
end # Process
-
end # Unix
-
end # ChildProcess
-
# encoding: utf-8
-
# Encoding.default_internal = 'UTF-8'
-
-
# = CodeRay Library
-
#
-
# CodeRay is a Ruby library for syntax highlighting.
-
#
-
# I try to make CodeRay easy to use and intuitive, but at the same time fully
-
# featured, complete, fast and efficient.
-
#
-
# See README.
-
#
-
# It consists mainly of
-
# * the main engine: CodeRay (Scanners::Scanner, Tokens, Encoders::Encoder)
-
# * the plugin system: PluginHost, Plugin
-
# * the scanners in CodeRay::Scanners
-
# * the encoders in CodeRay::Encoders
-
# * the styles in CodeRay::Styles
-
#
-
# Here's a fancy graphic to light up this gray docu:
-
#
-
# http://cycnus.de/raindark/coderay/scheme.png
-
#
-
# == Documentation
-
#
-
# See CodeRay, Encoders, Scanners, Tokens.
-
#
-
# == Usage
-
#
-
# Remember you need RubyGems to use CodeRay, unless you have it in your load
-
# path. Run Ruby with -rubygems option if required.
-
#
-
# === Highlight Ruby code in a string as html
-
#
-
# require 'coderay'
-
# print CodeRay.scan('puts "Hello, world!"', :ruby).html
-
#
-
# # prints something like this:
-
# puts <span class="s">"Hello, world!"</span>
-
#
-
#
-
# === Highlight C code from a file in a html div
-
#
-
# require 'coderay'
-
# print CodeRay.scan(File.read('ruby.h'), :c).div
-
# print CodeRay.scan_file('ruby.h').html.div
-
#
-
# You can include this div in your page. The used CSS styles can be printed with
-
#
-
# % coderay_stylesheet
-
#
-
# === Highlight without typing too much
-
#
-
# If you are one of the hasty (or lazy, or extremely curious) people, just run this file:
-
#
-
# % ruby -rubygems /path/to/coderay/coderay.rb > example.html
-
#
-
# and look at the file it created in your browser.
-
#
-
# = CodeRay Module
-
#
-
# The CodeRay module provides convenience methods for the engine.
-
#
-
# * The +lang+ and +format+ arguments select Scanner and Encoder to use. These are
-
# simply lower-case symbols, like <tt>:python</tt> or <tt>:html</tt>.
-
# * All methods take an optional hash as last parameter, +options+, that is send to
-
# the Encoder / Scanner.
-
# * Input and language are always sorted in this order: +code+, +lang+.
-
# (This is in alphabetical order, if you need a mnemonic ;)
-
#
-
# You should be able to highlight everything you want just using these methods;
-
# so there is no need to dive into CodeRay's deep class hierarchy.
-
#
-
# The examples in the demo directory demonstrate common cases using this interface.
-
#
-
# = Basic Access Ways
-
#
-
# Read this to get a general view what CodeRay provides.
-
#
-
# == Scanning
-
#
-
# Scanning means analysing an input string, splitting it up into Tokens.
-
# Each Token knows about what type it is: string, comment, class name, etc.
-
#
-
# Each +lang+ (language) has its own Scanner; for example, <tt>:ruby</tt> code is
-
# handled by CodeRay::Scanners::Ruby.
-
#
-
# CodeRay.scan:: Scan a string in a given language into Tokens.
-
# This is the most common method to use.
-
# CodeRay.scan_file:: Scan a file and guess the language using FileType.
-
#
-
# The Tokens object you get from these methods can encode itself; see Tokens.
-
#
-
# == Encoding
-
#
-
# Encoding means compiling Tokens into an output. This can be colored HTML or
-
# LaTeX, a textual statistic or just the number of non-whitespace tokens.
-
#
-
# Each Encoder provides output in a specific +format+, so you select Encoders via
-
# formats like <tt>:html</tt> or <tt>:statistic</tt>.
-
#
-
# CodeRay.encode:: Scan and encode a string in a given language.
-
# CodeRay.encode_tokens:: Encode the given tokens.
-
# CodeRay.encode_file:: Scan a file, guess the language using FileType and encode it.
-
#
-
# == All-in-One Encoding
-
#
-
# CodeRay.encode:: Highlight a string with a given input and output format.
-
#
-
# == Instanciating
-
#
-
# You can use an Encoder instance to highlight multiple inputs. This way, the setup
-
# for this Encoder must only be done once.
-
#
-
# CodeRay.encoder:: Create an Encoder instance with format and options.
-
# CodeRay.scanner:: Create an Scanner instance for lang, with '' as default code.
-
#
-
# To make use of CodeRay.scanner, use CodeRay::Scanner::code=.
-
#
-
# The scanning methods provide more flexibility; we recommend to use these.
-
#
-
# == Reusing Scanners and Encoders
-
#
-
# If you want to re-use scanners and encoders (because that is faster), see
-
# CodeRay::Duo for the most convenient (and recommended) interface.
-
1
module CodeRay
-
-
1
$CODERAY_DEBUG ||= false
-
-
1
CODERAY_PATH = File.expand_path('../coderay', __FILE__)
-
-
# Assuming the path is a subpath of lib/coderay/
-
1
def self.coderay_path *path
-
10
File.join CODERAY_PATH, *path
-
end
-
-
1
require 'coderay/version'
-
-
# helpers
-
1
autoload :FileType, coderay_path('helpers', 'file_type')
-
-
# Tokens
-
1
autoload :Tokens, coderay_path('tokens')
-
1
autoload :TokensProxy, coderay_path('tokens_proxy')
-
1
autoload :TokenKinds, coderay_path('token_kinds')
-
-
# Plugin system
-
1
autoload :PluginHost, coderay_path('helpers', 'plugin')
-
1
autoload :Plugin, coderay_path('helpers', 'plugin')
-
-
# Plugins
-
1
autoload :Scanners, coderay_path('scanner')
-
1
autoload :Encoders, coderay_path('encoder')
-
1
autoload :Styles, coderay_path('style')
-
-
# convenience access and reusable Encoder/Scanner pair
-
1
autoload :Duo, coderay_path('duo')
-
-
1
class << self
-
-
# Scans the given +code+ (a String) with the Scanner for +lang+.
-
#
-
# This is a simple way to use CodeRay. Example:
-
# require 'coderay'
-
# page = CodeRay.scan("puts 'Hello, world!'", :ruby).html
-
#
-
# See also demo/demo_simple.
-
1
def scan code, lang, options = {}, &block
-
TokensProxy.new code, lang, options, block
-
end
-
-
# Scans +filename+ (a path to a code file) with the Scanner for +lang+.
-
#
-
# If +lang+ is :auto or omitted, the CodeRay::FileType module is used to
-
# determine it. If it cannot find out what type it is, it uses
-
# CodeRay::Scanners::Text.
-
#
-
# Calls CodeRay.scan.
-
#
-
# Example:
-
# require 'coderay'
-
# page = CodeRay.scan_file('some_c_code.c').html
-
1
def scan_file filename, lang = :auto, options = {}, &block
-
lang = FileType.fetch filename, :text, true if lang == :auto
-
code = File.read filename
-
scan code, lang, options, &block
-
end
-
-
# Encode a string.
-
#
-
# This scans +code+ with the the Scanner for +lang+ and then
-
# encodes it with the Encoder for +format+.
-
# +options+ will be passed to the Encoder.
-
#
-
# See CodeRay::Encoder.encode.
-
1
def encode code, lang, format, options = {}
-
encoder(format, options).encode code, lang, options
-
end
-
-
# Encode pre-scanned Tokens.
-
# Use this together with CodeRay.scan:
-
#
-
# require 'coderay'
-
#
-
# # Highlight a short Ruby code example in a HTML span
-
# tokens = CodeRay.scan '1 + 2', :ruby
-
# puts CodeRay.encode_tokens(tokens, :span)
-
#
-
1
def encode_tokens tokens, format, options = {}
-
encoder(format, options).encode_tokens tokens, options
-
end
-
-
# Encodes +filename+ (a path to a code file) with the Scanner for +lang+.
-
#
-
# See CodeRay.scan_file.
-
# Notice that the second argument is the output +format+, not the input language.
-
#
-
# Example:
-
# require 'coderay'
-
# page = CodeRay.encode_file 'some_c_code.c', :html
-
1
def encode_file filename, format, options = {}
-
tokens = scan_file filename, :auto, get_scanner_options(options)
-
encode_tokens tokens, format, options
-
end
-
-
# Highlight a string into a HTML <div>.
-
#
-
# CSS styles use classes, so you have to include a stylesheet
-
# in your output.
-
#
-
# See encode.
-
1
def highlight code, lang, options = { :css => :class }, format = :div
-
encode code, lang, format, options
-
end
-
-
# Highlight a file into a HTML <div>.
-
#
-
# CSS styles use classes, so you have to include a stylesheet
-
# in your output.
-
#
-
# See encode.
-
1
def highlight_file filename, options = { :css => :class }, format = :div
-
encode_file filename, format, options
-
end
-
-
# Finds the Encoder class for +format+ and creates an instance, passing
-
# +options+ to it.
-
#
-
# Example:
-
# require 'coderay'
-
#
-
# stats = CodeRay.encoder(:statistic)
-
# stats.encode("puts 17 + 4\n", :ruby)
-
#
-
# puts '%d out of %d tokens have the kind :integer.' % [
-
# stats.type_stats[:integer].count,
-
# stats.real_token_count
-
# ]
-
# #-> 2 out of 4 tokens have the kind :integer.
-
1
def encoder format, options = {}
-
Encoders[format].new options
-
end
-
-
# Finds the Scanner class for +lang+ and creates an instance, passing
-
# +options+ to it.
-
#
-
# See Scanner.new.
-
1
def scanner lang, options = {}, &block
-
Scanners[lang].new '', options, &block
-
end
-
-
# Extract the options for the scanner from the +options+ hash.
-
#
-
# Returns an empty Hash if <tt>:scanner_options</tt> is not set.
-
#
-
# This is used if a method like CodeRay.encode has to provide options
-
# for Encoder _and_ scanner.
-
1
def get_scanner_options options
-
options.fetch :scanner_options, {}
-
end
-
-
end
-
-
end
-
1
module CodeRay
-
-
# This module holds the Encoder class and its subclasses.
-
# For example, the HTML encoder is named CodeRay::Encoders::HTML
-
# can be found in coderay/encoders/html.
-
#
-
# Encoders also provides methods and constants for the register
-
# mechanism and the [] method that returns the Encoder class
-
# belonging to the given format.
-
1
module Encoders
-
-
1
extend PluginHost
-
1
plugin_path File.dirname(__FILE__), 'encoders'
-
-
# = Encoder
-
#
-
# The Encoder base class. Together with Scanner and
-
# Tokens, it forms the highlighting triad.
-
#
-
# Encoder instances take a Tokens object and do something with it.
-
#
-
# The most common Encoder is surely the HTML encoder
-
# (CodeRay::Encoders::HTML). It highlights the code in a colorful
-
# html page.
-
# If you want the highlighted code in a div or a span instead,
-
# use its subclasses Div and Span.
-
1
class Encoder
-
1
extend Plugin
-
1
plugin_host Encoders
-
-
1
class << self
-
-
# If FILE_EXTENSION isn't defined, this method returns the
-
# downcase class name instead.
-
1
def const_missing sym
-
if sym == :FILE_EXTENSION
-
(defined?(@plugin_id) && @plugin_id || name[/\w+$/].downcase).to_s
-
else
-
super
-
end
-
end
-
-
# The default file extension for output file of this encoder class.
-
1
def file_extension
-
self::FILE_EXTENSION
-
end
-
-
end
-
-
# Subclasses are to store their default options in this constant.
-
1
DEFAULT_OPTIONS = { }
-
-
# The options you gave the Encoder at creating.
-
1
attr_accessor :options, :scanner
-
-
# Creates a new Encoder.
-
# +options+ is saved and used for all encode operations, as long
-
# as you don't overwrite it there by passing additional options.
-
#
-
# Encoder objects provide three encode methods:
-
# - encode simply takes a +code+ string and a +lang+
-
# - encode_tokens expects a +tokens+ object instead
-
#
-
# Each method has an optional +options+ parameter. These are
-
# added to the options you passed at creation.
-
1
def initialize options = {}
-
@options = self.class::DEFAULT_OPTIONS.merge options
-
@@CODERAY_TOKEN_INTERFACE_DEPRECATION_WARNING_GIVEN = false
-
end
-
-
# Encode a Tokens object.
-
1
def encode_tokens tokens, options = {}
-
options = @options.merge options
-
@scanner = tokens.scanner if tokens.respond_to? :scanner
-
setup options
-
compile tokens, options
-
finish options
-
end
-
-
# Encode the given +code+ using the Scanner for +lang+.
-
1
def encode code, lang, options = {}
-
options = @options.merge options
-
@scanner = Scanners[lang].new code, CodeRay.get_scanner_options(options).update(:tokens => self)
-
setup options
-
@scanner.tokenize
-
finish options
-
end
-
-
# You can use highlight instead of encode, if that seems
-
# more clear to you.
-
1
alias highlight encode
-
-
# The default file extension for this encoder.
-
1
def file_extension
-
self.class.file_extension
-
end
-
-
1
def << token
-
unless @@CODERAY_TOKEN_INTERFACE_DEPRECATION_WARNING_GIVEN
-
warn 'Using old Tokens#<< interface.'
-
@@CODERAY_TOKEN_INTERFACE_DEPRECATION_WARNING_GIVEN = true
-
end
-
self.token(*token)
-
end
-
-
# Called with +content+ and +kind+ of the currently scanned token.
-
# For simple scanners, it's enougth to implement this method.
-
#
-
# By default, it calls text_token, begin_group, end_group, begin_line,
-
# or end_line, depending on the +content+.
-
1
def token content, kind
-
case content
-
when String
-
text_token content, kind
-
when :begin_group
-
begin_group kind
-
when :end_group
-
end_group kind
-
when :begin_line
-
begin_line kind
-
when :end_line
-
end_line kind
-
else
-
raise ArgumentError, 'Unknown token content type: %p, kind = %p' % [content, kind]
-
end
-
end
-
-
# Called for each text token ([text, kind]), where text is a String.
-
1
def text_token text, kind
-
@out << text
-
end
-
-
# Starts a token group with the given +kind+.
-
1
def begin_group kind
-
end
-
-
# Ends a token group with the given +kind+.
-
1
def end_group kind
-
end
-
-
# Starts a new line token group with the given +kind+.
-
1
def begin_line kind
-
end
-
-
# Ends a new line token group with the given +kind+.
-
1
def end_line kind
-
end
-
-
1
protected
-
-
# Called with merged options before encoding starts.
-
# Sets @out to an empty string.
-
#
-
# See the HTML Encoder for an example of option caching.
-
1
def setup options
-
@out = get_output(options)
-
end
-
-
1
def get_output options
-
options[:out] || ''
-
end
-
-
# Append data.to_s to the output. Returns the argument.
-
1
def output data
-
@out << data.to_s
-
data
-
end
-
-
# Called with merged options after encoding starts.
-
# The return value is the result of encoding, typically @out.
-
1
def finish options
-
@out
-
end
-
-
# Do the encoding.
-
#
-
# The already created +tokens+ object must be used; it must be a
-
# Tokens object.
-
1
def compile tokens, options = {}
-
content = nil
-
for item in tokens
-
if item.is_a? Array
-
raise ArgumentError, 'Two-element array tokens are no longer supported.'
-
end
-
if content
-
token content, item
-
content = nil
-
else
-
content = item
-
end
-
end
-
raise 'odd number list for Tokens' if content
-
end
-
-
1
alias tokens compile
-
1
public :tokens
-
-
end
-
-
end
-
end
-
1
module CodeRay
-
1
module Encoders
-
-
1
map \
-
:loc => :lines_of_code,
-
:plain => :text,
-
:plaintext => :text,
-
:remove_comments => :comment_filter,
-
:stats => :statistic,
-
:term => :terminal,
-
:tty => :terminal,
-
:yml => :yaml
-
-
# No default because Tokens#nonsense should raise NoMethodError.
-
-
end
-
end
-
1
module CodeRay
-
1
module Encoders
-
-
# Outputs code highlighted for a color terminal.
-
#
-
# Note: This encoder is in beta. It currently doesn't use the Styles.
-
#
-
# Alias: +term+
-
#
-
# == Authors & License
-
#
-
# By Rob Aldred (http://robaldred.co.uk)
-
#
-
# Based on idea by Nathan Weizenbaum (http://nex-3.com)
-
#
-
# MIT License (http://www.opensource.org/licenses/mit-license.php)
-
1
class Terminal < Encoder
-
-
1
register_for :terminal
-
-
1
TOKEN_COLORS = {
-
:debug => "\e[1;37;44m",
-
-
:annotation => "\e[34m",
-
:attribute_name => "\e[35m",
-
:attribute_value => "\e[31m",
-
:binary => {
-
:self => "\e[31m",
-
:char => "\e[1;31m",
-
:delimiter => "\e[1;31m",
-
},
-
:char => {
-
:self => "\e[35m",
-
:delimiter => "\e[1;35m"
-
},
-
:class => "\e[1;35;4m",
-
:class_variable => "\e[36m",
-
:color => "\e[32m",
-
:comment => {
-
:self => "\e[1;30m",
-
:char => "\e[37m",
-
:delimiter => "\e[37m",
-
},
-
:constant => "\e[1;34;4m",
-
:decorator => "\e[35m",
-
:definition => "\e[1;33m",
-
:directive => "\e[33m",
-
:docstring => "\e[31m",
-
:doctype => "\e[1;34m",
-
:done => "\e[1;30;2m",
-
:entity => "\e[31m",
-
:error => "\e[1;37;41m",
-
:exception => "\e[1;31m",
-
:float => "\e[1;35m",
-
:function => "\e[1;34m",
-
:global_variable => "\e[1;32m",
-
:hex => "\e[1;36m",
-
:id => "\e[1;34m",
-
:include => "\e[31m",
-
:integer => "\e[1;34m",
-
:imaginary => "\e[1;34m",
-
:important => "\e[1;31m",
-
:key => {
-
:self => "\e[35m",
-
:char => "\e[1;35m",
-
:delimiter => "\e[1;35m",
-
},
-
:keyword => "\e[32m",
-
:label => "\e[1;33m",
-
:local_variable => "\e[33m",
-
:namespace => "\e[1;35m",
-
:octal => "\e[1;34m",
-
:predefined => "\e[36m",
-
:predefined_constant => "\e[1;36m",
-
:predefined_type => "\e[1;32m",
-
:preprocessor => "\e[1;36m",
-
:pseudo_class => "\e[1;34m",
-
:regexp => {
-
:self => "\e[35m",
-
:delimiter => "\e[1;35m",
-
:modifier => "\e[35m",
-
:char => "\e[1;35m",
-
},
-
:reserved => "\e[32m",
-
:shell => {
-
:self => "\e[33m",
-
:char => "\e[1;33m",
-
:delimiter => "\e[1;33m",
-
:escape => "\e[1;33m",
-
},
-
:string => {
-
:self => "\e[31m",
-
:modifier => "\e[1;31m",
-
:char => "\e[1;35m",
-
:delimiter => "\e[1;31m",
-
:escape => "\e[1;31m",
-
},
-
:symbol => {
-
:self => "\e[33m",
-
:delimiter => "\e[1;33m",
-
},
-
:tag => "\e[32m",
-
:type => "\e[1;34m",
-
:value => "\e[36m",
-
:variable => "\e[34m",
-
-
:insert => {
-
:self => "\e[42m",
-
:insert => "\e[1;32;42m",
-
:eyecatcher => "\e[102m",
-
},
-
:delete => {
-
:self => "\e[41m",
-
:delete => "\e[1;31;41m",
-
:eyecatcher => "\e[101m",
-
},
-
:change => {
-
:self => "\e[44m",
-
:change => "\e[37;44m",
-
},
-
:head => {
-
:self => "\e[45m",
-
:filename => "\e[37;45m"
-
},
-
}
-
-
1
TOKEN_COLORS[:keyword] = TOKEN_COLORS[:reserved]
-
1
TOKEN_COLORS[:method] = TOKEN_COLORS[:function]
-
1
TOKEN_COLORS[:escape] = TOKEN_COLORS[:delimiter]
-
-
1
protected
-
-
1
def setup(options)
-
super
-
@opened = []
-
@color_scopes = [TOKEN_COLORS]
-
end
-
-
1
public
-
-
1
def text_token text, kind
-
if color = @color_scopes.last[kind]
-
color = color[:self] if color.is_a? Hash
-
-
@out << color
-
@out << (text.index("\n") ? text.gsub("\n", "\e[0m\n" + color) : text)
-
@out << "\e[0m"
-
if outer_color = @color_scopes.last[:self]
-
@out << outer_color
-
end
-
else
-
@out << text
-
end
-
end
-
-
1
def begin_group kind
-
@opened << kind
-
@out << open_token(kind)
-
end
-
1
alias begin_line begin_group
-
-
1
def end_group kind
-
if @opened.pop
-
@color_scopes.pop
-
@out << "\e[0m"
-
if outer_color = @color_scopes.last[:self]
-
@out << outer_color
-
end
-
end
-
end
-
-
1
def end_line kind
-
@out << (@line_filler ||= "\t" * 100)
-
end_group kind
-
end
-
-
1
private
-
-
1
def open_token kind
-
if color = @color_scopes.last[kind]
-
if color.is_a? Hash
-
@color_scopes << color
-
color[:self]
-
else
-
@color_scopes << @color_scopes.last
-
color
-
end
-
else
-
@color_scopes << @color_scopes.last
-
''
-
end
-
end
-
end
-
end
-
end
-
1
module CodeRay
-
-
# = PluginHost
-
#
-
# A simple subclass/subfolder plugin system.
-
#
-
# Example:
-
# class Generators
-
# extend PluginHost
-
# plugin_path 'app/generators'
-
# end
-
#
-
# class Generator
-
# extend Plugin
-
# PLUGIN_HOST = Generators
-
# end
-
#
-
# class FancyGenerator < Generator
-
# register_for :fancy
-
# end
-
#
-
# Generators[:fancy] #-> FancyGenerator
-
# # or
-
# CodeRay.require_plugin 'Generators/fancy'
-
# # or
-
# Generators::Fancy
-
1
module PluginHost
-
-
# Raised if Encoders::[] fails because:
-
# * a file could not be found
-
# * the requested Plugin is not registered
-
1
PluginNotFound = Class.new LoadError
-
1
HostNotFound = Class.new LoadError
-
-
1
PLUGIN_HOSTS = []
-
1
PLUGIN_HOSTS_BY_ID = {} # dummy hash
-
-
# Loads all plugins using list and load.
-
1
def load_all
-
for plugin in list
-
load plugin
-
end
-
end
-
-
# Returns the Plugin for +id+.
-
#
-
# Example:
-
# yaml_plugin = MyPluginHost[:yaml]
-
1
def [] id, *args, &blk
-
1
plugin = validate_id(id)
-
begin
-
1
plugin = plugin_hash.[](plugin, *args, &blk)
-
1
end while plugin.is_a? String
-
1
plugin
-
end
-
-
1
alias load []
-
-
# Tries to +load+ the missing plugin by translating +const+ to the
-
# underscore form (eg. LinesOfCode becomes lines_of_code).
-
1
def const_missing const
-
1
id = const.to_s.
-
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
-
gsub(/([a-z\d])([A-Z])/,'\1_\2').
-
downcase
-
1
load id
-
end
-
-
1
class << self
-
-
# Adds the module/class to the PLUGIN_HOSTS list.
-
1
def extended mod
-
1
PLUGIN_HOSTS << mod
-
end
-
-
end
-
-
# The path where the plugins can be found.
-
1
def plugin_path *args
-
3
unless args.empty?
-
1
@plugin_path = File.expand_path File.join(*args)
-
end
-
3
@plugin_path ||= ''
-
end
-
-
# Map a plugin_id to another.
-
#
-
# Usage: Put this in a file plugin_path/_map.rb.
-
#
-
# class MyColorHost < PluginHost
-
# map :navy => :dark_blue,
-
# :maroon => :brown,
-
# :luna => :moon
-
# end
-
1
def map hash
-
1
for from, to in hash
-
8
from = validate_id from
-
8
to = validate_id to
-
8
plugin_hash[from] = to unless plugin_hash.has_key? from
-
end
-
end
-
-
# Define the default plugin to use when no plugin is found
-
# for a given id, or return the default plugin.
-
#
-
# See also map.
-
#
-
# class MyColorHost < PluginHost
-
# map :navy => :dark_blue
-
# default :gray
-
# end
-
#
-
# MyColorHost.default # loads and returns the Gray plugin
-
1
def default id = nil
-
if id
-
id = validate_id id
-
raise "The default plugin can't be named \"default\"." if id == :default
-
plugin_hash[:default] = id
-
else
-
load :default
-
end
-
end
-
-
# Every plugin must register itself for +id+ by calling register_for,
-
# which calls this method.
-
#
-
# See Plugin#register_for.
-
1
def register plugin, id
-
1
plugin_hash[validate_id(id)] = plugin
-
end
-
-
# A Hash of plugion_id => Plugin pairs.
-
1
def plugin_hash
-
19
@plugin_hash ||= (@plugin_hash = make_plugin_hash).tap { load_plugin_map }
-
end
-
-
# Returns an array of all .rb files in the plugin path.
-
#
-
# The extension .rb is not included.
-
1
def list
-
Dir[path_to('*')].select do |file|
-
File.basename(file)[/^(?!_)\w+\.rb$/]
-
end.map do |file|
-
File.basename(file, '.rb').to_sym
-
end
-
end
-
-
# Returns an array of all Plugins.
-
#
-
# Note: This loads all plugins using load_all.
-
1
def all_plugins
-
load_all
-
plugin_hash.values.grep(Class)
-
end
-
-
# Loads the map file (see map).
-
#
-
# This is done automatically when plugin_path is called.
-
1
def load_plugin_map
-
1
mapfile = path_to '_map'
-
1
if File.exist? mapfile
-
1
require mapfile
-
1
true
-
else
-
false
-
end
-
end
-
-
1
protected
-
-
# Return a plugin hash that automatically loads plugins.
-
1
def make_plugin_hash
-
1
Hash.new do |h, plugin_id|
-
1
id = validate_id(plugin_id)
-
1
path = path_to id
-
1
begin
-
1
require path
-
rescue LoadError => boom
-
if h.has_key?(:default)
-
h[:default]
-
else
-
raise PluginNotFound, '%p could not load plugin %p: %s' % [self, id, boom]
-
end
-
else
-
# Plugin should have registered by now
-
1
if h.has_key? id
-
1
h[id]
-
else
-
raise PluginNotFound, "No #{self.name} plugin for #{id.inspect} found in #{path}."
-
end
-
end
-
end
-
end
-
-
# Returns the expected path to the plugin file for the given id.
-
1
def path_to plugin_id
-
2
File.join plugin_path, "#{plugin_id}.rb"
-
end
-
-
# Converts +id+ to a valid plugin ID String, or returns +nil+.
-
#
-
# Raises +ArgumentError+ for all other objects, or if the
-
# given String includes non-alphanumeric characters (\W).
-
1
def validate_id id
-
19
case id
-
when Symbol
-
17
id.to_s
-
when String
-
2
if id[/\w+/] == id
-
2
id.downcase
-
else
-
raise ArgumentError, "Invalid id given: #{id}"
-
end
-
else
-
raise ArgumentError, "Symbol or String expected, but #{id.class} given."
-
end
-
end
-
-
end
-
-
-
# = Plugin
-
#
-
# Plugins have to include this module.
-
#
-
# IMPORTANT: Use extend for this module.
-
#
-
# See CodeRay::PluginHost for examples.
-
1
module Plugin
-
-
1
attr_reader :plugin_id
-
-
# Register this class for the given +id+.
-
#
-
# Example:
-
# class MyPlugin < PluginHost::BaseClass
-
# register_for :my_id
-
# ...
-
# end
-
#
-
# See PluginHost.register.
-
1
def register_for id
-
1
@plugin_id = id
-
1
plugin_host.register self, id
-
end
-
-
# Returns the title of the plugin, or sets it to the
-
# optional argument +title+.
-
1
def title title = nil
-
if title
-
@title = title.to_s
-
else
-
@title ||= name[/([^:]+)$/, 1]
-
end
-
end
-
-
# The PluginHost for this Plugin class.
-
1
def plugin_host host = nil
-
2
if host.is_a? PluginHost
-
1
const_set :PLUGIN_HOST, host
-
end
-
2
self::PLUGIN_HOST
-
end
-
-
1
def aliases
-
plugin_host.plugin_hash.inject [] do |aliases, (key, _)|
-
aliases << key if plugin_host[key] == self
-
aliases
-
end
-
end
-
-
end
-
-
end
-
1
module CodeRay
-
1
VERSION = '1.1.0'
-
end
-
1
require 'coffee-script'
-
1
require 'coffee/rails/engine'
-
1
require 'coffee/rails/template_handler'
-
1
require 'coffee/rails/version'
-
1
require 'rails/engine'
-
-
1
module Coffee
-
1
module Rails
-
1
class Engine < ::Rails::Engine
-
1
config.app_generators.javascript_engine :coffee
-
end
-
end
-
end
-
1
module Coffee
-
1
module Rails
-
1
class TemplateHandler
-
1
def self.erb_handler
-
@@erb_handler ||= ActionView::Template.registered_template_handler(:erb)
-
end
-
-
1
def self.call(template)
-
compiled_source = erb_handler.call(template)
-
"CoffeeScript.compile(begin;#{compiled_source};end)"
-
end
-
end
-
end
-
end
-
-
1
ActiveSupport.on_load(:action_view) do
-
1
ActionView::Template.register_template_handler :coffee, Coffee::Rails::TemplateHandler
-
end
-
1
module Coffee
-
1
module Rails
-
1
VERSION = "4.0.1"
-
end
-
end
-
1
require 'coffee_script'
-
1
require 'execjs'
-
1
require 'coffee_script/source'
-
-
1
module CoffeeScript
-
1
Error = ExecJS::Error
-
1
EngineError = ExecJS::RuntimeError
-
1
CompilationError = ExecJS::ProgramError
-
-
1
module Source
-
1
def self.path
-
@path ||= ENV['COFFEESCRIPT_SOURCE_PATH'] || bundled_path
-
end
-
-
1
def self.path=(path)
-
@contents = @version = @bare_option = @context = nil
-
@path = path
-
end
-
-
1
def self.contents
-
@contents ||= File.read(path)
-
end
-
-
1
def self.version
-
@version ||= contents[/CoffeeScript Compiler v([\d.]+)/, 1]
-
end
-
-
1
def self.bare_option
-
@bare_option ||= contents.match(/noWrap/) ? 'noWrap' : 'bare'
-
end
-
-
1
def self.context
-
@context ||= ExecJS.compile(contents)
-
end
-
end
-
-
1
class << self
-
1
def engine
-
end
-
-
1
def engine=(engine)
-
end
-
-
1
def version
-
Source.version
-
end
-
-
# Compile a script (String or IO) to JavaScript.
-
1
def compile(script, options = {})
-
script = script.read if script.respond_to?(:read)
-
-
if options.key?(:bare)
-
elsif options.key?(:no_wrap)
-
options[:bare] = options[:no_wrap]
-
else
-
options[:bare] = false
-
end
-
-
wrapper = <<-WRAPPER
-
(function(script, options) {
-
try {
-
return CoffeeScript.compile(script, options);
-
} catch (err) {
-
if (err instanceof SyntaxError && err.location) {
-
throw new SyntaxError([
-
err.filename || "[stdin]",
-
err.location.first_line + 1,
-
err.location.first_column + 1
-
].join(":") + ": " + err.message)
-
} else {
-
throw err;
-
}
-
}
-
})
-
WRAPPER
-
-
Source.context.call(wrapper, script, options)
-
end
-
end
-
end
-
1
module CoffeeScript
-
1
module Source
-
1
def self.bundled_path
-
File.expand_path("../coffee-script.js", __FILE__)
-
end
-
end
-
end
-
# Module to format an Array into a single string with embedded
-
# newlines, On printing the string, the columns are aligned.
-
#
-
# == Summary
-
#
-
# Return a string from an array with embedded newlines formatted so
-
# that when printed the columns are aligned.
-
# See below for examples and options to the main method +columnize+.
-
#
-
#
-
# == License
-
#
-
# Columnize is copyright (C) 2007-2011, 2013 Rocky Bernstein
-
# <rockyb@rubyforge.net>
-
#
-
# All rights reserved. You can redistribute and/or modify it under
-
# the same terms as Ruby.
-
#
-
# Also available in Python (columnize), and Perl (Array::Columnize)
-
-
1
module Columnize
-
# Pull in the rest of my pieces
-
1
ROOT_DIR = File.dirname(__FILE__)
-
1
%w(opts columnize version).each do |submod|
-
3
require File.join %W(#{ROOT_DIR} columnize #{submod})
-
end
-
-
# Add +columnize_opts+ instance variable to classes that mix in this module. The type should be a kind of hash in file +columnize/opts+.
-
1
attr_accessor :columnize_opts
-
-
# Columnize.columize([args]) => String
-
#
-
# Return a string from an array with embedded newlines formatted so
-
# that when printed the columns are aligned.
-
#
-
# For example, for a line width of 4 characters (arranged vertically):
-
# a = (1..4).to_a
-
# Columnize.columnize(a) => '1 3\n2 4\n'
-
#
-
# Alternatively:
-
# a.columnize => '1 3\n2 4\n'
-
#
-
# Arranged horizontally:
-
# a.columnize(:arrange_vertical => false) =>
-
# ['1', '2,', '3', '4'] => '1 2\n3 4\n'
-
#
-
# Formatted as an array using format specifier '%02d':
-
# puts (1..10).to_a.columnize(:arrange_array => true, :colfmt => '%02d',
-
# :displaywidth => 10) =>
-
# [01, 02,
-
# 03, 04,
-
# 05, 06,
-
# 07, 08,
-
# 09, 10,
-
# ]
-
#
-
# Each column is only as wide as necessary. By default, columns are
-
# separated by two spaces. Options are available for setting
-
# * the line display width
-
# * a column separator
-
# * a line prefix
-
# * a line suffix
-
# * A format specify for formatting each item each array item to a string
-
# * whether to ignore terminal codes in text size calculation
-
# * whether to left justify text instead of right justify
-
# * whether to format as an array - with surrounding [] and
-
# separating ', '
-
1
def self.columnize(*args)
-
list = args.shift
-
opts = parse_columnize_options(args)
-
Columnizer.new(list, opts).columnize
-
end
-
-
# Adds columnize_opts to the singleton level of included class
-
1
def self.included(base)
-
# screw class variables, we'll use an instance variable on the class singleton
-
6
class << base
-
6
attr_accessor :columnize_opts
-
end
-
6
base.columnize_opts = DEFAULT_OPTS.dup
-
end
-
-
1
def columnize(*args)
-
return Columnize.columnize(*args) if args.length > 1
-
opts = args.empty? ? {} : args.pop
-
@columnize_opts ||= self.class.columnize_opts.dup
-
@columnizer ||= Columnizer.new(self, @columnize_opts)
-
# make sure that any changes to list or opts get passed to columnizer
-
@columnizer.list = self unless @columnizer.list == self
-
@columnizer.opts = @columnize_opts.merge(opts) unless @columnizer.opts == @columnize_opts and opts.empty?
-
@columnizer.columnize
-
end
-
end
-
-
# Mix Columnize into Array
-
1
Array.send :include, Columnize
-
-
# Demo this sucker
-
1
if __FILE__ == $0
-
# include Columnize
-
-
a = (1..80).to_a
-
puts a.columnize :arrange_array => true
-
puts '=' * 50
-
-
b = (1..10).to_a
-
puts b.columnize(:displaywidth => 10)
-
-
puts '-' * 50
-
puts b.columnize(:arrange_array => true, :colfmt => '%02d', :displaywidth => 10)
-
-
[[4, 4], [4, 7], [100, 80]].each do |width, num|
-
data = (1..num).map{|i| i }
-
[[false, 'horizontal'], [true, 'vertical']].each do |bool, dir|
-
puts "Width: #{width}, direction: #{dir}"
-
print Columnize.columnize(data, :displaywidth => width, :colsep => ' ', :arrange_vertical => bool, :ljust => :auto)
-
end
-
end
-
-
puts Columnize.columnize(5)
-
puts Columnize.columnize([])
-
puts Columnize.columnize(["a", 2, "c"], :displaywidth =>10, :colsep => ', ')
-
puts Columnize.columnize(["oneitem"])
-
puts Columnize.columnize(["one", "two", "three"])
-
data = ["one", "two", "three",
-
"for", "five", "six",
-
"seven", "eight", "nine",
-
"ten", "eleven", "twelve",
-
"thirteen", "fourteen", "fifteen",
-
"sixteen", "seventeen", "eightteen",
-
"nineteen", "twenty", "twentyone",
-
"twentytwo", "twentythree", "twentyfour",
-
"twentyfive","twentysix", "twentyseven"]
-
-
puts Columnize.columnize(data)
-
puts Columnize.columnize(data, 80, ' ', false)
-
end
-
# Copyright (C) 2007-2011, 2013 Rocky Bernstein
-
# <rockyb@rubyforge.net>
-
#
-
# Part of Columnize to format in either direction
-
1
module Columnize
-
1
class Columnizer
-
1
ARRANGE_ARRAY_OPTS = {:array_prefix => '[', :line_prefix => ' ', :line_suffix => ',', :array_suffix => ']', :colsep => ', ', :arrange_vertical => false}
-
1
OLD_AND_NEW_KEYS = {:lineprefix => :line_prefix, :linesuffix => :line_suffix}
-
# TODO: change colfmt to cell_format; change colsep to something else
-
1
ATTRS = [:arrange_vertical, :array_prefix, :array_suffix, :line_prefix, :line_suffix, :colfmt, :colsep, :displaywidth, :ljust]
-
-
1
attr_reader :list, :opts
-
-
1
def initialize(list=[], opts={})
-
self.list = list
-
self.opts = DEFAULT_OPTS.merge(opts)
-
end
-
-
1
def list=(list)
-
@list = list
-
if @list.is_a? Array
-
@short_circuit = @list.empty? ? "<empty>\n" : nil
-
else
-
@short_circuit = ''
-
@list = []
-
end
-
end
-
-
# TODO: freeze @opts
-
1
def opts=(opts)
-
@opts = opts
-
OLD_AND_NEW_KEYS.each {|old, new| @opts[new] = @opts.delete(old) if @opts.keys.include?(old) and !@opts.keys.include?(new) }
-
@opts.merge!(ARRANGE_ARRAY_OPTS) if @opts[:arrange_array]
-
set_attrs_from_opts
-
end
-
-
1
def update_opts(opts)
-
self.opts = @opts.merge(opts)
-
end
-
-
1
def columnize
-
return @short_circuit if @short_circuit
-
-
rows, colwidths = min_rows_and_colwidths
-
ncols = colwidths.length
-
justify = lambda {|t, c|
-
@ljust ? t.ljust(colwidths[c]) : t.rjust(colwidths[c])
-
}
-
textify = lambda do |row|
-
row.map!.with_index(&justify) unless ncols == 1 && @ljust
-
"#{@line_prefix}#{row.join(@colsep)}#{@line_suffix}"
-
end
-
-
text = rows.map(&textify)
-
text.first.sub!(/^#{@line_prefix}/, @array_prefix) unless @array_prefix.empty?
-
text.last.sub!(/#{@line_suffix}$/, @array_suffix) unless @array_suffix.empty?
-
text.join("\n") # + "\n" # if we want extra separation
-
end
-
-
# TODO: make this a method, rather than a function (?)
-
# compute the smallest number of rows and the max widths for each column
-
1
def min_rows_and_colwidths
-
list = @list.map &@stringify
-
cell_widths = list.map(&@term_adjuster).map(&:size)
-
-
# Set default arrangement: one atom per row
-
cell_width_max = cell_widths.max
-
result = [arrange_by_row(list, list.size, 1), [cell_width_max]]
-
-
# If any atom > @displaywidth, stop and use one atom per row.
-
return result if cell_width_max > @displaywidth
-
-
# For horizontal arrangement, we want to *maximize* the number
-
# of columns. Thus the candidate number of rows (+sizes+) starts
-
# at the minumum number of rows, 1, and increases.
-
-
# For vertical arrangement, we want to *minimize* the number of
-
# rows. So here the candidate number of columns (+sizes+) starts
-
# at the maximum number of columns, list.length, and
-
# decreases. Also the roles of columns and rows are reversed
-
# from horizontal arrangement.
-
-
# Loop from most compact arrangement to least compact, stopping
-
# at the first successful packing. The below code is tricky,
-
# but very cool.
-
#
-
# FIXME: In the below code could be DRY'd. (The duplication got
-
# introduced when I revised the code - rocky)
-
if @arrange_vertical
-
(1..list.length).each do |size|
-
other_size = (list.size + size - 1) / size
-
colwidths = arrange_by_row(cell_widths, other_size, size).map(&:max)
-
totwidth = colwidths.inject(&:+) + ((colwidths.length-1) * @colsep.length)
-
return [arrange_by_column(list, other_size, size), colwidths] if
-
totwidth <= @displaywidth
-
end
-
else
-
list.length.downto(1).each do |size|
-
other_size = (list.size + size - 1) / size
-
colwidths = arrange_by_column(cell_widths, other_size, size).map(&:max)
-
totwidth = colwidths.inject(&:+) + ((colwidths.length-1) * @colsep.length)
-
return [arrange_by_row(list, other_size, size), colwidths] if
-
totwidth <= @displaywidth
-
end
-
end
-
result
-
end
-
-
# Given +list+, +ncols+, +nrows+, arrange the one-dimensional
-
# array into a 2-dimensional lists of lists organized by rows.
-
#
-
# In either horizontal or vertical arrangement, we will need to
-
# access this for the list data or for the width
-
# information.
-
#
-
# Here is an example:
-
# arrange_by_row((1..5).to_a, 3, 2) =>
-
# [[1,2], [3,4], [5]],
-
1
def arrange_by_row(list, nrows, ncols)
-
(0...nrows).map {|r| list[r*ncols, ncols] }.compact
-
end
-
-
# Given +list+, +ncols+, +nrows+, arrange the one-dimensional
-
# array into a 2-dimensional lists of lists organized by columns.
-
#
-
# In either horizontal or vertical arrangement, we will need to
-
# access this for the list data or for the width
-
# information.
-
#
-
# Here is an example:
-
# arrange_by_column((1..5).to_a, 2, 3) =>
-
# [[1,3,5], [2,4]]
-
1
def arrange_by_column(list, nrows, ncols)
-
(0...ncols).map do |i|
-
(0..nrows-1).inject([]) do |row, j|
-
k = i + (j * ncols)
-
k < list.length ? row << list[k] : row
-
end
-
end
-
end
-
-
1
def set_attrs_from_opts
-
ATTRS.each {|attr| self.instance_variable_set "@#{attr}", @opts[attr] }
-
-
@ljust = !@list.all? {|datum| datum.kind_of?(Numeric)} if @ljust == :auto
-
@displaywidth -= @line_prefix.length
-
@displaywidth = @line_prefix.length + 4 if @displaywidth < 4
-
@stringify = @colfmt ? lambda {|li| @colfmt % li } : lambda {|li| li.to_s }
-
@term_adjuster = @opts[:term_adjust] ? lambda {|c| c.gsub(/\e\[.*?m/, '') } : lambda {|c| c }
-
end
-
end
-
end
-
-
# Demo
-
1
if __FILE__ == $0
-
Columnize::DEFAULT_OPTS = {:line_prefix => '', :displaywidth => 80}
-
puts Columnize::Columnizer.new.arrange_by_row((1..5).to_a, 2, 3).inspect
-
puts Columnize::Columnizer.new.arrange_by_column((1..5).to_a, 2, 3).inspect
-
end
-
1
module Columnize
-
1
computed_displaywidth = (ENV['COLUMNS'] || '80').to_i
-
1
computed_displaywidth = 80 unless computed_displaywidth >= 10
-
-
# When an option is not specified for the below keys, these are the defaults.
-
1
DEFAULT_OPTS = {
-
:arrange_array => false,
-
:arrange_vertical => true,
-
:array_prefix => '',
-
:array_suffix => '',
-
:colfmt => nil,
-
:colsep => ' ',
-
:displaywidth => computed_displaywidth,
-
:line_prefix => '',
-
:line_suffix => '',
-
:ljust => :auto,
-
:term_adjust => false
-
}
-
-
# Options parsing routine for Columnize::columnize. In the preferred
-
# newer style, +args+ is a hash where each key is one of the option names.
-
#
-
# In the older style positional arguments are used and the positions
-
# are in the order: +displaywidth+, +colsep+, +arrange_vertical+,
-
# +ljust+, and +line_prefix+.
-
1
def self.parse_columnize_options(args)
-
if 1 == args.size && args[0].kind_of?(Hash) # explicitly passed as a hash
-
args[0]
-
elsif !args.empty? # passed as ugly positional parameters.
-
Hash[args.zip([:displaywidth, :colsep, :arrange_vertical, :ljust, :line_prefix]).map(&:reverse)]
-
else
-
{}
-
end
-
end
-
end
-
# Sets constant Columnize::VERSION, the version number of
-
# this package. It is used in Gem creation but can also be consulted after
-
# require'ing 'columnize'.
-
1
module Columnize
-
# The current version of this package
-
1
VERSION = '0.8.9'
-
end
-
1
require_relative 'connection_pool/version'
-
1
require_relative 'connection_pool/timed_stack'
-
-
# Generic connection pool class for e.g. sharing a limited number of network connections
-
# among many threads. Note: Connections are eager created.
-
#
-
# Example usage with block (faster):
-
#
-
# @pool = ConnectionPool.new { Redis.new }
-
#
-
# @pool.with do |redis|
-
# redis.lpop('my-list') if redis.llen('my-list') > 0
-
# end
-
#
-
# Using optional timeout override (for that single invocation)
-
#
-
# @pool.with(:timeout => 2.0) do |redis|
-
# redis.lpop('my-list') if redis.llen('my-list') > 0
-
# end
-
#
-
# Example usage replacing an existing connection (slower):
-
#
-
# $redis = ConnectionPool.wrap { Redis.new }
-
#
-
# def do_work
-
# $redis.lpop('my-list') if $redis.llen('my-list') > 0
-
# end
-
#
-
# Accepts the following options:
-
# - :size - number of connections to pool, defaults to 5
-
# - :timeout - amount of time to wait for a connection if none currently available, defaults to 5 seconds
-
#
-
1
class ConnectionPool
-
1
DEFAULTS = {size: 5, timeout: 5}
-
-
1
class Error < RuntimeError
-
end
-
-
1
def self.wrap(options, &block)
-
Wrapper.new(options, &block)
-
end
-
-
1
def initialize(options = {}, &block)
-
raise ArgumentError, 'Connection pool requires a block' unless block
-
-
options = DEFAULTS.merge(options)
-
-
@size = options.fetch(:size)
-
@timeout = options.fetch(:timeout)
-
-
@available = TimedStack.new(@size, &block)
-
@key = :"current-#{@available.object_id}"
-
end
-
-
1
def with(options = {})
-
conn = checkout(options)
-
begin
-
yield conn
-
ensure
-
checkin
-
end
-
end
-
-
1
def checkout(options = {})
-
stack = ::Thread.current[@key] ||= []
-
-
if stack.empty?
-
timeout = options[:timeout] || @timeout
-
conn = @available.pop(timeout)
-
else
-
conn = stack.last
-
end
-
-
stack.push conn
-
conn
-
end
-
-
1
def checkin
-
stack = ::Thread.current[@key]
-
raise ConnectionPool::Error, 'no connections are checked out' if
-
!stack || stack.empty?
-
-
conn = stack.pop
-
if stack.empty?
-
@available << conn
-
end
-
nil
-
end
-
-
1
def shutdown(&block)
-
@available.shutdown(&block)
-
end
-
-
1
class Wrapper < ::BasicObject
-
1
METHODS = [:with, :pool_shutdown]
-
-
1
def initialize(options = {}, &block)
-
@pool = ::ConnectionPool.new(options, &block)
-
end
-
-
1
def with
-
conn = @pool.checkout
-
begin
-
yield conn
-
ensure
-
@pool.checkin
-
end
-
end
-
-
1
def pool_shutdown(&block)
-
@pool.shutdown(&block)
-
end
-
-
1
def respond_to?(id, *args)
-
METHODS.include?(id) || @pool.with { |c| c.respond_to?(id, *args) }
-
end
-
-
1
def method_missing(name, *args, &block)
-
@pool.with do |connection|
-
connection.send(name, *args, &block)
-
end
-
end
-
end
-
end
-
1
require 'thread'
-
1
require 'timeout'
-
-
1
class ConnectionPool::PoolShuttingDownError < RuntimeError; end
-
-
1
class ConnectionPool::TimedStack
-
-
1
def initialize(size = 0, &block)
-
@create_block = block
-
@created = 0
-
@que = []
-
@max = size
-
@mutex = Mutex.new
-
@resource = ConditionVariable.new
-
@shutdown_block = nil
-
end
-
-
1
def push(obj)
-
@mutex.synchronize do
-
if @shutdown_block
-
@shutdown_block.call(obj)
-
else
-
@que.push obj
-
end
-
-
@resource.broadcast
-
end
-
end
-
1
alias_method :<<, :push
-
-
1
def pop(timeout=0.5)
-
deadline = Time.now + timeout
-
@mutex.synchronize do
-
loop do
-
raise ConnectionPool::PoolShuttingDownError if @shutdown_block
-
return @que.pop unless @que.empty?
-
unless @created == @max
-
@created += 1
-
return @create_block.call
-
end
-
to_wait = deadline - Time.now
-
raise Timeout::Error, "Waited #{timeout} sec" if to_wait <= 0
-
@resource.wait(@mutex, to_wait)
-
end
-
end
-
end
-
-
1
def shutdown(&block)
-
raise ArgumentError, "shutdown must receive a block" unless block_given?
-
-
@mutex.synchronize do
-
@shutdown_block = block
-
@resource.broadcast
-
-
@que.size.times do
-
conn = @que.pop
-
block.call(conn)
-
end
-
end
-
end
-
-
1
def empty?
-
(@created - @que.length) >= @max
-
end
-
-
1
def length
-
@max - @created + @que.length
-
end
-
end
-
1
class ConnectionPool
-
1
VERSION = "2.0.0"
-
end
-
1
module Crack
-
1
class ParseError < StandardError; end
-
end
-
-
1
require 'crack/util'
-
1
require 'crack/json'
-
1
require 'crack/xml'
-
# Copyright (c) 2004-2008 David Heinemeier Hansson
-
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
-
# The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-
1
require 'safe_yaml/load'
-
1
require 'strscan'
-
-
1
module Crack
-
1
class JSON
-
1
def self.parser_exceptions
-
@parser_exceptions ||= begin
-
exceptions = [ArgumentError]
-
-
if const_defined?(:Psych)
-
if Psych.const_defined?(:SyntaxError)
-
exceptions << Psych::SyntaxError
-
end
-
end
-
-
exceptions
-
end
-
end
-
-
1
def self.parse(json)
-
args = [unescape(convert_json_to_yaml(json))]
-
args << nil if SafeYAML::MULTI_ARGUMENT_YAML_LOAD
-
args << { :whitelisted_tags => ['!ruby/regexp'] }
-
-
SafeYAML.load(*args)
-
-
rescue *parser_exceptions
-
raise ParseError, "Invalid JSON string"
-
end
-
-
1
protected
-
1
def self.unescape(str)
-
# Force the encoding to be UTF-8 so we can perform regular expressions
-
# on 1.9.2 without blowing up.
-
# see http://stackoverflow.com/questions/1224204/ruby-mechanize-getting-force-encoding-exception for a similar issue
-
str.force_encoding('UTF-8') if defined?(Encoding) && str.respond_to?(:force_encoding)
-
str.gsub(/\\u0000/, "").gsub(/\\[u|U]([0-9a-fA-F]{4})/) { [$1.hex].pack("U") }
-
end
-
-
# matches YAML-formatted dates
-
1
DATE_REGEX = /^\d{4}-\d{2}-\d{2}$|^\d{4}-\d{1,2}-\d{1,2}[T \t]+\d{1,2}:\d{2}:\d{2}(\.[0-9]*)?(([ \t]*)Z|[-+]\d{2}?(:\d{2})?)$/
-
-
# Ensure that ":" and "," are always followed by a space
-
1
def self.convert_json_to_yaml(json) #:nodoc:
-
json = String.new(json) #can't modify a frozen string
-
scanner, quoting, marks, pos, date_starts, date_ends = StringScanner.new(json), false, [], nil, [], []
-
while scanner.scan_until(/(\\['"]|['":,\/\\]|\\.)/)
-
case char = scanner[1]
-
when '"', "'"
-
if !quoting
-
quoting = char
-
pos = scanner.pos
-
elsif quoting == char
-
if json[pos..scanner.pos-2] =~ DATE_REGEX
-
# found a date, track the exact positions of the quotes so we can remove them later.
-
# oh, and increment them for each current mark, each one is an extra padded space that bumps
-
# the position in the final YAML output
-
total_marks = marks.size
-
date_starts << pos+total_marks
-
date_ends << scanner.pos+total_marks
-
end
-
quoting = false
-
end
-
when "/"
-
if !quoting
-
json[scanner.pos - 1] = "!ruby/regexp /"
-
scanner.pos += 13
-
scanner.scan_until(/\/[mix]*/)
-
end
-
when ":",","
-
marks << scanner.pos - 1 unless quoting
-
when "\\"
-
scanner.skip(/\\/)
-
end
-
end
-
-
if marks.empty?
-
json.gsub(/\\\//, '/')
-
else
-
left_pos = [-1].push(*marks)
-
right_pos = marks << json.length
-
output = []
-
left_pos.each_with_index do |left, i|
-
output << json[left.succ..right_pos[i]]
-
end
-
output = output * " "
-
-
format_dates(output, date_starts, date_ends)
-
output.gsub!(/\\\//, '/')
-
output
-
end
-
end
-
-
1
def self.format_dates(output, date_starts, date_ends)
-
if YAML.constants.include?('Syck')
-
(date_starts + date_ends).each { |i| output[i-1] = ' ' }
-
else
-
date_starts.each { |i| output[i-2] = '!!timestamp ' }
-
end
-
end
-
end
-
end
-
1
module Crack
-
1
module Util
-
1
def snake_case(str)
-
return str.downcase if str =~ /^[A-Z]+$/
-
str.gsub(/([A-Z]+)(?=[A-Z][a-z]?)|\B[A-Z]/, '_\&') =~ /_*(.*)/
-
return $+.downcase
-
end
-
-
1
def to_xml_attributes(hash)
-
hash.map do |k,v|
-
%{#{Crack::Util.snake_case(k.to_s).sub(/^(.{1,1})/) { |m| m.downcase }}="#{v.to_s.gsub('"', '"')}"}
-
end.join(' ')
-
end
-
-
1
extend self
-
end
-
end
-
1
require 'rexml/parsers/streamparser'
-
1
require 'rexml/parsers/baseparser'
-
1
require 'rexml/light/node'
-
1
require 'rexml/text'
-
1
require "rexml/document"
-
1
require 'date'
-
1
require 'time'
-
1
require 'yaml'
-
1
require 'bigdecimal'
-
-
# The Reason behind redefining the String Class for this specific plugin is to
-
# avoid the dynamic insertion of stuff on it (see version previous to this commit).
-
# Doing that disables the possibility of efectuating a dump on the structure. This way it goes.
-
1
class REXMLUtiliyNodeString < String
-
1
attr_accessor :attributes
-
end
-
-
# This is a slighly modified version of the XMLUtilityNode from
-
# http://merb.devjavu.com/projects/merb/ticket/95 (has.sox@gmail.com)
-
# It's mainly just adding vowels, as I ht cd wth n vwls :)
-
# This represents the hard part of the work, all I did was change the
-
# underlying parser.
-
1
class REXMLUtilityNode #:nodoc:
-
1
attr_accessor :name, :attributes, :children, :type
-
-
1
def self.typecasts
-
11
@@typecasts
-
end
-
-
1
def self.typecasts=(obj)
-
1
@@typecasts = obj
-
end
-
-
1
def self.available_typecasts
-
@@available_typecasts
-
end
-
-
1
def self.available_typecasts=(obj)
-
1
@@available_typecasts = obj
-
end
-
-
1
self.typecasts = {}
-
1
self.typecasts["integer"] = lambda{|v| v.nil? ? nil : v.to_i}
-
1
self.typecasts["boolean"] = lambda{|v| v.nil? ? nil : (v.strip != "false")}
-
1
self.typecasts["datetime"] = lambda{|v| v.nil? ? nil : Time.parse(v).utc}
-
1
self.typecasts["date"] = lambda{|v| v.nil? ? nil : Date.parse(v)}
-
1
self.typecasts["dateTime"] = lambda{|v| v.nil? ? nil : Time.parse(v).utc}
-
1
self.typecasts["decimal"] = lambda{|v| v.nil? ? nil : BigDecimal(v.to_s)}
-
1
self.typecasts["double"] = lambda{|v| v.nil? ? nil : v.to_f}
-
1
self.typecasts["float"] = lambda{|v| v.nil? ? nil : v.to_f}
-
1
self.typecasts["string"] = lambda{|v| v.to_s}
-
1
self.typecasts["base64Binary"] = lambda{|v| v.unpack('m').first }
-
-
1
self.available_typecasts = self.typecasts.keys
-
-
1
def initialize(name, normalized_attributes = {})
-
-
# unnormalize attribute values
-
attributes = Hash[* normalized_attributes.map { |key, value|
-
[ key, unnormalize_xml_entities(value) ]
-
}.flatten]
-
-
@name = name.tr("-", "_")
-
# leave the type alone if we don't know what it is
-
@type = self.class.available_typecasts.include?(attributes["type"]) ? attributes.delete("type") : attributes["type"]
-
-
@nil_element = attributes.delete("nil") == "true"
-
@attributes = undasherize_keys(attributes)
-
@children = []
-
@text = false
-
end
-
-
1
def add_node(node)
-
@text = true if node.is_a? String
-
@children << node
-
end
-
-
1
def to_hash
-
# ACG: Added a check here to prevent an exception a type == "file" tag has nodes within it
-
if @type == "file" and (@children.first.nil? or @children.first.is_a?(String))
-
f = StringIO.new((@children.first || '').unpack('m').first)
-
class << f
-
attr_accessor :original_filename, :content_type
-
end
-
f.original_filename = attributes['name'] || 'untitled'
-
f.content_type = attributes['content_type'] || 'application/octet-stream'
-
return {name => f}
-
end
-
-
if @text
-
t = typecast_value( unnormalize_xml_entities( inner_html ) )
-
if t.is_a?(String)
-
t = REXMLUtiliyNodeString.new(t)
-
t.attributes = attributes
-
end
-
return { name => t }
-
else
-
#change repeating groups into an array
-
groups = @children.inject({}) { |s,e| (s[e.name] ||= []) << e; s }
-
-
out = nil
-
if @type == "array"
-
out = []
-
groups.each do |k, v|
-
if v.size == 1
-
out << v.first.to_hash.entries.first.last
-
else
-
out << v.map{|e| e.to_hash[k]}
-
end
-
end
-
out = out.flatten
-
-
else # If Hash
-
out = {}
-
groups.each do |k,v|
-
if v.size == 1
-
out.merge!(v.first)
-
else
-
out.merge!( k => v.map{|e| e.to_hash[k]})
-
end
-
end
-
out.merge! attributes unless attributes.empty?
-
out = out.empty? ? nil : out
-
end
-
-
if @type && out.nil?
-
{ name => typecast_value(out) }
-
else
-
{ name => out }
-
end
-
end
-
end
-
-
# Typecasts a value based upon its type. For instance, if
-
# +node+ has #type == "integer",
-
# {{[node.typecast_value("12") #=> 12]}}
-
#
-
# @param value<String> The value that is being typecast.
-
#
-
# @details [:type options]
-
# "integer"::
-
# converts +value+ to an integer with #to_i
-
# "boolean"::
-
# checks whether +value+, after removing spaces, is the literal
-
# "true"
-
# "datetime"::
-
# Parses +value+ using Time.parse, and returns a UTC Time
-
# "date"::
-
# Parses +value+ using Date.parse
-
#
-
# @return <Integer, TrueClass, FalseClass, Time, Date, Object>
-
# The result of typecasting +value+.
-
#
-
# @note
-
# If +self+ does not have a "type" key, or if it's not one of the
-
# options specified above, the raw +value+ will be returned.
-
1
def typecast_value(value)
-
return value unless @type
-
proc = self.class.typecasts[@type]
-
proc.nil? ? value : proc.call(value)
-
end
-
-
# Take keys of the form foo-bar and convert them to foo_bar
-
1
def undasherize_keys(params)
-
params.keys.each do |key, value|
-
params[key.tr("-", "_")] = params.delete(key)
-
end
-
params
-
end
-
-
# Get the inner_html of the REXML node.
-
1
def inner_html
-
@children.join
-
end
-
-
# Converts the node into a readable HTML node.
-
#
-
# @return <String> The HTML node in text form.
-
1
def to_html
-
attributes.merge!(:type => @type ) if @type
-
"<#{name}#{Crack::Util.to_xml_attributes(attributes)}>#{@nil_element ? '' : inner_html}</#{name}>"
-
end
-
-
# @alias #to_html #to_s
-
1
def to_s
-
to_html
-
end
-
-
1
private
-
-
1
def unnormalize_xml_entities value
-
REXML::Text.unnormalize(value)
-
end
-
end
-
-
1
module Crack
-
1
class REXMLParser
-
1
def self.parse(xml)
-
stack = []
-
parser = REXML::Parsers::BaseParser.new(xml)
-
-
while true
-
event = parser.pull
-
case event[0]
-
when :end_document
-
break
-
when :end_doctype, :start_doctype
-
# do nothing
-
when :start_element
-
stack.push REXMLUtilityNode.new(event[1], event[2])
-
when :end_element
-
if stack.size > 1
-
temp = stack.pop
-
stack.last.add_node(temp)
-
end
-
when :text, :cdata
-
stack.last.add_node(event[1]) unless event[1].strip.length == 0 || stack.empty?
-
end
-
end
-
-
stack.length > 0 ? stack.pop.to_hash : {}
-
end
-
end
-
-
1
class XML
-
1
def self.parser
-
@@parser ||= REXMLParser
-
end
-
-
1
def self.parser=(parser)
-
@@parser = parser
-
end
-
-
1
def self.parse(xml)
-
parser.parse(xml)
-
end
-
end
-
end
-
1
require 'dalli/compressor'
-
1
require 'dalli/client'
-
1
require 'dalli/ring'
-
1
require 'dalli/server'
-
1
require 'dalli/socket'
-
1
require 'dalli/version'
-
1
require 'dalli/options'
-
1
require 'dalli/railtie' if defined?(::Rails::Railtie)
-
-
1
module Dalli
-
# generic error
-
1
class DalliError < RuntimeError; end
-
# socket/server communication error
-
1
class NetworkError < DalliError; end
-
# no server available/alive error
-
1
class RingError < DalliError; end
-
# application error in marshalling serialization
-
1
class MarshalError < DalliError; end
-
# application error in marshalling deserialization or decompression
-
1
class UnmarshalError < DalliError; end
-
-
1
def self.logger
-
@logger ||= (rails_logger || default_logger)
-
end
-
-
1
def self.rails_logger
-
(defined?(Rails) && Rails.respond_to?(:logger) && Rails.logger) ||
-
(defined?(RAILS_DEFAULT_LOGGER) && RAILS_DEFAULT_LOGGER.respond_to?(:debug) && RAILS_DEFAULT_LOGGER)
-
end
-
-
1
def self.default_logger
-
require 'logger'
-
l = Logger.new(STDOUT)
-
l.level = Logger::INFO
-
l
-
end
-
-
1
def self.logger=(logger)
-
@logger = logger
-
end
-
-
end
-
-
1
if defined?(RAILS_VERSION) && RAILS_VERSION < '3'
-
raise Dalli::DalliError, "Dalli #{Dalli::VERSION} does not support Rails version < 3.0"
-
end
-
1
require 'digest/md5'
-
1
require 'set'
-
-
# encoding: ascii
-
1
module Dalli
-
1
class Client
-
-
##
-
# Dalli::Client is the main class which developers will use to interact with
-
# the memcached server. Usage:
-
#
-
# Dalli::Client.new(['localhost:11211:10', 'cache-2.example.com:11211:5', '192.168.0.1:22122:5'],
-
# :threadsafe => true, :failover => true, :expires_in => 300)
-
#
-
# servers is an Array of "host:port:weight" where weight allows you to distribute cache unevenly.
-
# Both weight and port are optional. If you pass in nil, Dalli will use the <tt>MEMCACHE_SERVERS</tt>
-
# environment variable or default to 'localhost:11211' if it is not present.
-
#
-
# Options:
-
# - :namespace - prepend each key with this value to provide simple namespacing.
-
# - :failover - if a server is down, look for and store values on another server in the ring. Default: true.
-
# - :threadsafe - ensure that only one thread is actively using a socket at a time. Default: true.
-
# - :expires_in - default TTL in seconds if you do not pass TTL as a parameter to an individual operation, defaults to 0 or forever
-
# - :compress - defaults to false, if true Dalli will compress values larger than 1024 bytes before sending them to memcached.
-
# - :serializer - defaults to Marshal
-
# - :compressor - defaults to zlib
-
#
-
1
def initialize(servers=nil, options={})
-
@servers = normalize_servers(servers || ENV["MEMCACHE_SERVERS"] || '127.0.0.1:11211')
-
@options = normalize_options(options)
-
@ring = nil
-
end
-
-
#
-
# The standard memcached instruction set
-
#
-
-
##
-
# Turn on quiet aka noreply support.
-
# All relevant operations within this block will be effectively
-
# pipelined as Dalli will use 'quiet' operations where possible.
-
# Currently supports the set, add, replace and delete operations.
-
1
def multi
-
old, Thread.current[:dalli_multi] = Thread.current[:dalli_multi], true
-
yield
-
ensure
-
Thread.current[:dalli_multi] = old
-
end
-
-
##
-
# Get the value associated with the key.
-
1
def get(key, options=nil)
-
perform(:get, key)
-
end
-
-
##
-
# Fetch multiple keys efficiently.
-
# If a block is given, yields key/value pairs one at a time.
-
# Otherwise returns a hash of { 'key' => 'value', 'key2' => 'value1' }
-
1
def get_multi(*keys)
-
if block_given?
-
get_multi_yielder(keys) {|k, data| yield k, data.first}
-
else
-
Hash.new.tap do |hash|
-
get_multi_yielder(keys) {|k, data| hash[k] = data.first}
-
end
-
end
-
end
-
-
1
def fetch(key, ttl=nil, options=nil)
-
ttl ||= @options[:expires_in].to_i
-
val = get(key, options)
-
if val.nil? && block_given?
-
val = yield
-
add(key, val, ttl, options)
-
end
-
val
-
end
-
-
##
-
# compare and swap values using optimistic locking.
-
# Fetch the existing value for key.
-
# If it exists, yield the value to the block.
-
# Add the block's return value as the new value for the key.
-
# Add will fail if someone else changed the value.
-
#
-
# Returns:
-
# - nil if the key did not exist.
-
# - false if the value was changed by someone else.
-
# - true if the value was successfully updated.
-
1
def cas(key, ttl=nil, options=nil, &block)
-
ttl ||= @options[:expires_in].to_i
-
(value, cas) = perform(:cas, key)
-
value = (!value || value == 'Not found') ? nil : value
-
if value
-
newvalue = block.call(value)
-
perform(:set, key, newvalue, ttl, cas, options)
-
end
-
end
-
-
1
def set(key, value, ttl=nil, options=nil)
-
ttl ||= @options[:expires_in].to_i
-
perform(:set, key, value, ttl, 0, options)
-
end
-
-
##
-
# Conditionally add a key/value pair, if the key does not already exist
-
# on the server. Returns truthy if the operation succeeded.
-
1
def add(key, value, ttl=nil, options=nil)
-
ttl ||= @options[:expires_in].to_i
-
perform(:add, key, value, ttl, options)
-
end
-
-
##
-
# Conditionally add a key/value pair, only if the key already exists
-
# on the server. Returns truthy if the operation succeeded.
-
1
def replace(key, value, ttl=nil, options=nil)
-
ttl ||= @options[:expires_in].to_i
-
perform(:replace, key, value, ttl, 0, options)
-
end
-
-
1
def delete(key)
-
perform(:delete, key, 0)
-
end
-
-
##
-
# Append value to the value already stored on the server for 'key'.
-
# Appending only works for values stored with :raw => true.
-
1
def append(key, value)
-
perform(:append, key, value.to_s)
-
end
-
-
##
-
# Prepend value to the value already stored on the server for 'key'.
-
# Prepending only works for values stored with :raw => true.
-
1
def prepend(key, value)
-
perform(:prepend, key, value.to_s)
-
end
-
-
1
def flush(delay=0)
-
time = -delay
-
ring.servers.map { |s| s.request(:flush, time += delay) }
-
end
-
-
1
alias_method :flush_all, :flush
-
-
##
-
# Incr adds the given amount to the counter on the memcached server.
-
# Amt must be a positive integer value.
-
#
-
# If default is nil, the counter must already exist or the operation
-
# will fail and will return nil. Otherwise this method will return
-
# the new value for the counter.
-
#
-
# Note that the ttl will only apply if the counter does not already
-
# exist. To increase an existing counter and update its TTL, use
-
# #cas.
-
1
def incr(key, amt=1, ttl=nil, default=nil)
-
raise ArgumentError, "Positive values only: #{amt}" if amt < 0
-
ttl ||= @options[:expires_in].to_i
-
perform(:incr, key, amt.to_i, ttl, default)
-
end
-
-
##
-
# Decr subtracts the given amount from the counter on the memcached server.
-
# Amt must be a positive integer value.
-
#
-
# memcached counters are unsigned and cannot hold negative values. Calling
-
# decr on a counter which is 0 will just return 0.
-
#
-
# If default is nil, the counter must already exist or the operation
-
# will fail and will return nil. Otherwise this method will return
-
# the new value for the counter.
-
#
-
# Note that the ttl will only apply if the counter does not already
-
# exist. To decrease an existing counter and update its TTL, use
-
# #cas.
-
1
def decr(key, amt=1, ttl=nil, default=nil)
-
raise ArgumentError, "Positive values only: #{amt}" if amt < 0
-
ttl ||= @options[:expires_in].to_i
-
perform(:decr, key, amt.to_i, ttl, default)
-
end
-
-
##
-
# Touch updates expiration time for a given key.
-
#
-
# Returns true if key exists, otherwise nil.
-
1
def touch(key, ttl=nil)
-
ttl ||= @options[:expires_in].to_i
-
resp = perform(:touch, key, ttl)
-
resp.nil? ? nil : true
-
end
-
-
##
-
# Collect the stats for each server.
-
# You can optionally pass a type including :items or :slabs to get specific stats
-
# Returns a hash like { 'hostname:port' => { 'stat1' => 'value1', ... }, 'hostname2:port' => { ... } }
-
1
def stats(type=nil)
-
type = nil if ![nil, :items,:slabs].include? type
-
values = {}
-
ring.servers.each do |server|
-
values["#{server.hostname}:#{server.port}"] = server.alive? ? server.request(:stats,type.to_s) : nil
-
end
-
values
-
end
-
-
##
-
# Reset stats for each server.
-
1
def reset_stats
-
ring.servers.map do |server|
-
server.alive? ? server.request(:reset_stats) : nil
-
end
-
end
-
-
##
-
## Make sure memcache servers are alive, or raise an Dalli::RingError
-
1
def alive!
-
ring.server_for_key("")
-
end
-
-
##
-
## Version of the memcache servers.
-
1
def version
-
values = {}
-
ring.servers.each do |server|
-
values["#{server.hostname}:#{server.port}"] = server.alive? ? server.request(:version) : nil
-
end
-
values
-
end
-
-
##
-
# Close our connection to each server.
-
# If you perform another operation after this, the connections will be re-established.
-
1
def close
-
if @ring
-
@ring.servers.each { |s| s.close }
-
@ring = nil
-
end
-
end
-
1
alias_method :reset, :close
-
-
# Stub method so a bare Dalli client can pretend to be a connection pool.
-
1
def with
-
yield self
-
end
-
-
1
private
-
-
1
def groups_for_keys(*keys)
-
groups = mapped_keys(keys).flatten.group_by do |key|
-
begin
-
ring.server_for_key(key)
-
rescue Dalli::RingError
-
Dalli.logger.debug { "unable to get key #{key}" }
-
nil
-
end
-
end
-
return groups
-
end
-
-
1
def mapped_keys(keys)
-
keys.flatten.map {|a| validate_key(a.to_s)}
-
end
-
-
1
def make_multi_get_requests(groups)
-
groups.each do |server, keys_for_server|
-
begin
-
# TODO: do this with the perform chokepoint?
-
# But given the fact that fetching the response doesn't take place
-
# in that slot it's misleading anyway. Need to move all of this method
-
# into perform to be meaningful
-
server.request(:send_multiget, keys_for_server)
-
rescue DalliError, NetworkError => e
-
Dalli.logger.debug { e.inspect }
-
Dalli.logger.debug { "unable to get keys for server #{server.hostname}:#{server.port}" }
-
end
-
end
-
end
-
-
1
def perform_multi_response_start(servers)
-
servers.each do |server|
-
next unless server.alive?
-
begin
-
server.multi_response_start
-
rescue DalliError, NetworkError => e
-
Dalli.logger.debug { e.inspect }
-
Dalli.logger.debug { "results from this server will be missing" }
-
servers.delete(server)
-
end
-
end
-
servers
-
end
-
-
##
-
# Normalizes the argument into an array of servers. If the argument is a string, it's expected to be of
-
# the format "memcache1.example.com:11211[,memcache2.example.com:11211[,memcache3.example.com:11211[...]]]
-
1
def normalize_servers(servers)
-
if servers.is_a? String
-
return servers.split(",")
-
else
-
return servers
-
end
-
end
-
-
1
def ring
-
@ring ||= Dalli::Ring.new(
-
@servers.map do |s|
-
server_options = {}
-
if s =~ %r{\Amemcached://}
-
uri = URI.parse(s)
-
server_options[:username] = uri.user
-
server_options[:password] = uri.password
-
s = "#{uri.host}:#{uri.port}"
-
end
-
Dalli::Server.new(s, @options.merge(server_options))
-
end, @options
-
)
-
end
-
-
# Chokepoint method for instrumentation
-
1
def perform(*all_args, &blk)
-
return blk.call if blk
-
op, key, *args = *all_args
-
-
key = key.to_s
-
key = validate_key(key)
-
begin
-
server = ring.server_for_key(key)
-
ret = server.request(op, key, *args)
-
ret
-
rescue NetworkError => e
-
Dalli.logger.debug { e.inspect }
-
Dalli.logger.debug { "retrying request with new server" }
-
retry
-
end
-
end
-
-
1
def validate_key(key)
-
raise ArgumentError, "key cannot be blank" if !key || key.length == 0
-
key = key_with_namespace(key)
-
if key.length > 250
-
max_length_before_namespace = 212 - (namespace || '').size
-
key = "#{key[0, max_length_before_namespace]}:md5:#{Digest::MD5.hexdigest(key)}"
-
end
-
return key
-
end
-
-
1
def key_with_namespace(key)
-
(ns = namespace) ? "#{ns}:#{key}" : key
-
end
-
-
1
def key_without_namespace(key)
-
(ns = namespace) ? key.sub(%r(\A#{ns}:), '') : key
-
end
-
-
1
def namespace
-
return nil unless @options[:namespace]
-
@options[:namespace].is_a?(Proc) ? @options[:namespace].call.to_s : @options[:namespace].to_s
-
end
-
-
1
def normalize_options(opts)
-
if opts[:compression]
-
Dalli.logger.warn "DEPRECATED: Dalli's :compression option is now just :compress => true. Please update your configuration."
-
opts[:compress] = opts.delete(:compression)
-
end
-
begin
-
opts[:expires_in] = opts[:expires_in].to_i if opts[:expires_in]
-
rescue NoMethodError
-
raise ArgumentError, "cannot convert :expires_in => #{opts[:expires_in].inspect} to an integer"
-
end
-
opts
-
end
-
-
##
-
# Yields, one at a time, keys and their values+attributes.
-
1
def get_multi_yielder(keys)
-
perform do
-
return {} if keys.empty?
-
ring.lock do
-
begin
-
groups = groups_for_keys(keys)
-
if unfound_keys = groups.delete(nil)
-
Dalli.logger.debug { "unable to get keys for #{unfound_keys.length} keys because no matching server was found" }
-
end
-
make_multi_get_requests(groups)
-
-
servers = groups.keys
-
return if servers.empty?
-
servers = perform_multi_response_start(servers)
-
-
start = Time.now
-
loop do
-
# remove any dead servers
-
servers.delete_if { |s| s.sock.nil? }
-
break if servers.empty?
-
-
# calculate remaining timeout
-
elapsed = Time.now - start
-
timeout = servers.first.options[:socket_timeout]
-
if elapsed > timeout
-
readable = nil
-
else
-
sockets = servers.map(&:sock)
-
readable, _ = IO.select(sockets, nil, nil, timeout - elapsed)
-
end
-
-
if readable.nil?
-
# no response within timeout; abort pending connections
-
servers.each do |server|
-
Dalli.logger.debug { "memcached at #{server.name} did not response within timeout" }
-
server.multi_response_abort
-
end
-
break
-
-
else
-
readable.each do |sock|
-
server = sock.server
-
-
begin
-
server.multi_response_nonblock.each_pair do |key, value_list|
-
yield key_without_namespace(key), value_list
-
end
-
-
if server.multi_response_completed?
-
servers.delete(server)
-
end
-
rescue NetworkError
-
servers.delete(server)
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
-
end
-
end
-
1
require 'zlib'
-
1
require 'stringio'
-
-
1
module Dalli
-
1
class Compressor
-
1
def self.compress(data)
-
Zlib::Deflate.deflate(data)
-
end
-
-
1
def self.decompress(data)
-
Zlib::Inflate.inflate(data)
-
end
-
end
-
-
1
class GzipCompressor
-
1
def self.compress(data)
-
io = StringIO.new("w")
-
gz = Zlib::GzipWriter.new(io)
-
gz.write(data)
-
gz.close
-
io.string
-
end
-
-
1
def self.decompress(data)
-
io = StringIO.new(data, "rb")
-
Zlib::GzipReader.new(io).read
-
end
-
end
-
end
-
1
require 'thread'
-
1
require 'monitor'
-
-
1
module Dalli
-
-
# Make Dalli threadsafe by using a lock around all
-
# public server methods.
-
#
-
# Dalli::Server.extend(Dalli::Threadsafe)
-
#
-
1
module Threadsafe
-
1
def self.extended(obj)
-
obj.init_threadsafe
-
end
-
-
1
def request(op, *args)
-
@lock.synchronize do
-
super
-
end
-
end
-
-
1
def alive?
-
@lock.synchronize do
-
super
-
end
-
end
-
-
1
def close
-
@lock.synchronize do
-
super
-
end
-
end
-
-
1
def multi_response_start
-
@lock.synchronize do
-
super
-
end
-
end
-
-
1
def multi_response_nonblock
-
@lock.synchronize do
-
super
-
end
-
end
-
-
1
def multi_response_abort
-
@lock.synchronize do
-
super
-
end
-
end
-
-
1
def lock!
-
@lock.mon_enter
-
end
-
-
1
def unlock!
-
@lock.mon_exit
-
end
-
-
1
def init_threadsafe
-
@lock = Monitor.new
-
end
-
end
-
end
-
1
module Dalli
-
1
class Railtie < ::Rails::Railtie
-
1
config.before_configuration do
-
1
config.cache_store = :dalli_store
-
end
-
end
-
end
-
1
require 'digest/sha1'
-
1
require 'zlib'
-
-
1
module Dalli
-
1
class Ring
-
1
POINTS_PER_SERVER = 160 # this is the default in libmemcached
-
-
1
attr_accessor :servers, :continuum
-
-
1
def initialize(servers, options)
-
@servers = servers
-
@continuum = nil
-
if servers.size > 1
-
total_weight = servers.inject(0) { |memo, srv| memo + srv.weight }
-
continuum = []
-
servers.each do |server|
-
entry_count_for(server, servers.size, total_weight).times do |idx|
-
hash = Digest::SHA1.hexdigest("#{server.hostname}:#{server.port}:#{idx}")
-
value = Integer("0x#{hash[0..7]}")
-
continuum << Dalli::Ring::Entry.new(value, server)
-
end
-
end
-
@continuum = continuum.sort { |a, b| a.value <=> b.value }
-
end
-
-
threadsafe! unless options[:threadsafe] == false
-
@failover = options[:failover] != false
-
end
-
-
1
def server_for_key(key)
-
if @continuum
-
hkey = hash_for(key)
-
20.times do |try|
-
entryidx = binary_search(@continuum, hkey)
-
server = @continuum[entryidx].server
-
return server if server.alive?
-
break unless @failover
-
hkey = hash_for("#{try}#{key}")
-
end
-
else
-
server = @servers.first
-
return server if server && server.alive?
-
end
-
-
raise Dalli::RingError, "No server available"
-
end
-
-
1
def lock
-
@servers.each { |s| s.lock! }
-
begin
-
return yield
-
ensure
-
@servers.each { |s| s.unlock! }
-
end
-
end
-
-
1
private
-
-
1
def threadsafe!
-
@servers.each do |s|
-
s.extend(Dalli::Threadsafe)
-
end
-
end
-
-
1
def hash_for(key)
-
Zlib.crc32(key)
-
end
-
-
1
def entry_count_for(server, total_servers, total_weight)
-
((total_servers * POINTS_PER_SERVER * server.weight) / Float(total_weight)).floor
-
end
-
-
# Native extension to perform the binary search within the continuum
-
# space. Fallback to a pure Ruby version if the compilation doesn't work.
-
# optional for performance and only necessary if you are using multiple
-
# memcached servers.
-
1
begin
-
1
require 'inline'
-
inline do |builder|
-
builder.c <<-EOM
-
int binary_search(VALUE ary, unsigned int r) {
-
long upper = RARRAY_LEN(ary) - 1;
-
long lower = 0;
-
long idx = 0;
-
ID value = rb_intern("value");
-
VALUE continuumValue;
-
unsigned int l;
-
-
while (lower <= upper) {
-
idx = (lower + upper) / 2;
-
-
continuumValue = rb_funcall(RARRAY_PTR(ary)[idx], value, 0);
-
l = NUM2UINT(continuumValue);
-
if (l == r) {
-
return idx;
-
}
-
else if (l > r) {
-
upper = idx - 1;
-
}
-
else {
-
lower = idx + 1;
-
}
-
}
-
return upper;
-
}
-
EOM
-
end
-
rescue LoadError
-
# Find the closest index in the Ring with value <= the given value
-
1
def binary_search(ary, value)
-
upper = ary.size - 1
-
lower = 0
-
idx = 0
-
-
while (lower <= upper) do
-
idx = (lower + upper) / 2
-
comp = ary[idx].value <=> value
-
-
if comp == 0
-
return idx
-
elsif comp > 0
-
upper = idx - 1
-
else
-
lower = idx + 1
-
end
-
end
-
return upper
-
end
-
end
-
-
1
class Entry
-
1
attr_reader :value
-
1
attr_reader :server
-
-
1
def initialize(val, srv)
-
@value = val
-
@server = srv
-
end
-
end
-
-
end
-
end
-
1
require 'socket'
-
1
require 'timeout'
-
-
1
module Dalli
-
1
class Server
-
1
attr_accessor :hostname
-
1
attr_accessor :port
-
1
attr_accessor :weight
-
1
attr_accessor :options
-
1
attr_reader :sock
-
-
1
DEFAULTS = {
-
# seconds between trying to contact a remote server
-
:down_retry_delay => 1,
-
# connect/read/write timeout for socket operations
-
:socket_timeout => 0.5,
-
# times a socket operation may fail before considering the server dead
-
:socket_max_failures => 2,
-
# amount of time to sleep between retries when a failure occurs
-
:socket_failure_delay => 0.01,
-
# max size of value in bytes (default is 1 MB, can be overriden with "memcached -I <size>")
-
:value_max_bytes => 1024 * 1024,
-
:compressor => Compressor,
-
# min byte size to attempt compression
-
:compression_min_size => 1024,
-
# max byte size for compression
-
:compression_max_size => false,
-
:serializer => Marshal,
-
:username => nil,
-
:password => nil,
-
:keepalive => true
-
}
-
-
1
def initialize(attribs, options = {})
-
(@hostname, @port, @weight) = parse_hostname(attribs)
-
@port ||= 11211
-
@port = Integer(@port)
-
@weight ||= 1
-
@weight = Integer(@weight)
-
@fail_count = 0
-
@down_at = nil
-
@last_down_at = nil
-
@options = DEFAULTS.merge(options)
-
@sock = nil
-
@msg = nil
-
@error = nil
-
@pid = nil
-
@inprogress = nil
-
end
-
-
1
def name
-
"#{@hostname}:#{@port}"
-
end
-
-
# Chokepoint method for instrumentation
-
1
def request(op, *args)
-
verify_state
-
raise Dalli::NetworkError, "#{hostname}:#{port} is down: #{@error} #{@msg}. If you are sure it is running, ensure memcached version is > 1.4." unless alive?
-
begin
-
send(op, *args)
-
rescue Dalli::NetworkError
-
raise
-
rescue Dalli::MarshalError => ex
-
Dalli.logger.error "Marshalling error for key '#{args.first}': #{ex.message}"
-
Dalli.logger.error "You are trying to cache a Ruby object which cannot be serialized to memcached."
-
Dalli.logger.error ex.backtrace.join("\n\t")
-
false
-
rescue Dalli::DalliError
-
raise
-
rescue => ex
-
Dalli.logger.error "Unexpected exception in Dalli: #{ex.class.name}: #{ex.message}"
-
Dalli.logger.error "This is a bug in Dalli, please enter an issue in Github if it does not already exist."
-
Dalli.logger.error ex.backtrace.join("\n\t")
-
down!
-
end
-
end
-
-
1
def alive?
-
return true if @sock
-
-
if @last_down_at && @last_down_at + options[:down_retry_delay] >= Time.now
-
time = @last_down_at + options[:down_retry_delay] - Time.now
-
Dalli.logger.debug { "down_retry_delay not reached for #{hostname}:#{port} (%.3f seconds left)" % time }
-
return false
-
end
-
-
connect
-
!!@sock
-
rescue Dalli::NetworkError
-
false
-
end
-
-
1
def close
-
return unless @sock
-
@sock.close rescue nil
-
@sock = nil
-
@pid = nil
-
@inprogress = false
-
end
-
-
1
def lock!
-
end
-
-
1
def unlock!
-
end
-
-
1
def serializer
-
@options[:serializer]
-
end
-
-
1
def compressor
-
@options[:compressor]
-
end
-
-
# Start reading key/value pairs from this connection. This is usually called
-
# after a series of GETKQ commands. A NOOP is sent, and the server begins
-
# flushing responses for kv pairs that were found.
-
#
-
# Returns nothing.
-
1
def multi_response_start
-
verify_state
-
write_noop
-
@multi_buffer = ''
-
@position = 0
-
@inprogress = true
-
end
-
-
# Did the last call to #multi_response_start complete successfully?
-
1
def multi_response_completed?
-
@multi_buffer.nil?
-
end
-
-
# Attempt to receive and parse as many key/value pairs as possible
-
# from this server. After #multi_response_start, this should be invoked
-
# repeatedly whenever this server's socket is readable until
-
# #multi_response_completed?.
-
#
-
# Returns a Hash of kv pairs received.
-
1
def multi_response_nonblock
-
raise 'multi_response has completed' if @multi_buffer.nil?
-
-
@multi_buffer << @sock.read_available
-
buf = @multi_buffer
-
pos = @position
-
values = {}
-
-
while buf.bytesize - pos >= 24
-
header = buf.slice(pos, 24)
-
(key_length, _, body_length, cas) = header.unpack(KV_HEADER)
-
-
if key_length == 0
-
# all done!
-
@multi_buffer = nil
-
@position = nil
-
@inprogress = false
-
break
-
-
elsif buf.bytesize - pos >= 24 + body_length
-
flags = buf.slice(pos + 24, 4).unpack('N')[0]
-
key = buf.slice(pos + 24 + 4, key_length)
-
value = buf.slice(pos + 24 + 4 + key_length, body_length - key_length - 4) if body_length - key_length - 4 > 0
-
-
pos = pos + 24 + body_length
-
-
begin
-
values[key] = [deserialize(value, flags), cas]
-
rescue DalliError
-
end
-
-
else
-
# not enough data yet, wait for more
-
break
-
end
-
end
-
@position = pos
-
-
values
-
rescue SystemCallError, Timeout::Error, EOFError => e
-
failure!(e)
-
end
-
-
# Abort an earlier #multi_response_start. Used to signal an external
-
# timeout. The underlying socket is disconnected, and the exception is
-
# swallowed.
-
#
-
# Returns nothing.
-
1
def multi_response_abort
-
@multi_buffer = nil
-
@position = nil
-
@inprogress = false
-
failure!(RuntimeError.new('External timeout'))
-
rescue NetworkError
-
true
-
end
-
-
# NOTE: Additional public methods should be overridden in Dalli::Threadsafe
-
-
1
private
-
-
1
def verify_state
-
failure!(RuntimeError.new('Already writing to socket')) if @inprogress
-
failure!(RuntimeError.new('Cannot share client between multiple processes')) if @pid && @pid != Process.pid
-
end
-
-
1
def failure!(exception)
-
message = "#{hostname}:#{port} failed (count: #{@fail_count}) #{exception.class}: #{exception.message}"
-
Dalli.logger.info { message }
-
-
@fail_count += 1
-
if @fail_count >= options[:socket_max_failures]
-
down!
-
else
-
close
-
sleep(options[:socket_failure_delay]) if options[:socket_failure_delay]
-
raise Dalli::NetworkError, "Socket operation failed, retrying..."
-
end
-
end
-
-
1
def down!
-
close
-
-
@last_down_at = Time.now
-
-
if @down_at
-
time = Time.now - @down_at
-
Dalli.logger.debug { "#{hostname}:#{port} is still down (for %.3f seconds now)" % time }
-
else
-
@down_at = @last_down_at
-
Dalli.logger.warn { "#{hostname}:#{port} is down" }
-
end
-
-
@error = $! && $!.class.name
-
@msg = @msg || ($! && $!.message && !$!.message.empty? && $!.message)
-
raise Dalli::NetworkError, "#{hostname}:#{port} is down: #{@error} #{@msg}"
-
end
-
-
1
def up!
-
if @down_at
-
time = Time.now - @down_at
-
Dalli.logger.warn { "#{hostname}:#{port} is back (downtime was %.3f seconds)" % time }
-
end
-
-
@fail_count = 0
-
@down_at = nil
-
@last_down_at = nil
-
@msg = nil
-
@error = nil
-
end
-
-
1
def multi?
-
Thread.current[:dalli_multi]
-
end
-
-
1
def get(key)
-
req = [REQUEST, OPCODES[:get], key.bytesize, 0, 0, 0, key.bytesize, 0, 0, key].pack(FORMAT[:get])
-
write(req)
-
generic_response(true)
-
end
-
-
1
def send_multiget(keys)
-
req = ""
-
keys.each do |key|
-
req << [REQUEST, OPCODES[:getkq], key.bytesize, 0, 0, 0, key.bytesize, 0, 0, key].pack(FORMAT[:getkq])
-
end
-
# Could send noop here instead of in multi_response_start
-
write(req)
-
end
-
-
1
def set(key, value, ttl, cas, options)
-
(value, flags) = serialize(key, value, options)
-
ttl = sanitize_ttl(ttl)
-
-
guard_max_value(key, value) do
-
req = [REQUEST, OPCODES[multi? ? :setq : :set], key.bytesize, 8, 0, 0, value.bytesize + key.bytesize + 8, 0, cas, flags, ttl, key, value].pack(FORMAT[:set])
-
write(req)
-
cas_response unless multi?
-
end
-
end
-
-
1
def add(key, value, ttl, options)
-
(value, flags) = serialize(key, value, options)
-
ttl = sanitize_ttl(ttl)
-
-
guard_max_value(key, value) do
-
req = [REQUEST, OPCODES[multi? ? :addq : :add], key.bytesize, 8, 0, 0, value.bytesize + key.bytesize + 8, 0, 0, flags, ttl, key, value].pack(FORMAT[:add])
-
write(req)
-
cas_response unless multi?
-
end
-
end
-
-
1
def replace(key, value, ttl, cas, options)
-
(value, flags) = serialize(key, value, options)
-
ttl = sanitize_ttl(ttl)
-
-
guard_max_value(key, value) do
-
req = [REQUEST, OPCODES[multi? ? :replaceq : :replace], key.bytesize, 8, 0, 0, value.bytesize + key.bytesize + 8, 0, cas, flags, ttl, key, value].pack(FORMAT[:replace])
-
write(req)
-
cas_response unless multi?
-
end
-
end
-
-
1
def delete(key, cas)
-
req = [REQUEST, OPCODES[multi? ? :deleteq : :delete], key.bytesize, 0, 0, 0, key.bytesize, 0, cas, key].pack(FORMAT[:delete])
-
write(req)
-
generic_response unless multi?
-
end
-
-
1
def flush(ttl)
-
req = [REQUEST, OPCODES[:flush], 0, 4, 0, 0, 4, 0, 0, 0].pack(FORMAT[:flush])
-
write(req)
-
generic_response
-
end
-
-
1
def decr_incr(opcode, key, count, ttl, default)
-
expiry = default ? sanitize_ttl(ttl) : 0xFFFFFFFF
-
default ||= 0
-
(h, l) = split(count)
-
(dh, dl) = split(default)
-
req = [REQUEST, OPCODES[opcode], key.bytesize, 20, 0, 0, key.bytesize + 20, 0, 0, h, l, dh, dl, expiry, key].pack(FORMAT[opcode])
-
write(req)
-
body = generic_response
-
body ? body.unpack('Q>').first : body
-
end
-
-
1
def decr(key, count, ttl, default)
-
decr_incr :decr, key, count, ttl, default
-
end
-
-
1
def incr(key, count, ttl, default)
-
decr_incr :incr, key, count, ttl, default
-
end
-
-
1
def write_append_prepend(opcode, key, value)
-
write_generic [REQUEST, OPCODES[opcode], key.bytesize, 0, 0, 0, value.bytesize + key.bytesize, 0, 0, key, value].pack(FORMAT[opcode])
-
end
-
-
1
def write_generic(bytes)
-
write(bytes)
-
generic_response
-
end
-
-
1
def write_noop
-
req = [REQUEST, OPCODES[:noop], 0, 0, 0, 0, 0, 0, 0].pack(FORMAT[:noop])
-
write(req)
-
end
-
-
# Noop is a keepalive operation but also used to demarcate the end of a set of pipelined commands.
-
# We need to read all the responses at once.
-
1
def noop
-
write_noop
-
multi_response
-
end
-
-
1
def append(key, value)
-
write_append_prepend :append, key, value
-
end
-
-
1
def prepend(key, value)
-
write_append_prepend :prepend, key, value
-
end
-
-
1
def stats(info='')
-
req = [REQUEST, OPCODES[:stat], info.bytesize, 0, 0, 0, info.bytesize, 0, 0, info].pack(FORMAT[:stat])
-
write(req)
-
keyvalue_response
-
end
-
-
1
def reset_stats
-
write_generic [REQUEST, OPCODES[:stat], 'reset'.bytesize, 0, 0, 0, 'reset'.bytesize, 0, 0, 'reset'].pack(FORMAT[:stat])
-
end
-
-
1
def cas(key)
-
req = [REQUEST, OPCODES[:get], key.bytesize, 0, 0, 0, key.bytesize, 0, 0, key].pack(FORMAT[:get])
-
write(req)
-
data_cas_response
-
end
-
-
1
def version
-
write_generic [REQUEST, OPCODES[:version], 0, 0, 0, 0, 0, 0, 0].pack(FORMAT[:noop])
-
end
-
-
1
def touch(key, ttl)
-
ttl = sanitize_ttl(ttl)
-
write_generic [REQUEST, OPCODES[:touch], key.bytesize, 4, 0, 0, key.bytesize + 4, 0, 0, ttl, key].pack(FORMAT[:touch])
-
end
-
-
# http://www.hjp.at/zettel/m/memcached_flags.rxml
-
# Looks like most clients use bit 0 to indicate native language serialization
-
# and bit 1 to indicate gzip compression.
-
1
FLAG_SERIALIZED = 0x1
-
1
FLAG_COMPRESSED = 0x2
-
-
1
def serialize(key, value, options=nil)
-
marshalled = false
-
value = unless options && options[:raw]
-
marshalled = true
-
begin
-
self.serializer.dump(value)
-
rescue => ex
-
# Marshalling can throw several different types of generic Ruby exceptions.
-
# Convert to a specific exception so we can special case it higher up the stack.
-
exc = Dalli::MarshalError.new(ex.message)
-
exc.set_backtrace ex.backtrace
-
raise exc
-
end
-
else
-
value.to_s
-
end
-
compressed = false
-
if @options[:compress] && value.bytesize >= @options[:compression_min_size] &&
-
(!@options[:compression_max_size] || value.bytesize <= @options[:compression_max_size])
-
value = self.compressor.compress(value)
-
compressed = true
-
end
-
-
flags = 0
-
flags |= FLAG_COMPRESSED if compressed
-
flags |= FLAG_SERIALIZED if marshalled
-
[value, flags]
-
end
-
-
1
def deserialize(value, flags)
-
value = self.compressor.decompress(value) if (flags & FLAG_COMPRESSED) != 0
-
value = self.serializer.load(value) if (flags & FLAG_SERIALIZED) != 0
-
value
-
rescue TypeError
-
raise if $!.message !~ /needs to have method `_load'|exception class\/object expected|instance of IO needed|incompatible marshal file format/
-
raise UnmarshalError, "Unable to unmarshal value: #{$!.message}"
-
rescue ArgumentError
-
raise if $!.message !~ /undefined class|marshal data too short/
-
raise UnmarshalError, "Unable to unmarshal value: #{$!.message}"
-
rescue Zlib::Error
-
raise UnmarshalError, "Unable to uncompress value: #{$!.message}"
-
end
-
-
1
def data_cas_response
-
(extras, _, status, count, _, cas) = read_header.unpack(CAS_HEADER)
-
data = read(count) if count > 0
-
if status == 1
-
nil
-
elsif status != 0
-
raise Dalli::DalliError, "Response error #{status}: #{RESPONSE_CODES[status]}"
-
elsif data
-
flags = data[0...extras].unpack('N')[0]
-
value = data[extras..-1]
-
data = deserialize(value, flags)
-
end
-
[data, cas]
-
end
-
-
1
CAS_HEADER = '@4CCnNNQ'
-
1
NORMAL_HEADER = '@4CCnN'
-
1
KV_HEADER = '@2n@6nN@16Q'
-
-
1
def guard_max_value(key, value)
-
if value.bytesize <= @options[:value_max_bytes]
-
yield
-
else
-
Dalli.logger.warn "Value for #{key} over max size: #{@options[:value_max_bytes]} <= #{value.bytesize}"
-
false
-
end
-
end
-
-
# https://code.google.com/p/memcached/wiki/NewCommands#Standard_Protocol
-
# > An expiration time, in seconds. Can be up to 30 days. After 30 days, is treated as a unix timestamp of an exact date.
-
1
MAX_ACCEPTABLE_EXPIRATION_INTERVAL = 30*24*60*60 # 30 days
-
1
def sanitize_ttl(ttl)
-
if ttl > MAX_ACCEPTABLE_EXPIRATION_INTERVAL
-
Dalli.logger.debug "Expiration interval too long for Memcached, converting to an expiration timestamp"
-
Time.now.to_i + ttl
-
else
-
ttl
-
end
-
end
-
-
1
def generic_response(unpack=false)
-
(extras, _, status, count) = read_header.unpack(NORMAL_HEADER)
-
data = read(count) if count > 0
-
if status == 1
-
nil
-
elsif status == 2 || status == 5
-
false # Not stored, normal status for add operation
-
elsif status != 0
-
raise Dalli::DalliError, "Response error #{status}: #{RESPONSE_CODES[status]}"
-
elsif data
-
flags = data[0...extras].unpack('N')[0]
-
value = data[extras..-1]
-
unpack ? deserialize(value, flags) : value
-
else
-
true
-
end
-
end
-
-
1
def cas_response
-
(_, _, status, count, _, cas) = read_header.unpack(CAS_HEADER)
-
read(count) if count > 0 # this is potential data that we don't care about
-
if status == 1
-
nil
-
elsif status == 2 || status == 5
-
false # Not stored, normal status for add operation
-
elsif status != 0
-
raise Dalli::DalliError, "Response error #{status}: #{RESPONSE_CODES[status]}"
-
else
-
cas
-
end
-
end
-
-
1
def keyvalue_response
-
hash = {}
-
loop do
-
(key_length, _, body_length, _) = read_header.unpack(KV_HEADER)
-
return hash if key_length == 0
-
key = read(key_length)
-
value = read(body_length - key_length) if body_length - key_length > 0
-
hash[key] = value
-
end
-
end
-
-
1
def multi_response
-
hash = {}
-
loop do
-
(key_length, _, body_length, _) = read_header.unpack(KV_HEADER)
-
return hash if key_length == 0
-
flags = read(4).unpack('N')[0]
-
key = read(key_length)
-
value = read(body_length - key_length - 4) if body_length - key_length - 4 > 0
-
hash[key] = deserialize(value, flags)
-
end
-
end
-
-
1
def write(bytes)
-
begin
-
@inprogress = true
-
result = @sock.write(bytes)
-
@inprogress = false
-
result
-
rescue SystemCallError, Timeout::Error => e
-
failure!(e)
-
end
-
end
-
-
1
def read(count)
-
begin
-
@inprogress = true
-
data = @sock.readfull(count)
-
@inprogress = false
-
data
-
rescue SystemCallError, Timeout::Error, EOFError => e
-
failure!(e)
-
end
-
end
-
-
1
def read_header
-
read(24) || raise(Dalli::NetworkError, 'No response')
-
end
-
-
1
def connect
-
Dalli.logger.debug { "Dalli::Server#connect #{hostname}:#{port}" }
-
-
begin
-
@pid = Process.pid
-
@sock = KSocket.open(hostname, port, self, options)
-
@version = version # trigger actual connect
-
sasl_authentication if need_auth?
-
up!
-
rescue Dalli::DalliError # SASL auth failure
-
raise
-
rescue SystemCallError, Timeout::Error, EOFError, SocketError => e
-
# SocketError = DNS resolution failure
-
failure!(e)
-
end
-
end
-
-
1
def split(n)
-
[n >> 32, 0xFFFFFFFF & n]
-
end
-
-
1
REQUEST = 0x80
-
1
RESPONSE = 0x81
-
-
1
RESPONSE_CODES = {
-
0 => 'No error',
-
1 => 'Key not found',
-
2 => 'Key exists',
-
3 => 'Value too large',
-
4 => 'Invalid arguments',
-
5 => 'Item not stored',
-
6 => 'Incr/decr on a non-numeric value',
-
0x20 => 'Authentication required',
-
0x81 => 'Unknown command',
-
0x82 => 'Out of memory',
-
}
-
-
1
OPCODES = {
-
:get => 0x00,
-
:set => 0x01,
-
:add => 0x02,
-
:replace => 0x03,
-
:delete => 0x04,
-
:incr => 0x05,
-
:decr => 0x06,
-
:flush => 0x08,
-
:noop => 0x0A,
-
:version => 0x0B,
-
:getkq => 0x0D,
-
:append => 0x0E,
-
:prepend => 0x0F,
-
:stat => 0x10,
-
:setq => 0x11,
-
:addq => 0x12,
-
:replaceq => 0x13,
-
:deleteq => 0x14,
-
:incrq => 0x15,
-
:decrq => 0x16,
-
:auth_negotiation => 0x20,
-
:auth_request => 0x21,
-
:auth_continue => 0x22,
-
:touch => 0x1C,
-
}
-
-
1
HEADER = "CCnCCnNNQ"
-
1
OP_FORMAT = {
-
:get => 'a*',
-
:set => 'NNa*a*',
-
:add => 'NNa*a*',
-
:replace => 'NNa*a*',
-
:delete => 'a*',
-
:incr => 'NNNNNa*',
-
:decr => 'NNNNNa*',
-
:flush => 'N',
-
:noop => '',
-
:getkq => 'a*',
-
:version => '',
-
:stat => 'a*',
-
:append => 'a*a*',
-
:prepend => 'a*a*',
-
:auth_request => 'a*a*',
-
:auth_continue => 'a*a*',
-
:touch => 'Na*',
-
}
-
18
FORMAT = OP_FORMAT.inject({}) { |memo, (k, v)| memo[k] = HEADER + v; memo }
-
-
-
#######
-
# SASL authentication support for NorthScale
-
#######
-
-
1
def need_auth?
-
@options[:username] || ENV['MEMCACHE_USERNAME']
-
end
-
-
1
def username
-
@options[:username] || ENV['MEMCACHE_USERNAME']
-
end
-
-
1
def password
-
@options[:password] || ENV['MEMCACHE_PASSWORD']
-
end
-
-
1
def sasl_authentication
-
Dalli.logger.info { "Dalli/SASL authenticating as #{username}" }
-
-
# negotiate
-
req = [REQUEST, OPCODES[:auth_negotiation], 0, 0, 0, 0, 0, 0, 0].pack(FORMAT[:noop])
-
write(req)
-
-
(extras, type, status, count) = read_header.unpack(NORMAL_HEADER)
-
raise Dalli::NetworkError, "Unexpected message format: #{extras} #{count}" unless extras == 0 && count > 0
-
content = read(count)
-
return (Dalli.logger.debug("Authentication not required/supported by server")) if status == 0x81
-
mechanisms = content.split(' ')
-
raise NotImplementedError, "Dalli only supports the PLAIN authentication mechanism" if !mechanisms.include?('PLAIN')
-
-
# request
-
mechanism = 'PLAIN'
-
msg = "\x0#{username}\x0#{password}"
-
req = [REQUEST, OPCODES[:auth_request], mechanism.bytesize, 0, 0, 0, mechanism.bytesize + msg.bytesize, 0, 0, mechanism, msg].pack(FORMAT[:auth_request])
-
write(req)
-
-
(extras, type, status, count) = read_header.unpack(NORMAL_HEADER)
-
raise Dalli::NetworkError, "Unexpected message format: #{extras} #{count}" unless extras == 0 && count > 0
-
content = read(count)
-
return Dalli.logger.info("Dalli/SASL: #{content}") if status == 0
-
-
raise Dalli::DalliError, "Error authenticating: #{status}" unless status == 0x21
-
raise NotImplementedError, "No two-step authentication mechanisms supported"
-
# (step, msg) = sasl.receive('challenge', content)
-
# raise Dalli::NetworkError, "Authentication failed" if sasl.failed? || step != 'response'
-
end
-
-
1
def parse_hostname(str)
-
res = str.match(/\A(\[([\h:]+)\]|[^:]+)(:(\d+))?(:(\d+))?\z/)
-
return res[2] || res[1], res[4], res[6]
-
end
-
end
-
end
-
1
begin
-
1
require 'kgio'
-
puts "Using kgio socket IO" if defined?($TESTING) && $TESTING
-
-
class Dalli::Server::KSocket < Kgio::Socket
-
attr_accessor :options, :server
-
-
def kgio_wait_readable
-
IO.select([self], nil, nil, options[:socket_timeout]) || raise(Timeout::Error, "IO timeout")
-
end
-
-
def kgio_wait_writable
-
IO.select(nil, [self], nil, options[:socket_timeout]) || raise(Timeout::Error, "IO timeout")
-
end
-
-
def self.open(host, port, server, options = {})
-
addr = Socket.pack_sockaddr_in(port, host)
-
sock = start(addr)
-
sock.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, true)
-
sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true) if options[:keepalive]
-
sock.options = options
-
sock.server = server
-
sock.kgio_wait_writable
-
sock
-
end
-
-
alias :write :kgio_write
-
-
def readfull(count)
-
value = ''
-
loop do
-
value << kgio_read!(count - value.bytesize)
-
break if value.bytesize == count
-
end
-
value
-
end
-
-
def read_available
-
value = ''
-
loop do
-
ret = kgio_tryread(8196)
-
case ret
-
when nil
-
raise EOFError, 'end of stream'
-
when :wait_readable
-
break
-
else
-
value << ret
-
end
-
end
-
value
-
end
-
-
end
-
-
if ::Kgio.respond_to?(:wait_readable=)
-
::Kgio.wait_readable = :kgio_wait_readable
-
::Kgio.wait_writable = :kgio_wait_writable
-
end
-
-
rescue LoadError
-
-
1
puts "Using standard socket IO (#{RUBY_DESCRIPTION})" if defined?($TESTING) && $TESTING
-
1
class Dalli::Server::KSocket < TCPSocket
-
1
attr_accessor :options, :server
-
-
1
def self.open(host, port, server, options = {})
-
Timeout.timeout(options[:socket_timeout]) do
-
sock = new(host, port)
-
sock.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, true)
-
sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true) if options[:keepalive]
-
sock.options = { :host => host, :port => port }.merge(options)
-
sock.server = server
-
sock
-
end
-
end
-
-
1
def readfull(count)
-
value = ''
-
begin
-
loop do
-
value << read_nonblock(count - value.bytesize)
-
break if value.bytesize == count
-
end
-
rescue Errno::EAGAIN, Errno::EWOULDBLOCK
-
if IO.select([self], nil, nil, options[:socket_timeout])
-
retry
-
else
-
raise Timeout::Error, "IO timeout: #{options.inspect}"
-
end
-
end
-
value
-
end
-
-
1
def read_available
-
value = ''
-
loop do
-
begin
-
value << read_nonblock(8196)
-
rescue Errno::EAGAIN, Errno::EWOULDBLOCK
-
break
-
end
-
end
-
value
-
end
-
-
end
-
end
-
1
module Dalli
-
1
VERSION = '2.7.2'
-
end
-
# $Id$
-
#
-
# Copyright (C) 2007, 2008 Rocky Bernstein <rockyb@rubyforge.net>
-
#
-
# This program is free software; you can redistribute it and/or modify
-
# it under the terms of the GNU General Public License as published by
-
# the Free Software Foundation; either version 2 of the License, or
-
# (at your option) any later version.
-
#
-
# This program is distributed in the hope that it will be useful,
-
# but WITHOUT ANY WARRANTY; without even the implied warranty of
-
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-
# GNU General Public License for more details.
-
#
-
# You should have received a copy of the GNU General Public License
-
# along with this program; if not, write to the Free Software
-
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-
# 02110-1301 USA.
-
#
-
-
# Author:: Rocky Bernstein (mailto:rockyb@rubyforge.net)
-
#
-
# = linecache
-
# A module to read and cache lines of a Ruby program.
-
-
# == SYNOPSIS
-
#
-
# The LineCache module allows one to get any line from any file,
-
# caching lines of the file on first access to the file. Although the
-
# file may be any file, the common use is when the file is a Ruby
-
# script since parsing of the file is done to figure out where the
-
# statement boundaries are.
-
#
-
# The routines here may be is useful when a small random sets of lines
-
# are read from a single file, in particular in a debugger to show
-
# source lines.
-
#
-
#
-
# require 'linecache19'
-
# lines = LineCache::getlines('/tmp/myruby.rb')
-
# # The following lines have same effect as the above.
-
# $: << '/tmp'
-
# Dir.chdir('/tmp') {lines = LineCache::getlines('myruby.rb')
-
#
-
# line = LineCache::getline('/tmp/myruby.rb', 6)
-
# # Note lines[6] == line (if /tmp/myruby.rb has 6 lines)
-
#
-
# LineCache::clear_file_cache
-
# LineCache::clear_file_cache('/tmp/myruby.rb')
-
# LineCache::update_cache # Check for modifications of all cached files.
-
#
-
# Some parts of the interface is derived from the Python module of the
-
# same name.
-
#
-
-
# Defining SCRIPT_LINES__ causes Ruby to cache the lines of files
-
# it reads. The key the setting of __FILE__ at the time when Ruby does
-
# its read. LineCache keeps a separate copy of the lines elsewhere
-
# and never destroys SCRIPT_LINES__
-
1
SCRIPT_LINES__ = {} unless defined? SCRIPT_LINES__
-
-
1
require 'digest/sha1'
-
1
require 'set'
-
-
1
require 'tracelines19'
-
-
# = module LineCache
-
# A module to read and cache lines of a Ruby program.
-
1
module LineCache
-
LineCacheInfo = Struct.new(:stat, :line_numbers, :lines, :path, :sha1) unless
-
1
defined?(LineCacheInfo)
-
-
# The file cache. The key is a name as would be given by Ruby for
-
# __FILE__. The value is a LineCacheInfo object.
-
1
@@file_cache = {}
-
-
# Maps a string filename (a String) to a key in @@file_cache (a
-
# String).
-
#
-
# One important use of @@file2file_remap is mapping the a full path
-
# of a file into the name stored in @@file_cache or given by Ruby's
-
# __FILE__. Applications such as those that get input from users,
-
# may want canonicalize a file name before looking it up. This map
-
# gives a way to do that.
-
#
-
# Another related use is when a template system is used. Here we'll
-
# probably want to remap not only the file name but also line
-
# ranges. Will probably use this for that, but I'm not sure.
-
1
@@file2file_remap = {}
-
1
@@file2file_remap_lines = {}
-
-
# Clear the file cache entirely.
-
1
def clear_file_cache()
-
@@file_cache = {}
-
@@file2file_remap = {}
-
@@file2file_remap_lines = {}
-
end
-
1
module_function :clear_file_cache
-
-
# Return an array of cached file names
-
1
def cached_files()
-
@@file_cache.keys
-
end
-
1
module_function :cached_files
-
-
# Discard cache entries that are out of date. If +filename+ is +nil+
-
# all entries in the file cache +@@file_cache+ are checked.
-
# If we don't have stat information about a file, which can happen
-
# if the file was read from SCRIPT_LINES__ but no corresponding file
-
# is found, it will be kept. Return a list of invalidated filenames.
-
# nil is returned if a filename was given but not found cached.
-
1
def checkcache(filename=nil, use_script_lines=false)
-
-
if !filename
-
filenames = @@file_cache.keys()
-
elsif @@file_cache.member?(filename)
-
filenames = [filename]
-
else
-
return nil
-
end
-
-
result = []
-
for filename in filenames
-
next unless @@file_cache.member?(filename)
-
path = @@file_cache[filename].path
-
if File.exist?(path)
-
cache_info = @@file_cache[filename].stat
-
stat = File.stat(path)
-
if stat &&
-
(cache_info.size != stat.size or cache_info.mtime != stat.mtime)
-
result << filename
-
update_cache(filename, use_script_lines)
-
end
-
end
-
end
-
return result
-
end
-
1
module_function :checkcache
-
-
# Cache filename if it's not already cached.
-
# Return the expanded filename for it in the cache
-
# or nil if we can't find the file.
-
1
def cache(filename, reload_on_change=false)
-
if @@file_cache.member?(filename)
-
checkcache(filename) if reload_on_change
-
else
-
update_cache(filename, true)
-
end
-
if @@file_cache.member?(filename)
-
@@file_cache[filename].path
-
else
-
nil
-
end
-
end
-
1
module_function :cache
-
-
# Return true if filename is cached
-
1
def cached?(filename)
-
@@file_cache.member?(unmap_file(filename))
-
end
-
1
module_function :cached?
-
-
1
def cached_script?(filename)
-
# In 1.8.6, the SCRIPT_LINES__ filename key can be unqualified
-
# In 1.9.1 it's the fully qualified name
-
if RUBY_VERSION < "1.9"
-
SCRIPT_LINES__.member?(unmap_file(filename))
-
else
-
SCRIPT_LINES__.member?(File.expand_path(unmap_file(filename)))
-
end
-
end
-
1
module_function :cached_script?
-
-
1
def empty?(filename)
-
filename=unmap_file(filename)
-
@@file_cache[filename].lines.empty?
-
end
-
1
module_function :empty?
-
-
# Get line +line_number+ from file named +filename+. Return nil if
-
# there was a problem. If a file named filename is not found, the
-
# function will look for it in the $: array.
-
#
-
# Examples:
-
#
-
# lines = LineCache::getline('/tmp/myfile.rb')
-
# # Same as above
-
# $: << '/tmp'
-
# lines = LineCache.getlines('myfile.rb')
-
#
-
1
def getline(filename, line_number, reload_on_change=true)
-
filename = unmap_file(filename)
-
filename, line_number = unmap_file_line(filename, line_number)
-
lines = getlines(filename, reload_on_change)
-
if lines and (1..lines.size) === line_number
-
return lines[line_number-1]
-
else
-
return nil
-
end
-
end
-
1
module_function :getline
-
-
# Read lines of +filename+ and cache the results. However +filename+ was
-
# previously cached use the results from the cache. Return nil
-
# if we can't get lines
-
1
def getlines(filename, reload_on_change=false)
-
filename = unmap_file(filename)
-
checkcache(filename) if reload_on_change
-
if @@file_cache.member?(filename)
-
return @@file_cache[filename].lines
-
else
-
update_cache(filename, true)
-
return @@file_cache[filename].lines if @@file_cache.member?(filename)
-
end
-
end
-
1
module_function :getlines
-
-
# Return full filename path for filename
-
1
def path(filename)
-
filename = unmap_file(filename)
-
return nil unless @@file_cache.member?(filename)
-
@@file_cache[filename].path
-
end
-
1
module_function :path
-
-
1
def remap_file(from_file, to_file)
-
@@file2file_remap[to_file] = from_file
-
end
-
1
module_function :remap_file
-
-
1
def remap_file_lines(from_file, to_file, range, start)
-
range = (range..range) if range.is_a?(Fixnum)
-
to_file = from_file unless to_file
-
if @@file2file_remap_lines[to_file]
-
# FIXME: need to check for overwriting ranges: whether
-
# they intersect or one encompasses another.
-
@@file2file_remap_lines[to_file] << [from_file, range, start]
-
else
-
@@file2file_remap_lines[to_file] = [[from_file, range, start]]
-
end
-
end
-
1
module_function :remap_file_lines
-
-
# Return SHA1 of filename.
-
1
def sha1(filename)
-
filename = unmap_file(filename)
-
return nil unless @@file_cache.member?(filename)
-
return @@file_cache[filename].sha1.hexdigest if
-
@@file_cache[filename].sha1
-
sha1 = Digest::SHA1.new
-
@@file_cache[filename].lines.each do |line|
-
sha1 << line
-
end
-
@@file_cache[filename].sha1 = sha1
-
sha1.hexdigest
-
end
-
1
module_function :sha1
-
-
# Return the number of lines in filename
-
1
def size(filename)
-
filename = unmap_file(filename)
-
return nil unless @@file_cache.member?(filename)
-
@@file_cache[filename].lines.length
-
end
-
1
module_function :size
-
-
# Return File.stat in the cache for filename.
-
1
def stat(filename)
-
return nil unless @@file_cache.member?(filename)
-
@@file_cache[filename].stat
-
end
-
1
module_function :stat
-
-
# Return an Array of breakpoints in filename.
-
# The list will contain an entry for each distinct line event call
-
# so it is possible (and possibly useful) for a line number appear more
-
# than once.
-
1
def trace_line_numbers(filename, reload_on_change=false)
-
fullname = cache(filename, reload_on_change)
-
return nil unless fullname
-
e = @@file_cache[filename]
-
unless e.line_numbers
-
e.line_numbers =
-
TraceLineNumbers.lnums_for_str_array(e.lines)
-
e.line_numbers = false unless e.line_numbers
-
end
-
e.line_numbers
-
end
-
1
module_function :trace_line_numbers
-
-
1
def unmap_file(file)
-
@@file2file_remap[file] ? @@file2file_remap[file] : file
-
end
-
1
module_function :unmap_file
-
-
1
def unmap_file_line(file, line)
-
if @@file2file_remap_lines[file]
-
@@file2file_remap_lines[file].each do |from_file, range, start|
-
if range === line
-
from_file = from_file || file
-
return [from_file, start+line-range.begin]
-
end
-
end
-
end
-
return [file, line]
-
end
-
1
module_function :unmap_file_line
-
-
# Update a cache entry. If something's
-
# wrong, return nil. Return true if the cache was updated and false
-
# if not. If use_script_lines is true, use that as the source for the
-
# lines of the file
-
1
def update_cache(filename, use_script_lines=false)
-
-
return nil unless filename
-
-
@@file_cache.delete(filename)
-
path = File.expand_path(filename)
-
-
if use_script_lines
-
list = [filename]
-
list << @@file2file_remap[path] if @@file2file_remap[path]
-
list.each do |name|
-
if !SCRIPT_LINES__[name].nil? && SCRIPT_LINES__[name] != true
-
begin
-
stat = File.stat(name)
-
rescue
-
stat = nil
-
end
-
lines = SCRIPT_LINES__[name]
-
if "ruby19".respond_to?(:force_encoding)
-
lines.each{|l| l.force_encoding(Encoding.default_external) }
-
end
-
@@file_cache[filename] = LineCacheInfo.new(stat, nil, lines, path, nil)
-
@@file2file_remap[path] = filename
-
return true
-
end
-
end
-
end
-
-
if File.exist?(path)
-
stat = File.stat(path)
-
elsif File.basename(filename) == filename
-
# try looking through the search path.
-
stat = nil
-
for dirname in $:
-
path = File.join(dirname, filename)
-
if File.exist?(path)
-
stat = File.stat(path)
-
break
-
end
-
end
-
return false unless stat
-
end
-
begin
-
fp = File.open(path, 'r')
-
lines = fp.readlines()
-
fp.close()
-
rescue
-
## print '*** cannot open', path, ':', msg
-
return nil
-
end
-
@@file_cache[filename] = LineCacheInfo.new(File.stat(path), nil, lines,
-
path, nil)
-
@@file2file_remap[path] = filename
-
return true
-
end
-
-
1
module_function :update_cache
-
-
end
-
-
# example usage
-
1
if __FILE__ == $0
-
def yes_no(var)
-
return var ? "" : "not "
-
end
-
-
lines = LineCache::getlines(__FILE__)
-
puts "#{__FILE__} has #{LineCache.size(__FILE__)} lines"
-
line = LineCache::getline(__FILE__, 6)
-
puts "The 6th line is\n#{line}"
-
line = LineCache::remap_file(__FILE__, 'another_name')
-
puts LineCache::getline('another_name', 7)
-
-
puts("Files cached: #{LineCache::cached_files.inspect}")
-
LineCache::update_cache(__FILE__)
-
LineCache::checkcache(__FILE__)
-
puts "#{__FILE__} has #{LineCache::size(__FILE__)} lines"
-
puts "#{__FILE__} trace line numbers:\n" +
-
"#{LineCache::trace_line_numbers(__FILE__).to_a.sort.inspect}"
-
puts("#{__FILE__} is %scached." %
-
yes_no(LineCache::cached?(__FILE__)))
-
puts LineCache::stat(__FILE__).inspect
-
puts "Full path: #{LineCache::path(__FILE__)}"
-
LineCache::checkcache # Check all files in the cache
-
LineCache::clear_file_cache
-
puts("#{__FILE__} is now %scached." %
-
yes_no(LineCache::cached?(__FILE__)))
-
digest = SCRIPT_LINES__.select{|k,v| k =~ /digest.rb$/}
-
puts digest.first[0] if digest
-
line = LineCache::getline(__FILE__, 7)
-
puts "The 7th line is\n#{line}"
-
LineCache::remap_file_lines(__FILE__, 'test2', (10..20), 6)
-
puts LineCache::getline('test2', 10)
-
puts "Remapped 10th line of test2 is\n#{line}"
-
end
-
#!/usr/bin/env ruby
-
# $Id$
-
-
1
module TraceLineNumbers
-
# Return an array of lines numbers that could be
-
# stopped at given a file name of a Ruby program.
-
-
1
def self.lnums_for_str src
-
name = "#{Time.new.to_i}_#{rand(2**31)}"
-
iseq = RubyVM::InstructionSequence.compile(src, name)
-
lines = {}
-
iseq.disasm.each_line{|line|
-
if /^\d+ (\w+)\s+.+\(\s*(\d+)\)$/ =~ line
-
insn = $1
-
lineno = $2.to_i
-
next unless insn == 'trace'
-
lines[lineno] = true
-
# p [lineno, line]
-
end
-
}
-
lines.keys
-
end
-
-
1
def lnums_for_file(file)
-
lnums_for_str(File.read(file))
-
end
-
1
module_function :lnums_for_file
-
-
# Return an array of lines numbers that could be
-
# stopped at given a file name of a Ruby program.
-
# We assume the each line has \n at the end. If not
-
# set the newline parameters to \n.
-
1
def lnums_for_str_array(string_array, newline='')
-
lnums_for_str(string_array.join(newline))
-
end
-
1
module_function :lnums_for_str_array
-
end
-
-
1
if __FILE__ == $0
-
SCRIPT_LINES__ = {} unless defined? SCRIPT_LINES__
-
# test_file = '../test/rcov-bug.rb'
-
test_file = '../test/lnum-data/begin1.rb'
-
if File.exists?(test_file)
-
puts TraceLineNumbers.lnums_for_file(test_file).inspect
-
load(test_file, 0) # for later
-
end
-
puts TraceLineNumbers.lnums_for_file(__FILE__).inspect
-
unless SCRIPT_LINES__.empty?
-
key = SCRIPT_LINES__.keys.first
-
puts key
-
puts SCRIPT_LINES__[key]
-
puts TraceLineNumbers.lnums_for_str_array(SCRIPT_LINES__[key]).inspect
-
end
-
end
-
# -*- ruby encoding: utf-8 -*-
-
-
1
module Diff; end unless defined? Diff
-
# = Diff::LCS 1.2.5
-
#
-
# Computes "intelligent" differences between two sequenced Enumerables. This
-
# is an implementation of the McIlroy-Hunt "diff" algorithm for Enumerable
-
# objects that include Diffable.
-
#
-
# Based on Mario I. Wolczko's Smalltalk version (1.2, 1993) and Ned Konz's
-
# Perl version (Algorithm::Diff 1.15).
-
#
-
# == Synopsis
-
# require 'diff/lcs'
-
#
-
# seq1 = %w(a b c e h j l m n p)
-
# seq2 = %w(b c d e f j k l m r s t)
-
#
-
# lcs = Diff::LCS.lcs(seq1, seq2)
-
# diffs = Diff::LCS.diff(seq1, seq2)
-
# sdiff = Diff::LCS.sdiff(seq1, seq2)
-
# seq = Diff::LCS.traverse_sequences(seq1, seq2, callback_obj)
-
# bal = Diff::LCS.traverse_balanced(seq1, seq2, callback_obj)
-
# seq2 == Diff::LCS.patch(seq1, diffs)
-
# seq2 == Diff::LCS.patch!(seq1, diffs)
-
# seq1 == Diff::LCS.unpatch(seq2, diffs)
-
# seq1 == Diff::LCS.unpatch!(seq2, diffs)
-
# seq2 == Diff::LCS.patch(seq1, sdiff)
-
# seq2 == Diff::LCS.patch!(seq1, sdiff)
-
# seq1 == Diff::LCS.unpatch(seq2, sdiff)
-
# seq1 == Diff::LCS.unpatch!(seq2, sdiff)
-
#
-
# Alternatively, objects can be extended with Diff::LCS:
-
#
-
# seq1.extend(Diff::LCS)
-
# lcs = seq1.lcs(seq2)
-
# diffs = seq1.diff(seq2)
-
# sdiff = seq1.sdiff(seq2)
-
# seq = seq1.traverse_sequences(seq2, callback_obj)
-
# bal = seq1.traverse_balanced(seq2, callback_obj)
-
# seq2 == seq1.patch(diffs)
-
# seq2 == seq1.patch!(diffs)
-
# seq1 == seq2.unpatch(diffs)
-
# seq1 == seq2.unpatch!(diffs)
-
# seq2 == seq1.patch(sdiff)
-
# seq2 == seq1.patch!(sdiff)
-
# seq1 == seq2.unpatch(sdiff)
-
# seq1 == seq2.unpatch!(sdiff)
-
#
-
# Default extensions are provided for Array and String objects through the
-
# use of 'diff/lcs/array' and 'diff/lcs/string'.
-
#
-
# == Introduction (by Mark-Jason Dominus)
-
#
-
# <em>The following text is from the Perl documentation. The only changes
-
# have been to make the text appear better in Rdoc</em>.
-
#
-
# I once read an article written by the authors of +diff+; they said that
-
# they hard worked very hard on the algorithm until they found the right
-
# one.
-
#
-
# I think what they ended up using (and I hope someone will correct me,
-
# because I am not very confident about this) was the `longest common
-
# subsequence' method. In the LCS problem, you have two sequences of items:
-
#
-
# a b c d f g h j q z
-
# a b c d e f g i j k r x y z
-
#
-
# and you want to find the longest sequence of items that is present in both
-
# original sequences in the same order. That is, you want to find a new
-
# sequence *S* which can be obtained from the first sequence by deleting
-
# some items, and from the second sequence by deleting other items. You also
-
# want *S* to be as long as possible. In this case *S* is:
-
#
-
# a b c d f g j z
-
#
-
# From there it's only a small step to get diff-like output:
-
#
-
# e h i k q r x y
-
# + - + + - + + +
-
#
-
# This module solves the LCS problem. It also includes a canned function to
-
# generate +diff+-like output.
-
#
-
# It might seem from the example above that the LCS of two sequences is
-
# always pretty obvious, but that's not always the case, especially when the
-
# two sequences have many repeated elements. For example, consider
-
#
-
# a x b y c z p d q
-
# a b c a x b y c z
-
#
-
# A naive approach might start by matching up the +a+ and +b+ that appear at
-
# the beginning of each sequence, like this:
-
#
-
# a x b y c z p d q
-
# a b c a b y c z
-
#
-
# This finds the common subsequence +a b c z+. But actually, the LCS is +a x
-
# b y c z+:
-
#
-
# a x b y c z p d q
-
# a b c a x b y c z
-
#
-
# == Author
-
# This version is by Austin Ziegler <austin@rubyforge.org>.
-
#
-
# It is based on the Perl Algorithm::Diff (1.15) by Ned Konz , copyright
-
# © 2000–2002 and the Smalltalk diff version by Mario I.
-
# Wolczko, copyright © 1993. Documentation includes work by
-
# Mark-Jason Dominus.
-
#
-
# == Licence
-
# Copyright © 2004–2013 Austin Ziegler
-
# This program is free software; you can redistribute it and/or modify it
-
# under the same terms as Ruby, or alternatively under the Perl Artistic
-
# licence.
-
#
-
# == Credits
-
# Much of the documentation is taken directly from the Perl Algorithm::Diff
-
# implementation and was written originally by Mark-Jason Dominus and later
-
# by Ned Konz. The basic Ruby implementation was re-ported from the
-
# Smalltalk implementation, available at
-
# ftp://st.cs.uiuc.edu/pub/Smalltalk/MANCHESTER/manchester/4.0/diff.st
-
#
-
# #sdiff and #traverse_balanced were written for the Perl version by Mike
-
# Schilli <m@perlmeister.com>.
-
#
-
# "The algorithm is described in <em>A Fast Algorithm for Computing Longest
-
# Common Subsequences</em>, CACM, vol.20, no.5, pp.350-353, May
-
# 1977, with a few minor improvements to improve the speed."
-
1
module Diff::LCS
-
1
VERSION = '1.2.5'
-
end
-
-
1
require 'diff/lcs/callbacks'
-
1
require 'diff/lcs/internals'
-
-
1
module Diff::LCS
-
# Returns an Array containing the longest common subsequence(s) between
-
# +self+ and +other+. See Diff::LCS#LCS.
-
#
-
# lcs = seq1.lcs(seq2)
-
1
def lcs(other, &block) #:yields self[i] if there are matched subsequences:
-
Diff::LCS.lcs(self, other, &block)
-
end
-
-
# Returns the difference set between +self+ and +other+. See
-
# Diff::LCS#diff.
-
1
def diff(other, callbacks = nil, &block)
-
Diff::LCS.diff(self, other, callbacks, &block)
-
end
-
-
# Returns the balanced ("side-by-side") difference set between +self+ and
-
# +other+. See Diff::LCS#sdiff.
-
1
def sdiff(other, callbacks = nil, &block)
-
Diff::LCS.sdiff(self, other, callbacks, &block)
-
end
-
-
# Traverses the discovered longest common subsequences between +self+ and
-
# +other+. See Diff::LCS#traverse_sequences.
-
1
def traverse_sequences(other, callbacks = nil, &block)
-
traverse_sequences(self, other, callbacks ||
-
Diff::LCS.YieldingCallbacks, &block)
-
end
-
-
# Traverses the discovered longest common subsequences between +self+ and
-
# +other+ using the alternate, balanced algorithm. See
-
# Diff::LCS#traverse_balanced.
-
1
def traverse_balanced(other, callbacks = nil, &block)
-
traverse_balanced(self, other, callbacks ||
-
Diff::LCS.YieldingCallbacks, &block)
-
end
-
-
# Attempts to patch +self+ with the provided +patchset+. A new sequence
-
# based on +self+ and the +patchset+ will be created. See Diff::LCS#patch.
-
# Attempts to autodiscover the direction of the patch.
-
1
def patch(patchset)
-
Diff::LCS.patch(self, patchset)
-
end
-
1
alias_method :unpatch, :patch
-
-
# Attempts to patch +self+ with the provided +patchset+. A new sequence
-
# based on +self+ and the +patchset+ will be created. See Diff::LCS#patch.
-
# Does no patch direction autodiscovery.
-
1
def patch!(patchset)
-
Diff::LCS.patch!(self, patchset)
-
end
-
-
# Attempts to unpatch +self+ with the provided +patchset+. A new sequence
-
# based on +self+ and the +patchset+ will be created. See Diff::LCS#unpatch.
-
# Does no patch direction autodiscovery.
-
1
def unpatch!(patchset)
-
Diff::LCS.unpatch!(self, patchset)
-
end
-
-
# Attempts to patch +self+ with the provided +patchset+, using #patch!. If
-
# the sequence this is used on supports #replace, the value of +self+ will
-
# be replaced. See Diff::LCS#patch. Does no patch direction autodiscovery.
-
1
def patch_me(patchset)
-
if respond_to? :replace
-
replace(patch!(patchset))
-
else
-
patch!(patchset)
-
end
-
end
-
-
# Attempts to unpatch +self+ with the provided +patchset+, using
-
# #unpatch!. If the sequence this is used on supports #replace, the value
-
# of +self+ will be replaced. See Diff::LCS#unpatch. Does no patch direction
-
# autodiscovery.
-
1
def unpatch_me(patchset)
-
if respond_to? :replace
-
replace(unpatch!(patchset))
-
else
-
unpatch!(patchset)
-
end
-
end
-
end
-
-
1
class << Diff::LCS
-
1
def lcs(seq1, seq2, &block) #:yields seq1[i] for each matched:
-
matches = Diff::LCS::Internals.lcs(seq1, seq2)
-
ret = []
-
string = seq1.kind_of? String
-
matches.each_with_index do |e, i|
-
unless matches[i].nil?
-
v = string ? seq1[i, 1] : seq1[i]
-
v = block[v] if block
-
ret << v
-
end
-
end
-
ret
-
end
-
1
alias_method :LCS, :lcs
-
-
# #diff computes the smallest set of additions and deletions necessary to
-
# turn the first sequence into the second, and returns a description of
-
# these changes.
-
#
-
# See Diff::LCS::DiffCallbacks for the default behaviour. An alternate
-
# behaviour may be implemented with Diff::LCS::ContextDiffCallbacks. If a
-
# Class argument is provided for +callbacks+, #diff will attempt to
-
# initialise it. If the +callbacks+ object (possibly initialised) responds
-
# to #finish, it will be called.
-
1
def diff(seq1, seq2, callbacks = nil, &block) # :yields diff changes:
-
diff_traversal(:diff, seq1, seq2, callbacks || Diff::LCS::DiffCallbacks,
-
&block)
-
end
-
-
# #sdiff computes all necessary components to show two sequences and their
-
# minimized differences side by side, just like the Unix utility
-
# <em>sdiff</em> does:
-
#
-
# old < -
-
# same same
-
# before | after
-
# - > new
-
#
-
# See Diff::LCS::SDiffCallbacks for the default behaviour. An alternate
-
# behaviour may be implemented with Diff::LCS::ContextDiffCallbacks. If a
-
# Class argument is provided for +callbacks+, #diff will attempt to
-
# initialise it. If the +callbacks+ object (possibly initialised) responds
-
# to #finish, it will be called.
-
1
def sdiff(seq1, seq2, callbacks = nil, &block) #:yields diff changes:
-
diff_traversal(:sdiff, seq1, seq2, callbacks || Diff::LCS::SDiffCallbacks,
-
&block)
-
end
-
-
# #traverse_sequences is the most general facility provided by this
-
# module; #diff and #lcs are implemented as calls to it.
-
#
-
# The arguments to #traverse_sequences are the two sequences to traverse,
-
# and a callback object, like this:
-
#
-
# traverse_sequences(seq1, seq2, Diff::LCS::ContextDiffCallbacks.new)
-
#
-
# == Callback Methods
-
#
-
# Optional callback methods are <em>emphasized</em>.
-
#
-
# callbacks#match:: Called when +a+ and +b+ are pointing to
-
# common elements in +A+ and +B+.
-
# callbacks#discard_a:: Called when +a+ is pointing to an
-
# element not in +B+.
-
# callbacks#discard_b:: Called when +b+ is pointing to an
-
# element not in +A+.
-
# <em>callbacks#finished_a</em>:: Called when +a+ has reached the end of
-
# sequence +A+.
-
# <em>callbacks#finished_b</em>:: Called when +b+ has reached the end of
-
# sequence +B+.
-
#
-
# == Algorithm
-
#
-
# a---+
-
# v
-
# A = a b c e h j l m n p
-
# B = b c d e f j k l m r s t
-
# ^
-
# b---+
-
#
-
# If there are two arrows (+a+ and +b+) pointing to elements of sequences
-
# +A+ and +B+, the arrows will initially point to the first elements of
-
# their respective sequences. #traverse_sequences will advance the arrows
-
# through the sequences one element at a time, calling a method on the
-
# user-specified callback object before each advance. It will advance the
-
# arrows in such a way that if there are elements <tt>A[i]</tt> and
-
# <tt>B[j]</tt> which are both equal and part of the longest common
-
# subsequence, there will be some moment during the execution of
-
# #traverse_sequences when arrow +a+ is pointing to <tt>A[i]</tt> and
-
# arrow +b+ is pointing to <tt>B[j]</tt>. When this happens,
-
# #traverse_sequences will call <tt>callbacks#match</tt> and then it will
-
# advance both arrows.
-
#
-
# Otherwise, one of the arrows is pointing to an element of its sequence
-
# that is not part of the longest common subsequence. #traverse_sequences
-
# will advance that arrow and will call <tt>callbacks#discard_a</tt> or
-
# <tt>callbacks#discard_b</tt>, depending on which arrow it advanced. If
-
# both arrows point to elements that are not part of the longest common
-
# subsequence, then #traverse_sequences will advance one of them and call
-
# the appropriate callback, but it is not specified which it will call.
-
#
-
# The methods for <tt>callbacks#match</tt>, <tt>callbacks#discard_a</tt>,
-
# and <tt>callbacks#discard_b</tt> are invoked with an event comprising
-
# the action ("=", "+", or "-", respectively), the indicies +i+ and +j+,
-
# and the elements <tt>A[i]</tt> and <tt>B[j]</tt>. Return values are
-
# discarded by #traverse_sequences.
-
#
-
# === End of Sequences
-
#
-
# If arrow +a+ reaches the end of its sequence before arrow +b+ does,
-
# #traverse_sequence will try to call <tt>callbacks#finished_a</tt> with
-
# the last index and element of +A+ (<tt>A[-1]</tt>) and the current index
-
# and element of +B+ (<tt>B[j]</tt>). If <tt>callbacks#finished_a</tt>
-
# does not exist, then <tt>callbacks#discard_b</tt> will be called on each
-
# element of +B+ until the end of the sequence is reached (the call will
-
# be done with <tt>A[-1]</tt> and <tt>B[j]</tt> for each element).
-
#
-
# If +b+ reaches the end of +B+ before +a+ reaches the end of +A+,
-
# <tt>callbacks#finished_b</tt> will be called with the current index and
-
# element of +A+ (<tt>A[i]</tt>) and the last index and element of +B+
-
# (<tt>A[-1]</tt>). Again, if <tt>callbacks#finished_b</tt> does not exist
-
# on the callback object, then <tt>callbacks#discard_a</tt> will be called
-
# on each element of +A+ until the end of the sequence is reached
-
# (<tt>A[i]</tt> and <tt>B[-1]</tt>).
-
#
-
# There is a chance that one additional <tt>callbacks#discard_a</tt> or
-
# <tt>callbacks#discard_b</tt> will be called after the end of the
-
# sequence is reached, if +a+ has not yet reached the end of +A+ or +b+
-
# has not yet reached the end of +B+.
-
1
def traverse_sequences(seq1, seq2, callbacks = Diff::LCS::SequenceCallbacks, &block) #:yields change events:
-
callbacks ||= Diff::LCS::SequenceCallbacks
-
matches = Diff::LCS::Internals.lcs(seq1, seq2)
-
-
run_finished_a = run_finished_b = false
-
string = seq1.kind_of?(String)
-
-
a_size = seq1.size
-
b_size = seq2.size
-
ai = bj = 0
-
-
(0..matches.size).each do |i|
-
b_line = matches[i]
-
-
ax = string ? seq1[i, 1] : seq1[i]
-
bx = string ? seq2[bj, 1] : seq2[bj]
-
-
if b_line.nil?
-
unless ax.nil? or (string and ax.empty?)
-
event = Diff::LCS::ContextChange.new('-', i, ax, bj, bx)
-
event = yield event if block_given?
-
callbacks.discard_a(event)
-
end
-
else
-
loop do
-
break unless bj < b_line
-
bx = string ? seq2[bj, 1] : seq2[bj]
-
event = Diff::LCS::ContextChange.new('+', i, ax, bj, bx)
-
event = yield event if block_given?
-
callbacks.discard_b(event)
-
bj += 1
-
end
-
bx = string ? seq2[bj, 1] : seq2[bj]
-
event = Diff::LCS::ContextChange.new('=', i, ax, bj, bx)
-
event = yield event if block_given?
-
callbacks.match(event)
-
bj += 1
-
end
-
ai = i
-
end
-
ai += 1
-
-
# The last entry (if any) processed was a match. +ai+ and +bj+ point
-
# just past the last matching lines in their sequences.
-
while (ai < a_size) or (bj < b_size)
-
# last A?
-
if ai == a_size and bj < b_size
-
if callbacks.respond_to?(:finished_a) and not run_finished_a
-
ax = string ? seq1[-1, 1] : seq1[-1]
-
bx = string ? seq2[bj, 1] : seq2[bj]
-
event = Diff::LCS::ContextChange.new('>', (a_size - 1), ax, bj, bx)
-
event = yield event if block_given?
-
callbacks.finished_a(event)
-
run_finished_a = true
-
else
-
ax = string ? seq1[ai, 1] : seq1[ai]
-
loop do
-
bx = string ? seq2[bj, 1] : seq2[bj]
-
event = Diff::LCS::ContextChange.new('+', ai, ax, bj, bx)
-
event = yield event if block_given?
-
callbacks.discard_b(event)
-
bj += 1
-
break unless bj < b_size
-
end
-
end
-
end
-
-
# last B?
-
if bj == b_size and ai < a_size
-
if callbacks.respond_to?(:finished_b) and not run_finished_b
-
ax = string ? seq1[ai, 1] : seq1[ai]
-
bx = string ? seq2[-1, 1] : seq2[-1]
-
event = Diff::LCS::ContextChange.new('<', ai, ax, (b_size - 1), bx)
-
event = yield event if block_given?
-
callbacks.finished_b(event)
-
run_finished_b = true
-
else
-
bx = string ? seq2[bj, 1] : seq2[bj]
-
loop do
-
ax = string ? seq1[ai, 1] : seq1[ai]
-
event = Diff::LCS::ContextChange.new('-', ai, ax, bj, bx)
-
event = yield event if block_given?
-
callbacks.discard_a(event)
-
ai += 1
-
break unless bj < b_size
-
end
-
end
-
end
-
-
if ai < a_size
-
ax = string ? seq1[ai, 1] : seq1[ai]
-
bx = string ? seq2[bj, 1] : seq2[bj]
-
event = Diff::LCS::ContextChange.new('-', ai, ax, bj, bx)
-
event = yield event if block_given?
-
callbacks.discard_a(event)
-
ai += 1
-
end
-
-
if bj < b_size
-
ax = string ? seq1[ai, 1] : seq1[ai]
-
bx = string ? seq2[bj, 1] : seq2[bj]
-
event = Diff::LCS::ContextChange.new('+', ai, ax, bj, bx)
-
event = yield event if block_given?
-
callbacks.discard_b(event)
-
bj += 1
-
end
-
end
-
end
-
-
# #traverse_balanced is an alternative to #traverse_sequences. It uses a
-
# different algorithm to iterate through the entries in the computed
-
# longest common subsequence. Instead of viewing the changes as insertions
-
# or deletions from one of the sequences, #traverse_balanced will report
-
# <em>changes</em> between the sequences.
-
#
-
# The arguments to #traverse_balanced are the two sequences to traverse
-
# and a callback object, like this:
-
#
-
# traverse_balanced(seq1, seq2, Diff::LCS::ContextDiffCallbacks.new)
-
#
-
# #sdiff is implemented with #traverse_balanced.
-
#
-
# == Callback Methods
-
#
-
# Optional callback methods are <em>emphasized</em>.
-
#
-
# callbacks#match:: Called when +a+ and +b+ are pointing to
-
# common elements in +A+ and +B+.
-
# callbacks#discard_a:: Called when +a+ is pointing to an
-
# element not in +B+.
-
# callbacks#discard_b:: Called when +b+ is pointing to an
-
# element not in +A+.
-
# <em>callbacks#change</em>:: Called when +a+ and +b+ are pointing to
-
# the same relative position, but
-
# <tt>A[a]</tt> and <tt>B[b]</tt> are not
-
# the same; a <em>change</em> has
-
# occurred.
-
#
-
# #traverse_balanced might be a bit slower than #traverse_sequences,
-
# noticable only while processing huge amounts of data.
-
#
-
# == Algorithm
-
#
-
# a---+
-
# v
-
# A = a b c e h j l m n p
-
# B = b c d e f j k l m r s t
-
# ^
-
# b---+
-
#
-
# === Matches
-
#
-
# If there are two arrows (+a+ and +b+) pointing to elements of sequences
-
# +A+ and +B+, the arrows will initially point to the first elements of
-
# their respective sequences. #traverse_sequences will advance the arrows
-
# through the sequences one element at a time, calling a method on the
-
# user-specified callback object before each advance. It will advance the
-
# arrows in such a way that if there are elements <tt>A[i]</tt> and
-
# <tt>B[j]</tt> which are both equal and part of the longest common
-
# subsequence, there will be some moment during the execution of
-
# #traverse_sequences when arrow +a+ is pointing to <tt>A[i]</tt> and
-
# arrow +b+ is pointing to <tt>B[j]</tt>. When this happens,
-
# #traverse_sequences will call <tt>callbacks#match</tt> and then it will
-
# advance both arrows.
-
#
-
# === Discards
-
#
-
# Otherwise, one of the arrows is pointing to an element of its sequence
-
# that is not part of the longest common subsequence. #traverse_sequences
-
# will advance that arrow and will call <tt>callbacks#discard_a</tt> or
-
# <tt>callbacks#discard_b</tt>, depending on which arrow it advanced.
-
#
-
# === Changes
-
#
-
# If both +a+ and +b+ point to elements that are not part of the longest
-
# common subsequence, then #traverse_sequences will try to call
-
# <tt>callbacks#change</tt> and advance both arrows. If
-
# <tt>callbacks#change</tt> is not implemented, then
-
# <tt>callbacks#discard_a</tt> and <tt>callbacks#discard_b</tt> will be
-
# called in turn.
-
#
-
# The methods for <tt>callbacks#match</tt>, <tt>callbacks#discard_a</tt>,
-
# <tt>callbacks#discard_b</tt>, and <tt>callbacks#change</tt> are invoked
-
# with an event comprising the action ("=", "+", "-", or "!",
-
# respectively), the indicies +i+ and +j+, and the elements
-
# <tt>A[i]</tt> and <tt>B[j]</tt>. Return values are discarded by
-
# #traverse_balanced.
-
#
-
# === Context
-
# Note that +i+ and +j+ may not be the same index position, even if +a+
-
# and +b+ are considered to be pointing to matching or changed elements.
-
1
def traverse_balanced(seq1, seq2, callbacks = Diff::LCS::BalancedCallbacks)
-
matches = Diff::LCS::Internals.lcs(seq1, seq2)
-
a_size = seq1.size
-
b_size = seq2.size
-
ai = bj = mb = 0
-
ma = -1
-
string = seq1.kind_of?(String)
-
-
# Process all the lines in the match vector.
-
loop do
-
# Find next match indices +ma+ and +mb+
-
loop do
-
ma += 1
-
break unless ma < matches.size and matches[ma].nil?
-
end
-
-
break if ma >= matches.size # end of matches?
-
mb = matches[ma]
-
-
# Change(seq2)
-
while (ai < ma) or (bj < mb)
-
ax = string ? seq1[ai, 1] : seq1[ai]
-
bx = string ? seq2[bj, 1] : seq2[bj]
-
-
case [(ai < ma), (bj < mb)]
-
when [true, true]
-
if callbacks.respond_to?(:change)
-
event = Diff::LCS::ContextChange.new('!', ai, ax, bj, bx)
-
event = yield event if block_given?
-
callbacks.change(event)
-
ai += 1
-
bj += 1
-
else
-
event = Diff::LCS::ContextChange.new('-', ai, ax, bj, bx)
-
event = yield event if block_given?
-
callbacks.discard_a(event)
-
ai += 1
-
ax = string ? seq1[ai, 1] : seq1[ai]
-
event = Diff::LCS::ContextChange.new('+', ai, ax, bj, bx)
-
event = yield event if block_given?
-
callbacks.discard_b(event)
-
bj += 1
-
end
-
when [true, false]
-
event = Diff::LCS::ContextChange.new('-', ai, ax, bj, bx)
-
event = yield event if block_given?
-
callbacks.discard_a(event)
-
ai += 1
-
when [false, true]
-
event = Diff::LCS::ContextChange.new('+', ai, ax, bj, bx)
-
event = yield event if block_given?
-
callbacks.discard_b(event)
-
bj += 1
-
end
-
end
-
-
# Match
-
ax = string ? seq1[ai, 1] : seq1[ai]
-
bx = string ? seq2[bj, 1] : seq2[bj]
-
event = Diff::LCS::ContextChange.new('=', ai, ax, bj, bx)
-
event = yield event if block_given?
-
callbacks.match(event)
-
ai += 1
-
bj += 1
-
end
-
-
while (ai < a_size) or (bj < b_size)
-
ax = string ? seq1[ai, 1] : seq1[ai]
-
bx = string ? seq2[bj, 1] : seq2[bj]
-
-
case [(ai < a_size), (bj < b_size)]
-
when [true, true]
-
if callbacks.respond_to?(:change)
-
event = Diff::LCS::ContextChange.new('!', ai, ax, bj, bx)
-
event = yield event if block_given?
-
callbacks.change(event)
-
ai += 1
-
bj += 1
-
else
-
event = Diff::LCS::ContextChange.new('-', ai, ax, bj, bx)
-
event = yield event if block_given?
-
callbacks.discard_a(event)
-
ai += 1
-
ax = string ? seq1[ai, 1] : seq1[ai]
-
event = Diff::LCS::ContextChange.new('+', ai, ax, bj, bx)
-
event = yield event if block_given?
-
callbacks.discard_b(event)
-
bj += 1
-
end
-
when [true, false]
-
event = Diff::LCS::ContextChange.new('-', ai, ax, bj, bx)
-
event = yield event if block_given?
-
callbacks.discard_a(event)
-
ai += 1
-
when [false, true]
-
event = Diff::LCS::ContextChange.new('+', ai, ax, bj, bx)
-
event = yield event if block_given?
-
callbacks.discard_b(event)
-
bj += 1
-
end
-
end
-
end
-
-
1
PATCH_MAP = { #:nodoc:
-
:patch => { '+' => '+', '-' => '-', '!' => '!', '=' => '=' },
-
:unpatch => { '+' => '-', '-' => '+', '!' => '!', '=' => '=' }
-
}
-
-
# Applies a +patchset+ to the sequence +src+ according to the +direction+
-
# (<tt>:patch</tt> or <tt>:unpatch</tt>), producing a new sequence.
-
#
-
# If the +direction+ is not specified, Diff::LCS::patch will attempt to
-
# discover the direction of the +patchset+.
-
#
-
# A +patchset+ can be considered to apply forward (<tt>:patch</tt>) if the
-
# following expression is true:
-
#
-
# patch(s1, diff(s1, s2)) -> s2
-
#
-
# A +patchset+ can be considered to apply backward (<tt>:unpatch</tt>) if
-
# the following expression is true:
-
#
-
# patch(s2, diff(s1, s2)) -> s1
-
#
-
# If the +patchset+ contains no changes, the +src+ value will be returned
-
# as either <tt>src.dup</tt> or +src+. A +patchset+ can be deemed as
-
# having no changes if the following predicate returns true:
-
#
-
# patchset.empty? or
-
# patchset.flatten.all? { |change| change.unchanged? }
-
#
-
# === Patchsets
-
#
-
# A +patchset+ is always an enumerable sequence of changes, hunks of
-
# changes, or a mix of the two. A hunk of changes is an enumerable
-
# sequence of changes:
-
#
-
# [ # patchset
-
# # change
-
# [ # hunk
-
# # change
-
# ]
-
# ]
-
#
-
# The +patch+ method accepts <tt>patchset</tt>s that are enumerable
-
# sequences containing either Diff::LCS::Change objects (or a subclass) or
-
# the array representations of those objects. Prior to application, array
-
# representations of Diff::LCS::Change objects will be reified.
-
1
def patch(src, patchset, direction = nil)
-
# Normalize the patchset.
-
has_changes, patchset = Diff::LCS::Internals.analyze_patchset(patchset)
-
-
if not has_changes
-
return src.dup if src.respond_to? :dup
-
return src
-
end
-
-
string = src.kind_of?(String)
-
# Start with a new empty type of the source's class
-
res = src.class.new
-
-
direction ||= Diff::LCS::Internals.intuit_diff_direction(src, patchset)
-
-
ai = bj = 0
-
-
patch_map = PATCH_MAP[direction]
-
-
patchset.flatten.each do |change|
-
# Both Change and ContextChange support #action
-
action = patch_map[change.action]
-
-
case change
-
when Diff::LCS::ContextChange
-
case direction
-
when :patch
-
el = change.new_element
-
op = change.old_position
-
np = change.new_position
-
when :unpatch
-
el = change.old_element
-
op = change.new_position
-
np = change.old_position
-
end
-
-
case action
-
when '-' # Remove details from the old string
-
while ai < op
-
res << (string ? src[ai, 1] : src[ai])
-
ai += 1
-
bj += 1
-
end
-
ai += 1
-
when '+'
-
while bj < np
-
res << (string ? src[ai, 1] : src[ai])
-
ai += 1
-
bj += 1
-
end
-
-
res << el
-
bj += 1
-
when '='
-
# This only appears in sdiff output with the SDiff callback.
-
# Therefore, we only need to worry about dealing with a single
-
# element.
-
res << el
-
-
ai += 1
-
bj += 1
-
when '!'
-
while ai < op
-
res << (string ? src[ai, 1] : src[ai])
-
ai += 1
-
bj += 1
-
end
-
-
bj += 1
-
ai += 1
-
-
res << el
-
end
-
when Diff::LCS::Change
-
case action
-
when '-'
-
while ai < change.position
-
res << (string ? src[ai, 1] : src[ai])
-
ai += 1
-
bj += 1
-
end
-
ai += 1
-
when '+'
-
while bj < change.position
-
res << (string ? src[ai, 1] : src[ai])
-
ai += 1
-
bj += 1
-
end
-
-
bj += 1
-
-
res << change.element
-
end
-
end
-
end
-
-
while ai < src.size
-
res << (string ? src[ai, 1] : src[ai])
-
ai += 1
-
bj += 1
-
end
-
-
res
-
end
-
-
# Given a set of patchset, convert the current version to the prior
-
# version. Does no auto-discovery.
-
1
def unpatch!(src, patchset)
-
patch(src, patchset, :unpatch)
-
end
-
-
# Given a set of patchset, convert the current version to the next
-
# version. Does no auto-discovery.
-
1
def patch!(src, patchset)
-
patch(src, patchset, :patch)
-
end
-
end
-
# -*- ruby encoding: utf-8 -*-
-
-
# A block is an operation removing, adding, or changing a group of items.
-
# Basically, this is just a list of changes, where each change adds or
-
# deletes a single item. Used by bin/ldiff.
-
1
class Diff::LCS::Block
-
1
attr_reader :changes, :insert, :remove
-
-
1
def initialize(chunk)
-
@changes = []
-
@insert = []
-
@remove = []
-
-
chunk.each do |item|
-
@changes << item
-
@remove << item if item.deleting?
-
@insert << item if item.adding?
-
end
-
end
-
-
1
def diff_size
-
@insert.size - @remove.size
-
end
-
-
1
def op
-
case [@remove.empty?, @insert.empty?]
-
when [false, false]
-
'!'
-
when [false, true]
-
'-'
-
when [true, false]
-
'+'
-
else # [true, true]
-
'^'
-
end
-
end
-
end
-
# -*- ruby encoding: utf-8 -*-
-
-
1
require 'diff/lcs/change'
-
-
1
module Diff::LCS
-
# This callback object implements the default set of callback events,
-
# which only returns the event itself. Note that #finished_a and
-
# #finished_b are not implemented -- I haven't yet figured out where they
-
# would be useful.
-
#
-
# Note that this is intended to be called as is, e.g.,
-
#
-
# Diff::LCS.LCS(seq1, seq2, Diff::LCS::DefaultCallbacks)
-
1
class DefaultCallbacks
-
1
class << self
-
# Called when two items match.
-
1
def match(event)
-
event
-
end
-
# Called when the old value is discarded in favour of the new value.
-
1
def discard_a(event)
-
event
-
end
-
# Called when the new value is discarded in favour of the old value.
-
1
def discard_b(event)
-
event
-
end
-
# Called when both the old and new values have changed.
-
1
def change(event)
-
event
-
end
-
-
1
private :new
-
end
-
end
-
-
# An alias for DefaultCallbacks that is used in
-
# Diff::LCS#traverse_sequences.
-
#
-
# Diff::LCS.LCS(seq1, seq2, Diff::LCS::SequenceCallbacks)
-
1
SequenceCallbacks = DefaultCallbacks
-
-
# An alias for DefaultCallbacks that is used in
-
# Diff::LCS#traverse_balanced.
-
#
-
# Diff::LCS.LCS(seq1, seq2, Diff::LCS::BalancedCallbacks)
-
1
BalancedCallbacks = DefaultCallbacks
-
-
1
def self.callbacks_for(callbacks)
-
callbacks.new rescue callbacks
-
end
-
end
-
-
# This will produce a compound array of simple diff change objects. Each
-
# element in the #diffs array is a +hunk+ or +hunk+ array, where each
-
# element in each +hunk+ array is a single Change object representing the
-
# addition or removal of a single element from one of the two tested
-
# sequences. The +hunk+ provides the full context for the changes.
-
#
-
# diffs = Diff::LCS.diff(seq1, seq2)
-
# # This example shows a simplified array format.
-
# # [ [ [ '-', 0, 'a' ] ], # 1
-
# # [ [ '+', 2, 'd' ] ], # 2
-
# # [ [ '-', 4, 'h' ], # 3
-
# # [ '+', 4, 'f' ] ],
-
# # [ [ '+', 6, 'k' ] ], # 4
-
# # [ [ '-', 8, 'n' ], # 5
-
# # [ '-', 9, 'p' ],
-
# # [ '+', 9, 'r' ],
-
# # [ '+', 10, 's' ],
-
# # [ '+', 11, 't' ] ] ]
-
#
-
# There are five hunks here. The first hunk says that the +a+ at position 0
-
# of the first sequence should be deleted (<tt>'-'</tt>). The second hunk
-
# says that the +d+ at position 2 of the second sequence should be inserted
-
# (<tt>'+'</tt>). The third hunk says that the +h+ at position 4 of the
-
# first sequence should be removed and replaced with the +f+ from position 4
-
# of the second sequence. The other two hunks are described similarly.
-
#
-
# === Use
-
#
-
# This callback object must be initialised and is used by the Diff::LCS#diff
-
# method.
-
#
-
# cbo = Diff::LCS::DiffCallbacks.new
-
# Diff::LCS.LCS(seq1, seq2, cbo)
-
# cbo.finish
-
#
-
# Note that the call to #finish is absolutely necessary, or the last set of
-
# changes will not be visible. Alternatively, can be used as:
-
#
-
# cbo = Diff::LCS::DiffCallbacks.new { |tcbo| Diff::LCS.LCS(seq1, seq2, tcbo) }
-
#
-
# The necessary #finish call will be made.
-
#
-
# === Simplified Array Format
-
#
-
# The simplified array format used in the example above can be obtained
-
# with:
-
#
-
# require 'pp'
-
# pp diffs.map { |e| e.map { |f| f.to_a } }
-
1
class Diff::LCS::DiffCallbacks
-
# Returns the difference set collected during the diff process.
-
1
attr_reader :diffs
-
-
1
def initialize # :yields self:
-
@hunk = []
-
@diffs = []
-
-
if block_given?
-
begin
-
yield self
-
ensure
-
self.finish
-
end
-
end
-
end
-
-
# Finalizes the diff process. If an unprocessed hunk still exists, then it
-
# is appended to the diff list.
-
1
def finish
-
finish_hunk
-
end
-
-
1
def match(event)
-
finish_hunk
-
end
-
-
1
def discard_a(event)
-
@hunk << Diff::LCS::Change.new('-', event.old_position, event.old_element)
-
end
-
-
1
def discard_b(event)
-
@hunk << Diff::LCS::Change.new('+', event.new_position, event.new_element)
-
end
-
-
1
def finish_hunk
-
@diffs << @hunk unless @hunk.empty?
-
@hunk = []
-
end
-
1
private :finish_hunk
-
end
-
-
# This will produce a compound array of contextual diff change objects. Each
-
# element in the #diffs array is a "hunk" array, where each element in each
-
# "hunk" array is a single change. Each change is a Diff::LCS::ContextChange
-
# that contains both the old index and new index values for the change. The
-
# "hunk" provides the full context for the changes. Both old and new objects
-
# will be presented for changed objects. +nil+ will be substituted for a
-
# discarded object.
-
#
-
# seq1 = %w(a b c e h j l m n p)
-
# seq2 = %w(b c d e f j k l m r s t)
-
#
-
# diffs = Diff::LCS.diff(seq1, seq2, Diff::LCS::ContextDiffCallbacks)
-
# # This example shows a simplified array format.
-
# # [ [ [ '-', [ 0, 'a' ], [ 0, nil ] ] ], # 1
-
# # [ [ '+', [ 3, nil ], [ 2, 'd' ] ] ], # 2
-
# # [ [ '-', [ 4, 'h' ], [ 4, nil ] ], # 3
-
# # [ '+', [ 5, nil ], [ 4, 'f' ] ] ],
-
# # [ [ '+', [ 6, nil ], [ 6, 'k' ] ] ], # 4
-
# # [ [ '-', [ 8, 'n' ], [ 9, nil ] ], # 5
-
# # [ '+', [ 9, nil ], [ 9, 'r' ] ],
-
# # [ '-', [ 9, 'p' ], [ 10, nil ] ],
-
# # [ '+', [ 10, nil ], [ 10, 's' ] ],
-
# # [ '+', [ 10, nil ], [ 11, 't' ] ] ] ]
-
#
-
# The five hunks shown are comprised of individual changes; if there is a
-
# related set of changes, they are still shown individually.
-
#
-
# This callback can also be used with Diff::LCS#sdiff, which will produce
-
# results like:
-
#
-
# diffs = Diff::LCS.sdiff(seq1, seq2, Diff::LCS::ContextCallbacks)
-
# # This example shows a simplified array format.
-
# # [ [ [ "-", [ 0, "a" ], [ 0, nil ] ] ], # 1
-
# # [ [ "+", [ 3, nil ], [ 2, "d" ] ] ], # 2
-
# # [ [ "!", [ 4, "h" ], [ 4, "f" ] ] ], # 3
-
# # [ [ "+", [ 6, nil ], [ 6, "k" ] ] ], # 4
-
# # [ [ "!", [ 8, "n" ], [ 9, "r" ] ], # 5
-
# # [ "!", [ 9, "p" ], [ 10, "s" ] ],
-
# # [ "+", [ 10, nil ], [ 11, "t" ] ] ] ]
-
#
-
# The five hunks are still present, but are significantly shorter in total
-
# presentation, because changed items are shown as changes ("!") instead of
-
# potentially "mismatched" pairs of additions and deletions.
-
#
-
# The result of this operation is similar to that of
-
# Diff::LCS::SDiffCallbacks. They may be compared as:
-
#
-
# s = Diff::LCS.sdiff(seq1, seq2).reject { |e| e.action == "=" }
-
# c = Diff::LCS.sdiff(seq1, seq2, Diff::LCS::ContextDiffCallbacks).flatten
-
#
-
# s == c # -> true
-
#
-
# === Use
-
#
-
# This callback object must be initialised and can be used by the
-
# Diff::LCS#diff or Diff::LCS#sdiff methods.
-
#
-
# cbo = Diff::LCS::ContextDiffCallbacks.new
-
# Diff::LCS.LCS(seq1, seq2, cbo)
-
# cbo.finish
-
#
-
# Note that the call to #finish is absolutely necessary, or the last set of
-
# changes will not be visible. Alternatively, can be used as:
-
#
-
# cbo = Diff::LCS::ContextDiffCallbacks.new { |tcbo| Diff::LCS.LCS(seq1, seq2, tcbo) }
-
#
-
# The necessary #finish call will be made.
-
#
-
# === Simplified Array Format
-
#
-
# The simplified array format used in the example above can be obtained
-
# with:
-
#
-
# require 'pp'
-
# pp diffs.map { |e| e.map { |f| f.to_a } }
-
1
class Diff::LCS::ContextDiffCallbacks < Diff::LCS::DiffCallbacks
-
1
def discard_a(event)
-
@hunk << Diff::LCS::ContextChange.simplify(event)
-
end
-
-
1
def discard_b(event)
-
@hunk << Diff::LCS::ContextChange.simplify(event)
-
end
-
-
1
def change(event)
-
@hunk << Diff::LCS::ContextChange.simplify(event)
-
end
-
end
-
-
# This will produce a simple array of diff change objects. Each element in
-
# the #diffs array is a single ContextChange. In the set of #diffs provided
-
# by SDiffCallbacks, both old and new objects will be presented for both
-
# changed <strong>and unchanged</strong> objects. +nil+ will be substituted
-
# for a discarded object.
-
#
-
# The diffset produced by this callback, when provided to Diff::LCS#sdiff,
-
# will compute and display the necessary components to show two sequences
-
# and their minimized differences side by side, just like the Unix utility
-
# +sdiff+.
-
#
-
# same same
-
# before | after
-
# old < -
-
# - > new
-
#
-
# seq1 = %w(a b c e h j l m n p)
-
# seq2 = %w(b c d e f j k l m r s t)
-
#
-
# diffs = Diff::LCS.sdiff(seq1, seq2)
-
# # This example shows a simplified array format.
-
# # [ [ "-", [ 0, "a"], [ 0, nil ] ],
-
# # [ "=", [ 1, "b"], [ 0, "b" ] ],
-
# # [ "=", [ 2, "c"], [ 1, "c" ] ],
-
# # [ "+", [ 3, nil], [ 2, "d" ] ],
-
# # [ "=", [ 3, "e"], [ 3, "e" ] ],
-
# # [ "!", [ 4, "h"], [ 4, "f" ] ],
-
# # [ "=", [ 5, "j"], [ 5, "j" ] ],
-
# # [ "+", [ 6, nil], [ 6, "k" ] ],
-
# # [ "=", [ 6, "l"], [ 7, "l" ] ],
-
# # [ "=", [ 7, "m"], [ 8, "m" ] ],
-
# # [ "!", [ 8, "n"], [ 9, "r" ] ],
-
# # [ "!", [ 9, "p"], [ 10, "s" ] ],
-
# # [ "+", [ 10, nil], [ 11, "t" ] ] ]
-
#
-
# The result of this operation is similar to that of
-
# Diff::LCS::ContextDiffCallbacks. They may be compared as:
-
#
-
# s = Diff::LCS.sdiff(seq1, seq2).reject { |e| e.action == "=" }
-
# c = Diff::LCS.sdiff(seq1, seq2, Diff::LCS::ContextDiffCallbacks).flatten
-
#
-
# s == c # -> true
-
#
-
# === Use
-
#
-
# This callback object must be initialised and is used by the Diff::LCS#sdiff
-
# method.
-
#
-
# cbo = Diff::LCS::SDiffCallbacks.new
-
# Diff::LCS.LCS(seq1, seq2, cbo)
-
#
-
# As with the other initialisable callback objects,
-
# Diff::LCS::SDiffCallbacks can be initialised with a block. As there is no
-
# "fininishing" to be done, this has no effect on the state of the object.
-
#
-
# cbo = Diff::LCS::SDiffCallbacks.new { |tcbo| Diff::LCS.LCS(seq1, seq2, tcbo) }
-
#
-
# === Simplified Array Format
-
#
-
# The simplified array format used in the example above can be obtained
-
# with:
-
#
-
# require 'pp'
-
# pp diffs.map { |e| e.to_a }
-
1
class Diff::LCS::SDiffCallbacks
-
# Returns the difference set collected during the diff process.
-
1
attr_reader :diffs
-
-
1
def initialize #:yields self:
-
@diffs = []
-
yield self if block_given?
-
end
-
-
1
def match(event)
-
@diffs << Diff::LCS::ContextChange.simplify(event)
-
end
-
-
1
def discard_a(event)
-
@diffs << Diff::LCS::ContextChange.simplify(event)
-
end
-
-
1
def discard_b(event)
-
@diffs << Diff::LCS::ContextChange.simplify(event)
-
end
-
-
1
def change(event)
-
@diffs << Diff::LCS::ContextChange.simplify(event)
-
end
-
end
-
# -*- ruby encoding: utf-8 -*-
-
-
# Represents a simplistic (non-contextual) change. Represents the removal or
-
# addition of an element from either the old or the new sequenced
-
# enumerable.
-
1
class Diff::LCS::Change
-
# The only actions valid for changes are '+' (add), '-' (delete), '='
-
# (no change), '!' (changed), '<' (tail changes from first sequence), or
-
# '>' (tail changes from second sequence). The last two ('<>') are only
-
# found with Diff::LCS::diff and Diff::LCS::sdiff.
-
1
VALID_ACTIONS = %W(+ - = ! > <)
-
-
1
def self.valid_action?(action)
-
VALID_ACTIONS.include? action
-
end
-
-
# Returns the action this Change represents.
-
1
attr_reader :action
-
-
# Returns the position of the Change.
-
1
attr_reader :position
-
# Returns the sequence element of the Change.
-
1
attr_reader :element
-
-
1
def initialize(*args)
-
@action, @position, @element = *args
-
-
unless Diff::LCS::Change.valid_action?(@action)
-
raise "Invalid Change Action '#{@action}'"
-
end
-
raise "Invalid Position Type" unless @position.kind_of? Fixnum
-
end
-
-
1
def inspect
-
to_a.inspect
-
end
-
-
1
def to_a
-
[ @action, @position, @element ]
-
end
-
-
1
def self.from_a(arr)
-
arr = arr.flatten(1)
-
case arr.size
-
when 5
-
Diff::LCS::ContextChange.new(*(arr[0...5]))
-
when 3
-
Diff::LCS::Change.new(*(arr[0...3]))
-
else
-
raise "Invalid change array format provided."
-
end
-
end
-
-
1
include Comparable
-
-
1
def ==(other)
-
(self.action == other.action) and
-
(self.position == other.position) and
-
(self.element == other.element)
-
end
-
-
1
def <=>(other)
-
r = self.action <=> other.action
-
r = self.position <=> other.position if r.zero?
-
r = self.element <=> other.element if r.zero?
-
r
-
end
-
-
1
def adding?
-
@action == '+'
-
end
-
-
1
def deleting?
-
@action == '-'
-
end
-
-
1
def unchanged?
-
@action == '='
-
end
-
-
1
def changed?
-
@action == '!'
-
end
-
-
1
def finished_a?
-
@action == '>'
-
end
-
-
1
def finished_b?
-
@action == '<'
-
end
-
end
-
-
# Represents a contextual change. Contains the position and values of the
-
# elements in the old and the new sequenced enumerables as well as the action
-
# taken.
-
1
class Diff::LCS::ContextChange < Diff::LCS::Change
-
# We don't need these two values.
-
1
undef :position
-
1
undef :element
-
-
# Returns the old position being changed.
-
1
attr_reader :old_position
-
# Returns the new position being changed.
-
1
attr_reader :new_position
-
# Returns the old element being changed.
-
1
attr_reader :old_element
-
# Returns the new element being changed.
-
1
attr_reader :new_element
-
-
1
def initialize(*args)
-
@action, @old_position, @old_element, @new_position, @new_element = *args
-
-
unless Diff::LCS::Change.valid_action?(@action)
-
raise "Invalid Change Action '#{@action}'"
-
end
-
unless @old_position.nil? or @old_position.kind_of? Fixnum
-
raise "Invalid (Old) Position Type"
-
end
-
unless @new_position.nil? or @new_position.kind_of? Fixnum
-
raise "Invalid (New) Position Type"
-
end
-
end
-
-
1
def to_a
-
[ @action,
-
[ @old_position, @old_element ],
-
[ @new_position, @new_element ]
-
]
-
end
-
-
1
def inspect(*args)
-
to_a.inspect
-
end
-
-
1
def self.from_a(arr)
-
Diff::LCS::Change.from_a(arr)
-
end
-
-
# Simplifies a context change for use in some diff callbacks. '<' actions
-
# are converted to '-' and '>' actions are converted to '+'.
-
1
def self.simplify(event)
-
ea = event.to_a
-
-
case ea[0]
-
when '-'
-
ea[2][1] = nil
-
when '<'
-
ea[0] = '-'
-
ea[2][1] = nil
-
when '+'
-
ea[1][1] = nil
-
when '>'
-
ea[0] = '+'
-
ea[1][1] = nil
-
end
-
-
Diff::LCS::ContextChange.from_a(ea)
-
end
-
-
1
def ==(other)
-
(@action == other.action) and
-
(@old_position == other.old_position) and
-
(@new_position == other.new_position) and
-
(@old_element == other.old_element) and
-
(@new_element == other.new_element)
-
end
-
-
1
def <=>(other)
-
r = @action <=> other.action
-
r = @old_position <=> other.old_position if r.zero?
-
r = @new_position <=> other.new_position if r.zero?
-
r = @old_element <=> other.old_element if r.zero?
-
r = @new_element <=> other.new_element if r.zero?
-
r
-
end
-
end
-
# -*- ruby encoding: utf-8 -*-
-
-
1
require 'diff/lcs/block'
-
-
# A Hunk is a group of Blocks which overlap because of the context
-
# surrounding each block. (So if we're not using context, every hunk will
-
# contain one block.) Used in the diff program (bin/diff).
-
1
class Diff::LCS::Hunk
-
# Create a hunk using references to both the old and new data, as well as
-
# the piece of data.
-
1
def initialize(data_old, data_new, piece, flag_context, file_length_difference)
-
# At first, a hunk will have just one Block in it
-
@blocks = [ Diff::LCS::Block.new(piece) ]
-
if String.method_defined?(:encoding)
-
@preferred_data_encoding = data_old.fetch(0, data_new.fetch(0,'') ).encoding
-
end
-
@data_old = data_old
-
@data_new = data_new
-
-
before = after = file_length_difference
-
after += @blocks[0].diff_size
-
@file_length_difference = after # The caller must get this manually
-
-
# Save the start & end of each array. If the array doesn't exist (e.g.,
-
# we're only adding items in this block), then figure out the line
-
# number based on the line number of the other file and the current
-
# difference in file lengths.
-
if @blocks[0].remove.empty?
-
a1 = a2 = nil
-
else
-
a1 = @blocks[0].remove[0].position
-
a2 = @blocks[0].remove[-1].position
-
end
-
-
if @blocks[0].insert.empty?
-
b1 = b2 = nil
-
else
-
b1 = @blocks[0].insert[0].position
-
b2 = @blocks[0].insert[-1].position
-
end
-
-
@start_old = a1 || (b1 - before)
-
@start_new = b1 || (a1 + before)
-
@end_old = a2 || (b2 - after)
-
@end_new = b2 || (a2 + after)
-
-
self.flag_context = flag_context
-
end
-
-
1
attr_reader :blocks
-
1
attr_reader :start_old, :start_new
-
1
attr_reader :end_old, :end_new
-
1
attr_reader :file_length_difference
-
-
# Change the "start" and "end" fields to note that context should be added
-
# to this hunk.
-
1
attr_accessor :flag_context
-
1
undef :flag_context=;
-
1
def flag_context=(context) #:nodoc:
-
return if context.nil? or context.zero?
-
-
add_start = (context > @start_old) ? @start_old : context
-
@start_old -= add_start
-
@start_new -= add_start
-
-
if (@end_old + context) > @data_old.size
-
add_end = @data_old.size - @end_old
-
else
-
add_end = context
-
end
-
@end_old += add_end
-
@end_new += add_end
-
end
-
-
# Merges this hunk and the provided hunk together if they overlap. Returns
-
# a truthy value so that if there is no overlap, you can know the merge
-
# was skipped.
-
1
def merge(hunk)
-
if overlaps?(hunk)
-
@start_old = hunk.start_old
-
@start_new = hunk.start_new
-
blocks.unshift(*hunk.blocks)
-
else
-
nil
-
end
-
end
-
1
alias_method :unshift, :merge
-
-
# Determines whether there is an overlap between this hunk and the
-
# provided hunk. This will be true if the difference between the two hunks
-
# start or end positions is within one position of each other.
-
1
def overlaps?(hunk)
-
hunk and (((@start_old - hunk.end_old) <= 1) or
-
((@start_new - hunk.end_new) <= 1))
-
end
-
-
# Returns a diff string based on a format.
-
1
def diff(format)
-
case format
-
when :old
-
old_diff
-
when :unified
-
unified_diff
-
when :context
-
context_diff
-
when :ed
-
self
-
when :reverse_ed, :ed_finish
-
ed_diff(format)
-
else
-
raise "Unknown diff format #{format}."
-
end
-
end
-
-
# Note that an old diff can't have any context. Therefore, we know that
-
# there's only one block in the hunk.
-
1
def old_diff
-
warn "Expecting only one block in an old diff hunk!" if @blocks.size > 1
-
op_act = { "+" => 'a', "-" => 'd', "!" => "c" }
-
-
block = @blocks[0]
-
-
# Calculate item number range. Old diff range is just like a context
-
# diff range, except the ranges are on one line with the action between
-
# them.
-
s = encode("#{context_range(:old)}#{op_act[block.op]}#{context_range(:new)}\n")
-
# If removing anything, just print out all the remove lines in the hunk
-
# which is just all the remove lines in the block.
-
@data_old[@start_old .. @end_old].each { |e| s << encode("< ") + e + encode("\n") } unless block.remove.empty?
-
s << encode("---\n") if block.op == "!"
-
@data_new[@start_new .. @end_new].each { |e| s << encode("> ") + e + encode("\n") } unless block.insert.empty?
-
s
-
end
-
1
private :old_diff
-
-
1
def unified_diff
-
# Calculate item number range.
-
s = encode("@@ -#{unified_range(:old)} +#{unified_range(:new)} @@\n")
-
-
# Outlist starts containing the hunk of the old file. Removing an item
-
# just means putting a '-' in front of it. Inserting an item requires
-
# getting it from the new file and splicing it in. We splice in
-
# +num_added+ items. Remove blocks use +num_added+ because splicing
-
# changed the length of outlist.
-
#
-
# We remove +num_removed+ items. Insert blocks use +num_removed+
-
# because their item numbers -- corresponding to positions in the NEW
-
# file -- don't take removed items into account.
-
lo, hi, num_added, num_removed = @start_old, @end_old, 0, 0
-
-
outlist = @data_old[lo .. hi].map { |e| e.insert(0, encode(' ')) }
-
-
@blocks.each do |block|
-
block.remove.each do |item|
-
op = item.action.to_s # -
-
offset = item.position - lo + num_added
-
outlist[offset][0, 1] = encode(op)
-
num_removed += 1
-
end
-
block.insert.each do |item|
-
op = item.action.to_s # +
-
offset = item.position - @start_new + num_removed
-
outlist[offset, 0] = encode(op) + @data_new[item.position]
-
num_added += 1
-
end
-
end
-
-
s << outlist.join(encode("\n"))
-
end
-
1
private :unified_diff
-
-
1
def context_diff
-
s = encode("***************\n")
-
s << encode("*** #{context_range(:old)} ****\n")
-
r = context_range(:new)
-
-
# Print out file 1 part for each block in context diff format if there
-
# are any blocks that remove items
-
lo, hi = @start_old, @end_old
-
removes = @blocks.select { |e| not e.remove.empty? }
-
if removes
-
outlist = @data_old[lo .. hi].map { |e| e.insert(0, encode(' ')) }
-
-
removes.each do |block|
-
block.remove.each do |item|
-
outlist[item.position - lo][0, 1] = encode(block.op) # - or !
-
end
-
end
-
s << outlist.join("\n")
-
end
-
-
s << encode("\n--- #{r} ----\n")
-
lo, hi = @start_new, @end_new
-
inserts = @blocks.select { |e| not e.insert.empty? }
-
if inserts
-
outlist = @data_new[lo .. hi].collect { |e| e.insert(0, encode(' ')) }
-
inserts.each do |block|
-
block.insert.each do |item|
-
outlist[item.position - lo][0, 1] = encode(block.op) # + or !
-
end
-
end
-
s << outlist.join("\n")
-
end
-
s
-
end
-
1
private :context_diff
-
-
1
def ed_diff(format)
-
op_act = { "+" => 'a', "-" => 'd', "!" => "c" }
-
warn "Expecting only one block in an old diff hunk!" if @blocks.size > 1
-
-
if format == :reverse_ed
-
s = encode("#{op_act[@blocks[0].op]}#{context_range(:old)}\n")
-
else
-
s = encode("#{context_range(:old, ' ')}#{op_act[@blocks[0].op]}\n")
-
end
-
-
unless @blocks[0].insert.empty?
-
@data_new[@start_new .. @end_new].each { |e| s << e + encode("\n") }
-
s << encode(".\n")
-
end
-
s
-
end
-
1
private :ed_diff
-
-
# Generate a range of item numbers to print. Only print 1 number if the
-
# range has only one item in it. Otherwise, it's 'start,end'
-
1
def context_range(mode, op = ',')
-
case mode
-
when :old
-
s, e = (@start_old + 1), (@end_old + 1)
-
when :new
-
s, e = (@start_new + 1), (@end_new + 1)
-
end
-
-
(s < e) ? "#{s}#{op}#{e}" : "#{e}"
-
end
-
1
private :context_range
-
-
# Generate a range of item numbers to print for unified diff. Print number
-
# where block starts, followed by number of lines in the block
-
# (don't print number of lines if it's 1)
-
1
def unified_range(mode)
-
case mode
-
when :old
-
s, e = (@start_old + 1), (@end_old + 1)
-
when :new
-
s, e = (@start_new + 1), (@end_new + 1)
-
end
-
-
length = e - s + 1
-
first = (length < 2) ? e : s # "strange, but correct"
-
(length == 1) ? "#{first}" : "#{first},#{length}"
-
end
-
1
private :unified_range
-
-
1
if String.method_defined?(:encoding)
-
1
def encode(literal, target_encoding = @preferred_data_encoding)
-
literal.encode target_encoding
-
end
-
-
1
def encode_as(string, *args)
-
args.map { |arg| arg.encode(string.encoding) }
-
end
-
else
-
def encode(literal, target_encoding = nil)
-
literal
-
end
-
def encode_as(string, *args)
-
args
-
end
-
end
-
-
1
private :encode
-
1
private :encode_as
-
end
-
# -*- ruby encoding: utf-8 -*-
-
-
1
class << Diff::LCS
-
1
def diff_traversal(method, seq1, seq2, callbacks, &block)
-
callbacks = callbacks_for(callbacks)
-
case method
-
when :diff
-
traverse_sequences(seq1, seq2, callbacks)
-
when :sdiff
-
traverse_balanced(seq1, seq2, callbacks)
-
end
-
callbacks.finish if callbacks.respond_to? :finish
-
-
if block
-
callbacks.diffs.map do |hunk|
-
if hunk.kind_of? Array
-
hunk.map { |hunk_block| block[hunk_block] }
-
else
-
block[hunk]
-
end
-
end
-
else
-
callbacks.diffs
-
end
-
end
-
1
private :diff_traversal
-
end
-
-
1
module Diff::LCS::Internals # :nodoc:
-
end
-
-
1
class << Diff::LCS::Internals
-
# Compute the longest common subsequence between the sequenced
-
# Enumerables +a+ and +b+. The result is an array whose contents is such
-
# that
-
#
-
# result = Diff::LCS::Internals.lcs(a, b)
-
# result.each_with_index do |e, i|
-
# assert_equal(a[i], b[e]) unless e.nil?
-
# end
-
1
def lcs(a, b)
-
a_start = b_start = 0
-
a_finish = a.size - 1
-
b_finish = b.size - 1
-
vector = []
-
-
# Prune off any common elements at the beginning...
-
while ((a_start <= a_finish) and (b_start <= b_finish) and
-
(a[a_start] == b[b_start]))
-
vector[a_start] = b_start
-
a_start += 1
-
b_start += 1
-
end
-
b_start = a_start
-
-
# Now the end...
-
while ((a_start <= a_finish) and (b_start <= b_finish) and
-
(a[a_finish] == b[b_finish]))
-
vector[a_finish] = b_finish
-
a_finish -= 1
-
b_finish -= 1
-
end
-
-
# Now, compute the equivalence classes of positions of elements.
-
b_matches = position_hash(b, b_start..b_finish)
-
-
thresh = []
-
links = []
-
string = a.kind_of?(String)
-
-
(a_start .. a_finish).each do |i|
-
ai = string ? a[i, 1] : a[i]
-
bm = b_matches[ai]
-
k = nil
-
bm.reverse_each do |j|
-
if k and (thresh[k] > j) and (thresh[k - 1] < j)
-
thresh[k] = j
-
else
-
k = replace_next_larger(thresh, j, k)
-
end
-
links[k] = [ (k > 0) ? links[k - 1] : nil, i, j ] unless k.nil?
-
end
-
end
-
-
unless thresh.empty?
-
link = links[thresh.size - 1]
-
while not link.nil?
-
vector[link[1]] = link[2]
-
link = link[0]
-
end
-
end
-
-
vector
-
end
-
-
# This method will analyze the provided patchset to provide a
-
# single-pass normalization (conversion of the array form of
-
# Diff::LCS::Change objects to the object form of same) and detection of
-
# whether the patchset represents changes to be made.
-
1
def analyze_patchset(patchset, depth = 0)
-
raise "Patchset too complex" if depth > 1
-
-
has_changes = false
-
-
# Format:
-
# [ # patchset
-
# # hunk (change)
-
# [ # hunk
-
# # change
-
# ]
-
# ]
-
-
patchset = patchset.map do |hunk|
-
case hunk
-
when Diff::LCS::Change
-
has_changes ||= !hunk.unchanged?
-
hunk
-
when Array
-
# Detect if the 'hunk' is actually an array-format
-
# Change object.
-
if Diff::LCS::Change.valid_action? hunk[0]
-
hunk = Diff::LCS::Change.from_a(hunk)
-
has_changes ||= !hunk.unchanged?
-
hunk
-
else
-
with_changes, hunk = analyze_patchset(hunk, depth + 1)
-
has_changes ||= with_changes
-
hunk.flatten
-
end
-
else
-
raise ArgumentError, "Cannot normalise a hunk of class #{hunk.class}."
-
end
-
end
-
-
[ has_changes, patchset.flatten(1) ]
-
end
-
-
# Examine the patchset and the source to see in which direction the
-
# patch should be applied.
-
#
-
# WARNING: By default, this examines the whole patch, so this could take
-
# some time. This also works better with Diff::LCS::ContextChange or
-
# Diff::LCS::Change as its source, as an array will cause the creation
-
# of one of the above.
-
#
-
# Note: This will be deprecated as a public function in a future release.
-
1
def intuit_diff_direction(src, patchset, limit = nil)
-
string = src.kind_of?(String)
-
count = left_match = left_miss = right_match = right_miss = 0
-
-
patchset.each do |change|
-
count += 1
-
-
case change
-
when Diff::LCS::ContextChange
-
le = string ? src[change.old_position, 1] : src[change.old_position]
-
re = string ? src[change.new_position, 1] : src[change.new_position]
-
-
case change.action
-
when '-' # Remove details from the old string
-
if le == change.old_element
-
left_match += 1
-
else
-
left_miss += 1
-
end
-
when '+'
-
if re == change.new_element
-
right_match += 1
-
else
-
right_miss += 1
-
end
-
when '='
-
left_miss += 1 if le != change.old_element
-
right_miss += 1 if re != change.new_element
-
when '!'
-
if le == change.old_element
-
left_match += 1
-
else
-
if re == change.new_element
-
right_match += 1
-
else
-
left_miss += 1
-
right_miss += 1
-
end
-
end
-
end
-
when Diff::LCS::Change
-
# With a simplistic change, we can't tell the difference between
-
# the left and right on '!' actions, so we ignore those. On '='
-
# actions, if there's a miss, we miss both left and right.
-
element = string ? src[change.position, 1] : src[change.position]
-
-
case change.action
-
when '-'
-
if element == change.element
-
left_match += 1
-
else
-
left_miss += 1
-
end
-
when '+'
-
if element == change.element
-
right_match += 1
-
else
-
right_miss += 1
-
end
-
when '='
-
if element != change.element
-
left_miss += 1
-
right_miss += 1
-
end
-
end
-
end
-
-
break if (not limit.nil?) && (count > limit)
-
end
-
-
no_left = (left_match == 0) && (left_miss > 0)
-
no_right = (right_match == 0) && (right_miss > 0)
-
-
case [no_left, no_right]
-
when [false, true]
-
:patch
-
when [true, false]
-
:unpatch
-
else
-
case left_match <=> right_match
-
when 1
-
:patch
-
when -1
-
:unpatch
-
else
-
raise "The provided patchset does not appear to apply to the provided value as either source or destination value."
-
end
-
end
-
end
-
-
# Find the place at which +value+ would normally be inserted into the
-
# Enumerable. If that place is already occupied by +value+, do nothing
-
# and return +nil+. If the place does not exist (i.e., it is off the end
-
# of the Enumerable), add it to the end. Otherwise, replace the element
-
# at that point with +value+. It is assumed that the Enumerable's values
-
# are numeric.
-
#
-
# This operation preserves the sort order.
-
1
def replace_next_larger(enum, value, last_index = nil)
-
# Off the end?
-
if enum.empty? or (value > enum[-1])
-
enum << value
-
return enum.size - 1
-
end
-
-
# Binary search for the insertion point
-
last_index ||= enum.size
-
first_index = 0
-
while (first_index <= last_index)
-
i = (first_index + last_index) >> 1
-
-
found = enum[i]
-
-
if value == found
-
return nil
-
elsif value > found
-
first_index = i + 1
-
else
-
last_index = i - 1
-
end
-
end
-
-
# The insertion point is in first_index; overwrite the next larger
-
# value.
-
enum[first_index] = value
-
return first_index
-
end
-
1
private :replace_next_larger
-
-
# If +vector+ maps the matching elements of another collection onto this
-
# Enumerable, compute the inverse of +vector+ that maps this Enumerable
-
# onto the collection. (Currently unused.)
-
1
def inverse_vector(a, vector)
-
inverse = a.dup
-
(0...vector.size).each do |i|
-
inverse[vector[i]] = i unless vector[i].nil?
-
end
-
inverse
-
end
-
1
private :inverse_vector
-
-
# Returns a hash mapping each element of an Enumerable to the set of
-
# positions it occupies in the Enumerable, optionally restricted to the
-
# elements specified in the range of indexes specified by +interval+.
-
1
def position_hash(enum, interval)
-
string = enum.kind_of?(String)
-
hash = Hash.new { |h, k| h[k] = [] }
-
interval.each do |i|
-
k = string ? enum[i, 1] : enum[i]
-
hash[k] << i
-
end
-
hash
-
end
-
1
private :position_hash
-
end
-
##
-
## $Release: 2.7.0 $
-
## copyright(c) 2006-2011 kuwata-lab.com all rights reserved.
-
##
-
-
##
-
## an implementation of eRuby
-
##
-
## ex.
-
## input = <<'END'
-
## <ul>
-
## <% for item in @list %>
-
## <li><%= item %>
-
## <%== item %></li>
-
## <% end %>
-
## </ul>
-
## END
-
## list = ['<aaa>', 'b&b', '"ccc"']
-
## eruby = Erubis::Eruby.new(input)
-
## puts "--- code ---"
-
## puts eruby.src
-
## puts "--- result ---"
-
## context = Erubis::Context.new() # or new(:list=>list)
-
## context[:list] = list
-
## puts eruby.evaluate(context)
-
##
-
## result:
-
## --- source ---
-
## _buf = ''; _buf << '<ul>
-
## '; for item in @list
-
## _buf << ' <li>'; _buf << ( item ).to_s; _buf << '
-
## '; _buf << ' '; _buf << Erubis::XmlHelper.escape_xml( item ); _buf << '</li>
-
## '; end
-
## _buf << '</ul>
-
## ';
-
## _buf.to_s
-
## --- result ---
-
## <ul>
-
## <li><aaa>
-
## <aaa></li>
-
## <li>b&b
-
## b&b</li>
-
## <li>"ccc"
-
## "ccc"</li>
-
## </ul>
-
##
-
-
-
1
module Erubis
-
1
VERSION = ('$Release: 2.7.0 $' =~ /([.\d]+)/) && $1
-
end
-
-
1
require 'erubis/engine'
-
#require 'erubis/generator'
-
#require 'erubis/converter'
-
#require 'erubis/evaluator'
-
#require 'erubis/error'
-
#require 'erubis/context'
-
#requier 'erubis/util'
-
1
require 'erubis/helper'
-
1
require 'erubis/enhancer'
-
#require 'erubis/tiny'
-
1
require 'erubis/engine/eruby'
-
#require 'erubis/engine/enhanced' # enhanced eruby engines
-
#require 'erubis/engine/optimized' # generates optimized ruby code
-
#require 'erubis/engine/ephp'
-
#require 'erubis/engine/ec'
-
#require 'erubis/engine/ejava'
-
#require 'erubis/engine/escheme'
-
#require 'erubis/engine/eperl'
-
#require 'erubis/engine/ejavascript'
-
-
1
require 'erubis/local-setting'
-
##
-
## $Release: 2.7.0 $
-
## copyright(c) 2006-2011 kuwata-lab.com all rights reserved.
-
##
-
-
-
1
module Erubis
-
-
-
##
-
## context object for Engine#evaluate
-
##
-
## ex.
-
## template = <<'END'
-
## Hello <%= @user %>!
-
## <% for item in @list %>
-
## - <%= item %>
-
## <% end %>
-
## END
-
##
-
## context = Erubis::Context.new(:user=>'World', :list=>['a','b','c'])
-
## # or
-
## # context = Erubis::Context.new
-
## # context[:user] = 'World'
-
## # context[:list] = ['a', 'b', 'c']
-
##
-
## eruby = Erubis::Eruby.new(template)
-
## print eruby.evaluate(context)
-
##
-
1
class Context
-
1
include Enumerable
-
-
1
def initialize(hash=nil)
-
hash.each do |name, value|
-
self[name] = value
-
end if hash
-
end
-
-
1
def [](key)
-
return instance_variable_get("@#{key}")
-
end
-
-
1
def []=(key, value)
-
return instance_variable_set("@#{key}", value)
-
end
-
-
1
def keys
-
return instance_variables.collect { |name| name[1..-1] }
-
end
-
-
1
def each
-
instance_variables.each do |name|
-
key = name[1..-1]
-
value = instance_variable_get(name)
-
yield(key, value)
-
end
-
end
-
-
1
def to_hash
-
hash = {}
-
self.keys.each { |key| hash[key] = self[key] }
-
return hash
-
end
-
-
1
def update(context_or_hash)
-
arg = context_or_hash
-
if arg.is_a?(Hash)
-
arg.each do |key, val|
-
self[key] = val
-
end
-
else
-
arg.instance_variables.each do |varname|
-
key = varname[1..-1]
-
val = arg.instance_variable_get(varname)
-
self[key] = val
-
end
-
end
-
end
-
-
end
-
-
-
end
-
##
-
## $Release: 2.7.0 $
-
## copyright(c) 2006-2011 kuwata-lab.com all rights reserved.
-
##
-
-
1
require 'erubis/util'
-
-
1
module Erubis
-
-
-
##
-
## convert
-
##
-
1
module Converter
-
-
1
attr_accessor :preamble, :postamble, :escape
-
-
1
def self.supported_properties # :nodoc:
-
return [
-
[:preamble, nil, "preamble (no preamble when false)"],
-
[:postamble, nil, "postamble (no postamble when false)"],
-
[:escape, nil, "escape expression or not in default"],
-
]
-
end
-
-
1
def init_converter(properties={})
-
@preamble = properties[:preamble]
-
@postamble = properties[:postamble]
-
@escape = properties[:escape]
-
end
-
-
## convert input string into target language
-
1
def convert(input)
-
codebuf = "" # or []
-
@preamble.nil? ? add_preamble(codebuf) : (@preamble && (codebuf << @preamble))
-
convert_input(codebuf, input)
-
@postamble.nil? ? add_postamble(codebuf) : (@postamble && (codebuf << @postamble))
-
@_proc = nil # clear cached proc object
-
return codebuf # or codebuf.join()
-
end
-
-
1
protected
-
-
##
-
## detect spaces at beginning of line
-
##
-
1
def detect_spaces_at_bol(text, is_bol)
-
lspace = nil
-
if text.empty?
-
lspace = "" if is_bol
-
elsif text[-1] == ?\n
-
lspace = ""
-
else
-
rindex = text.rindex(?\n)
-
if rindex
-
s = text[rindex+1..-1]
-
if s =~ /\A[ \t]*\z/
-
lspace = s
-
#text = text[0..rindex]
-
text[rindex+1..-1] = ''
-
end
-
else
-
if is_bol && text =~ /\A[ \t]*\z/
-
#lspace = text
-
#text = nil
-
lspace = text.dup
-
text[0..-1] = ''
-
end
-
end
-
end
-
return lspace
-
end
-
-
##
-
## (abstract) convert input to code
-
##
-
1
def convert_input(codebuf, input)
-
not_implemented
-
end
-
-
end
-
-
-
1
module Basic
-
end
-
-
-
##
-
## basic converter which supports '<% ... %>' notation.
-
##
-
1
module Basic::Converter
-
1
include Erubis::Converter
-
-
1
def self.supported_properties # :nodoc:
-
return [
-
[:pattern, '<% %>', "embed pattern"],
-
[:trim, true, "trim spaces around <% ... %>"],
-
]
-
end
-
-
1
attr_accessor :pattern, :trim
-
-
1
def init_converter(properties={})
-
super(properties)
-
@pattern = properties[:pattern]
-
@trim = properties[:trim] != false
-
end
-
-
1
protected
-
-
## return regexp of pattern to parse eRuby script
-
1
def pattern_regexp(pattern)
-
1
@prefix, @postfix = pattern.split() # '<% %>' => '<%', '%>'
-
#return /(.*?)(^[ \t]*)?#{@prefix}(=+|\#)?(.*?)-?#{@postfix}([ \t]*\r?\n)?/m
-
#return /(^[ \t]*)?#{@prefix}(=+|\#)?(.*?)-?#{@postfix}([ \t]*\r?\n)?/m
-
1
return /#{@prefix}(=+|-|\#|%)?(.*?)([-=])?#{@postfix}([ \t]*\r?\n)?/m
-
end
-
1
module_function :pattern_regexp
-
-
#DEFAULT_REGEXP = /(.*?)(^[ \t]*)?<%(=+|\#)?(.*?)-?%>([ \t]*\r?\n)?/m
-
#DEFAULT_REGEXP = /(^[ \t]*)?<%(=+|\#)?(.*?)-?%>([ \t]*\r?\n)?/m
-
#DEFAULT_REGEXP = /<%(=+|\#)?(.*?)-?%>([ \t]*\r?\n)?/m
-
1
DEFAULT_REGEXP = pattern_regexp('<% %>')
-
-
1
public
-
-
1
def convert_input(src, input)
-
pat = @pattern
-
regexp = pat.nil? || pat == '<% %>' ? DEFAULT_REGEXP : pattern_regexp(pat)
-
pos = 0
-
is_bol = true # is beginning of line
-
input.scan(regexp) do |indicator, code, tailch, rspace|
-
match = Regexp.last_match()
-
len = match.begin(0) - pos
-
text = input[pos, len]
-
pos = match.end(0)
-
ch = indicator ? indicator[0] : nil
-
lspace = ch == ?= ? nil : detect_spaces_at_bol(text, is_bol)
-
is_bol = rspace ? true : false
-
add_text(src, text) if text && !text.empty?
-
## * when '<%= %>', do nothing
-
## * when '<% %>' or '<%# %>', delete spaces iff only spaces are around '<% %>'
-
if ch == ?= # <%= %>
-
rspace = nil if tailch && !tailch.empty?
-
add_text(src, lspace) if lspace
-
add_expr(src, code, indicator)
-
add_text(src, rspace) if rspace
-
elsif ch == ?\# # <%# %>
-
n = code.count("\n") + (rspace ? 1 : 0)
-
if @trim && lspace && rspace
-
add_stmt(src, "\n" * n)
-
else
-
add_text(src, lspace) if lspace
-
add_stmt(src, "\n" * n)
-
add_text(src, rspace) if rspace
-
end
-
elsif ch == ?% # <%% %>
-
s = "#{lspace}#{@prefix||='<%'}#{code}#{tailch}#{@postfix||='%>'}#{rspace}"
-
add_text(src, s)
-
else # <% %>
-
if @trim && lspace && rspace
-
add_stmt(src, "#{lspace}#{code}#{rspace}")
-
else
-
add_text(src, lspace) if lspace
-
add_stmt(src, code)
-
add_text(src, rspace) if rspace
-
end
-
end
-
end
-
#rest = $' || input # ruby1.8
-
rest = pos == 0 ? input : input[pos..-1] # ruby1.9
-
add_text(src, rest)
-
end
-
-
## add expression code to src
-
1
def add_expr(src, code, indicator)
-
case indicator
-
when '='
-
@escape ? add_expr_escaped(src, code) : add_expr_literal(src, code)
-
when '=='
-
@escape ? add_expr_literal(src, code) : add_expr_escaped(src, code)
-
when '==='
-
add_expr_debug(src, code)
-
end
-
end
-
-
end
-
-
-
1
module PI
-
end
-
-
##
-
## Processing Instructions (PI) converter for XML.
-
## this class converts '<?rb ... ?>' and '${...}' notation.
-
##
-
1
module PI::Converter
-
1
include Erubis::Converter
-
-
1
def self.desc # :nodoc:
-
"use processing instructions (PI) instead of '<% %>'"
-
end
-
-
1
def self.supported_properties # :nodoc:
-
return [
-
[:trim, true, "trim spaces around <% ... %>"],
-
[:pi, 'rb', "PI (Processing Instrunctions) name"],
-
[:embchar, '@', "char for embedded expression pattern('@{...}@')"],
-
[:pattern, '<% %>', "embed pattern"],
-
]
-
end
-
-
1
attr_accessor :pi, :prefix
-
-
1
def init_converter(properties={})
-
super(properties)
-
@trim = properties.fetch(:trim, true)
-
@pi = properties[:pi] if properties[:pi]
-
@embchar = properties[:embchar] || '@'
-
@pattern = properties[:pattern]
-
@pattern = '<% %>' if @pattern.nil? #|| @pattern == true
-
end
-
-
1
def convert(input)
-
code = super(input)
-
return @header || @footer ? "#{@header}#{code}#{@footer}" : code
-
end
-
-
1
protected
-
-
1
def convert_input(codebuf, input)
-
unless @regexp
-
@pi ||= 'e'
-
ch = Regexp.escape(@embchar)
-
if @pattern
-
left, right = @pattern.split(' ')
-
@regexp = /<\?#{@pi}(?:-(\w+))?(\s.*?)\?>([ \t]*\r?\n)?|#{ch}(!*)?\{(.*?)\}#{ch}|#{left}(=+)(.*?)#{right}/m
-
else
-
@regexp = /<\?#{@pi}(?:-(\w+))?(\s.*?)\?>([ \t]*\r?\n)?|#{ch}(!*)?\{(.*?)\}#{ch}/m
-
end
-
end
-
#
-
is_bol = true
-
pos = 0
-
input.scan(@regexp) do |pi_arg, stmt, rspace,
-
indicator1, expr1, indicator2, expr2|
-
match = Regexp.last_match
-
len = match.begin(0) - pos
-
text = input[pos, len]
-
pos = match.end(0)
-
lspace = stmt ? detect_spaces_at_bol(text, is_bol) : nil
-
is_bol = stmt && rspace ? true : false
-
add_text(codebuf, text) # unless text.empty?
-
#
-
if stmt
-
if @trim && lspace && rspace
-
add_pi_stmt(codebuf, "#{lspace}#{stmt}#{rspace}", pi_arg)
-
else
-
add_text(codebuf, lspace) if lspace
-
add_pi_stmt(codebuf, stmt, pi_arg)
-
add_text(codebuf, rspace) if rspace
-
end
-
else
-
add_pi_expr(codebuf, expr1 || expr2, indicator1 || indicator2)
-
end
-
end
-
#rest = $' || input # ruby1.8
-
rest = pos == 0 ? input : input[pos..-1] # ruby1.9
-
add_text(codebuf, rest)
-
end
-
-
#--
-
#def convert_input(codebuf, input)
-
# parse_stmts(codebuf, input)
-
# #parse_stmts2(codebuf, input)
-
#end
-
#
-
#def parse_stmts(codebuf, input)
-
# #regexp = pattern_regexp(@pattern)
-
# @pi ||= 'e'
-
# @stmt_pattern ||= /<\?#{@pi}(?:-(\w+))?(\s.*?)\?>([ \t]*\r?\n)?/m
-
# is_bol = true
-
# pos = 0
-
# input.scan(@stmt_pattern) do |pi_arg, code, rspace|
-
# match = Regexp.last_match
-
# len = match.begin(0) - pos
-
# text = input[pos, len]
-
# pos = match.end(0)
-
# lspace = detect_spaces_at_bol(text, is_bol)
-
# is_bol = rspace ? true : false
-
# parse_exprs(codebuf, text) # unless text.empty?
-
# if @trim && lspace && rspace
-
# add_pi_stmt(codebuf, "#{lspace}#{code}#{rspace}", pi_arg)
-
# else
-
# add_text(codebuf, lspace)
-
# add_pi_stmt(codebuf, code, pi_arg)
-
# add_text(codebuf, rspace)
-
# end
-
# end
-
# rest = $' || input
-
# parse_exprs(codebuf, rest)
-
#end
-
#
-
#def parse_exprs(codebuf, input)
-
# unless @expr_pattern
-
# ch = Regexp.escape(@embchar)
-
# if @pattern
-
# left, right = @pattern.split(' ')
-
# @expr_pattern = /#{ch}(!*)?\{(.*?)\}#{ch}|#{left}(=+)(.*?)#{right}/
-
# else
-
# @expr_pattern = /#{ch}(!*)?\{(.*?)\}#{ch}/
-
# end
-
# end
-
# pos = 0
-
# input.scan(@expr_pattern) do |indicator1, code1, indicator2, code2|
-
# indicator = indicator1 || indicator2
-
# code = code1 || code2
-
# match = Regexp.last_match
-
# len = match.begin(0) - pos
-
# text = input[pos, len]
-
# pos = match.end(0)
-
# add_text(codebuf, text) # unless text.empty?
-
# add_pi_expr(codebuf, code, indicator)
-
# end
-
# rest = $' || input
-
# add_text(codebuf, rest)
-
#end
-
#++
-
-
1
def add_pi_stmt(codebuf, code, pi_arg) # :nodoc:
-
case pi_arg
-
when nil ; add_stmt(codebuf, code)
-
when 'header' ; @header = code
-
when 'footer' ; @footer = code
-
when 'comment'; add_stmt(codebuf, "\n" * code.count("\n"))
-
when 'value' ; add_expr_literal(codebuf, code)
-
else ; add_stmt(codebuf, code)
-
end
-
end
-
-
1
def add_pi_expr(codebuf, code, indicator) # :nodoc:
-
case indicator
-
when nil, '', '==' # @{...}@ or <%== ... %>
-
@escape == false ? add_expr_literal(codebuf, code) : add_expr_escaped(codebuf, code)
-
when '!', '=' # @!{...}@ or <%= ... %>
-
@escape == false ? add_expr_escaped(codebuf, code) : add_expr_literal(codebuf, code)
-
when '!!', '===' # @!!{...}@ or <%=== ... %>
-
add_expr_debug(codebuf, code)
-
else
-
# ignore
-
end
-
end
-
-
end
-
-
-
end
-
##
-
## $Release: 2.7.0 $
-
## copyright(c) 2006-2011 kuwata-lab.com all rights reserved.
-
##
-
-
-
1
require 'erubis/generator'
-
1
require 'erubis/converter'
-
1
require 'erubis/evaluator'
-
1
require 'erubis/context'
-
-
-
1
module Erubis
-
-
-
##
-
## (abstract) abstract engine class.
-
## subclass must include evaluator and converter module.
-
##
-
1
class Engine
-
#include Evaluator
-
#include Converter
-
#include Generator
-
-
1
def initialize(input=nil, properties={})
-
#@input = input
-
init_generator(properties)
-
init_converter(properties)
-
init_evaluator(properties)
-
@src = convert(input) if input
-
end
-
-
-
##
-
## convert input string and set it to @src
-
##
-
1
def convert!(input)
-
@src = convert(input)
-
end
-
-
-
##
-
## load file, write cache file, and return engine object.
-
## this method create code cache file automatically.
-
## cachefile name can be specified with properties[:cachename],
-
## or filname + 'cache' is used as default.
-
##
-
1
def self.load_file(filename, properties={})
-
cachename = properties[:cachename] || (filename + '.cache')
-
properties[:filename] = filename
-
timestamp = File.mtime(filename)
-
if test(?f, cachename) && timestamp == File.mtime(cachename)
-
engine = self.new(nil, properties)
-
engine.src = File.read(cachename)
-
else
-
input = File.open(filename, 'rb') {|f| f.read }
-
engine = self.new(input, properties)
-
tmpname = cachename + rand().to_s[1,8]
-
File.open(tmpname, 'wb') {|f| f.write(engine.src) }
-
File.rename(tmpname, cachename)
-
File.utime(timestamp, timestamp, cachename)
-
end
-
engine.src.untaint # ok?
-
return engine
-
end
-
-
-
##
-
## helper method to convert and evaluate input text with context object.
-
## context may be Binding, Hash, or Object.
-
##
-
1
def process(input, context=nil, filename=nil)
-
code = convert(input)
-
filename ||= '(erubis)'
-
if context.is_a?(Binding)
-
return eval(code, context, filename)
-
else
-
context = Context.new(context) if context.is_a?(Hash)
-
return context.instance_eval(code, filename)
-
end
-
end
-
-
-
##
-
## helper method evaluate Proc object with contect object.
-
## context may be Binding, Hash, or Object.
-
##
-
1
def process_proc(proc_obj, context=nil, filename=nil)
-
if context.is_a?(Binding)
-
filename ||= '(erubis)'
-
return eval(proc_obj, context, filename)
-
else
-
context = Context.new(context) if context.is_a?(Hash)
-
return context.instance_eval(&proc_obj)
-
end
-
end
-
-
-
end # end of class Engine
-
-
-
##
-
## (abstract) base engine class for Eruby, Eperl, Ejava, and so on.
-
## subclass must include generator.
-
##
-
1
class Basic::Engine < Engine
-
1
include Evaluator
-
1
include Basic::Converter
-
1
include Generator
-
end
-
-
-
1
class PI::Engine < Engine
-
1
include Evaluator
-
1
include PI::Converter
-
1
include Generator
-
end
-
-
-
end
-
##
-
## $Release: 2.7.0 $
-
## copyright(c) 2006-2011 kuwata-lab.com all rights reserved.
-
##
-
-
-
1
module Erubis
-
-
-
##
-
## switch '<%= ... %>' to escaped and '<%== ... %>' to unescaped
-
##
-
## ex.
-
## class XmlEruby < Eruby
-
## include EscapeEnhancer
-
## end
-
##
-
## this is language-indenedent.
-
##
-
1
module EscapeEnhancer
-
-
1
def self.desc # :nodoc:
-
"switch '<%= %>' to escaped and '<%== %>' to unescaped"
-
end
-
-
#--
-
#def self.included(klass)
-
# klass.class_eval <<-END
-
# alias _add_expr_literal add_expr_literal
-
# alias _add_expr_escaped add_expr_escaped
-
# alias add_expr_literal _add_expr_escaped
-
# alias add_expr_escaped _add_expr_literal
-
# END
-
#end
-
#++
-
-
1
def add_expr(src, code, indicator)
-
case indicator
-
when '='
-
@escape ? add_expr_literal(src, code) : add_expr_escaped(src, code)
-
when '=='
-
@escape ? add_expr_escaped(src, code) : add_expr_literal(src, code)
-
when '==='
-
add_expr_debug(src, code)
-
end
-
end
-
-
end
-
-
-
#--
-
## (obsolete)
-
#module FastEnhancer
-
#end
-
#++
-
-
-
##
-
## use $stdout instead of string
-
##
-
## this is only for Eruby.
-
##
-
1
module StdoutEnhancer
-
-
1
def self.desc # :nodoc:
-
"use $stdout instead of array buffer or string buffer"
-
end
-
-
1
def add_preamble(src)
-
src << "#{@bufvar} = $stdout;"
-
end
-
-
1
def add_postamble(src)
-
src << "\n''\n"
-
end
-
-
end
-
-
-
##
-
## use print statement instead of '_buf << ...'
-
##
-
## this is only for Eruby.
-
##
-
1
module PrintOutEnhancer
-
-
1
def self.desc # :nodoc:
-
"use print statement instead of '_buf << ...'"
-
end
-
-
1
def add_preamble(src)
-
end
-
-
1
def add_text(src, text)
-
src << " print '#{escape_text(text)}';" unless text.empty?
-
end
-
-
1
def add_expr_literal(src, code)
-
src << " print((#{code}).to_s);"
-
end
-
-
1
def add_expr_escaped(src, code)
-
src << " print #{escaped_expr(code)};"
-
end
-
-
1
def add_postamble(src)
-
src << "\n" unless src[-1] == ?\n
-
end
-
-
end
-
-
-
##
-
## enable print function
-
##
-
## Notice: use Eruby#evaluate() and don't use Eruby#result()
-
## to be enable print function.
-
##
-
## this is only for Eruby.
-
##
-
1
module PrintEnabledEnhancer
-
-
1
def self.desc # :nodoc:
-
"enable to use print function in '<% %>'"
-
end
-
-
1
def add_preamble(src)
-
src << "@_buf = "
-
super
-
end
-
-
1
def print(*args)
-
args.each do |arg|
-
@_buf << arg.to_s
-
end
-
end
-
-
1
def evaluate(context=nil)
-
_src = @src
-
if context.is_a?(Hash)
-
context.each do |key, val| instance_variable_set("@#{key}", val) end
-
elsif context
-
context.instance_variables.each do |name|
-
instance_variable_set(name, context.instance_variable_get(name))
-
end
-
end
-
return instance_eval(_src, (@filename || '(erubis)'))
-
end
-
-
end
-
-
-
##
-
## return array instead of string
-
##
-
## this is only for Eruby.
-
##
-
1
module ArrayEnhancer
-
-
1
def self.desc # :nodoc:
-
"return array instead of string"
-
end
-
-
1
def add_preamble(src)
-
src << "#{@bufvar} = [];"
-
end
-
-
1
def add_postamble(src)
-
src << "\n" unless src[-1] == ?\n
-
src << "#{@bufvar}\n"
-
end
-
-
end
-
-
-
##
-
## use an Array object as buffer (included in Eruby by default)
-
##
-
## this is only for Eruby.
-
##
-
1
module ArrayBufferEnhancer
-
-
1
def self.desc # :nodoc:
-
"use an Array object for buffering (included in Eruby class)"
-
end
-
-
1
def add_preamble(src)
-
src << "_buf = [];"
-
end
-
-
1
def add_postamble(src)
-
src << "\n" unless src[-1] == ?\n
-
src << "_buf.join\n"
-
end
-
-
end
-
-
-
##
-
## use String class for buffering
-
##
-
## this is only for Eruby.
-
##
-
1
module StringBufferEnhancer
-
-
1
def self.desc # :nodoc:
-
"use a String object for buffering"
-
end
-
-
1
def add_preamble(src)
-
src << "#{@bufvar} = '';"
-
end
-
-
1
def add_postamble(src)
-
src << "\n" unless src[-1] == ?\n
-
src << "#{@bufvar}.to_s\n"
-
end
-
-
end
-
-
-
##
-
## use StringIO class for buffering
-
##
-
## this is only for Eruby.
-
##
-
1
module StringIOEnhancer # :nodoc:
-
-
1
def self.desc # :nodoc:
-
"use a StringIO object for buffering"
-
end
-
-
1
def add_preamble(src)
-
src << "#{@bufvar} = StringIO.new;"
-
end
-
-
1
def add_postamble(src)
-
src << "\n" unless src[-1] == ?\n
-
src << "#{@bufvar}.string\n"
-
end
-
-
end
-
-
-
##
-
## set buffer variable name to '_erbout' as well as '_buf'
-
##
-
## this is only for Eruby.
-
##
-
1
module ErboutEnhancer
-
-
1
def self.desc # :nodoc:
-
"set '_erbout = _buf = \"\";' to be compatible with ERB."
-
end
-
-
1
def add_preamble(src)
-
src << "_erbout = #{@bufvar} = '';"
-
end
-
-
1
def add_postamble(src)
-
src << "\n" unless src[-1] == ?\n
-
src << "#{@bufvar}.to_s\n"
-
end
-
-
end
-
-
-
##
-
## remove text and leave code, especially useful when debugging.
-
##
-
## ex.
-
## $ erubis -s -E NoText file.eruby | more
-
##
-
## this is language independent.
-
##
-
1
module NoTextEnhancer
-
-
1
def self.desc # :nodoc:
-
"remove text and leave code (useful when debugging)"
-
end
-
-
1
def add_text(src, text)
-
src << ("\n" * text.count("\n"))
-
if text[-1] != ?\n
-
text =~ /^(.*?)\z/
-
src << (' ' * $1.length)
-
end
-
end
-
-
end
-
-
-
##
-
## remove code and leave text, especially useful when validating HTML tags.
-
##
-
## ex.
-
## $ erubis -s -E NoCode file.eruby | tidy -errors
-
##
-
## this is language independent.
-
##
-
1
module NoCodeEnhancer
-
-
1
def self.desc # :nodoc:
-
"remove code and leave text (useful when validating HTML)"
-
end
-
-
1
def add_preamble(src)
-
end
-
-
1
def add_postamble(src)
-
end
-
-
1
def add_text(src, text)
-
src << text
-
end
-
-
1
def add_expr(src, code, indicator)
-
src << "\n" * code.count("\n")
-
end
-
-
1
def add_stmt(src, code)
-
src << "\n" * code.count("\n")
-
end
-
-
end
-
-
-
##
-
## get convert faster, but spaces around '<%...%>' are not trimmed.
-
##
-
## this is language-independent.
-
##
-
1
module SimplifyEnhancer
-
-
1
def self.desc # :nodoc:
-
"get convert faster but leave spaces around '<% %>'"
-
end
-
-
#DEFAULT_REGEXP = /(^[ \t]*)?<%(=+|\#)?(.*?)-?%>([ \t]*\r?\n)?/m
-
1
SIMPLE_REGEXP = /<%(=+|\#)?(.*?)-?%>/m
-
-
1
def convert(input)
-
src = ""
-
add_preamble(src)
-
#regexp = pattern_regexp(@pattern)
-
pos = 0
-
input.scan(SIMPLE_REGEXP) do |indicator, code|
-
match = Regexp.last_match
-
index = match.begin(0)
-
text = input[pos, index - pos]
-
pos = match.end(0)
-
add_text(src, text)
-
if !indicator # <% %>
-
add_stmt(src, code)
-
elsif indicator[0] == ?\# # <%# %>
-
n = code.count("\n")
-
add_stmt(src, "\n" * n)
-
else # <%= %>
-
add_expr(src, code, indicator)
-
end
-
end
-
#rest = $' || input # ruby1.8
-
rest = pos == 0 ? input : input[pos..-1] # ruby1.9
-
add_text(src, rest)
-
add_postamble(src)
-
return src
-
end
-
-
end
-
-
-
##
-
## enable to use other embedded expression pattern (default is '\[= =\]').
-
##
-
## notice! this is an experimental. spec may change in the future.
-
##
-
## ex.
-
## input = <<END
-
## <% for item in list %>
-
## <%= item %> : <%== item %>
-
## [= item =] : [== item =]
-
## <% end %>
-
## END
-
##
-
## class BiPatternEruby
-
## include BiPatternEnhancer
-
## end
-
## eruby = BiPatternEruby.new(input, :bipattern=>'\[= =\]')
-
## list = ['<a>', 'b&b', '"c"']
-
## print eruby.result(binding())
-
##
-
## ## output
-
## <a> : <a>
-
## <a> : <a>
-
## b&b : b&b
-
## b&b : b&b
-
## "c" : "c"
-
## "c" : "c"
-
##
-
## this is language independent.
-
##
-
1
module BiPatternEnhancer
-
-
1
def self.desc # :nodoc:
-
"another embedded expression pattern (default '\[= =\]')."
-
end
-
-
1
def initialize(input, properties={})
-
self.bipattern = properties[:bipattern] # or '\$\{ \}'
-
super
-
end
-
-
## when pat is nil then '\[= =\]' is used
-
1
def bipattern=(pat) # :nodoc:
-
@bipattern = pat || '\[= =\]'
-
pre, post = @bipattern.split()
-
@bipattern_regexp = /(.*?)#{pre}(=*)(.*?)#{post}/m
-
end
-
-
1
def add_text(src, text)
-
return unless text
-
m = nil
-
text.scan(@bipattern_regexp) do |txt, indicator, code|
-
m = Regexp.last_match
-
super(src, txt)
-
add_expr(src, code, '=' + indicator)
-
end
-
#rest = $' || text # ruby1.8
-
rest = m ? text[m.end(0)..-1] : text # ruby1.9
-
super(src, rest)
-
end
-
-
end
-
-
-
##
-
## regards lines starting with '^[ \t]*%' as program code
-
##
-
## in addition you can specify prefix character (default '%')
-
##
-
## this is language-independent.
-
##
-
1
module PrefixedLineEnhancer
-
-
1
def self.desc # :nodoc:
-
"regard lines matched to '^[ \t]*%' as program code"
-
end
-
-
1
def init_generator(properties={})
-
super
-
@prefixchar = properties[:prefixchar]
-
end
-
-
1
def add_text(src, text)
-
unless @prefixrexp
-
@prefixchar ||= '%'
-
@prefixrexp = Regexp.compile("^([ \\t]*)\\#{@prefixchar}(.*?\\r?\\n)")
-
end
-
pos = 0
-
text2 = ''
-
text.scan(@prefixrexp) do
-
space = $1
-
line = $2
-
space, line = '', $1 unless $2
-
match = Regexp.last_match
-
len = match.begin(0) - pos
-
str = text[pos, len]
-
pos = match.end(0)
-
if text2.empty?
-
text2 = str
-
else
-
text2 << str
-
end
-
if line[0, 1] == @prefixchar
-
text2 << space << line
-
else
-
super(src, text2)
-
text2 = ''
-
add_stmt(src, space + line)
-
end
-
end
-
#rest = pos == 0 ? text : $' # ruby1.8
-
rest = pos == 0 ? text : text[pos..-1] # ruby1.9
-
unless text2.empty?
-
text2 << rest if rest
-
rest = text2
-
end
-
super(src, rest)
-
end
-
-
end
-
-
-
##
-
## regards lines starting with '%' as program code
-
##
-
## this is for compatibility to eruby and ERB.
-
##
-
## this is language-independent.
-
##
-
1
module PercentLineEnhancer
-
1
include PrefixedLineEnhancer
-
-
1
def self.desc # :nodoc:
-
"regard lines starting with '%' as program code"
-
end
-
-
#--
-
#def init_generator(properties={})
-
# super
-
# @prefixchar = '%'
-
# @prefixrexp = /^\%(.*?\r?\n)/
-
#end
-
#++
-
-
1
def add_text(src, text)
-
unless @prefixrexp
-
@prefixchar = '%'
-
@prefixrexp = /^\%(.*?\r?\n)/
-
end
-
super(src, text)
-
end
-
-
end
-
-
-
##
-
## [experimental] allow header and footer in eRuby script
-
##
-
## ex.
-
## ====================
-
## ## without header and footer
-
## $ cat ex1.eruby
-
## <% def list_items(list) %>
-
## <% for item in list %>
-
## <li><%= item %></li>
-
## <% end %>
-
## <% end %>
-
##
-
## $ erubis -s ex1.eruby
-
## _buf = []; def list_items(list)
-
## ; for item in list
-
## ; _buf << '<li>'; _buf << ( item ).to_s; _buf << '</li>
-
## '; end
-
## ; end
-
## ;
-
## _buf.join
-
##
-
## ## with header and footer
-
## $ cat ex2.eruby
-
## <!--#header:
-
## def list_items(list)
-
## #-->
-
## <% for item in list %>
-
## <li><%= item %></li>
-
## <% end %>
-
## <!--#footer:
-
## end
-
## #-->
-
##
-
## $ erubis -s -c HeaderFooterEruby ex4.eruby
-
##
-
## def list_items(list)
-
## _buf = []; _buf << '
-
## '; for item in list
-
## ; _buf << '<li>'; _buf << ( item ).to_s; _buf << '</li>
-
## '; end
-
## ; _buf << '
-
## ';
-
## _buf.join
-
## end
-
##
-
## ====================
-
##
-
## this is language-independent.
-
##
-
1
module HeaderFooterEnhancer
-
-
1
def self.desc # :nodoc:
-
"allow header/footer in document (ex. '<!--#header: #-->')"
-
end
-
-
1
HEADER_FOOTER_PATTERN = /(.*?)(^[ \t]*)?<!--\#(\w+):(.*?)\#-->([ \t]*\r?\n)?/m
-
-
1
def add_text(src, text)
-
m = nil
-
text.scan(HEADER_FOOTER_PATTERN) do |txt, lspace, word, content, rspace|
-
m = Regexp.last_match
-
flag_trim = @trim && lspace && rspace
-
super(src, txt)
-
content = "#{lspace}#{content}#{rspace}" if flag_trim
-
super(src, lspace) if !flag_trim && lspace
-
instance_variable_set("@#{word}", content)
-
super(src, rspace) if !flag_trim && rspace
-
end
-
#rest = $' || text # ruby1.8
-
rest = m ? text[m.end(0)..-1] : text # ruby1.9
-
super(src, rest)
-
end
-
-
1
attr_accessor :header, :footer
-
-
1
def convert(input)
-
source = super
-
return @src = "#{@header}#{source}#{@footer}"
-
end
-
-
end
-
-
-
##
-
## delete indentation of HTML.
-
##
-
## this is language-independent.
-
##
-
1
module DeleteIndentEnhancer
-
-
1
def self.desc # :nodoc:
-
"delete indentation of HTML."
-
end
-
-
1
def convert_input(src, input)
-
input = input.gsub(/^[ \t]+</, '<')
-
super(src, input)
-
end
-
-
end
-
-
-
##
-
## convert "<h1><%=title%></h1>" into "_buf << %Q`<h1>#{title}</h1>`"
-
##
-
## this is only for Eruby.
-
##
-
1
module InterpolationEnhancer
-
-
1
def self.desc # :nodoc:
-
"convert '<p><%=text%></p>' into '_buf << %Q`<p>\#{text}</p>`'"
-
end
-
-
1
def convert_input(src, input)
-
pat = @pattern
-
regexp = pat.nil? || pat == '<% %>' ? Basic::Converter::DEFAULT_REGEXP : pattern_regexp(pat)
-
pos = 0
-
is_bol = true # is beginning of line
-
str = ''
-
input.scan(regexp) do |indicator, code, tailch, rspace|
-
match = Regexp.last_match()
-
len = match.begin(0) - pos
-
text = input[pos, len]
-
pos = match.end(0)
-
ch = indicator ? indicator[0] : nil
-
lspace = ch == ?= ? nil : detect_spaces_at_bol(text, is_bol)
-
is_bol = rspace ? true : false
-
_add_text_to_str(str, text)
-
## * when '<%= %>', do nothing
-
## * when '<% %>' or '<%# %>', delete spaces iff only spaces are around '<% %>'
-
if ch == ?= # <%= %>
-
rspace = nil if tailch && !tailch.empty?
-
str << lspace if lspace
-
add_expr(str, code, indicator)
-
str << rspace if rspace
-
elsif ch == ?\# # <%# %>
-
n = code.count("\n") + (rspace ? 1 : 0)
-
if @trim && lspace && rspace
-
add_text(src, str)
-
str = ''
-
add_stmt(src, "\n" * n)
-
else
-
str << lspace if lspace
-
add_text(src, str)
-
str = ''
-
add_stmt(src, "\n" * n)
-
str << rspace if rspace
-
end
-
else # <% %>
-
if @trim && lspace && rspace
-
add_text(src, str)
-
str = ''
-
add_stmt(src, "#{lspace}#{code}#{rspace}")
-
else
-
str << lspace if lspace
-
add_text(src, str)
-
str = ''
-
add_stmt(src, code)
-
str << rspace if rspace
-
end
-
end
-
end
-
#rest = $' || input # ruby1.8
-
rest = pos == 0 ? input : input[pos..-1] # ruby1.9
-
_add_text_to_str(str, rest)
-
add_text(src, str)
-
end
-
-
1
def add_text(src, text)
-
return if !text || text.empty?
-
#src << " _buf << %Q`" << text << "`;"
-
if text[-1] == ?\n
-
text[-1] = "\\n"
-
src << " #{@bufvar} << %Q`#{text}`\n"
-
else
-
src << " #{@bufvar} << %Q`#{text}`;"
-
end
-
end
-
-
1
def _add_text_to_str(str, text)
-
return if !text || text.empty?
-
str << text.gsub(/[`\#\\]/, '\\\\\&')
-
end
-
-
1
def add_expr_escaped(str, code)
-
str << "\#{#{escaped_expr(code)}}"
-
end
-
-
1
def add_expr_literal(str, code)
-
str << "\#{#{code}}"
-
end
-
-
end
-
-
-
end
-
##
-
## $Release: 2.7.0 $
-
## copyright(c) 2006-2011 kuwata-lab.com all rights reserved.
-
##
-
-
1
module Erubis
-
-
-
##
-
## base error class
-
##
-
1
class ErubisError < StandardError
-
end
-
-
-
##
-
## raised when method or function is not supported
-
##
-
1
class NotSupportedError < ErubisError
-
end
-
-
-
end
-
##
-
## $Release: 2.7.0 $
-
## copyright(c) 2006-2011 kuwata-lab.com all rights reserved.
-
##
-
-
1
require 'erubis/error'
-
1
require 'erubis/context'
-
-
-
1
module Erubis
-
-
1
EMPTY_BINDING = binding()
-
-
-
##
-
## evaluate code
-
##
-
1
module Evaluator
-
-
1
def self.supported_properties # :nodoc:
-
return []
-
end
-
-
1
attr_accessor :src, :filename
-
-
1
def init_evaluator(properties)
-
@filename = properties[:filename]
-
end
-
-
1
def result(*args)
-
raise NotSupportedError.new("evaluation of code except Ruby is not supported.")
-
end
-
-
1
def evaluate(*args)
-
raise NotSupportedError.new("evaluation of code except Ruby is not supported.")
-
end
-
-
end
-
-
-
##
-
## evaluator for Ruby
-
##
-
1
module RubyEvaluator
-
1
include Evaluator
-
-
1
def self.supported_properties # :nodoc:
-
list = Evaluator.supported_properties
-
return list
-
end
-
-
## eval(@src) with binding object
-
1
def result(_binding_or_hash=TOPLEVEL_BINDING)
-
_arg = _binding_or_hash
-
if _arg.is_a?(Hash)
-
_b = binding()
-
eval _arg.collect{|k,v| "#{k} = _arg[#{k.inspect}]; "}.join, _b
-
elsif _arg.is_a?(Binding)
-
_b = _arg
-
elsif _arg.nil?
-
_b = binding()
-
else
-
raise ArgumentError.new("#{self.class.name}#result(): argument should be Binding or Hash but passed #{_arg.class.name} object.")
-
end
-
return eval(@src, _b, (@filename || '(erubis'))
-
end
-
-
## invoke context.instance_eval(@src)
-
1
def evaluate(_context=Context.new)
-
_context = Context.new(_context) if _context.is_a?(Hash)
-
#return _context.instance_eval(@src, @filename || '(erubis)')
-
#@_proc ||= eval("proc { #{@src} }", Erubis::EMPTY_BINDING, @filename || '(erubis)')
-
@_proc ||= eval("proc { #{@src} }", binding(), @filename || '(erubis)')
-
return _context.instance_eval(&@_proc)
-
end
-
-
## if object is an Class or Module then define instance method to it,
-
## else define singleton method to it.
-
1
def def_method(object, method_name, filename=nil)
-
m = object.is_a?(Module) ? :module_eval : :instance_eval
-
object.__send__(m, "def #{method_name}; #{@src}; end", filename || @filename || '(erubis)')
-
end
-
-
-
end
-
-
-
end
-
##
-
## $Release: 2.7.0 $
-
## copyright(c) 2006-2011 kuwata-lab.com all rights reserved.
-
##
-
-
1
require 'erubis/util'
-
-
1
module Erubis
-
-
-
##
-
## code generator, called by Converter module
-
##
-
1
module Generator
-
-
1
def self.supported_properties() # :nodoc:
-
return [
-
[:escapefunc, nil, "escape function name"],
-
]
-
end
-
-
1
attr_accessor :escapefunc
-
-
1
def init_generator(properties={})
-
@escapefunc = properties[:escapefunc]
-
end
-
-
-
## (abstract) escape text string
-
##
-
## ex.
-
## def escape_text(text)
-
## return text.dump
-
## # or return "'" + text.gsub(/['\\]/, '\\\\\&') + "'"
-
## end
-
1
def escape_text(text)
-
not_implemented
-
end
-
-
## return escaped expression code (ex. 'h(...)' or 'htmlspecialchars(...)')
-
1
def escaped_expr(code)
-
code.strip!
-
return "#{@escapefunc}(#{code})"
-
end
-
-
## (abstract) add @preamble to src
-
1
def add_preamble(src)
-
not_implemented
-
end
-
-
## (abstract) add text string to src
-
1
def add_text(src, text)
-
not_implemented
-
end
-
-
## (abstract) add statement code to src
-
1
def add_stmt(src, code)
-
not_implemented
-
end
-
-
## (abstract) add expression literal code to src. this is called by add_expr().
-
1
def add_expr_literal(src, code)
-
not_implemented
-
end
-
-
## (abstract) add escaped expression code to src. this is called by add_expr().
-
1
def add_expr_escaped(src, code)
-
not_implemented
-
end
-
-
## (abstract) add expression code to src for debug. this is called by add_expr().
-
1
def add_expr_debug(src, code)
-
not_implemented
-
end
-
-
## (abstract) add @postamble to src
-
1
def add_postamble(src)
-
not_implemented
-
end
-
-
-
end
-
-
-
end
-
##
-
## $Release: 2.7.0 $
-
## copyright(c) 2006-2011 kuwata-lab.com all rights reserved.
-
##
-
-
-
1
module Erubis
-
-
##
-
## helper for xml
-
##
-
1
module XmlHelper
-
-
1
module_function
-
-
1
ESCAPE_TABLE = {
-
'&' => '&',
-
'<' => '<',
-
'>' => '>',
-
'"' => '"',
-
"'" => ''',
-
}
-
-
1
def escape_xml(value)
-
value.to_s.gsub(/[&<>"]/) { |s| ESCAPE_TABLE[s] } # or /[&<>"']/
-
#value.to_s.gsub(/[&<>"]/) { ESCAPE_TABLE[$&] }
-
end
-
-
1
def escape_xml2(value)
-
return value.to_s.gsub(/\&/,'&').gsub(/</,'<').gsub(/>/,'>').gsub(/"/,'"')
-
end
-
-
1
alias h escape_xml
-
1
alias html_escape escape_xml
-
-
1
def url_encode(str)
-
return str.gsub(/[^-_.a-zA-Z0-9]+/) { |s|
-
s.unpack('C*').collect { |i| "%%%02X" % i }.join
-
}
-
end
-
-
1
alias u url_encode
-
-
end
-
-
-
end
-
##
-
## $Release: 2.7.0 $
-
## copyright(c) 2006-2011 kuwata-lab.com all rights reserved.
-
##
-
-
##
-
## you can add site-local settings here.
-
## this files is required by erubis.rb
-
##
-
##
-
## $Release: 2.7.0 $
-
## copyright(c) 2006-2011 kuwata-lab.com all rights reserved.
-
##
-
-
1
module Kernel
-
-
##
-
## raise NotImplementedError
-
##
-
1
def not_implemented #:doc:
-
backtrace = caller()
-
method_name = (backtrace.shift =~ /`(\w+)'$/) && $1
-
mesg = "class #{self.class.name} must implement abstract method '#{method_name}()'."
-
#mesg = "#{self.class.name}##{method_name}() is not implemented."
-
err = NotImplementedError.new mesg
-
err.set_backtrace backtrace
-
raise err
-
end
-
1
private :not_implemented
-
-
end
-
1
require "execjs/module"
-
1
require "execjs/runtimes"
-
-
1
module ExecJS
-
1
self.runtime ||= Runtimes.autodetect
-
end
-
1
require "execjs/runtime"
-
-
1
module ExecJS
-
1
class DisabledRuntime < Runtime
-
1
def name
-
"Disabled"
-
end
-
-
1
def exec(source)
-
raise Error, "ExecJS disabled"
-
end
-
-
1
def eval(source)
-
raise Error, "ExecJS disabled"
-
end
-
-
1
def compile(source)
-
raise Error, "ExecJS disabled"
-
end
-
-
1
def deprecated?
-
true
-
end
-
-
1
def available?
-
true
-
end
-
end
-
end
-
1
module ExecJS
-
# Encodes strings as UTF-8
-
1
module Encoding
-
1
if RUBY_ENGINE == 'jruby' || RUBY_ENGINE == 'rbx'
-
# workaround for jruby bug http://jira.codehaus.org/browse/JRUBY-6588
-
# workaround for rbx bug https://github.com/rubinius/rubinius/issues/1729
-
def encode(string)
-
if string.encoding.name == 'ASCII-8BIT'
-
data = string.dup
-
data.force_encoding('UTF-8')
-
-
unless data.valid_encoding?
-
raise ::Encoding::UndefinedConversionError, "Could not encode ASCII-8BIT data #{string.dump} as UTF-8"
-
end
-
else
-
data = string.encode('UTF-8')
-
end
-
data
-
end
-
else
-
1
def encode(string)
-
string.encode('UTF-8')
-
end
-
end
-
end
-
end
-
1
require "tmpdir"
-
1
require "execjs/runtime"
-
-
1
module ExecJS
-
1
class ExternalRuntime < Runtime
-
1
class Context < Runtime::Context
-
1
def initialize(runtime, source = "")
-
source = encode(source)
-
-
@runtime = runtime
-
@source = source
-
end
-
-
1
def eval(source, options = {})
-
source = encode(source)
-
-
if /\S/ =~ source
-
exec("return eval(#{::JSON.generate("(#{source})", quirks_mode: true)})")
-
end
-
end
-
-
1
def exec(source, options = {})
-
source = encode(source)
-
source = "#{@source}\n#{source}" if @source
-
source = @runtime.compile_source(source)
-
-
tmpfile = write_to_tempfile(source)
-
begin
-
extract_result(@runtime.exec_runtime(tmpfile.path))
-
ensure
-
File.unlink(tmpfile)
-
end
-
end
-
-
1
def call(identifier, *args)
-
eval "#{identifier}.apply(this, #{::JSON.generate(args)})"
-
end
-
-
1
protected
-
# See Tempfile.create on Ruby 2.1
-
1
def create_tempfile(basename)
-
tmpfile = nil
-
Dir::Tmpname.create(basename) do |tmpname|
-
mode = File::WRONLY | File::CREAT | File::EXCL
-
tmpfile = File.open(tmpname, mode, 0600)
-
end
-
tmpfile
-
end
-
-
1
def write_to_tempfile(contents)
-
tmpfile = create_tempfile(['execjs', 'js'])
-
tmpfile.write(contents)
-
tmpfile.close
-
tmpfile
-
end
-
-
1
def extract_result(output)
-
status, value = output.empty? ? [] : ::JSON.parse(output, create_additions: false)
-
if status == "ok"
-
value
-
elsif value =~ /SyntaxError:/
-
raise RuntimeError, value
-
else
-
raise ProgramError, value
-
end
-
end
-
end
-
-
1
attr_reader :name
-
-
1
def initialize(options)
-
4
@name = options[:name]
-
4
@command = options[:command]
-
4
@runner_path = options[:runner_path]
-
4
@encoding = options[:encoding]
-
4
@deprecated = !!options[:deprecated]
-
4
@binary = nil
-
-
4
@popen_options = {}
-
4
@popen_options[:external_encoding] = @encoding if @encoding
-
4
@popen_options[:internal_encoding] = ::Encoding.default_internal || 'UTF-8'
-
-
4
if @runner_path
-
4
instance_eval generate_compile_method(@runner_path)
-
end
-
end
-
-
1
def available?
-
require 'json'
-
binary ? true : false
-
end
-
-
1
def deprecated?
-
4
@deprecated
-
end
-
-
1
private
-
1
def binary
-
@binary ||= which(@command)
-
end
-
-
1
def locate_executable(cmd)
-
if ExecJS.windows? && File.extname(cmd) == ""
-
cmd << ".exe"
-
end
-
-
if File.executable? cmd
-
cmd
-
else
-
path = ENV['PATH'].split(File::PATH_SEPARATOR).find { |p|
-
full_path = File.join(p, cmd)
-
File.executable?(full_path) && File.file?(full_path)
-
}
-
path && File.expand_path(cmd, path)
-
end
-
end
-
-
1
protected
-
1
def generate_compile_method(path)
-
<<-RUBY
-
def compile_source(source)
-
<<-RUNNER
-
4
#{IO.read(path)}
-
RUNNER
-
end
-
RUBY
-
end
-
-
1
def json2_source
-
@json2_source ||= IO.read(ExecJS.root + "/support/json2.js")
-
end
-
-
1
def encode_source(source)
-
encoded_source = encode_unicode_codepoints(source)
-
::JSON.generate("(function(){ #{encoded_source} })()", quirks_mode: true)
-
end
-
-
1
def encode_unicode_codepoints(str)
-
str.gsub(/[\u0080-\uffff]/) do |ch|
-
"\\u%04x" % ch.codepoints.to_a
-
end
-
end
-
-
1
if ExecJS.windows?
-
def exec_runtime(filename)
-
path = Dir::Tmpname.create(['execjs', 'json']) {}
-
begin
-
command = binary.split(" ") << filename
-
`#{shell_escape(*command)} 2>&1 > #{path}`
-
output = File.open(path, 'rb', @popen_options) { |f| f.read }
-
ensure
-
File.unlink(path) if path
-
end
-
-
if $?.success?
-
output
-
else
-
raise RuntimeError, output
-
end
-
end
-
-
def shell_escape(*args)
-
# see http://technet.microsoft.com/en-us/library/cc723564.aspx#XSLTsection123121120120
-
args.map { |arg|
-
arg = %Q("#{arg.gsub('"','""')}") if arg.match(/[&|()<>^ "]/)
-
arg
-
}.join(" ")
-
end
-
else
-
1
def exec_runtime(filename)
-
io = IO.popen(binary.split(' ') << filename, @popen_options.merge({err: [:child, :out]}))
-
output = io.read
-
io.close
-
-
if $?.success?
-
output
-
else
-
raise RuntimeError, output
-
end
-
end
-
end
-
# Internally exposed for Context.
-
1
public :exec_runtime
-
-
1
def which(command)
-
Array(command).find do |name|
-
name, args = name.split(/\s+/, 2)
-
path = locate_executable(name)
-
-
next unless path
-
-
args ? "#{path} #{args}" : path
-
end
-
end
-
end
-
end
-
1
require "execjs/runtime"
-
-
1
module ExecJS
-
1
class JohnsonRuntime < Runtime
-
1
class Context < Runtime::Context
-
1
def initialize(runtime, source = "")
-
source = encode(source)
-
-
@runtime = Johnson::Runtime.new
-
@runtime.evaluate(source)
-
end
-
-
1
def exec(source, options = {})
-
source = encode(source)
-
-
if /\S/ =~ source
-
eval "(function(){#{source}})()", options
-
end
-
end
-
-
1
def eval(source, options = {})
-
source = encode(source)
-
-
if /\S/ =~ source
-
unbox @runtime.evaluate("(#{source})")
-
end
-
rescue Johnson::Error => e
-
if syntax_error?(e)
-
raise RuntimeError, e.message
-
else
-
raise ProgramError, e.message
-
end
-
end
-
-
1
def call(properties, *args)
-
unbox @runtime.evaluate(properties).call(*args)
-
rescue Johnson::Error => e
-
if syntax_error?(e)
-
raise RuntimeError, e.message
-
else
-
raise ProgramError, e.message
-
end
-
end
-
-
1
def unbox(value)
-
case
-
when function?(value)
-
nil
-
when string?(value)
-
value.force_encoding('UTF-8')
-
when array?(value)
-
value.map { |v| unbox(v) }
-
when object?(value)
-
value.inject({}) do |vs, (k, v)|
-
vs[k] = unbox(v) unless function?(v)
-
vs
-
end
-
else
-
value
-
end
-
end
-
-
1
private
-
1
def syntax_error?(error)
-
error.message =~ /^syntax error at /
-
end
-
-
1
def function?(value)
-
value.respond_to?(:function?) && value.function?
-
end
-
-
1
def string?(value)
-
value.is_a?(String)
-
end
-
-
1
def array?(value)
-
array_test.call(value)
-
end
-
-
1
def object?(value)
-
value.respond_to?(:inject)
-
end
-
-
1
def array_test
-
@array_test ||= @runtime.evaluate("(function(a) {return a instanceof [].constructor})")
-
end
-
end
-
-
1
def name
-
"Johnson (SpiderMonkey)"
-
end
-
-
1
def available?
-
require "johnson"
-
true
-
rescue LoadError
-
false
-
end
-
-
1
def deprecated?
-
1
true
-
end
-
end
-
end
-
1
require "execjs/version"
-
1
require "rbconfig"
-
-
1
module ExecJS
-
1
class Error < ::StandardError; end
-
1
class RuntimeError < Error; end
-
1
class ProgramError < Error; end
-
1
class RuntimeUnavailable < RuntimeError; end
-
-
1
class << self
-
1
attr_reader :runtime
-
-
1
def runtime=(runtime)
-
1
raise RuntimeUnavailable, "#{runtime.name} is unavailable on this system" unless runtime.available?
-
1
@runtime = runtime
-
end
-
-
1
def exec(source)
-
runtime.exec(source)
-
end
-
-
1
def eval(source)
-
runtime.eval(source)
-
end
-
-
1
def compile(source)
-
runtime.compile(source)
-
end
-
-
1
def root
-
4
@root ||= File.expand_path("..", __FILE__)
-
end
-
-
1
def windows?
-
1
@windows ||= RbConfig::CONFIG["host_os"] =~ /mswin|mingw/
-
end
-
end
-
end
-
1
require "execjs/runtime"
-
-
1
module ExecJS
-
1
class MustangRuntime < Runtime
-
1
class Context < Runtime::Context
-
1
def initialize(runtime, source = "")
-
source = encode(source)
-
-
@v8_context = ::Mustang::Context.new
-
@v8_context.eval(source)
-
end
-
-
1
def exec(source, options = {})
-
source = encode(source)
-
-
if /\S/ =~ source
-
eval "(function(){#{source}})()", options
-
end
-
end
-
-
1
def eval(source, options = {})
-
source = encode(source)
-
-
if /\S/ =~ source
-
unbox @v8_context.eval("(#{source})")
-
end
-
end
-
-
1
def call(properties, *args)
-
unbox @v8_context.eval(properties).call(*args)
-
rescue NoMethodError => e
-
raise ProgramError, e.message
-
end
-
-
1
def unbox(value)
-
case value
-
when Mustang::V8::Array
-
value.map { |v| unbox(v) }
-
when Mustang::V8::Boolean
-
value.to_bool
-
when Mustang::V8::NullClass, Mustang::V8::UndefinedClass
-
nil
-
when Mustang::V8::Function
-
nil
-
when Mustang::V8::SyntaxError
-
raise RuntimeError, value.message
-
when Mustang::V8::Error
-
raise ProgramError, value.message
-
when Mustang::V8::Object
-
value.inject({}) { |h, (k, v)|
-
v = unbox(v)
-
h[k] = v if v
-
h
-
}
-
else
-
value.respond_to?(:delegate) ? value.delegate : value
-
end
-
end
-
end
-
-
1
def name
-
"Mustang (V8)"
-
end
-
-
1
def available?
-
require "mustang"
-
true
-
rescue LoadError
-
false
-
end
-
-
1
def deprecated?
-
1
true
-
end
-
end
-
end
-
1
require "execjs/runtime"
-
-
1
module ExecJS
-
1
class RubyRacerRuntime < Runtime
-
1
class Context < Runtime::Context
-
1
def initialize(runtime, source = "")
-
source = encode(source)
-
-
lock do
-
@v8_context = ::V8::Context.new
-
@v8_context.eval(source)
-
end
-
end
-
-
1
def exec(source, options = {})
-
source = encode(source)
-
-
if /\S/ =~ source
-
eval "(function(){#{source}})()", options
-
end
-
end
-
-
1
def eval(source, options = {})
-
source = encode(source)
-
-
if /\S/ =~ source
-
lock do
-
begin
-
unbox @v8_context.eval("(#{source})")
-
rescue ::V8::JSError => e
-
if e.value["name"] == "SyntaxError"
-
raise RuntimeError, e.value.to_s
-
else
-
raise ProgramError, e.value.to_s
-
end
-
end
-
end
-
end
-
end
-
-
1
def call(properties, *args)
-
lock do
-
begin
-
unbox @v8_context.eval(properties).call(*args)
-
rescue ::V8::JSError => e
-
if e.value["name"] == "SyntaxError"
-
raise RuntimeError, e.value.to_s
-
else
-
raise ProgramError, e.value.to_s
-
end
-
end
-
end
-
end
-
-
1
def unbox(value)
-
case value
-
when ::V8::Function
-
nil
-
when ::V8::Array
-
value.map { |v| unbox(v) }
-
when ::V8::Object
-
value.inject({}) do |vs, (k, v)|
-
vs[k] = unbox(v) unless v.is_a?(::V8::Function)
-
vs
-
end
-
when String
-
value.force_encoding('UTF-8')
-
else
-
value
-
end
-
end
-
-
1
private
-
1
def lock
-
result, exception = nil, nil
-
V8::C::Locker() do
-
begin
-
result = yield
-
rescue Exception => e
-
exception = e
-
end
-
end
-
-
if exception
-
raise exception
-
else
-
result
-
end
-
end
-
end
-
-
1
def name
-
"therubyracer (V8)"
-
end
-
-
1
def available?
-
2
require "v8"
-
2
true
-
rescue LoadError
-
false
-
end
-
end
-
end
-
1
require "execjs/runtime"
-
-
1
module ExecJS
-
1
class RubyRhinoRuntime < Runtime
-
1
class Context < Runtime::Context
-
1
def initialize(runtime, source = "")
-
source = encode(source)
-
-
@rhino_context = ::Rhino::Context.new
-
fix_memory_limit! @rhino_context
-
@rhino_context.eval(source)
-
end
-
-
1
def exec(source, options = {})
-
source = encode(source)
-
-
if /\S/ =~ source
-
eval "(function(){#{source}})()", options
-
end
-
end
-
-
1
def eval(source, options = {})
-
source = encode(source)
-
-
if /\S/ =~ source
-
unbox @rhino_context.eval("(#{source})")
-
end
-
rescue ::Rhino::JSError => e
-
if e.message =~ /^syntax error/
-
raise RuntimeError, e.message
-
else
-
raise ProgramError, e.message
-
end
-
end
-
-
1
def call(properties, *args)
-
unbox @rhino_context.eval(properties).call(*args)
-
rescue ::Rhino::JSError => e
-
if e.message == "syntax error"
-
raise RuntimeError, e.message
-
else
-
raise ProgramError, e.message
-
end
-
end
-
-
1
def unbox(value)
-
case value = ::Rhino::to_ruby(value)
-
when Java::OrgMozillaJavascript::NativeFunction
-
nil
-
when Java::OrgMozillaJavascript::NativeObject
-
value.inject({}) do |vs, (k, v)|
-
case v
-
when Java::OrgMozillaJavascript::NativeFunction, ::Rhino::JS::Function
-
nil
-
else
-
vs[k] = unbox(v)
-
end
-
vs
-
end
-
when Array
-
value.map { |v| unbox(v) }
-
else
-
value
-
end
-
end
-
-
1
private
-
# Disables bytecode compiling which limits you to 64K scripts
-
1
def fix_memory_limit!(context)
-
if context.respond_to?(:optimization_level=)
-
context.optimization_level = -1
-
else
-
context.instance_eval { @native.setOptimizationLevel(-1) }
-
end
-
end
-
end
-
-
1
def name
-
"therubyrhino (Rhino)"
-
end
-
-
1
def available?
-
require "rhino"
-
true
-
rescue LoadError
-
false
-
end
-
end
-
end
-
1
require "execjs/encoding"
-
-
1
module ExecJS
-
# Abstract base class for runtimes
-
1
class Runtime
-
1
class Context
-
1
include Encoding
-
-
1
def initialize(runtime, source = "")
-
end
-
-
1
def exec(source, options = {})
-
raise NotImplementedError
-
end
-
-
1
def eval(source, options = {})
-
raise NotImplementedError
-
end
-
-
1
def call(properties, *args)
-
raise NotImplementedError
-
end
-
end
-
-
1
def name
-
raise NotImplementedError
-
end
-
-
1
def context_class
-
self.class::Context
-
end
-
-
1
def exec(source)
-
context = context_class.new(self)
-
context.exec(source)
-
end
-
-
1
def eval(source)
-
context = context_class.new(self)
-
context.eval(source)
-
end
-
-
1
def compile(source)
-
context_class.new(self, source)
-
end
-
-
1
def deprecated?
-
2
false
-
end
-
-
1
def available?
-
raise NotImplementedError
-
end
-
end
-
end
-
1
require "execjs/module"
-
1
require "execjs/disabled_runtime"
-
1
require "execjs/external_runtime"
-
1
require "execjs/johnson_runtime"
-
1
require "execjs/mustang_runtime"
-
1
require "execjs/ruby_racer_runtime"
-
1
require "execjs/ruby_rhino_runtime"
-
-
1
module ExecJS
-
1
module Runtimes
-
1
Disabled = DisabledRuntime.new
-
-
1
RubyRacer = RubyRacerRuntime.new
-
-
1
RubyRhino = RubyRhinoRuntime.new
-
-
1
Johnson = JohnsonRuntime.new
-
-
1
Mustang = MustangRuntime.new
-
-
1
Node = ExternalRuntime.new(
-
name: "Node.js (V8)",
-
command: ["nodejs", "node"],
-
runner_path: ExecJS.root + "/support/node_runner.js",
-
encoding: 'UTF-8'
-
)
-
-
1
JavaScriptCore = ExternalRuntime.new(
-
name: "JavaScriptCore",
-
command: "/System/Library/Frameworks/JavaScriptCore.framework/Versions/A/Resources/jsc",
-
runner_path: ExecJS.root + "/support/jsc_runner.js"
-
)
-
-
1
SpiderMonkey = Spidermonkey = ExternalRuntime.new(
-
name: "SpiderMonkey",
-
command: "js",
-
runner_path: ExecJS.root + "/support/spidermonkey_runner.js",
-
deprecated: true
-
)
-
-
1
JScript = ExternalRuntime.new(
-
name: "JScript",
-
command: "cscript //E:jscript //Nologo //U",
-
runner_path: ExecJS.root + "/support/jscript_runner.js",
-
encoding: 'UTF-16LE' # CScript with //U returns UTF-16LE
-
)
-
-
-
1
def self.autodetect
-
1
from_environment || best_available ||
-
raise(RuntimeUnavailable, "Could not find a JavaScript runtime. " +
-
"See https://github.com/sstephenson/execjs for a list of available runtimes.")
-
end
-
-
1
def self.best_available
-
1
runtimes.reject(&:deprecated?).find(&:available?)
-
end
-
-
1
def self.from_environment
-
1
if name = ENV["EXECJS_RUNTIME"]
-
if runtime = const_get(name)
-
if runtime.available?
-
runtime if runtime.available?
-
else
-
raise RuntimeUnavailable, "#{runtime.name} runtime is not available on this system"
-
end
-
elsif !name.empty?
-
raise RuntimeUnavailable, "#{name} runtime is not defined"
-
end
-
end
-
end
-
-
1
def self.names
-
@names ||= constants.inject({}) { |h, name| h.merge(const_get(name) => name) }.values
-
end
-
-
1
def self.runtimes
-
@runtimes ||= [
-
RubyRacer,
-
RubyRhino,
-
Johnson,
-
Mustang,
-
Node,
-
JavaScriptCore,
-
SpiderMonkey,
-
JScript
-
1
]
-
end
-
end
-
-
1
def self.runtimes
-
Runtimes.runtimes
-
end
-
end
-
1
module ExecJS
-
1
VERSION = "2.2.2"
-
end
-
1
require 'thread'
-
1
require 'cgi'
-
1
require 'set'
-
1
require 'forwardable'
-
-
# Public: This is the main namespace for Faraday. You can either use it to
-
# create Faraday::Connection objects, or access it directly.
-
#
-
# Examples
-
#
-
# Faraday.get "http://faraday.com"
-
#
-
# conn = Faraday.new "http://faraday.com"
-
# conn.get '/'
-
#
-
1
module Faraday
-
1
VERSION = "0.9.0"
-
-
1
class << self
-
# Public: Gets or sets the root path that Faraday is being loaded from.
-
# This is the root from where the libraries are auto-loaded from.
-
1
attr_accessor :root_path
-
-
# Public: Gets or sets the path that the Faraday libs are loaded from.
-
1
attr_accessor :lib_path
-
-
# Public: Gets or sets the Symbol key identifying a default Adapter to use
-
# for the default Faraday::Connection.
-
1
attr_reader :default_adapter
-
-
# Public: Sets the default Faraday::Connection for simple scripts that
-
# access the Faraday constant directly.
-
#
-
# Faraday.get "https://faraday.com"
-
1
attr_writer :default_connection
-
-
# Public: Sets the default options used when calling Faraday#new.
-
1
attr_writer :default_connection_options
-
-
# Public: Initializes a new Faraday::Connection.
-
#
-
# url - The optional String base URL to use as a prefix for all
-
# requests. Can also be the options Hash.
-
# options - The optional Hash used to configure this Faraday::Connection.
-
# Any of these values will be set on every request made, unless
-
# overridden for a specific request.
-
# :url - String base URL.
-
# :params - Hash of URI query unencoded key/value pairs.
-
# :headers - Hash of unencoded HTTP header key/value pairs.
-
# :request - Hash of request options.
-
# :ssl - Hash of SSL options.
-
# :proxy - Hash of Proxy options.
-
#
-
# Examples
-
#
-
# Faraday.new 'http://faraday.com'
-
#
-
# # http://faraday.com?page=1
-
# Faraday.new 'http://faraday.com', :params => {:page => 1}
-
#
-
# # same
-
#
-
# Faraday.new :url => 'http://faraday.com',
-
# :params => {:page => 1}
-
#
-
# Returns a Faraday::Connection.
-
1
def new(url = nil, options = nil)
-
block = block_given? ? Proc.new : nil
-
options = options ? default_connection_options.merge(options) : default_connection_options.dup
-
Faraday::Connection.new(url, options, &block)
-
end
-
-
# Internal: Requires internal Faraday libraries.
-
#
-
# *libs - One or more relative String names to Faraday classes.
-
#
-
# Returns nothing.
-
1
def require_libs(*libs)
-
3
libs.each do |lib|
-
13
require "#{lib_path}/#{lib}"
-
end
-
end
-
-
# Public: Updates default adapter while resetting
-
# #default_connection.
-
#
-
# Returns the new default_adapter.
-
1
def default_adapter=(adapter)
-
1
@default_connection = nil
-
1
@default_adapter = adapter
-
end
-
-
1
alias require_lib require_libs
-
-
1
private
-
# Internal: Proxies method calls on the Faraday constant to
-
# #default_connection.
-
1
def method_missing(name, *args, &block)
-
default_connection.send(name, *args, &block)
-
end
-
end
-
-
1
self.root_path = File.expand_path "..", __FILE__
-
1
self.lib_path = File.expand_path "../faraday", __FILE__
-
1
self.default_adapter = :net_http
-
-
# Gets the default connection used for simple scripts.
-
#
-
# Returns a Faraday::Connection, configured with the #default_adapter.
-
1
def self.default_connection
-
@default_connection ||= Connection.new
-
end
-
-
# Gets the default connection options used when calling Faraday#new.
-
#
-
# Returns a Faraday::ConnectionOptions.
-
1
def self.default_connection_options
-
@default_connection_options ||= ConnectionOptions.new
-
end
-
-
1
if (!defined?(RUBY_ENGINE) || "ruby" == RUBY_ENGINE) && RUBY_VERSION < '1.9'
-
begin
-
require 'system_timer'
-
Timer = SystemTimer
-
rescue LoadError
-
warn "Faraday: you may want to install system_timer for reliable timeouts"
-
end
-
end
-
-
1
unless const_defined? :Timer
-
1
require 'timeout'
-
1
Timer = Timeout
-
end
-
-
# Public: Adds the ability for other modules to register and lookup
-
# middleware classes.
-
1
module MiddlewareRegistry
-
# Public: Register middleware class(es) on the current module.
-
#
-
# mapping - A Hash mapping Symbol keys to classes. Classes can be expressed
-
# as fully qualified constant, or a Proc that will be lazily
-
# called to return the former.
-
#
-
# Examples
-
#
-
# module Faraday
-
# class Whatever
-
# # Middleware looked up by :foo returns Faraday::Whatever::Foo.
-
# register_middleware :foo => Foo
-
#
-
# # Middleware looked up by :bar returns Faraday::Whatever.const_get(:Bar)
-
# register_middleware :bar => :Bar
-
#
-
# # Middleware looked up by :baz requires 'baz' and returns Faraday::Whatever.const_get(:Baz)
-
# register_middleware :baz => [:Baz, 'baz']
-
# end
-
# end
-
#
-
# Returns nothing.
-
1
def register_middleware(autoload_path = nil, mapping = nil)
-
6
if mapping.nil?
-
3
mapping = autoload_path
-
3
autoload_path = nil
-
end
-
6
middleware_mutex do
-
6
@middleware_autoload_path = autoload_path if autoload_path
-
6
(@registered_middleware ||= {}).update(mapping)
-
end
-
end
-
-
# Public: Lookup middleware class with a registered Symbol shortcut.
-
#
-
# key - The Symbol key for the registered middleware.
-
#
-
# Examples
-
#
-
# module Faraday
-
# class Whatever
-
# register_middleware :foo => Foo
-
# end
-
# end
-
#
-
# Faraday::Whatever.lookup_middleware(:foo)
-
# # => Faraday::Whatever::Foo
-
#
-
# Returns a middleware Class.
-
1
def lookup_middleware(key)
-
load_middleware(key) ||
-
raise(Faraday::Error.new("#{key.inspect} is not registered on #{self}"))
-
end
-
-
1
def middleware_mutex(&block)
-
@middleware_mutex ||= begin
-
4
require 'monitor'
-
4
Monitor.new
-
6
end
-
6
@middleware_mutex.synchronize(&block)
-
end
-
-
1
def fetch_middleware(key)
-
defined?(@registered_middleware) && @registered_middleware[key]
-
end
-
-
1
def load_middleware(key)
-
value = fetch_middleware(key)
-
case value
-
when Module
-
value
-
when Symbol, String
-
middleware_mutex do
-
@registered_middleware[key] = const_get(value)
-
end
-
when Proc
-
middleware_mutex do
-
@registered_middleware[key] = value.call
-
end
-
when Array
-
middleware_mutex do
-
const, path = value
-
if root = @middleware_autoload_path
-
path = "#{root}/#{path}"
-
end
-
require(path)
-
@registered_middleware[key] = const
-
end
-
load_middleware(key)
-
end
-
end
-
end
-
-
1
def self.const_missing(name)
-
if name.to_sym == :Builder
-
warn "Faraday::Builder is now Faraday::RackBuilder."
-
const_set name, RackBuilder
-
else
-
super
-
end
-
end
-
-
1
require_libs "utils", "options", "connection", "rack_builder", "parameters",
-
"middleware", "adapter", "request", "response", "upload_io", "error"
-
-
1
if !ENV["FARADAY_NO_AUTOLOAD"]
-
1
require_lib 'autoload'
-
end
-
end
-
-
# not pulling in active-support JUST for this method. And I love this method.
-
1
class Object
-
# The primary purpose of this method is to "tap into" a method chain,
-
# in order to perform operations on intermediate results within the chain.
-
#
-
# Examples
-
#
-
# (1..10).tap { |x| puts "original: #{x.inspect}" }.to_a.
-
# tap { |x| puts "array: #{x.inspect}" }.
-
# select { |x| x%2 == 0 }.
-
# tap { |x| puts "evens: #{x.inspect}" }.
-
# map { |x| x*x }.
-
# tap { |x| puts "squares: #{x.inspect}" }
-
#
-
# Yields self.
-
# Returns self.
-
def tap
-
yield(self)
-
self
-
1
end unless Object.respond_to?(:tap)
-
end
-
1
module Faraday
-
# Public: This is a base class for all Faraday adapters. Adapters are
-
# responsible for fulfilling a Faraday request.
-
1
class Adapter < Middleware
-
1
CONTENT_LENGTH = 'Content-Length'.freeze
-
-
1
register_middleware File.expand_path('../adapter', __FILE__),
-
:test => [:Test, 'test'],
-
:net_http => [:NetHttp, 'net_http'],
-
:net_http_persistent => [:NetHttpPersistent, 'net_http_persistent'],
-
:typhoeus => [:Typhoeus, 'typhoeus'],
-
:patron => [:Patron, 'patron'],
-
:em_synchrony => [:EMSynchrony, 'em_synchrony'],
-
:em_http => [:EMHttp, 'em_http'],
-
:excon => [:Excon, 'excon'],
-
:rack => [:Rack, 'rack'],
-
:httpclient => [:HTTPClient, 'httpclient']
-
-
# Public: This module marks an Adapter as supporting parallel requests.
-
1
module Parallelism
-
1
attr_writer :supports_parallel
-
1
def supports_parallel?() @supports_parallel end
-
-
1
def inherited(subclass)
-
super
-
subclass.supports_parallel = self.supports_parallel?
-
end
-
end
-
-
1
extend Parallelism
-
1
self.supports_parallel = false
-
-
1
def call(env)
-
env.clear_body if env.needs_body?
-
end
-
-
1
def save_response(env, status, body, headers = nil)
-
env.status = status
-
env.body = body
-
env.response_headers = Utils::Headers.new.tap do |response_headers|
-
response_headers.update headers unless headers.nil?
-
yield(response_headers) if block_given?
-
end
-
end
-
end
-
end
-
1
module Faraday
-
# Internal: Adds the ability for other modules to manage autoloadable
-
# constants.
-
1
module AutoloadHelper
-
# Internal: Registers the constants to be auto loaded.
-
#
-
# prefix - The String require prefix. If the path is inside Faraday, then
-
# it will be prefixed with the root path of this loaded Faraday
-
# version.
-
# options - Hash of Symbol => String library names.
-
#
-
# Examples.
-
#
-
# Faraday.autoload_all 'faraday/foo',
-
# :Bar => 'bar'
-
#
-
# # requires faraday/foo/bar to load Faraday::Bar.
-
# Faraday::Bar
-
#
-
#
-
# Returns nothing.
-
1
def autoload_all(prefix, options)
-
3
if prefix =~ /^faraday(\/|$)/i
-
3
prefix = File.join(Faraday.root_path, prefix)
-
end
-
3
options.each do |const_name, path|
-
20
autoload const_name, File.join(prefix, path)
-
end
-
end
-
-
# Internal: Loads each autoloaded constant. If thread safety is a concern,
-
# wrap this in a Mutex.
-
#
-
# Returns nothing.
-
1
def load_autoloaded_constants
-
constants.each do |const|
-
const_get(const) if autoload?(const)
-
end
-
end
-
-
# Internal: Filters the module's contents with those that have been already
-
# autoloaded.
-
#
-
# Returns an Array of Class/Module objects.
-
1
def all_loaded_constants
-
constants.map { |c| const_get(c) }.
-
select { |a| a.respond_to?(:loaded?) && a.loaded? }
-
end
-
end
-
-
1
class Adapter
-
1
extend AutoloadHelper
-
1
autoload_all 'faraday/adapter',
-
:NetHttp => 'net_http',
-
:NetHttpPersistent => 'net_http_persistent',
-
:Typhoeus => 'typhoeus',
-
:EMSynchrony => 'em_synchrony',
-
:EMHttp => 'em_http',
-
:Patron => 'patron',
-
:Excon => 'excon',
-
:Test => 'test',
-
:Rack => 'rack',
-
:HTTPClient => 'httpclient'
-
end
-
-
1
class Request
-
1
extend AutoloadHelper
-
1
autoload_all 'faraday/request',
-
:UrlEncoded => 'url_encoded',
-
:Multipart => 'multipart',
-
:Retry => 'retry',
-
:Timeout => 'timeout',
-
:Authorization => 'authorization',
-
:BasicAuthentication => 'basic_authentication',
-
:TokenAuthentication => 'token_authentication',
-
:Instrumentation => 'instrumentation'
-
end
-
-
1
class Response
-
1
extend AutoloadHelper
-
1
autoload_all 'faraday/response',
-
:RaiseError => 'raise_error',
-
:Logger => 'logger'
-
end
-
end
-
1
module Faraday
-
# Public: Connection objects manage the default properties and the middleware
-
# stack for fulfilling an HTTP request.
-
#
-
# Examples
-
#
-
# conn = Faraday::Connection.new 'http://sushi.com'
-
#
-
# # GET http://sushi.com/nigiri
-
# conn.get 'nigiri'
-
# # => #<Faraday::Response>
-
#
-
1
class Connection
-
# A Set of allowed HTTP verbs.
-
1
METHODS = Set.new [:get, :post, :put, :delete, :head, :patch, :options]
-
-
# Public: Returns a Hash of URI query unencoded key/value pairs.
-
1
attr_reader :params
-
-
# Public: Returns a Hash of unencoded HTTP header key/value pairs.
-
1
attr_reader :headers
-
-
# Public: Returns a URI with the prefix used for all requests from this
-
# Connection. This includes a default host name, scheme, port, and path.
-
1
attr_reader :url_prefix
-
-
# Public: Returns the Faraday::Builder for this Connection.
-
1
attr_reader :builder
-
-
# Public: Returns a Hash of the request options.
-
1
attr_reader :options
-
-
# Public: Returns a Hash of the SSL options.
-
1
attr_reader :ssl
-
-
# Public: Returns the parallel manager for this Connection.
-
1
attr_reader :parallel_manager
-
-
# Public: Sets the default parallel manager for this connection.
-
1
attr_writer :default_parallel_manager
-
-
# Public: Initializes a new Faraday::Connection.
-
#
-
# url - URI or String base URL to use as a prefix for all
-
# requests (optional).
-
# options - Hash or Faraday::ConnectionOptions.
-
# :url - URI or String base URL (default: "http:/").
-
# :params - Hash of URI query unencoded key/value pairs.
-
# :headers - Hash of unencoded HTTP header key/value pairs.
-
# :request - Hash of request options.
-
# :ssl - Hash of SSL options.
-
# :proxy - URI, String or Hash of HTTP proxy options
-
# (default: "http_proxy" environment variable).
-
# :uri - URI or String
-
# :user - String (optional)
-
# :password - String (optional)
-
1
def initialize(url = nil, options = nil)
-
if url.is_a?(Hash)
-
options = ConnectionOptions.from(url)
-
url = options.url
-
else
-
options = ConnectionOptions.from(options)
-
end
-
-
@parallel_manager = nil
-
@headers = Utils::Headers.new
-
@params = Utils::ParamsHash.new
-
@options = options.request
-
@ssl = options.ssl
-
@default_parallel_manager = options.parallel_manager
-
-
@builder = options.builder || begin
-
# pass an empty block to Builder so it doesn't assume default middleware
-
options.new_builder(block_given? ? Proc.new { |b| } : nil)
-
end
-
-
self.url_prefix = url || 'http:/'
-
-
@params.update(options.params) if options.params
-
@headers.update(options.headers) if options.headers
-
-
@proxy = nil
-
proxy(options.fetch(:proxy) {
-
uri = ENV['http_proxy']
-
if uri && !uri.empty?
-
uri = 'http://' + uri if uri !~ /^http/i
-
uri
-
end
-
})
-
-
yield(self) if block_given?
-
-
@headers[:user_agent] ||= "Faraday v#{VERSION}"
-
end
-
-
# Public: Sets the Hash of URI query unencoded key/value pairs.
-
1
def params=(hash)
-
@params.replace hash
-
end
-
-
# Public: Sets the Hash of unencoded HTTP header key/value pairs.
-
1
def headers=(hash)
-
@headers.replace hash
-
end
-
-
1
extend Forwardable
-
-
1
def_delegators :builder, :build, :use, :request, :response, :adapter, :app
-
-
# Public: Makes an HTTP request without a body.
-
#
-
# url - The optional String base URL to use as a prefix for all
-
# requests. Can also be the options Hash.
-
# params - Hash of URI query unencoded key/value pairs.
-
# headers - Hash of unencoded HTTP header key/value pairs.
-
#
-
# Examples
-
#
-
# conn.get '/items', {:page => 1}, :accept => 'application/json'
-
# conn.head '/items/1'
-
#
-
# # ElasticSearch example sending a body with GET.
-
# conn.get '/twitter/tweet/_search' do |req|
-
# req.headers[:content_type] = 'application/json'
-
# req.params[:routing] = 'kimchy'
-
# req.body = JSON.generate(:query => {...})
-
# end
-
#
-
# Yields a Faraday::Response for further request customizations.
-
# Returns a Faraday::Response.
-
#
-
# Signature
-
#
-
# <verb>(url = nil, params = nil, headers = nil)
-
#
-
# verb - An HTTP verb: get, head, or delete.
-
1
%w[get head delete].each do |method|
-
3
class_eval <<-RUBY, __FILE__, __LINE__ + 1
-
def #{method}(url = nil, params = nil, headers = nil)
-
run_request(:#{method}, url, nil, headers) { |request|
-
request.params.update(params) if params
-
yield(request) if block_given?
-
}
-
end
-
RUBY
-
end
-
-
# Public: Makes an HTTP request with a body.
-
#
-
# url - The optional String base URL to use as a prefix for all
-
# requests. Can also be the options Hash.
-
# body - The String body for the request.
-
# headers - Hash of unencoded HTTP header key/value pairs.
-
#
-
# Examples
-
#
-
# conn.post '/items', data, :content_type => 'application/json'
-
#
-
# # Simple ElasticSearch indexing sample.
-
# conn.post '/twitter/tweet' do |req|
-
# req.headers[:content_type] = 'application/json'
-
# req.params[:routing] = 'kimchy'
-
# req.body = JSON.generate(:user => 'kimchy', ...)
-
# end
-
#
-
# Yields a Faraday::Response for further request customizations.
-
# Returns a Faraday::Response.
-
#
-
# Signature
-
#
-
# <verb>(url = nil, body = nil, headers = nil)
-
#
-
# verb - An HTTP verb: post, put, or patch.
-
1
%w[post put patch].each do |method|
-
3
class_eval <<-RUBY, __FILE__, __LINE__ + 1
-
def #{method}(url = nil, body = nil, headers = nil, &block)
-
run_request(:#{method}, url, body, headers, &block)
-
end
-
RUBY
-
end
-
-
# Public: Sets up the Authorization header with these credentials, encoded
-
# with base64.
-
#
-
# login - The authentication login.
-
# pass - The authentication password.
-
#
-
# Examples
-
#
-
# conn.basic_auth 'Aladdin', 'open sesame'
-
# conn.headers['Authorization']
-
# # => "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
-
#
-
# Returns nothing.
-
1
def basic_auth(login, pass)
-
set_authorization_header(:basic_auth, login, pass)
-
end
-
-
# Public: Sets up the Authorization header with the given token.
-
#
-
# token - The String token.
-
# options - Optional Hash of extra token options.
-
#
-
# Examples
-
#
-
# conn.token_auth 'abcdef', :foo => 'bar'
-
# conn.headers['Authorization']
-
# # => "Token token=\"abcdef\",
-
# foo=\"bar\""
-
#
-
# Returns nothing.
-
1
def token_auth(token, options = nil)
-
set_authorization_header(:token_auth, token, options)
-
end
-
-
# Public: Sets up a custom Authorization header.
-
#
-
# type - The String authorization type.
-
# token - The String or Hash token. A String value is taken literally, and
-
# a Hash is encoded into comma separated key/value pairs.
-
#
-
# Examples
-
#
-
# conn.authorization :Bearer, 'mF_9.B5f-4.1JqM'
-
# conn.headers['Authorization']
-
# # => "Bearer mF_9.B5f-4.1JqM"
-
#
-
# conn.authorization :Token, :token => 'abcdef', :foo => 'bar'
-
# conn.headers['Authorization']
-
# # => "Token token=\"abcdef\",
-
# foo=\"bar\""
-
#
-
# Returns nothing.
-
1
def authorization(type, token)
-
set_authorization_header(:authorization, type, token)
-
end
-
-
# Internal: Traverse the middleware stack in search of a
-
# parallel-capable adapter.
-
#
-
# Yields in case of not found.
-
#
-
# Returns a parallel manager or nil if not found.
-
1
def default_parallel_manager
-
@default_parallel_manager ||= begin
-
handler = @builder.handlers.detect do |h|
-
h.klass.respond_to?(:supports_parallel?) and h.klass.supports_parallel?
-
end
-
-
if handler
-
handler.klass.setup_parallel_manager
-
elsif block_given?
-
yield
-
end
-
end
-
end
-
-
# Public: Determine if this Faraday::Connection can make parallel requests.
-
#
-
# Returns true or false.
-
1
def in_parallel?
-
!!@parallel_manager
-
end
-
-
# Public: Sets up the parallel manager to make a set of requests.
-
#
-
# manager - The parallel manager that this Connection's Adapter uses.
-
#
-
# Yields a block to execute multiple requests.
-
# Returns nothing.
-
1
def in_parallel(manager = nil)
-
@parallel_manager = manager || default_parallel_manager {
-
warn "Warning: `in_parallel` called but no parallel-capable adapter on Faraday stack"
-
warn caller[2,10].join("\n")
-
nil
-
}
-
yield
-
@parallel_manager && @parallel_manager.run
-
ensure
-
@parallel_manager = nil
-
end
-
-
# Public: Gets or Sets the Hash proxy options.
-
1
def proxy(arg = nil)
-
return @proxy if arg.nil?
-
@proxy = ProxyOptions.from(arg)
-
end
-
-
1
def_delegators :url_prefix, :scheme, :scheme=, :host, :host=, :port, :port=
-
1
def_delegator :url_prefix, :path, :path_prefix
-
-
# Public: Parses the giving url with URI and stores the individual
-
# components in this connection. These components serve as defaults for
-
# requests made by this connection.
-
#
-
# url - A String or URI.
-
#
-
# Examples
-
#
-
# conn = Faraday::Connection.new { ... }
-
# conn.url_prefix = "https://sushi.com/api"
-
# conn.scheme # => https
-
# conn.path_prefix # => "/api"
-
#
-
# conn.get("nigiri?page=2") # accesses https://sushi.com/api/nigiri
-
#
-
# Returns the parsed URI from teh given input..
-
1
def url_prefix=(url, encoder = nil)
-
uri = @url_prefix = Utils.URI(url)
-
self.path_prefix = uri.path
-
-
params.merge_query(uri.query, encoder)
-
uri.query = nil
-
-
with_uri_credentials(uri) do |user, password|
-
basic_auth user, password
-
uri.user = uri.password = nil
-
end
-
-
uri
-
end
-
-
# Public: Sets the path prefix and ensures that it always has a leading
-
# slash.
-
#
-
# value - A String.
-
#
-
# Returns the new String path prefix.
-
1
def path_prefix=(value)
-
url_prefix.path = if value
-
value = '/' + value unless value[0,1] == '/'
-
value
-
end
-
end
-
-
# Public: Takes a relative url for a request and combines it with the defaults
-
# set on the connection instance.
-
#
-
# conn = Faraday::Connection.new { ... }
-
# conn.url_prefix = "https://sushi.com/api?token=abc"
-
# conn.scheme # => https
-
# conn.path_prefix # => "/api"
-
#
-
# conn.build_url("nigiri?page=2") # => https://sushi.com/api/nigiri?token=abc&page=2
-
# conn.build_url("nigiri", :page => 2) # => https://sushi.com/api/nigiri?token=abc&page=2
-
#
-
1
def build_url(url = nil, extra_params = nil)
-
uri = build_exclusive_url(url)
-
-
query_values = params.dup.merge_query(uri.query, options.params_encoder)
-
query_values.update extra_params if extra_params
-
uri.query = query_values.empty? ? nil : query_values.to_query(options.params_encoder)
-
-
uri
-
end
-
-
# Builds and runs the Faraday::Request.
-
#
-
# method - The Symbol HTTP method.
-
# url - The String or URI to access.
-
# body - The String body
-
# headers - Hash of unencoded HTTP header key/value pairs.
-
#
-
# Returns a Faraday::Response.
-
1
def run_request(method, url, body, headers)
-
if !METHODS.include?(method)
-
raise ArgumentError, "unknown http method: #{method}"
-
end
-
-
request = build_request(method) do |req|
-
req.url(url) if url
-
req.headers.update(headers) if headers
-
req.body = body if body
-
yield(req) if block_given?
-
end
-
-
builder.build_response(self, request)
-
end
-
-
# Creates and configures the request object.
-
#
-
# Returns the new Request.
-
1
def build_request(method)
-
Request.create(method) do |req|
-
req.params = self.params.dup
-
req.headers = self.headers.dup
-
req.options = self.options.merge(:proxy => self.proxy)
-
yield(req) if block_given?
-
end
-
end
-
-
# Internal: Build an absolute URL based on url_prefix.
-
#
-
# url - A String or URI-like object
-
# params - A Faraday::Utils::ParamsHash to replace the query values
-
# of the resulting url (default: nil).
-
#
-
# Returns the resulting URI instance.
-
1
def build_exclusive_url(url = nil, params = nil)
-
url = nil if url.respond_to?(:empty?) and url.empty?
-
base = url_prefix
-
if url and base.path and base.path !~ /\/$/
-
base = base.dup
-
base.path = base.path + '/' # ensure trailing slash
-
end
-
uri = url ? base + url : base
-
uri.query = params.to_query(options.params_encoder) if params
-
uri.query = nil if uri.query and uri.query.empty?
-
uri
-
end
-
-
# Internal: Creates a duplicate of this Faraday::Connection.
-
#
-
# Returns a Faraday::Connection.
-
1
def dup
-
self.class.new(build_exclusive_url, :headers => headers.dup, :params => params.dup, :builder => builder.dup, :ssl => ssl.dup)
-
end
-
-
# Internal: Yields username and password extracted from a URI if they both exist.
-
1
def with_uri_credentials(uri)
-
if uri.user and uri.password
-
yield(Utils.unescape(uri.user), Utils.unescape(uri.password))
-
end
-
end
-
-
1
def set_authorization_header(header_type, *args)
-
header = Faraday::Request.lookup_middleware(header_type).
-
header(*args)
-
headers[Faraday::Request::Authorization::KEY] = header
-
end
-
end
-
end
-
1
module Faraday
-
1
class Error < StandardError; end
-
1
class MissingDependency < Error; end
-
-
1
class ClientError < Error
-
1
attr_reader :response
-
-
1
def initialize(ex, response = nil)
-
@wrapped_exception = nil
-
@response = response
-
-
if ex.respond_to?(:backtrace)
-
super(ex.message)
-
@wrapped_exception = ex
-
elsif ex.respond_to?(:each_key)
-
super("the server responded with status #{ex[:status]}")
-
@response = ex
-
else
-
super(ex.to_s)
-
end
-
end
-
-
1
def backtrace
-
if @wrapped_exception
-
@wrapped_exception.backtrace
-
else
-
super
-
end
-
end
-
-
1
def inspect
-
%(#<#{self.class}>)
-
end
-
end
-
-
1
class ConnectionFailed < ClientError; end
-
1
class ResourceNotFound < ClientError; end
-
1
class ParsingError < ClientError; end
-
-
1
class TimeoutError < ClientError
-
1
def initialize(ex = nil)
-
super(ex || "timeout")
-
end
-
end
-
-
1
class SSLError < ClientError
-
end
-
-
[:MissingDependency, :ClientError, :ConnectionFailed, :ResourceNotFound,
-
1
:ParsingError, :TimeoutError, :SSLError].each do |const|
-
7
Error.const_set(const, Faraday.const_get(const))
-
end
-
end
-
1
module Faraday
-
1
class Middleware
-
1
extend MiddlewareRegistry
-
-
1
class << self
-
1
attr_accessor :load_error
-
1
private :load_error=
-
end
-
-
1
self.load_error = nil
-
-
# Executes a block which should try to require and reference dependent libraries
-
1
def self.dependency(lib = nil)
-
lib ? require(lib) : yield
-
rescue LoadError, NameError => error
-
self.load_error = error
-
end
-
-
1
def self.new(*)
-
raise "missing dependency for #{self}: #{load_error.message}" unless loaded?
-
super
-
end
-
-
1
def self.loaded?
-
load_error.nil?
-
end
-
-
1
def self.inherited(subclass)
-
4
super
-
4
subclass.send(:load_error=, self.load_error)
-
end
-
-
1
def initialize(app = nil)
-
@app = app
-
end
-
end
-
end
-
1
module Faraday
-
# Subclasses Struct with some special helpers for converting from a Hash to
-
# a Struct.
-
1
class Options < Struct
-
# Public
-
1
def self.from(value)
-
value ? new.update(value) : new
-
end
-
-
# Public
-
1
def each
-
return to_enum(:each) unless block_given?
-
members.each do |key|
-
yield(key.to_sym, send(key))
-
end
-
end
-
-
# Public
-
1
def update(obj)
-
obj.each do |key, value|
-
if sub_options = self.class.options_for(key)
-
value = sub_options.from(value) if value
-
elsif Hash === value
-
hash = {}
-
value.each do |hash_key, hash_value|
-
hash[hash_key] = hash_value
-
end
-
value = hash
-
end
-
-
self.send("#{key}=", value) unless value.nil?
-
end
-
self
-
end
-
-
1
alias merge! update
-
-
# Public
-
1
def delete(key)
-
value = send(key)
-
send("#{key}=", nil)
-
value
-
end
-
-
# Public
-
1
def clear
-
members.each { |member| delete(member) }
-
end
-
-
# Public
-
1
def merge(value)
-
dup.update(value)
-
end
-
-
# Public
-
1
def fetch(key, *args)
-
unless symbolized_key_set.include?(key.to_sym)
-
key_setter = "#{key}="
-
if args.size > 0
-
send(key_setter, args.first)
-
elsif block_given?
-
send(key_setter, Proc.new.call(key))
-
else
-
raise self.class.fetch_error_class, "key not found: #{key.inspect}"
-
end
-
end
-
send(key)
-
end
-
-
# Public
-
1
def values_at(*keys)
-
keys.map { |key| send(key) }
-
end
-
-
# Public
-
1
def keys
-
members.reject { |member| send(member).nil? }
-
end
-
-
# Public
-
1
def empty?
-
keys.empty?
-
end
-
-
# Public
-
1
def each_key
-
return to_enum(:each_key) unless block_given?
-
keys.each do |key|
-
yield(key)
-
end
-
end
-
-
# Public
-
1
def key?(key)
-
keys.include?(key)
-
end
-
-
1
alias has_key? key?
-
-
# Public
-
1
def each_value
-
return to_enum(:each_value) unless block_given?
-
values.each do |value|
-
yield(value)
-
end
-
end
-
-
# Public
-
1
def value?(value)
-
values.include?(value)
-
end
-
-
1
alias has_value? value?
-
-
# Public
-
1
def to_hash
-
hash = {}
-
members.each do |key|
-
value = send(key)
-
hash[key.to_sym] = value unless value.nil?
-
end
-
hash
-
end
-
-
# Internal
-
1
def inspect
-
values = []
-
members.each do |member|
-
value = send(member)
-
values << "#{member}=#{value.inspect}" if value
-
end
-
values = values.empty? ? ' (empty)' : (' ' << values.join(", "))
-
-
%(#<#{self.class}#{values}>)
-
end
-
-
# Internal
-
1
def self.options(mapping)
-
2
attribute_options.update(mapping)
-
end
-
-
# Internal
-
1
def self.options_for(key)
-
attribute_options[key]
-
end
-
-
# Internal
-
1
def self.attribute_options
-
22
@attribute_options ||= {}
-
end
-
-
1
def self.memoized(key)
-
5
memoized_attributes[key.to_sym] = Proc.new
-
5
class_eval <<-RUBY, __FILE__, __LINE__ + 1
-
def #{key}() self[:#{key}]; end
-
RUBY
-
end
-
-
1
def self.memoized_attributes
-
25
@memoized_attributes ||= {}
-
end
-
-
1
def [](key)
-
key = key.to_sym
-
if method = self.class.memoized_attributes[key]
-
super(key) || (self[key] = instance_eval(&method))
-
else
-
super
-
end
-
end
-
-
1
def symbolized_key_set
-
@symbolized_key_set ||= Set.new(keys.map { |k| k.to_sym })
-
end
-
-
1
def self.inherited(subclass)
-
10
super
-
10
subclass.attribute_options.update(attribute_options)
-
10
subclass.memoized_attributes.update(memoized_attributes)
-
end
-
-
1
def self.fetch_error_class
-
@fetch_error_class ||= if Object.const_defined?(:KeyError)
-
::KeyError
-
else
-
::IndexError
-
end
-
end
-
end
-
-
class RequestOptions < Options.new(:params_encoder, :proxy, :bind,
-
:timeout, :open_timeout, :boundary,
-
1
:oauth)
-
-
1
def []=(key, value)
-
if key && key.to_sym == :proxy
-
super(key, value ? ProxyOptions.from(value) : nil)
-
else
-
super(key, value)
-
end
-
end
-
end
-
-
class SSLOptions < Options.new(:verify, :ca_file, :ca_path, :verify_mode,
-
1
:cert_store, :client_cert, :client_key, :certificate, :private_key, :verify_depth, :version)
-
-
1
def verify?
-
verify != false
-
end
-
-
1
def disable?
-
!verify?
-
end
-
end
-
-
1
class ProxyOptions < Options.new(:uri, :user, :password)
-
1
extend Forwardable
-
1
def_delegators :uri, :scheme, :scheme=, :host, :host=, :port, :port=, :path, :path=
-
-
1
def self.from(value)
-
case value
-
when String
-
value = {:uri => Utils.URI(value)}
-
when URI
-
value = {:uri => value}
-
when Hash, Options
-
if uri = value.delete(:uri)
-
value[:uri] = Utils.URI(uri)
-
end
-
end
-
super(value)
-
end
-
-
1
memoized(:user) { uri.user && Utils.unescape(uri.user) }
-
1
memoized(:password) { uri.password && Utils.unescape(uri.password) }
-
end
-
-
class ConnectionOptions < Options.new(:request, :proxy, :ssl, :builder, :url,
-
1
:parallel_manager, :params, :headers, :builder_class)
-
-
1
options :request => RequestOptions, :ssl => SSLOptions
-
-
1
memoized(:request) { self.class.options_for(:request).new }
-
-
1
memoized(:ssl) { self.class.options_for(:ssl).new }
-
-
1
memoized(:builder_class) { RackBuilder }
-
-
1
def new_builder(block)
-
builder_class.new(&block)
-
end
-
end
-
-
class Env < Options.new(:method, :body, :url, :request, :request_headers,
-
1
:ssl, :parallel_manager, :params, :response, :response_headers, :status)
-
-
1
ContentLength = 'Content-Length'.freeze
-
1
StatusesWithoutBody = Set.new [204, 304]
-
1
SuccessfulStatuses = 200..299
-
-
# A Set of HTTP verbs that typically send a body. If no body is set for
-
# these requests, the Content-Length header is set to 0.
-
1
MethodsWithBodies = Set.new [:post, :put, :patch, :options]
-
-
1
options :request => RequestOptions,
-
:request_headers => Utils::Headers, :response_headers => Utils::Headers
-
-
1
extend Forwardable
-
-
1
def_delegators :request, :params_encoder
-
-
# Public
-
1
def [](key)
-
if in_member_set?(key)
-
super(key)
-
else
-
custom_members[key]
-
end
-
end
-
-
# Public
-
1
def []=(key, value)
-
if in_member_set?(key)
-
super(key, value)
-
else
-
custom_members[key] = value
-
end
-
end
-
-
# Public
-
1
def success?
-
SuccessfulStatuses.include?(status)
-
end
-
-
# Public
-
1
def needs_body?
-
!body && MethodsWithBodies.include?(method)
-
end
-
-
# Public
-
1
def clear_body
-
request_headers[ContentLength] = '0'
-
self.body = ''
-
end
-
-
# Public
-
1
def parse_body?
-
!StatusesWithoutBody.include?(status)
-
end
-
-
# Public
-
1
def parallel?
-
!!parallel_manager
-
end
-
-
1
def inspect
-
attrs = [nil]
-
members.each do |mem|
-
if value = send(mem)
-
attrs << "@#{mem}=#{value.inspect}"
-
end
-
end
-
if !custom_members.empty?
-
attrs << "@custom=#{custom_members.inspect}"
-
end
-
%(#<#{self.class}#{attrs.join(" ")}>)
-
end
-
-
# Internal
-
1
def custom_members
-
@custom_members ||= {}
-
end
-
-
# Internal
-
1
if members.first.is_a?(Symbol)
-
1
def in_member_set?(key)
-
self.class.member_set.include?(key.to_sym)
-
end
-
else
-
def in_member_set?(key)
-
self.class.member_set.include?(key.to_s)
-
end
-
end
-
-
# Internal
-
1
def self.member_set
-
@member_set ||= Set.new(members)
-
end
-
end
-
end
-
1
module Faraday
-
1
module NestedParamsEncoder
-
1
ESCAPE_RE = /[^a-zA-Z0-9 .~_-]/
-
-
1
def self.escape(s)
-
return s.to_s.gsub(ESCAPE_RE) {
-
'%' + $&.unpack('H2' * $&.bytesize).join('%').upcase
-
}.tr(' ', '+')
-
end
-
-
1
def self.unescape(s)
-
CGI.unescape(s.to_s)
-
end
-
-
1
def self.encode(params)
-
return nil if params == nil
-
-
if !params.is_a?(Array)
-
if !params.respond_to?(:to_hash)
-
raise TypeError,
-
"Can't convert #{params.class} into Hash."
-
end
-
params = params.to_hash
-
params = params.map do |key, value|
-
key = key.to_s if key.kind_of?(Symbol)
-
[key, value]
-
end
-
# Useful default for OAuth and caching.
-
# Only to be used for non-Array inputs. Arrays should preserve order.
-
params.sort!
-
end
-
-
# Helper lambda
-
to_query = lambda do |parent, value|
-
if value.is_a?(Hash)
-
value = value.map do |key, val|
-
key = escape(key)
-
[key, val]
-
end
-
value.sort!
-
buffer = ""
-
value.each do |key, val|
-
new_parent = "#{parent}%5B#{key}%5D"
-
buffer << "#{to_query.call(new_parent, val)}&"
-
end
-
return buffer.chop
-
elsif value.is_a?(Array)
-
buffer = ""
-
value.each_with_index do |val, i|
-
new_parent = "#{parent}%5B%5D"
-
buffer << "#{to_query.call(new_parent, val)}&"
-
end
-
return buffer.chop
-
else
-
encoded_value = escape(value)
-
return "#{parent}=#{encoded_value}"
-
end
-
end
-
-
# The params have form [['key1', 'value1'], ['key2', 'value2']].
-
buffer = ''
-
params.each do |parent, value|
-
encoded_parent = escape(parent)
-
buffer << "#{to_query.call(encoded_parent, value)}&"
-
end
-
return buffer.chop
-
end
-
-
1
def self.decode(query)
-
return nil if query == nil
-
# Recursive helper lambda
-
dehash = lambda do |hash|
-
hash.each do |(key, value)|
-
if value.kind_of?(Hash)
-
hash[key] = dehash.call(value)
-
end
-
end
-
# Numeric keys implies an array
-
if hash != {} && hash.keys.all? { |key| key =~ /^\d+$/ }
-
hash.sort.inject([]) do |accu, (_, value)|
-
accu << value; accu
-
end
-
else
-
hash
-
end
-
end
-
-
empty_accumulator = {}
-
return ((query.split('&').map do |pair|
-
pair.split('=', 2) if pair && !pair.empty?
-
end).compact.inject(empty_accumulator.dup) do |accu, (key, value)|
-
key = unescape(key)
-
if value.kind_of?(String)
-
value = unescape(value.gsub(/\+/, ' '))
-
end
-
-
array_notation = !!(key =~ /\[\]$/)
-
subkeys = key.split(/[\[\]]+/)
-
current_hash = accu
-
for i in 0...(subkeys.size - 1)
-
subkey = subkeys[i]
-
current_hash[subkey] = {} unless current_hash[subkey]
-
current_hash = current_hash[subkey]
-
end
-
if array_notation
-
current_hash[subkeys.last] = [] unless current_hash[subkeys.last]
-
current_hash[subkeys.last] << value
-
else
-
current_hash[subkeys.last] = value
-
end
-
accu
-
end).inject(empty_accumulator.dup) do |accu, (key, value)|
-
accu[key] = value.kind_of?(Hash) ? dehash.call(value) : value
-
accu
-
end
-
end
-
end
-
-
1
module FlatParamsEncoder
-
1
ESCAPE_RE = /[^a-zA-Z0-9 .~_-]/
-
-
1
def self.escape(s)
-
return s.to_s.gsub(ESCAPE_RE) {
-
'%' + $&.unpack('H2' * $&.bytesize).join('%').upcase
-
}.tr(' ', '+')
-
end
-
-
1
def self.unescape(s)
-
CGI.unescape(s.to_s)
-
end
-
-
1
def self.encode(params)
-
return nil if params == nil
-
-
if !params.is_a?(Array)
-
if !params.respond_to?(:to_hash)
-
raise TypeError,
-
"Can't convert #{params.class} into Hash."
-
end
-
params = params.to_hash
-
params = params.map do |key, value|
-
key = key.to_s if key.kind_of?(Symbol)
-
[key, value]
-
end
-
# Useful default for OAuth and caching.
-
# Only to be used for non-Array inputs. Arrays should preserve order.
-
params.sort!
-
end
-
-
# The params have form [['key1', 'value1'], ['key2', 'value2']].
-
buffer = ''
-
params.each do |key, value|
-
encoded_key = escape(key)
-
value = value.to_s if value == true || value == false
-
if value == nil
-
buffer << "#{encoded_key}&"
-
elsif value.kind_of?(Array)
-
value.each do |sub_value|
-
encoded_value = escape(sub_value)
-
buffer << "#{encoded_key}=#{encoded_value}&"
-
end
-
else
-
encoded_value = escape(value)
-
buffer << "#{encoded_key}=#{encoded_value}&"
-
end
-
end
-
return buffer.chop
-
end
-
-
1
def self.decode(query)
-
empty_accumulator = {}
-
return nil if query == nil
-
split_query = (query.split('&').map do |pair|
-
pair.split('=', 2) if pair && !pair.empty?
-
end).compact
-
return split_query.inject(empty_accumulator.dup) do |accu, pair|
-
pair[0] = unescape(pair[0])
-
pair[1] = true if pair[1].nil?
-
if pair[1].respond_to?(:to_str)
-
pair[1] = unescape(pair[1].to_str.gsub(/\+/, " "))
-
end
-
if accu[pair[0]].kind_of?(Array)
-
accu[pair[0]] << pair[1]
-
elsif accu[pair[0]]
-
accu[pair[0]] = [accu[pair[0]], pair[1]]
-
else
-
accu[pair[0]] = pair[1]
-
end
-
accu
-
end
-
end
-
end
-
end
-
1
module Faraday
-
# A Builder that processes requests into responses by passing through an inner
-
# middleware stack (heavily inspired by Rack).
-
#
-
# Faraday::Connection.new(:url => 'http://sushi.com') do |builder|
-
# builder.request :url_encoded # Faraday::Request::UrlEncoded
-
# builder.adapter :net_http # Faraday::Adapter::NetHttp
-
# end
-
1
class RackBuilder
-
1
attr_accessor :handlers
-
-
# Error raised when trying to modify the stack after calling `lock!`
-
1
class StackLocked < RuntimeError; end
-
-
# borrowed from ActiveSupport::Dependencies::Reference &
-
# ActionDispatch::MiddlewareStack::Middleware
-
1
class Handler
-
1
@@constants_mutex = Mutex.new
-
1
@@constants = Hash.new { |h, k|
-
value = k.respond_to?(:constantize) ? k.constantize : Object.const_get(k)
-
@@constants_mutex.synchronize { h[k] = value }
-
}
-
-
1
attr_reader :name
-
-
1
def initialize(klass, *args, &block)
-
@name = klass.to_s
-
if klass.respond_to?(:name)
-
@@constants_mutex.synchronize { @@constants[@name] = klass }
-
end
-
@args, @block = args, block
-
end
-
-
1
def klass() @@constants[@name] end
-
1
def inspect() @name end
-
-
1
def ==(other)
-
if other.is_a? Handler
-
self.name == other.name
-
elsif other.respond_to? :name
-
klass == other
-
else
-
@name == other.to_s
-
end
-
end
-
-
1
def build(app)
-
klass.new(app, *@args, &@block)
-
end
-
end
-
-
1
def initialize(handlers = [])
-
@handlers = handlers
-
if block_given?
-
build(&Proc.new)
-
elsif @handlers.empty?
-
# default stack, if nothing else is configured
-
self.request :url_encoded
-
self.adapter Faraday.default_adapter
-
end
-
end
-
-
1
def build(options = {})
-
raise_if_locked
-
@handlers.clear unless options[:keep]
-
yield(self) if block_given?
-
end
-
-
1
def [](idx)
-
@handlers[idx]
-
end
-
-
# Locks the middleware stack to ensure no further modifications are possible.
-
1
def lock!
-
@handlers.freeze
-
end
-
-
1
def locked?
-
@handlers.frozen?
-
end
-
-
1
def use(klass, *args, &block)
-
if klass.is_a? Symbol
-
use_symbol(Faraday::Middleware, klass, *args, &block)
-
else
-
raise_if_locked
-
@handlers << self.class::Handler.new(klass, *args, &block)
-
end
-
end
-
-
1
def request(key, *args, &block)
-
use_symbol(Faraday::Request, key, *args, &block)
-
end
-
-
1
def response(key, *args, &block)
-
use_symbol(Faraday::Response, key, *args, &block)
-
end
-
-
1
def adapter(key, *args, &block)
-
use_symbol(Faraday::Adapter, key, *args, &block)
-
end
-
-
## methods to push onto the various positions in the stack:
-
-
1
def insert(index, *args, &block)
-
raise_if_locked
-
index = assert_index(index)
-
handler = self.class::Handler.new(*args, &block)
-
@handlers.insert(index, handler)
-
end
-
-
1
alias_method :insert_before, :insert
-
-
1
def insert_after(index, *args, &block)
-
index = assert_index(index)
-
insert(index + 1, *args, &block)
-
end
-
-
1
def swap(index, *args, &block)
-
raise_if_locked
-
index = assert_index(index)
-
@handlers.delete_at(index)
-
insert(index, *args, &block)
-
end
-
-
1
def delete(handler)
-
raise_if_locked
-
@handlers.delete(handler)
-
end
-
-
# Processes a Request into a Response by passing it through this Builder's
-
# middleware stack.
-
#
-
# connection - Faraday::Connection
-
# request - Faraday::Request
-
#
-
# Returns a Faraday::Response.
-
1
def build_response(connection, request)
-
app.call(build_env(connection, request))
-
end
-
-
# The "rack app" wrapped in middleware. All requests are sent here.
-
#
-
# The builder is responsible for creating the app object. After this,
-
# the builder gets locked to ensure no further modifications are made
-
# to the middleware stack.
-
#
-
# Returns an object that responds to `call` and returns a Response.
-
1
def app
-
@app ||= begin
-
lock!
-
to_app(lambda { |env|
-
response = Response.new
-
response.finish(env) unless env.parallel?
-
env.response = response
-
})
-
end
-
end
-
-
1
def to_app(inner_app)
-
# last added handler is the deepest and thus closest to the inner app
-
@handlers.reverse.inject(inner_app) { |app, handler| handler.build(app) }
-
end
-
-
1
def ==(other)
-
other.is_a?(self.class) && @handlers == other.handlers
-
end
-
-
1
def dup
-
self.class.new(@handlers.dup)
-
end
-
-
# ENV Keys
-
# :method - a symbolized request method (:get, :post)
-
# :body - the request body that will eventually be converted to a string.
-
# :url - URI instance for the current request.
-
# :status - HTTP response status code
-
# :request_headers - hash of HTTP Headers to be sent to the server
-
# :response_headers - Hash of HTTP headers from the server
-
# :parallel_manager - sent if the connection is in parallel mode
-
# :request - Hash of options for configuring the request.
-
# :timeout - open/read timeout Integer in seconds
-
# :open_timeout - read timeout Integer in seconds
-
# :proxy - Hash of proxy options
-
# :uri - Proxy Server URI
-
# :user - Proxy server username
-
# :password - Proxy server password
-
# :ssl - Hash of options for configuring SSL requests.
-
1
def build_env(connection, request)
-
Env.new(request.method, request.body,
-
connection.build_exclusive_url(request.path, request.params),
-
request.options, request.headers, connection.ssl,
-
connection.parallel_manager)
-
end
-
-
1
private
-
-
1
def raise_if_locked
-
raise StackLocked, "can't modify middleware stack after making a request" if locked?
-
end
-
-
1
def use_symbol(mod, key, *args, &block)
-
use(mod.lookup_middleware(key), *args, &block)
-
end
-
-
1
def assert_index(index)
-
idx = index.is_a?(Integer) ? index : @handlers.index(index)
-
raise "No such handler: #{index.inspect}" unless idx
-
idx
-
end
-
end
-
end
-
1
module Faraday
-
# Used to setup urls, params, headers, and the request body in a sane manner.
-
#
-
# @connection.post do |req|
-
# req.url 'http://localhost', 'a' => '1' # 'http://localhost?a=1'
-
# req.headers['b'] = '2' # Header
-
# req.params['c'] = '3' # GET Param
-
# req['b'] = '2' # also Header
-
# req.body = 'abc'
-
# end
-
#
-
1
class Request < Struct.new(:method, :path, :params, :headers, :body, :options)
-
1
extend MiddlewareRegistry
-
-
1
register_middleware File.expand_path('../request', __FILE__),
-
:url_encoded => [:UrlEncoded, 'url_encoded'],
-
:multipart => [:Multipart, 'multipart'],
-
:retry => [:Retry, 'retry'],
-
:authorization => [:Authorization, 'authorization'],
-
:basic_auth => [:BasicAuthentication, 'basic_authentication'],
-
:token_auth => [:TokenAuthentication, 'token_authentication'],
-
:instrumentation => [:Instrumentation, 'instrumentation']
-
-
1
def self.create(request_method)
-
new(request_method).tap do |request|
-
yield(request) if block_given?
-
end
-
end
-
-
# Public: Replace params, preserving the existing hash type
-
1
def params=(hash)
-
if params
-
params.replace hash
-
else
-
super
-
end
-
end
-
-
# Public: Replace request headers, preserving the existing hash type
-
1
def headers=(hash)
-
if headers
-
headers.replace hash
-
else
-
super
-
end
-
end
-
-
1
def url(path, params = nil)
-
if path.respond_to? :query
-
if query = path.query
-
path = path.dup
-
path.query = nil
-
end
-
else
-
path, query = path.split('?', 2)
-
end
-
self.path = path
-
self.params.merge_query query, options.params_encoder
-
self.params.update(params) if params
-
end
-
-
1
def [](key)
-
headers[key]
-
end
-
-
1
def []=(key, value)
-
headers[key] = value
-
end
-
-
# ENV Keys
-
# :method - a symbolized request method (:get, :post)
-
# :body - the request body that will eventually be converted to a string.
-
# :url - URI instance for the current request.
-
# :status - HTTP response status code
-
# :request_headers - hash of HTTP Headers to be sent to the server
-
# :response_headers - Hash of HTTP headers from the server
-
# :parallel_manager - sent if the connection is in parallel mode
-
# :request - Hash of options for configuring the request.
-
# :timeout - open/read timeout Integer in seconds
-
# :open_timeout - read timeout Integer in seconds
-
# :proxy - Hash of proxy options
-
# :uri - Proxy Server URI
-
# :user - Proxy server username
-
# :password - Proxy server password
-
# :ssl - Hash of options for configuring SSL requests.
-
1
def to_env(connection)
-
Env.new(method, body, connection.build_exclusive_url(path, params),
-
options, headers, connection.ssl, connection.parallel_manager)
-
end
-
end
-
end
-
-
1
require 'forwardable'
-
-
1
module Faraday
-
1
class Response
-
# Used for simple response middleware.
-
1
class Middleware < Faraday::Middleware
-
1
def call(env)
-
@app.call(env).on_complete do |environment|
-
on_complete(environment)
-
end
-
end
-
-
# Override this to modify the environment after the response has finished.
-
# Calls the `parse` method if defined
-
1
def on_complete(env)
-
env.body = parse(env.body) if respond_to?(:parse) && env.parse_body?
-
end
-
end
-
-
1
extend Forwardable
-
1
extend MiddlewareRegistry
-
-
1
register_middleware File.expand_path('../response', __FILE__),
-
:raise_error => [:RaiseError, 'raise_error'],
-
:logger => [:Logger, 'logger']
-
-
1
def initialize(env = nil)
-
@env = Env.from(env) if env
-
@on_complete_callbacks = []
-
end
-
-
1
attr_reader :env
-
-
1
def_delegators :env, :to_hash
-
-
1
def status
-
finished? ? env.status : nil
-
end
-
-
1
def headers
-
finished? ? env.response_headers : {}
-
end
-
1
def_delegator :headers, :[]
-
-
1
def body
-
finished? ? env.body : nil
-
end
-
-
1
def finished?
-
!!env
-
end
-
-
1
def on_complete
-
if not finished?
-
@on_complete_callbacks << Proc.new
-
else
-
yield(env)
-
end
-
return self
-
end
-
-
1
def finish(env)
-
raise "response already finished" if finished?
-
@env = Env.from(env)
-
@on_complete_callbacks.each { |callback| callback.call(env) }
-
return self
-
end
-
-
1
def success?
-
finished? && env.success?
-
end
-
-
# because @on_complete_callbacks cannot be marshalled
-
1
def marshal_dump
-
!finished? ? nil : {
-
:status => @env.status, :body => @env.body,
-
:response_headers => @env.response_headers
-
}
-
end
-
-
1
def marshal_load(env)
-
@env = Env.from(env)
-
end
-
-
# Expand the env with more properties, without overriding existing ones.
-
# Useful for applying request params after restoring a marshalled Response.
-
1
def apply_request(request_env)
-
raise "response didn't finish yet" unless finished?
-
@env = Env.from(request_env).update(@env)
-
return self
-
end
-
end
-
end
-
1
begin
-
1
require 'composite_io'
-
1
require 'parts'
-
1
require 'stringio'
-
rescue LoadError
-
$stderr.puts "Install the multipart-post gem."
-
raise
-
end
-
-
1
module Faraday
-
# Similar but not compatible with ::CompositeReadIO provided by multipart-post.
-
1
class CompositeReadIO
-
1
def initialize(*parts)
-
@parts = parts.flatten
-
@ios = @parts.map { |part| part.to_io }
-
@index = 0
-
end
-
-
1
def length
-
@parts.inject(0) { |sum, part| sum + part.length }
-
end
-
-
1
def rewind
-
@ios.each { |io| io.rewind }
-
@index = 0
-
end
-
-
# Read from IOs in order until `length` bytes have been received.
-
1
def read(length = nil, outbuf = nil)
-
got_result = false
-
outbuf = outbuf ? outbuf.replace("") : ""
-
-
while io = current_io
-
if result = io.read(length)
-
got_result ||= !result.nil?
-
result.force_encoding("BINARY") if result.respond_to?(:force_encoding)
-
outbuf << result
-
length -= result.length if length
-
break if length == 0
-
end
-
advance_io
-
end
-
(!got_result && length) ? nil : outbuf
-
end
-
-
1
def close
-
@ios.each { |io| io.close }
-
end
-
-
1
def ensure_open_and_readable
-
# Rubinius compatibility
-
end
-
-
1
private
-
-
1
def current_io
-
@ios[@index]
-
end
-
-
1
def advance_io
-
@index += 1
-
end
-
end
-
-
1
UploadIO = ::UploadIO
-
1
Parts = ::Parts
-
end
-
1
require 'thread'
-
1
Faraday.require_libs 'parameters'
-
-
1
module Faraday
-
1
module Utils
-
1
extend self
-
-
# Adapted from Rack::Utils::HeaderHash
-
1
class Headers < ::Hash
-
1
def self.from(value)
-
new(value)
-
end
-
-
1
def initialize(hash = nil)
-
super()
-
@names = {}
-
self.update(hash || {})
-
end
-
-
# need to synchronize concurrent writes to the shared KeyMap
-
1
keymap_mutex = Mutex.new
-
-
# symbol -> string mapper + cache
-
1
KeyMap = Hash.new do |map, key|
-
value = if key.respond_to?(:to_str)
-
key
-
else
-
key.to_s.split('_'). # :user_agent => %w(user agent)
-
each { |w| w.capitalize! }. # => %w(User Agent)
-
join('-') # => "User-Agent"
-
end
-
keymap_mutex.synchronize { map[key] = value }
-
end
-
1
KeyMap[:etag] = "ETag"
-
-
1
def [](k)
-
k = KeyMap[k]
-
super(k) || super(@names[k.downcase])
-
end
-
-
1
def []=(k, v)
-
k = KeyMap[k]
-
k = (@names[k.downcase] ||= k)
-
# join multiple values with a comma
-
v = v.to_ary.join(', ') if v.respond_to? :to_ary
-
super(k, v)
-
end
-
-
1
def fetch(k, *args, &block)
-
k = KeyMap[k]
-
key = @names.fetch(k.downcase, k)
-
super(key, *args, &block)
-
end
-
-
1
def delete(k)
-
k = KeyMap[k]
-
if k = @names[k.downcase]
-
@names.delete k.downcase
-
super(k)
-
end
-
end
-
-
1
def include?(k)
-
@names.include? k.downcase
-
end
-
-
1
alias_method :has_key?, :include?
-
1
alias_method :member?, :include?
-
1
alias_method :key?, :include?
-
-
1
def merge!(other)
-
other.each { |k, v| self[k] = v }
-
self
-
end
-
1
alias_method :update, :merge!
-
-
1
def merge(other)
-
hash = dup
-
hash.merge! other
-
end
-
-
1
def replace(other)
-
clear
-
self.update other
-
self
-
end
-
-
1
def to_hash() ::Hash.new.update(self) end
-
-
1
def parse(header_string)
-
return unless header_string && !header_string.empty?
-
header_string.split(/\r\n/).
-
tap { |a| a.shift if a.first.index('HTTP/') == 0 }. # drop the HTTP status line
-
map { |h| h.split(/:\s+/, 2) }.reject { |p| p[0].nil? }. # split key and value, ignore blank lines
-
each { |key, value|
-
# join multiple values with a comma
-
if self[key]
-
self[key] << ', ' << value
-
else
-
self[key] = value
-
end
-
}
-
end
-
end
-
-
# hash with stringified keys
-
1
class ParamsHash < Hash
-
1
def [](key)
-
super(convert_key(key))
-
end
-
-
1
def []=(key, value)
-
super(convert_key(key), value)
-
end
-
-
1
def delete(key)
-
super(convert_key(key))
-
end
-
-
1
def include?(key)
-
super(convert_key(key))
-
end
-
-
1
alias_method :has_key?, :include?
-
1
alias_method :member?, :include?
-
1
alias_method :key?, :include?
-
-
1
def update(params)
-
params.each do |key, value|
-
self[key] = value
-
end
-
self
-
end
-
1
alias_method :merge!, :update
-
-
1
def merge(params)
-
dup.update(params)
-
end
-
-
1
def replace(other)
-
clear
-
update(other)
-
end
-
-
1
def merge_query(query, encoder = nil)
-
if query && !query.empty?
-
update((encoder || Utils.default_params_encoder).decode(query))
-
end
-
self
-
end
-
-
1
def to_query(encoder = nil)
-
(encoder || Utils.default_params_encoder).encode(self)
-
end
-
-
1
private
-
-
1
def convert_key(key)
-
key.to_s
-
end
-
end
-
-
1
def build_query(params)
-
FlatParamsEncoder.encode(params)
-
end
-
-
1
def build_nested_query(params)
-
NestedParamsEncoder.encode(params)
-
end
-
-
1
ESCAPE_RE = /[^a-zA-Z0-9 .~_-]/
-
-
1
def escape(s)
-
s.to_s.gsub(ESCAPE_RE) {|match|
-
'%' + match.unpack('H2' * match.bytesize).join('%').upcase
-
}.tr(' ', '+')
-
end
-
-
1
def unescape(s) CGI.unescape s.to_s end
-
-
1
DEFAULT_SEP = /[&;] */n
-
-
# Adapted from Rack
-
1
def parse_query(query)
-
FlatParamsEncoder.decode(query)
-
end
-
-
1
def parse_nested_query(query)
-
NestedParamsEncoder.decode(query)
-
end
-
-
1
def default_params_encoder
-
@default_params_encoder ||= NestedParamsEncoder
-
end
-
-
1
class << self
-
1
attr_writer :default_params_encoder
-
end
-
-
# Stolen from Rack
-
1
def normalize_params(params, name, v = nil)
-
name =~ %r(\A[\[\]]*([^\[\]]+)\]*)
-
k = $1 || ''
-
after = $' || ''
-
-
return if k.empty?
-
-
if after == ""
-
if params[k]
-
params[k] = Array[params[k]] unless params[k].kind_of?(Array)
-
params[k] << v
-
else
-
params[k] = v
-
end
-
elsif after == "[]"
-
params[k] ||= []
-
raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
-
params[k] << v
-
elsif after =~ %r(^\[\]\[([^\[\]]+)\]$) || after =~ %r(^\[\](.+)$)
-
child_key = $1
-
params[k] ||= []
-
raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
-
if params[k].last.is_a?(Hash) && !params[k].last.key?(child_key)
-
normalize_params(params[k].last, child_key, v)
-
else
-
params[k] << normalize_params({}, child_key, v)
-
end
-
else
-
params[k] ||= {}
-
raise TypeError, "expected Hash (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Hash)
-
params[k] = normalize_params(params[k], after, v)
-
end
-
-
return params
-
end
-
-
# Normalize URI() behavior across Ruby versions
-
#
-
# url - A String or URI.
-
#
-
# Returns a parsed URI.
-
1
def URI(url)
-
if url.respond_to?(:host)
-
url
-
elsif url.respond_to?(:to_str)
-
default_uri_parser.call(url)
-
else
-
raise ArgumentError, "bad argument (expected URI object or URI string)"
-
end
-
end
-
-
1
def default_uri_parser
-
@default_uri_parser ||= begin
-
require 'uri'
-
Kernel.method(:URI)
-
end
-
end
-
-
1
def default_uri_parser=(parser)
-
@default_uri_parser = if parser.respond_to?(:call) || parser.nil?
-
parser
-
else
-
parser.method(:parse)
-
end
-
end
-
-
# Receives a String or URI and returns just the path with the query string sorted.
-
1
def normalize_path(url)
-
url = URI(url)
-
(url.path.start_with?('/') ? url.path : '/' + url.path) +
-
(url.query ? "?#{sort_query_params(url.query)}" : "")
-
end
-
-
# Recursive hash update
-
1
def deep_merge!(target, hash)
-
hash.each do |key, value|
-
if Hash === value and Hash === target[key]
-
target[key] = deep_merge(target[key], value)
-
else
-
target[key] = value
-
end
-
end
-
target
-
end
-
-
# Recursive hash merge
-
1
def deep_merge(source, hash)
-
deep_merge!(source.dup, hash)
-
end
-
-
1
protected
-
-
1
def sort_query_params(query)
-
query.split('&').sort.join('&')
-
end
-
end
-
end
-
1
require 'faraday'
-
-
1
module FaradayMiddleware
-
1
autoload :OAuth, 'faraday_middleware/request/oauth'
-
1
autoload :OAuth2, 'faraday_middleware/request/oauth2'
-
1
autoload :EncodeJson, 'faraday_middleware/request/encode_json'
-
1
autoload :MethodOverride, 'faraday_middleware/request/method_override'
-
1
autoload :Mashify, 'faraday_middleware/response/mashify'
-
1
autoload :Rashify, 'faraday_middleware/response/rashify'
-
1
autoload :ParseJson, 'faraday_middleware/response/parse_json'
-
1
autoload :ParseXml, 'faraday_middleware/response/parse_xml'
-
1
autoload :ParseMarshal, 'faraday_middleware/response/parse_marshal'
-
1
autoload :ParseYaml, 'faraday_middleware/response/parse_yaml'
-
1
autoload :ParseDates, 'faraday_middleware/response/parse_dates'
-
1
autoload :Caching, 'faraday_middleware/response/caching'
-
1
autoload :Chunked, 'faraday_middleware/response/chunked'
-
1
autoload :RackCompatible, 'faraday_middleware/rack_compatible'
-
1
autoload :FollowRedirects, 'faraday_middleware/response/follow_redirects'
-
1
autoload :Instrumentation, 'faraday_middleware/instrumentation'
-
-
1
if Faraday::Middleware.respond_to? :register_middleware
-
1
Faraday::Request.register_middleware \
-
:oauth => lambda { OAuth },
-
:oauth2 => lambda { OAuth2 },
-
:json => lambda { EncodeJson },
-
:method_override => lambda { MethodOverride }
-
-
1
Faraday::Response.register_middleware \
-
:mashify => lambda { Mashify },
-
:rashify => lambda { Rashify },
-
:json => lambda { ParseJson },
-
:json_fix => lambda { ParseJson::MimeTypeFix },
-
:xml => lambda { ParseXml },
-
:marshal => lambda { ParseMarshal },
-
:yaml => lambda { ParseYaml },
-
:dates => lambda { ParseDates },
-
:caching => lambda { Caching },
-
:follow_redirects => lambda { FollowRedirects },
-
:chunked => lambda { Chunked }
-
-
1
Faraday::Middleware.register_middleware \
-
:instrumentation => lambda { Instrumentation }
-
end
-
end
-
-
1
require 'faraday_middleware/backwards_compatibility'
-
# deprecated constants
-
-
1
Faraday::Request.class_eval do
-
1
autoload :OAuth, 'faraday_middleware/request/oauth'
-
1
autoload :OAuth2, 'faraday_middleware/request/oauth2'
-
end
-
-
1
Faraday::Response.class_eval do
-
1
autoload :Mashify, 'faraday_middleware/response/mashify'
-
1
autoload :Rashify, 'faraday_middleware/response/rashify'
-
1
autoload :ParseJson, 'faraday_middleware/response/parse_json'
-
1
autoload :ParseXml, 'faraday_middleware/response/parse_xml'
-
1
autoload :ParseMarshal, 'faraday_middleware/response/parse_marshal'
-
1
autoload :ParseYaml, 'faraday_middleware/response/parse_yaml'
-
end
-
1
require "figaro/error"
-
1
require "figaro/env"
-
1
require "figaro/application"
-
-
1
module Figaro
-
1
extend self
-
-
1
attr_writer :adapter, :application
-
-
1
def env
-
Figaro::ENV
-
end
-
-
1
def adapter
-
1
@adapter ||= Figaro::Application
-
end
-
-
1
def application
-
1
@application ||= adapter.new
-
end
-
-
1
def load
-
1
application.load
-
end
-
-
1
def require_keys(*keys)
-
missing_keys = keys.flatten - ::ENV.keys
-
raise MissingKeys.new(missing_keys) if missing_keys.any?
-
end
-
end
-
-
1
require "figaro/rails"
-
1
require "erb"
-
1
require "yaml"
-
-
1
module Figaro
-
1
class Application
-
1
FIGARO_ENV_PREFIX = "_FIGARO_"
-
-
1
include Enumerable
-
-
1
def initialize(options = {})
-
1
@options = options.inject({}) { |m, (k, v)| m[k.to_sym] = v; m }
-
end
-
-
1
def path
-
4
@options.fetch(:path) { default_path }.to_s
-
end
-
-
1
def path=(path)
-
@options[:path] = path
-
end
-
-
1
def environment
-
2
environment = @options.fetch(:environment) { default_environment }
-
1
environment.nil? ? nil : environment.to_s
-
end
-
-
1
def environment=(environment)
-
@options[:environment] = environment
-
end
-
-
1
def configuration
-
1
global_configuration.merge(environment_configuration)
-
end
-
-
1
def load
-
1
each do |key, value|
-
4
skip?(key) ? key_skipped!(key) : set(key, value)
-
end
-
end
-
-
1
def each(&block)
-
1
configuration.each(&block)
-
end
-
-
1
private
-
-
1
def default_path
-
raise NotImplementedError
-
end
-
-
1
def default_environment
-
nil
-
end
-
-
1
def raw_configuration
-
3
(@parsed ||= Hash.new { |hash, path| hash[path] = parse(path) })[path]
-
end
-
-
1
def parse(path)
-
1
File.exist?(path) && YAML.load(ERB.new(File.read(path)).result) || {}
-
end
-
-
1
def global_configuration
-
5
raw_configuration.reject { |_, value| value.is_a?(Hash) }
-
end
-
-
1
def environment_configuration
-
2
raw_configuration.fetch(environment) { {} }
-
end
-
-
1
def set(key, value)
-
4
non_string_configuration!(key) unless key.is_a?(String)
-
4
non_string_configuration!(value) unless value.is_a?(String) || value.nil?
-
-
4
::ENV[key.to_s] = value.nil? ? nil : value.to_s
-
4
::ENV[FIGARO_ENV_PREFIX + key.to_s] = value.nil? ? nil: value.to_s
-
end
-
-
1
def skip?(key)
-
4
::ENV.key?(key.to_s) && !::ENV.key?(FIGARO_ENV_PREFIX + key.to_s)
-
end
-
-
1
def non_string_configuration!(value)
-
warn "WARNING: Use strings for Figaro configuration. #{value.inspect} was converted to #{value.to_s.inspect}."
-
end
-
-
1
def key_skipped!(key)
-
warn "WARNING: Skipping key #{key.inspect}. Already set in ENV."
-
end
-
end
-
end
-
1
module Figaro
-
1
module ENV
-
1
extend self
-
-
1
def respond_to?(method, *)
-
key, punctuation = extract_key_from_method(method)
-
-
case punctuation
-
when "!" then has_key?(key) || super
-
when "?", nil then true
-
else super
-
end
-
end
-
-
1
private
-
-
1
def method_missing(method, *)
-
key, punctuation = extract_key_from_method(method)
-
-
case punctuation
-
when "!" then send(key) || missing_key!(key)
-
when "?" then !!send(key)
-
when nil then get_value(key)
-
else super
-
end
-
end
-
-
1
def extract_key_from_method(method)
-
method.to_s.downcase.match(/^(.+?)([!?=])?$/).captures
-
end
-
-
1
def has_key?(key)
-
::ENV.any? { |k, _| k.downcase == key }
-
end
-
-
1
def missing_key!(key)
-
raise MissingKey.new(key)
-
end
-
-
1
def get_value(key)
-
_, value = ::ENV.detect { |k, _| k.downcase == key }
-
value
-
end
-
end
-
end
-
1
module Figaro
-
1
class Error < StandardError; end
-
-
1
class RailsNotInitialized < Error; end
-
-
1
class MissingKey < Error
-
1
def initialize(key)
-
super("Missing required configuration key: #{key.inspect}")
-
end
-
end
-
-
1
class MissingKeys < Error
-
1
def initialize(keys)
-
super("Missing required configuration keys: #{keys.inspect}")
-
end
-
end
-
end
-
1
begin
-
1
require "rails"
-
rescue LoadError
-
else
-
1
require "figaro/rails/application"
-
1
require "figaro/rails/railtie"
-
-
1
Figaro.adapter = Figaro::Rails::Application
-
end
-
1
module Figaro
-
1
module Rails
-
1
class Application < Figaro::Application
-
1
private
-
-
1
def default_path
-
2
rails_not_initialized! unless ::Rails.root
-
-
2
::Rails.root.join("config", "application.yml")
-
end
-
-
1
def default_environment
-
1
::Rails.env
-
end
-
-
1
def rails_not_initialized!
-
raise RailsNotInitialized
-
end
-
end
-
end
-
end
-
1
module Figaro
-
1
module Rails
-
1
class Railtie < ::Rails::Railtie
-
1
config.before_configuration do
-
1
Figaro.load
-
end
-
end
-
end
-
end
-
1
module Hashie
-
1
module Extensions
-
1
module PrettyInspect
-
1
def self.included(base)
-
2
base.send :alias_method, :hash_inspect, :inspect
-
2
base.send :alias_method, :inspect, :hashie_inspect
-
end
-
-
1
def hashie_inspect
-
ret = "#<#{self.class}"
-
keys.sort_by { |key| key.to_s }.each do |key|
-
ret << " #{key}=#{self[key].inspect}"
-
end
-
ret << '>'
-
ret
-
end
-
end
-
end
-
end
-
1
module Hashie
-
1
module Extensions
-
1
module StringifyKeys
-
# Convert all keys in the hash to strings.
-
#
-
# @example
-
# test = {:abc => 'def'}
-
# test.stringify_keys!
-
# test # => {'abc' => 'def'}
-
1
def stringify_keys!
-
keys.each do |k|
-
stringify_keys_recursively!(self[k])
-
self[k.to_s] = delete(k)
-
end
-
self
-
end
-
-
# Return a new hash with all keys converted
-
# to strings.
-
1
def stringify_keys
-
dup.stringify_keys!
-
end
-
-
1
protected
-
-
# Stringify all keys recursively within nested
-
# hashes and arrays.
-
1
def stringify_keys_recursively!(object)
-
if self.class === object
-
object.stringify_keys!
-
elsif ::Array === object
-
object.each do |i|
-
stringify_keys_recursively!(i)
-
end
-
object
-
elsif object.respond_to?(:stringify_keys!)
-
object.stringify_keys!
-
elsif ::Hash === object
-
object.keys.each do |k|
-
stringify_keys_recursively!(object[k])
-
object[k.to_s] = object.delete(k)
-
end
-
object
-
else
-
object
-
end
-
end
-
end
-
end
-
end
-
1
require 'hashie/extensions/stringify_keys'
-
1
require 'hashie/extensions/pretty_inspect'
-
-
1
module Hashie
-
# A Hashie Hash is simply a Hash that has convenience
-
# functions baked in such as stringify_keys that may
-
# not be available in all libraries.
-
1
class Hash < ::Hash
-
1
include Hashie::Extensions::PrettyInspect
-
1
include Hashie::Extensions::StringifyKeys
-
-
# Convert this hash into a Mash
-
1
def to_mash
-
::Hashie::Mash.new(self)
-
end
-
-
# Converts a mash back to a hash (with stringified or symbolized keys)
-
1
def to_hash(options = {})
-
out = {}
-
keys.each do |k|
-
assignment_key = if options[:stringify_keys]
-
k.to_s
-
elsif options[:symbolize_keys]
-
k.to_s.to_sym
-
else
-
k
-
end
-
if self[k].is_a?(Array)
-
out[assignment_key] ||= []
-
self[k].each do |array_object|
-
out[assignment_key] << (Hash === array_object ? flexibly_convert_to_hash(array_object, options) : array_object)
-
end
-
else
-
out[assignment_key] = (Hash === self[k] || self[k].respond_to?(:to_hash)) ? flexibly_convert_to_hash(self[k], options) : self[k]
-
end
-
end
-
out
-
end
-
-
# The C generator for the json gem doesn't like mashies
-
1
def to_json(*args)
-
to_hash.to_json(*args)
-
end
-
-
1
private
-
-
1
def flexibly_convert_to_hash(object, options = {})
-
if object.method(:to_hash).arity == 0
-
object.to_hash
-
else
-
object.to_hash(options)
-
end
-
end
-
end
-
end
-
1
require 'hashie/hash'
-
-
1
module Hashie
-
# Mash allows you to create pseudo-objects that have method-like
-
# accessors for hash keys. This is useful for such implementations
-
# as an API-accessing library that wants to fake robust objects
-
# without the overhead of actually doing so. Think of it as OpenStruct
-
# with some additional goodies.
-
#
-
# A Mash will look at the methods you pass it and perform operations
-
# based on the following rules:
-
#
-
# * No punctuation: Returns the value of the hash for that key, or nil if none exists.
-
# * Assignment (<tt>=</tt>): Sets the attribute of the given method name.
-
# * Existence (<tt>?</tt>): Returns true or false depending on whether that key has been set.
-
# * Bang (<tt>!</tt>): Forces the existence of this key, used for deep Mashes. Think of it as "touch" for mashes.
-
# * Under Bang (<tt>_</tt>): Like Bang, but returns a new Mash rather than creating a key. Used to test existance in deep Mashes.
-
#
-
# == Basic Example
-
#
-
# mash = Mash.new
-
# mash.name? # => false
-
# mash.name = "Bob"
-
# mash.name # => "Bob"
-
# mash.name? # => true
-
#
-
# == Hash Conversion Example
-
#
-
# hash = {:a => {:b => 23, :d => {:e => "abc"}}, :f => [{:g => 44, :h => 29}, 12]}
-
# mash = Mash.new(hash)
-
# mash.a.b # => 23
-
# mash.a.d.e # => "abc"
-
# mash.f.first.g # => 44
-
# mash.f.last # => 12
-
#
-
# == Bang Example
-
#
-
# mash = Mash.new
-
# mash.author # => nil
-
# mash.author! # => <Mash>
-
#
-
# mash = Mash.new
-
# mash.author!.name = "Michael Bleigh"
-
# mash.author # => <Mash name="Michael Bleigh">
-
#
-
# == Under Bang Example
-
#
-
# mash = Mash.new
-
# mash.author # => nil
-
# mash.author_ # => <Mash>
-
# mash.author_.name # => nil
-
#
-
# mash = Mash.new
-
# mash.author_.name = "Michael Bleigh" (assigned to temp object)
-
# mash.author # => <Mash>
-
#
-
1
class Mash < Hash
-
1
include Hashie::Extensions::PrettyInspect
-
-
1
ALLOWED_SUFFIXES = %w(? ! = _)
-
-
1
def self.load(path, options = {})
-
@_mashes ||= new do |h, file_path|
-
fail ArgumentError, "The following file doesn't exist: #{file_path}" unless File.file?(file_path)
-
-
parser = options.fetch(:parser) { Hashie::Extensions::Parsers::YamlErbParser }
-
h[file_path] = new(parser.perform(file_path)).freeze
-
end
-
@_mashes[path]
-
end
-
-
1
def to_module(mash_method_name = :settings)
-
mash = self
-
Module.new do |m|
-
m.send :define_method, mash_method_name.to_sym do
-
mash
-
end
-
end
-
end
-
-
1
alias_method :to_s, :inspect
-
-
# If you pass in an existing hash, it will
-
# convert it to a Mash including recursively
-
# descending into arrays and hashes, converting
-
# them as well.
-
1
def initialize(source_hash = nil, default = nil, &blk)
-
23
deep_update(source_hash) if source_hash
-
23
default ? super(default) : super(&blk)
-
end
-
-
2
class << self; alias_method :[], :new; end
-
-
1
def id #:nodoc:
-
self['id']
-
end
-
-
1
def type #:nodoc:
-
self['type']
-
end
-
-
1
alias_method :regular_reader, :[]
-
1
alias_method :regular_writer, :[]=
-
-
# Retrieves an attribute set in the Mash. Will convert
-
# any key passed in to a string before retrieving.
-
1
def custom_reader(key)
-
value = regular_reader(convert_key(key))
-
yield value if block_given?
-
value
-
end
-
-
# Sets an attribute in the Mash. Key will be converted to
-
# a string before it is set, and Hashes will be converted
-
# into Mashes for nesting purposes.
-
1
def custom_writer(key, value, convert = true) #:nodoc:
-
54
regular_writer(convert_key(key), convert ? convert_value(value) : value)
-
end
-
-
1
alias_method :[], :custom_reader
-
1
alias_method :[]=, :custom_writer
-
-
# This is the bang method reader, it will return a new Mash
-
# if there isn't a value already assigned to the key requested.
-
1
def initializing_reader(key)
-
ck = convert_key(key)
-
regular_writer(ck, self.class.new) unless key?(ck)
-
regular_reader(ck)
-
end
-
-
# This is the under bang method reader, it will return a temporary new Mash
-
# if there isn't a value already assigned to the key requested.
-
1
def underbang_reader(key)
-
ck = convert_key(key)
-
if key?(ck)
-
regular_reader(ck)
-
else
-
self.class.new
-
end
-
end
-
-
1
def fetch(key, *args)
-
super(convert_key(key), *args)
-
end
-
-
1
def delete(key)
-
super(convert_key(key))
-
end
-
-
1
def values_at(*keys)
-
super(*keys.map { |key| convert_key(key) })
-
end
-
-
1
alias_method :regular_dup, :dup
-
# Duplicates the current mash as a new mash.
-
1
def dup
-
7
self.class.new(self, default)
-
end
-
-
1
def key?(key)
-
super(convert_key(key))
-
end
-
1
alias_method :has_key?, :key?
-
1
alias_method :include?, :key?
-
1
alias_method :member?, :key?
-
-
# Performs a deep_update on a duplicate of the
-
# current mash.
-
1
def deep_merge(other_hash, &blk)
-
dup.deep_update(other_hash, &blk)
-
end
-
1
alias_method :merge, :deep_merge
-
-
# Recursively merges this mash with the passed
-
# in hash, merging each hash in the hierarchy.
-
1
def deep_update(other_hash, &blk)
-
23
other_hash.each_pair do |k, v|
-
31
key = convert_key(k)
-
31
if regular_reader(key).is_a?(Mash) && v.is_a?(::Hash)
-
custom_reader(key).deep_update(v, &blk)
-
else
-
31
value = convert_value(v, true)
-
31
value = convert_value(blk.call(key, self[k], value), true) if blk
-
31
custom_writer(key, value, false)
-
end
-
end
-
23
self
-
end
-
1
alias_method :deep_merge!, :deep_update
-
1
alias_method :update, :deep_update
-
1
alias_method :merge!, :update
-
-
# Assigns a value to a key
-
1
def assign_property(name, value)
-
self[name] = value
-
end
-
-
# Performs a shallow_update on a duplicate of the current mash
-
1
def shallow_merge(other_hash)
-
dup.shallow_update(other_hash)
-
end
-
-
# Merges (non-recursively) the hash from the argument,
-
# changing the receiving hash
-
1
def shallow_update(other_hash)
-
other_hash.each_pair do |k, v|
-
regular_writer(convert_key(k), convert_value(v, true))
-
end
-
self
-
end
-
-
1
def replace(other_hash)
-
(keys - other_hash.keys).each { |key| delete(key) }
-
other_hash.each { |key, value| self[key] = value }
-
self
-
end
-
-
1
def respond_to_missing?(method_name, *args)
-
return true if key?(method_name)
-
_, suffix = method_suffix(method_name)
-
case suffix
-
when '=', '?', '!', '_'
-
return true
-
else
-
super
-
end
-
end
-
-
1
def prefix_method?(method_name)
-
method_name = method_name.to_s
-
method_name.end_with?(*ALLOWED_SUFFIXES) && key?(method_name.chop)
-
end
-
-
1
def method_missing(method_name, *args, &blk)
-
return self.[](method_name, &blk) if key?(method_name)
-
name, suffix = method_suffix(method_name)
-
case suffix
-
when '='
-
assign_property(name, args.first)
-
when '?'
-
!!self[name]
-
when '!'
-
initializing_reader(name)
-
when '_'
-
underbang_reader(name)
-
else
-
default(method_name)
-
end
-
end
-
-
1
protected
-
-
1
def method_suffix(method_name)
-
suffixes_regex = ALLOWED_SUFFIXES.join
-
match = method_name.to_s.match(/(.*?)([#{suffixes_regex}]?)$/)
-
[match[1], match[2]]
-
end
-
-
1
def convert_key(key) #:nodoc:
-
85
key.to_s
-
end
-
-
1
def convert_value(val, duping = false) #:nodoc:
-
56
case val
-
when self.class
-
7
val.dup
-
when Hash
-
duping ? val.dup : val
-
when ::Hash
-
10
val = val.dup if duping
-
10
self.class.new(val)
-
when Array
-
6
val.map { |e| convert_value(e) }
-
else
-
35
val
-
end
-
end
-
end
-
end
-
1
module Hike
-
1
VERSION = "1.2.0"
-
-
1
autoload :Extensions, "hike/extensions"
-
1
autoload :Index, "hike/index"
-
1
autoload :NormalizedArray, "hike/normalized_array"
-
1
autoload :Paths, "hike/paths"
-
1
autoload :Trail, "hike/trail"
-
end
-
1
require 'hike/normalized_array'
-
-
1
module Hike
-
# `Extensions` is an internal collection for tracking extension names.
-
1
class Extensions < NormalizedArray
-
# Extensions added to this array are normalized with a leading
-
# `.`.
-
#
-
# extensions << "js"
-
# extensions << ".css"
-
#
-
# extensions
-
# # => [".js", ".css"]
-
#
-
1
def normalize_element(extension)
-
11
if extension[/^\./]
-
11
extension
-
else
-
".#{extension}"
-
end
-
end
-
end
-
end
-
1
require 'pathname'
-
-
1
module Hike
-
# `Index` is an internal cached variant of `Trail`. It assumes the
-
# file system does not change between `find` calls. All `stat` and
-
# `entries` calls are cached for the lifetime of the `Index` object.
-
1
class Index
-
# `Index#paths` is an immutable `Paths` collection.
-
1
attr_reader :paths
-
-
# `Index#extensions` is an immutable `Extensions` collection.
-
1
attr_reader :extensions
-
-
# `Index#aliases` is an immutable `Hash` mapping an extension to
-
# an `Array` of aliases.
-
1
attr_reader :aliases
-
-
# `Index.new` is an internal method. Instead of constructing it
-
# directly, create a `Trail` and call `Trail#index`.
-
1
def initialize(root, paths, extensions, aliases)
-
1
@root = root
-
-
# Freeze is used here so an error is throw if a mutator method
-
# is called on the array. Mutating `@paths`, `@extensions`, or
-
# `@aliases` would have unpredictable results.
-
1
@paths = paths.dup.freeze
-
1
@extensions = extensions.dup.freeze
-
1
@aliases = aliases.inject({}) { |h, (k, a)|
-
5
h[k] = a.dup.freeze; h
-
}.freeze
-
14
@pathnames = paths.map { |path| Pathname.new(path) }
-
-
1
@stats = {}
-
1
@entries = {}
-
1
@patterns = {}
-
end
-
-
# `Index#root` returns root path as a `String`. This attribute is immutable.
-
1
def root
-
@root.to_s
-
end
-
-
# `Index#index` returns `self` to be compatable with the `Trail` interface.
-
1
def index
-
self
-
end
-
-
# The real implementation of `find`. `Trail#find` generates a one
-
# time index and delegates here.
-
#
-
# See `Trail#find` for usage.
-
1
def find(*logical_paths, &block)
-
if block_given?
-
options = extract_options!(logical_paths)
-
base_path = Pathname.new(options[:base_path] || @root)
-
-
logical_paths.each do |logical_path|
-
logical_path = Pathname.new(logical_path.sub(/^\//, ''))
-
-
if relative?(logical_path)
-
find_in_base_path(logical_path, base_path, &block)
-
else
-
find_in_paths(logical_path, &block)
-
end
-
end
-
-
nil
-
else
-
find(*logical_paths) do |path|
-
return path
-
end
-
end
-
end
-
-
# A cached version of `Dir.entries` that filters out `.` files and
-
# `~` swap files. Returns an empty `Array` if the directory does
-
# not exist.
-
1
def entries(path)
-
@entries[path.to_s] ||= begin
-
pathname = Pathname.new(path)
-
if pathname.directory?
-
pathname.entries.reject { |entry| entry.to_s =~ /^\.|~$|^\#.*\#$/ }.sort
-
else
-
[]
-
end
-
end
-
end
-
-
# A cached version of `File.stat`. Returns nil if the file does
-
# not exist.
-
1
def stat(path)
-
key = path.to_s
-
if @stats.key?(key)
-
@stats[key]
-
elsif File.exist?(path)
-
@stats[key] = File.stat(path)
-
else
-
@stats[key] = nil
-
end
-
end
-
-
1
protected
-
1
def extract_options!(arguments)
-
arguments.last.is_a?(Hash) ? arguments.pop.dup : {}
-
end
-
-
1
def relative?(logical_path)
-
logical_path.to_s =~ /^\.\.?\//
-
end
-
-
# Finds logical path across all `paths`
-
1
def find_in_paths(logical_path, &block)
-
dirname, basename = logical_path.split
-
@pathnames.each do |base_path|
-
match(base_path.join(dirname), basename, &block)
-
end
-
end
-
-
# Finds relative logical path, `../test/test_trail`. Requires a
-
# `base_path` for reference.
-
1
def find_in_base_path(logical_path, base_path, &block)
-
candidate = base_path.join(logical_path)
-
dirname, basename = candidate.split
-
match(dirname, basename, &block) if paths_contain?(dirname)
-
end
-
-
# Checks if the path is actually on the file system and performs
-
# any syscalls if necessary.
-
1
def match(dirname, basename)
-
# Potential `entries` syscall
-
matches = entries(dirname)
-
-
pattern = pattern_for(basename)
-
matches = matches.select { |m| m.to_s =~ pattern }
-
-
sort_matches(matches, basename).each do |path|
-
pathname = dirname.join(path)
-
-
# Potential `stat` syscall
-
stat = stat(pathname)
-
-
# Exclude directories
-
if stat && stat.file?
-
yield pathname.to_s
-
end
-
end
-
end
-
-
# Returns true if `dirname` is a subdirectory of any of the `paths`
-
1
def paths_contain?(dirname)
-
paths.any? { |path| dirname.to_s[0, path.length] == path }
-
end
-
-
# Cache results of `build_pattern_for`
-
1
def pattern_for(basename)
-
@patterns[basename] ||= build_pattern_for(basename)
-
end
-
-
# Returns a `Regexp` that matches the allowed extensions.
-
#
-
# pattern_for("index.html") #=> /^index(.html|.htm)(.builder|.erb)*$/
-
1
def build_pattern_for(basename)
-
extname = basename.extname
-
aliases = find_aliases_for(extname)
-
-
if aliases.any?
-
basename = basename.basename(extname)
-
aliases = [extname] + aliases
-
aliases_pattern = aliases.map { |e| Regexp.escape(e) }.join("|")
-
basename_re = Regexp.escape(basename.to_s) + "(?:#{aliases_pattern})"
-
else
-
basename_re = Regexp.escape(basename.to_s)
-
end
-
-
extension_pattern = extensions.map { |e| Regexp.escape(e) }.join("|")
-
/^#{basename_re}(?:#{extension_pattern})*$/
-
end
-
-
# Sorts candidate matches by their extension
-
# priority. Extensions in the front of the `extensions` carry
-
# more weight.
-
1
def sort_matches(matches, basename)
-
aliases = find_aliases_for(basename.extname)
-
-
matches.sort_by do |match|
-
extnames = match.sub(basename.to_s, '').to_s.scan(/\.[^.]+/)
-
extnames.inject(0) do |sum, ext|
-
if i = extensions.index(ext)
-
sum + i + 1
-
elsif i = aliases.index(ext)
-
sum + i + 11
-
else
-
sum
-
end
-
end
-
end
-
end
-
-
1
def find_aliases_for(extension)
-
@aliases.inject([]) do |aliases, (key, value)|
-
aliases.push(key) if value == extension
-
aliases
-
end
-
end
-
end
-
end
-
1
module Hike
-
# `NormalizedArray` is an internal abstract wrapper class that calls
-
# a callback `normalize_element` anytime an element is added to the
-
# Array.
-
#
-
# `Extensions` and `Paths` are subclasses of `NormalizedArray`.
-
1
class NormalizedArray < Array
-
1
def initialize
-
4
super()
-
end
-
-
1
def []=(*args)
-
value = args.pop
-
-
if value.respond_to?(:to_ary)
-
value = normalize_elements(value)
-
else
-
value = normalize_element(value)
-
end
-
-
super(*args.concat([value]))
-
end
-
-
1
def <<(element)
-
super normalize_element(element)
-
end
-
-
1
def collect!
-
super do |element|
-
result = yield element
-
normalize_element(result)
-
end
-
end
-
-
1
alias_method :map!, :collect!
-
-
1
def insert(index, *elements)
-
super index, *normalize_elements(elements)
-
end
-
-
1
def push(*elements)
-
24
super(*normalize_elements(elements))
-
end
-
-
1
def replace(elements)
-
super normalize_elements(elements)
-
end
-
-
1
def unshift(*elements)
-
super(*normalize_elements(elements))
-
end
-
-
1
def normalize_elements(elements)
-
24
elements.map do |element|
-
24
normalize_element(element)
-
end
-
end
-
end
-
end
-
1
require 'pathname'
-
1
require 'hike/normalized_array'
-
-
1
module Hike
-
# `Paths` is an internal collection for tracking path strings.
-
1
class Paths < NormalizedArray
-
1
def initialize(root = ".")
-
2
@root = Pathname.new(root)
-
2
super()
-
end
-
-
# Relative paths added to this array are expanded relative to `@root`.
-
#
-
# paths = Paths.new("/usr/local")
-
# paths << "tmp"
-
# paths << "/tmp"
-
#
-
# paths
-
# # => ["/usr/local/tmp", "/tmp"]
-
#
-
1
def normalize_element(path)
-
13
path = Pathname.new(path)
-
13
path = @root.join(path) if path.relative?
-
13
path.expand_path.to_s
-
end
-
end
-
end
-
1
require 'pathname'
-
1
require 'hike/extensions'
-
1
require 'hike/index'
-
1
require 'hike/paths'
-
-
1
module Hike
-
# `Trail` is the public container class for holding paths and extensions.
-
1
class Trail
-
# `Trail#paths` is a mutable `Paths` collection.
-
#
-
# trail = Hike::Trail.new
-
# trail.paths.push "~/Projects/hike/lib", "~/Projects/hike/test"
-
#
-
# The order of the paths is significant. Paths in the beginning of
-
# the collection will be checked first. In the example above,
-
# `~/Projects/hike/lib/hike.rb` would shadow the existent of
-
# `~/Projects/hike/test/hike.rb`.
-
1
attr_reader :paths
-
-
# `Trail#extensions` is a mutable `Extensions` collection.
-
#
-
# trail = Hike::Trail.new
-
# trail.paths.push "~/Projects/hike/lib"
-
# trail.extensions.push ".rb"
-
#
-
# Extensions allow you to find files by just their name omitting
-
# their extension. Is similar to Ruby's require mechanism that
-
# allows you to require files with specifiying `foo.rb`.
-
1
attr_reader :extensions
-
-
# `Index#aliases` is a mutable `Hash` mapping an extension to
-
# an `Array` of aliases.
-
#
-
# trail = Hike::Trail.new
-
# trail.paths.push "~/Projects/hike/site"
-
# trail.aliases['.htm'] = 'html'
-
# trail.aliases['.xhtml'] = 'html'
-
# trail.aliases['.php'] = 'html'
-
#
-
# Aliases provide a fallback when the primary extension is not
-
# matched. In the example above, a lookup for "foo.html" will
-
# check for the existence of "foo.htm", "foo.xhtml", or "foo.php".
-
1
attr_reader :aliases
-
-
# A Trail accepts an optional root path that defaults to your
-
# current working directory. Any relative paths added to
-
# `Trail#paths` will expanded relative to the root.
-
1
def initialize(root = ".")
-
2
@root = Pathname.new(root).expand_path
-
2
@paths = Paths.new(@root)
-
2
@extensions = Extensions.new
-
2
@aliases = Hash.new { |h, k| h[k] = Extensions.new }
-
end
-
-
# `Trail#root` returns root path as a `String`. This attribute is immutable.
-
1
def root
-
1
@root.to_s
-
end
-
-
# Prepend `path` to `Paths` collection
-
1
def prepend_paths(*paths)
-
self.paths.unshift(*paths)
-
end
-
1
alias_method :prepend_path, :prepend_paths
-
-
# Append `path` to `Paths` collection
-
1
def append_paths(*paths)
-
13
self.paths.push(*paths)
-
end
-
1
alias_method :append_path, :append_paths
-
-
# Remove `path` from `Paths` collection
-
1
def remove_path(path)
-
self.paths.delete(path)
-
end
-
-
# Prepend `extension` to `Extensions` collection
-
1
def prepend_extensions(*extensions)
-
self.extensions.unshift(*extensions)
-
end
-
1
alias_method :prepend_extension, :prepend_extensions
-
-
# Append `extension` to `Extensions` collection
-
1
def append_extensions(*extensions)
-
11
self.extensions.push(*extensions)
-
end
-
1
alias_method :append_extension, :append_extensions
-
-
# Remove `extension` from `Extensions` collection
-
1
def remove_extension(extension)
-
self.extensions.delete(extension)
-
end
-
-
# Alias `new_extension` to `old_extension`
-
1
def alias_extension(new_extension, old_extension)
-
5
aliases[normalize_extension(new_extension)] = normalize_extension(old_extension)
-
end
-
-
# Remove the alias for `extension`
-
1
def unalias_extension(extension)
-
aliases.delete(normalize_extension(extension))
-
end
-
-
# `Trail#find` returns a the expand path for a logical path in the
-
# path collection.
-
#
-
# trail = Hike::Trail.new "~/Projects/hike"
-
# trail.extensions.push ".rb"
-
# trail.paths.push "lib", "test"
-
#
-
# trail.find "hike/trail"
-
# # => "~/Projects/hike/lib/hike/trail.rb"
-
#
-
# trail.find "test_trail"
-
# # => "~/Projects/hike/test/test_trail.rb"
-
#
-
# `find` accepts multiple fallback logical paths that returns the
-
# first match.
-
#
-
# trail.find "hike", "hike/index"
-
#
-
# is equivalent to
-
#
-
# trail.find("hike") || trail.find("hike/index")
-
#
-
# Though `find` always returns the first match, it is possible
-
# to iterate over all shadowed matches and fallbacks by supplying
-
# a block.
-
#
-
# trail.find("hike", "hike/index") { |path| warn path }
-
#
-
# This allows you to filter your matches by any condition.
-
#
-
# trail.find("application") do |path|
-
# return path if mime_type_for(path) == "text/css"
-
# end
-
#
-
1
def find(*args, &block)
-
index.find(*args, &block)
-
end
-
-
# `Trail#index` returns an `Index` object that has the same
-
# interface as `Trail`. An `Index` is a cached `Trail` object that
-
# does not update when the file system changes. If you are
-
# confident that you are not making changes the paths you are
-
# searching, `index` will avoid excess system calls.
-
#
-
# index = trail.index
-
# index.find "hike/trail"
-
# index.find "test_trail"
-
#
-
1
def index
-
1
Index.new(root, paths, extensions, aliases)
-
end
-
-
# `Trail#entries` is equivalent to `Dir#entries`. It is not
-
# recommend to use this method for general purposes. It exists for
-
# parity with `Index#entries`.
-
1
def entries(path)
-
pathname = Pathname.new(path)
-
if pathname.directory?
-
pathname.entries.reject { |entry| entry.to_s =~ /^\.|~$|^\#.*\#$/ }.sort
-
else
-
[]
-
end
-
end
-
-
# `Trail#stat` is equivalent to `File#stat`. It is not
-
# recommend to use this method for general purposes. It exists for
-
# parity with `Index#stat`.
-
1
def stat(path)
-
if File.exist?(path)
-
File.stat(path.to_s)
-
else
-
nil
-
end
-
end
-
-
1
private
-
1
def normalize_extension(extension)
-
10
if extension[/^\./]
-
10
extension
-
else
-
".#{extension}"
-
end
-
end
-
end
-
end
-
1
require 'pathname'
-
1
require 'net/http'
-
1
require 'net/https'
-
1
require 'uri'
-
1
require 'zlib'
-
1
require 'multi_xml'
-
1
require 'json'
-
1
require 'csv'
-
-
1
require 'httparty/module_inheritable_attributes'
-
1
require 'httparty/cookie_hash'
-
1
require 'httparty/net_digest_auth'
-
1
require 'httparty/version'
-
1
require 'httparty/connection_adapter'
-
1
require 'httparty/logger/logger'
-
-
# @see HTTParty::ClassMethods
-
1
module HTTParty
-
1
module AllowedFormatsDeprecation
-
1
def const_missing(const)
-
if const.to_s =~ /AllowedFormats$/
-
Kernel.warn("Deprecated: Use HTTParty::Parser::SupportedFormats")
-
HTTParty::Parser::SupportedFormats
-
else
-
super
-
end
-
end
-
end
-
-
1
extend AllowedFormatsDeprecation
-
-
1
def self.included(base)
-
1
base.extend ClassMethods
-
1
base.send :include, HTTParty::ModuleInheritableAttributes
-
1
base.send(:mattr_inheritable, :default_options)
-
1
base.send(:mattr_inheritable, :default_cookies)
-
1
base.instance_variable_set("@default_options", {})
-
1
base.instance_variable_set("@default_cookies", CookieHash.new)
-
end
-
-
# == Common Request Options
-
# Request methods (get, post, patch, put, delete, head, options) all take a common set of options. These are:
-
#
-
# [:+body+:] Body of the request. If passed a Hash, will try to normalize it first, by default passing it to ActiveSupport::to_params. Any other kind of object will get used as-is.
-
# [:+http_proxyaddr+:] Address of proxy server to use.
-
# [:+http_proxyport+:] Port of proxy server to use.
-
# [:+http_proxyuser+:] User for proxy server authentication.
-
# [:+http_proxypass+:] Password for proxy server authentication.
-
# [:+limit+:] Maximum number of redirects to follow. Takes precedences over :+no_follow+.
-
# [:+query+:] Query string, or a Hash representing it. Normalized according to the same rules as :+body+. If you specify this on a POST, you must use a Hash. See also HTTParty::ClassMethods.default_params.
-
# [:+timeout+:] Timeout for opening connection and reading data.
-
# [:+local_host:] Local address to bind to before connecting.
-
# [:+local_port:] Local port to bind to before connecting.
-
#
-
# There are also another set of options with names corresponding to various class methods. The methods in question are those that let you set a class-wide default, and the options override the defaults on a request-by-request basis. Those options are:
-
# * :+base_uri+: see HTTParty::ClassMethods.base_uri.
-
# * :+basic_auth+: see HTTParty::ClassMethods.basic_auth. Only one of :+basic_auth+ and :+digest_auth+ can be used at a time; if you try using both, you'll get an ArgumentError.
-
# * :+debug_output+: see HTTParty::ClassMethods.debug_output.
-
# * :+digest_auth+: see HTTParty::ClassMethods.digest_auth. Only one of :+basic_auth+ and :+digest_auth+ can be used at a time; if you try using both, you'll get an ArgumentError.
-
# * :+format+: see HTTParty::ClassMethods.format.
-
# * :+headers+: see HTTParty::ClassMethods.headers. Must be a Hash.
-
# * :+maintain_method_across_redirects+: see HTTParty::ClassMethods.maintain_method_across_redirects.
-
# * :+no_follow+: see HTTParty::ClassMethods.no_follow.
-
# * :+parser+: see HTTParty::ClassMethods.parser.
-
# * :+connection_adapter+: see HTTParty::ClassMethods.connection_adapter.
-
# * :+pem+: see HTTParty::ClassMethods.pem.
-
# * :+query_string_normalizer+: see HTTParty::ClassMethods.query_string_normalizer
-
# * :+ssl_ca_file+: see HTTParty::ClassMethods.ssl_ca_file.
-
# * :+ssl_ca_path+: see HTTParty::ClassMethods.ssl_ca_path.
-
-
1
module ClassMethods
-
-
1
extend AllowedFormatsDeprecation
-
-
# Turns on logging
-
#
-
# class Foo
-
# include HTTParty
-
# logger Logger.new('http_logger'), :info, :apache
-
# end
-
1
def logger(logger, level=:info, format=:apache)
-
default_options[:logger] = logger
-
default_options[:log_level] = level
-
default_options[:log_format] = format
-
end
-
-
# Allows setting http proxy information to be used
-
#
-
# class Foo
-
# include HTTParty
-
# http_proxy 'http://foo.com', 80, 'user', 'pass'
-
# end
-
1
def http_proxy(addr=nil, port=nil, user=nil, pass=nil)
-
default_options[:http_proxyaddr] = addr
-
default_options[:http_proxyport] = port
-
default_options[:http_proxyuser] = user
-
default_options[:http_proxypass] = pass
-
end
-
-
# Allows setting a base uri to be used for each request.
-
# Will normalize uri to include http, etc.
-
#
-
# class Foo
-
# include HTTParty
-
# base_uri 'twitter.com'
-
# end
-
1
def base_uri(uri=nil)
-
return default_options[:base_uri] unless uri
-
default_options[:base_uri] = HTTParty.normalize_base_uri(uri)
-
end
-
-
# Allows setting basic authentication username and password.
-
#
-
# class Foo
-
# include HTTParty
-
# basic_auth 'username', 'password'
-
# end
-
1
def basic_auth(u, p)
-
default_options[:basic_auth] = {:username => u, :password => p}
-
end
-
-
# Allows setting digest authentication username and password.
-
#
-
# class Foo
-
# include HTTParty
-
# digest_auth 'username', 'password'
-
# end
-
1
def digest_auth(u, p)
-
default_options[:digest_auth] = {:username => u, :password => p}
-
end
-
-
# Do not send rails style query strings.
-
# Specically, don't use bracket notation when sending an array
-
#
-
# For a query:
-
# get '/', :query => {:selected_ids => [1,2,3]}
-
#
-
# The default query string looks like this:
-
# /?selected_ids[]=1&selected_ids[]=2&selected_ids[]=3
-
#
-
# Call `disable_rails_query_string_format` to transform the query string
-
# into:
-
# /?selected_ids=1&selected_ids=2&selected_ids=3
-
#
-
# @example
-
# class Foo
-
# include HTTParty
-
# disable_rails_query_string_format
-
# end
-
1
def disable_rails_query_string_format
-
query_string_normalizer Request::NON_RAILS_QUERY_STRING_NORMALIZER
-
end
-
-
# Allows setting default parameters to be appended to each request.
-
# Great for api keys and such.
-
#
-
# class Foo
-
# include HTTParty
-
# default_params :api_key => 'secret', :another => 'foo'
-
# end
-
1
def default_params(h={})
-
raise ArgumentError, 'Default params must be a hash' unless h.is_a?(Hash)
-
default_options[:default_params] ||= {}
-
default_options[:default_params].merge!(h)
-
end
-
-
# Allows setting a default timeout for all HTTP calls
-
# Timeout is specified in seconds.
-
#
-
# class Foo
-
# include HTTParty
-
# default_timeout 10
-
# end
-
1
def default_timeout(t)
-
raise ArgumentError, 'Timeout must be an integer or float' unless t && (t.is_a?(Integer) || t.is_a?(Float))
-
default_options[:timeout] = t
-
end
-
-
# Allows setting a default open_timeout for all HTTP calls in seconds
-
#
-
# class Foo
-
# include HTTParty
-
# open_timeout 10
-
# end
-
1
def open_timeout(t)
-
raise ArgumentError, 'open_timeout must be an integer or float' unless t && (t.is_a?(Integer) || t.is_a?(Float))
-
default_options[:open_timeout] = t
-
end
-
-
# Allows setting a default read_timeout for all HTTP calls in seconds
-
#
-
# class Foo
-
# include HTTParty
-
# read_timeout 10
-
# end
-
1
def read_timeout(t)
-
raise ArgumentError, 'read_timeout must be an integer or float' unless t && (t.is_a?(Integer) || t.is_a?(Float))
-
default_options[:read_timeout] = t
-
end
-
-
# Set an output stream for debugging, defaults to $stderr.
-
# The output stream is passed on to Net::HTTP#set_debug_output.
-
#
-
# class Foo
-
# include HTTParty
-
# debug_output $stderr
-
# end
-
1
def debug_output(stream = $stderr)
-
default_options[:debug_output] = stream
-
end
-
-
# Allows setting HTTP headers to be used for each request.
-
#
-
# class Foo
-
# include HTTParty
-
# headers 'Accept' => 'text/html'
-
# end
-
1
def headers(h={})
-
raise ArgumentError, 'Headers must be a hash' unless h.is_a?(Hash)
-
default_options[:headers] ||= {}
-
default_options[:headers].merge!(h)
-
end
-
-
1
def cookies(h={})
-
raise ArgumentError, 'Cookies must be a hash' unless h.is_a?(Hash)
-
default_cookies.add_cookies(h)
-
end
-
-
# Proceed to the location header when an HTTP response dictates a redirect.
-
# Redirects are always followed by default.
-
#
-
# @example
-
# class Foo
-
# include HTTParty
-
# base_uri 'http://google.com'
-
# follow_redirects true
-
# end
-
1
def follow_redirects(value = true)
-
default_options[:follow_redirects] = value
-
end
-
-
# Allows setting the format with which to parse.
-
# Must be one of the allowed formats ie: json, xml
-
#
-
# class Foo
-
# include HTTParty
-
# format :json
-
# end
-
1
def format(f = nil)
-
if f.nil?
-
default_options[:format]
-
else
-
parser(Parser) if parser.nil?
-
default_options[:format] = f
-
validate_format
-
end
-
end
-
-
# Declare whether or not to follow redirects. When true, an
-
# {HTTParty::RedirectionTooDeep} error will raise upon encountering a
-
# redirect. You can then gain access to the response object via
-
# HTTParty::RedirectionTooDeep#response.
-
#
-
# @see HTTParty::ResponseError#response
-
#
-
# @example
-
# class Foo
-
# include HTTParty
-
# base_uri 'http://google.com'
-
# no_follow true
-
# end
-
#
-
# begin
-
# Foo.get('/')
-
# rescue HTTParty::RedirectionTooDeep => e
-
# puts e.response.body
-
# end
-
1
def no_follow(value = false)
-
default_options[:no_follow] = value
-
end
-
-
# Declare that you wish to maintain the chosen HTTP method across redirects.
-
# The default behavior is to follow redirects via the GET method.
-
# If you wish to maintain the original method, you can set this option to true.
-
#
-
# @example
-
# class Foo
-
# include HTTParty
-
# base_uri 'http://google.com'
-
# maintain_method_across_redirects true
-
# end
-
-
1
def maintain_method_across_redirects(value = true)
-
default_options[:maintain_method_across_redirects] = value
-
end
-
-
# Allows setting a PEM file to be used
-
#
-
# class Foo
-
# include HTTParty
-
# pem File.read('/home/user/my.pem'), "optional password"
-
# end
-
1
def pem(pem_contents, password=nil)
-
default_options[:pem] = pem_contents
-
default_options[:pem_password] = password
-
end
-
-
# Allows setting a PKCS12 file to be used
-
#
-
# class Foo
-
# include HTTParty
-
# pkcs12 File.read('/home/user/my.p12'), "password"
-
# end
-
1
def pkcs12(p12_contents, password)
-
default_options[:p12] = p12_contents
-
default_options[:p12_password] = password
-
end
-
-
# Override the way query strings are normalized.
-
# Helpful for overriding the default rails normalization of Array queries.
-
#
-
# For a query:
-
# get '/', :query => {:selected_ids => [1,2,3]}
-
#
-
# The default query string normalizer returns:
-
# /?selected_ids[]=1&selected_ids[]=2&selected_ids[]=3
-
#
-
# Let's change it to this:
-
# /?selected_ids=1&selected_ids=2&selected_ids=3
-
#
-
# Pass a Proc to the query normalizer which accepts the yielded query.
-
#
-
# @example Modifying Array query strings
-
# class ServiceWrapper
-
# include HTTParty
-
#
-
# query_string_normalizer proc { |query|
-
# query.map do |key, value|
-
# value.map {|v| "#{key}=#{v}"}
-
# end.join('&')
-
# }
-
# end
-
#
-
# @param [Proc] normalizer custom query string normalizer.
-
# @yield [Hash, String] query string
-
# @yieldreturn [Array] an array that will later be joined with '&'
-
1
def query_string_normalizer(normalizer)
-
default_options[:query_string_normalizer] = normalizer
-
end
-
-
# Allows setting of SSL version to use. This only works in Ruby 1.9+.
-
# You can get a list of valid versions from OpenSSL::SSL::SSLContext::METHODS.
-
#
-
# class Foo
-
# include HTTParty
-
# ssl_version :SSLv3
-
# end
-
1
def ssl_version(version)
-
default_options[:ssl_version] = version
-
end
-
-
# Allows setting of SSL ciphers to use. This only works in Ruby 1.9+.
-
# You can get a list of valid specific ciphers from OpenSSL::Cipher.ciphers.
-
# You also can specify a cipher suite here, listed here at openssl.org:
-
# http://www.openssl.org/docs/apps/ciphers.html#CIPHER_SUITE_NAMES
-
#
-
# class Foo
-
# include HTTParty
-
# ciphers "RC4-SHA"
-
# end
-
1
def ciphers(cipher_names)
-
default_options[:ciphers] = cipher_names
-
end
-
-
# Allows setting an OpenSSL certificate authority file. The file
-
# should contain one or more certificates in PEM format.
-
#
-
# Setting this option enables certificate verification. All
-
# certificates along a chain must be available in ssl_ca_file or
-
# ssl_ca_path for verification to succeed.
-
#
-
#
-
# class Foo
-
# include HTTParty
-
# ssl_ca_file '/etc/ssl/certs/ca-certificates.crt'
-
# end
-
1
def ssl_ca_file(path)
-
default_options[:ssl_ca_file] = path
-
end
-
-
# Allows setting an OpenSSL certificate authority path (directory).
-
#
-
# Setting this option enables certificate verification. All
-
# certificates along a chain must be available in ssl_ca_file or
-
# ssl_ca_path for verification to succeed.
-
#
-
# class Foo
-
# include HTTParty
-
# ssl_ca_path '/etc/ssl/certs/'
-
# end
-
1
def ssl_ca_path(path)
-
default_options[:ssl_ca_path] = path
-
end
-
-
# Allows setting a custom parser for the response.
-
#
-
# class Foo
-
# include HTTParty
-
# parser Proc.new {|data| ...}
-
# end
-
1
def parser(custom_parser = nil)
-
if custom_parser.nil?
-
default_options[:parser]
-
else
-
default_options[:parser] = custom_parser
-
validate_format
-
end
-
end
-
-
# Allows setting a custom connection_adapter for the http connections
-
#
-
# @example
-
# class Foo
-
# include HTTParty
-
# connection_adapter Proc.new {|uri, options| ... }
-
# end
-
#
-
# @example provide optional configuration for your connection_adapter
-
# class Foo
-
# include HTTParty
-
# connection_adapter Proc.new {|uri, options| ... }, {:foo => :bar}
-
# end
-
#
-
# @see HTTParty::ConnectionAdapter
-
1
def connection_adapter(custom_adapter = nil, options = nil)
-
if custom_adapter.nil?
-
default_options[:connection_adapter]
-
else
-
default_options[:connection_adapter] = custom_adapter
-
default_options[:connection_adapter_options] = options
-
end
-
end
-
-
# Allows making a get request to a url.
-
#
-
# class Foo
-
# include HTTParty
-
# end
-
#
-
# # Simple get with full url
-
# Foo.get('http://foo.com/resource.json')
-
#
-
# # Simple get with full url and query parameters
-
# # ie: http://foo.com/resource.json?limit=10
-
# Foo.get('http://foo.com/resource.json', :query => {:limit => 10})
-
1
def get(path, options={}, &block)
-
perform_request Net::HTTP::Get, path, options, &block
-
end
-
-
# Allows making a post request to a url.
-
#
-
# class Foo
-
# include HTTParty
-
# end
-
#
-
# # Simple post with full url and setting the body
-
# Foo.post('http://foo.com/resources', :body => {:bar => 'baz'})
-
#
-
# # Simple post with full url using :query option,
-
# # which gets set as form data on the request.
-
# Foo.post('http://foo.com/resources', :query => {:bar => 'baz'})
-
1
def post(path, options={}, &block)
-
perform_request Net::HTTP::Post, path, options, &block
-
end
-
-
# Perform a PATCH request to a path
-
1
def patch(path, options={}, &block)
-
perform_request Net::HTTP::Patch, path, options, &block
-
end
-
-
# Perform a PUT request to a path
-
1
def put(path, options={}, &block)
-
perform_request Net::HTTP::Put, path, options, &block
-
end
-
-
# Perform a DELETE request to a path
-
1
def delete(path, options={}, &block)
-
perform_request Net::HTTP::Delete, path, options, &block
-
end
-
-
# Perform a MOVE request to a path
-
1
def move(path, options={}, &block)
-
perform_request Net::HTTP::Move, path, options, &block
-
end
-
-
# Perform a COPY request to a path
-
1
def copy(path, options={}, &block)
-
perform_request Net::HTTP::Copy, path, options, &block
-
end
-
-
# Perform a HEAD request to a path
-
1
def head(path, options={}, &block)
-
perform_request Net::HTTP::Head, path, options, &block
-
end
-
-
# Perform an OPTIONS request to a path
-
1
def options(path, options={}, &block)
-
perform_request Net::HTTP::Options, path, options, &block
-
end
-
-
1
def default_options #:nodoc:
-
@default_options
-
end
-
-
1
private
-
-
1
def perform_request(http_method, path, options, &block) #:nodoc:
-
options = default_options.merge(options)
-
process_headers(options)
-
process_cookies(options)
-
Request.new(http_method, path, options).perform(&block)
-
end
-
-
1
def process_headers(options)
-
if options[:headers] && headers.any?
-
options[:headers] = headers.merge(options[:headers])
-
end
-
end
-
-
1
def process_cookies(options) #:nodoc:
-
return unless options[:cookies] || default_cookies.any?
-
options[:headers] ||= headers.dup
-
options[:headers]["cookie"] = cookies.merge(options.delete(:cookies) || {}).to_cookie_string
-
end
-
-
1
def validate_format
-
if format && parser.respond_to?(:supports_format?) && !parser.supports_format?(format)
-
raise UnsupportedFormat, "'#{format.inspect}' Must be one of: #{parser.supported_formats.map{|f| f.to_s}.sort.join(', ')}"
-
end
-
end
-
end
-
-
1
def self.normalize_base_uri(url) #:nodoc:
-
normalized_url = url.dup
-
use_ssl = (normalized_url =~ /^https/) || (normalized_url =~ /:443\b/)
-
ends_with_slash = normalized_url =~ /\/$/
-
-
normalized_url.chop! if ends_with_slash
-
normalized_url.gsub!(/^https?:\/\//i, '')
-
-
"http#{'s' if use_ssl}://#{normalized_url}"
-
end
-
-
1
class Basement #:nodoc:
-
1
include HTTParty
-
end
-
-
1
def self.get(*args, &block)
-
Basement.get(*args, &block)
-
end
-
-
1
def self.post(*args, &block)
-
Basement.post(*args, &block)
-
end
-
-
1
def self.patch(*args, &block)
-
Basement.patch(*args, &block)
-
end
-
-
1
def self.put(*args, &block)
-
Basement.put(*args, &block)
-
end
-
-
1
def self.delete(*args, &block)
-
Basement.delete(*args, &block)
-
end
-
-
1
def self.move(*args, &block)
-
Basement.move(*args, &block)
-
end
-
-
1
def self.copy(*args, &block)
-
Basement.copy(*args, &block)
-
end
-
-
1
def self.head(*args, &block)
-
Basement.head(*args, &block)
-
end
-
-
1
def self.options(*args, &block)
-
Basement.options(*args, &block)
-
end
-
end
-
-
1
require 'httparty/core_extensions'
-
1
require 'httparty/hash_conversions'
-
1
require 'httparty/exceptions'
-
1
require 'httparty/parser'
-
1
require 'httparty/request'
-
1
require 'httparty/response'
-
1
module HTTParty
-
# Default connection adapter that returns a new Net::HTTP each time
-
#
-
# == Custom Connection Factories
-
#
-
# If you like to implement your own connection adapter, subclassing
-
# HTTPParty::ConnectionAdapter will make it easier. Just override
-
# the #connection method. The uri and options attributes will have
-
# all the info you need to construct your http connection. Whatever
-
# you return from your connection method needs to adhere to the
-
# Net::HTTP interface as this is what HTTParty expects.
-
#
-
# @example log the uri and options
-
# class LoggingConnectionAdapter < HTTParty::ConnectionAdapter
-
# def connection
-
# puts uri
-
# puts options
-
# Net::HTTP.new(uri)
-
# end
-
# end
-
#
-
# @example count number of http calls
-
# class CountingConnectionAdapter < HTTParty::ConnectionAdapter
-
# @@count = 0
-
#
-
# self.count
-
# @@count
-
# end
-
#
-
# def connection
-
# self.count += 1
-
# super
-
# end
-
# end
-
#
-
# === Configuration
-
# There is lots of configuration data available for your connection adapter
-
# in the #options attribute. It is up to you to interpret them within your
-
# connection adapter. Take a look at the implementation of
-
# HTTParty::ConnectionAdapter#connection for examples of how they are used.
-
# Some things that are probably interesting are as follows:
-
# * :+timeout+: timeout in seconds
-
# * :+open_timeout+: http connection open_timeout in seconds, overrides timeout if set
-
# * :+read_timeout+: http connection read_timeout in seconds, overrides timeout if set
-
# * :+debug_output+: see HTTParty::ClassMethods.debug_output.
-
# * :+pem+: contains pem data. see HTTParty::ClassMethods.pem.
-
# * :+verify+: verify the server’s certificate against the ca certificate.
-
# * :+ssl_ca_file+: see HTTParty::ClassMethods.ssl_ca_file.
-
# * :+ssl_ca_path+: see HTTParty::ClassMethods.ssl_ca_path.
-
# * :+connection_adapter_options+: contains the hash you passed to HTTParty.connection_adapter when you configured your connection adapter
-
1
class ConnectionAdapter
-
-
# Private: Regex used to strip brackets from IPv6 URIs.
-
1
StripIpv6BracketsRegex = /\A\[(.*)\]\z/
-
-
# Public
-
1
def self.call(uri, options)
-
new(uri, options).connection
-
end
-
-
1
attr_reader :uri, :options
-
-
1
def initialize(uri, options={})
-
raise ArgumentError, "uri must be a URI, not a #{uri.class}" unless uri.kind_of? URI
-
-
@uri = uri
-
@options = options
-
end
-
-
1
def connection
-
host = clean_host(uri.host)
-
if options[:http_proxyaddr]
-
http = Net::HTTP.new(host, uri.port, options[:http_proxyaddr], options[:http_proxyport], options[:http_proxyuser], options[:http_proxypass])
-
else
-
http = Net::HTTP.new(host, uri.port)
-
end
-
-
http.use_ssl = ssl_implied?(uri)
-
-
attach_ssl_certificates(http, options)
-
-
if options[:timeout] && (options[:timeout].is_a?(Integer) || options[:timeout].is_a?(Float))
-
http.open_timeout = options[:timeout]
-
http.read_timeout = options[:timeout]
-
end
-
-
if options[:read_timeout] && (options[:read_timeout].is_a?(Integer) || options[:read_timeout].is_a?(Float))
-
http.read_timeout = options[:read_timeout]
-
end
-
-
if options[:open_timeout] && (options[:open_timeout].is_a?(Integer) || options[:open_timeout].is_a?(Float))
-
http.open_timeout = options[:open_timeout]
-
end
-
-
if options[:debug_output]
-
http.set_debug_output(options[:debug_output])
-
end
-
-
if options[:ciphers]
-
http.ciphers = options[:ciphers]
-
end
-
-
# Bind to a specific local address or port
-
#
-
# @see https://bugs.ruby-lang.org/issues/6617
-
if options[:local_host]
-
if RUBY_VERSION >= "2.0.0"
-
http.local_host = options[:local_host]
-
else
-
Kernel.warn("Warning: option :local_host requires Ruby version 2.0 or later")
-
end
-
end
-
-
if options[:local_port]
-
if RUBY_VERSION >= "2.0.0"
-
http.local_port = options[:local_port]
-
else
-
Kernel.warn("Warning: option :local_port requires Ruby version 2.0 or later")
-
end
-
end
-
-
return http
-
end
-
-
1
private
-
-
1
def clean_host(host)
-
strip_ipv6_brackets(host)
-
end
-
-
1
def strip_ipv6_brackets(host)
-
StripIpv6BracketsRegex =~ host ? $1 : host
-
end
-
-
1
def ssl_implied?(uri)
-
uri.port == 443 || uri.instance_of?(URI::HTTPS)
-
end
-
-
1
def attach_ssl_certificates(http, options)
-
if http.use_ssl?
-
if options.fetch(:verify, true)
-
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
-
if options[:cert_store]
-
http.cert_store = options[:cert_store]
-
else
-
# Use the default cert store by default, i.e. system ca certs
-
http.cert_store = OpenSSL::X509::Store.new
-
http.cert_store.set_default_paths
-
end
-
else
-
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
-
end
-
-
# Client certificate authentication
-
if options[:pem]
-
http.cert = OpenSSL::X509::Certificate.new(options[:pem])
-
http.key = OpenSSL::PKey::RSA.new(options[:pem], options[:pem_password])
-
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
-
end
-
-
# PKCS12 client certificate authentication
-
if options[:p12]
-
p12 = OpenSSL::PKCS12.new(options[:p12], options[:p12_password])
-
http.cert = p12.certificate
-
http.key = p12.key
-
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
-
end
-
-
# SSL certificate authority file and/or directory
-
if options[:ssl_ca_file]
-
http.ca_file = options[:ssl_ca_file]
-
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
-
end
-
-
if options[:ssl_ca_path]
-
http.ca_path = options[:ssl_ca_path]
-
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
-
end
-
-
# This is only Ruby 1.9+
-
if options[:ssl_version] && http.respond_to?(:ssl_version=)
-
http.ssl_version = options[:ssl_version]
-
end
-
end
-
end
-
end
-
end
-
1
class HTTParty::CookieHash < Hash #:nodoc:
-
-
1
CLIENT_COOKIES = %w{path expires domain path secure HTTPOnly}
-
-
1
def add_cookies(value)
-
case value
-
when Hash
-
merge!(value)
-
when String
-
value.split('; ').each do |cookie|
-
array = cookie.split('=',2)
-
self[array[0].to_sym] = array[1]
-
end
-
else
-
raise "add_cookies only takes a Hash or a String"
-
end
-
end
-
-
1
def to_cookie_string
-
delete_if { |k, v| CLIENT_COOKIES.include?(k.to_s.downcase) }.collect { |k, v| "#{k}=#{v}" }.join("; ")
-
end
-
end
-
1
module HTTParty
-
1
if defined?(::BasicObject)
-
1
BasicObject = ::BasicObject #:nodoc:
-
else
-
class BasicObject #:nodoc:
-
instance_methods.each { |m| undef_method m unless m =~ /^__|instance_eval/ }
-
end
-
end
-
-
1
unless defined?(Net::HTTP::Patch)
-
class Net::HTTP
-
def patch(path, data, initheader = nil, dest = nil, &block) #:nodoc:
-
res = nil
-
request(Patch.new(path, initheader), data) {|r|
-
r.read_body dest, &block
-
res = r
-
}
-
unless @newimpl
-
res.value
-
return res, res.body
-
end
-
res
-
end
-
-
class Patch < Net::HTTPRequest
-
METHOD = 'PATCH'
-
REQUEST_HAS_BODY = true
-
RESPONSE_HAS_BODY = true
-
end
-
end
-
end
-
end
-
1
module HTTParty
-
# @abstact Exceptions raised by HTTParty inherit from Error
-
1
class Error < StandardError; end
-
-
# Exception raised when you attempt to set a non-existant format
-
1
class UnsupportedFormat < Error; end
-
-
# Exception raised when using a URI scheme other than HTTP or HTTPS
-
1
class UnsupportedURIScheme < Error; end
-
-
# @abstract Exceptions which inherit from ResponseError contain the Net::HTTP
-
# response object accessible via the {#response} method.
-
1
class ResponseError < Error
-
# Returns the response of the last request
-
# @return [Net::HTTPResponse] A subclass of Net::HTTPResponse, e.g.
-
# Net::HTTPOK
-
1
attr_reader :response
-
-
# Instantiate an instance of ResponseError with a Net::HTTPResponse object
-
# @param [Net::HTTPResponse]
-
1
def initialize(response)
-
@response = response
-
end
-
end
-
-
# Exception that is raised when request has redirected too many times.
-
# Calling {#response} returns the Net:HTTP response object.
-
1
class RedirectionTooDeep < ResponseError; end
-
end
-
1
module HTTParty
-
1
module HashConversions
-
# @return <String> This hash as a query string
-
#
-
# @example
-
# { :name => "Bob",
-
# :address => {
-
# :street => '111 Ruby Ave.',
-
# :city => 'Ruby Central',
-
# :phones => ['111-111-1111', '222-222-2222']
-
# }
-
# }.to_params
-
# #=> "name=Bob&address[city]=Ruby Central&address[phones][]=111-111-1111&address[phones][]=222-222-2222&address[street]=111 Ruby Ave."
-
1
def self.to_params(hash)
-
params = hash.map { |k,v| normalize_param(k,v) }.join
-
params.chop! # trailing &
-
params
-
end
-
-
# @param key<Object> The key for the param.
-
# @param value<Object> The value for the param.
-
#
-
# @return <String> This key value pair as a param
-
#
-
# @example normalize_param(:name, "Bob Jones") #=> "name=Bob%20Jones&"
-
1
def self.normalize_param(key, value)
-
param = ''
-
stack = []
-
-
if value.is_a?(Array)
-
param << value.map { |element| normalize_param("#{key}[]", element) }.join
-
elsif value.is_a?(Hash)
-
stack << [key,value]
-
else
-
param << "#{key}=#{URI.encode(value.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))}&"
-
end
-
-
stack.each do |parent, hash|
-
hash.each do |k, v|
-
if v.is_a?(Hash)
-
stack << ["#{parent}[#{k}]", v]
-
else
-
param << normalize_param("#{parent}[#{k}]", v)
-
end
-
end
-
end
-
-
param
-
end
-
end
-
end
-
1
module HTTParty
-
1
module Logger
-
1
class ApacheLogger #:nodoc:
-
1
TAG_NAME = HTTParty.name
-
-
1
attr_accessor :level, :logger, :current_time
-
-
1
def initialize(logger, level)
-
@logger = logger
-
@level = level.to_sym
-
end
-
-
1
def format(request, response)
-
current_time = Time.now.strftime("%Y-%m-%d %H:%M:%S %z")
-
http_method = request.http_method.name.split("::").last.upcase
-
path = request.path.to_s
-
content_length = response['Content-Length']
-
@logger.send @level, "[#{TAG_NAME}] [#{current_time}] #{response.code} \"#{http_method} #{path}\" #{content_length || "-"} "
-
end
-
end
-
end
-
end
-
1
module HTTParty
-
1
module Logger
-
1
class CurlLogger #:nodoc:
-
1
TAG_NAME = HTTParty.name
-
1
OUT = ">"
-
1
IN = "<"
-
-
1
attr_accessor :level, :logger, :current_time
-
-
1
def initialize(logger, level)
-
@logger = logger
-
@level = level.to_sym
-
end
-
-
1
def format(request, response)
-
messages = []
-
time = Time.now.strftime("%Y-%m-%d %H:%M:%S %z")
-
http_method = request.http_method.name.split("::").last.upcase
-
path = request.path.to_s
-
-
messages << print(time, OUT, "#{http_method} #{path}")
-
-
if request.options[:headers] && request.options[:headers].size > 0
-
request.options[:headers].each do |k, v|
-
messages << print(time, OUT, "#{k}: #{v}")
-
end
-
end
-
-
messages << print(time, OUT, request.raw_body)
-
messages << print(time, OUT, "")
-
messages << print(time, IN, "HTTP/#{response.http_version} #{response.code}")
-
-
headers = response.respond_to?(:headers) ? response.headers : response
-
response.each_header do |response_header|
-
messages << print(time, IN, "#{response_header.capitalize}: #{headers[response_header]}")
-
end
-
-
messages << print(time, IN, "\n#{response.body}")
-
-
@logger.send @level, messages.join("\n")
-
end
-
-
1
def print(time, direction, line)
-
"[#{TAG_NAME}] [#{time}] #{direction} #{line}"
-
end
-
end
-
end
-
end
-
1
require 'httparty/logger/apache_logger'
-
1
require 'httparty/logger/curl_logger'
-
-
1
module HTTParty
-
1
module Logger
-
1
def self.build(logger, level, formatter)
-
level ||= :info
-
format ||= :apache
-
-
case formatter
-
when :curl
-
Logger::CurlLogger.new(logger, level)
-
else
-
Logger::ApacheLogger.new(logger, level)
-
end
-
end
-
end
-
end
-
1
module HTTParty
-
1
module ModuleInheritableAttributes #:nodoc:
-
1
def self.included(base)
-
1
base.extend(ClassMethods)
-
end
-
-
# borrowed from Rails 3.2 ActiveSupport
-
1
def self.hash_deep_dup(hash)
-
duplicate = hash.dup
-
-
duplicate.each_pair do |key, value|
-
duplicate[key] = if value.is_a?(Hash)
-
hash_deep_dup(value)
-
elsif value.is_a?(Proc)
-
duplicate[key] = value.dup
-
else
-
value
-
end
-
end
-
-
duplicate
-
end
-
-
1
module ClassMethods #:nodoc:
-
1
def mattr_inheritable(*args)
-
2
@mattr_inheritable_attrs ||= [:mattr_inheritable_attrs]
-
2
@mattr_inheritable_attrs += args
-
-
2
args.each do |arg|
-
2
module_eval %(class << self; attr_accessor :#{arg} end)
-
end
-
-
2
@mattr_inheritable_attrs
-
end
-
-
1
def inherited(subclass)
-
super
-
@mattr_inheritable_attrs.each do |inheritable_attribute|
-
ivar = "@#{inheritable_attribute}"
-
subclass.instance_variable_set(ivar, instance_variable_get(ivar).clone)
-
-
if instance_variable_get(ivar).respond_to?(:merge)
-
method = <<-EOM
-
def self.#{inheritable_attribute}
-
duplicate = ModuleInheritableAttributes.hash_deep_dup(#{ivar})
-
#{ivar} = superclass.#{inheritable_attribute}.merge(duplicate)
-
end
-
EOM
-
-
subclass.class_eval method
-
end
-
end
-
end
-
end
-
end
-
end
-
1
require 'digest/md5'
-
1
require 'net/http'
-
-
1
module Net
-
1
module HTTPHeader
-
1
def digest_auth(username, password, response)
-
@header['Authorization'] = DigestAuthenticator.new(username, password,
-
@method, @path, response).authorization_header
-
end
-
-
-
1
class DigestAuthenticator
-
1
def initialize(username, password, method, path, response_header)
-
@username = username
-
@password = password
-
@method = method
-
@path = path
-
@response = parse(response_header)
-
end
-
-
1
def authorization_header
-
@cnonce = md5(random)
-
header = [
-
%Q(Digest username="#{@username}"),
-
%Q(realm="#{@response['realm']}"),
-
%Q(nonce="#{@response['nonce']}"),
-
%Q(uri="#{@path}"),
-
%Q(response="#{request_digest}"),
-
]
-
-
if qop_present?
-
fields = [
-
%Q(cnonce="#{@cnonce}"),
-
%Q(qop="#{@response['qop']}"),
-
%Q(nc=00000001)
-
]
-
fields.each { |field| header << field }
-
end
-
-
header << %Q(opaque="#{@response['opaque']}") if opaque_present?
-
header
-
end
-
-
1
private
-
-
1
def parse(response_header)
-
response_header['www-authenticate'] =~ /Digest (.*)/
-
params = {}
-
$1.gsub(/(\w+)="(.*?)"/) { params[$1] = $2 }
-
params
-
end
-
-
1
def opaque_present?
-
@response.has_key?('opaque') and not @response['opaque'].empty?
-
end
-
-
1
def qop_present?
-
@response.has_key?('qop') and not @response['qop'].empty?
-
end
-
-
1
def random
-
"%x" % (Time.now.to_i + rand(65535))
-
end
-
-
1
def request_digest
-
a = [md5(a1), @response['nonce'], md5(a2)]
-
a.insert(2, "00000001", @cnonce, @response['qop']) if qop_present?
-
md5(a.join(":"))
-
end
-
-
1
def md5(str)
-
Digest::MD5.hexdigest(str)
-
end
-
-
1
def a1
-
[@username, @response['realm'], @password].join(":")
-
end
-
-
1
def a2
-
[@method, @path].join(":")
-
end
-
end
-
end
-
end
-
1
module HTTParty
-
# The default parser used by HTTParty, supports xml, json, html, csv and
-
# plain text.
-
#
-
# == Custom Parsers
-
#
-
# If you'd like to do your own custom parsing, subclassing HTTParty::Parser
-
# will make that process much easier. There are a few different ways you can
-
# utilize HTTParty::Parser as a superclass.
-
#
-
# @example Intercept the parsing for all formats
-
# class SimpleParser < HTTParty::Parser
-
# def parse
-
# perform_parsing
-
# end
-
# end
-
#
-
# @example Add the atom format and parsing method to the default parser
-
# class AtomParsingIncluded < HTTParty::Parser
-
# SupportedFormats.merge!(
-
# {"application/atom+xml" => :atom}
-
# )
-
#
-
# def atom
-
# perform_atom_parsing
-
# end
-
# end
-
#
-
# @example Only support the atom format
-
# class ParseOnlyAtom < HTTParty::Parser
-
# SupportedFormats = {"application/atom+xml" => :atom}
-
#
-
# def atom
-
# perform_atom_parsing
-
# end
-
# end
-
#
-
# @abstract Read the Custom Parsers section for more information.
-
1
class Parser
-
1
SupportedFormats = {
-
'text/xml' => :xml,
-
'application/xml' => :xml,
-
'application/json' => :json,
-
'text/json' => :json,
-
'application/javascript' => :plain,
-
'text/javascript' => :plain,
-
'text/html' => :html,
-
'text/plain' => :plain,
-
'text/csv' => :csv,
-
'application/csv' => :csv,
-
'text/comma-separated-values' => :csv
-
}
-
-
# The response body of the request
-
# @return [String]
-
1
attr_reader :body
-
-
# The intended parsing format for the request
-
# @return [Symbol] e.g. :json
-
1
attr_reader :format
-
-
# Instantiate the parser and call {#parse}.
-
# @param [String] body the response body
-
# @param [Symbol] format the response format
-
# @return parsed response
-
1
def self.call(body, format)
-
new(body, format).parse
-
end
-
-
# @return [Hash] the SupportedFormats hash
-
1
def self.formats
-
const_get(:SupportedFormats)
-
end
-
-
# @param [String] mimetype response MIME type
-
# @return [Symbol]
-
# @return [nil] mime type not supported
-
1
def self.format_from_mimetype(mimetype)
-
formats[formats.keys.detect {|k| mimetype.include?(k)}]
-
end
-
-
# @return [Array<Symbol>] list of supported formats
-
1
def self.supported_formats
-
formats.values.uniq
-
end
-
-
# @param [Symbol] format e.g. :json, :xml
-
# @return [Boolean]
-
1
def self.supports_format?(format)
-
supported_formats.include?(format)
-
end
-
-
1
def initialize(body, format)
-
@body = body
-
@format = format
-
end
-
-
# @return [Object] the parsed body
-
# @return [nil] when the response body is nil, an empty string, spaces only or "null"
-
1
def parse
-
return nil if body.nil? || body.strip.empty? || body == "null"
-
if supports_format?
-
parse_supported_format
-
else
-
body
-
end
-
end
-
-
1
protected
-
-
1
def xml
-
MultiXml.parse(body)
-
end
-
-
1
def json
-
JSON.load(body, nil)
-
end
-
-
1
def csv
-
CSV.parse(body)
-
end
-
-
1
def html
-
body
-
end
-
-
1
def plain
-
body
-
end
-
-
1
def supports_format?
-
self.class.supports_format?(format)
-
end
-
-
1
def parse_supported_format
-
send(format)
-
rescue NoMethodError => e
-
raise NotImplementedError, "#{self.class.name} has not implemented a parsing method for the #{format.inspect} format.", e.backtrace
-
end
-
end
-
end
-
1
module HTTParty
-
1
class Request #:nodoc:
-
1
SupportedHTTPMethods = [
-
Net::HTTP::Get,
-
Net::HTTP::Post,
-
Net::HTTP::Patch,
-
Net::HTTP::Put,
-
Net::HTTP::Delete,
-
Net::HTTP::Head,
-
Net::HTTP::Options,
-
Net::HTTP::Move,
-
Net::HTTP::Copy
-
]
-
-
1
SupportedURISchemes = [URI::HTTP, URI::HTTPS, URI::Generic]
-
-
1
NON_RAILS_QUERY_STRING_NORMALIZER = Proc.new do |query|
-
Array(query).sort_by { |a| a[0].to_s }.map do |key, value|
-
if value.nil?
-
key.to_s
-
elsif value.is_a?(Array)
-
value.map {|v| "#{key}=#{URI.encode(v.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))}"}
-
else
-
HashConversions.to_params(key => value)
-
end
-
end.flatten.join('&')
-
end
-
-
1
attr_accessor :http_method, :options, :last_response, :redirect, :last_uri
-
1
attr_reader :path
-
-
1
def initialize(http_method, path, o={})
-
self.http_method = http_method
-
self.path = path
-
self.options = {
-
:limit => o.delete(:no_follow) ? 1 : 5,
-
:assume_utf16_is_big_endian => true,
-
:default_params => {},
-
:follow_redirects => true,
-
:parser => Parser,
-
:connection_adapter => ConnectionAdapter
-
}.merge(o)
-
end
-
-
1
def path=(uri)
-
@path = URI(uri)
-
end
-
-
1
def request_uri(uri)
-
if uri.respond_to? :request_uri
-
uri.request_uri
-
else
-
uri.path
-
end
-
end
-
-
1
def uri
-
new_uri = path.relative? ? URI.parse("#{base_uri}#{path}") : path.clone
-
-
# avoid double query string on redirects [#12]
-
unless redirect
-
new_uri.query = query_string(new_uri)
-
end
-
-
unless SupportedURISchemes.include? new_uri.class
-
raise UnsupportedURIScheme, "'#{new_uri}' Must be HTTP, HTTPS or Generic"
-
end
-
-
@last_uri = new_uri
-
end
-
-
1
def base_uri
-
redirect ? "#{@last_uri.scheme}://#{@last_uri.host}" : options[:base_uri]
-
end
-
-
1
def format
-
options[:format] || (format_from_mimetype(last_response['content-type']) if last_response)
-
end
-
-
1
def parser
-
options[:parser]
-
end
-
-
1
def connection_adapter
-
options[:connection_adapter]
-
end
-
-
1
def perform(&block)
-
validate
-
setup_raw_request
-
chunked_body = nil
-
-
self.last_response = http.request(@raw_request) do |http_response|
-
if block
-
chunks = []
-
-
http_response.read_body do |fragment|
-
chunks << fragment
-
block.call(fragment)
-
end
-
-
chunked_body = chunks.join
-
end
-
end
-
-
handle_deflation unless http_method == Net::HTTP::Head
-
handle_response(chunked_body, &block)
-
end
-
-
1
def raw_body
-
@raw_request.body
-
end
-
-
1
private
-
-
1
def http
-
connection_adapter.call(uri, options)
-
end
-
-
1
def body
-
options[:body].is_a?(Hash) ? normalize_query(options[:body]) : options[:body]
-
end
-
-
1
def credentials
-
options[:basic_auth] || options[:digest_auth]
-
end
-
-
1
def username
-
credentials[:username]
-
end
-
-
1
def password
-
credentials[:password]
-
end
-
-
1
def normalize_query(query)
-
if query_string_normalizer
-
query_string_normalizer.call(query)
-
else
-
HashConversions.to_params(query)
-
end
-
end
-
-
1
def query_string_normalizer
-
options[:query_string_normalizer]
-
end
-
-
1
def setup_raw_request
-
@raw_request = http_method.new(request_uri(uri))
-
@raw_request.body = body if body
-
@raw_request.body_stream = options[:body_stream] if options[:body_stream]
-
@raw_request.initialize_http_header(options[:headers])
-
@raw_request.basic_auth(username, password) if options[:basic_auth]
-
setup_digest_auth if options[:digest_auth]
-
end
-
-
1
def setup_digest_auth
-
auth_request = http_method.new(uri.request_uri)
-
auth_request.initialize_http_header(options[:headers])
-
res = http.request(auth_request)
-
-
if res['www-authenticate'] != nil && res['www-authenticate'].length > 0
-
@raw_request.digest_auth(username, password, res)
-
end
-
end
-
-
1
def query_string(uri)
-
query_string_parts = []
-
query_string_parts << uri.query unless uri.query.nil?
-
-
if options[:query].is_a?(Hash)
-
query_string_parts << normalize_query(options[:default_params].merge(options[:query]))
-
else
-
query_string_parts << normalize_query(options[:default_params]) unless options[:default_params].empty?
-
query_string_parts << options[:query] unless options[:query].nil?
-
end
-
-
query_string_parts.reject!(&:empty?) unless query_string_parts == [""]
-
query_string_parts.size > 0 ? query_string_parts.join('&') : nil
-
end
-
-
1
def get_charset
-
content_type = last_response["content-type"]
-
if content_type.nil?
-
return nil
-
end
-
-
if content_type =~ /;\s*charset\s*=\s*([^=,;"\s]+)/i
-
return $1
-
end
-
-
if content_type =~ /;\s*charset\s*=\s*"((\\.|[^\\"])+)"/i
-
return $1.gsub(/\\(.)/, '\1')
-
end
-
-
nil
-
end
-
-
1
def encode_with_ruby_encoding(body, charset)
-
begin
-
encoding = Encoding.find(charset)
-
body.force_encoding(encoding)
-
rescue
-
body
-
end
-
end
-
-
1
def assume_utf16_is_big_endian
-
options[:assume_utf16_is_big_endian]
-
end
-
-
1
def encode_utf_16(body)
-
if body.bytesize >= 2
-
if body.getbyte(0) == 0xFF && body.getbyte(1) == 0xFE
-
return body.force_encoding("UTF-16LE")
-
elsif body.getbyte(0) == 0xFE && body.getbyte(1) == 0xFF
-
return body.force_encoding("UTF-16BE")
-
end
-
end
-
-
if assume_utf16_is_big_endian
-
body.force_encoding("UTF-16BE")
-
else
-
body.force_encoding("UTF-16LE")
-
end
-
-
end
-
-
1
def _encode_body(body)
-
charset = get_charset
-
-
if charset.nil?
-
return body
-
end
-
-
if "utf-16".casecmp(charset) == 0
-
encode_utf_16(body)
-
else
-
encode_with_ruby_encoding(body, charset)
-
end
-
end
-
-
1
def encode_body(body)
-
if "".respond_to?(:encoding)
-
_encode_body(body)
-
else
-
body
-
end
-
end
-
-
1
def handle_response(body, &block)
-
if response_redirects?
-
options[:limit] -= 1
-
if options[:logger]
-
logger = HTTParty::Logger.build(options[:logger], options[:log_level], options[:log_format])
-
logger.format(self, last_response)
-
end
-
self.path = last_response['location']
-
self.redirect = true
-
self.http_method = Net::HTTP::Get unless options[:maintain_method_across_redirects]
-
capture_cookies(last_response)
-
perform(&block)
-
else
-
body = body || last_response.body
-
body = encode_body(body)
-
Response.new(self, last_response, lambda { parse_response(body) }, :body => body)
-
end
-
end
-
-
# Inspired by Ruby 1.9
-
1
def handle_deflation
-
case last_response["content-encoding"]
-
when "gzip", "x-gzip"
-
body_io = StringIO.new(last_response.body)
-
last_response.body.replace Zlib::GzipReader.new(body_io).read
-
last_response.delete('content-encoding')
-
when "deflate"
-
last_response.body.replace Zlib::Inflate.inflate(last_response.body)
-
last_response.delete('content-encoding')
-
end
-
end
-
-
1
def response_redirects?
-
case last_response
-
when Net::HTTPMultipleChoice, # 300
-
Net::HTTPMovedPermanently, # 301
-
Net::HTTPFound, # 302
-
Net::HTTPSeeOther, # 303
-
Net::HTTPUseProxy, # 305
-
Net::HTTPTemporaryRedirect
-
options[:follow_redirects] && last_response.key?('location')
-
end
-
end
-
-
1
def parse_response(body)
-
parser.call(body, format)
-
end
-
-
1
def capture_cookies(response)
-
return unless response['Set-Cookie']
-
cookies_hash = HTTParty::CookieHash.new()
-
cookies_hash.add_cookies(options[:headers]['Cookie']) if options[:headers] && options[:headers]['Cookie']
-
response.get_fields('Set-Cookie').each { |cookie| cookies_hash.add_cookies(cookie) }
-
options[:headers] ||= {}
-
options[:headers]['Cookie'] = cookies_hash.to_cookie_string
-
end
-
-
# Uses the HTTP Content-Type header to determine the format of the
-
# response It compares the MIME type returned to the types stored in the
-
# SupportedFormats hash
-
1
def format_from_mimetype(mimetype)
-
if mimetype && parser.respond_to?(:format_from_mimetype)
-
parser.format_from_mimetype(mimetype)
-
end
-
end
-
-
1
def validate
-
raise HTTParty::RedirectionTooDeep.new(last_response), 'HTTP redirects too deep' if options[:limit].to_i <= 0
-
raise ArgumentError, 'only get, post, patch, put, delete, head, and options methods are supported' unless SupportedHTTPMethods.include?(http_method)
-
raise ArgumentError, ':headers must be a hash' if options[:headers] && !options[:headers].is_a?(Hash)
-
raise ArgumentError, 'only one authentication method, :basic_auth or :digest_auth may be used at a time' if options[:basic_auth] && options[:digest_auth]
-
raise ArgumentError, ':basic_auth must be a hash' if options[:basic_auth] && !options[:basic_auth].is_a?(Hash)
-
raise ArgumentError, ':digest_auth must be a hash' if options[:digest_auth] && !options[:digest_auth].is_a?(Hash)
-
raise ArgumentError, ':query must be hash if using HTTP Post' if post? && !options[:query].nil? && !options[:query].is_a?(Hash)
-
end
-
-
1
def post?
-
Net::HTTP::Post == http_method
-
end
-
end
-
end
-
1
module HTTParty
-
1
class Response < HTTParty::BasicObject #:nodoc:
-
1
def self.underscore(string)
-
56
string.gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').gsub(/([a-z])([A-Z])/,'\1_\2').downcase
-
end
-
-
1
attr_reader :request, :response, :body, :headers
-
-
1
def initialize(request, response, parsed_block, options={})
-
@request = request
-
@response = response
-
@body = options[:body] || response.body
-
@parsed_block = parsed_block
-
@headers = Headers.new(response.to_hash)
-
-
if request.options[:logger]
-
logger = ::HTTParty::Logger.build(request.options[:logger], request.options[:log_level], request.options[:log_format])
-
logger.format(request, self)
-
end
-
end
-
-
1
def parsed_response
-
@parsed_response ||= @parsed_block.call
-
end
-
-
1
def class
-
Response
-
end
-
-
1
def code
-
response.code.to_i
-
end
-
-
1
def inspect
-
inspect_id = "%x" % (object_id * 2)
-
%(#<#{self.class}:0x#{inspect_id} parsed_response=#{parsed_response.inspect}, @response=#{response.inspect}, @headers=#{headers.inspect}>)
-
end
-
-
1
CODES_TO_OBJ = ::Net::HTTPResponse::CODE_CLASS_TO_OBJ.merge ::Net::HTTPResponse::CODE_TO_OBJ
-
-
1
CODES_TO_OBJ.each do |response_code, klass|
-
56
name = klass.name.sub("Net::HTTP", '')
-
56
define_method("#{underscore(name)}?") do
-
klass === response
-
end
-
end
-
-
# Support old multiple_choice? method from pre 2.0.0 era.
-
1
if ::RUBY_VERSION >= "2.0.0" && ::RUBY_PLATFORM != "java"
-
1
alias_method :multiple_choice?, :multiple_choices?
-
end
-
-
1
def respond_to?(name, include_all = false)
-
return true if [:request, :response, :parsed_response, :body, :headers].include?(name)
-
parsed_response.respond_to?(name, include_all) || response.respond_to?(name, include_all)
-
end
-
-
1
protected
-
-
1
def method_missing(name, *args, &block)
-
if parsed_response.respond_to?(name)
-
parsed_response.send(name, *args, &block)
-
elsif response.respond_to?(name)
-
response.send(name, *args, &block)
-
else
-
super
-
end
-
end
-
end
-
end
-
-
1
require 'httparty/response/headers'
-
1
module HTTParty
-
1
class Response #:nodoc:
-
1
class Headers
-
1
include ::Net::HTTPHeader
-
-
1
def initialize(header = {})
-
@header = header
-
end
-
-
1
def ==(other)
-
@header == other
-
end
-
-
1
def inspect
-
@header.inspect
-
end
-
-
1
def method_missing(name, *args, &block)
-
if @header.respond_to?(name)
-
@header.send(name, *args, &block)
-
else
-
super
-
end
-
end
-
-
1
def respond_to?(method, include_all = false)
-
super || @header.respond_to?(method, include_all)
-
end
-
end
-
end
-
end
-
1
module HTTParty
-
1
VERSION = "0.13.1"
-
end
-
1
require 'i18n/version'
-
1
require 'i18n/exceptions'
-
1
require 'i18n/interpolate/ruby'
-
-
1
module I18n
-
1
autoload :Backend, 'i18n/backend'
-
1
autoload :Config, 'i18n/config'
-
1
autoload :Gettext, 'i18n/gettext'
-
1
autoload :Locale, 'i18n/locale'
-
1
autoload :Tests, 'i18n/tests'
-
-
1
RESERVED_KEYS = [:scope, :default, :separator, :resolve, :object, :fallback, :format, :cascade, :throw, :raise, :rescue_format]
-
1
RESERVED_KEYS_PATTERN = /%\{(#{RESERVED_KEYS.join("|")})\}/
-
-
1
extend(Module.new {
-
# Gets I18n configuration object.
-
1
def config
-
27
Thread.current[:i18n_config] ||= I18n::Config.new
-
end
-
-
# Sets I18n configuration object.
-
1
def config=(value)
-
8
Thread.current[:i18n_config] = value
-
end
-
-
# Write methods which delegates to the configuration object
-
%w(locale backend default_locale available_locales default_separator
-
1
exception_handler load_path enforce_available_locales).each do |method|
-
8
module_eval <<-DELEGATORS, __FILE__, __LINE__ + 1
-
def #{method}
-
config.#{method}
-
end
-
-
def #{method}=(value)
-
config.#{method} = (value)
-
end
-
DELEGATORS
-
end
-
-
# Tells the backend to reload translations. Used in situations like the
-
# Rails development environment. Backends can implement whatever strategy
-
# is useful.
-
1
def reload!
-
1
config.backend.reload!
-
end
-
-
# Translates, pluralizes and interpolates a given key using a given locale,
-
# scope, and default, as well as interpolation values.
-
#
-
# *LOOKUP*
-
#
-
# Translation data is organized as a nested hash using the upper-level keys
-
# as namespaces. <em>E.g.</em>, ActionView ships with the translation:
-
# <tt>:date => {:formats => {:short => "%b %d"}}</tt>.
-
#
-
# Translations can be looked up at any level of this hash using the key argument
-
# and the scope option. <em>E.g.</em>, in this example <tt>I18n.t :date</tt>
-
# returns the whole translations hash <tt>{:formats => {:short => "%b %d"}}</tt>.
-
#
-
# Key can be either a single key or a dot-separated key (both Strings and Symbols
-
# work). <em>E.g.</em>, the short format can be looked up using both:
-
# I18n.t 'date.formats.short'
-
# I18n.t :'date.formats.short'
-
#
-
# Scope can be either a single key, a dot-separated key or an array of keys
-
# or dot-separated keys. Keys and scopes can be combined freely. So these
-
# examples will all look up the same short date format:
-
# I18n.t 'date.formats.short'
-
# I18n.t 'formats.short', :scope => 'date'
-
# I18n.t 'short', :scope => 'date.formats'
-
# I18n.t 'short', :scope => %w(date formats)
-
#
-
# *INTERPOLATION*
-
#
-
# Translations can contain interpolation variables which will be replaced by
-
# values passed to #translate as part of the options hash, with the keys matching
-
# the interpolation variable names.
-
#
-
# <em>E.g.</em>, with a translation <tt>:foo => "foo %{bar}"</tt> the option
-
# value for the key +bar+ will be interpolated into the translation:
-
# I18n.t :foo, :bar => 'baz' # => 'foo baz'
-
#
-
# *PLURALIZATION*
-
#
-
# Translation data can contain pluralized translations. Pluralized translations
-
# are arrays of singluar/plural versions of translations like <tt>['Foo', 'Foos']</tt>.
-
#
-
# Note that <tt>I18n::Backend::Simple</tt> only supports an algorithm for English
-
# pluralization rules. Other algorithms can be supported by custom backends.
-
#
-
# This returns the singular version of a pluralized translation:
-
# I18n.t :foo, :count => 1 # => 'Foo'
-
#
-
# These both return the plural version of a pluralized translation:
-
# I18n.t :foo, :count => 0 # => 'Foos'
-
# I18n.t :foo, :count => 2 # => 'Foos'
-
#
-
# The <tt>:count</tt> option can be used both for pluralization and interpolation.
-
# <em>E.g.</em>, with the translation
-
# <tt>:foo => ['%{count} foo', '%{count} foos']</tt>, count will
-
# be interpolated to the pluralized translation:
-
# I18n.t :foo, :count => 1 # => '1 foo'
-
#
-
# *DEFAULTS*
-
#
-
# This returns the translation for <tt>:foo</tt> or <tt>default</tt> if no translation was found:
-
# I18n.t :foo, :default => 'default'
-
#
-
# This returns the translation for <tt>:foo</tt> or the translation for <tt>:bar</tt> if no
-
# translation for <tt>:foo</tt> was found:
-
# I18n.t :foo, :default => :bar
-
#
-
# Returns the translation for <tt>:foo</tt> or the translation for <tt>:bar</tt>
-
# or <tt>default</tt> if no translations for <tt>:foo</tt> and <tt>:bar</tt> were found.
-
# I18n.t :foo, :default => [:bar, 'default']
-
#
-
# *BULK LOOKUP*
-
#
-
# This returns an array with the translations for <tt>:foo</tt> and <tt>:bar</tt>.
-
# I18n.t [:foo, :bar]
-
#
-
# Can be used with dot-separated nested keys:
-
# I18n.t [:'baz.foo', :'baz.bar']
-
#
-
# Which is the same as using a scope option:
-
# I18n.t [:foo, :bar], :scope => :baz
-
#
-
# *LAMBDAS*
-
#
-
# Both translations and defaults can be given as Ruby lambdas. Lambdas will be
-
# called and passed the key and options.
-
#
-
# E.g. assuming the key <tt>:salutation</tt> resolves to:
-
# lambda { |key, options| options[:gender] == 'm' ? "Mr. %{options[:name]}" : "Mrs. %{options[:name]}" }
-
#
-
# Then <tt>I18n.t(:salutation, :gender => 'w', :name => 'Smith') will result in "Mrs. Smith".
-
#
-
# It is recommended to use/implement lambdas in an "idempotent" way. E.g. when
-
# a cache layer is put in front of I18n.translate it will generate a cache key
-
# from the argument values passed to #translate. Therefor your lambdas should
-
# always return the same translations/values per unique combination of argument
-
# values.
-
1
def translate(*args)
-
options = args.last.is_a?(Hash) ? args.pop.dup : {}
-
key = args.shift
-
backend = config.backend
-
locale = options.delete(:locale) || config.locale
-
handling = options.delete(:throw) && :throw || options.delete(:raise) && :raise # TODO deprecate :raise
-
-
enforce_available_locales!(locale)
-
raise I18n::ArgumentError if key.is_a?(String) && key.empty?
-
-
result = catch(:exception) do
-
if key.is_a?(Array)
-
key.map { |k| backend.translate(locale, k, options) }
-
else
-
backend.translate(locale, key, options)
-
end
-
end
-
result.is_a?(MissingTranslation) ? handle_exception(handling, result, locale, key, options) : result
-
end
-
1
alias :t :translate
-
-
# Wrapper for <tt>translate</tt> that adds <tt>:raise => true</tt>. With
-
# this option, if no translation is found, it will raise <tt>I18n::MissingTranslationData</tt>
-
1
def translate!(key, options={})
-
translate(key, options.merge(:raise => true))
-
end
-
1
alias :t! :translate!
-
-
# Returns true if a translation exists for a given key, otherwise returns false.
-
1
def exists?(key, locale = config.locale)
-
raise I18n::ArgumentError if key.is_a?(String) && key.empty?
-
config.backend.exists?(locale, key)
-
end
-
-
# Transliterates UTF-8 characters to ASCII. By default this method will
-
# transliterate only Latin strings to an ASCII approximation:
-
#
-
# I18n.transliterate("Ærøskøbing")
-
# # => "AEroskobing"
-
#
-
# I18n.transliterate("日本語")
-
# # => "???"
-
#
-
# It's also possible to add support for per-locale transliterations. I18n
-
# expects transliteration rules to be stored at
-
# <tt>i18n.transliterate.rule</tt>.
-
#
-
# Transliteration rules can either be a Hash or a Proc. Procs must accept a
-
# single string argument. Hash rules inherit the default transliteration
-
# rules, while Procs do not.
-
#
-
# *Examples*
-
#
-
# Setting a Hash in <locale>.yml:
-
#
-
# i18n:
-
# transliterate:
-
# rule:
-
# ü: "ue"
-
# ö: "oe"
-
#
-
# Setting a Hash using Ruby:
-
#
-
# store_translations(:de, :i18n => {
-
# :transliterate => {
-
# :rule => {
-
# "ü" => "ue",
-
# "ö" => "oe"
-
# }
-
# }
-
# )
-
#
-
# Setting a Proc:
-
#
-
# translit = lambda {|string| MyTransliterator.transliterate(string) }
-
# store_translations(:xx, :i18n => {:transliterate => {:rule => translit})
-
#
-
# Transliterating strings:
-
#
-
# I18n.locale = :en
-
# I18n.transliterate("Jürgen") # => "Jurgen"
-
# I18n.locale = :de
-
# I18n.transliterate("Jürgen") # => "Juergen"
-
# I18n.transliterate("Jürgen", :locale => :en) # => "Jurgen"
-
# I18n.transliterate("Jürgen", :locale => :de) # => "Juergen"
-
1
def transliterate(*args)
-
options = args.pop.dup if args.last.is_a?(Hash)
-
key = args.shift
-
locale = options && options.delete(:locale) || config.locale
-
handling = options && (options.delete(:throw) && :throw || options.delete(:raise) && :raise)
-
replacement = options && options.delete(:replacement)
-
enforce_available_locales!(locale)
-
config.backend.transliterate(locale, key, replacement)
-
rescue I18n::ArgumentError => exception
-
handle_exception(handling, exception, locale, key, options || {})
-
end
-
-
# Localizes certain objects, such as dates and numbers to local formatting.
-
1
def localize(object, options = nil)
-
options = options ? options.dup : {}
-
locale = options.delete(:locale) || config.locale
-
format = options.delete(:format) || :default
-
enforce_available_locales!(locale)
-
config.backend.localize(locale, object, format, options)
-
end
-
1
alias :l :localize
-
-
# Executes block with given I18n.locale set.
-
1
def with_locale(tmp_locale = nil)
-
if tmp_locale
-
current_locale = self.locale
-
self.locale = tmp_locale
-
end
-
yield
-
ensure
-
self.locale = current_locale if tmp_locale
-
end
-
-
# Merges the given locale, key and scope into a single array of keys.
-
# Splits keys that contain dots into multiple keys. Makes sure all
-
# keys are Symbols.
-
1
def normalize_keys(locale, key, scope, separator = nil)
-
separator ||= I18n.default_separator
-
-
keys = []
-
keys.concat normalize_key(locale, separator)
-
keys.concat normalize_key(scope, separator)
-
keys.concat normalize_key(key, separator)
-
keys
-
end
-
-
# Returns true when the passed locale, which can be either a String or a
-
# Symbol, is in the list of available locales. Returns false otherwise.
-
1
def locale_available?(locale)
-
I18n.config.available_locales_set.include?(locale)
-
end
-
-
# Raises an InvalidLocale exception when the passed locale is not available.
-
1
def enforce_available_locales!(locale)
-
handle_enforce_available_locales_deprecation
-
-
if config.enforce_available_locales
-
raise I18n::InvalidLocale.new(locale) if !locale_available?(locale)
-
end
-
end
-
-
# making these private until Ruby 1.9.2 can send to protected methods again
-
# see http://redmine.ruby-lang.org/repositories/revision/ruby-19?rev=24280
-
1
private
-
-
# Any exceptions thrown in translate will be sent to the @@exception_handler
-
# which can be a Symbol, a Proc or any other Object unless they're forced to
-
# be raised or thrown (MissingTranslation).
-
#
-
# If exception_handler is a Symbol then it will simply be sent to I18n as
-
# a method call. A Proc will simply be called. In any other case the
-
# method #call will be called on the exception_handler object.
-
#
-
# Examples:
-
#
-
# I18n.exception_handler = :default_exception_handler # this is the default
-
# I18n.default_exception_handler(exception, locale, key, options) # will be called like this
-
#
-
# I18n.exception_handler = lambda { |*args| ... } # a lambda
-
# I18n.exception_handler.call(exception, locale, key, options) # will be called like this
-
#
-
# I18n.exception_handler = I18nExceptionHandler.new # an object
-
# I18n.exception_handler.call(exception, locale, key, options) # will be called like this
-
1
def handle_exception(handling, exception, locale, key, options)
-
case handling
-
when :raise
-
raise(exception.respond_to?(:to_exception) ? exception.to_exception : exception)
-
when :throw
-
throw :exception, exception
-
else
-
case handler = options[:exception_handler] || config.exception_handler
-
when Symbol
-
send(handler, exception, locale, key, options)
-
else
-
handler.call(exception, locale, key, options)
-
end
-
end
-
end
-
-
1
def normalize_key(key, separator)
-
normalized_key_cache[separator][key] ||=
-
case key
-
when Array
-
key.map { |k| normalize_key(k, separator) }.flatten
-
else
-
keys = key.to_s.split(separator)
-
keys.delete('')
-
keys.map! { |k| k.to_sym }
-
keys
-
end
-
end
-
-
1
def normalized_key_cache
-
@normalized_key_cache ||= Hash.new { |h,k| h[k] = {} }
-
end
-
-
# DEPRECATED. Use I18n.normalize_keys instead.
-
1
def normalize_translation_keys(locale, key, scope, separator = nil)
-
puts "I18n.normalize_translation_keys is deprecated. Please use the class I18n.normalize_keys instead."
-
normalize_keys(locale, key, scope, separator)
-
end
-
-
# DEPRECATED. Please use the I18n::ExceptionHandler class instead.
-
1
def default_exception_handler(exception, locale, key, options)
-
puts "I18n.default_exception_handler is deprecated. Please use the class I18n::ExceptionHandler instead " +
-
"(an instance of which is set to I18n.exception_handler by default)."
-
exception.is_a?(MissingTranslation) ? exception.message : raise(exception)
-
end
-
-
1
def handle_enforce_available_locales_deprecation
-
if config.enforce_available_locales.nil? && !defined?(@unenforced_available_locales_deprecation)
-
$stderr.puts "[deprecated] I18n.enforce_available_locales will default to true in the future. If you really want to skip validation of your locale you can set I18n.enforce_available_locales = false to avoid this message."
-
@unenforced_available_locales_deprecation = true
-
end
-
end
-
})
-
end
-
1
module I18n
-
1
module Backend
-
1
autoload :Base, 'i18n/backend/base'
-
1
autoload :InterpolationCompiler, 'i18n/backend/interpolation_compiler'
-
1
autoload :Cache, 'i18n/backend/cache'
-
1
autoload :Cascade, 'i18n/backend/cascade'
-
1
autoload :Chain, 'i18n/backend/chain'
-
1
autoload :Fallbacks, 'i18n/backend/fallbacks'
-
1
autoload :Flatten, 'i18n/backend/flatten'
-
1
autoload :Gettext, 'i18n/backend/gettext'
-
1
autoload :KeyValue, 'i18n/backend/key_value'
-
1
autoload :Memoize, 'i18n/backend/memoize'
-
1
autoload :Metadata, 'i18n/backend/metadata'
-
1
autoload :Pluralization, 'i18n/backend/pluralization'
-
1
autoload :Simple, 'i18n/backend/simple'
-
1
autoload :Transliterator, 'i18n/backend/transliterator'
-
end
-
end
-
1
require 'yaml'
-
1
require 'i18n/core_ext/hash'
-
1
require 'i18n/core_ext/kernel/suppress_warnings'
-
-
1
module I18n
-
1
module Backend
-
1
module Base
-
1
include I18n::Backend::Transliterator
-
-
# Accepts a list of paths to translation files. Loads translations from
-
# plain Ruby (*.rb) or YAML files (*.yml). See #load_rb and #load_yml
-
# for details.
-
1
def load_translations(*filenames)
-
filenames = I18n.load_path if filenames.empty?
-
filenames.flatten.each { |filename| load_file(filename) }
-
end
-
-
# This method receives a locale, a data hash and options for storing translations.
-
# Should be implemented
-
1
def store_translations(locale, data, options = {})
-
raise NotImplementedError
-
end
-
-
1
def translate(locale, key, options = {})
-
raise InvalidLocale.new(locale) unless locale
-
entry = key && lookup(locale, key, options[:scope], options)
-
-
if options.empty?
-
entry = resolve(locale, key, entry, options)
-
else
-
count, default = options.values_at(:count, :default)
-
values = options.except(*RESERVED_KEYS)
-
entry = entry.nil? && default ?
-
default(locale, key, default, options) : resolve(locale, key, entry, options)
-
end
-
-
throw(:exception, I18n::MissingTranslation.new(locale, key, options)) if entry.nil?
-
entry = entry.dup if entry.is_a?(String)
-
-
entry = pluralize(locale, entry, count) if count
-
entry = interpolate(locale, entry, values) if values
-
entry
-
end
-
-
1
def exists?(locale, key)
-
lookup(locale, key) != nil
-
end
-
-
# Acts the same as +strftime+, but uses a localized version of the
-
# format string. Takes a key from the date/time formats translations as
-
# a format argument (<em>e.g.</em>, <tt>:short</tt> in <tt>:'date.formats'</tt>).
-
1
def localize(locale, object, format = :default, options = {})
-
raise ArgumentError, "Object must be a Date, DateTime or Time object. #{object.inspect} given." unless object.respond_to?(:strftime)
-
-
if Symbol === format
-
key = format
-
type = object.respond_to?(:sec) ? 'time' : 'date'
-
options = options.merge(:raise => true, :object => object, :locale => locale)
-
format = I18n.t(:"#{type}.formats.#{key}", options)
-
end
-
-
# format = resolve(locale, object, format, options)
-
format = format.to_s.gsub(/%[aAbBpP]/) do |match|
-
case match
-
when '%a' then I18n.t(:"date.abbr_day_names", :locale => locale, :format => format)[object.wday]
-
when '%A' then I18n.t(:"date.day_names", :locale => locale, :format => format)[object.wday]
-
when '%b' then I18n.t(:"date.abbr_month_names", :locale => locale, :format => format)[object.mon]
-
when '%B' then I18n.t(:"date.month_names", :locale => locale, :format => format)[object.mon]
-
when '%p' then I18n.t(:"time.#{object.hour < 12 ? :am : :pm}", :locale => locale, :format => format).upcase if object.respond_to? :hour
-
when '%P' then I18n.t(:"time.#{object.hour < 12 ? :am : :pm}", :locale => locale, :format => format).downcase if object.respond_to? :hour
-
end
-
end
-
-
object.strftime(format)
-
end
-
-
# Returns an array of locales for which translations are available
-
# ignoring the reserved translation meta data key :i18n.
-
1
def available_locales
-
raise NotImplementedError
-
end
-
-
1
def reload!
-
1
@skip_syntax_deprecation = false
-
end
-
-
1
protected
-
-
# The method which actually looks up for the translation in the store.
-
1
def lookup(locale, key, scope = [], options = {})
-
raise NotImplementedError
-
end
-
-
# Evaluates defaults.
-
# If given subject is an Array, it walks the array and returns the
-
# first translation that can be resolved. Otherwise it tries to resolve
-
# the translation directly.
-
1
def default(locale, object, subject, options = {})
-
options = options.dup.reject { |key, value| key == :default }
-
case subject
-
when Array
-
subject.each do |item|
-
result = resolve(locale, object, item, options) and return result
-
end and nil
-
else
-
resolve(locale, object, subject, options)
-
end
-
end
-
-
# Resolves a translation.
-
# If the given subject is a Symbol, it will be translated with the
-
# given options. If it is a Proc then it will be evaluated. All other
-
# subjects will be returned directly.
-
1
def resolve(locale, object, subject, options = {})
-
return subject if options[:resolve] == false
-
result = catch(:exception) do
-
case subject
-
when Symbol
-
I18n.translate(subject, options.merge(:locale => locale, :throw => true))
-
when Proc
-
date_or_time = options.delete(:object) || object
-
resolve(locale, object, subject.call(date_or_time, options))
-
else
-
subject
-
end
-
end
-
result unless result.is_a?(MissingTranslation)
-
end
-
-
# Picks a translation from a pluralized mnemonic subkey according to English
-
# pluralization rules :
-
# - It will pick the :one subkey if count is equal to 1.
-
# - It will pick the :other subkey otherwise.
-
# - It will pick the :zero subkey in the special case where count is
-
# equal to 0 and there is a :zero subkey present. This behaviour is
-
# not stand with regards to the CLDR pluralization rules.
-
# Other backends can implement more flexible or complex pluralization rules.
-
1
def pluralize(locale, entry, count)
-
return entry unless entry.is_a?(Hash) && count
-
-
key = :zero if count == 0 && entry.has_key?(:zero)
-
key ||= count == 1 ? :one : :other
-
raise InvalidPluralizationData.new(entry, count) unless entry.has_key?(key)
-
entry[key]
-
end
-
-
# Interpolates values into a given string.
-
#
-
# interpolate "file %{file} opened by %%{user}", :file => 'test.txt', :user => 'Mr. X'
-
# # => "file test.txt opened by %{user}"
-
1
def interpolate(locale, string, values = {})
-
if string.is_a?(::String) && !values.empty?
-
I18n.interpolate(string, values)
-
else
-
string
-
end
-
end
-
-
# Loads a single translations file by delegating to #load_rb or
-
# #load_yml depending on the file extension and directly merges the
-
# data to the existing translations. Raises I18n::UnknownFileType
-
# for all other file extensions.
-
1
def load_file(filename)
-
type = File.extname(filename).tr('.', '').downcase
-
raise UnknownFileType.new(type, filename) unless respond_to?(:"load_#{type}", true)
-
data = send(:"load_#{type}", filename)
-
unless data.is_a?(Hash)
-
raise InvalidLocaleData.new(filename, 'expects it to return a hash, but does not')
-
end
-
data.each { |locale, d| store_translations(locale, d || {}) }
-
end
-
-
# Loads a plain Ruby translations file. eval'ing the file must yield
-
# a Hash containing translation data with locales as toplevel keys.
-
1
def load_rb(filename)
-
eval(IO.read(filename), binding, filename)
-
end
-
-
# Loads a YAML translations file. The data must have locales as
-
# toplevel keys.
-
1
def load_yml(filename)
-
begin
-
YAML.load_file(filename)
-
rescue TypeError, ScriptError, StandardError => e
-
raise InvalidLocaleData.new(filename, e.inspect)
-
end
-
end
-
end
-
end
-
end
-
1
module I18n
-
1
module Backend
-
# A simple backend that reads translations from YAML files and stores them in
-
# an in-memory hash. Relies on the Base backend.
-
#
-
# The implementation is provided by a Implementation module allowing to easily
-
# extend Simple backend's behavior by including modules. E.g.:
-
#
-
# module I18n::Backend::Pluralization
-
# def pluralize(*args)
-
# # extended pluralization logic
-
# super
-
# end
-
# end
-
#
-
# I18n::Backend::Simple.include(I18n::Backend::Pluralization)
-
1
class Simple
-
3
(class << self; self; end).class_eval { public :include }
-
-
1
module Implementation
-
1
include Base
-
-
1
def initialized?
-
@initialized ||= false
-
end
-
-
# Stores translations for the given locale in memory.
-
# This uses a deep merge for the translations hash, so existing
-
# translations will be overwritten by new ones only at the deepest
-
# level of the hash.
-
1
def store_translations(locale, data, options = {})
-
locale = locale.to_sym
-
translations[locale] ||= {}
-
data = data.deep_symbolize_keys
-
translations[locale].deep_merge!(data)
-
end
-
-
# Get available locales from the translations hash
-
1
def available_locales
-
init_translations unless initialized?
-
translations.inject([]) do |locales, (locale, data)|
-
locales << locale unless (data.keys - [:i18n]).empty?
-
locales
-
end
-
end
-
-
# Clean up translations hash and set initialized to false on reload!
-
1
def reload!
-
1
@initialized = false
-
1
@translations = nil
-
1
super
-
end
-
-
1
protected
-
-
1
def init_translations
-
load_translations
-
@initialized = true
-
end
-
-
1
def translations
-
@translations ||= {}
-
end
-
-
# Looks up a translation from the translations hash. Returns nil if
-
# eiher key is nil, or locale, scope or key do not exist as a key in the
-
# nested translations hash. Splits keys or scopes containing dots
-
# into multiple keys, i.e. <tt>currency.format</tt> is regarded the same as
-
# <tt>%w(currency format)</tt>.
-
1
def lookup(locale, key, scope = [], options = {})
-
init_translations unless initialized?
-
keys = I18n.normalize_keys(locale, key, scope, options[:separator])
-
-
keys.inject(translations) do |result, _key|
-
_key = _key.to_sym
-
return nil unless result.is_a?(Hash) && result.has_key?(_key)
-
result = result[_key]
-
result = resolve(locale, _key, result, options.merge(:scope => nil)) if result.is_a?(Symbol)
-
result
-
end
-
end
-
end
-
-
1
include Implementation
-
end
-
end
-
end
-
# encoding: utf-8
-
1
module I18n
-
1
module Backend
-
1
module Transliterator
-
1
DEFAULT_REPLACEMENT_CHAR = "?"
-
-
# Given a locale and a UTF-8 string, return the locale's ASCII
-
# approximation for the string.
-
1
def transliterate(locale, string, replacement = nil)
-
@transliterators ||= {}
-
@transliterators[locale] ||= Transliterator.get I18n.t(:'i18n.transliterate.rule',
-
:locale => locale, :resolve => false, :default => {})
-
@transliterators[locale].transliterate(string, replacement)
-
end
-
-
# Get a transliterator instance.
-
1
def self.get(rule = nil)
-
if !rule || rule.kind_of?(Hash)
-
HashTransliterator.new(rule)
-
elsif rule.kind_of? Proc
-
ProcTransliterator.new(rule)
-
else
-
raise I18n::ArgumentError, "Transliteration rule must be a proc or a hash."
-
end
-
end
-
-
# A transliterator which accepts a Proc as its transliteration rule.
-
1
class ProcTransliterator
-
1
def initialize(rule)
-
@rule = rule
-
end
-
-
1
def transliterate(string, replacement = nil)
-
@rule.call(string)
-
end
-
end
-
-
# A transliterator which accepts a Hash of characters as its translation
-
# rule.
-
1
class HashTransliterator
-
1
DEFAULT_APPROXIMATIONS = {
-
"À"=>"A", "Á"=>"A", "Â"=>"A", "Ã"=>"A", "Ä"=>"A", "Å"=>"A", "Æ"=>"AE",
-
"Ç"=>"C", "È"=>"E", "É"=>"E", "Ê"=>"E", "Ë"=>"E", "Ì"=>"I", "Í"=>"I",
-
"Î"=>"I", "Ï"=>"I", "Ð"=>"D", "Ñ"=>"N", "Ò"=>"O", "Ó"=>"O", "Ô"=>"O",
-
"Õ"=>"O", "Ö"=>"O", "×"=>"x", "Ø"=>"O", "Ù"=>"U", "Ú"=>"U", "Û"=>"U",
-
"Ü"=>"U", "Ý"=>"Y", "Þ"=>"Th", "ß"=>"ss", "à"=>"a", "á"=>"a", "â"=>"a",
-
"ã"=>"a", "ä"=>"a", "å"=>"a", "æ"=>"ae", "ç"=>"c", "è"=>"e", "é"=>"e",
-
"ê"=>"e", "ë"=>"e", "ì"=>"i", "í"=>"i", "î"=>"i", "ï"=>"i", "ð"=>"d",
-
"ñ"=>"n", "ò"=>"o", "ó"=>"o", "ô"=>"o", "õ"=>"o", "ö"=>"o", "ø"=>"o",
-
"ù"=>"u", "ú"=>"u", "û"=>"u", "ü"=>"u", "ý"=>"y", "þ"=>"th", "ÿ"=>"y",
-
"Ā"=>"A", "ā"=>"a", "Ă"=>"A", "ă"=>"a", "Ą"=>"A", "ą"=>"a", "Ć"=>"C",
-
"ć"=>"c", "Ĉ"=>"C", "ĉ"=>"c", "Ċ"=>"C", "ċ"=>"c", "Č"=>"C", "č"=>"c",
-
"Ď"=>"D", "ď"=>"d", "Đ"=>"D", "đ"=>"d", "Ē"=>"E", "ē"=>"e", "Ĕ"=>"E",
-
"ĕ"=>"e", "Ė"=>"E", "ė"=>"e", "Ę"=>"E", "ę"=>"e", "Ě"=>"E", "ě"=>"e",
-
"Ĝ"=>"G", "ĝ"=>"g", "Ğ"=>"G", "ğ"=>"g", "Ġ"=>"G", "ġ"=>"g", "Ģ"=>"G",
-
"ģ"=>"g", "Ĥ"=>"H", "ĥ"=>"h", "Ħ"=>"H", "ħ"=>"h", "Ĩ"=>"I", "ĩ"=>"i",
-
"Ī"=>"I", "ī"=>"i", "Ĭ"=>"I", "ĭ"=>"i", "Į"=>"I", "į"=>"i", "İ"=>"I",
-
"ı"=>"i", "IJ"=>"IJ", "ij"=>"ij", "Ĵ"=>"J", "ĵ"=>"j", "Ķ"=>"K", "ķ"=>"k",
-
"ĸ"=>"k", "Ĺ"=>"L", "ĺ"=>"l", "Ļ"=>"L", "ļ"=>"l", "Ľ"=>"L", "ľ"=>"l",
-
"Ŀ"=>"L", "ŀ"=>"l", "Ł"=>"L", "ł"=>"l", "Ń"=>"N", "ń"=>"n", "Ņ"=>"N",
-
"ņ"=>"n", "Ň"=>"N", "ň"=>"n", "ʼn"=>"'n", "Ŋ"=>"NG", "ŋ"=>"ng",
-
"Ō"=>"O", "ō"=>"o", "Ŏ"=>"O", "ŏ"=>"o", "Ő"=>"O", "ő"=>"o", "Œ"=>"OE",
-
"œ"=>"oe", "Ŕ"=>"R", "ŕ"=>"r", "Ŗ"=>"R", "ŗ"=>"r", "Ř"=>"R", "ř"=>"r",
-
"Ś"=>"S", "ś"=>"s", "Ŝ"=>"S", "ŝ"=>"s", "Ş"=>"S", "ş"=>"s", "Š"=>"S",
-
"š"=>"s", "Ţ"=>"T", "ţ"=>"t", "Ť"=>"T", "ť"=>"t", "Ŧ"=>"T", "ŧ"=>"t",
-
"Ũ"=>"U", "ũ"=>"u", "Ū"=>"U", "ū"=>"u", "Ŭ"=>"U", "ŭ"=>"u", "Ů"=>"U",
-
"ů"=>"u", "Ű"=>"U", "ű"=>"u", "Ų"=>"U", "ų"=>"u", "Ŵ"=>"W", "ŵ"=>"w",
-
"Ŷ"=>"Y", "ŷ"=>"y", "Ÿ"=>"Y", "Ź"=>"Z", "ź"=>"z", "Ż"=>"Z", "ż"=>"z",
-
"Ž"=>"Z", "ž"=>"z"
-
}.freeze
-
-
1
def initialize(rule = nil)
-
@rule = rule
-
add DEFAULT_APPROXIMATIONS.dup
-
add rule if rule
-
end
-
-
1
def transliterate(string, replacement = nil)
-
string.gsub(/[^\x00-\x7f]/u) do |char|
-
approximations[char] || replacement || DEFAULT_REPLACEMENT_CHAR
-
end
-
end
-
-
1
private
-
-
1
def approximations
-
@approximations ||= {}
-
end
-
-
# Add transliteration rules to the approximations hash.
-
1
def add(hash)
-
hash.each do |key, value|
-
approximations[key.to_s] = value.to_s
-
end
-
end
-
end
-
end
-
end
-
end
-
1
class Hash
-
def slice(*keep_keys)
-
h = {}
-
keep_keys.each { |key| h[key] = fetch(key) }
-
h
-
1
end unless Hash.method_defined?(:slice)
-
-
def except(*less_keys)
-
slice(*keys - less_keys)
-
1
end unless Hash.method_defined?(:except)
-
-
def deep_symbolize_keys
-
inject({}) { |result, (key, value)|
-
value = value.deep_symbolize_keys if value.is_a?(Hash)
-
result[(key.to_sym rescue key) || key] = value
-
result
-
}
-
1
end unless Hash.method_defined?(:deep_symbolize_keys)
-
-
# deep_merge_hash! by Stefan Rusterholz, see http://www.ruby-forum.com/topic/142809
-
1
MERGER = proc do |key, v1, v2|
-
Hash === v1 && Hash === v2 ? v1.merge(v2, &MERGER) : v2
-
end
-
-
def deep_merge!(data)
-
merge!(data, &MERGER)
-
1
end unless Hash.method_defined?(:deep_merge!)
-
end
-
-
1
module Kernel
-
1
def suppress_warnings
-
original_verbosity, $VERBOSE = $VERBOSE, nil
-
yield
-
ensure
-
$VERBOSE = original_verbosity
-
end
-
end
-
1
require 'cgi'
-
-
1
module I18n
-
# Handles exceptions raised in the backend. All exceptions except for
-
# MissingTranslationData exceptions are re-thrown. When a MissingTranslationData
-
# was caught the handler returns an error message string containing the key/scope.
-
# Note that the exception handler is not called when the option :throw was given.
-
1
class ExceptionHandler
-
1
include Module.new {
-
1
def call(exception, locale, key, options)
-
if exception.is_a?(MissingTranslation)
-
#
-
# TODO: this block is to be replaced by `exception.message` when
-
# rescue_format is removed
-
if options[:rescue_format] == :html
-
if !defined?(@rescue_format_deprecation)
-
$stderr.puts "[DEPRECATED] I18n's :recue_format option will be removed from a future release. All exception messages will be plain text. If you need the exception handler to return an html format please set or pass a custom exception handler."
-
@rescue_format_deprecation = true
-
end
-
exception.html_message
-
else
-
exception.message
-
end
-
-
elsif exception.is_a?(Exception)
-
raise exception
-
else
-
throw :exception, exception
-
end
-
end
-
}
-
end
-
-
1
class ArgumentError < ::ArgumentError; end
-
-
1
class InvalidLocale < ArgumentError
-
1
attr_reader :locale
-
1
def initialize(locale)
-
@locale = locale
-
super "#{locale.inspect} is not a valid locale"
-
end
-
end
-
-
1
class InvalidLocaleData < ArgumentError
-
1
attr_reader :filename
-
1
def initialize(filename, exception_message)
-
@filename, @exception_message = filename, exception_message
-
super "can not load translations from #{filename}: #{exception_message}"
-
end
-
end
-
-
1
class MissingTranslation
-
1
module Base
-
1
attr_reader :locale, :key, :options
-
-
1
def initialize(locale, key, options = nil)
-
@key, @locale, @options = key, locale, options.dup || {}
-
options.each { |k, v| self.options[k] = v.inspect if v.is_a?(Proc) }
-
end
-
-
1
def html_message
-
key = CGI.escapeHTML titleize(keys.last)
-
path = CGI.escapeHTML keys.join('.')
-
%(<span class="translation_missing" title="translation missing: #{path}">#{key}</span>)
-
end
-
-
1
def keys
-
@keys ||= I18n.normalize_keys(locale, key, options[:scope]).tap do |keys|
-
keys << 'no key' if keys.size < 2
-
end
-
end
-
-
1
def message
-
"translation missing: #{keys.join('.')}"
-
end
-
1
alias :to_s :message
-
-
1
def to_exception
-
MissingTranslationData.new(locale, key, options)
-
end
-
-
1
protected
-
-
# TODO : remove when #html_message is removed
-
1
def titleize(key)
-
key.to_s.gsub('_', ' ').gsub(/\b('?[a-z])/) { $1.capitalize }
-
end
-
end
-
-
1
include Base
-
end
-
-
1
class MissingTranslationData < ArgumentError
-
1
include MissingTranslation::Base
-
end
-
-
1
class InvalidPluralizationData < ArgumentError
-
1
attr_reader :entry, :count
-
1
def initialize(entry, count)
-
@entry, @count = entry, count
-
super "translation data #{entry.inspect} can not be used with :count => #{count}"
-
end
-
end
-
-
1
class MissingInterpolationArgument < ArgumentError
-
1
attr_reader :key, :values, :string
-
1
def initialize(key, values, string)
-
@key, @values, @string = key, values, string
-
super "missing interpolation argument #{key.inspect} in #{string.inspect} (#{values.inspect} given)"
-
end
-
end
-
-
1
class ReservedInterpolationKey < ArgumentError
-
1
attr_reader :key, :string
-
1
def initialize(key, string)
-
@key, @string = key, string
-
super "reserved key #{key.inspect} used in #{string.inspect}"
-
end
-
end
-
-
1
class UnknownFileType < ArgumentError
-
1
attr_reader :type, :filename
-
1
def initialize(type, filename)
-
@type, @filename = type, filename
-
super "can not load translations from #{filename}, the file type #{type} is not known"
-
end
-
end
-
end
-
# heavily based on Masao Mutoh's gettext String interpolation extension
-
# http://github.com/mutoh/gettext/blob/f6566738b981fe0952548c421042ad1e0cdfb31e/lib/gettext/core_ext/string.rb
-
-
1
module I18n
-
1
INTERPOLATION_PATTERN = Regexp.union(
-
/%%/,
-
/%\{(\w+)\}/, # matches placeholders like "%{foo}"
-
/%<(\w+)>(.*?\d*\.?\d*[bBdiouxXeEfgGcps])/ # matches placeholders like "%<foo>.d"
-
)
-
-
1
class << self
-
# Return String or raises MissingInterpolationArgument exception.
-
# Missing argument's logic is handled by I18n.config.missing_interpolation_argument_handler.
-
1
def interpolate(string, values)
-
raise ReservedInterpolationKey.new($1.to_sym, string) if string =~ RESERVED_KEYS_PATTERN
-
raise ArgumentError.new('Interpolation values must be a Hash.') unless values.kind_of?(Hash)
-
interpolate_hash(string, values)
-
end
-
-
1
def interpolate_hash(string, values)
-
string.gsub(INTERPOLATION_PATTERN) do |match|
-
if match == '%%'
-
'%'
-
else
-
key = ($1 || $2).to_sym
-
value = if values.key?(key)
-
values[key]
-
else
-
config.missing_interpolation_argument_handler.call(key, values, string)
-
end
-
value = value.call(values) if value.respond_to?(:call)
-
$3 ? sprintf("%#{$3}", value) : value
-
end
-
end
-
end
-
end
-
end
-
1
module I18n
-
1
VERSION = "0.6.11"
-
end
-
1
require 'faraday'
-
-
# @private
-
1
module FaradayMiddleware
-
# @private
-
1
class InstagramOAuth2 < Faraday::Middleware
-
1
def call(env)
-
-
if env[:method] == :get or env[:method] == :delete
-
if env[:url].query.nil?
-
query = {}
-
else
-
query = Faraday::Utils.parse_query(env[:url].query)
-
end
-
-
if @access_token and not query["client_secret"]
-
env[:url].query = Faraday::Utils.build_query(query.merge(:access_token => @access_token))
-
env[:request_headers] = env[:request_headers].merge('Authorization' => "Token token=\"#{@access_token}\"")
-
elsif @client_id
-
env[:url].query = Faraday::Utils.build_query(query.merge(:client_id => @client_id))
-
end
-
else
-
if @access_token and not env[:body] && env[:body][:client_secret]
-
env[:body] = {} if env[:body].nil?
-
env[:body] = env[:body].merge(:access_token => @access_token)
-
env[:request_headers] = env[:request_headers].merge('Authorization' => "Token token=\"#{@access_token}\"")
-
elsif @client_id
-
env[:body] = env[:body].merge(:client_id => @client_id)
-
end
-
end
-
-
-
@app.call env
-
end
-
-
1
def initialize(app, client_id, access_token=nil)
-
@app = app
-
@client_id = client_id
-
@access_token = access_token
-
end
-
end
-
end
-
1
require 'faraday'
-
-
# @private
-
1
module FaradayMiddleware
-
# @private
-
1
class RaiseHttpException < Faraday::Middleware
-
1
def call(env)
-
@app.call(env).on_complete do |response|
-
case response[:status].to_i
-
when 400
-
raise Instagram::BadRequest, error_message_400(response)
-
when 404
-
raise Instagram::NotFound, error_message_400(response)
-
when 429
-
raise Instagram::TooManyRequests, error_message_400(response)
-
when 500
-
raise Instagram::InternalServerError, error_message_500(response, "Something is technically wrong.")
-
when 502
-
raise Instagram::BadGateway, error_message_500(response, "The server returned an invalid or incomplete response.")
-
when 503
-
raise Instagram::ServiceUnavailable, error_message_500(response, "Instagram is rate limiting your requests.")
-
when 504
-
raise Instagram::GatewayTimeout, error_message_500(response, "504 Gateway Time-out")
-
end
-
end
-
end
-
-
1
def initialize(app)
-
super app
-
@parser = nil
-
end
-
-
1
private
-
-
1
def error_message_400(response)
-
"#{response[:method].to_s.upcase} #{response[:url].to_s}: #{response[:status]}#{error_body(response[:body])}"
-
end
-
-
1
def error_body(body)
-
# body gets passed as a string, not sure if it is passed as something else from other spots?
-
if not body.nil? and not body.empty? and body.kind_of?(String)
-
# removed multi_json thanks to wesnolte's commit
-
body = ::JSON.parse(body)
-
end
-
-
if body.nil?
-
nil
-
elsif body['meta'] and body['meta']['error_message'] and not body['meta']['error_message'].empty?
-
": #{body['meta']['error_message']}"
-
elsif body['error_message'] and not body['error_message'].empty?
-
": #{body['error_type']}: #{body['error_message']}"
-
end
-
end
-
-
1
def error_message_500(response, body=nil)
-
"#{response[:method].to_s.upcase} #{response[:url].to_s}: #{[response[:status].to_s + ':', body].compact.join(' ')}"
-
end
-
end
-
end
-
1
require File.expand_path('../instagram/error', __FILE__)
-
1
require File.expand_path('../instagram/configuration', __FILE__)
-
1
require File.expand_path('../instagram/api', __FILE__)
-
1
require File.expand_path('../instagram/client', __FILE__)
-
1
require File.expand_path('../instagram/response', __FILE__)
-
-
1
module Instagram
-
1
extend Configuration
-
-
# Alias for Instagram::Client.new
-
#
-
# @return [Instagram::Client]
-
1
def self.client(options={})
-
Instagram::Client.new(options)
-
end
-
-
# Delegate to Instagram::Client
-
1
def self.method_missing(method, *args, &block)
-
return super unless client.respond_to?(method)
-
client.send(method, *args, &block)
-
end
-
-
# Delegate to Instagram::Client
-
1
def self.respond_to?(method, include_all=false)
-
return client.respond_to?(method, include_all) || super
-
end
-
end
-
1
require File.expand_path('../connection', __FILE__)
-
1
require File.expand_path('../request', __FILE__)
-
1
require File.expand_path('../oauth', __FILE__)
-
-
1
module Instagram
-
# @private
-
1
class API
-
# @private
-
1
attr_accessor *Configuration::VALID_OPTIONS_KEYS
-
-
# Creates a new API
-
1
def initialize(options={})
-
options = Instagram.options.merge(options)
-
Configuration::VALID_OPTIONS_KEYS.each do |key|
-
send("#{key}=", options[key])
-
end
-
end
-
-
1
def config
-
conf = {}
-
Configuration::VALID_OPTIONS_KEYS.each do |key|
-
conf[key] = send key
-
end
-
conf
-
end
-
-
1
include Connection
-
1
include Request
-
1
include OAuth
-
end
-
end
-
1
module Instagram
-
# Wrapper for the Instagram REST API
-
#
-
# @note All methods have been separated into modules and follow the same grouping used in http://instagram.com/developer/
-
# @see http://instagram.com/developer/
-
1
class Client < API
-
11
Dir[File.expand_path('../client/*.rb', __FILE__)].each{|f| require f}
-
-
1
include Instagram::Client::Utils
-
-
1
include Instagram::Client::Users
-
1
include Instagram::Client::Media
-
1
include Instagram::Client::Locations
-
1
include Instagram::Client::Geographies
-
1
include Instagram::Client::Tags
-
1
include Instagram::Client::Comments
-
1
include Instagram::Client::Likes
-
1
include Instagram::Client::Subscriptions
-
1
include Instagram::Client::Embedding
-
end
-
end
-
1
module Instagram
-
1
class Client
-
# Defines methods related to comments
-
1
module Comments
-
# Returns a list of comments for a given media item ID
-
#
-
# @overload media_comments(id)
-
# @param id [Integer] An Instagram media item ID
-
# @return [Hashie::Mash] The requested comments.
-
# @example Returns a list of comments for the media item of ID 1234
-
# Instagram.media_comments(777)
-
# @format :json
-
# @authenticated true
-
#
-
# If getting this data of a protected user, you must be authenticated (and be allowed to see that user).
-
# @rate_limited true
-
# @see http://instagram.com/developer/endpoints/comments/#get_media_comments
-
1
def media_comments(id, *args)
-
response = get("media/#{id}/comments")
-
response
-
end
-
-
# Creates a comment for a given media item ID
-
#
-
# @overload create_media_comment(id, text)
-
# @param id [Integer] An Instagram media item ID
-
# @param text [String] The text of your comment
-
# @return [Hashie::Mash] The comment created.
-
# @example Creates a new comment on media item with ID 777
-
# Instagram.create_media_comment(777, "Oh noes!")
-
# @format :json
-
# @authenticated true
-
#
-
# If getting this data of a protected user, you must be authenticated (and be allowed to see that user).
-
# @rate_limited true
-
# @see http://instagram.com/developer/endpoints/comments/#post_media_comments
-
1
def create_media_comment(id, text, options={})
-
response = post("media/#{id}/comments", options.merge(:text => text), signature=true)
-
response
-
end
-
-
# Deletes a comment for a given media item ID
-
#
-
# @overload delete_media_comment(media_id, comment_id)
-
# @param media_id [Integer] An Instagram media item ID.
-
# @param comment_id [Integer] Your comment ID of the comment you wish to delete.
-
# @return [nil]
-
# @example Delete the comment with ID of 1234, on the media item with ID of 777
-
# Instagram.delete_media_comment(777, 1234)
-
# @format :json
-
# @authenticated true
-
#
-
# In order to remove a comment, you must be the owner of the comment, the media item, or both.
-
# @rate_limited true
-
# @see http://instagram.com/developer/endpoints/comments/#delete_media_comments
-
1
def delete_media_comment(media_id, comment_id, options={})
-
response = delete("media/#{media_id}/comments/#{comment_id}", options, signature=true)
-
response
-
end
-
end
-
end
-
end
-
1
module Instagram
-
1
class Client
-
# Defines methods related to embedding
-
1
module Embedding
-
# Returns information about the media associated with the given short link
-
#
-
# @overload oembed(url=nil, options={})
-
# @param url [String] An instagram short link
-
# @param options [Hash] A customizable set of options
-
# @option options [Integer] :maxheight Maximum height of returned media
-
# @option options [Integer] :maxwidth Maximum width of returned media
-
# @option options [Integer] :callback A JSON callback to be invoked
-
# @return [Hashie::Mash] Information about the media associated with given short link
-
# @example Return information about the media associated with http://instagr.am/p/BUG/
-
# Instagram.oembed(http://instagr.am/p/BUG/)
-
#
-
# @see http://instagram.com/developer/embedding/#oembed
-
# @format :json
-
# @authenticated false
-
# @rate_limited true
-
1
def oembed(*args)
-
url = args.first
-
return nil unless url
-
get("oembed?url=#{url}", {}, false, false, true)
-
end
-
end
-
end
-
end
-
1
module Instagram
-
1
class Client
-
# Defines methods related to real-time geographies
-
1
module Geographies
-
# Returns a list of recent media items for a given real-time geography
-
#
-
# @overload geography_recent_media(id, options={})
-
# @param user [Integer] A geography ID from a real-time subscription.
-
# @param options [Hash] A customizable set of options.
-
# @option options [Integer] :count (nil) Limit the number of results returned
-
# @option options [Integer] :min_id (nil) Return media before this min_id
-
# @option options [Integer] :max_id (nil) Return media after this max_id
-
# @option options [Integer] :min_timestamp (nil) Return media after this UNIX timestamp
-
# @option options [Integer] :max_timestamp (nil) Return media before this UNIX timestamp
-
# @return [Hashie::Mash]
-
# @example Return a list of the most recent media items taken within a specific geography
-
# Instagram.geography_recent_media(514276)
-
# @see http://instagram.com/developer/endpoints/geographies/
-
# @format :json
-
# @authenticated false
-
# @rate_limited true
-
1
def geography_recent_media(id, *args)
-
options = args.last.is_a?(Hash) ? args.pop : {}
-
response = get("geographies/#{id}/media/recent", options)
-
response
-
end
-
end
-
end
-
end
-
1
module Instagram
-
1
class Client
-
# Defines methods related to likes
-
1
module Likes
-
# Returns a list of users who like a given media item ID
-
#
-
# @overload media_likes(id)
-
# @param media [Integer] An Instagram media item ID
-
# @return [Hashie::Mash] A list of users.
-
# @example Returns a list of users who like the media item of ID 1234
-
# Instagram.media_likes(777)
-
# @format :json
-
# @authenticated true
-
#
-
# If getting this data of a protected user, you must be authenticated (and be allowed to see that user).
-
# @rate_limited true
-
# @see http://instagram.com/developer/endpoints/likes/#get_media_likes
-
1
def media_likes(id, *args)
-
response = get("media/#{id}/likes")
-
response
-
end
-
-
# Issues a like by the currently authenticated user, for a given media item ID
-
#
-
# @overload like_media(id, text)
-
# @param id [Integer] An Instagram media item ID
-
# @return [Hashie::Mash] Metadata
-
# @example Like media item with ID 777
-
# Instagram.like_media(777)
-
# @format :json
-
# @authenticated true
-
#
-
# If getting this data of a protected user, you must be authenticated (and be allowed to see that user).
-
# @rate_limited true
-
# @see http://instagram.com/developer/endpoints/likes/#post_likes
-
1
def like_media(id, options={})
-
response = post("media/#{id}/likes", options, signature=true)
-
response
-
end
-
-
# Removes the like on a givem media item ID for the currently authenticated user
-
#
-
# @overload unlike_media(id)
-
# @param media_id [Integer] An Instagram media item ID.
-
# @return [Hashie::Mash] Metadata
-
# @example Remove the like for the currently authenticated user on the media item with the ID of 777
-
# Instagram.unlike_media(777)
-
# @format :json
-
# @authenticated true
-
# @rate_limited true
-
# @see http://instagram.com/developer/endpoints/likes/#delete_likes
-
1
def unlike_media(id, options={})
-
response = delete("media/#{id}/likes", options, signature=true)
-
response
-
end
-
end
-
end
-
end
-
1
module Instagram
-
1
class Client
-
# Defines methods related to media items
-
1
module Locations
-
# Returns extended information of a given Instagram location
-
#
-
# @overload location(id)
-
# @param location [Integer] An Instagram location ID
-
# @return [Hashie::Mash] The requested location.
-
# @example Return extended information for the Instagram office
-
# Instagram.location(514276)
-
# @format :json
-
# @authenticated false
-
# @rate_limited true
-
# @see http://instagram.com/developer/endpoints/locations/#get_locations
-
1
def location(id, *args)
-
response = get("locations/#{id}")
-
response
-
end
-
-
# Returns a list of recent media items for a given Instagram location
-
#
-
# @overload location_recent_media(id, options={})
-
# @param user [Integer] An Instagram location ID.
-
# @param options [Hash] A customizable set of options.
-
# @option options [Integer] :max_id (nil) Returns results with an ID less than (that is, older than) or equal to the specified ID.
-
# @option options [Integer] :count (nil) Limits the number of results returned per page.
-
# @return [Hashie::Mash]
-
# @example Return a list of the most recent media items taken at the Instagram office
-
# Instagram.location_recent_media(514276)
-
# @see http://instagram.com/developer/endpoints/locations/#get_locations_media_recent
-
# @format :json
-
# @authenticated false
-
# @rate_limited true
-
1
def location_recent_media(id, *args)
-
options = args.last.is_a?(Hash) ? args.pop : {}
-
response = get("locations/#{id}/media/recent", options)
-
response
-
end
-
-
# Returns Instagram locations within proximity of given lat,lng or foursquare venue id
-
#
-
# @overload location_search(options={})
-
# @param foursquare_v2_id [String] A valid Foursquare Venue ID (v2)
-
# @param lat [String] A given latitude in decimal format
-
# @param lng [String] A given longitude in decimal format
-
# @option options [Integer] :count The number of media items to retrieve.
-
# @return [Hashie::Mash] location resultm object, #data is an Array.
-
# @example 1: Return a location with the Foursquare Venue ID = ()
-
# Instagram.location_search("3fd66200f964a520c5f11ee3") (Schiller's Liquor Bar, 131 Rivington St., NY, NY 10002)
-
# @example 2: Return locations around 37.7808851, -122.3948632 (164 S Park, SF, CA USA)
-
# Instagram.location_search("37.7808851", "-122.3948632")
-
# @see http://instagram.com/developer/endpoints/locations/#get_locations_search
-
# @format :json
-
# @authenticated false
-
# @rate_limited true
-
1
def location_search(*args)
-
options = args.last.is_a?(Hash) ? args.pop : {}
-
case args.size
-
when 1
-
foursquare_v2_id = args.first
-
response = get('locations/search', options.merge(:foursquare_v2_id => foursquare_v2_id))
-
when 2
-
lat, lng = args
-
response = get('locations/search', options.merge(:lat => lat, :lng => lng))
-
when 3
-
lat, lng, distance = args
-
response = get('locations/search', options.merge(:lat => lat, :lng => lng, :distance => distance))
-
end
-
response
-
end
-
end
-
end
-
end
-
1
module Instagram
-
1
class Client
-
# Defines methods related to media items
-
1
module Media
-
# Returns extended information of a given media item
-
#
-
# @overload media_item(id)
-
# @param user [Integer] An Instagram media item ID
-
# @return [Hashie::Mash] The requested media item.
-
# @example Return extended information for media item 1234
-
# Instagram.media_item(1324)
-
# @format :json
-
# @authenticated false unless requesting media from a protected user
-
#
-
# If getting this data of a protected user, you must authenticate (and be allowed to see that user).
-
# @rate_limited true
-
# @see http://instagram.com/developer/endpoints/media/#get_media
-
1
def media_item(*args)
-
id = args.first || 'self'
-
response = get("media/#{id}")
-
response
-
end
-
-
# Returns a list of the overall most popular media
-
#
-
# @overload media_popular(options={})
-
# @param options [Hash] A customizable set of options.
-
# @return [Hashie::Mash]
-
# @example Returns a list of the overall most popular media
-
# Instagram.media_popular
-
# @see http://instagram.com/developer/endpoints/media/#get_media_popular
-
# @format :json
-
# @authenticated false unless requesting it from a protected user
-
#
-
# If getting this data of a protected user, you must authenticate (and be allowed to see that user).
-
# @rate_limited true
-
1
def media_popular(*args)
-
options = args.last.is_a?(Hash) ? args.pop : {}
-
id = args.first || "self"
-
response = get("media/popular", options)
-
response
-
end
-
-
# Returns media items within proximity of given lat,lng
-
#
-
# @param lat [String] A given latitude in decimal format
-
# @param lng [String] A given longitude in decimal format
-
# @param options [Hash] A customizable set of options.
-
# @option options [Integer] :count The number of media items to retrieve.
-
# @return [Hashie::Mash] A list of matching media
-
# @example Return media around 37.7808851, -122.3948632 (164 S Park, SF, CA USA)
-
# Instagram.media_search("37.7808851", "-122.3948632")
-
# @see http://instagram.com/developer/endpoints/media/#get_media_search
-
# @format :json
-
# @authenticated false
-
# @rate_limited true
-
1
def media_search(lat, lng, options={})
-
response = get('media/search', options.merge(:lat => lat, :lng => lng))
-
response
-
end
-
end
-
end
-
end
-
1
require 'openssl'
-
1
require 'multi_json'
-
-
1
module Instagram
-
1
class Client
-
# Defines methods related to real-time
-
1
module Subscriptions
-
# Returns a list of active real-time subscriptions
-
#
-
# @overload subscriptions(options={})
-
# @return [Hashie::Mash] The list of subscriptions.
-
# @example Returns a list of subscriptions for the authenticated application
-
# Instagram.subscriptions
-
# @format :json
-
# @authenticated true
-
#
-
# Requires client_secret to be set on the client or passed in options
-
# @rate_limited true
-
# @see https://api.instagram.com/developer/realtime/
-
1
def subscriptions(options={})
-
response = get("subscriptions", options.merge(:client_secret => client_secret))
-
response
-
end
-
-
# Creates a real-time subscription
-
#
-
# @overload create_subscription(options={})
-
# @param options [Hash] A set of parameters
-
# @option options [String] :object The object you'd like to subscribe to (user, tag, location or geography)
-
# @option options [String] :callback_url The subscription callback URL
-
# @option options [String] :aspect The aspect of the object you'd like to subscribe to (in this case, "media").
-
# @option options [String, Integer] :object_id When specifying a location or tag use the location's ID or tag name respectively
-
# @option options [String, Float] :lat The center latitude of an area, used when subscribing to a geography object
-
# @option options [String, Float] :lng The center longitude of an area, used when subscribing to a geography object
-
# @option options [String, Integer] :radius The distance in meters you'd like to capture around a given point
-
# @overload create_subscription(object, callback_url, aspect="media", options={})
-
# @param object [String] The object you'd like to subscribe to (user, tag, location or geography)
-
# @param callback_url [String] The subscription callback URL
-
# @param aspect [String] he aspect of the object you'd like to subscribe to (in this case, "media").
-
# @param options [Hash] Addition options and parameters
-
# @option options [String, Integer] :object_id When specifying a location or tag use the location's ID or tag name respectively
-
# @option options [String, Float] :lat The center latitude of an area, used when subscribing to a geography object
-
# @option options [String, Float] :lng The center longitude of an area, used when subscribing to a geography object
-
# @option options [String, Integer] :radius The distance in meters you'd like to capture around a given point
-
#
-
# Note that we only support "media" at this time, but we might support other types of subscriptions in the future.
-
# @return [Hashie::Mash] The subscription created.
-
# @example Creates a new subscription to receive notifications for user media changes.
-
# Instagram.create_subscription("user", "http://example.com/instagram/callback")
-
# @format :json
-
# @authenticated true
-
#
-
# Requires client_secret to be set on the client or passed in options
-
# @rate_limited true
-
# @see https://api.instagram.com/developer/realtime/
-
1
def create_subscription(*args)
-
options = args.last.is_a?(Hash) ? args.pop : {}
-
object = args.shift
-
callback_url = args.shift
-
aspect = args.shift
-
options.tap {|o|
-
o[:object] = object unless object.nil?
-
o[:callback_url] = callback_url unless callback_url.nil?
-
o[:aspect] = aspect || o[:aspect] || "media"
-
}
-
response = post("subscriptions", options.merge(:client_secret => client_secret))
-
response
-
end
-
-
# Deletes a real-time subscription
-
#
-
# @overload delete_subscription(options={})
-
# @param options [Hash] Addition options and parameters
-
# @option options [Integer] :subscription_id The subscription's ID
-
# @option options [String] :object When specified will remove all subscriptions of this object type, unless an :object_id is also specified (user, tag, location or geography)
-
# @option options [String, Integer] :object_id When specifying :object, inlcude an :object_id to only remove subscriptions of that object and object_id
-
# @overload delete_subscription(subscription_id, options={})
-
# @param subscription_id [Integer] The subscription's ID
-
# @param options [Hash] Addition options and parameters
-
# @option options [String] :object When specified will remove all subscriptions of this object type, unless an :object_id is also specified (user, tag, location or geography)
-
# @option options [String, Integer] :object_id When specifying :object, inlcude an :object_id to only remove subscriptions of that object and object_id
-
# @return [Hashie::Mash]
-
# @example Deletes an application's user change subscription
-
# Instagram.delete_subscription(:object => "user")
-
# @format :json
-
# @authenticated true
-
#
-
# Requires client_secret to be set on the client or passed in options
-
# @rate_limited true
-
# @see https://api.instagram.com/developer/realtime/
-
1
def delete_subscription(*args)
-
options = args.last.is_a?(Hash) ? args.pop : {}
-
subscription_id = args.first
-
options.merge!(:id => subscription_id) if subscription_id
-
response = delete("subscriptions", options.merge(:client_secret => client_secret))
-
response
-
end
-
-
# As a security measure (to prevent DDoS attacks), Instagram sends a verification request to your server
-
# after you request a subscription.
-
# This method parses the challenge params and makes sure the call is legitimate.
-
#
-
# @param params the request parameters sent by Instagram. (You can pass in a Rails params hash.)
-
# @param verify_token the verify token sent in the {#subscribe subscription request}, if you provided one
-
#
-
# @yield verify_token if you need to compute the verification token
-
# (for instance, if your callback URL includes a record ID, which you look up
-
# and use to calculate a hash), you can pass meet_challenge a block, which
-
# will receive the verify_token received back from Instagram.
-
#
-
# @return the challenge string to be sent back to Instagram, or false if the request is invalid.
-
1
def meet_challenge(params, verify_token = nil, &verification_block)
-
if params["hub.mode"] == "subscribe" &&
-
# you can make sure this is legitimate through two ways
-
# if your store the token across the calls, you can pass in the token value
-
# and we'll make sure it matches
-
((verify_token && params["hub.verify_token"] == verify_token) ||
-
# alternately, if you sent a specially-constructed value (such as a hash of various secret values)
-
# you can pass in a block, which we'll call with the verify_token sent by Instagram
-
# if it's legit, return anything that evaluates to true; otherwise, return nil or false
-
(verification_block && yield(params["hub.verify_token"])))
-
params["hub.challenge"]
-
else
-
false
-
end
-
end
-
-
# Public: As a security measure, all updates from Instagram are signed using
-
# X-Hub-Signature: sha1=XXXX where XXX is the sha1 of the json payload
-
# using your application secret as the key.
-
#
-
# Example:
-
# # in Rails controller
-
# def receive_update
-
# if Instagram.validate_update(request.body, headers)
-
# ...
-
# else
-
# render text: "not authorized", status: 401
-
# end
-
# end
-
1
def validate_update(body, headers)
-
unless client_secret
-
raise ArgumentError, "client_secret must be set during configure"
-
end
-
-
if request_signature = headers['X-Hub-Signature'] || headers['HTTP_X_HUB_SIGNATURE'] and
-
signature_parts = request_signature.split('sha1=')
-
request_signature = signature_parts[1]
-
calculated_signature = OpenSSL::HMAC.hexdigest('sha1', client_secret, body)
-
calculated_signature == request_signature
-
end
-
end
-
-
# Process a subscription notification JSON payload
-
#
-
# @overload process_subscription(json, &block)
-
# @param json [String] The JSON response received by the Instagram real-time server
-
# @param block [Proc] A callable in which callbacks are defined
-
# @option options [String] :signature Pass in an X-Hub-Signature to use for payload validation
-
# @return [nil]
-
# @example Process and handle a notification for a user media change
-
# Instagram.process_subscription(params[:body]) do |handler|
-
#
-
# handler.on_user_changed do |user_id, data|
-
#
-
# user = User.by_instagram_id(user_id)
-
# @client = Instagram.client(:access_token => _access_token_for_user(user))
-
# latest_media = @client.user_recent_media[0]
-
# user.media.create_with_hash(latest_media)
-
# end
-
#
-
# end
-
# @format :json
-
# @authenticated true
-
#
-
# Requires client_secret to be set on the client or passed in options
-
# @rate_limited true
-
# @see https://api.instagram.com/developer/realtime/
-
1
def process_subscription(json, options={}, &block)
-
raise ArgumentError, "callbacks block expected" unless block_given?
-
-
if options.has_key?(:signature)
-
if !client_secret
-
raise ArgumentError, "client_secret must be set during configure"
-
end
-
digest = OpenSSL::Digest.new('sha1')
-
verify_signature = OpenSSL::HMAC.hexdigest(digest, client_secret, json)
-
-
if options[:signature] != verify_signature
-
raise Instagram::InvalidSignature, "invalid X-Hub-Signature does not match verify signature against client_secret"
-
end
-
end
-
-
payload = MultiJson.decode(json)
-
@changes = Hash.new { |h,k| h[k] = [] }
-
for change in payload
-
@changes[change['object']] << change
-
end
-
block.call(self)
-
end
-
-
1
[:user, :tag, :location, :geography].each do |object|
-
4
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ +1
-
def on_#{object}_changed(&block)
-
for change in @changes['#{object}']
-
yield change.delete('object_id'), change
-
end
-
end
-
RUBY_EVAL
-
end
-
end
-
end
-
end
-
1
module Instagram
-
1
class Client
-
# Defines methods related to tags
-
1
module Tags
-
# Returns extended information of a given Instagram tag
-
#
-
# @overload tag(tag)
-
# @param tag [String] An Instagram tag name
-
# @return [Hashie::Mash] The requested tag.
-
# @example Return extended information for the tag "cat"
-
# Instagram.tag('cat')
-
# @format :json
-
# @authenticated false
-
# @rate_limited true
-
# @see http://instagram.com/developer/endpoints/tags/#get_tags
-
1
def tag(tag, *args)
-
response = get("tags/#{tag}")
-
response
-
end
-
-
# Returns a list of recent media items for a given Instagram tag
-
#
-
# @overload tag_recent_media(tag, options={})
-
# @param tag-name [String] An Instagram tag name.
-
# @param options [Hash] A customizable set of options.
-
# @option options [Integer] :max_id (nil) Returns results with an ID less than (that is, older than) or equal to the specified ID.
-
# @option options [Integer] :min_id (nil) Returns results with an ID greater than (that is, newer than) or equal to the specified ID.
-
# @return [Hashie::Mash]
-
# @example Return a list of the most recent media items tagged "cat"
-
# Instagram.tag_recent_media('cat')
-
# @see http://instagram.com/developer/endpoints/tags/#get_tags_media_recent
-
# @format :json
-
# @authenticated false
-
# @rate_limited true
-
1
def tag_recent_media(id, *args)
-
options = args.last.is_a?(Hash) ? args.pop : {}
-
response = get("tags/#{id}/media/recent", options, false, false, false)
-
response
-
end
-
-
# Returns a list of tags starting with the given search query
-
#
-
# @format :json
-
# @authenticated false
-
# @rate_limited true
-
# @param query [String] The beginning or complete tag name to search for
-
# @param options [Hash] A customizable set of options.
-
# @option options [Integer] :count The number of media items to retrieve.
-
# @return [Hashie::Mash]
-
# @see http://instagram.com/developer/endpoints/tags/#get_tags_search
-
# @example Return tags that start with "cat"
-
# Instagram.tag_search("cat")
-
1
def tag_search(query, options={})
-
response = get('tags/search', options.merge(:q => query))
-
response
-
end
-
end
-
end
-
end
-
1
module Instagram
-
1
class Client
-
# Defines methods related to users
-
1
module Users
-
# Returns extended information of a given user
-
#
-
# @overload user(id=nil, options={})
-
# @param user [Integer] An Instagram user ID
-
# @return [Hashie::Mash] The requested user.
-
# @example Return extended information for @shayne
-
# Instagram.user(20)
-
# @format :json
-
# @authenticated false unless requesting it from a protected user
-
#
-
# If getting this data of a protected user, you must authenticate (and be allowed to see that user).
-
# @rate_limited true
-
# @see http://instagram.com/developer/endpoints/users/#get_users
-
1
def user(*args)
-
options = args.last.is_a?(Hash) ? args.pop : {}
-
id = args.first || 'self'
-
response = get("users/#{id}", options)
-
response
-
end
-
-
# Returns users that match the given query
-
#
-
# @format :json
-
# @authenticated false
-
# @rate_limited true
-
# @param query [String] The search query to run against user search.
-
# @param options [Hash] A customizable set of options.
-
# @option options [Integer] :count The number of users to retrieve.
-
# @return [Hashie::Mash]
-
# @see http://instagram.com/developer/endpoints/users/#get_users_search
-
# @example Return users that match "Shayne Sweeney"
-
# Instagram.user_search("Shayne Sweeney")
-
1
def user_search(query, options={})
-
response = get('users/search', options.merge(:q => query))
-
response
-
end
-
-
# Returns a list of users whom a given user follows
-
#
-
# @overload user_follows(id=nil, options={})
-
# @param options [Hash] A customizable set of options.
-
# @return [Hashie::Mash]
-
# @example Returns a list of users the authenticated user follows
-
# Instagram.user_follows
-
# @overload user_follows(id=nil, options={})
-
# @param user [Integer] An Instagram user ID.
-
# @param options [Hash] A customizable set of options.
-
# @option options [Integer] :cursor (nil) Breaks the results into pages. Provide values as returned in the response objects's next_cursor attribute to page forward in the list.
-
# @option options [Integer] :count (nil) Limits the number of results returned per page.
-
# @return [Hashie::Mash]
-
# @example Return a list of users @mikeyk follows
-
# Instagram.user_follows(4) # @mikeyk user ID being 4
-
# @see http://instagram.com/developer/endpoints/relationships/#get_users_follows
-
# @format :json
-
# @authenticated false unless requesting it from a protected user
-
#
-
# If getting this data of a protected user, you must authenticate (and be allowed to see that user).
-
# @rate_limited true
-
1
def user_follows(*args)
-
options = args.last.is_a?(Hash) ? args.pop : {}
-
id = args.first || "self"
-
response = get("users/#{id}/follows", options)
-
response
-
end
-
end
-
-
# Returns a list of users whom a given user is followed by
-
#
-
# @overload user_followed_by(id=nil, options={})
-
# @param options [Hash] A customizable set of options.
-
# @return [Hashie::Mash]
-
# @example Returns a list of users the authenticated user is followed by
-
# Instagram.user_followed_by
-
# @overload user_followed_by(id=nil, options={})
-
# @param user [Integer] An Instagram user ID.
-
# @param options [Hash] A customizable set of options.
-
# @option options [Integer] :cursor (nil) Breaks the results into pages. Provide values as returned in the response objects's next_cursor attribute to page forward in the list.
-
# @option options [Integer] :count (nil) Limits the number of results returned per page.
-
# @return [Hashie::Mash]
-
# @example Return a list of users @mikeyk is followed by
-
# Instagram.user_followed_by(4) # @mikeyk user ID being 4
-
# @see http://instagram.com/developer/endpoints/relationships/#get_users_followed_by
-
# @format :json
-
# @authenticated false unless requesting it from a protected user
-
#
-
# If getting this data of a protected user, you must authenticate (and be allowed to see that user).
-
# @rate_limited true
-
1
def user_followed_by(*args)
-
options = args.last.is_a?(Hash) ? args.pop : {}
-
id = args.first || "self"
-
response = get("users/#{id}/followed-by", options)
-
response
-
end
-
-
# Returns a list of users who have requested the currently authorized user's permission to follow
-
#
-
# @overload user_requested_by()
-
# @param options [Hash] A customizable set of options.
-
# @return [Hashie::Mash]
-
# @example Returns a list of users awaiting approval of a ollow request, for the authenticated user
-
# Instagram.user_requested_by
-
# @overload user_requested_by()
-
# @return [Hashie::Mash]
-
# @example Return a list of users who have requested to follow the authenticated user
-
# Instagram.user_requested_by()
-
# @see http://instagram.com/developer/endpoints/relationships/#get_incoming_requests
-
# @format :json
-
# @authenticated true
-
# @rate_limited true
-
1
def user_requested_by()
-
response = get("users/self/requested-by")
-
response
-
end
-
-
# Returns most recent media items from the currently authorized user's feed
-
#
-
# @overload user_media_feed(options={})
-
# @param options [Hash] A customizable set of options.
-
# @option options [Integer] :max_id Returns results with an ID less than (that is, older than) or equal to the specified ID.
-
# @option options [Integer] :min_id Return media later than this min_id
-
# @option options [Integer] :count Specifies the number of records to retrieve, per page.
-
# @return [Hashie::Mash]
-
# @example Return most recent media images that would appear on @shayne's feed
-
# Instagram.user_media_feed() # assuming @shayne is the authorized user
-
# @format :json
-
# @authenticated true
-
# @rate_limited true
-
# @see http://instagram.com/developer/endpoints/users/#get_users_feed
-
1
def user_media_feed(*args)
-
options = args.first.is_a?(Hash) ? args.pop : {}
-
response = get('users/self/feed', options)
-
response
-
end
-
-
# Returns a list of recent media items for a given user
-
#
-
# @overload user_recent_media(options={})
-
# @param options [Hash] A customizable set of options.
-
# @return [Hashie::Mash]
-
# @example Returns a list of recent media items for the currently authenticated user
-
# Instagram.user_recent_media
-
# @overload user_recent_media(id=nil, options={})
-
# @param user [Integer] An Instagram user ID.
-
# @param options [Hash] A customizable set of options.
-
# @option options [Integer] :max_id (nil) Returns results with an ID less than (that is, older than) or equal to the specified ID.
-
# @option options [Integer] :count (nil) Limits the number of results returned per page.
-
# @return [Hashie::Mash]
-
# @example Return a list of media items taken by @mikeyk
-
# Instagram.user_recent_media(4) # @mikeyk user ID being 4
-
# @see http://instagram.com/developer/endpoints/users/#get_users_media_recent
-
# @format :json
-
# @authenticated false unless requesting it from a protected user
-
#
-
# If getting this data of a protected user, you must authenticate (and be allowed to see that user).
-
# @rate_limited true
-
1
def user_recent_media(*args)
-
options = args.last.is_a?(Hash) ? args.pop : {}
-
id = args.first || "self"
-
response = get("users/#{id}/media/recent", options)
-
response
-
end
-
-
# Returns a list of media items liked by the current user
-
#
-
# @overload user_liked_media(options={})
-
# @param options [Hash] A customizable set of options.
-
# @option options [Integer] :max_like_id (nil) Returns results with an ID less than (that is, older than) or equal to the specified ID.
-
# @option options [Integer] :count (nil) Limits the number of results returned per page.
-
# @return [Hashie::Mash]
-
# @example Returns a list of media items liked by the currently authenticated user
-
# Instagram.user_liked_media
-
# @see http://instagram.com/developer/endpoints/users/#get_users_liked_feed
-
# @format :json
-
# @authenticated true
-
# @rate_limited true
-
1
def user_liked_media(options={})
-
response = get("users/self/media/liked", options)
-
response
-
end
-
-
# Returns information about the current user's relationship (follow/following/etc) to another user
-
#
-
# @overload user_relationship(id, options={})
-
# @param user [Integer] An Instagram user ID.
-
# @param options [Hash] An optional options hash
-
# @return [Hashie::Mash]
-
# @example Return the relationship status between the currently authenticated user and @mikeyk
-
# Instagram.user_relationship(4) # @mikeyk user ID being 4
-
# @see http://instagram.com/developer/endpoints/relationships/#get_relationship
-
# @format :json
-
# @authenticated true
-
# @rate_limited true
-
1
def user_relationship(id, options={})
-
response = get("users/#{id}/relationship", options)
-
response
-
end
-
-
# Create a follows relationship between the current user and the target user
-
#
-
# @overload follow_user(id, options={})
-
# @param user [Integer] An Instagram user ID.
-
# @param options [Hash] An optional options hash
-
# @return [Hashie::Mash]
-
# @example Request the current user to follow the target user
-
# Instagram.follow_user(4)
-
# @see http://instagram.com/developer/endpoints/relationships/#post_relationship
-
# @format :json
-
# @authenticated true
-
# @rate_limited true
-
1
def follow_user(id, options={})
-
options["action"] = "follow"
-
response = post("users/#{id}/relationship", options, signature=true)
-
response
-
end
-
-
# Destroy a follows relationship between the current user and the target user
-
#
-
# @overload unfollow_user(id, options={})
-
# @param user [Integer] An Instagram user ID.
-
# @param options [Hash] An optional options hash
-
# @return [Hashie::Mash]
-
# @example Remove a follows relationship between the current user and the target user
-
# Instagram.unfollow_user(4)
-
# @see http://instagram.com/developer/endpoints/relationships/#post_relationship
-
# @format :json
-
# @authenticated true
-
# @rate_limited true
-
1
def unfollow_user(id, options={})
-
options["action"] = "unfollow"
-
response = post("users/#{id}/relationship", options, signature=true)
-
response
-
end
-
-
# Block a relationship between the current user and the target user
-
#
-
# @overload unfollow_user(id, options={})
-
# @param user [Integer] An Instagram user ID.
-
# @param options [Hash] An optional options hash
-
# @return [Hashie::Mash]
-
# @example Block a relationship between the current user and the target user
-
# Instagram.block_user(4)
-
# @see http://instagram.com/developer/endpoints/relationships/#post_relationship
-
# @format :json
-
# @authenticated true
-
# @rate_limited true
-
1
def block_user(id, options={})
-
options["action"] = "block"
-
response = post("users/#{id}/relationship", options, signature=true)
-
response
-
end
-
-
# Remove a relationship block between the current user and the target user
-
#
-
# @overload unblock_user(id, options={})
-
# @param user [Integer] An Instagram user ID.
-
# @param options [Hash] An optional options hash
-
# @return [Hashie::Mash]
-
# @example Remove a relationship block between the current user and the target user
-
# Instagram.unblock_user(4)
-
# @see http://instagram.com/developer/endpoints/relationships/#post_relationship
-
# @format :json
-
# @authenticated true
-
# @rate_limited true
-
1
def unblock_user(id, options={})
-
options["action"] = "unblock"
-
response = post("users/#{id}/relationship", options, signature=true)
-
response
-
end
-
-
# Approve a relationship request between the current user and the target user
-
#
-
# @overload approve_user(id, options={})
-
# @param user [Integer] An Instagram user ID.
-
# @param options [Hash] An optional options hash
-
# @return [Hashie::Mash]
-
# @example Approve a relationship request between the current user and the target user
-
# Instagram.approve_user(4)
-
# @see http://instagram.com/developer/endpoints/relationships/#post_relationship
-
# @format :json
-
# @authenticated true
-
# @rate_limited true
-
1
def approve_user(id, options={})
-
options["action"] = "approve"
-
response = post("users/#{id}/relationship", options, signature=true)
-
response
-
end
-
-
# Deny a relationship request between the current user and the target user
-
#
-
# @overload deny_user(id, options={})
-
# @param user [Integer] An Instagram user ID.
-
# @param options [Hash] An optional options hash
-
# @return [Hashie::Mash]
-
# @example Deny a relationship request between the current user and the target user
-
# Instagram.deny_user(4)
-
# @see http://instagram.com/developer/endpoints/relationships/#post_relationship
-
# @format :json
-
# @authenticated true
-
# @rate_limited true
-
1
def deny_user(id, options={})
-
options["action"] = "deny"
-
response = post("users/#{id}/relationship", options, signature=true)
-
response
-
end
-
end
-
end
-
1
module Instagram
-
1
class Client
-
# @private
-
1
module Utils
-
# Returns the raw full response including all headers. Can be used to access the values for 'X-Ratelimit-Limit' and 'X-Ratelimit-Remaining'
-
# ==== Examples
-
#
-
# client = Instagram.client(:access_token => session[:access_token])
-
# response = client.utils_raw_response
-
# remaining = response.headers[:x_ratelimit_remaining]
-
# limit = response.headers[:x_ratelimit_limit]
-
#
-
1
def utils_raw_response
-
response = get('users/self/feed',nil, false, true)
-
response
-
end
-
-
1
private
-
-
# Returns the configured user name or the user name of the authenticated user
-
#
-
# @return [String]
-
1
def get_username
-
@user_name ||= self.user.username
-
end
-
end
-
end
-
end
-
1
require 'faraday'
-
1
require File.expand_path('../version', __FILE__)
-
-
1
module Instagram
-
# Defines constants and methods related to configuration
-
1
module Configuration
-
# An array of valid keys in the options hash when configuring a {Instagram::API}
-
1
VALID_OPTIONS_KEYS = [
-
:access_token,
-
:adapter,
-
:client_id,
-
:client_secret,
-
:client_ips,
-
:connection_options,
-
:scope,
-
:redirect_uri,
-
:endpoint,
-
:format,
-
:proxy,
-
:user_agent,
-
:no_response_wrapper
-
].freeze
-
-
# By default, don't set a user access token
-
1
DEFAULT_ACCESS_TOKEN = nil
-
-
# The adapter that will be used to connect if none is set
-
#
-
# @note The default faraday adapter is Net::HTTP.
-
1
DEFAULT_ADAPTER = Faraday.default_adapter
-
-
# By default, don't set an application ID
-
1
DEFAULT_CLIENT_ID = nil
-
-
# By default, don't set an application secret
-
1
DEFAULT_CLIENT_SECRET = nil
-
-
# By default, don't set application IPs
-
1
DEFAULT_CLIENT_IPS = nil
-
-
# By default, don't set any connection options
-
1
DEFAULT_CONNECTION_OPTIONS = {}
-
-
# The endpoint that will be used to connect if none is set
-
#
-
# @note There is no reason to use any other endpoint at this time
-
1
DEFAULT_ENDPOINT = 'https://api.instagram.com/v1/'.freeze
-
-
# The response format appended to the path and sent in the 'Accept' header if none is set
-
#
-
# @note JSON is the only available format at this time
-
1
DEFAULT_FORMAT = :json
-
-
# By default, don't use a proxy server
-
1
DEFAULT_PROXY = nil
-
-
# By default, don't set an application redirect uri
-
1
DEFAULT_REDIRECT_URI = nil
-
-
# By default, don't set a user scope
-
1
DEFAULT_SCOPE = nil
-
-
# By default, don't wrap responses with meta data (i.e. pagination)
-
1
DEFAULT_NO_RESPONSE_WRAPPER = false
-
-
# The user agent that will be sent to the API endpoint if none is set
-
1
DEFAULT_USER_AGENT = "Instagram Ruby Gem #{Instagram::VERSION}".freeze
-
-
# An array of valid request/response formats
-
#
-
# @note Not all methods support the XML format.
-
1
VALID_FORMATS = [
-
:json].freeze
-
-
# @private
-
1
attr_accessor *VALID_OPTIONS_KEYS
-
-
# When this module is extended, set all configuration options to their default values
-
1
def self.extended(base)
-
1
base.reset
-
end
-
-
# Convenience method to allow configuration options to be set in a block
-
1
def configure
-
yield self
-
end
-
-
# Create a hash of options and their values
-
1
def options
-
VALID_OPTIONS_KEYS.inject({}) do |option, key|
-
option.merge!(key => send(key))
-
end
-
end
-
-
# Reset all configuration options to defaults
-
1
def reset
-
1
self.access_token = DEFAULT_ACCESS_TOKEN
-
1
self.adapter = DEFAULT_ADAPTER
-
1
self.client_id = DEFAULT_CLIENT_ID
-
1
self.client_secret = DEFAULT_CLIENT_SECRET
-
1
self.client_ips = DEFAULT_CLIENT_IPS
-
1
self.connection_options = DEFAULT_CONNECTION_OPTIONS
-
1
self.scope = DEFAULT_SCOPE
-
1
self.redirect_uri = DEFAULT_REDIRECT_URI
-
1
self.endpoint = DEFAULT_ENDPOINT
-
1
self.format = DEFAULT_FORMAT
-
1
self.proxy = DEFAULT_PROXY
-
1
self.user_agent = DEFAULT_USER_AGENT
-
1
self.no_response_wrapper= DEFAULT_NO_RESPONSE_WRAPPER
-
end
-
end
-
end
-
1
require 'faraday_middleware'
-
3
Dir[File.expand_path('../../faraday/*.rb', __FILE__)].each{|f| require f}
-
-
1
module Instagram
-
# @private
-
1
module Connection
-
1
private
-
-
1
def connection(raw=false)
-
options = {
-
:headers => {'Accept' => "application/#{format}; charset=utf-8", 'User-Agent' => user_agent},
-
:proxy => proxy,
-
:url => endpoint,
-
}.merge(connection_options)
-
-
Faraday::Connection.new(options) do |connection|
-
connection.use FaradayMiddleware::InstagramOAuth2, client_id, access_token
-
connection.use Faraday::Request::UrlEncoded
-
connection.use FaradayMiddleware::Mashify unless raw
-
unless raw
-
case format.to_s.downcase
-
when 'json' then connection.use Faraday::Response::ParseJson
-
end
-
end
-
connection.use FaradayMiddleware::RaiseHttpException
-
connection.adapter(adapter)
-
end
-
end
-
end
-
end
-
1
module Instagram
-
# Custom error class for rescuing from all Instagram errors
-
1
class Error < StandardError; end
-
-
# Raised when Instagram returns the HTTP status code 400
-
1
class BadRequest < Error; end
-
-
# Raised when Instagram returns the HTTP status code 404
-
1
class NotFound < Error; end
-
-
# Raised when Instagram returns the HTTP status code 429
-
1
class TooManyRequests < Error; end
-
-
# Raised when Instagram returns the HTTP status code 500
-
1
class InternalServerError < Error; end
-
-
# Raised when Instagram returns the HTTP status code 502
-
1
class BadGateway < Error; end
-
-
# Raised when Instagram returns the HTTP status code 503
-
1
class ServiceUnavailable < Error; end
-
-
# Raised when Instagram returns the HTTP status code 504
-
1
class GatewayTimeout < Error; end
-
-
# Raised when a subscription payload hash is invalid
-
1
class InvalidSignature < Error; end
-
-
# Raised when Instagram returns the HTTP status code 429
-
1
class RateLimitExceeded < Error; end
-
end
-
1
module Instagram
-
# Defines HTTP request methods
-
1
module OAuth
-
# Return URL for OAuth authorization
-
1
def authorize_url(options={})
-
options[:response_type] ||= "code"
-
options[:scope] ||= scope if !scope.nil? && !scope.empty?
-
options[:redirect_uri] ||= self.redirect_uri
-
params = authorization_params.merge(options)
-
connection.build_url("/oauth/authorize/", params).to_s
-
end
-
-
# Return an access token from authorization
-
1
def get_access_token(code, options={})
-
options[:grant_type] ||= "authorization_code"
-
options[:redirect_uri] ||= self.redirect_uri
-
params = access_token_params.merge(options)
-
post("/oauth/access_token/", params.merge(:code => code), signature=false, raw=false, unformatted=true, no_response_wrapper=true)
-
end
-
-
1
private
-
-
1
def authorization_params
-
{
-
:client_id => client_id
-
}
-
end
-
-
1
def access_token_params
-
{
-
:client_id => client_id,
-
:client_secret => client_secret
-
}
-
end
-
end
-
end
-
1
require 'openssl'
-
1
require 'base64'
-
-
1
module Instagram
-
# Defines HTTP request methods
-
1
module Request
-
# Perform an HTTP GET request
-
1
def get(path, options={}, signature=false, raw=false, unformatted=false, no_response_wrapper=no_response_wrapper)
-
request(:get, path, options, signature, raw, unformatted, no_response_wrapper)
-
end
-
-
# Perform an HTTP POST request
-
1
def post(path, options={}, signature=false, raw=false, unformatted=false, no_response_wrapper=no_response_wrapper)
-
request(:post, path, options, signature, raw, unformatted, no_response_wrapper)
-
end
-
-
# Perform an HTTP PUT request
-
1
def put(path, options={}, signature=false, raw=false, unformatted=false, no_response_wrapper=no_response_wrapper)
-
request(:put, path, options, signature, raw, unformatted, no_response_wrapper)
-
end
-
-
# Perform an HTTP DELETE request
-
1
def delete(path, options={}, signature=false, raw=false, unformatted=false, no_response_wrapper=no_response_wrapper)
-
request(:delete, path, options, signature, raw, unformatted, no_response_wrapper)
-
end
-
-
1
private
-
-
# Perform an HTTP request
-
1
def request(method, path, options, signature=false, raw=false, unformatted=false, no_response_wrapper=false)
-
response = connection(raw).send(method) do |request|
-
path = formatted_path(path) unless unformatted
-
case method
-
when :get, :delete
-
request.url(path, options)
-
when :post, :put
-
request.path = path
-
request.body = options unless options.empty?
-
end
-
if signature && client_ips != nil
-
request.headers["X-Insta-Forwarded-For"] = get_insta_fowarded_for(client_ips, client_secret)
-
end
-
end
-
return response if raw
-
return response.body if no_response_wrapper
-
return Response.create( response.body )
-
end
-
-
1
def formatted_path(path)
-
[path, format].compact.join('.')
-
end
-
-
1
def get_insta_fowarded_for(ips, secret)
-
digest = OpenSSL::Digest.new('sha256')
-
signature = OpenSSL::HMAC.hexdigest(digest, secret, ips)
-
return [ips, signature].join('|')
-
end
-
-
end
-
end
-
1
module Instagram
-
1
module Response
-
1
def self.create( response_hash )
-
data = response_hash.data.dup rescue response_hash
-
data.extend( self )
-
data.instance_exec do
-
@pagination = response_hash.pagination
-
@meta = response_hash.meta
-
end
-
data
-
end
-
-
1
attr_reader :pagination
-
1
attr_reader :meta
-
end
-
end
-
1
module Instagram
-
1
VERSION = '1.1.3'.freeze unless defined?(::Instagram::VERSION)
-
end
-
1
require 'jbuilder/jbuilder'
-
1
require 'jbuilder/key_formatter'
-
1
require 'jbuilder/errors'
-
1
require 'multi_json'
-
-
1
class Jbuilder
-
1
@@key_formatter = KeyFormatter.new
-
1
@@ignore_nil = false
-
-
1
def initialize(options = {})
-
@attributes = {}
-
-
@key_formatter = options.fetch(:key_formatter){ @@key_formatter.clone }
-
@ignore_nil = options.fetch(:ignore_nil, @@ignore_nil)
-
-
yield self if ::Kernel.block_given?
-
end
-
-
# Yields a builder and automatically turns the result into a JSON string
-
1
def self.encode(*args, &block)
-
new(*args, &block).target!
-
end
-
-
1
BLANK = ::Object.new
-
-
1
def set!(key, value = BLANK, *args, &block)
-
result = if block
-
if !_blank?(value)
-
# json.comments @post.comments { |comment| ... }
-
# { "comments": [ { ... }, { ... } ] }
-
_scope{ array! value, &block }
-
else
-
# json.comments { ... }
-
# { "comments": ... }
-
_merge_block(key){ yield self }
-
end
-
elsif args.empty?
-
if ::Jbuilder === value
-
# json.age 32
-
# json.person another_jbuilder
-
# { "age": 32, "person": { ... }
-
value.attributes!
-
else
-
# json.age 32
-
# { "age": 32 }
-
value
-
end
-
elsif _mapable_arguments?(value, *args)
-
# json.comments @post.comments, :content, :created_at
-
# { "comments": [ { "content": "hello", "created_at": "..." }, { "content": "world", "created_at": "..." } ] }
-
_scope{ array! value, *args }
-
else
-
# json.author @post.creator, :name, :email_address
-
# { "author": { "name": "David", "email_address": "david@loudthinking.com" } }
-
_merge_block(key){ extract! value, *args }
-
end
-
-
_set_value key, result
-
end
-
-
1
alias_method :method_missing, :set!
-
1
private :method_missing
-
-
# Specifies formatting to be applied to the key. Passing in a name of a function
-
# will cause that function to be called on the key. So :upcase will upper case
-
# the key. You can also pass in lambdas for more complex transformations.
-
#
-
# Example:
-
#
-
# json.key_format! :upcase
-
# json.author do
-
# json.name "David"
-
# json.age 32
-
# end
-
#
-
# { "AUTHOR": { "NAME": "David", "AGE": 32 } }
-
#
-
# You can pass parameters to the method using a hash pair.
-
#
-
# json.key_format! camelize: :lower
-
# json.first_name "David"
-
#
-
# { "firstName": "David" }
-
#
-
# Lambdas can also be used.
-
#
-
# json.key_format! ->(key){ "_" + key }
-
# json.first_name "David"
-
#
-
# { "_first_name": "David" }
-
#
-
1
def key_format!(*args)
-
@key_formatter = KeyFormatter.new(*args)
-
end
-
-
# Same as the instance method key_format! except sets the default.
-
1
def self.key_format(*args)
-
@@key_formatter = KeyFormatter.new(*args)
-
end
-
-
# If you want to skip adding nil values to your JSON hash. This is useful
-
# for JSON clients that don't deal well with nil values, and would prefer
-
# not to receive keys which have null values.
-
#
-
# Example:
-
# json.ignore_nil! false
-
# json.id User.new.id
-
#
-
# { "id": null }
-
#
-
# json.ignore_nil!
-
# json.id User.new.id
-
#
-
# {}
-
#
-
1
def ignore_nil!(value = true)
-
@ignore_nil = value
-
end
-
-
# Same as instance method ignore_nil! except sets the default.
-
1
def self.ignore_nil(value = true)
-
@@ignore_nil = value
-
end
-
-
# Turns the current element into an array and yields a builder to add a hash.
-
#
-
# Example:
-
#
-
# json.comments do
-
# json.child! { json.content "hello" }
-
# json.child! { json.content "world" }
-
# end
-
#
-
# { "comments": [ { "content": "hello" }, { "content": "world" } ]}
-
#
-
# More commonly, you'd use the combined iterator, though:
-
#
-
# json.comments(@post.comments) do |comment|
-
# json.content comment.formatted_content
-
# end
-
1
def child!
-
@attributes = [] unless ::Array === @attributes
-
@attributes << _scope{ yield self }
-
end
-
-
# Turns the current element into an array and iterates over the passed collection, adding each iteration as
-
# an element of the resulting array.
-
#
-
# Example:
-
#
-
# json.array!(@people) do |person|
-
# json.name person.name
-
# json.age calculate_age(person.birthday)
-
# end
-
#
-
# [ { "name": David", "age": 32 }, { "name": Jamie", "age": 31 } ]
-
#
-
# If you are using Ruby 1.9+, you can use the call syntax instead of an explicit extract! call:
-
#
-
# json.(@people) { |person| ... }
-
#
-
# It's generally only needed to use this method for top-level arrays. If you have named arrays, you can do:
-
#
-
# json.people(@people) do |person|
-
# json.name person.name
-
# json.age calculate_age(person.birthday)
-
# end
-
#
-
# { "people": [ { "name": David", "age": 32 }, { "name": Jamie", "age": 31 } ] }
-
#
-
# If you omit the block then you can set the top level array directly:
-
#
-
# json.array! [1, 2, 3]
-
#
-
# [1,2,3]
-
1
def array!(collection = [], *attributes, &block)
-
array = if collection.nil?
-
[]
-
elsif block
-
_map_collection(collection, &block)
-
elsif attributes.any?
-
_map_collection(collection) { |element| extract! element, *attributes }
-
else
-
collection.to_a
-
end
-
-
merge! array
-
end
-
-
# Extracts the mentioned attributes or hash elements from the passed object and turns them into attributes of the JSON.
-
#
-
# Example:
-
#
-
# @person = Struct.new(:name, :age).new('David', 32)
-
#
-
# or you can utilize a Hash
-
#
-
# @person = { name: 'David', age: 32 }
-
#
-
# json.extract! @person, :name, :age
-
#
-
# { "name": David", "age": 32 }, { "name": Jamie", "age": 31 }
-
#
-
# You can also use the call syntax instead of an explicit extract! call:
-
#
-
# json.(@person, :name, :age)
-
1
def extract!(object, *attributes)
-
if ::Hash === object
-
_extract_hash_values(object, *attributes)
-
else
-
_extract_method_values(object, *attributes)
-
end
-
end
-
-
1
def call(object, *attributes, &block)
-
if block
-
array! object, &block
-
else
-
extract! object, *attributes
-
end
-
end
-
-
# Returns the nil JSON.
-
1
def nil!
-
@attributes = nil
-
end
-
-
1
alias_method :null!, :nil!
-
-
# Returns the attributes of the current builder.
-
1
def attributes!
-
@attributes
-
end
-
-
# Merges hash or array into current builder.
-
1
def merge!(hash_or_array)
-
@attributes = _merge_values(@attributes, hash_or_array)
-
end
-
-
# Encodes the current builder as JSON.
-
1
def target!
-
::MultiJson.dump(@attributes)
-
end
-
-
1
private
-
-
1
def _extract_hash_values(object, *attributes)
-
attributes.each{ |key| _set_value key, object.fetch(key) }
-
end
-
-
1
def _extract_method_values(object, *attributes)
-
attributes.each{ |key| _set_value key, object.public_send(key) }
-
end
-
-
1
def _merge_block(key)
-
current_value = _blank? ? BLANK : @attributes.fetch(_key(key), BLANK)
-
raise NullError.build(key) if current_value.nil?
-
new_value = _scope{ yield self }
-
_merge_values(current_value, new_value)
-
end
-
-
1
def _merge_values(current_value, updates)
-
if _blank?(updates)
-
current_value
-
elsif _blank?(current_value) || updates.nil?
-
updates
-
elsif ::Array === updates
-
current_value = ::Array === current_value ? current_value.dup : []
-
current_value.concat updates
-
else
-
current_value = current_value.dup
-
current_value.update updates
-
end
-
end
-
-
-
1
def _write(key, value)
-
@attributes = {} if _blank?
-
@attributes[_key(key)] = value
-
end
-
-
1
def _key(key)
-
@key_formatter.format(key)
-
end
-
-
1
def _set_value(key, value)
-
raise NullError.build(key) if @attributes.nil?
-
return if @ignore_nil && value.nil?
-
return if _blank?(value)
-
_write key, value
-
end
-
-
1
def _map_collection(collection)
-
collection.map do |element|
-
_scope{ yield element }
-
end - [BLANK]
-
end
-
-
1
def _scope
-
parent_attributes, parent_formatter = @attributes, @key_formatter
-
@attributes = BLANK
-
yield
-
@attributes
-
ensure
-
@attributes, @key_formatter = parent_attributes, parent_formatter
-
end
-
-
1
def _mapable_arguments?(value, *args)
-
value.respond_to?(:map)
-
end
-
-
1
def _blank?(value=@attributes)
-
BLANK == value
-
end
-
end
-
-
1
require 'jbuilder/dependency_tracker'
-
1
require 'jbuilder/railtie' if defined?(Rails)
-
1
require 'jbuilder/jbuilder'
-
-
1
dependency_tracker = false
-
-
1
begin
-
1
require 'action_view'
-
1
require 'action_view/dependency_tracker'
-
1
dependency_tracker = ::ActionView::DependencyTracker
-
rescue LoadError
-
begin
-
require 'cache_digests'
-
dependency_tracker = ::CacheDigests::DependencyTracker
-
rescue LoadError
-
end
-
end
-
-
1
if dependency_tracker
-
1
class Jbuilder
-
1
module DependencyTrackerMethods
-
# Matches:
-
# json.partial! "messages/message"
-
# json.partial!('messages/message')
-
#
-
1
DIRECT_RENDERS = /
-
\w+\.partial! # json.partial!
-
\(?\s* # optional parenthesis
-
(['"])([^'"]+)\1 # quoted value
-
/x
-
-
# Matches:
-
# json.partial! partial: "comments/comment"
-
# json.comments @post.comments, partial: "comments/comment", as: :comment
-
# json.array! @posts, partial: "posts/post", as: :post
-
# = render partial: "account"
-
#
-
1
INDIRECT_RENDERS = /
-
(?::partial\s*=>|partial:) # partial: or :partial =>
-
\s* # optional whitespace
-
(['"])([^'"]+)\1 # quoted value
-
/x
-
-
1
def dependencies
-
direct_dependencies + indirect_dependencies + explicit_dependencies
-
end
-
-
1
private
-
-
1
def direct_dependencies
-
source.scan(DIRECT_RENDERS).map(&:second)
-
end
-
-
1
def indirect_dependencies
-
source.scan(INDIRECT_RENDERS).map(&:second)
-
end
-
end
-
end
-
-
1
::Jbuilder::DependencyTracker = Class.new(dependency_tracker::ERBTracker)
-
1
::Jbuilder::DependencyTracker.send :include, ::Jbuilder::DependencyTrackerMethods
-
1
dependency_tracker.register_tracker :jbuilder, ::Jbuilder::DependencyTracker
-
end
-
1
require 'jbuilder/jbuilder'
-
-
1
class Jbuilder
-
1
class NullError < ::NoMethodError
-
1
def self.build(key)
-
message = "Failed to add #{key.to_s.inspect} property to null object"
-
new(message)
-
end
-
end
-
end
-
1
Jbuilder = Class.new(begin
-
1
require 'active_support/proxy_object'
-
1
ActiveSupport::ProxyObject
-
rescue LoadError
-
require 'active_support/basic_object'
-
ActiveSupport::BasicObject
-
end)
-
1
require 'jbuilder/jbuilder'
-
1
require 'action_dispatch/http/mime_type'
-
1
require 'active_support/cache'
-
-
1
class JbuilderTemplate < Jbuilder
-
1
class << self
-
1
attr_accessor :template_lookup_options
-
end
-
-
1
self.template_lookup_options = { handlers: [:jbuilder] }
-
-
1
def initialize(context, *args, &block)
-
@context = context
-
super(*args, &block)
-
end
-
-
1
def partial!(name_or_options, locals = {})
-
case name_or_options
-
when ::Hash
-
# partial! partial: 'name', locals: { foo: 'bar' }
-
options = name_or_options
-
else
-
# partial! 'name', foo: 'bar'
-
options = { partial: name_or_options, locals: locals }
-
as = locals.delete(:as)
-
options[:as] = as if as.present?
-
options[:collection] = locals[:collection] if locals.key?(:collection)
-
end
-
-
_render_partial_with_options options
-
end
-
-
1
def array!(collection = [], *attributes)
-
options = attributes.extract_options!
-
-
if options.key?(:partial)
-
partial! options[:partial], options.merge(collection: collection)
-
else
-
super
-
end
-
end
-
-
# Caches the json constructed within the block passed. Has the same signature as the `cache` helper
-
# method in `ActionView::Helpers::CacheHelper` and so can be used in the same way.
-
#
-
# Example:
-
#
-
# json.cache! ['v1', @person], expires_in: 10.minutes do
-
# json.extract! @person, :name, :age
-
# end
-
1
def cache!(key=nil, options={})
-
if @context.controller.perform_caching
-
value = ::Rails.cache.fetch(_cache_key(key, options), options) do
-
_scope { yield self }
-
end
-
-
merge! value
-
else
-
yield
-
end
-
end
-
-
# Conditionally catches the json depending in the condition given as first parameter. Has the same
-
# signature as the `cache` helper method in `ActionView::Helpers::CacheHelper` and so can be used in
-
# the same way.
-
#
-
# Example:
-
#
-
# json.cache_if! !admin?, @person, expires_in: 10.minutes do
-
# json.extract! @person, :name, :age
-
# end
-
1
def cache_if!(condition, *args, &block)
-
condition ? cache!(*args, &block) : yield
-
end
-
-
1
protected
-
-
1
def _render_partial_with_options(options)
-
options.reverse_merge! locals: {}
-
options.reverse_merge! ::JbuilderTemplate.template_lookup_options
-
as = options[:as]
-
-
if as && options.key?(:collection)
-
as = as.to_sym
-
collection = options.delete(:collection)
-
locals = options.delete(:locals)
-
array! collection do |member|
-
member_locals = locals.clone
-
member_locals.merge! collection: collection
-
member_locals.merge! as => member
-
_render_partial options.merge(locals: member_locals)
-
end
-
else
-
_render_partial options
-
end
-
end
-
-
1
def _render_partial(options)
-
options[:locals].merge! json: self
-
@context.render options
-
end
-
-
1
def _cache_key(key, options)
-
key = _fragment_name_with_digest(key, options)
-
key = url_for(key).split('://', 2).last if ::Hash === key
-
::ActiveSupport::Cache.expand_cache_key(key, :jbuilder)
-
end
-
-
1
private
-
-
1
def _fragment_name_with_digest(key, options)
-
if @context.respond_to?(:cache_fragment_name)
-
# Current compatibility, fragment_name_with_digest is private again and cache_fragment_name
-
# should be used instead.
-
@context.cache_fragment_name(key, options)
-
elsif @context.respond_to?(:fragment_name_with_digest)
-
# Backwards compatibility for period of time when fragment_name_with_digest was made public.
-
@context.fragment_name_with_digest(key)
-
else
-
key
-
end
-
end
-
-
1
def _mapable_arguments?(value, *args)
-
return true if super
-
options = args.last
-
::Hash === options && options.key?(:as)
-
end
-
end
-
-
1
class JbuilderHandler
-
1
cattr_accessor :default_format
-
1
self.default_format = Mime::JSON
-
-
1
def self.call(template)
-
# this juggling is required to keep line numbers right in the error
-
%{__already_defined = defined?(json); json||=JbuilderTemplate.new(self); #{template.source}
-
json.target! unless (__already_defined && __already_defined != "method")}
-
end
-
end
-
1
require 'jbuilder/jbuilder'
-
1
require 'active_support/core_ext/array'
-
-
1
class Jbuilder
-
1
class KeyFormatter
-
1
def initialize(*args)
-
1
@format = {}
-
1
@cache = {}
-
-
1
options = args.extract_options!
-
1
args.each do |name|
-
@format[name] = []
-
end
-
1
options.each do |name, paramaters|
-
@format[name] = paramaters
-
end
-
end
-
-
1
def initialize_copy(original)
-
@cache = {}
-
end
-
-
1
def format(key)
-
@cache[key] ||= @format.inject(key.to_s) do |result, args|
-
func, args = args
-
if ::Proc === func
-
func.call result, *args
-
else
-
result.send func, *args
-
end
-
end
-
end
-
end
-
end
-
1
require 'rails/railtie'
-
1
require 'jbuilder/jbuilder_template'
-
-
1
class Jbuilder
-
1
class Railtie < ::Rails::Railtie
-
1
initializer :jbuilder do |app|
-
1
ActiveSupport.on_load :action_view do
-
1
ActionView::Template.register_template_handler :jbuilder, JbuilderHandler
-
end
-
end
-
-
1
if Rails::VERSION::MAJOR == 4
-
1
generators do |app|
-
Rails::Generators.configure! app.config.generators
-
Rails::Generators.hidden_namespaces.uniq!
-
require 'generators/rails/scaffold_controller_generator'
-
end
-
end
-
end
-
end
-
1
module ActionDispatch
-
1
module Assertions
-
1
module SelectorAssertions
-
# Selects content from a JQuery response. Patterned loosely on
-
# assert_select_rjs.
-
#
-
# === Narrowing down
-
#
-
# With no arguments, asserts that one or more method calls are made.
-
#
-
# Use the +method+ argument to narrow down the assertion to only
-
# statements that call that specific method.
-
#
-
# Use the +opt+ argument to narrow down the assertion to only statements
-
# that pass +opt+ as the first argument.
-
#
-
# Use the +id+ argument to narrow down the assertion to only statements
-
# that invoke methods on the result of using that identifier as a
-
# selector.
-
#
-
# === Using blocks
-
#
-
# Without a block, +assert_select_jquery_ merely asserts that the
-
# response contains one or more statements that match the conditions
-
# specified above
-
#
-
# With a block +assert_select_jquery_ also asserts that the method call
-
# passes a javascript escaped string containing HTML. All such HTML
-
# fragments are selected and passed to the block. Nested assertions are
-
# supported.
-
#
-
# === Examples
-
#
-
# # asserts that the #notice element is hidden
-
# assert_select :hide, '#notice'
-
#
-
# # asserts that the #cart element is shown with a blind parameter
-
# assert_select :show, :blind, '#cart'
-
#
-
# # asserts that #cart content contains a #current_item
-
# assert_select :html, '#cart' do
-
# assert_select '#current_item'
-
# end
-
-
1
PATTERN_HTML = "\"((\\\\\"|[^\"])*)\""
-
1
PATTERN_UNICODE_ESCAPED_CHAR = /\\u([0-9a-zA-Z]{4})/
-
-
1
def assert_select_jquery(*args, &block)
-
jquery_method = args.first.is_a?(Symbol) ? args.shift : nil
-
jquery_opt = args.first.is_a?(Symbol) ? args.shift : nil
-
id = args.first.is_a?(String) ? args.shift : nil
-
-
pattern = "\\.#{jquery_method || '\\w+'}\\("
-
pattern = "#{pattern}['\"]#{jquery_opt}['\"],?\\s*" if jquery_opt
-
pattern = "#{pattern}#{PATTERN_HTML}"
-
pattern = "(?:jQuery|\\$)\\(['\"]#{id}['\"]\\)#{pattern}" if id
-
-
fragments = []
-
response.body.scan(Regexp.new(pattern)).each do |match|
-
doc = HTML::Document.new(unescape_js(match.first))
-
doc.root.children.each do |child|
-
fragments.push child if child.tag?
-
end
-
end
-
-
if fragments.empty?
-
opts = [jquery_method, jquery_opt, id].compact
-
flunk "No JQuery call matches #{opts.inspect}"
-
end
-
-
if block
-
begin
-
in_scope, @selected = @selected, fragments
-
yield
-
ensure
-
@selected = in_scope
-
end
-
end
-
end
-
-
1
private
-
-
# Unescapes a JS string.
-
1
def unescape_js(js_string)
-
# js encodes double quotes and line breaks.
-
unescaped= js_string.gsub('\"', '"')
-
unescaped.gsub!('\\\'', "'")
-
unescaped.gsub!(/\\\//, '/')
-
unescaped.gsub!('\n', "\n")
-
unescaped.gsub!('\076', '>')
-
unescaped.gsub!('\074', '<')
-
# js encodes non-ascii characters.
-
unescaped.gsub!(PATTERN_UNICODE_ESCAPED_CHAR) {|u| [$1.hex].pack('U*')}
-
unescaped
-
end
-
-
end
-
end
-
end
-
1
require 'jquery/assert_select' if ::Rails.env.test?
-
1
require 'jquery/rails/engine' if ::Rails.version >= '3.1'
-
1
require 'jquery/rails/railtie'
-
1
require 'jquery/rails/version'
-
-
1
module Jquery
-
1
module Rails
-
1
PROTOTYPE_JS = %w{prototype effects dragdrop controls}
-
end
-
end
-
1
module Jquery
-
1
module Rails
-
1
class Engine < ::Rails::Engine
-
end
-
end
-
end
-
# Used to ensure that Rails 3.0.x, as well as Rails >= 3.1 with asset pipeline disabled
-
# get the minified version of the scripts included into the layout in production.
-
1
module Jquery
-
1
module Rails
-
1
class Railtie < ::Rails::Railtie
-
1
config.before_configuration do
-
1
if config.action_view.javascript_expansions
-
jq_defaults = ::Rails.env.production? || ::Rails.env.test? ? %w(jquery.min) : %w(jquery)
-
-
# Merge the jQuery scripts, remove the Prototype defaults and finally add 'jquery_ujs'
-
# at the end, because load order is important
-
config.action_view.javascript_expansions[:defaults] -= PROTOTYPE_JS + ['rails']
-
config.action_view.javascript_expansions[:defaults] |= jq_defaults + ['jquery_ujs']
-
end
-
end
-
end
-
end
-
end
-
1
module Jquery
-
1
module Rails
-
1
VERSION = "3.1.2"
-
1
JQUERY_VERSION = "1.11.1"
-
1
JQUERY_UJS_VERSION = "1.0.1"
-
end
-
end
-
#
-
# JSON Web Token implementation
-
#
-
# Should be up to date with the latest spec:
-
# http://self-issued.info/docs/draft-jones-json-web-token-06.html
-
-
1
require "base64"
-
1
require "openssl"
-
1
require "jwt/json"
-
-
1
module JWT
-
1
class DecodeError < StandardError; end
-
1
extend JWT::Json
-
-
1
module_function
-
-
1
def sign(algorithm, msg, key)
-
if ["HS256", "HS384", "HS512"].include?(algorithm)
-
sign_hmac(algorithm, msg, key)
-
elsif ["RS256", "RS384", "RS512"].include?(algorithm)
-
sign_rsa(algorithm, msg, key)
-
else
-
raise NotImplementedError.new("Unsupported signing method")
-
end
-
end
-
-
1
def sign_rsa(algorithm, msg, private_key)
-
private_key.sign(OpenSSL::Digest.new(algorithm.sub("RS", "sha")), msg)
-
end
-
-
1
def verify_rsa(algorithm, public_key, signing_input, signature)
-
public_key.verify(OpenSSL::Digest.new(algorithm.sub("RS", "sha")), signature, signing_input)
-
end
-
-
1
def sign_hmac(algorithm, msg, key)
-
OpenSSL::HMAC.digest(OpenSSL::Digest.new(algorithm.sub("HS", "sha")), key, msg)
-
end
-
-
1
def base64url_decode(str)
-
str += "=" * (4 - str.length.modulo(4))
-
Base64.decode64(str.tr("-_", "+/"))
-
end
-
-
1
def base64url_encode(str)
-
Base64.encode64(str).tr("+/", "-_").gsub(/[\n=]/, "")
-
end
-
-
1
def encoded_header(algorithm="HS256", header_fields={})
-
header = {"typ" => "JWT", "alg" => algorithm}.merge(header_fields)
-
base64url_encode(encode_json(header))
-
end
-
-
1
def encoded_payload(payload)
-
base64url_encode(encode_json(payload))
-
end
-
-
1
def encoded_signature(signing_input, key, algorithm)
-
if algorithm == "none"
-
""
-
else
-
signature = sign(algorithm, signing_input, key)
-
base64url_encode(signature)
-
end
-
end
-
-
1
def encode(payload, key, algorithm="HS256", header_fields={})
-
algorithm ||= "none"
-
segments = []
-
segments << encoded_header(algorithm, header_fields)
-
segments << encoded_payload(payload)
-
segments << encoded_signature(segments.join("."), key, algorithm)
-
segments.join(".")
-
end
-
-
1
def raw_segments(jwt, verify=true)
-
segments = jwt.split(".")
-
required_num_segments = verify ? [3] : [2,3]
-
raise JWT::DecodeError.new("Not enough or too many segments") unless required_num_segments.include? segments.length
-
segments
-
end
-
-
1
def decode_header_and_payload(header_segment, payload_segment)
-
header = decode_json(base64url_decode(header_segment))
-
payload = decode_json(base64url_decode(payload_segment))
-
[header, payload]
-
end
-
-
1
def decoded_segments(jwt, verify=true)
-
header_segment, payload_segment, crypto_segment = raw_segments(jwt, verify)
-
header, payload = decode_header_and_payload(header_segment, payload_segment)
-
signature = base64url_decode(crypto_segment.to_s) if verify
-
signing_input = [header_segment, payload_segment].join(".")
-
[header, payload, signature, signing_input]
-
end
-
-
1
def decode(jwt, key=nil, verify=true, &keyfinder)
-
raise JWT::DecodeError.new("Nil JSON web token") unless jwt
-
-
header, payload, signature, signing_input = decoded_segments(jwt, verify)
-
raise JWT::DecodeError.new("Not enough or too many segments") unless header && payload
-
-
if verify
-
algo, key = signature_algorithm_and_key(header, key, &keyfinder)
-
verify_signature(algo, key, signing_input, signature)
-
end
-
return payload,header
-
end
-
-
1
def signature_algorithm_and_key(header, key, &keyfinder)
-
if keyfinder
-
key = keyfinder.call(header)
-
end
-
[header['alg'], key]
-
end
-
-
1
def verify_signature(algo, key, signing_input, signature)
-
begin
-
if ["HS256", "HS384", "HS512"].include?(algo)
-
raise JWT::DecodeError.new("Signature verification failed") unless secure_compare(signature, sign_hmac(algo, signing_input, key))
-
elsif ["RS256", "RS384", "RS512"].include?(algo)
-
raise JWT::DecodeError.new("Signature verification failed") unless verify_rsa(algo, key, signing_input, signature)
-
else
-
raise JWT::DecodeError.new("Algorithm not supported")
-
end
-
rescue OpenSSL::PKey::PKeyError
-
raise JWT::DecodeError.new("Signature verification failed")
-
ensure
-
OpenSSL.errors.clear
-
end
-
end
-
-
# From devise
-
# constant-time comparison algorithm to prevent timing attacks
-
1
def secure_compare(a, b)
-
return false if a.nil? || b.nil? || a.empty? || b.empty? || a.bytesize != b.bytesize
-
l = a.unpack "C#{a.bytesize}"
-
-
res = 0
-
b.each_byte { |byte| res |= byte ^ l.shift }
-
res == 0
-
end
-
-
end
-
1
module JWT
-
1
module Json
-
1
if RUBY_VERSION >= "1.9" && !defined?(MultiJson)
-
require 'json'
-
-
def decode_json(encoded)
-
JSON.parse(encoded)
-
rescue JSON::ParserError
-
raise JWT::DecodeError.new("Invalid segment encoding")
-
end
-
-
def encode_json(raw)
-
JSON.generate(raw)
-
end
-
-
else
-
1
require "multi_json"
-
-
1
def decode_json(encoded)
-
MultiJson.decode(encoded)
-
rescue MultiJson::LoadError
-
raise JWT::DecodeError.new("Invalid segment encoding")
-
end
-
-
1
def encode_json(raw)
-
MultiJson.encode(raw)
-
end
-
end
-
end
-
end
-
1
require 'set'
-
1
module Launchy
-
#
-
# Application is the base class of all the application types that launchy may
-
# invoke. It essentially defines the public api of the launchy system.
-
#
-
# Every class that inherits from Application must define:
-
#
-
# 1. A constructor taking no parameters
-
# 2. An instance method 'open' taking a string or URI as the first parameter and a
-
# hash as the second
-
# 3. A class method 'handles?' that takes a String and returns true if that
-
# class can handle the input.
-
1
class Application
-
1
extend DescendantTracker
-
-
1
class << self
-
# Find the application that handles the given uri.
-
#
-
# returns the Class that can handle the uri
-
1
def handling( uri )
-
klass = find_child( :handles?, uri )
-
return klass if klass
-
raise ApplicationNotFoundError, "No application found to handle '#{uri}'"
-
end
-
-
#
-
# Find the given executable in the available paths
-
1
def find_executable( bin, *paths )
-
paths = ENV['PATH'].split( File::PATH_SEPARATOR ) if paths.empty?
-
paths.each do |path|
-
file = File.join( path, bin )
-
if File.executable?( file ) then
-
Launchy.log "#{self.name} : found executable #{file}"
-
return file
-
end
-
end
-
Launchy.log "#{self.name} : Unable to find `#{bin}' in #{paths.join(", ")}"
-
return nil
-
end
-
end
-
-
1
attr_reader :host_os_family
-
1
attr_reader :ruby_engine
-
1
attr_reader :runner
-
-
1
def initialize
-
@host_os_family = Launchy::Detect::HostOsFamily.detect
-
@ruby_engine = Launchy::Detect::RubyEngine.detect
-
@runner = Launchy::Detect::Runner.detect
-
end
-
-
1
def find_executable( bin, *paths )
-
Application.find_executable( bin, *paths )
-
end
-
-
1
def run( cmd, *args )
-
runner.run( cmd, *args )
-
end
-
end
-
end
-
1
require 'launchy/applications/browser'
-
1
class Launchy::Application
-
#
-
# The class handling the browser application and all of its schemes
-
#
-
1
class Browser < Launchy::Application
-
1
def self.schemes
-
%w[ http https ftp file ]
-
end
-
-
1
def self.handles?( uri )
-
return true if schemes.include?( uri.scheme )
-
return true if File.exist?( uri.path )
-
end
-
-
1
def windows_app_list
-
[ 'start "launchy" /b' ]
-
end
-
-
1
def cygwin_app_list
-
[ 'cmd /C start "launchy" /b' ]
-
end
-
-
# hardcode this to open?
-
1
def darwin_app_list
-
[ find_executable( "open" ) ]
-
end
-
-
1
def nix_app_list
-
nix_de = Launchy::Detect::NixDesktopEnvironment.detect
-
list = nix_de.browsers
-
list.find_all { |argv| argv.valid? }
-
end
-
-
# use a call back mechanism to get the right app_list that is decided by the
-
# host_os_family class.
-
1
def app_list
-
host_os_family.app_list( self )
-
end
-
-
1
def browser_env
-
return [] unless ENV['BROWSER']
-
browser_env = ENV['BROWSER'].split( File::PATH_SEPARATOR )
-
browser_env.flatten!
-
browser_env.delete_if { |b| b.nil? || (b.strip.size == 0) }
-
return browser_env
-
end
-
-
# Get the full commandline of what we are going to add the uri to
-
1
def browser_cmdline
-
browser_env.each do |p|
-
Launchy.log "#{self.class.name} : possibility from BROWSER environment variable : #{p}"
-
end
-
app_list.each do |p|
-
Launchy.log "#{self.class.name} : possibility from app_list : #{p}"
-
end
-
-
possibilities = (browser_env + app_list).flatten
-
-
if browser = possibilities.shift then
-
Launchy.log "#{self.class.name} : Using browser value '#{browser}'"
-
return browser
-
end
-
raise Launchy::CommandNotFoundError, "Unable to find a browser command. If this is unexpected, #{Launchy.bug_report_message}"
-
end
-
-
1
def cmd_and_args( uri, options = {} )
-
cmd = browser_cmdline
-
args = [ uri.to_s ]
-
if cmd =~ /%s/ then
-
cmd.gsub!( /%s/, args.shift )
-
end
-
return [cmd, args]
-
end
-
-
# final assembly of the command and do %s substitution
-
# http://www.catb.org/~esr/BROWSER/index.html
-
1
def open( uri, options = {} )
-
cmd, args = cmd_and_args( uri, options )
-
run( cmd, args )
-
end
-
end
-
end
-
1
module Launchy
-
1
class Argv
-
1
attr_reader :argv
-
1
def initialize( *args )
-
@argv = args.flatten
-
end
-
-
1
def to_s
-
@argv.join(' ')
-
end
-
-
1
def to_str
-
to_s
-
end
-
-
1
def [](idx)
-
@argv[idx]
-
end
-
-
1
def valid?
-
(not blank?) && executable?
-
end
-
-
1
def blank?
-
@argv.empty? || (@argv.first.strip.size == 0)
-
end
-
-
1
def executable?
-
::Launchy::Application.find_executable( @argv.first )
-
end
-
-
1
def ==( other )
-
@argv == other.argv
-
end
-
end
-
end
-
1
require 'optparse'
-
-
1
module Launchy
-
1
class Cli
-
-
1
attr_reader :options
-
1
def initialize
-
@options = {}
-
end
-
-
1
def parser
-
@parser ||= OptionParser.new do |op|
-
op.banner = "Usage: launchy [options] thing-to-launch"
-
-
op.separator ""
-
op.separator "Launch Options:"
-
-
op.on( "-a", "--application APPLICATION",
-
"Explicitly specify the application class to use in the launch") do |app|
-
@options[:application] = app
-
end
-
-
op.on( "-d", "--debug",
-
"Force debug. Output lots of information.") do |d|
-
@options[:debug] = true
-
end
-
-
op.on( "-e", "--engine RUBY_ENGINE",
-
"Force launchy to behave as if it was on a particular ruby engine.") do |e|
-
@options[:ruby_engine] = e
-
end
-
-
op.on( "-n", "--dry-run", "Don't launchy, print the command to be executed on stdout" ) do |x|
-
@options[:dry_run] = true
-
end
-
-
op.on( "-o", "--host-os HOST_OS",
-
"Force launchy to behave as if it was on a particular host os.") do |os|
-
@options[:host_os] = os
-
end
-
-
-
op.separator ""
-
op.separator "Standard Options:"
-
-
op.on( "-h", "--help", "Print this message.") do |h|
-
$stdout.puts op.to_s
-
exit 0
-
end
-
-
op.on( "-v", "--version", "Output the version of Launchy") do |v|
-
$stdout.puts "Launchy version #{Launchy::VERSION}"
-
exit 0
-
end
-
-
end
-
end
-
-
1
def parse( argv, env )
-
parser.parse!( argv )
-
return true
-
rescue ::OptionParser::ParseError => pe
-
error_output( pe )
-
end
-
-
1
def good_run( argv, env )
-
if parse( argv, env ) then
-
Launchy.open( argv.shift, options ) { |e| error_output( e ) }
-
return true
-
else
-
return false
-
end
-
end
-
-
1
def error_output( error )
-
$stderr.puts "ERROR: #{error}"
-
Launchy.log "ERROR: #{error}"
-
error.backtrace.each do |bt|
-
Launchy.log bt
-
end
-
$stderr.puts "Try `#{parser.program_name} --help' for more information."
-
return false
-
end
-
-
1
def run( argv = ARGV, env = ENV )
-
exit 1 unless good_run( argv, env )
-
end
-
end
-
end
-
1
module Launchy
-
#
-
# This class is deprecated and will be removed
-
#
-
1
class Browser
-
1
def self.run( *args )
-
Browser.new.visit( args[0] )
-
end
-
-
1
def visit( url )
-
_warn "You made a call to a deprecated Launchy API. This call should be changed to 'Launchy.open( uri )'"
-
report_caller_context( caller )
-
-
::Launchy.open( url )
-
end
-
-
1
private
-
-
1
def find_caller_context( stack )
-
caller_file = stack.find do |line|
-
not line.index( __FILE__ )
-
end
-
if caller_file then
-
caller_fname, caller_line, _ = caller_file.split(":")
-
if File.readable?( caller_fname ) then
-
caller_lines = IO.readlines( caller_fname )
-
context = [ caller_file ]
-
context << caller_lines[(caller_line.to_i)-3, 5]
-
return context.flatten
-
end
-
end
-
return []
-
end
-
-
1
def report_caller_context( stack )
-
context = find_caller_context( stack )
-
if context.size > 0 then
-
_warn "I think I was able to find the location that needs to be fixed. Please go look at:"
-
_warn
-
context.each do |line|
-
_warn line.rstrip
-
end
-
_warn
-
_warn "If this is not the case, please file a bug. #{Launchy.bug_report_message}"
-
end
-
end
-
-
1
def _warn( msg = "" )
-
warn "WARNING: #{msg}"
-
end
-
end
-
end
-
1
require 'set'
-
-
1
module Launchy
-
#
-
# Use by either
-
#
-
# class Foo
-
# extend DescendantTracker
-
# end
-
#
-
# or
-
#
-
# class Foo
-
# class << self
-
# include DescendantTracker
-
# end
-
# end
-
#
-
# It will track all the classes that inherit from the extended class and keep
-
# them in a Set that is available via the 'children' method.
-
#
-
1
module DescendantTracker
-
1
def inherited( klass )
-
17
return unless klass.instance_of?( Class )
-
17
self.children << klass
-
end
-
-
#
-
# The list of children that are registered
-
#
-
1
def children
-
17
unless defined? @children
-
5
@children = Array.new
-
end
-
17
return @children
-
end
-
-
#
-
# Find one of the child classes by calling the given method
-
# and passing all the rest of the parameters to that method in
-
# each child
-
1
def find_child( method, *args )
-
children.find do |child|
-
Launchy.log "Checking if class #{child} is the one for #{method}(#{args.join(', ')})}"
-
child.send( method, *args )
-
end
-
end
-
end
-
end
-
1
module Launchy
-
1
module Detect
-
end
-
end
-
-
1
require 'launchy/detect/host_os'
-
1
require 'launchy/detect/host_os_family'
-
1
require 'launchy/detect/ruby_engine'
-
1
require 'launchy/detect/nix_desktop_environment'
-
1
require 'launchy/detect/runner'
-
1
require 'rbconfig'
-
-
1
module Launchy::Detect
-
1
class HostOs
-
-
1
attr_reader :host_os
-
1
alias to_s host_os
-
1
alias to_str host_os
-
-
1
def initialize( host_os = nil )
-
@host_os = host_os
-
-
if not @host_os then
-
if @host_os = override_host_os then
-
Launchy.log "Using LAUNCHY_HOST_OS override value of '#{Launchy.host_os}'"
-
else
-
@host_os = default_host_os
-
end
-
end
-
end
-
-
1
def default_host_os
-
::RbConfig::CONFIG['host_os']
-
end
-
-
1
def override_host_os
-
Launchy.host_os
-
end
-
-
end
-
-
end
-
1
module Launchy::Detect
-
# Detect the current host os family
-
#
-
# If the current host familiy cannot be detected then return
-
# HostOsFamily::Unknown
-
1
class HostOsFamily
-
1
class NotFoundError < Launchy::Error; end
-
1
extend ::Launchy::DescendantTracker
-
-
1
class << self
-
-
1
def detect( host_os = HostOs.new )
-
found = find_child( :matches?, host_os )
-
return found.new( host_os ) if found
-
raise NotFoundError, "Unknown OS family for host os '#{host_os}'. #{Launchy.bug_report_message}"
-
end
-
-
1
def matches?( host_os )
-
matching_regex.match( host_os.to_s )
-
end
-
-
1
def windows?() self == Windows; end
-
1
def darwin?() self == Darwin; end
-
1
def nix?() self == Nix; end
-
1
def cygwin?() self == Cygwin; end
-
end
-
-
-
1
attr_reader :host_os
-
1
def initialize( host_os = HostOs.new )
-
@host_os = host_os
-
end
-
-
1
def windows?() self.class.windows?; end
-
1
def darwin?() self.class.darwin?; end
-
1
def nix?() self.class.nix?; end
-
1
def cygwin?() self.class.cygwin?; end
-
-
#---------------------------
-
# All known host os families
-
#---------------------------
-
#
-
1
class Windows < HostOsFamily
-
1
def self.matching_regex
-
/(mingw|mswin|windows)/i
-
end
-
1
def app_list( app ) app.windows_app_list; end
-
end
-
-
1
class Darwin < HostOsFamily
-
1
def self.matching_regex
-
/(darwin|mac os)/i
-
end
-
1
def app_list( app ) app.darwin_app_list; end
-
end
-
-
1
class Nix < HostOsFamily
-
1
def self.matching_regex
-
/(linux|bsd|aix|solaris)/i
-
end
-
1
def app_list( app ) app.nix_app_list; end
-
end
-
-
1
class Cygwin < HostOsFamily
-
1
def self.matching_regex
-
/cygwin/i
-
end
-
1
def app_list( app ) app.cygwin_app_list; end
-
end
-
end
-
end
-
1
module Launchy::Detect
-
#
-
# Detect the current desktop environment for *nix machines
-
# Currently this is Linux centric. The detection is based upon the detection
-
# used by xdg-open from http://portland.freedesktop.org/
-
1
class NixDesktopEnvironment
-
1
class NotFoundError < Launchy::Error; end
-
-
1
extend ::Launchy::DescendantTracker
-
-
# Detect the current *nix desktop environment
-
#
-
# If the current dekstop environment be detected, the return
-
# NixDekstopEnvironment::Unknown
-
1
def self.detect
-
found = find_child( :is_current_desktop_environment? )
-
Launchy.log("Current Desktop environment not found. #{Launchy.bug_report_message}") unless found
-
return found
-
end
-
-
1
def self.fallback_browsers
-
%w[ firefox seamonkey opera mozilla netscape galeon ].map { |x| ::Launchy::Argv.new( x ) }
-
end
-
-
1
def self.browsers
-
[ browser, fallback_browsers ].flatten
-
end
-
-
#---------------------------------------
-
# The list of known desktop environments
-
#---------------------------------------
-
-
1
class Kde < NixDesktopEnvironment
-
1
def self.is_current_desktop_environment?
-
ENV['KDE_FULL_SESSION']
-
end
-
-
1
def self.browser
-
::Launchy::Argv.new( %w[ kfmclient openURL ] )
-
end
-
end
-
-
1
class Gnome < NixDesktopEnvironment
-
1
def self.is_current_desktop_environment?
-
ENV['GNOME_DESKTOP_SESSION_ID']
-
end
-
-
1
def self.browser
-
::Launchy::Argv.new( 'gnome-open' )
-
end
-
end
-
-
1
class Xfce < NixDesktopEnvironment
-
1
def self.is_current_desktop_environment?
-
if Launchy::Application.find_executable( 'xprop' ) then
-
%x[ xprop -root _DT_SAVE_MODE].include?("xfce")
-
else
-
false
-
end
-
end
-
-
1
def self.browser
-
::Launchy::Argv.new( 'exo-open' )
-
end
-
end
-
-
# Fall back environment as the last case
-
1
class Xdg < NixDesktopEnvironment
-
1
def self.is_current_desktop_environment?
-
Launchy::Application.find_executable( browser )
-
end
-
-
1
def self.browser
-
::Launchy::Argv.new( 'xdg-open' )
-
end
-
end
-
-
# The one that is found when all else fails. And this must be declared last
-
1
class NotFound < NixDesktopEnvironment
-
1
def self.is_current_desktop_environment?
-
true
-
end
-
-
1
def self.browser
-
::Launchy::Argv.new
-
end
-
end
-
-
end
-
end
-
-
1
module Launchy::Detect
-
1
class RubyEngine
-
1
class NotFoundError < Launchy::Error; end
-
-
1
extend ::Launchy::DescendantTracker
-
-
# Detect the current ruby engine.
-
#
-
# If the current ruby engine cannot be detected, the return
-
# RubyEngine::Unknown
-
1
def self.detect( ruby_engine = RubyEngine.new )
-
found = find_child( :is_current_engine?, ruby_engine.to_s )
-
return found if found
-
raise NotFoundError, "#{ruby_engine_error_message( ruby_engine )} #{Launchy.bug_report_message}"
-
end
-
-
1
def self.ruby_engine_error_message( ruby_engine )
-
msg = "Unkonwn RUBY_ENGINE "
-
if ruby_engine then
-
msg += " '#{ruby_engine}'."
-
elsif defined?( RUBY_ENGINE ) then
-
msg += " '#{RUBY_ENGINE}'."
-
else
-
msg = "RUBY_ENGINE not defined for #{RUBY_DESCRIPTION}."
-
end
-
return msg
-
end
-
-
1
def self.is_current_engine?( ruby_engine )
-
return ruby_engine == self.engine_name
-
end
-
-
1
def self.mri?() self == Mri; end
-
1
def self.jruby?() self == Jruby; end
-
1
def self.rbx?() self == Rbx; end
-
1
def self.macruby?() self == MacRuby; end
-
-
1
attr_reader :ruby_engine
-
1
alias to_s ruby_engine
-
1
def initialize( ruby_engine = Launchy.ruby_engine )
-
if ruby_engine then
-
@ruby_engine = ruby_engine
-
else
-
@ruby_engine = defined?( RUBY_ENGINE ) ? RUBY_ENGINE : "ruby"
-
end
-
end
-
-
-
#-------------------------------
-
# The list of known ruby engines
-
#-------------------------------
-
-
#
-
# This is the ruby engine if the RUBY_ENGINE constant is not defined
-
1
class Mri < RubyEngine
-
1
def self.engine_name() "ruby"; end
-
1
def self.is_current_engine?( ruby_engine )
-
if ruby_engine then
-
super( ruby_engine )
-
else
-
return true if not Launchy.ruby_engine and not defined?( RUBY_ENGINE )
-
end
-
end
-
end
-
-
1
class Jruby < RubyEngine
-
1
def self.engine_name() "jruby"; end
-
end
-
-
1
class Rbx < RubyEngine
-
1
def self.engine_name() "rbx"; end
-
end
-
-
1
class MacRuby < RubyEngine
-
1
def self.engine_name() "macruby"; end
-
end
-
end
-
end
-
1
require 'shellwords'
-
-
1
module Launchy::Detect
-
1
class Runner
-
1
class NotFoundError < Launchy::Error; end
-
-
1
extend ::Launchy::DescendantTracker
-
-
# Detect the current command runner
-
#
-
# This will return an instance of the Runner to be used to do the
-
# application launching.
-
#
-
# If a runner cannot be detected then raise Runner::NotFoundError
-
#
-
# The runner rules are, in order:
-
#
-
# 1) If you are on windows, you use the Windows Runner no matter what
-
# 2) If you are using the jruby engine, use the Jruby Runner. Unless rule
-
# (1) took effect
-
# 3) Use Forkable (barring rules (1) and (2))
-
1
def self.detect
-
host_os_family = Launchy::Detect::HostOsFamily.detect
-
ruby_engine = Launchy::Detect::RubyEngine.detect
-
-
return Windows.new if host_os_family.windows?
-
if ruby_engine.jruby? then
-
return Jruby.new
-
end
-
return Forkable.new
-
end
-
-
#
-
# cut it down to just the shell commands that will be passed to exec or
-
# posix_spawn. The cmd argument is split according to shell rules and the
-
# args are not escaped because they whole set is passed to system as *args
-
# and in that case system shell escaping rules are not done.
-
#
-
1
def shell_commands( cmd, args )
-
cmdline = [ cmd.to_s.shellsplit ]
-
cmdline << args.flatten.collect{ |a| a.to_s }
-
return commandline_normalize( cmdline )
-
end
-
-
1
def commandline_normalize( cmdline )
-
c = cmdline.flatten!
-
c = c.find_all { |a| (not a.nil?) and ( a.size > 0 ) }
-
Launchy.log "commandline_normalized => #{c.join(' ')}"
-
return c
-
end
-
-
1
def dry_run( cmd, *args )
-
shell_commands(cmd, args).join(" ")
-
end
-
-
1
def run( cmd, *args )
-
raise Launchy::CommandNotFoundError, "No command found to run with args '#{args.join(' ')}'. If this is unexpected, #{Launchy.bug_report_message}" unless cmd
-
if Launchy.dry_run? then
-
$stdout.puts dry_run( cmd, *args )
-
else
-
wet_run( cmd, *args )
-
end
-
end
-
-
-
#---------------------------------------
-
# The list of known runners
-
#---------------------------------------
-
-
1
class Windows < Runner
-
-
1
def all_args( cmd, *args )
-
args = [ 'cmd', '/c', *shell_commands( cmd, *args ) ]
-
Launchy.log "Windows: all_args => #{args.inspect}"
-
return args
-
end
-
-
1
def dry_run( cmd, *args )
-
all_args( cmd, *args ).join(" ")
-
end
-
-
# escape the reserved shell characters in windows command shell
-
# http://technet.microsoft.com/en-us/library/cc723564.aspx
-
#
-
# Also make sure that the item after 'start' is guaranteed to be quoted.
-
# https://github.com/copiousfreetime/launchy/issues/62
-
1
def shell_commands( cmd, *args )
-
parts = cmd.shellsplit
-
-
if start_idx = parts.index('start') then
-
title_idx = start_idx + 1
-
title = parts[title_idx]
-
title = title.sub(/^/,'"') unless title[0] == '"'
-
title = title.sub(/$/,'"') unless title[-1] == '"'
-
parts[title_idx] = title
-
end
-
-
cmdline = [ parts ]
-
cmdline << args.flatten.collect { |a| a.to_s.gsub(/([&|()<>^])/, "^\\1") }
-
return commandline_normalize( cmdline )
-
end
-
-
1
def wet_run( cmd, *args )
-
system( *all_args( cmd, *args ) )
-
end
-
end
-
-
1
class Jruby < Runner
-
1
def wet_run( cmd, *args )
-
require 'spoon'
-
Spoon.spawnp( *shell_commands( cmd, *args ) )
-
end
-
end
-
-
1
class Forkable < Runner
-
1
def wet_run( cmd, *args )
-
child_pid = fork do
-
close_file_descriptors unless Launchy.debug?
-
Launchy.log("wet_run: before exec in child process")
-
exec( *shell_commands( cmd, *args ))
-
exit!
-
end
-
Process.detach( child_pid )
-
end
-
-
1
def close_file_descriptors
-
[$stdin, $stdout, $stderr].each do |io|
-
io.reopen( "/dev/null", "r+" )
-
end
-
end
-
-
1
private :close_file_descriptors
-
end
-
end
-
end
-
1
module Launchy
-
1
class Error < ::StandardError; end
-
1
class ApplicationNotFoundError < Error; end
-
1
class CommandNotFoundError < Error; end
-
1
class ArgumentError < Error; end
-
end
-
1
module Launchy
-
1
VERSION = "2.4.2"
-
-
1
module Version
-
-
1
MAJOR = Integer(VERSION.split('.')[0])
-
1
MINOR = Integer(VERSION.split('.')[1])
-
1
PATCH = Integer(VERSION.split('.')[2])
-
-
1
def self.to_a
-
[MAJOR, MINOR, PATCH]
-
end
-
-
1
def self.to_s
-
VERSION
-
end
-
end
-
end
-
# encoding: utf-8
-
1
module Mail # :doc:
-
-
1
require 'date'
-
1
require 'shellwords'
-
-
1
require 'uri'
-
1
require 'net/smtp'
-
1
require 'mime/types'
-
-
1
if RUBY_VERSION <= '1.8.6'
-
begin
-
require 'tlsmail'
-
rescue LoadError
-
raise "You need to install tlsmail if you are using ruby <= 1.8.6"
-
end
-
end
-
-
1
if RUBY_VERSION >= "1.9.0"
-
1
require 'mail/version_specific/ruby_1_9'
-
1
RubyVer = Ruby19
-
else
-
require 'mail/version_specific/ruby_1_8'
-
RubyVer = Ruby18
-
end
-
-
1
require 'mail/version'
-
-
1
require 'mail/core_extensions/nil'
-
1
require 'mail/core_extensions/object'
-
1
require 'mail/core_extensions/string'
-
1
require 'mail/core_extensions/smtp' if RUBY_VERSION < '1.9.3'
-
1
require 'mail/indifferent_hash'
-
-
# Only load our multibyte extensions if AS is not already loaded
-
1
if defined?(ActiveSupport)
-
1
require 'active_support/inflector'
-
else
-
require 'mail/core_extensions/string/access'
-
require 'mail/core_extensions/string/multibyte'
-
require 'mail/multibyte'
-
end
-
-
1
require 'mail/patterns'
-
1
require 'mail/utilities'
-
1
require 'mail/configuration'
-
-
1
@@autoloads = {}
-
1
def self.register_autoload(name, path)
-
53
@@autoloads[name] = path
-
53
autoload(name, path)
-
end
-
-
# This runs through the autoload list and explictly requires them for you.
-
# Useful when running mail in a threaded process.
-
#
-
# Usage:
-
#
-
# require 'mail'
-
# Mail.eager_autoload!
-
1
def self.eager_autoload!
-
@@autoloads.each { |_,path| require(path) }
-
end
-
-
# Autoload mail send and receive classes.
-
1
require 'mail/network'
-
-
1
require 'mail/message'
-
1
require 'mail/part'
-
1
require 'mail/header'
-
1
require 'mail/parts_list'
-
1
require 'mail/attachments_list'
-
1
require 'mail/body'
-
1
require 'mail/field'
-
1
require 'mail/field_list'
-
-
1
require 'mail/envelope'
-
-
1
require 'mail/parsers'
-
-
# Autoload header field elements and transfer encodings.
-
1
require 'mail/elements'
-
1
require 'mail/encodings'
-
1
require 'mail/encodings/base64'
-
1
require 'mail/encodings/quoted_printable'
-
-
1
require 'mail/matchers/has_sent_mail'
-
-
# Finally... require all the Mail.methods
-
1
require 'mail/mail'
-
end
-
# encoding: utf-8
-
1
module Mail
-
-
# = Body
-
#
-
# The body is where the text of the email is stored. Mail treats the body
-
# as a single object. The body itself has no information about boundaries
-
# used in the MIME standard, it just looks at its content as either a single
-
# block of text, or (if it is a multipart message) as an array of blocks of text.
-
#
-
# A body has to be told to split itself up into a multipart message by calling
-
# #split with the correct boundary. This is because the body object has no way
-
# of knowing what the correct boundary is for itself (there could be many
-
# boundaries in a body in the case of a nested MIME text).
-
#
-
# Once split is called, Mail::Body will slice itself up on this boundary,
-
# assigning anything that appears before the first part to the preamble, and
-
# anything that appears after the closing boundary to the epilogue, then
-
# each part gets initialized into a Mail::Part object.
-
#
-
# The boundary that is used to split up the Body is also stored in the Body
-
# object for use on encoding itself back out to a string. You can
-
# overwrite this if it needs to be changed.
-
#
-
# On encoding, the body will return the preamble, then each part joined by
-
# the boundary, followed by a closing boundary string and then the epilogue.
-
1
class Body
-
-
1
def initialize(string = '')
-
@boundary = nil
-
@preamble = nil
-
@epilogue = nil
-
@charset = nil
-
@part_sort_order = [ "text/plain", "text/enriched", "text/html" ]
-
@parts = Mail::PartsList.new
-
if string.blank?
-
@raw_source = ''
-
else
-
# Do join first incase we have been given an Array in Ruby 1.9
-
if string.respond_to?(:join)
-
@raw_source = string.join('')
-
elsif string.respond_to?(:to_s)
-
@raw_source = string.to_s
-
else
-
raise "You can only assign a string or an object that responds_to? :join or :to_s to a body."
-
end
-
end
-
@encoding = (only_us_ascii? ? '7bit' : '8bit')
-
set_charset
-
end
-
-
# Matches this body with another body. Also matches the decoded value of this
-
# body with a string.
-
#
-
# Examples:
-
#
-
# body = Mail::Body.new('The body')
-
# body == body #=> true
-
#
-
# body = Mail::Body.new('The body')
-
# body == 'The body' #=> true
-
#
-
# body = Mail::Body.new("VGhlIGJvZHk=\n")
-
# body.encoding = 'base64'
-
# body == "The body" #=> true
-
1
def ==(other)
-
if other.class == String
-
self.decoded == other
-
else
-
super
-
end
-
end
-
-
# Accepts a string and performs a regular expression against the decoded text
-
#
-
# Examples:
-
#
-
# body = Mail::Body.new('The body')
-
# body =~ /The/ #=> 0
-
#
-
# body = Mail::Body.new("VGhlIGJvZHk=\n")
-
# body.encoding = 'base64'
-
# body =~ /The/ #=> 0
-
1
def =~(regexp)
-
self.decoded =~ regexp
-
end
-
-
# Accepts a string and performs a regular expression against the decoded text
-
#
-
# Examples:
-
#
-
# body = Mail::Body.new('The body')
-
# body.match(/The/) #=> #<MatchData "The">
-
#
-
# body = Mail::Body.new("VGhlIGJvZHk=\n")
-
# body.encoding = 'base64'
-
# body.match(/The/) #=> #<MatchData "The">
-
1
def match(regexp)
-
self.decoded.match(regexp)
-
end
-
-
# Accepts anything that responds to #to_s and checks if it's a substring of the decoded text
-
#
-
# Examples:
-
#
-
# body = Mail::Body.new('The body')
-
# body.include?('The') #=> true
-
#
-
# body = Mail::Body.new("VGhlIGJvZHk=\n")
-
# body.encoding = 'base64'
-
# body.include?('The') #=> true
-
1
def include?(other)
-
self.decoded.include?(other.to_s)
-
end
-
-
# Allows you to set the sort order of the parts, overriding the default sort order.
-
# Defaults to 'text/plain', then 'text/enriched', then 'text/html' with any other content
-
# type coming after.
-
1
def set_sort_order(order)
-
@part_sort_order = order
-
end
-
-
# Allows you to sort the parts according to the default sort order, or the sort order you
-
# set with :set_sort_order.
-
#
-
# sort_parts! is also called from :encode, so there is no need for you to call this explicitly
-
1
def sort_parts!
-
@parts.each do |p|
-
p.body.set_sort_order(@part_sort_order)
-
p.body.sort_parts!
-
end
-
@parts.sort!(@part_sort_order)
-
end
-
-
# Returns the raw source that the body was initialized with, without
-
# any tampering
-
1
def raw_source
-
@raw_source
-
end
-
-
1
def get_best_encoding(target)
-
target_encoding = Mail::Encodings.get_encoding(target)
-
target_encoding.get_best_compatible(encoding, raw_source)
-
end
-
-
# Returns a body encoded using transfer_encoding. Multipart always uses an
-
# identiy encoding (i.e. no encoding).
-
# Calling this directly is not a good idea, but supported for compatibility
-
# TODO: Validate that preamble and epilogue are valid for requested encoding
-
1
def encoded(transfer_encoding = '8bit')
-
if multipart?
-
self.sort_parts!
-
encoded_parts = parts.map { |p| p.encoded }
-
([preamble] + encoded_parts).join(crlf_boundary) + end_boundary + epilogue.to_s
-
else
-
be = get_best_encoding(transfer_encoding)
-
dec = Mail::Encodings::get_encoding(encoding)
-
enc = Mail::Encodings::get_encoding(be)
-
if transfer_encoding == encoding and dec.nil?
-
# Cannot decode, so skip normalization
-
raw_source
-
else
-
# Decode then encode to normalize and allow transforming
-
# from base64 to Q-P and vice versa
-
decoded = dec.decode(raw_source)
-
if defined?(Encoding) && charset && charset != "US-ASCII"
-
decoded.encode!(charset)
-
decoded.force_encoding('BINARY') unless Encoding.find(charset).ascii_compatible?
-
end
-
enc.encode(decoded)
-
end
-
end
-
end
-
-
1
def decoded
-
if !Encodings.defined?(encoding)
-
raise UnknownEncodingType, "Don't know how to decode #{encoding}, please call #encoded and decode it yourself."
-
else
-
Encodings.get_encoding(encoding).decode(raw_source)
-
end
-
end
-
-
1
def to_s
-
decoded
-
end
-
-
1
def charset
-
@charset
-
end
-
-
1
def charset=( val )
-
@charset = val
-
end
-
-
1
def encoding(val = nil)
-
if val
-
self.encoding = val
-
else
-
@encoding
-
end
-
end
-
-
1
def encoding=( val )
-
@encoding = if val == "text" || val.blank?
-
(only_us_ascii? ? '7bit' : '8bit')
-
else
-
val
-
end
-
end
-
-
# Returns the preamble (any text that is before the first MIME boundary)
-
1
def preamble
-
@preamble
-
end
-
-
# Sets the preamble to a string (adds text before the first MIME boundary)
-
1
def preamble=( val )
-
@preamble = val
-
end
-
-
# Returns the epilogue (any text that is after the last MIME boundary)
-
1
def epilogue
-
@epilogue
-
end
-
-
# Sets the epilogue to a string (adds text after the last MIME boundary)
-
1
def epilogue=( val )
-
@epilogue = val
-
end
-
-
# Returns true if there are parts defined in the body
-
1
def multipart?
-
true unless parts.empty?
-
end
-
-
# Returns the boundary used by the body
-
1
def boundary
-
@boundary
-
end
-
-
# Allows you to change the boundary of this Body object
-
1
def boundary=( val )
-
@boundary = val
-
end
-
-
1
def parts
-
@parts
-
end
-
-
1
def <<( val )
-
if @parts
-
@parts << val
-
else
-
@parts = Mail::PartsList.new[val]
-
end
-
end
-
-
1
def split!(boundary)
-
self.boundary = boundary
-
parts = raw_source.split(/(?:\A|\r\n)--#{Regexp.escape(boundary || "")}(?=(?:--)?\s*$)/)
-
# Make the preamble equal to the preamble (if any)
-
self.preamble = parts[0].to_s.strip
-
# Make the epilogue equal to the epilogue (if any)
-
self.epilogue = parts[-1].to_s.sub('--', '').strip
-
parts[1...-1].to_a.each { |part| @parts << Mail::Part.new(part) }
-
self
-
end
-
-
1
def only_us_ascii?
-
!(raw_source =~ /[^\x01-\x7f]/)
-
end
-
-
1
def empty?
-
!!raw_source.to_s.empty?
-
end
-
-
1
private
-
-
1
def crlf_boundary
-
"\r\n--#{boundary}\r\n"
-
end
-
-
1
def end_boundary
-
"\r\n--#{boundary}--\r\n"
-
end
-
-
1
def set_charset
-
only_us_ascii? ? @charset = 'US-ASCII' : @charset = nil
-
end
-
end
-
end
-
1
module Mail
-
1
module CheckDeliveryParams
-
1
def check_delivery_params(mail)
-
if mail.smtp_envelope_from.blank?
-
raise ArgumentError.new('An SMTP From address is required to send a message. Set the message smtp_envelope_from, return_path, sender, or from address.')
-
end
-
-
if mail.smtp_envelope_to.blank?
-
raise ArgumentError.new('An SMTP To address is required to send a message. Set the message smtp_envelope_to, to, cc, or bcc address.')
-
end
-
-
message = mail.encoded if mail.respond_to?(:encoded)
-
if message.blank?
-
raise ArgumentError.new('An encoded message is required to send an email')
-
end
-
-
[mail.smtp_envelope_from, mail.smtp_envelope_to, message]
-
end
-
end
-
end
-
# encoding: utf-8
-
#
-
# Thanks to Nicolas Fouché for this wrapper
-
#
-
1
require 'singleton'
-
-
1
module Mail
-
-
# The Configuration class is a Singleton used to hold the default
-
# configuration for all Mail objects.
-
#
-
# Each new mail object gets a copy of these values at initialization
-
# which can be overwritten on a per mail object basis.
-
1
class Configuration
-
1
include Singleton
-
-
1
def initialize
-
@delivery_method = nil
-
@retriever_method = nil
-
super
-
end
-
-
1
def delivery_method(method = nil, settings = {})
-
return @delivery_method if @delivery_method && method.nil?
-
@delivery_method = lookup_delivery_method(method).new(settings)
-
end
-
-
1
def lookup_delivery_method(method)
-
case method.is_a?(String) ? method.to_sym : method
-
when nil
-
Mail::SMTP
-
when :smtp
-
Mail::SMTP
-
when :sendmail
-
Mail::Sendmail
-
when :exim
-
Mail::Exim
-
when :file
-
Mail::FileDelivery
-
when :smtp_connection
-
Mail::SMTPConnection
-
when :test
-
Mail::TestMailer
-
else
-
method
-
end
-
end
-
-
1
def retriever_method(method = nil, settings = {})
-
return @retriever_method if @retriever_method && method.nil?
-
@retriever_method = lookup_retriever_method(method).new(settings)
-
end
-
-
1
def lookup_retriever_method(method)
-
case method
-
when nil
-
Mail::POP3
-
when :pop3
-
Mail::POP3
-
when :imap
-
Mail::IMAP
-
when :test
-
Mail::TestRetriever
-
else
-
method
-
end
-
end
-
-
1
def param_encode_language(value = nil)
-
value ? @encode_language = value : @encode_language ||= 'en'
-
end
-
-
end
-
-
end
-
# encoding: utf-8
-
-
# This is not loaded if ActiveSupport is already loaded
-
-
1
class NilClass #:nodoc:
-
1
unless nil.respond_to? :blank?
-
def blank?
-
true
-
end
-
end
-
-
1
def to_crlf
-
''
-
end
-
-
1
def to_lf
-
''
-
end
-
end
-
# encoding: utf-8
-
-
1
unless Object.method_defined? :blank?
-
class Object
-
def blank?
-
if respond_to?(:empty?)
-
empty?
-
else
-
!self
-
end
-
end
-
end
-
end
-
# encoding: utf-8
-
1
class String #:nodoc:
-
-
1
if RUBY_VERSION >= '1.9'
-
# This 1.9 only regex can save a reasonable amount of time (~20%)
-
# by not matching "\r\n" so the string is returned unchanged in
-
# the common case.
-
1
CRLF_REGEX = Regexp.new("(?<!\r)\n|\r(?!\n)")
-
else
-
CRLF_REGEX = /\n|\r\n|\r/
-
end
-
-
1
def to_crlf
-
to_str.gsub(CRLF_REGEX, "\r\n")
-
end
-
-
1
def to_lf
-
to_str.gsub(/\r\n|\r/, "\n")
-
end
-
-
167
unless String.instance_methods(false).map {|m| m.to_sym}.include?(:blank?)
-
def blank?
-
self !~ /\S/
-
end
-
end
-
-
1
unless method_defined?(:ascii_only?)
-
# Backport from Ruby 1.9 checks for non-us-ascii characters.
-
def ascii_only?
-
self !~ MATCH_NON_US_ASCII
-
end
-
-
MATCH_NON_US_ASCII = /[^\x00-\x7f]/
-
end
-
-
1
def not_ascii_only?
-
!ascii_only?
-
end
-
-
1
unless method_defined?(:bytesize)
-
alias :bytesize :length
-
end
-
end
-
1
module Mail
-
1
register_autoload :Address, 'mail/elements/address'
-
1
register_autoload :AddressList, 'mail/elements/address_list'
-
1
register_autoload :ContentDispositionElement, 'mail/elements/content_disposition_element'
-
1
register_autoload :ContentLocationElement, 'mail/elements/content_location_element'
-
1
register_autoload :ContentTransferEncodingElement, 'mail/elements/content_transfer_encoding_element'
-
1
register_autoload :ContentTypeElement, 'mail/elements/content_type_element'
-
1
register_autoload :DateTimeElement, 'mail/elements/date_time_element'
-
1
register_autoload :EnvelopeFromElement, 'mail/elements/envelope_from_element'
-
1
register_autoload :MessageIdsElement, 'mail/elements/message_ids_element'
-
1
register_autoload :MimeVersionElement, 'mail/elements/mime_version_element'
-
1
register_autoload :PhraseList, 'mail/elements/phrase_list'
-
1
register_autoload :ReceivedElement, 'mail/elements/received_element'
-
end
-
# encoding: utf-8
-
-
1
module Mail
-
# Raised when attempting to decode an unknown encoding type
-
1
class UnknownEncodingType < StandardError #:nodoc:
-
end
-
-
1
module Encodings
-
-
1
include Mail::Patterns
-
1
extend Mail::Utilities
-
-
1
@transfer_encodings = {}
-
-
# Register transfer encoding
-
#
-
# Example
-
#
-
# Encodings.register "base64", Mail::Encodings::Base64
-
1
def Encodings.register(name, cls)
-
5
@transfer_encodings[get_name(name)] = cls
-
end
-
-
# Is the encoding we want defined?
-
#
-
# Example:
-
#
-
# Encodings.defined?(:base64) #=> true
-
1
def Encodings.defined?( str )
-
@transfer_encodings.include? get_name(str)
-
end
-
-
# Gets a defined encoding type, QuotedPrintable or Base64 for now.
-
#
-
# Each encoding needs to be defined as a Mail::Encodings::ClassName for
-
# this to work, allows us to add other encodings in the future.
-
#
-
# Example:
-
#
-
# Encodings.get_encoding(:base64) #=> Mail::Encodings::Base64
-
1
def Encodings.get_encoding( str )
-
@transfer_encodings[get_name(str)]
-
end
-
-
1
def Encodings.get_all
-
@transfer_encodings.values
-
end
-
-
1
def Encodings.get_name(enc)
-
5
enc = enc.to_s.gsub("-", "_").downcase
-
end
-
-
# Encodes a parameter value using URI Escaping, note the language field 'en' can
-
# be set using Mail::Configuration, like so:
-
#
-
# Mail.defaults do
-
# param_encode_language 'jp'
-
# end
-
#
-
# The character set used for encoding will either be the value of $KCODE for
-
# Ruby < 1.9 or the encoding on the string passed in.
-
#
-
# Example:
-
#
-
# Mail::Encodings.param_encode("This is fun") #=> "us-ascii'en'This%20is%20fun"
-
1
def Encodings.param_encode(str)
-
case
-
when str.ascii_only? && str =~ TOKEN_UNSAFE
-
%Q{"#{str}"}
-
when str.ascii_only?
-
str
-
else
-
RubyVer.param_encode(str)
-
end
-
end
-
-
# Decodes a parameter value using URI Escaping.
-
#
-
# Example:
-
#
-
# Mail::Encodings.param_decode("This%20is%20fun", 'us-ascii') #=> "This is fun"
-
#
-
# str = Mail::Encodings.param_decode("This%20is%20fun", 'iso-8559-1')
-
# str.encoding #=> 'ISO-8859-1' ## Only on Ruby 1.9
-
# str #=> "This is fun"
-
1
def Encodings.param_decode(str, encoding)
-
RubyVer.param_decode(str, encoding)
-
end
-
-
# Decodes or encodes a string as needed for either Base64 or QP encoding types in
-
# the =?<encoding>?[QB]?<string>?=" format.
-
#
-
# The output type needs to be :decode to decode the input string or :encode to
-
# encode the input string. The character set used for encoding will either be
-
# the value of $KCODE for Ruby < 1.9 or the encoding on the string passed in.
-
#
-
# On encoding, will only send out Base64 encoded strings.
-
1
def Encodings.decode_encode(str, output_type)
-
case
-
when output_type == :decode
-
Encodings.value_decode(str)
-
else
-
if str.ascii_only?
-
str
-
else
-
Encodings.b_value_encode(str, find_encoding(str))
-
end
-
end
-
end
-
-
# Decodes a given string as Base64 or Quoted Printable, depending on what
-
# type it is.
-
#
-
# String has to be of the format =?<encoding>?[QB]?<string>?=
-
1
def Encodings.value_decode(str)
-
# Optimization: If there's no encoded-words in the string, just return it
-
return str unless str =~ /\=\?[^?]+\?[QB]\?[^?]+?\?\=/xmi
-
-
lines = collapse_adjacent_encodings(str)
-
-
# Split on white-space boundaries with capture, so we capture the white-space as well
-
lines.map do |line|
-
line.split(/([ \t])/).map do |text|
-
if text.index('=?').nil?
-
text
-
else
-
# Search for occurences of quoted strings or plain strings
-
text.scan(/( # Group around entire regex to include it in matches
-
\=\?[^?]+\?([QB])\?[^?]+?\?\= # Quoted String with subgroup for encoding method
-
| # or
-
.+?(?=\=\?|$) # Plain String
-
)/xmi).map do |matches|
-
string, method = *matches
-
if method == 'b' || method == 'B'
-
b_value_decode(string)
-
elsif method == 'q' || method == 'Q'
-
q_value_decode(string)
-
else
-
string
-
end
-
end
-
end
-
end
-
end.flatten.join("")
-
end
-
-
# Takes an encoded string of the format =?<encoding>?[QB]?<string>?=
-
1
def Encodings.unquote_and_convert_to(str, to_encoding)
-
output = value_decode( str ).to_s # output is already converted to UTF-8
-
-
if 'utf8' == to_encoding.to_s.downcase.gsub("-", "")
-
output
-
elsif to_encoding
-
begin
-
if RUBY_VERSION >= '1.9'
-
output.encode(to_encoding)
-
else
-
require 'iconv'
-
Iconv.iconv(to_encoding, 'UTF-8', output).first
-
end
-
rescue Iconv::IllegalSequence, Iconv::InvalidEncoding, Errno::EINVAL
-
# the 'from' parameter specifies a charset other than what the text
-
# actually is...not much we can do in this case but just return the
-
# unconverted text.
-
#
-
# Ditto if either parameter represents an unknown charset, like
-
# X-UNKNOWN.
-
output
-
end
-
else
-
output
-
end
-
end
-
-
1
def Encodings.address_encode(address, charset = 'utf-8')
-
if address.is_a?(Array)
-
# loop back through for each element
-
address.compact.map { |a| Encodings.address_encode(a, charset) }.join(", ")
-
else
-
# find any word boundary that is not ascii and encode it
-
encode_non_usascii(address, charset) if address
-
end
-
end
-
-
1
def Encodings.encode_non_usascii(address, charset)
-
return address if address.ascii_only? or charset.nil?
-
us_ascii = %Q{\x00-\x7f}
-
# Encode any non usascii strings embedded inside of quotes
-
address = address.gsub(/(".*?[^#{us_ascii}].*?")/) { |s| Encodings.b_value_encode(unquote(s), charset) }
-
# Then loop through all remaining items and encode as needed
-
tokens = address.split(/\s/)
-
map_with_index(tokens) do |word, i|
-
if word.ascii_only?
-
word
-
else
-
previous_non_ascii = i>0 && tokens[i-1] && !tokens[i-1].ascii_only?
-
if previous_non_ascii #why are we adding an extra space here?
-
word = " #{word}"
-
end
-
Encodings.b_value_encode(word, charset)
-
end
-
end.join(' ')
-
end
-
-
# Encode a string with Base64 Encoding and returns it ready to be inserted
-
# as a value for a field, that is, in the =?<charset>?B?<string>?= format
-
#
-
# Example:
-
#
-
# Encodings.b_value_encode('This is あ string', 'UTF-8')
-
# #=> "=?UTF-8?B?VGhpcyBpcyDjgYIgc3RyaW5n?="
-
1
def Encodings.b_value_encode(encoded_str, encoding = nil)
-
return encoded_str if encoded_str.to_s.ascii_only?
-
string, encoding = RubyVer.b_value_encode(encoded_str, encoding)
-
map_lines(string) do |str|
-
"=?#{encoding}?B?#{str.chomp}?="
-
end.join(" ")
-
end
-
-
# Encode a string with Quoted-Printable Encoding and returns it ready to be inserted
-
# as a value for a field, that is, in the =?<charset>?Q?<string>?= format
-
#
-
# Example:
-
#
-
# Encodings.q_value_encode('This is あ string', 'UTF-8')
-
# #=> "=?UTF-8?Q?This_is_=E3=81=82_string?="
-
1
def Encodings.q_value_encode(encoded_str, encoding = nil)
-
return encoded_str if encoded_str.to_s.ascii_only?
-
string, encoding = RubyVer.q_value_encode(encoded_str, encoding)
-
string.gsub!("=\r\n", '') # We already have limited the string to the length we want
-
map_lines(string) do |str|
-
"=?#{encoding}?Q?#{str.chomp.gsub(/ /, '_')}?="
-
end.join(" ")
-
end
-
-
1
private
-
-
# Decodes a Base64 string from the "=?UTF-8?B?VGhpcyBpcyDjgYIgc3RyaW5n?=" format
-
#
-
# Example:
-
#
-
# Encodings.b_value_decode("=?UTF-8?B?VGhpcyBpcyDjgYIgc3RyaW5n?=")
-
# #=> 'This is あ string'
-
1
def Encodings.b_value_decode(str)
-
RubyVer.b_value_decode(str)
-
end
-
-
# Decodes a Quoted-Printable string from the "=?UTF-8?Q?This_is_=E3=81=82_string?=" format
-
#
-
# Example:
-
#
-
# Encodings.q_value_decode("=?UTF-8?Q?This_is_=E3=81=82_string?=")
-
# #=> 'This is あ string'
-
1
def Encodings.q_value_decode(str)
-
RubyVer.q_value_decode(str)
-
end
-
-
1
def Encodings.split_encoding_from_string( str )
-
match = str.match(/\=\?([^?]+)?\?[QB]\?(.+)?\?\=/mi)
-
if match
-
match[1]
-
else
-
nil
-
end
-
end
-
-
1
def Encodings.find_encoding(str)
-
RUBY_VERSION >= '1.9' ? str.encoding : $KCODE
-
end
-
-
# Gets the encoding type (Q or B) from the string.
-
1
def Encodings.split_value_encoding_from_string(str)
-
match = str.match(/\=\?[^?]+?\?([QB])\?(.+)?\?\=/mi)
-
if match
-
match[1]
-
else
-
nil
-
end
-
end
-
-
# When the encoded string consists of multiple lines, lines with the same
-
# encoding (Q or B) can be joined together.
-
#
-
# String has to be of the format =?<encoding>?[QB]?<string>?=
-
1
def Encodings.collapse_adjacent_encodings(str)
-
lines = str.split(/(\?=)\s*(=\?)/).each_slice(2).map(&:join)
-
results = []
-
previous_encoding = nil
-
-
lines.each do |line|
-
encoding = split_value_encoding_from_string(line)
-
-
if encoding == previous_encoding
-
line = results.pop + line
-
end
-
-
previous_encoding = encoding
-
results << line
-
end
-
-
results
-
end
-
end
-
end
-
# encoding: utf-8
-
1
require 'mail/encodings/8bit'
-
-
1
module Mail
-
1
module Encodings
-
1
class SevenBit < EightBit
-
1
NAME = '7bit'
-
-
1
PRIORITY = 1
-
-
# 7bit and 8bit operate the same
-
-
# Decode the string
-
1
def self.decode(str)
-
super
-
end
-
-
# Encode the string
-
1
def self.encode(str)
-
super
-
end
-
-
# Idenity encodings have a fixed cost, 1 byte out per 1 byte in
-
1
def self.cost(str)
-
super
-
end
-
-
1
Encodings.register(NAME, self)
-
end
-
end
-
end
-
# encoding: utf-8
-
1
require 'mail/encodings/binary'
-
-
1
module Mail
-
1
module Encodings
-
1
class EightBit < Binary
-
1
NAME = '8bit'
-
-
1
PRIORITY = 4
-
-
# 8bit is an identiy encoding, meaning nothing to do
-
-
# Decode the string
-
1
def self.decode(str)
-
str.to_lf
-
end
-
-
# Encode the string
-
1
def self.encode(str)
-
str.to_crlf
-
end
-
-
# Idenity encodings have a fixed cost, 1 byte out per 1 byte in
-
1
def self.cost(str)
-
1.0
-
end
-
-
1
Encodings.register(NAME, self)
-
end
-
end
-
end
-
# encoding: utf-8
-
1
require 'mail/encodings/7bit'
-
-
1
module Mail
-
1
module Encodings
-
1
class Base64 < SevenBit
-
1
NAME = 'base64'
-
-
1
PRIORITY = 3
-
-
1
def self.can_encode?(enc)
-
true
-
end
-
-
# Decode the string from Base64
-
1
def self.decode(str)
-
RubyVer.decode_base64( str )
-
end
-
-
# Encode the string to Base64
-
1
def self.encode(str)
-
RubyVer.encode_base64( str ).to_crlf
-
end
-
-
# Base64 has a fixed cost, 4 bytes out per 3 bytes in
-
1
def self.cost(str)
-
4.0/3
-
end
-
-
1
Encodings.register(NAME, self)
-
end
-
end
-
end
-
# encoding: utf-8
-
1
require 'mail/encodings/transfer_encoding'
-
-
1
module Mail
-
1
module Encodings
-
1
class Binary < TransferEncoding
-
1
NAME = 'binary'
-
-
1
PRIORITY = 5
-
-
# Binary is an identiy encoding, meaning nothing to do
-
-
# Decode the string
-
1
def self.decode(str)
-
str
-
end
-
-
# Encode the string
-
1
def self.encode(str)
-
str
-
end
-
-
# Idenity encodings have a fixed cost, 1 byte out per 1 byte in
-
1
def self.cost(str)
-
1.0
-
end
-
-
1
Encodings.register(NAME, self)
-
end
-
end
-
end
-
# encoding: utf-8
-
1
require 'mail/encodings/7bit'
-
-
1
module Mail
-
1
module Encodings
-
1
class QuotedPrintable < SevenBit
-
1
NAME='quoted-printable'
-
-
1
PRIORITY = 2
-
-
1
def self.can_encode?(str)
-
EightBit.can_encode? str
-
end
-
-
# Decode the string from Quoted-Printable. Cope with hard line breaks
-
# that were incorrectly encoded as hex instead of literal CRLF.
-
1
def self.decode(str)
-
str.gsub(/(?:=0D=0A|=0D|=0A)\r\n/, "\r\n").unpack("M*").first.to_lf
-
end
-
-
1
def self.encode(str)
-
[str.to_lf].pack("M").to_crlf
-
end
-
-
1
def self.cost(str)
-
# These bytes probably do not need encoding
-
c = str.count("\x9\xA\xD\x20-\x3C\x3E-\x7E")
-
# Everything else turns into =XX where XX is a
-
# two digit hex number (taking 3 bytes)
-
total = (str.bytesize - c)*3 + c
-
total.to_f/str.bytesize
-
end
-
-
1
private
-
-
1
Encodings.register(NAME, self)
-
end
-
end
-
end
-
# encoding: utf-8
-
1
module Mail
-
1
module Encodings
-
1
class TransferEncoding
-
1
NAME = ''
-
-
1
PRIORITY = -1
-
-
1
def self.can_transport?(enc)
-
enc = Encodings.get_name(enc)
-
if Encodings.defined? enc
-
Encodings.get_encoding(enc).new.is_a? self
-
else
-
false
-
end
-
end
-
-
1
def self.can_encode?(enc)
-
can_transport? enc
-
end
-
-
1
def self.cost(str)
-
raise "Unimplemented"
-
end
-
-
1
def self.to_s
-
self::NAME
-
end
-
-
1
def self.get_best_compatible(source_encoding, str)
-
if self.can_transport? source_encoding then
-
source_encoding
-
else
-
choices = []
-
Encodings.get_all.each do |enc|
-
choices << enc if self.can_transport? enc and enc.can_encode? source_encoding
-
end
-
best = nil
-
best_cost = 100
-
choices.each do |enc|
-
this_cost = enc.cost str
-
if this_cost < best_cost then
-
best_cost = this_cost
-
best = enc
-
elsif this_cost == best_cost then
-
best = enc if enc::PRIORITY < best::PRIORITY
-
end
-
end
-
best
-
end
-
end
-
-
1
def to_s
-
self.class.to_s
-
end
-
end
-
end
-
end
-
# encoding: utf-8
-
#
-
# = Mail Envelope
-
#
-
# The Envelope class provides a field for the first line in an
-
# mbox file, that looks like "From mikel@test.lindsaar.net DATETIME"
-
#
-
# This envelope class reads that line, and turns it into an
-
# Envelope.from and Envelope.date for your use.
-
1
module Mail
-
1
class Envelope < StructuredField
-
-
1
def initialize(*args)
-
super(FIELD_NAME, strip_field(FIELD_NAME, args.last))
-
end
-
-
1
def element
-
@element ||= Mail::EnvelopeFromElement.new(value)
-
end
-
-
1
def date
-
::DateTime.parse("#{element.date_time}")
-
end
-
-
1
def from
-
element.address
-
end
-
-
end
-
end
-
1
require 'mail/fields'
-
-
# encoding: utf-8
-
1
module Mail
-
# Provides a single class to call to create a new structured or unstructured
-
# field. Works out per RFC what field of field it is being given and returns
-
# the correct field of class back on new.
-
#
-
# ===Per RFC 2822
-
#
-
# 2.2. Header Fields
-
#
-
# Header fields are lines composed of a field name, followed by a colon
-
# (":"), followed by a field body, and terminated by CRLF. A field
-
# name MUST be composed of printable US-ASCII characters (i.e.,
-
# characters that have values between 33 and 126, inclusive), except
-
# colon. A field body may be composed of any US-ASCII characters,
-
# except for CR and LF. However, a field body may contain CRLF when
-
# used in header "folding" and "unfolding" as described in section
-
# 2.2.3. All field bodies MUST conform to the syntax described in
-
# sections 3 and 4 of this standard.
-
#
-
1
class Field
-
-
1
include Utilities
-
1
include Comparable
-
-
1
STRUCTURED_FIELDS = %w[ bcc cc content-description content-disposition
-
content-id content-location content-transfer-encoding
-
content-type date from in-reply-to keywords message-id
-
mime-version received references reply-to
-
resent-bcc resent-cc resent-date resent-from
-
resent-message-id resent-sender resent-to
-
return-path sender to ]
-
-
1
KNOWN_FIELDS = STRUCTURED_FIELDS + ['comments', 'subject']
-
-
1
FIELDS_MAP = {
-
"to" => ToField,
-
"cc" => CcField,
-
"bcc" => BccField,
-
"message-id" => MessageIdField,
-
"in-reply-to" => InReplyToField,
-
"references" => ReferencesField,
-
"subject" => SubjectField,
-
"comments" => CommentsField,
-
"keywords" => KeywordsField,
-
"date" => DateField,
-
"from" => FromField,
-
"sender" => SenderField,
-
"reply-to" => ReplyToField,
-
"resent-date" => ResentDateField,
-
"resent-from" => ResentFromField,
-
"resent-sender" => ResentSenderField,
-
"resent-to" => ResentToField,
-
"resent-cc" => ResentCcField,
-
"resent-bcc" => ResentBccField,
-
"resent-message-id" => ResentMessageIdField,
-
"return-path" => ReturnPathField,
-
"received" => ReceivedField,
-
"mime-version" => MimeVersionField,
-
"content-transfer-encoding" => ContentTransferEncodingField,
-
"content-description" => ContentDescriptionField,
-
"content-disposition" => ContentDispositionField,
-
"content-type" => ContentTypeField,
-
"content-id" => ContentIdField,
-
"content-location" => ContentLocationField,
-
}
-
-
1
FIELD_NAME_MAP = FIELDS_MAP.inject({}) do |map, (field, field_klass)|
-
29
map.update(field => field_klass::CAPITALIZED_FIELD)
-
end
-
-
# Generic Field Exception
-
1
class FieldError < StandardError
-
end
-
-
# Raised when a parsing error has occurred (ie, a StructuredField has tried
-
# to parse a field that is invalid or improperly written)
-
1
class ParseError < FieldError #:nodoc:
-
1
attr_accessor :element, :value, :reason
-
-
1
def initialize(element, value, reason)
-
@element = element
-
@value = value
-
@reason = reason
-
super("#{element} can not parse |#{value}|\nReason was: #{reason}")
-
end
-
end
-
-
# Raised when attempting to set a structured field's contents to an invalid syntax
-
1
class SyntaxError < FieldError #:nodoc:
-
end
-
-
# Accepts a string:
-
#
-
# Field.new("field-name: field data")
-
#
-
# Or name, value pair:
-
#
-
# Field.new("field-name", "value")
-
#
-
# Or a name by itself:
-
#
-
# Field.new("field-name")
-
#
-
# Note, does not want a terminating carriage return. Returns
-
# self appropriately parsed. If value is not a string, then
-
# it will be passed through as is, for example, content-type
-
# field can accept an array with the type and a hash of
-
# parameters:
-
#
-
# Field.new('content-type', ['text', 'plain', {:charset => 'UTF-8'}])
-
1
def initialize(name, value = nil, charset = 'utf-8')
-
case
-
when name =~ /:/ # Field.new("field-name: field data")
-
@charset = value.blank? ? charset : value
-
@name = name[FIELD_PREFIX]
-
@raw_value = name
-
@value = nil
-
when name !~ /:/ && value.blank? # Field.new("field-name")
-
@name = name
-
@value = nil
-
@raw_value = nil
-
@charset = charset
-
else # Field.new("field-name", "value")
-
@name = name
-
@value = value
-
@raw_value = nil
-
@charset = charset
-
end
-
@name = FIELD_NAME_MAP[@name.to_s.downcase] || @name
-
end
-
-
1
def field=(value)
-
@field = value
-
end
-
-
1
def field
-
_, @value = split(@raw_value) if @raw_value && !@value
-
@field ||= create_field(@name, @value, @charset)
-
end
-
-
1
def name
-
@name
-
end
-
-
1
def value
-
field.value
-
end
-
-
1
def value=(val)
-
@field = create_field(name, val, @charset)
-
end
-
-
1
def to_s
-
field.to_s
-
end
-
-
1
def inspect
-
"#<#{self.class.name} 0x#{(object_id * 2).to_s(16)} #{instance_variables.map do |ivar|
-
"#{ivar}=#{instance_variable_get(ivar).inspect}"
-
end.join(" ")}>"
-
end
-
-
1
def update(name, value)
-
@field = create_field(name, value, @charset)
-
end
-
-
1
def same( other )
-
match_to_s(other.name, self.name)
-
end
-
-
1
def responsible_for?( val )
-
name.to_s.casecmp(val.to_s) == 0
-
end
-
-
1
alias_method :==, :same
-
-
1
def <=>( other )
-
self.field_order_id <=> other.field_order_id
-
end
-
-
1
def field_order_id
-
@field_order_id ||= (FIELD_ORDER_LOOKUP[self.name.to_s.downcase] || 100)
-
end
-
-
1
def method_missing(name, *args, &block)
-
field.send(name, *args, &block)
-
end
-
-
1
FIELD_ORDER = %w[ return-path received
-
resent-date resent-from resent-sender resent-to
-
resent-cc resent-bcc resent-message-id
-
date from sender reply-to to cc bcc
-
message-id in-reply-to references
-
subject comments keywords
-
mime-version content-type content-transfer-encoding
-
content-location content-disposition content-description ]
-
-
1
FIELD_ORDER_LOOKUP = Hash[FIELD_ORDER.each_with_index.to_a]
-
-
1
private
-
-
1
def split(raw_field)
-
match_data = raw_field.mb_chars.match(FIELD_SPLIT)
-
[match_data[1].to_s.mb_chars.strip, match_data[2].to_s.mb_chars.strip.to_s]
-
rescue
-
STDERR.puts "WARNING: Could not parse (and so ignoring) '#{raw_field}'"
-
end
-
-
# 2.2.3. Long Header Fields
-
#
-
# The process of moving from this folded multiple-line representation
-
# of a header field to its single line representation is called
-
# "unfolding". Unfolding is accomplished by simply removing any CRLF
-
# that is immediately followed by WSP. Each header field should be
-
# treated in its unfolded form for further syntactic and semantic
-
# evaluation.
-
1
def unfold(string)
-
string.gsub(/[\r\n \t]+/m, ' ')
-
end
-
-
1
def create_field(name, value, charset)
-
value = unfold(value) if value.is_a?(String)
-
-
begin
-
new_field(name, value, charset)
-
rescue Mail::Field::ParseError => e
-
field = Mail::UnstructuredField.new(name, value)
-
field.errors << [name, value, e]
-
field
-
end
-
end
-
-
1
def new_field(name, value, charset)
-
lower_case_name = name.to_s.downcase
-
if field_klass = FIELDS_MAP[lower_case_name]
-
field_klass.new(value, charset)
-
else
-
OptionalField.new(name, value, charset)
-
end
-
end
-
-
end
-
-
end
-
# encoding: utf-8
-
1
module Mail
-
-
# Field List class provides an enhanced array that keeps a list of
-
# email fields in order. And allows you to insert new fields without
-
# having to worry about the order they will appear in.
-
1
class FieldList < Array
-
-
1
include Enumerable
-
-
# Insert the field in sorted order.
-
#
-
# Heavily based on bisect.insort from Python, which is:
-
# Copyright (C) 2001-2013 Python Software Foundation.
-
# Licensed under <http://docs.python.org/license.html>
-
# From <http://hg.python.org/cpython/file/2.7/Lib/bisect.py>
-
1
def <<( new_field )
-
lo = 0
-
hi = size
-
-
while lo < hi
-
mid = (lo + hi) / 2
-
if new_field < self[mid]
-
hi = mid
-
else
-
lo = mid + 1
-
end
-
end
-
-
insert(lo, new_field)
-
end
-
end
-
end
-
1
module Mail
-
1
register_autoload :UnstructuredField, 'mail/fields/unstructured_field'
-
1
register_autoload :StructuredField, 'mail/fields/structured_field'
-
1
register_autoload :OptionalField, 'mail/fields/optional_field'
-
-
1
register_autoload :BccField, 'mail/fields/bcc_field'
-
1
register_autoload :CcField, 'mail/fields/cc_field'
-
1
register_autoload :CommentsField, 'mail/fields/comments_field'
-
1
register_autoload :ContentDescriptionField, 'mail/fields/content_description_field'
-
1
register_autoload :ContentDispositionField, 'mail/fields/content_disposition_field'
-
1
register_autoload :ContentIdField, 'mail/fields/content_id_field'
-
1
register_autoload :ContentLocationField, 'mail/fields/content_location_field'
-
1
register_autoload :ContentTransferEncodingField, 'mail/fields/content_transfer_encoding_field'
-
1
register_autoload :ContentTypeField, 'mail/fields/content_type_field'
-
1
register_autoload :DateField, 'mail/fields/date_field'
-
1
register_autoload :FromField, 'mail/fields/from_field'
-
1
register_autoload :InReplyToField, 'mail/fields/in_reply_to_field'
-
1
register_autoload :KeywordsField, 'mail/fields/keywords_field'
-
1
register_autoload :MessageIdField, 'mail/fields/message_id_field'
-
1
register_autoload :MimeVersionField, 'mail/fields/mime_version_field'
-
1
register_autoload :ReceivedField, 'mail/fields/received_field'
-
1
register_autoload :ReferencesField, 'mail/fields/references_field'
-
1
register_autoload :ReplyToField, 'mail/fields/reply_to_field'
-
1
register_autoload :ResentBccField, 'mail/fields/resent_bcc_field'
-
1
register_autoload :ResentCcField, 'mail/fields/resent_cc_field'
-
1
register_autoload :ResentDateField, 'mail/fields/resent_date_field'
-
1
register_autoload :ResentFromField, 'mail/fields/resent_from_field'
-
1
register_autoload :ResentMessageIdField, 'mail/fields/resent_message_id_field'
-
1
register_autoload :ResentSenderField, 'mail/fields/resent_sender_field'
-
1
register_autoload :ResentToField, 'mail/fields/resent_to_field'
-
1
register_autoload :ReturnPathField, 'mail/fields/return_path_field'
-
1
register_autoload :SenderField, 'mail/fields/sender_field'
-
1
register_autoload :SubjectField, 'mail/fields/subject_field'
-
1
register_autoload :ToField, 'mail/fields/to_field'
-
end
-
# encoding: utf-8
-
#
-
# = Blind Carbon Copy Field
-
#
-
# The Bcc field inherits from StructuredField and handles the Bcc: header
-
# field in the email.
-
#
-
# Sending bcc to a mail message will instantiate a Mail::Field object that
-
# has a BccField as its field type. This includes all Mail::CommonAddress
-
# module instance metods.
-
#
-
# Only one Bcc field can appear in a header, though it can have multiple
-
# addresses and groups of addresses.
-
#
-
# == Examples:
-
#
-
# mail = Mail.new
-
# mail.bcc = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.bcc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
# mail[:bcc] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::BccField:0x180e1c4
-
# mail['bcc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::BccField:0x180e1c4
-
# mail['Bcc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::BccField:0x180e1c4
-
#
-
# mail[:bcc].encoded #=> '' # Bcc field does not get output into an email
-
# mail[:bcc].decoded #=> 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail[:bcc].addresses #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
# mail[:bcc].formatted #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
-
#
-
1
require 'mail/fields/common/common_address'
-
-
1
module Mail
-
1
class BccField < StructuredField
-
-
1
include Mail::CommonAddress
-
-
1
FIELD_NAME = 'bcc'
-
1
CAPITALIZED_FIELD = 'Bcc'
-
-
1
def initialize(value = '', charset = 'utf-8')
-
@charset = charset
-
super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
-
self.parse
-
self
-
end
-
-
# Bcc field should never be :encoded
-
1
def encoded
-
''
-
end
-
-
1
def decoded
-
do_decode
-
end
-
-
end
-
end
-
# encoding: utf-8
-
#
-
# = Carbon Copy Field
-
#
-
# The Cc field inherits from StructuredField and handles the Cc: header
-
# field in the email.
-
#
-
# Sending cc to a mail message will instantiate a Mail::Field object that
-
# has a CcField as its field type. This includes all Mail::CommonAddress
-
# module instance metods.
-
#
-
# Only one Cc field can appear in a header, though it can have multiple
-
# addresses and groups of addresses.
-
#
-
# == Examples:
-
#
-
# mail = Mail.new
-
# mail.cc = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.cc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
# mail[:cc] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CcField:0x180e1c4
-
# mail['cc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CcField:0x180e1c4
-
# mail['Cc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CcField:0x180e1c4
-
#
-
# mail[:cc].encoded #=> 'Cc: Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net\r\n'
-
# mail[:cc].decoded #=> 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail[:cc].addresses #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
# mail[:cc].formatted #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
-
#
-
1
require 'mail/fields/common/common_address'
-
-
1
module Mail
-
1
class CcField < StructuredField
-
-
1
include Mail::CommonAddress
-
-
1
FIELD_NAME = 'cc'
-
1
CAPITALIZED_FIELD = 'Cc'
-
-
1
def initialize(value = nil, charset = 'utf-8')
-
self.charset = charset
-
super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
-
self.parse
-
self
-
end
-
-
1
def encoded
-
do_encode(CAPITALIZED_FIELD)
-
end
-
-
1
def decoded
-
do_decode
-
end
-
-
end
-
end
-
# encoding: utf-8
-
#
-
# = Comments Field
-
#
-
# The Comments field inherits from UnstructuredField and handles the Comments:
-
# header field in the email.
-
#
-
# Sending comments to a mail message will instantiate a Mail::Field object that
-
# has a CommentsField as its field type.
-
#
-
# An email header can have as many comments fields as it wants. There is no upper
-
# limit, the comments field is also optional (that is, no comment is needed)
-
#
-
# == Examples:
-
#
-
# mail = Mail.new
-
# mail.comments = 'This is a comment'
-
# mail.comments #=> 'This is a comment'
-
# mail[:comments] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CommentsField:0x180e1c4
-
# mail['comments'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CommentsField:0x180e1c4
-
# mail['comments'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::CommentsField:0x180e1c4
-
#
-
# mail.comments = "This is another comment"
-
# mail[:comments].map { |c| c.to_s }
-
# #=> ['This is a comment', "This is another comment"]
-
#
-
1
module Mail
-
1
class CommentsField < UnstructuredField
-
-
1
FIELD_NAME = 'comments'
-
1
CAPITALIZED_FIELD = 'Comments'
-
-
1
def initialize(value = nil, charset = 'utf-8')
-
@charset = charset
-
super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value))
-
self.parse
-
self
-
end
-
-
end
-
end
-
1
module Mail
-
-
1
class AddressContainer < Array
-
-
1
def initialize(field, list = [])
-
@field = field
-
super(list)
-
end
-
-
1
def << (address)
-
@field << address
-
end
-
-
end
-
-
end
-
# encoding: utf-8
-
1
require 'mail/fields/common/address_container'
-
-
1
module Mail
-
1
module CommonAddress # :nodoc:
-
-
1
def parse(val = value)
-
unless val.blank?
-
@address_list = AddressList.new(encode_if_needed(val))
-
else
-
nil
-
end
-
end
-
-
1
def charset
-
@charset
-
end
-
-
1
def encode_if_needed(val)
-
Encodings.address_encode(val, charset)
-
end
-
-
# Allows you to iterate through each address object in the address_list
-
1
def each
-
address_list.addresses.each do |address|
-
yield(address)
-
end
-
end
-
-
# Returns the address string of all the addresses in the address list
-
1
def addresses
-
list = address_list.addresses.map { |a| a.address }
-
Mail::AddressContainer.new(self, list)
-
end
-
-
# Returns the formatted string of all the addresses in the address list
-
1
def formatted
-
list = address_list.addresses.map { |a| a.format }
-
Mail::AddressContainer.new(self, list)
-
end
-
-
# Returns the display name of all the addresses in the address list
-
1
def display_names
-
list = address_list.addresses.map { |a| a.display_name }
-
Mail::AddressContainer.new(self, list)
-
end
-
-
# Returns the actual address objects in the address list
-
1
def addrs
-
list = address_list.addresses
-
Mail::AddressContainer.new(self, list)
-
end
-
-
# Returns a hash of group name => address strings for the address list
-
1
def groups
-
address_list.addresses_grouped_by_group
-
end
-
-
# Returns the addresses that are part of groups
-
1
def group_addresses
-
decoded_group_addresses
-
end
-
-
# Returns a list of decoded group addresses
-
1
def decoded_group_addresses
-
groups.map { |k,v| v.map { |a| a.decoded } }.flatten
-
end
-
-
# Returns a list of encoded group addresses
-
1
def encoded_group_addresses
-
groups.map { |k,v| v.map { |a| a.encoded } }.flatten
-
end
-
-
# Returns the name of all the groups in a string
-
1
def group_names # :nodoc:
-
address_list.group_names
-
end
-
-
1
def default
-
addresses
-
end
-
-
1
def <<(val)
-
case
-
when val.nil?
-
raise ArgumentError, "Need to pass an address to <<"
-
when val.blank?
-
parse(encoded)
-
else
-
self.value = [self.value, val].reject {|a| a.blank? }.join(", ")
-
end
-
end
-
-
1
def value=(val)
-
super
-
parse(self.value)
-
end
-
-
1
private
-
-
1
def do_encode(field_name)
-
return '' if value.blank?
-
address_array = address_list.addresses.reject { |a| encoded_group_addresses.include?(a.encoded) }.compact.map { |a| a.encoded }
-
address_text = address_array.join(", \r\n\s")
-
group_array = groups.map { |k,v| "#{k}: #{v.map { |a| a.encoded }.join(", \r\n\s")};" }
-
group_text = group_array.join(" \r\n\s")
-
return_array = [address_text, group_text].reject { |a| a.blank? }
-
"#{field_name}: #{return_array.join(", \r\n\s")}\r\n"
-
end
-
-
1
def do_decode
-
return nil if value.blank?
-
address_array = address_list.addresses.reject { |a| decoded_group_addresses.include?(a.decoded) }.map { |a| a.decoded }
-
address_text = address_array.join(", ")
-
group_array = groups.map { |k,v| "#{k}: #{v.map { |a| a.decoded }.join(", ")};" }
-
group_text = group_array.join(" ")
-
return_array = [address_text, group_text].reject { |a| a.blank? }
-
return_array.join(", ")
-
end
-
-
1
def address_list # :nodoc:
-
@address_list ||= AddressList.new(value)
-
end
-
-
1
def get_group_addresses(group_list)
-
if group_list.respond_to?(:addresses)
-
group_list.addresses.map do |address|
-
Mail::Address.new(address)
-
end
-
else
-
[]
-
end
-
end
-
end
-
end
-
# encoding: utf-8
-
1
module Mail
-
1
module CommonDate # :nodoc:
-
# Returns a date time object of the parsed date
-
1
def date_time
-
::DateTime.parse("#{element.date_string} #{element.time_string}")
-
end
-
-
1
def default
-
date_time
-
end
-
-
1
def parse(val = value)
-
unless val.blank?
-
@element = Mail::DateTimeElement.new(val)
-
else
-
nil
-
end
-
end
-
-
1
private
-
-
1
def do_encode(field_name)
-
"#{field_name}: #{value}\r\n"
-
end
-
-
1
def do_decode
-
"#{value}"
-
end
-
-
1
def element
-
@element ||= Mail::DateTimeElement.new(value)
-
end
-
end
-
end
-
# encoding: utf-8
-
1
module Mail
-
1
module CommonField # :nodoc:
-
-
1
def name=(value)
-
@name = value
-
end
-
-
1
def name
-
@name ||= nil
-
end
-
-
1
def value=(value)
-
@length = nil
-
@tree = nil
-
@element = nil
-
@value = value
-
end
-
-
1
def value
-
@value
-
end
-
-
1
def to_s
-
decoded.to_s
-
end
-
-
1
def default
-
decoded
-
end
-
-
1
def field_length
-
@length ||= "#{name}: #{encode(decoded)}".length
-
end
-
-
1
def responsible_for?( val )
-
name.to_s.casecmp(val.to_s) == 0
-
end
-
-
1
private
-
-
1
def strip_field(field_name, value)
-
if value.is_a?(Array)
-
value
-
else
-
value.to_s.sub(/#{field_name}:\s+/i, '')
-
end
-
end
-
-
1
FILENAME_RE = /\b(filename|name)=([^;"\r\n]+\s[^;"\r\n]+)/
-
1
def ensure_filename_quoted(value)
-
if value.is_a?(String)
-
value.sub! FILENAME_RE, '\1="\2"'
-
end
-
end
-
end
-
end
-
# encoding: utf-8
-
1
module Mail
-
1
module CommonMessageId # :nodoc:
-
1
def element
-
@element ||= Mail::MessageIdsElement.new(value) unless value.blank?
-
end
-
-
1
def parse(val = value)
-
unless val.blank?
-
@element = Mail::MessageIdsElement.new(val)
-
else
-
nil
-
end
-
end
-
-
1
def message_id
-
element.message_id if element
-
end
-
-
1
def message_ids
-
element.message_ids if element
-
end
-
-
1
def default
-
return nil unless message_ids
-
if message_ids.length == 1
-
message_ids[0]
-
else
-
message_ids
-
end
-
end
-
-
1
private
-
-
1
def do_encode(field_name)
-
%Q{#{field_name}: #{formated_message_ids("\r\n ")}\r\n}
-
end
-
-
1
def do_decode
-
formated_message_ids(' ')
-
end
-
-
1
def formated_message_ids(join)
-
message_ids.map{ |m| "<#{m}>" }.join(join) if message_ids
-
end
-
-
end
-
end
-
# encoding: utf-8
-
1
module Mail
-
-
# ParameterHash is an intelligent Hash that allows you to add
-
# parameter values including the MIME extension paramaters that
-
# have the name*0="blah", name*1="bleh" keys, and will just return
-
# a single key called name="blahbleh" and do any required un-encoding
-
# to make that happen
-
# Parameters are defined in RFC2045, split keys are in RFC2231
-
-
1
class ParameterHash < IndifferentHash
-
-
1
include Mail::Utilities
-
-
1
def [](key_name)
-
key_pattern = Regexp.escape(key_name.to_s)
-
pairs = []
-
exact = nil
-
each do |k,v|
-
if k =~ /^#{key_pattern}(\*|$)/i
-
if $1 == '*'
-
pairs << [k, v]
-
else
-
exact = k
-
end
-
end
-
end
-
if pairs.empty? # Just dealing with a single value pair
-
super(exact || key_name)
-
else # Dealing with a multiple value pair or a single encoded value pair
-
string = pairs.sort { |a,b| a.first.to_s <=> b.first.to_s }.map { |v| v.last }.join('')
-
if mt = string.match(/([\w\-]+)'(\w\w)'(.*)/)
-
string = mt[3]
-
encoding = mt[1]
-
else
-
encoding = nil
-
end
-
Mail::Encodings.param_decode(string, encoding)
-
end
-
end
-
-
1
def encoded
-
map.sort { |a,b| a.first.to_s <=> b.first.to_s }.map do |key_name, value|
-
unless value.ascii_only?
-
value = Mail::Encodings.param_encode(value)
-
key_name = "#{key_name}*"
-
end
-
%Q{#{key_name}=#{quote_token(value)}}
-
end.join(";\r\n\s")
-
end
-
-
1
def decoded
-
map.sort { |a,b| a.first.to_s <=> b.first.to_s }.map do |key_name, value|
-
%Q{#{key_name}=#{quote_token(value)}}
-
end.join("; ")
-
end
-
end
-
end
-
# encoding: utf-8
-
#
-
#
-
#
-
1
module Mail
-
1
class ContentDescriptionField < UnstructuredField
-
-
1
FIELD_NAME = 'content-description'
-
1
CAPITALIZED_FIELD = 'Content-Description'
-
-
1
def initialize(value = nil, charset = 'utf-8')
-
self.charset = charset
-
super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
-
self.parse
-
self
-
end
-
-
end
-
end
-
# encoding: utf-8
-
1
require 'mail/fields/common/parameter_hash'
-
-
1
module Mail
-
1
class ContentDispositionField < StructuredField
-
-
1
FIELD_NAME = 'content-disposition'
-
1
CAPITALIZED_FIELD = 'Content-Disposition'
-
-
1
def initialize(value = nil, charset = 'utf-8')
-
self.charset = charset
-
ensure_filename_quoted(value)
-
super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
-
self.parse
-
self
-
end
-
-
1
def parse(val = value)
-
unless val.blank?
-
@element = Mail::ContentDispositionElement.new(val)
-
end
-
end
-
-
1
def element
-
@element ||= Mail::ContentDispositionElement.new(value)
-
end
-
-
1
def disposition_type
-
element.disposition_type
-
end
-
-
1
def parameters
-
@parameters = ParameterHash.new
-
element.parameters.each { |p| @parameters.merge!(p) }
-
@parameters
-
end
-
-
1
def filename
-
case
-
when !parameters['filename'].blank?
-
@filename = parameters['filename']
-
when !parameters['name'].blank?
-
@filename = parameters['name']
-
else
-
@filename = nil
-
end
-
@filename
-
end
-
-
# TODO: Fix this up
-
1
def encoded
-
if parameters.length > 0
-
p = ";\r\n\s#{parameters.encoded}\r\n"
-
else
-
p = "\r\n"
-
end
-
"#{CAPITALIZED_FIELD}: #{disposition_type}" + p
-
end
-
-
1
def decoded
-
if parameters.length > 0
-
p = "; #{parameters.decoded}"
-
else
-
p = ""
-
end
-
"#{disposition_type}" + p
-
end
-
-
end
-
end
-
# encoding: utf-8
-
#
-
#
-
#
-
1
module Mail
-
1
class ContentIdField < StructuredField
-
-
1
FIELD_NAME = 'content-id'
-
1
CAPITALIZED_FIELD = "Content-ID"
-
-
1
def initialize(value = nil, charset = 'utf-8')
-
self.charset = charset
-
@uniq = 1
-
if value.blank?
-
value = generate_content_id
-
else
-
value = strip_field(FIELD_NAME, value)
-
end
-
super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
-
self.parse
-
self
-
end
-
-
1
def parse(val = value)
-
unless val.blank?
-
@element = Mail::MessageIdsElement.new(val)
-
end
-
end
-
-
1
def element
-
@element ||= Mail::MessageIdsElement.new(value)
-
end
-
-
1
def name
-
'Content-ID'
-
end
-
-
1
def content_id
-
element.message_id
-
end
-
-
1
def to_s
-
"<#{content_id}>"
-
end
-
-
# TODO: Fix this up
-
1
def encoded
-
"#{CAPITALIZED_FIELD}: #{to_s}\r\n"
-
end
-
-
1
def decoded
-
"#{to_s}"
-
end
-
-
1
private
-
-
1
def generate_content_id
-
"<#{Mail.random_tag}@#{::Socket.gethostname}.mail>"
-
end
-
-
end
-
end
-
# encoding: utf-8
-
#
-
#
-
#
-
1
module Mail
-
1
class ContentLocationField < StructuredField
-
-
1
FIELD_NAME = 'content-location'
-
1
CAPITALIZED_FIELD = 'Content-Location'
-
-
1
def initialize(value = nil, charset = 'utf-8')
-
self.charset = charset
-
super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
-
self.parse
-
self
-
end
-
-
1
def parse(val = value)
-
unless val.blank?
-
@element = Mail::ContentLocationElement.new(val)
-
end
-
end
-
-
1
def element
-
@element ||= Mail::ContentLocationElement.new(value)
-
end
-
-
1
def location
-
element.location
-
end
-
-
# TODO: Fix this up
-
1
def encoded
-
"#{CAPITALIZED_FIELD}: #{location}\r\n"
-
end
-
-
1
def decoded
-
location
-
end
-
-
end
-
end
-
# encoding: utf-8
-
#
-
#
-
#
-
1
module Mail
-
1
class ContentTransferEncodingField < StructuredField
-
-
1
FIELD_NAME = 'content-transfer-encoding'
-
1
CAPITALIZED_FIELD = 'Content-Transfer-Encoding'
-
-
1
def initialize(value = nil, charset = 'utf-8')
-
self.charset = charset
-
value = '7bit' if value.to_s =~ /7-?bits?/i
-
value = '8bit' if value.to_s =~ /8-?bits?/i
-
super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
-
self.parse
-
self
-
end
-
-
1
def parse(val = value)
-
unless val.blank?
-
@element = Mail::ContentTransferEncodingElement.new(val)
-
end
-
end
-
-
1
def element
-
@element ||= Mail::ContentTransferEncodingElement.new(value)
-
end
-
-
1
def encoding
-
element.encoding
-
end
-
-
# TODO: Fix this up
-
1
def encoded
-
"#{CAPITALIZED_FIELD}: #{encoding}\r\n"
-
end
-
-
1
def decoded
-
encoding
-
end
-
-
end
-
end
-
# encoding: utf-8
-
1
require 'mail/fields/common/parameter_hash'
-
-
1
module Mail
-
1
class ContentTypeField < StructuredField
-
-
1
FIELD_NAME = 'content-type'
-
1
CAPITALIZED_FIELD = 'Content-Type'
-
-
1
def initialize(value = nil, charset = 'utf-8')
-
self.charset = charset
-
if value.class == Array
-
@main_type = value[0]
-
@sub_type = value[1]
-
@parameters = ParameterHash.new.merge!(value.last)
-
else
-
@main_type = nil
-
@sub_type = nil
-
@parameters = nil
-
value = strip_field(FIELD_NAME, value)
-
end
-
ensure_filename_quoted(value)
-
super(CAPITALIZED_FIELD, value, charset)
-
self.parse
-
self
-
end
-
-
1
def parse(val = value)
-
unless val.blank?
-
self.value = val
-
@element = nil
-
element
-
end
-
end
-
-
1
def element
-
begin
-
@element ||= Mail::ContentTypeElement.new(value)
-
rescue
-
attempt_to_clean
-
end
-
end
-
-
1
def attempt_to_clean
-
# Sanitize the value, handle special cases
-
@element ||= Mail::ContentTypeElement.new(sanatize(value))
-
rescue
-
# All else fails, just get the MIME media type
-
@element ||= Mail::ContentTypeElement.new(get_mime_type(value))
-
end
-
-
1
def main_type
-
@main_type ||= element.main_type
-
end
-
-
1
def sub_type
-
@sub_type ||= element.sub_type
-
end
-
-
1
def string
-
"#{main_type}/#{sub_type}"
-
end
-
-
1
def default
-
decoded
-
end
-
-
1
alias :content_type :string
-
-
1
def parameters
-
unless @parameters
-
@parameters = ParameterHash.new
-
element.parameters.each { |p| @parameters.merge!(p) }
-
end
-
@parameters
-
end
-
-
1
def ContentTypeField.with_boundary(type)
-
new("#{type}; boundary=#{generate_boundary}")
-
end
-
-
1
def ContentTypeField.generate_boundary
-
"--==_mimepart_#{Mail.random_tag}"
-
end
-
-
1
def value
-
if @value.class == Array
-
"#{@main_type}/#{@sub_type}; #{stringify(parameters)}"
-
else
-
@value
-
end
-
end
-
-
1
def stringify(params)
-
params.map { |k,v| "#{k}=#{Encodings.param_encode(v)}" }.join("; ")
-
end
-
-
1
def filename
-
case
-
when parameters['filename']
-
@filename = parameters['filename']
-
when parameters['name']
-
@filename = parameters['name']
-
else
-
@filename = nil
-
end
-
@filename
-
end
-
-
# TODO: Fix this up
-
1
def encoded
-
if parameters.length > 0
-
p = ";\r\n\s#{parameters.encoded}"
-
else
-
p = ""
-
end
-
"#{CAPITALIZED_FIELD}: #{content_type}#{p}\r\n"
-
end
-
-
1
def decoded
-
if parameters.length > 0
-
p = "; #{parameters.decoded}"
-
else
-
p = ""
-
end
-
"#{content_type}" + p
-
end
-
-
1
private
-
-
1
def method_missing(name, *args, &block)
-
if name.to_s =~ /(\w+)=/
-
self.parameters[$1] = args.first
-
@value = "#{content_type}; #{stringify(parameters)}"
-
else
-
super
-
end
-
end
-
-
# Various special cases from random emails found that I am not going to change
-
# the parser for
-
1
def sanatize( val )
-
-
# TODO: check if there are cases where whitespace is not a separator
-
val = val.
-
gsub(/\s*=\s*/,'='). # remove whitespaces around equal sign
-
tr(' ',';').
-
squeeze(';').
-
gsub(';', '; '). #use '; ' as a separator (or EOL)
-
gsub(/;\s*$/,'') #remove trailing to keep examples below
-
-
if val =~ /(boundary=(\S*))/i
-
val = "#{$`.downcase}boundary=#{$2}#{$'.downcase}"
-
else
-
val.downcase!
-
end
-
-
case
-
when val.chomp =~ /^\s*([\w\-]+)\/([\w\-]+)\s*;;+(.*)$/i
-
# Handles 'text/plain;; format="flowed"' (double semi colon)
-
"#{$1}/#{$2}; #{$3}"
-
when val.chomp =~ /^\s*([\w\-]+)\/([\w\-]+)\s*;\s?(ISO[\w\-]+)$/i
-
# Microsoft helper:
-
# Handles 'type/subtype;ISO-8559-1'
-
"#{$1}/#{$2}; charset=#{quote_atom($3)}"
-
when val.chomp =~ /^text;?$/i
-
# Handles 'text;' and 'text'
-
"text/plain;"
-
when val.chomp =~ /^(\w+);\s(.*)$/i
-
# Handles 'text; <parameters>'
-
"text/plain; #{$2}"
-
when val =~ /([\w\-]+\/[\w\-]+);\scharset="charset="(\w+)""/i
-
# Handles text/html; charset="charset="GB2312""
-
"#{$1}; charset=#{quote_atom($2)}"
-
when val =~ /([\w\-]+\/[\w\-]+);\s+(.*)/i
-
type = $1
-
# Handles misquoted param values
-
# e.g: application/octet-stream; name=archiveshelp1[1].htm
-
# and: audio/x-midi;\r\n\sname=Part .exe
-
params = $2.to_s.split(/\s+/)
-
params = params.map { |i| i.to_s.chomp.strip }
-
params = params.map { |i| i.split(/\s*\=\s*/) }
-
params = params.map { |i| "#{i[0]}=#{dquote(i[1].to_s.gsub(/;$/,""))}" }.join('; ')
-
"#{type}; #{params}"
-
when val =~ /^\s*$/
-
'text/plain'
-
else
-
''
-
end
-
end
-
-
1
def get_mime_type( val )
-
case
-
when val =~ /^([\w\-]+)\/([\w\-]+);.+$/i
-
"#{$1}/#{$2}"
-
else
-
'text/plain'
-
end
-
end
-
end
-
end
-
# encoding: utf-8
-
#
-
# = Date Field
-
#
-
# The Date field inherits from StructuredField and handles the Date: header
-
# field in the email.
-
#
-
# Sending date to a mail message will instantiate a Mail::Field object that
-
# has a DateField as its field type. This includes all Mail::CommonAddress
-
# module instance methods.
-
#
-
# There must be excatly one Date field in an RFC2822 email.
-
#
-
# == Examples:
-
#
-
# mail = Mail.new
-
# mail.date = 'Mon, 24 Nov 1997 14:22:01 -0800'
-
# mail.date #=> #<DateTime: 211747170121/86400,-1/3,2299161>
-
# mail.date.to_s #=> 'Mon, 24 Nov 1997 14:22:01 -0800'
-
# mail[:date] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::DateField:0x180e1c4
-
# mail['date'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::DateField:0x180e1c4
-
# mail['Date'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::DateField:0x180e1c4
-
#
-
1
require 'mail/fields/common/common_date'
-
-
1
module Mail
-
1
class DateField < StructuredField
-
-
1
include Mail::CommonDate
-
-
1
FIELD_NAME = 'date'
-
1
CAPITALIZED_FIELD = "Date"
-
-
1
def initialize(value = nil, charset = 'utf-8')
-
self.charset = charset
-
if value.blank?
-
value = ::DateTime.now.strftime('%a, %d %b %Y %H:%M:%S %z')
-
else
-
value = strip_field(FIELD_NAME, value)
-
value.to_s.gsub!(/\(.*?\)/, '')
-
value = ::DateTime.parse(value.to_s.squeeze(" ")).strftime('%a, %d %b %Y %H:%M:%S %z')
-
end
-
super(CAPITALIZED_FIELD, value, charset)
-
rescue ArgumentError => e
-
raise e unless "invalid date"==e.message
-
end
-
-
1
def encoded
-
do_encode(CAPITALIZED_FIELD)
-
end
-
-
1
def decoded
-
do_decode
-
end
-
-
end
-
end
-
# encoding: utf-8
-
#
-
# = From Field
-
#
-
# The From field inherits from StructuredField and handles the From: header
-
# field in the email.
-
#
-
# Sending from to a mail message will instantiate a Mail::Field object that
-
# has a FromField as its field type. This includes all Mail::CommonAddress
-
# module instance metods.
-
#
-
# Only one From field can appear in a header, though it can have multiple
-
# addresses and groups of addresses.
-
#
-
# == Examples:
-
#
-
# mail = Mail.new
-
# mail.from = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.from #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
# mail[:from] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::FromField:0x180e1c4
-
# mail['from'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::FromField:0x180e1c4
-
# mail['From'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::FromField:0x180e1c4
-
#
-
# mail[:from].encoded #=> 'from: Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net\r\n'
-
# mail[:from].decoded #=> 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail[:from].addresses #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
# mail[:from].formatted #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
-
#
-
1
require 'mail/fields/common/common_address'
-
-
1
module Mail
-
1
class FromField < StructuredField
-
-
1
include Mail::CommonAddress
-
-
1
FIELD_NAME = 'from'
-
1
CAPITALIZED_FIELD = 'From'
-
-
1
def initialize(value = nil, charset = 'utf-8')
-
self.charset = charset
-
super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
-
self.parse
-
self
-
end
-
-
1
def encoded
-
do_encode(CAPITALIZED_FIELD)
-
end
-
-
1
def decoded
-
do_decode
-
end
-
-
end
-
end
-
# encoding: utf-8
-
#
-
# = In-Reply-To Field
-
#
-
# The In-Reply-To field inherits from StructuredField and handles the
-
# In-Reply-To: header field in the email.
-
#
-
# Sending in_reply_to to a mail message will instantiate a Mail::Field object that
-
# has a InReplyToField as its field type. This includes all Mail::CommonMessageId
-
# module instance metods.
-
#
-
# Note that, the #message_ids method will return an array of message IDs without the
-
# enclosing angle brackets which per RFC are not syntactically part of the message id.
-
#
-
# Only one InReplyTo field can appear in a header, though it can have multiple
-
# Message IDs.
-
#
-
# == Examples:
-
#
-
# mail = Mail.new
-
# mail.in_reply_to = '<F6E2D0B4-CC35-4A91-BA4C-C7C712B10C13@test.me.dom>'
-
# mail.in_reply_to #=> '<F6E2D0B4-CC35-4A91-BA4C-C7C712B10C13@test.me.dom>'
-
# mail[:in_reply_to] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::InReplyToField:0x180e1c4
-
# mail['in_reply_to'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::InReplyToField:0x180e1c4
-
# mail['In-Reply-To'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::InReplyToField:0x180e1c4
-
#
-
# mail[:in_reply_to].message_ids #=> ['F6E2D0B4-CC35-4A91-BA4C-C7C712B10C13@test.me.dom']
-
#
-
1
require 'mail/fields/common/common_message_id'
-
-
1
module Mail
-
1
class InReplyToField < StructuredField
-
-
1
include Mail::CommonMessageId
-
-
1
FIELD_NAME = 'in-reply-to'
-
1
CAPITALIZED_FIELD = 'In-Reply-To'
-
-
1
def initialize(value = nil, charset = 'utf-8')
-
self.charset = charset
-
value = value.join("\r\n\s") if value.is_a?(Array)
-
super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
-
self.parse
-
self
-
end
-
-
1
def encoded
-
do_encode(CAPITALIZED_FIELD)
-
end
-
-
1
def decoded
-
do_decode
-
end
-
-
end
-
end
-
# encoding: utf-8
-
#
-
# keywords = "Keywords:" phrase *("," phrase) CRLF
-
1
module Mail
-
1
class KeywordsField < StructuredField
-
-
1
FIELD_NAME = 'keywords'
-
1
CAPITALIZED_FIELD = 'Keywords'
-
-
1
def initialize(value = nil, charset = 'utf-8')
-
self.charset = charset
-
super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
-
self.parse
-
self
-
end
-
-
1
def parse(val = value)
-
unless val.blank?
-
@phrase_list ||= PhraseList.new(value)
-
end
-
end
-
-
1
def phrase_list
-
@phrase_list ||= PhraseList.new(value)
-
end
-
-
1
def keywords
-
phrase_list.phrases
-
end
-
-
1
def encoded
-
"#{CAPITALIZED_FIELD}: #{keywords.join(",\r\n ")}\r\n"
-
end
-
-
1
def decoded
-
keywords.join(', ')
-
end
-
-
1
def default
-
keywords
-
end
-
-
end
-
end
-
# encoding: utf-8
-
#
-
# = Message-ID Field
-
#
-
# The Message-ID field inherits from StructuredField and handles the
-
# Message-ID: header field in the email.
-
#
-
# Sending message_id to a mail message will instantiate a Mail::Field object that
-
# has a MessageIdField as its field type. This includes all Mail::CommonMessageId
-
# module instance metods.
-
#
-
# Only one MessageId field can appear in a header, and syntactically it can only have
-
# one Message ID. The message_ids method call has been left in however as it will only
-
# return the one message id, ie, an array of length 1.
-
#
-
# Note that, the #message_ids method will return an array of message IDs without the
-
# enclosing angle brackets which per RFC are not syntactically part of the message id.
-
#
-
# == Examples:
-
#
-
# mail = Mail.new
-
# mail.message_id = '<F6E2D0B4-CC35-4A91-BA4C-C7C712B10C13@test.me.dom>'
-
# mail.message_id #=> '<F6E2D0B4-CC35-4A91-BA4C-C7C712B10C13@test.me.dom>'
-
# mail[:message_id] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::MessageIdField:0x180e1c4
-
# mail['message_id'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::MessageIdField:0x180e1c4
-
# mail['Message-ID'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::MessageIdField:0x180e1c4
-
#
-
# mail[:message_id].message_id #=> 'F6E2D0B4-CC35-4A91-BA4C-C7C712B10C13@test.me.dom'
-
# mail[:message_id].message_ids #=> ['F6E2D0B4-CC35-4A91-BA4C-C7C712B10C13@test.me.dom']
-
#
-
1
require 'mail/fields/common/common_message_id'
-
-
1
module Mail
-
1
class MessageIdField < StructuredField
-
-
1
include Mail::CommonMessageId
-
-
1
FIELD_NAME = 'message-id'
-
1
CAPITALIZED_FIELD = 'Message-ID'
-
-
1
def initialize(value = nil, charset = 'utf-8')
-
self.charset = charset
-
@uniq = 1
-
if value.blank?
-
self.name = CAPITALIZED_FIELD
-
self.value = generate_message_id
-
else
-
super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
-
end
-
self.parse
-
self
-
-
end
-
-
1
def name
-
'Message-ID'
-
end
-
-
1
def message_ids
-
[message_id]
-
end
-
-
1
def to_s
-
"<#{message_id}>"
-
end
-
-
1
def encoded
-
do_encode(CAPITALIZED_FIELD)
-
end
-
-
1
def decoded
-
do_decode
-
end
-
-
1
private
-
-
1
def generate_message_id
-
"<#{Mail.random_tag}@#{::Socket.gethostname}.mail>"
-
end
-
-
end
-
end
-
# encoding: utf-8
-
#
-
#
-
#
-
1
module Mail
-
1
class MimeVersionField < StructuredField
-
-
1
FIELD_NAME = 'mime-version'
-
1
CAPITALIZED_FIELD = 'Mime-Version'
-
-
1
def initialize(value = nil, charset = 'utf-8')
-
self.charset = charset
-
if value.blank?
-
value = '1.0'
-
end
-
super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
-
self.parse
-
self
-
-
end
-
-
1
def parse(val = value)
-
unless val.blank?
-
@element = Mail::MimeVersionElement.new(val)
-
end
-
end
-
-
1
def element
-
@element ||= Mail::MimeVersionElement.new(value)
-
end
-
-
1
def version
-
"#{element.major}.#{element.minor}"
-
end
-
-
1
def major
-
element.major.to_i
-
end
-
-
1
def minor
-
element.minor.to_i
-
end
-
-
1
def encoded
-
"#{CAPITALIZED_FIELD}: #{version}\r\n"
-
end
-
-
1
def decoded
-
version
-
end
-
-
end
-
end
-
# encoding: utf-8
-
#
-
# trace = [return]
-
# 1*received
-
#
-
# return = "Return-Path:" path CRLF
-
#
-
# path = ([CFWS] "<" ([CFWS] / addr-spec) ">" [CFWS]) /
-
# obs-path
-
#
-
# received = "Received:" name-val-list ";" date-time CRLF
-
#
-
# name-val-list = [CFWS] [name-val-pair *(CFWS name-val-pair)]
-
#
-
# name-val-pair = item-name CFWS item-value
-
#
-
# item-name = ALPHA *(["-"] (ALPHA / DIGIT))
-
#
-
# item-value = 1*angle-addr / addr-spec /
-
# atom / domain / msg-id
-
#
-
1
module Mail
-
1
class ReceivedField < StructuredField
-
-
1
FIELD_NAME = 'received'
-
1
CAPITALIZED_FIELD = 'Received'
-
-
1
def initialize(value = nil, charset = 'utf-8')
-
self.charset = charset
-
super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
-
self.parse
-
self
-
-
end
-
-
1
def parse(val = value)
-
unless val.blank?
-
@element = Mail::ReceivedElement.new(val)
-
end
-
end
-
-
1
def element
-
@element ||= Mail::ReceivedElement.new(value)
-
end
-
-
1
def date_time
-
@datetime ||= ::DateTime.parse("#{element.date_time}")
-
end
-
-
1
def info
-
element.info
-
end
-
-
1
def formatted_date
-
date_time.strftime("%a, %d %b %Y %H:%M:%S ") + date_time.zone.delete(':')
-
end
-
-
1
def encoded
-
if value.blank?
-
"#{CAPITALIZED_FIELD}: \r\n"
-
else
-
"#{CAPITALIZED_FIELD}: #{info}; #{formatted_date}\r\n"
-
end
-
end
-
-
1
def decoded
-
if value.blank?
-
""
-
else
-
"#{info}; #{formatted_date}"
-
end
-
end
-
-
end
-
end
-
# encoding: utf-8
-
#
-
# = References Field
-
#
-
# The References field inherits references StructuredField and handles the References: header
-
# field in the email.
-
#
-
# Sending references to a mail message will instantiate a Mail::Field object that
-
# has a ReferencesField as its field type. This includes all Mail::CommonAddress
-
# module instance metods.
-
#
-
# Note that, the #message_ids method will return an array of message IDs without the
-
# enclosing angle brackets which per RFC are not syntactically part of the message id.
-
#
-
# Only one References field can appear in a header, though it can have multiple
-
# Message IDs.
-
#
-
# == Examples:
-
#
-
# mail = Mail.new
-
# mail.references = '<F6E2D0B4-CC35-4A91-BA4C-C7C712B10C13@test.me.dom>'
-
# mail.references #=> '<F6E2D0B4-CC35-4A91-BA4C-C7C712B10C13@test.me.dom>'
-
# mail[:references] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ReferencesField:0x180e1c4
-
# mail['references'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ReferencesField:0x180e1c4
-
# mail['References'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ReferencesField:0x180e1c4
-
#
-
# mail[:references].message_ids #=> ['F6E2D0B4-CC35-4A91-BA4C-C7C712B10C13@test.me.dom']
-
#
-
1
require 'mail/fields/common/common_message_id'
-
-
1
module Mail
-
1
class ReferencesField < StructuredField
-
-
1
include CommonMessageId
-
-
1
FIELD_NAME = 'references'
-
1
CAPITALIZED_FIELD = 'References'
-
-
1
def initialize(value = nil, charset = 'utf-8')
-
self.charset = charset
-
value = value.join("\r\n\s") if value.is_a?(Array)
-
super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
-
self.parse
-
self
-
end
-
-
1
def encoded
-
do_encode(CAPITALIZED_FIELD)
-
end
-
-
1
def decoded
-
do_decode
-
end
-
-
end
-
end
-
# encoding: utf-8
-
#
-
# = Reply-To Field
-
#
-
# The Reply-To field inherits reply-to StructuredField and handles the Reply-To: header
-
# field in the email.
-
#
-
# Sending reply_to to a mail message will instantiate a Mail::Field object that
-
# has a ReplyToField as its field type. This includes all Mail::CommonAddress
-
# module instance metods.
-
#
-
# Only one Reply-To field can appear in a header, though it can have multiple
-
# addresses and groups of addresses.
-
#
-
# == Examples:
-
#
-
# mail = Mail.new
-
# mail.reply_to = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.reply_to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
# mail[:reply_to] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ReplyToField:0x180e1c4
-
# mail['reply-to'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ReplyToField:0x180e1c4
-
# mail['Reply-To'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ReplyToField:0x180e1c4
-
#
-
# mail[:reply_to].encoded #=> 'Reply-To: Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net\r\n'
-
# mail[:reply_to].decoded #=> 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail[:reply_to].addresses #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
# mail[:reply_to].formatted #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
-
#
-
1
require 'mail/fields/common/common_address'
-
-
1
module Mail
-
1
class ReplyToField < StructuredField
-
-
1
include Mail::CommonAddress
-
-
1
FIELD_NAME = 'reply-to'
-
1
CAPITALIZED_FIELD = 'Reply-To'
-
-
1
def initialize(value = nil, charset = 'utf-8')
-
self.charset = charset
-
super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
-
self.parse
-
self
-
end
-
-
1
def encoded
-
do_encode(CAPITALIZED_FIELD)
-
end
-
-
1
def decoded
-
do_decode
-
end
-
-
end
-
end
-
# encoding: utf-8
-
#
-
# = Resent-Bcc Field
-
#
-
# The Resent-Bcc field inherits resent-bcc StructuredField and handles the
-
# Resent-Bcc: header field in the email.
-
#
-
# Sending resent_bcc to a mail message will instantiate a Mail::Field object that
-
# has a ResentBccField as its field type. This includes all Mail::CommonAddress
-
# module instance metods.
-
#
-
# Only one Resent-Bcc field can appear in a header, though it can have multiple
-
# addresses and groups of addresses.
-
#
-
# == Examples:
-
#
-
# mail = Mail.new
-
# mail.resent_bcc = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.resent_bcc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
# mail[:resent_bcc] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ResentBccField:0x180e1c4
-
# mail['resent-bcc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ResentBccField:0x180e1c4
-
# mail['Resent-Bcc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ResentBccField:0x180e1c4
-
#
-
# mail[:resent_bcc].encoded #=> 'Resent-Bcc: Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net\r\n'
-
# mail[:resent_bcc].decoded #=> 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail[:resent_bcc].addresses #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
# mail[:resent_bcc].formatted #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
-
#
-
1
require 'mail/fields/common/common_address'
-
-
1
module Mail
-
1
class ResentBccField < StructuredField
-
-
1
include Mail::CommonAddress
-
-
1
FIELD_NAME = 'resent-bcc'
-
1
CAPITALIZED_FIELD = 'Resent-Bcc'
-
-
1
def initialize(value = nil, charset = 'utf-8')
-
self.charset = charset
-
super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
-
self.parse
-
self
-
end
-
-
1
def encoded
-
do_encode(CAPITALIZED_FIELD)
-
end
-
-
1
def decoded
-
do_decode
-
end
-
-
end
-
end
-
# encoding: utf-8
-
#
-
# = Resent-Cc Field
-
#
-
# The Resent-Cc field inherits resent-cc StructuredField and handles the Resent-Cc: header
-
# field in the email.
-
#
-
# Sending resent_cc to a mail message will instantiate a Mail::Field object that
-
# has a ResentCcField as its field type. This includes all Mail::CommonAddress
-
# module instance metods.
-
#
-
# Only one Resent-Cc field can appear in a header, though it can have multiple
-
# addresses and groups of addresses.
-
#
-
# == Examples:
-
#
-
# mail = Mail.new
-
# mail.resent_cc = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.resent_cc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
# mail[:resent_cc] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ResentCcField:0x180e1c4
-
# mail['resent-cc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ResentCcField:0x180e1c4
-
# mail['Resent-Cc'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ResentCcField:0x180e1c4
-
#
-
# mail[:resent_cc].encoded #=> 'Resent-Cc: Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net\r\n'
-
# mail[:resent_cc].decoded #=> 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail[:resent_cc].addresses #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
# mail[:resent_cc].formatted #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
-
#
-
1
require 'mail/fields/common/common_address'
-
-
1
module Mail
-
1
class ResentCcField < StructuredField
-
-
1
include Mail::CommonAddress
-
-
1
FIELD_NAME = 'resent-cc'
-
1
CAPITALIZED_FIELD = 'Resent-Cc'
-
-
1
def initialize(value = nil, charset = 'utf-8')
-
self.charset = charset
-
super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
-
self.parse
-
self
-
end
-
-
1
def encoded
-
do_encode(CAPITALIZED_FIELD)
-
end
-
-
1
def decoded
-
do_decode
-
end
-
-
end
-
end
-
# encoding: utf-8
-
#
-
# resent-date = "Resent-Date:" date-time CRLF
-
1
require 'mail/fields/common/common_date'
-
-
1
module Mail
-
1
class ResentDateField < StructuredField
-
-
1
include Mail::CommonDate
-
-
1
FIELD_NAME = 'resent-date'
-
1
CAPITALIZED_FIELD = 'Resent-Date'
-
-
1
def initialize(value = nil, charset = 'utf-8')
-
self.charset = charset
-
if value.blank?
-
value = ::DateTime.now.strftime('%a, %d %b %Y %H:%M:%S %z')
-
else
-
value = strip_field(FIELD_NAME, value)
-
value = ::DateTime.parse(value.to_s).strftime('%a, %d %b %Y %H:%M:%S %z')
-
end
-
super(CAPITALIZED_FIELD, value, charset)
-
self
-
end
-
-
1
def encoded
-
do_encode(CAPITALIZED_FIELD)
-
end
-
-
1
def decoded
-
do_decode
-
end
-
-
end
-
end
-
# encoding: utf-8
-
#
-
# = Resent-From Field
-
#
-
# The Resent-From field inherits resent-from StructuredField and handles the Resent-From: header
-
# field in the email.
-
#
-
# Sending resent_from to a mail message will instantiate a Mail::Field object that
-
# has a ResentFromField as its field type. This includes all Mail::CommonAddress
-
# module instance metods.
-
#
-
# Only one Resent-From field can appear in a header, though it can have multiple
-
# addresses and groups of addresses.
-
#
-
# == Examples:
-
#
-
# mail = Mail.new
-
# mail.resent_from = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.resent_from #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
# mail[:resent_from] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ResentFromField:0x180e1c4
-
# mail['resent-from'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ResentFromField:0x180e1c4
-
# mail['Resent-From'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ResentFromField:0x180e1c4
-
#
-
# mail[:resent_from].encoded #=> 'Resent-From: Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net\r\n'
-
# mail[:resent_from].decoded #=> 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail[:resent_from].addresses #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
# mail[:resent_from].formatted #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
-
#
-
1
require 'mail/fields/common/common_address'
-
-
1
module Mail
-
1
class ResentFromField < StructuredField
-
-
1
include Mail::CommonAddress
-
-
1
FIELD_NAME = 'resent-from'
-
1
CAPITALIZED_FIELD = 'Resent-From'
-
-
1
def initialize(value = nil, charset = 'utf-8')
-
self.charset = charset
-
super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
-
self.parse
-
self
-
end
-
-
1
def encoded
-
do_encode(CAPITALIZED_FIELD)
-
end
-
-
1
def decoded
-
do_decode
-
end
-
-
end
-
end
-
# encoding: utf-8
-
#
-
# resent-msg-id = "Resent-Message-ID:" msg-id CRLF
-
1
require 'mail/fields/common/common_message_id'
-
-
1
module Mail
-
1
class ResentMessageIdField < StructuredField
-
-
1
include CommonMessageId
-
-
1
FIELD_NAME = 'resent-message-id'
-
1
CAPITALIZED_FIELD = 'Resent-Message-ID'
-
-
1
def initialize(value = nil, charset = 'utf-8')
-
self.charset = charset
-
super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
-
self.parse
-
self
-
end
-
-
1
def name
-
'Resent-Message-ID'
-
end
-
-
1
def encoded
-
do_encode(CAPITALIZED_FIELD)
-
end
-
-
1
def decoded
-
do_decode
-
end
-
-
end
-
end
-
# encoding: utf-8
-
#
-
# = Resent-Sender Field
-
#
-
# The Resent-Sender field inherits resent-sender StructuredField and handles the Resent-Sender: header
-
# field in the email.
-
#
-
# Sending resent_sender to a mail message will instantiate a Mail::Field object that
-
# has a ResentSenderField as its field type. This includes all Mail::CommonAddress
-
# module instance metods.
-
#
-
# Only one Resent-Sender field can appear in a header, though it can have multiple
-
# addresses and groups of addresses.
-
#
-
# == Examples:
-
#
-
# mail = Mail.new
-
# mail.resent_sender = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.resent_sender #=> ['mikel@test.lindsaar.net']
-
# mail[:resent_sender] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ResentSenderField:0x180e1c4
-
# mail['resent-sender'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ResentSenderField:0x180e1c4
-
# mail['Resent-Sender'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ResentSenderField:0x180e1c4
-
#
-
# mail.resent_sender.to_s #=> 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.resent_sender.addresses #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
# mail.resent_sender.formatted #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
-
#
-
1
require 'mail/fields/common/common_address'
-
-
1
module Mail
-
1
class ResentSenderField < StructuredField
-
-
1
include Mail::CommonAddress
-
-
1
FIELD_NAME = 'resent-sender'
-
1
CAPITALIZED_FIELD = 'Resent-Sender'
-
-
1
def initialize(value = nil, charset = 'utf-8')
-
self.charset = charset
-
super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
-
self.parse
-
self
-
end
-
-
1
def addresses
-
[address.address]
-
end
-
-
1
def address
-
address_list.addresses.first
-
end
-
-
1
def encoded
-
do_encode(CAPITALIZED_FIELD)
-
end
-
-
1
def decoded
-
do_decode
-
end
-
-
end
-
end
-
# encoding: utf-8
-
#
-
# = Resent-To Field
-
#
-
# The Resent-To field inherits resent-to StructuredField and handles the Resent-To: header
-
# field in the email.
-
#
-
# Sending resent_to to a mail message will instantiate a Mail::Field object that
-
# has a ResentToField as its field type. This includes all Mail::CommonAddress
-
# module instance metods.
-
#
-
# Only one Resent-To field can appear in a header, though it can have multiple
-
# addresses and groups of addresses.
-
#
-
# == Examples:
-
#
-
# mail = Mail.new
-
# mail.resent_to = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.resent_to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
# mail[:resent_to] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ResentToField:0x180e1c4
-
# mail['resent-to'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ResentToField:0x180e1c4
-
# mail['Resent-To'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ResentToField:0x180e1c4
-
#
-
# mail[:resent_to].encoded #=> 'Resent-To: Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net\r\n'
-
# mail[:resent_to].decoded #=> 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail[:resent_to].addresses #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
# mail[:resent_to].formatted #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
-
#
-
1
require 'mail/fields/common/common_address'
-
-
1
module Mail
-
1
class ResentToField < StructuredField
-
-
1
include Mail::CommonAddress
-
-
1
FIELD_NAME = 'resent-to'
-
1
CAPITALIZED_FIELD = 'Resent-To'
-
-
1
def initialize(value = nil, charset = 'utf-8')
-
self.charset = charset
-
super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
-
self.parse
-
self
-
end
-
-
1
def encoded
-
do_encode(CAPITALIZED_FIELD)
-
end
-
-
1
def decoded
-
do_decode
-
end
-
-
end
-
end
-
# encoding: utf-8
-
#
-
# 4.4.3. REPLY-TO / RESENT-REPLY-TO
-
#
-
# Note: The "Return-Path" field is added by the mail transport
-
# service, at the time of final deliver. It is intended
-
# to identify a path back to the orginator of the mes-
-
# sage. The "Reply-To" field is added by the message
-
# originator and is intended to direct replies.
-
#
-
# trace = [return]
-
# 1*received
-
#
-
# return = "Return-Path:" path CRLF
-
#
-
# path = ([CFWS] "<" ([CFWS] / addr-spec) ">" [CFWS]) /
-
# obs-path
-
#
-
# received = "Received:" name-val-list ";" date-time CRLF
-
#
-
# name-val-list = [CFWS] [name-val-pair *(CFWS name-val-pair)]
-
#
-
# name-val-pair = item-name CFWS item-value
-
#
-
# item-name = ALPHA *(["-"] (ALPHA / DIGIT))
-
#
-
# item-value = 1*angle-addr / addr-spec /
-
# atom / domain / msg-id
-
#
-
1
require 'mail/fields/common/common_address'
-
-
1
module Mail
-
1
class ReturnPathField < StructuredField
-
-
1
include Mail::CommonAddress
-
-
1
FIELD_NAME = 'return-path'
-
1
CAPITALIZED_FIELD = 'Return-Path'
-
-
1
def initialize(value = nil, charset = 'utf-8')
-
value = nil if value == '<>'
-
self.charset = charset
-
super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
-
self.parse
-
self
-
end
-
-
1
def encoded
-
"#{CAPITALIZED_FIELD}: <#{address}>\r\n"
-
end
-
-
1
def decoded
-
do_decode
-
end
-
-
1
def address
-
addresses.first
-
end
-
-
1
def default
-
address
-
end
-
-
end
-
end
-
# encoding: utf-8
-
#
-
# = Sender Field
-
#
-
# The Sender field inherits sender StructuredField and handles the Sender: header
-
# field in the email.
-
#
-
# Sending sender to a mail message will instantiate a Mail::Field object that
-
# has a SenderField as its field type. This includes all Mail::CommonAddress
-
# module instance metods.
-
#
-
# Only one Sender field can appear in a header, though it can have multiple
-
# addresses and groups of addresses.
-
#
-
# == Examples:
-
#
-
# mail = Mail.new
-
# mail.sender = 'Mikel Lindsaar <mikel@test.lindsaar.net>'
-
# mail.sender #=> 'mikel@test.lindsaar.net'
-
# mail[:sender] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::SenderField:0x180e1c4
-
# mail['sender'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::SenderField:0x180e1c4
-
# mail['Sender'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::SenderField:0x180e1c4
-
#
-
# mail[:sender].encoded #=> "Sender: Mikel Lindsaar <mikel@test.lindsaar.net>\r\n"
-
# mail[:sender].decoded #=> 'Mikel Lindsaar <mikel@test.lindsaar.net>'
-
# mail[:sender].addresses #=> ['mikel@test.lindsaar.net']
-
# mail[:sender].formatted #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>']
-
#
-
1
require 'mail/fields/common/common_address'
-
-
1
module Mail
-
1
class SenderField < StructuredField
-
-
1
include Mail::CommonAddress
-
-
1
FIELD_NAME = 'sender'
-
1
CAPITALIZED_FIELD = 'Sender'
-
-
1
def initialize(value = nil, charset = 'utf-8')
-
self.charset = charset
-
super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
-
self.parse
-
self
-
end
-
-
1
def addresses
-
[address.address]
-
end
-
-
1
def address
-
address_list.addresses.first
-
end
-
-
1
def encoded
-
do_encode(CAPITALIZED_FIELD)
-
end
-
-
1
def decoded
-
do_decode
-
end
-
-
1
def default
-
address.address
-
end
-
-
end
-
end
-
# encoding: utf-8
-
1
require 'mail/fields/common/common_field'
-
-
1
module Mail
-
# Provides access to a structured header field
-
#
-
# ===Per RFC 2822:
-
# 2.2.2. Structured Header Field Bodies
-
#
-
# Some field bodies in this standard have specific syntactical
-
# structure more restrictive than the unstructured field bodies
-
# described above. These are referred to as "structured" field bodies.
-
# Structured field bodies are sequences of specific lexical tokens as
-
# described in sections 3 and 4 of this standard. Many of these tokens
-
# are allowed (according to their syntax) to be introduced or end with
-
# comments (as described in section 3.2.3) as well as the space (SP,
-
# ASCII value 32) and horizontal tab (HTAB, ASCII value 9) characters
-
# (together known as the white space characters, WSP), and those WSP
-
# characters are subject to header "folding" and "unfolding" as
-
# described in section 2.2.3. Semantic analysis of structured field
-
# bodies is given along with their syntax.
-
1
class StructuredField
-
-
1
include Mail::CommonField
-
1
include Mail::Utilities
-
-
1
def initialize(name = nil, value = nil, charset = nil)
-
self.name = name
-
self.value = value
-
self.charset = charset
-
self
-
end
-
-
1
def charset
-
@charset
-
end
-
-
1
def charset=(val)
-
@charset = val
-
end
-
-
1
def default
-
decoded
-
end
-
-
1
def errors
-
[]
-
end
-
-
end
-
end
-
# encoding: utf-8
-
#
-
# subject = "Subject:" unstructured CRLF
-
1
module Mail
-
1
class SubjectField < UnstructuredField
-
-
1
FIELD_NAME = 'subject'
-
1
CAPITALIZED_FIELD = "Subject"
-
-
1
def initialize(value = nil, charset = 'utf-8')
-
self.charset = charset
-
super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
-
end
-
-
end
-
end
-
# encoding: utf-8
-
#
-
# = To Field
-
#
-
# The To field inherits to StructuredField and handles the To: header
-
# field in the email.
-
#
-
# Sending to to a mail message will instantiate a Mail::Field object that
-
# has a ToField as its field type. This includes all Mail::CommonAddress
-
# module instance metods.
-
#
-
# Only one To field can appear in a header, though it can have multiple
-
# addresses and groups of addresses.
-
#
-
# == Examples:
-
#
-
# mail = Mail.new
-
# mail.to = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
# mail[:to] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ToField:0x180e1c4
-
# mail['to'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ToField:0x180e1c4
-
# mail['To'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ToField:0x180e1c4
-
#
-
# mail[:to].encoded #=> 'To: Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net\r\n'
-
# mail[:to].decoded #=> 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail[:to].addresses #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
# mail[:to].formatted #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
-
#
-
1
require 'mail/fields/common/common_address'
-
-
1
module Mail
-
1
class ToField < StructuredField
-
-
1
include Mail::CommonAddress
-
-
1
FIELD_NAME = 'to'
-
1
CAPITALIZED_FIELD = 'To'
-
-
1
def initialize(value = nil, charset = 'utf-8')
-
self.charset = charset
-
super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
-
self.parse
-
self
-
end
-
-
1
def encoded
-
do_encode(CAPITALIZED_FIELD)
-
end
-
-
1
def decoded
-
do_decode
-
end
-
-
end
-
end
-
# encoding: utf-8
-
1
require 'mail/fields/common/common_field'
-
-
1
module Mail
-
# Provides access to an unstructured header field
-
#
-
# ===Per RFC 2822:
-
# 2.2.1. Unstructured Header Field Bodies
-
#
-
# Some field bodies in this standard are defined simply as
-
# "unstructured" (which is specified below as any US-ASCII characters,
-
# except for CR and LF) with no further restrictions. These are
-
# referred to as unstructured field bodies. Semantically, unstructured
-
# field bodies are simply to be treated as a single line of characters
-
# with no further processing (except for header "folding" and
-
# "unfolding" as described in section 2.2.3).
-
1
class UnstructuredField
-
-
1
include Mail::CommonField
-
1
include Mail::Utilities
-
-
1
attr_accessor :charset
-
1
attr_reader :errors
-
-
1
def initialize(name, value, charset = nil)
-
@errors = []
-
-
if value.is_a?(Array)
-
# Probably has arrived here from a failed parse of an AddressList Field
-
value = value.join(', ')
-
else
-
# Ensure we are dealing with a string
-
value = value.to_s
-
end
-
-
if charset
-
self.charset = charset
-
else
-
if value.respond_to?(:encoding)
-
self.charset = value.encoding
-
else
-
self.charset = $KCODE
-
end
-
end
-
self.name = name
-
self.value = value
-
self
-
end
-
-
1
def encoded
-
do_encode
-
end
-
-
1
def decoded
-
do_decode
-
end
-
-
1
def default
-
decoded
-
end
-
-
1
def parse # An unstructured field does not parse
-
self
-
end
-
-
1
private
-
-
1
def do_encode
-
value.nil? ? '' : "#{wrapped_value}\r\n"
-
end
-
-
1
def do_decode
-
value.blank? ? nil : Encodings.decode_encode(value, :decode)
-
end
-
-
# 2.2.3. Long Header Fields
-
#
-
# Each header field is logically a single line of characters comprising
-
# the field name, the colon, and the field body. For convenience
-
# however, and to deal with the 998/78 character limitations per line,
-
# the field body portion of a header field can be split into a multiple
-
# line representation; this is called "folding". The general rule is
-
# that wherever this standard allows for folding white space (not
-
# simply WSP characters), a CRLF may be inserted before any WSP. For
-
# example, the header field:
-
#
-
# Subject: This is a test
-
#
-
# can be represented as:
-
#
-
# Subject: This
-
# is a test
-
#
-
# Note: Though structured field bodies are defined in such a way that
-
# folding can take place between many of the lexical tokens (and even
-
# within some of the lexical tokens), folding SHOULD be limited to
-
# placing the CRLF at higher-level syntactic breaks. For instance, if
-
# a field body is defined as comma-separated values, it is recommended
-
# that folding occur after the comma separating the structured items in
-
# preference to other places where the field could be folded, even if
-
# it is allowed elsewhere.
-
1
def wrapped_value # :nodoc:
-
wrap_lines(name, fold("#{name}: ".length))
-
end
-
-
# 6.2. Display of 'encoded-word's
-
#
-
# When displaying a particular header field that contains multiple
-
# 'encoded-word's, any 'linear-white-space' that separates a pair of
-
# adjacent 'encoded-word's is ignored. (This is to allow the use of
-
# multiple 'encoded-word's to represent long strings of unencoded text,
-
# without having to separate 'encoded-word's where spaces occur in the
-
# unencoded text.)
-
1
def wrap_lines(name, folded_lines)
-
result = ["#{name}: #{folded_lines.shift}"]
-
result.concat(folded_lines)
-
result.join("\r\n\s")
-
end
-
-
1
def fold(prepend = 0) # :nodoc:
-
encoding = normalized_encoding
-
decoded_string = decoded.to_s
-
should_encode = decoded_string.not_ascii_only?
-
if should_encode
-
first = true
-
words = decoded_string.split(/[ \t]/).map do |word|
-
if first
-
first = !first
-
else
-
word = " " << word
-
end
-
if word.not_ascii_only?
-
word
-
else
-
word.scan(/.{7}|.+$/)
-
end
-
end.flatten
-
else
-
words = decoded_string.split(/[ \t]/)
-
end
-
-
folded_lines = []
-
while !words.empty?
-
limit = 78 - prepend
-
limit = limit - 7 - encoding.length if should_encode
-
line = ""
-
first_word = true
-
while !words.empty?
-
break unless word = words.first.dup
-
word.encode!(charset) if charset && word.respond_to?(:encode!)
-
word = encode(word) if should_encode
-
word = encode_crlf(word)
-
# Skip to next line if we're going to go past the limit
-
# Unless this is the first word, in which case we're going to add it anyway
-
# Note: This means that a word that's longer than 998 characters is going to break the spec. Please fix if this is a problem for you.
-
# (The fix, it seems, would be to use encoded-word encoding on it, because that way you can break it across multiple lines and
-
# the linebreak will be ignored)
-
break if !line.empty? && (line.length + word.length + 1 > limit)
-
# Remove the word from the queue ...
-
words.shift
-
# Add word separator
-
if first_word
-
first_word = false
-
else
-
line << " " if !should_encode
-
end
-
-
# ... add it in encoded form to the current line
-
line << word
-
end
-
# Encode the line if necessary
-
line = "=?#{encoding}?Q?#{line}?=" if should_encode
-
# Add the line to the output and reset the prepend
-
folded_lines << line
-
prepend = 0
-
end
-
folded_lines
-
end
-
-
1
def encode(value)
-
value = [value].pack("M").gsub("=\n", '')
-
value.gsub!(/"/, '=22')
-
value.gsub!(/\(/, '=28')
-
value.gsub!(/\)/, '=29')
-
value.gsub!(/\?/, '=3F')
-
value.gsub!(/_/, '=5F')
-
value.gsub!(/ /, '_')
-
value
-
end
-
-
1
def encode_crlf(value)
-
value.gsub!("\r", '=0D')
-
value.gsub!("\n", '=0A')
-
value
-
end
-
-
1
def normalized_encoding
-
encoding = charset.to_s.upcase.gsub('_', '-')
-
encoding = 'UTF-8' if encoding == 'UTF8' # Ruby 1.8.x and $KCODE == 'u'
-
encoding
-
end
-
-
end
-
end
-
# encoding: utf-8
-
1
module Mail
-
-
# Provides access to a header object.
-
#
-
# ===Per RFC2822
-
#
-
# 2.2. Header Fields
-
#
-
# Header fields are lines composed of a field name, followed by a colon
-
# (":"), followed by a field body, and terminated by CRLF. A field
-
# name MUST be composed of printable US-ASCII characters (i.e.,
-
# characters that have values between 33 and 126, inclusive), except
-
# colon. A field body may be composed of any US-ASCII characters,
-
# except for CR and LF. However, a field body may contain CRLF when
-
# used in header "folding" and "unfolding" as described in section
-
# 2.2.3. All field bodies MUST conform to the syntax described in
-
# sections 3 and 4 of this standard.
-
1
class Header
-
1
include Patterns
-
1
include Utilities
-
1
include Enumerable
-
-
1
@@maximum_amount = 1000
-
-
# Large amount of headers in Email might create extra high CPU load
-
# Use this parameter to limit number of headers that will be parsed by
-
# mail library.
-
# Default: 1000
-
1
def self.maximum_amount
-
@@maximum_amount
-
end
-
-
1
def self.maximum_amount=(value)
-
@@maximum_amount = value
-
end
-
-
# Creates a new header object.
-
#
-
# Accepts raw text or nothing. If given raw text will attempt to parse
-
# it and split it into the various fields, instantiating each field as
-
# it goes.
-
#
-
# If it finds a field that should be a structured field (such as content
-
# type), but it fails to parse it, it will simply make it an unstructured
-
# field and leave it alone. This will mean that the data is preserved but
-
# no automatic processing of that field will happen. If you find one of
-
# these cases, please make a patch and send it in, or at the least, send
-
# me the example so we can fix it.
-
1
def initialize(header_text = nil, charset = nil)
-
@charset = charset
-
self.raw_source = header_text.to_crlf.lstrip
-
split_header if header_text
-
end
-
-
1
def initialize_copy(original)
-
super
-
@fields = @fields.dup
-
end
-
-
# The preserved raw source of the header as you passed it in, untouched
-
# for your Regexing glory.
-
1
def raw_source
-
@raw_source
-
end
-
-
# Returns an array of all the fields in the header in order that they
-
# were read in.
-
1
def fields
-
@fields ||= FieldList.new
-
end
-
-
# 3.6. Field definitions
-
#
-
# It is important to note that the header fields are not guaranteed to
-
# be in a particular order. They may appear in any order, and they
-
# have been known to be reordered occasionally when transported over
-
# the Internet. However, for the purposes of this standard, header
-
# fields SHOULD NOT be reordered when a message is transported or
-
# transformed. More importantly, the trace header fields and resent
-
# header fields MUST NOT be reordered, and SHOULD be kept in blocks
-
# prepended to the message. See sections 3.6.6 and 3.6.7 for more
-
# information.
-
#
-
# Populates the fields container with Field objects in the order it
-
# receives them in.
-
#
-
# Acceps an array of field string values, for example:
-
#
-
# h = Header.new
-
# h.fields = ['From: mikel@me.com', 'To: bob@you.com']
-
1
def fields=(unfolded_fields)
-
@fields = Mail::FieldList.new
-
warn "Warning: more than #{self.class.maximum_amount} header fields only using the first #{self.class.maximum_amount}" if unfolded_fields.length > self.class.maximum_amount
-
unfolded_fields[0..(self.class.maximum_amount-1)].each do |field|
-
-
field = Field.new(field, nil, charset)
-
if limited_field?(field.name) && (selected = select_field_for(field.name)) && selected.any?
-
selected.first.update(field.name, field.value)
-
else
-
@fields << field
-
end
-
end
-
-
end
-
-
1
def errors
-
@fields.map(&:errors).flatten(1)
-
end
-
-
# 3.6. Field definitions
-
#
-
# The following table indicates limits on the number of times each
-
# field may occur in a message header as well as any special
-
# limitations on the use of those fields. An asterisk next to a value
-
# in the minimum or maximum column indicates that a special restriction
-
# appears in the Notes column.
-
#
-
# <snip table from 3.6>
-
#
-
# As per RFC, many fields can appear more than once, we will return a string
-
# of the value if there is only one header, or if there is more than one
-
# matching header, will return an array of values in order that they appear
-
# in the header ordered from top to bottom.
-
#
-
# Example:
-
#
-
# h = Header.new
-
# h.fields = ['To: mikel@me.com', 'X-Mail-SPAM: 15', 'X-Mail-SPAM: 20']
-
# h['To'] #=> 'mikel@me.com'
-
# h['X-Mail-SPAM'] #=> ['15', '20']
-
1
def [](name)
-
name = dasherize(name).downcase
-
selected = select_field_for(name)
-
case
-
when selected.length > 1
-
selected.map { |f| f }
-
when !selected.blank?
-
selected.first
-
else
-
nil
-
end
-
end
-
-
# Sets the FIRST matching field in the header to passed value, or deletes
-
# the FIRST field matched from the header if passed nil
-
#
-
# Example:
-
#
-
# h = Header.new
-
# h.fields = ['To: mikel@me.com', 'X-Mail-SPAM: 15', 'X-Mail-SPAM: 20']
-
# h['To'] = 'bob@you.com'
-
# h['To'] #=> 'bob@you.com'
-
# h['X-Mail-SPAM'] = '10000'
-
# h['X-Mail-SPAM'] # => ['15', '20', '10000']
-
# h['X-Mail-SPAM'] = nil
-
# h['X-Mail-SPAM'] # => nil
-
1
def []=(name, value)
-
name = dasherize(name)
-
if name.include?(':')
-
raise ArgumentError, "Header names may not contain a colon: #{name.inspect}"
-
end
-
fn = name.downcase
-
selected = select_field_for(fn)
-
-
case
-
# User wants to delete the field
-
when !selected.blank? && value == nil
-
fields.delete_if { |f| selected.include?(f) }
-
-
# User wants to change the field
-
when !selected.blank? && limited_field?(fn)
-
selected.first.update(fn, value)
-
-
# User wants to create the field
-
else
-
# Need to insert in correct order for trace fields
-
self.fields << Field.new(name.to_s, value, charset)
-
end
-
if dasherize(fn) == "content-type"
-
# Update charset if specified in Content-Type
-
params = self[:content_type].parameters rescue nil
-
@charset = params[:charset] if params && params[:charset]
-
end
-
end
-
-
1
def charset
-
@charset
-
end
-
-
1
def charset=(val)
-
params = self[:content_type].parameters rescue nil
-
if params
-
params[:charset] = val
-
end
-
@charset = val
-
end
-
-
1
LIMITED_FIELDS = %w[ date from sender reply-to to cc bcc
-
message-id in-reply-to references subject
-
return-path content-type mime-version
-
content-transfer-encoding content-description
-
content-id content-disposition content-location]
-
-
1
def encoded
-
buffer = ''
-
buffer.force_encoding('us-ascii') if buffer.respond_to?(:force_encoding)
-
fields.each do |field|
-
buffer << field.encoded
-
end
-
buffer
-
end
-
-
1
def to_s
-
encoded
-
end
-
-
1
def decoded
-
raise NoMethodError, 'Can not decode an entire header as there could be character set conflicts, try calling #decoded on the various fields.'
-
end
-
-
1
def field_summary
-
fields.map { |f| "<#{f.name}: #{f.value}>" }.join(", ")
-
end
-
-
# Returns true if the header has a Message-ID defined (empty or not)
-
1
def has_message_id?
-
!fields.select { |f| f.responsible_for?('Message-ID') }.empty?
-
end
-
-
# Returns true if the header has a Content-ID defined (empty or not)
-
1
def has_content_id?
-
!fields.select { |f| f.responsible_for?('Content-ID') }.empty?
-
end
-
-
# Returns true if the header has a Date defined (empty or not)
-
1
def has_date?
-
!fields.select { |f| f.responsible_for?('Date') }.empty?
-
end
-
-
# Returns true if the header has a MIME version defined (empty or not)
-
1
def has_mime_version?
-
!fields.select { |f| f.responsible_for?('Mime-Version') }.empty?
-
end
-
-
1
private
-
-
1
def raw_source=(val)
-
@raw_source = val
-
end
-
-
# Splits an unfolded and line break cleaned header into individual field
-
# strings.
-
1
def split_header
-
self.fields = raw_source.split(HEADER_SPLIT)
-
end
-
-
1
def select_field_for(name)
-
fields.select { |f| f.responsible_for?(name) }
-
end
-
-
1
def limited_field?(name)
-
LIMITED_FIELDS.include?(name.to_s.downcase)
-
end
-
-
# Enumerable support; yield each field in order to the block if there is one,
-
# or return an Enumerator for them if there isn't.
-
1
def each( &block )
-
return self.fields.each( &block ) if block
-
self.fields.each
-
end
-
-
end
-
end
-
# encoding: utf-8
-
-
# This is an almost cut and paste from ActiveSupport v3.0.6, copied in here so that Mail
-
# itself does not depend on ActiveSupport to avoid versioning conflicts
-
-
1
module Mail
-
1
class IndifferentHash < Hash
-
-
1
def initialize(constructor = {})
-
if constructor.is_a?(Hash)
-
super()
-
update(constructor)
-
else
-
super(constructor)
-
end
-
end
-
-
1
def default(key = nil)
-
if key.is_a?(Symbol) && include?(key = key.to_s)
-
self[key]
-
else
-
super
-
end
-
end
-
-
1
def self.new_from_hash_copying_default(hash)
-
IndifferentHash.new(hash).tap do |new_hash|
-
new_hash.default = hash.default
-
end
-
end
-
-
1
alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
-
1
alias_method :regular_update, :update unless method_defined?(:regular_update)
-
-
# Assigns a new value to the hash:
-
#
-
# hash = HashWithIndifferentAccess.new
-
# hash[:key] = "value"
-
#
-
1
def []=(key, value)
-
regular_writer(convert_key(key), convert_value(value))
-
end
-
-
1
alias_method :store, :[]=
-
-
# Updates the instantized hash with values from the second:
-
#
-
# hash_1 = HashWithIndifferentAccess.new
-
# hash_1[:key] = "value"
-
#
-
# hash_2 = HashWithIndifferentAccess.new
-
# hash_2[:key] = "New Value!"
-
#
-
# hash_1.update(hash_2) # => {"key"=>"New Value!"}
-
#
-
1
def update(other_hash)
-
other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) }
-
self
-
end
-
-
1
alias_method :merge!, :update
-
-
# Checks the hash for a key matching the argument passed in:
-
#
-
# hash = HashWithIndifferentAccess.new
-
# hash["key"] = "value"
-
# hash.key? :key # => true
-
# hash.key? "key" # => true
-
#
-
1
def key?(key)
-
super(convert_key(key))
-
end
-
-
1
alias_method :include?, :key?
-
1
alias_method :has_key?, :key?
-
1
alias_method :member?, :key?
-
-
# Fetches the value for the specified key, same as doing hash[key]
-
1
def fetch(key, *extras)
-
super(convert_key(key), *extras)
-
end
-
-
# Returns an array of the values at the specified indices:
-
#
-
# hash = HashWithIndifferentAccess.new
-
# hash[:a] = "x"
-
# hash[:b] = "y"
-
# hash.values_at("a", "b") # => ["x", "y"]
-
#
-
1
def values_at(*indices)
-
indices.collect {|key| self[convert_key(key)]}
-
end
-
-
# Returns an exact copy of the hash.
-
1
def dup
-
IndifferentHash.new(self)
-
end
-
-
# Merges the instantized and the specified hashes together, giving precedence to the values from the second hash
-
# Does not overwrite the existing hash.
-
1
def merge(hash)
-
self.dup.update(hash)
-
end
-
-
# Performs the opposite of merge, with the keys and values from the first hash taking precedence over the second.
-
# This overloaded definition prevents returning a regular hash, if reverse_merge is called on a HashWithDifferentAccess.
-
1
def reverse_merge(other_hash)
-
super self.class.new_from_hash_copying_default(other_hash)
-
end
-
-
1
def reverse_merge!(other_hash)
-
replace(reverse_merge( other_hash ))
-
end
-
-
# Removes a specified key from the hash.
-
1
def delete(key)
-
super(convert_key(key))
-
end
-
-
1
def stringify_keys!; self end
-
1
def stringify_keys; dup end
-
1
def symbolize_keys; to_hash.symbolize_keys end
-
1
def to_options!; self end
-
-
1
def to_hash
-
Hash.new(default).merge!(self)
-
end
-
-
1
protected
-
-
1
def convert_key(key)
-
key.kind_of?(Symbol) ? key.to_s : key
-
end
-
-
1
def convert_value(value)
-
if value.class == Hash
-
self.class.new_from_hash_copying_default(value)
-
elsif value.is_a?(Array)
-
value.dup.replace(value.map { |e| convert_value(e) })
-
else
-
value
-
end
-
end
-
-
end
-
end
-
# encoding: utf-8
-
1
module Mail
-
-
# Allows you to create a new Mail::Message object.
-
#
-
# You can make an email via passing a string or passing a block.
-
#
-
# For example, the following two examples will create the same email
-
# message:
-
#
-
# Creating via a string:
-
#
-
# string = "To: mikel@test.lindsaar.net\r\n"
-
# string << "From: bob@test.lindsaar.net\r\n"
-
# string << "Subject: This is an email\r\n"
-
# string << "\r\n"
-
# string << "This is the body"
-
# Mail.new(string)
-
#
-
# Or creating via a block:
-
#
-
# message = Mail.new do
-
# to 'mikel@test.lindsaar.net'
-
# from 'bob@test.lindsaar.net'
-
# subject 'This is an email'
-
# body 'This is the body'
-
# end
-
#
-
# Or creating via a hash (or hash like object):
-
#
-
# message = Mail.new({:to => 'mikel@test.lindsaar.net',
-
# 'from' => 'bob@test.lindsaar.net',
-
# :subject => 'This is an email',
-
# :body => 'This is the body' })
-
#
-
# Note, the hash keys can be strings or symbols, the passed in object
-
# does not need to be a hash, it just needs to respond to :each_pair
-
# and yield each key value pair.
-
#
-
# As a side note, you can also create a new email through creating
-
# a Mail::Message object directly and then passing in values via string,
-
# symbol or direct method calls. See Mail::Message for more information.
-
#
-
# mail = Mail.new
-
# mail.to = 'mikel@test.lindsaar.net'
-
# mail[:from] = 'bob@test.lindsaar.net'
-
# mail['subject'] = 'This is an email'
-
# mail.body = 'This is the body'
-
1
def self.new(*args, &block)
-
Message.new(args, &block)
-
end
-
-
# Sets the default delivery method and retriever method for all new Mail objects.
-
# The delivery_method and retriever_method default to :smtp and :pop3, with defaults
-
# set.
-
#
-
# So sending a new email, if you have an SMTP server running on localhost is
-
# as easy as:
-
#
-
# Mail.deliver do
-
# to 'mikel@test.lindsaar.net'
-
# from 'bob@test.lindsaar.net'
-
# subject 'hi there!'
-
# body 'this is a body'
-
# end
-
#
-
# If you do not specify anything, you will get the following equivalent code set in
-
# every new mail object:
-
#
-
# Mail.defaults do
-
# delivery_method :smtp, { :address => "localhost",
-
# :port => 25,
-
# :domain => 'localhost.localdomain',
-
# :user_name => nil,
-
# :password => nil,
-
# :authentication => nil,
-
# :enable_starttls_auto => true }
-
#
-
# retriever_method :pop3, { :address => "localhost",
-
# :port => 995,
-
# :user_name => nil,
-
# :password => nil,
-
# :enable_ssl => true }
-
# end
-
#
-
# Mail.delivery_method.new #=> Mail::SMTP instance
-
# Mail.retriever_method.new #=> Mail::POP3 instance
-
#
-
# Each mail object inherits the default set in Mail.delivery_method, however, on
-
# a per email basis, you can override the method:
-
#
-
# mail.delivery_method :sendmail
-
#
-
# Or you can override the method and pass in settings:
-
#
-
# mail.delivery_method :sendmail, { :address => 'some.host' }
-
#
-
# You can also just modify the settings:
-
#
-
# mail.delivery_settings = { :address => 'some.host' }
-
#
-
# The passed in hash is just merged against the defaults with +merge!+ and the result
-
# assigned the mail object. So the above example will change only the :address value
-
# of the global smtp_settings to be 'some.host', keeping all other values
-
1
def self.defaults(&block)
-
Configuration.instance.instance_eval(&block)
-
end
-
-
# Returns the delivery method selected, defaults to an instance of Mail::SMTP
-
1
def self.delivery_method
-
Configuration.instance.delivery_method
-
end
-
-
# Returns the retriever method selected, defaults to an instance of Mail::POP3
-
1
def self.retriever_method
-
Configuration.instance.retriever_method
-
end
-
-
# Send an email using the default configuration. You do need to set a default
-
# configuration first before you use self.deliver, if you don't, an appropriate
-
# error will be raised telling you to.
-
#
-
# If you do not specify a delivery type, SMTP will be used.
-
#
-
# Mail.deliver do
-
# to 'mikel@test.lindsaar.net'
-
# from 'ada@test.lindsaar.net'
-
# subject 'This is a test email'
-
# body 'Not much to say here'
-
# end
-
#
-
# You can also do:
-
#
-
# mail = Mail.read('email.eml')
-
# mail.deliver!
-
#
-
# And your email object will be created and sent.
-
1
def self.deliver(*args, &block)
-
mail = self.new(args, &block)
-
mail.deliver
-
mail
-
end
-
-
# Find emails from the default retriever
-
# See Mail::Retriever for a complete documentation.
-
1
def self.find(*args, &block)
-
retriever_method.find(*args, &block)
-
end
-
-
# Finds and then deletes retrieved emails from the default retriever
-
# See Mail::Retriever for a complete documentation.
-
1
def self.find_and_delete(*args, &block)
-
retriever_method.find_and_delete(*args, &block)
-
end
-
-
# Receive the first email(s) from the default retriever
-
# See Mail::Retriever for a complete documentation.
-
1
def self.first(*args, &block)
-
retriever_method.first(*args, &block)
-
end
-
-
# Receive the first email(s) from the default retriever
-
# See Mail::Retriever for a complete documentation.
-
1
def self.last(*args, &block)
-
retriever_method.last(*args, &block)
-
end
-
-
# Receive all emails from the default retriever
-
# See Mail::Retriever for a complete documentation.
-
1
def self.all(*args, &block)
-
retriever_method.all(*args, &block)
-
end
-
-
# Reads in an email message from a path and instantiates it as a new Mail::Message
-
1
def self.read(filename)
-
self.new(File.open(filename, 'rb') { |f| f.read })
-
end
-
-
# Delete all emails from the default retriever
-
# See Mail::Retriever for a complete documentation.
-
1
def self.delete_all(*args, &block)
-
retriever_method.delete_all(*args, &block)
-
end
-
-
# Instantiates a new Mail::Message using a string
-
1
def Mail.read_from_string(mail_as_string)
-
Mail.new(mail_as_string)
-
end
-
-
1
def Mail.connection(&block)
-
retriever_method.connection(&block)
-
end
-
-
# Initialize the observers and interceptors arrays
-
1
@@delivery_notification_observers = []
-
1
@@delivery_interceptors = []
-
-
# You can register an object to be informed of every email that is sent through
-
# this method.
-
#
-
# Your object needs to respond to a single method #delivered_email(mail)
-
# which receives the email that is sent.
-
1
def self.register_observer(observer)
-
unless @@delivery_notification_observers.include?(observer)
-
@@delivery_notification_observers << observer
-
end
-
end
-
-
# Unregister the given observer, allowing mail to resume operations
-
# without it.
-
1
def self.unregister_observer(observer)
-
@@delivery_notification_observers.delete(observer)
-
end
-
-
# You can register an object to be given every mail object that will be sent,
-
# before it is sent. So if you want to add special headers or modify any
-
# email that gets sent through the Mail library, you can do so.
-
#
-
# Your object needs to respond to a single method #delivering_email(mail)
-
# which receives the email that is about to be sent. Make your modifications
-
# directly to this object.
-
1
def self.register_interceptor(interceptor)
-
unless @@delivery_interceptors.include?(interceptor)
-
@@delivery_interceptors << interceptor
-
end
-
end
-
-
# Unregister the given interceptor, allowing mail to resume operations
-
# without it.
-
1
def self.unregister_interceptor(interceptor)
-
@@delivery_interceptors.delete(interceptor)
-
end
-
-
1
def self.inform_observers(mail)
-
@@delivery_notification_observers.each do |observer|
-
observer.delivered_email(mail)
-
end
-
end
-
-
1
def self.inform_interceptors(mail)
-
@@delivery_interceptors.each do |interceptor|
-
interceptor.delivering_email(mail)
-
end
-
end
-
-
1
protected
-
-
1
def self.random_tag
-
t = Time.now
-
sprintf('%x%x_%x%x%d%x',
-
t.to_i, t.tv_usec,
-
$$, Thread.current.object_id.abs, self.uniq, rand(255))
-
end
-
-
1
private
-
-
1
def self.something_random
-
1
(Thread.current.object_id * rand(255) / Time.now.to_f).to_s.slice(-3..-1).to_i
-
end
-
-
1
def self.uniq
-
@@uniq += 1
-
end
-
-
1
@@uniq = self.something_random
-
-
end
-
# encoding: utf-8
-
1
require "yaml"
-
-
1
module Mail
-
# The Message class provides a single point of access to all things to do with an
-
# email message.
-
#
-
# You create a new email message by calling the Mail::Message.new method, or just
-
# Mail.new
-
#
-
# A Message object by default has the following objects inside it:
-
#
-
# * A Header object which contains all information and settings of the header of the email
-
# * Body object which contains all parts of the email that are not part of the header, this
-
# includes any attachments, body text, MIME parts etc.
-
#
-
# ==Per RFC2822
-
#
-
# 2.1. General Description
-
#
-
# At the most basic level, a message is a series of characters. A
-
# message that is conformant with this standard is comprised of
-
# characters with values in the range 1 through 127 and interpreted as
-
# US-ASCII characters [ASCII]. For brevity, this document sometimes
-
# refers to this range of characters as simply "US-ASCII characters".
-
#
-
# Note: This standard specifies that messages are made up of characters
-
# in the US-ASCII range of 1 through 127. There are other documents,
-
# specifically the MIME document series [RFC2045, RFC2046, RFC2047,
-
# RFC2048, RFC2049], that extend this standard to allow for values
-
# outside of that range. Discussion of those mechanisms is not within
-
# the scope of this standard.
-
#
-
# Messages are divided into lines of characters. A line is a series of
-
# characters that is delimited with the two characters carriage-return
-
# and line-feed; that is, the carriage return (CR) character (ASCII
-
# value 13) followed immediately by the line feed (LF) character (ASCII
-
# value 10). (The carriage-return/line-feed pair is usually written in
-
# this document as "CRLF".)
-
#
-
# A message consists of header fields (collectively called "the header
-
# of the message") followed, optionally, by a body. The header is a
-
# sequence of lines of characters with special syntax as defined in
-
# this standard. The body is simply a sequence of characters that
-
# follows the header and is separated from the header by an empty line
-
# (i.e., a line with nothing preceding the CRLF).
-
1
class Message
-
-
1
include Patterns
-
1
include Utilities
-
-
# ==Making an email
-
#
-
# You can make an new mail object via a block, passing a string, file or direct assignment.
-
#
-
# ===Making an email via a block
-
#
-
# mail = Mail.new do
-
# from 'mikel@test.lindsaar.net'
-
# to 'you@test.lindsaar.net'
-
# subject 'This is a test email'
-
# body File.read('body.txt')
-
# end
-
#
-
# mail.to_s #=> "From: mikel@test.lindsaar.net\r\nTo: you@...
-
#
-
# ===Making an email via passing a string
-
#
-
# mail = Mail.new("To: mikel@test.lindsaar.net\r\nSubject: Hello\r\n\r\nHi there!")
-
# mail.body.to_s #=> 'Hi there!'
-
# mail.subject #=> 'Hello'
-
# mail.to #=> 'mikel@test.lindsaar.net'
-
#
-
# ===Making an email from a file
-
#
-
# mail = Mail.read('path/to/file.eml')
-
# mail.body.to_s #=> 'Hi there!'
-
# mail.subject #=> 'Hello'
-
# mail.to #=> 'mikel@test.lindsaar.net'
-
#
-
# ===Making an email via assignment
-
#
-
# You can assign values to a mail object via four approaches:
-
#
-
# * Message#field_name=(value)
-
# * Message#field_name(value)
-
# * Message#['field_name']=(value)
-
# * Message#[:field_name]=(value)
-
#
-
# Examples:
-
#
-
# mail = Mail.new
-
# mail['from'] = 'mikel@test.lindsaar.net'
-
# mail[:to] = 'you@test.lindsaar.net'
-
# mail.subject 'This is a test email'
-
# mail.body = 'This is a body'
-
#
-
# mail.to_s #=> "From: mikel@test.lindsaar.net\r\nTo: you@...
-
#
-
1
def initialize(*args, &block)
-
@body = nil
-
@body_raw = nil
-
@separate_parts = false
-
@text_part = nil
-
@html_part = nil
-
@errors = nil
-
@header = nil
-
@charset = 'UTF-8'
-
@defaulted_charset = true
-
-
@smtp_envelope_from = nil
-
@smtp_envelope_to = nil
-
-
@perform_deliveries = true
-
@raise_delivery_errors = true
-
-
@delivery_handler = nil
-
-
@delivery_method = Mail.delivery_method.dup
-
-
@transport_encoding = Mail::Encodings.get_encoding('7bit')
-
-
@mark_for_delete = false
-
-
if args.flatten.first.respond_to?(:each_pair)
-
init_with_hash(args.flatten.first)
-
else
-
init_with_string(args.flatten[0].to_s)
-
end
-
-
if block_given?
-
instance_eval(&block)
-
end
-
-
self
-
end
-
-
# If you assign a delivery handler, mail will call :deliver_mail on the
-
# object you assign to delivery_handler, it will pass itself as the
-
# single argument.
-
#
-
# If you define a delivery_handler, then you are responsible for the
-
# following actions in the delivery cycle:
-
#
-
# * Appending the mail object to Mail.deliveries as you see fit.
-
# * Checking the mail.perform_deliveries flag to decide if you should
-
# actually call :deliver! the mail object or not.
-
# * Checking the mail.raise_delivery_errors flag to decide if you
-
# should raise delivery errors if they occur.
-
# * Actually calling :deliver! (with the bang) on the mail object to
-
# get it to deliver itself.
-
#
-
# A simplest implementation of a delivery_handler would be
-
#
-
# class MyObject
-
#
-
# def initialize
-
# @mail = Mail.new('To: mikel@test.lindsaar.net')
-
# @mail.delivery_handler = self
-
# end
-
#
-
# attr_accessor :mail
-
#
-
# def deliver_mail(mail)
-
# yield
-
# end
-
# end
-
#
-
# Then doing:
-
#
-
# obj = MyObject.new
-
# obj.mail.deliver
-
#
-
# Would cause Mail to call obj.deliver_mail passing itself as a parameter,
-
# which then can just yield and let Mail do its own private do_delivery
-
# method.
-
1
attr_accessor :delivery_handler
-
-
# If set to false, mail will go through the motions of doing a delivery,
-
# but not actually call the delivery method or append the mail object to
-
# the Mail.deliveries collection. Useful for testing.
-
#
-
# Mail.deliveries.size #=> 0
-
# mail.delivery_method :smtp
-
# mail.perform_deliveries = false
-
# mail.deliver # Mail::SMTP not called here
-
# Mail.deliveries.size #=> 0
-
#
-
# If you want to test and query the Mail.deliveries collection to see what
-
# mail you sent, you should set perform_deliveries to true and use
-
# the :test mail delivery_method:
-
#
-
# Mail.deliveries.size #=> 0
-
# mail.delivery_method :test
-
# mail.perform_deliveries = true
-
# mail.deliver
-
# Mail.deliveries.size #=> 1
-
#
-
# This setting is ignored by mail (though still available as a flag) if you
-
# define a delivery_handler
-
1
attr_accessor :perform_deliveries
-
-
# If set to false, mail will silently catch and ignore any exceptions
-
# raised through attempting to deliver an email.
-
#
-
# This setting is ignored by mail (though still available as a flag) if you
-
# define a delivery_handler
-
1
attr_accessor :raise_delivery_errors
-
-
1
def register_for_delivery_notification(observer)
-
STDERR.puts("Message#register_for_delivery_notification is deprecated, please call Mail.register_observer instead")
-
Mail.register_observer(observer)
-
end
-
-
1
def inform_observers
-
Mail.inform_observers(self)
-
end
-
-
1
def inform_interceptors
-
Mail.inform_interceptors(self)
-
end
-
-
# Delivers an mail object.
-
#
-
# Examples:
-
#
-
# mail = Mail.read('file.eml')
-
# mail.deliver
-
1
def deliver
-
inform_interceptors
-
if delivery_handler
-
delivery_handler.deliver_mail(self) { do_delivery }
-
else
-
do_delivery
-
end
-
inform_observers
-
self
-
end
-
-
# This method bypasses checking perform_deliveries and raise_delivery_errors,
-
# so use with caution.
-
#
-
# It still however fires off the interceptors and calls the observers callbacks if they are defined.
-
#
-
# Returns self
-
1
def deliver!
-
inform_interceptors
-
response = delivery_method.deliver!(self)
-
inform_observers
-
delivery_method.settings[:return_response] ? response : self
-
end
-
-
1
def delivery_method(method = nil, settings = {})
-
unless method
-
@delivery_method
-
else
-
@delivery_method = Configuration.instance.lookup_delivery_method(method).new(settings)
-
end
-
end
-
-
1
def reply(*args, &block)
-
self.class.new.tap do |reply|
-
if message_id
-
bracketed_message_id = "<#{message_id}>"
-
reply.in_reply_to = bracketed_message_id
-
if !references.nil?
-
refs = [references].flatten.map { |r| "<#{r}>" }
-
refs << bracketed_message_id
-
reply.references = refs.join(' ')
-
elsif !in_reply_to.nil? && !in_reply_to.kind_of?(Array)
-
reply.references = "<#{in_reply_to}> #{bracketed_message_id}"
-
end
-
reply.references ||= bracketed_message_id
-
end
-
if subject
-
reply.subject = subject =~ /^Re:/i ? subject : "RE: #{subject}"
-
end
-
if reply_to || from
-
reply.to = self[reply_to ? :reply_to : :from].to_s
-
end
-
if to
-
reply.from = self[:to].formatted.first.to_s
-
end
-
-
unless args.empty?
-
if args.flatten.first.respond_to?(:each_pair)
-
reply.send(:init_with_hash, args.flatten.first)
-
else
-
reply.send(:init_with_string, args.flatten[0].to_s.strip)
-
end
-
end
-
-
if block_given?
-
reply.instance_eval(&block)
-
end
-
end
-
end
-
-
# Provides the operator needed for sort et al.
-
#
-
# Compares this mail object with another mail object, this is done by date, so an
-
# email that is older than another will appear first.
-
#
-
# Example:
-
#
-
# mail1 = Mail.new do
-
# date(Time.now)
-
# end
-
# mail2 = Mail.new do
-
# date(Time.now - 86400) # 1 day older
-
# end
-
# [mail2, mail1].sort #=> [mail2, mail1]
-
1
def <=>(other)
-
if other.nil?
-
1
-
else
-
self.date <=> other.date
-
end
-
end
-
-
# Two emails are the same if they have the same fields and body contents. One
-
# gotcha here is that Mail will insert Message-IDs when calling encoded, so doing
-
# mail1.encoded == mail2.encoded is most probably not going to return what you think
-
# as the assigned Message-IDs by Mail (if not already defined as the same) will ensure
-
# that the two objects are unique, and this comparison will ALWAYS return false.
-
#
-
# So the == operator has been defined like so: Two messages are the same if they have
-
# the same content, ignoring the Message-ID field, unless BOTH emails have a defined and
-
# different Message-ID value, then they are false.
-
#
-
# So, in practice the == operator works like this:
-
#
-
# m1 = Mail.new("Subject: Hello\r\n\r\nHello")
-
# m2 = Mail.new("Subject: Hello\r\n\r\nHello")
-
# m1 == m2 #=> true
-
#
-
# m1 = Mail.new("Subject: Hello\r\n\r\nHello")
-
# m2 = Mail.new("Message-ID: <1234@test>\r\nSubject: Hello\r\n\r\nHello")
-
# m1 == m2 #=> true
-
#
-
# m1 = Mail.new("Message-ID: <1234@test>\r\nSubject: Hello\r\n\r\nHello")
-
# m2 = Mail.new("Subject: Hello\r\n\r\nHello")
-
# m1 == m2 #=> true
-
#
-
# m1 = Mail.new("Message-ID: <1234@test>\r\nSubject: Hello\r\n\r\nHello")
-
# m2 = Mail.new("Message-ID: <1234@test>\r\nSubject: Hello\r\n\r\nHello")
-
# m1 == m2 #=> true
-
#
-
# m1 = Mail.new("Message-ID: <1234@test>\r\nSubject: Hello\r\n\r\nHello")
-
# m2 = Mail.new("Message-ID: <DIFFERENT@test>\r\nSubject: Hello\r\n\r\nHello")
-
# m1 == m2 #=> false
-
1
def ==(other)
-
return false unless other.respond_to?(:encoded)
-
-
if self.message_id && other.message_id
-
self.encoded == other.encoded
-
else
-
self_message_id, other_message_id = self.message_id, other.message_id
-
begin
-
self.message_id, other.message_id = '<temp@test>', '<temp@test>'
-
self.encoded == other.encoded
-
ensure
-
self.message_id, other.message_id = self_message_id, other_message_id
-
end
-
end
-
end
-
-
1
def initialize_copy(original)
-
super
-
@header = @header.dup
-
end
-
-
# Provides access to the raw source of the message as it was when it
-
# was instantiated. This is set at initialization and so is untouched
-
# by the parsers or decoder / encoders
-
#
-
# Example:
-
#
-
# mail = Mail.new('This is an invalid email message')
-
# mail.raw_source #=> "This is an invalid email message"
-
1
def raw_source
-
@raw_source
-
end
-
-
# Sets the envelope from for the email
-
1
def set_envelope( val )
-
@raw_envelope = val
-
@envelope = Mail::Envelope.new( val )
-
end
-
-
# The raw_envelope is the From mikel@test.lindsaar.net Mon May 2 16:07:05 2009
-
# type field that you can see at the top of any email that has come
-
# from a mailbox
-
1
def raw_envelope
-
@raw_envelope
-
end
-
-
1
def envelope_from
-
@envelope ? @envelope.from : nil
-
end
-
-
1
def envelope_date
-
@envelope ? @envelope.date : nil
-
end
-
-
# Sets the header of the message object.
-
#
-
# Example:
-
#
-
# mail.header = 'To: mikel@test.lindsaar.net\r\nFrom: Bob@bob.com'
-
# mail.header #=> <#Mail::Header
-
1
def header=(value)
-
@header = Mail::Header.new(value, charset)
-
end
-
-
# Returns the header object of the message object. Or, if passed
-
# a parameter sets the value.
-
#
-
# Example:
-
#
-
# mail = Mail::Message.new('To: mikel\r\nFrom: you')
-
# mail.header #=> #<Mail::Header:0x13ce14 @raw_source="To: mikel\r\nFr...
-
#
-
# mail.header #=> nil
-
# mail.header 'To: mikel\r\nFrom: you'
-
# mail.header #=> #<Mail::Header:0x13ce14 @raw_source="To: mikel\r\nFr...
-
1
def header(value = nil)
-
value ? self.header = value : @header
-
end
-
-
# Provides a way to set custom headers, by passing in a hash
-
1
def headers(hash = {})
-
hash.each_pair do |k,v|
-
header[k] = v
-
end
-
end
-
-
# Returns a list of parser errors on the header, each field that had an error
-
# will be reparsed as an unstructured field to preserve the data inside, but
-
# will not be used for further processing.
-
#
-
# It returns a nested array of [field_name, value, original_error_message]
-
# per error found.
-
#
-
# Example:
-
#
-
# message = Mail.new("Content-Transfer-Encoding: weirdo\r\n")
-
# message.errors.size #=> 1
-
# message.errors.first[0] #=> "Content-Transfer-Encoding"
-
# message.errors.first[1] #=> "weirdo"
-
# message.errors.first[3] #=> <The original error message exception>
-
#
-
# This is a good first defence on detecting spam by the way. Some spammers send
-
# invalid emails to try and get email parsers to give up parsing them.
-
1
def errors
-
header.errors
-
end
-
-
# Returns the Bcc value of the mail object as an array of strings of
-
# address specs.
-
#
-
# Example:
-
#
-
# mail.bcc = 'Mikel <mikel@test.lindsaar.net>'
-
# mail.bcc #=> ['mikel@test.lindsaar.net']
-
# mail.bcc = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.bcc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
#
-
# Also allows you to set the value by passing a value as a parameter
-
#
-
# Example:
-
#
-
# mail.bcc 'Mikel <mikel@test.lindsaar.net>'
-
# mail.bcc #=> ['mikel@test.lindsaar.net']
-
#
-
# Additionally, you can append new addresses to the returned Array like
-
# object.
-
#
-
# Example:
-
#
-
# mail.bcc 'Mikel <mikel@test.lindsaar.net>'
-
# mail.bcc << 'ada@test.lindsaar.net'
-
# mail.bcc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
1
def bcc( val = nil )
-
default :bcc, val
-
end
-
-
# Sets the Bcc value of the mail object, pass in a string of the field
-
#
-
# Example:
-
#
-
# mail.bcc = 'Mikel <mikel@test.lindsaar.net>'
-
# mail.bcc #=> ['mikel@test.lindsaar.net']
-
# mail.bcc = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.bcc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
1
def bcc=( val )
-
header[:bcc] = val
-
end
-
-
# Returns the Cc value of the mail object as an array of strings of
-
# address specs.
-
#
-
# Example:
-
#
-
# mail.cc = 'Mikel <mikel@test.lindsaar.net>'
-
# mail.cc #=> ['mikel@test.lindsaar.net']
-
# mail.cc = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.cc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
#
-
# Also allows you to set the value by passing a value as a parameter
-
#
-
# Example:
-
#
-
# mail.cc 'Mikel <mikel@test.lindsaar.net>'
-
# mail.cc #=> ['mikel@test.lindsaar.net']
-
#
-
# Additionally, you can append new addresses to the returned Array like
-
# object.
-
#
-
# Example:
-
#
-
# mail.cc 'Mikel <mikel@test.lindsaar.net>'
-
# mail.cc << 'ada@test.lindsaar.net'
-
# mail.cc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
1
def cc( val = nil )
-
default :cc, val
-
end
-
-
# Sets the Cc value of the mail object, pass in a string of the field
-
#
-
# Example:
-
#
-
# mail.cc = 'Mikel <mikel@test.lindsaar.net>'
-
# mail.cc #=> ['mikel@test.lindsaar.net']
-
# mail.cc = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.cc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
1
def cc=( val )
-
header[:cc] = val
-
end
-
-
1
def comments( val = nil )
-
default :comments, val
-
end
-
-
1
def comments=( val )
-
header[:comments] = val
-
end
-
-
1
def content_description( val = nil )
-
default :content_description, val
-
end
-
-
1
def content_description=( val )
-
header[:content_description] = val
-
end
-
-
1
def content_disposition( val = nil )
-
default :content_disposition, val
-
end
-
-
1
def content_disposition=( val )
-
header[:content_disposition] = val
-
end
-
-
1
def content_id( val = nil )
-
default :content_id, val
-
end
-
-
1
def content_id=( val )
-
header[:content_id] = val
-
end
-
-
1
def content_location( val = nil )
-
default :content_location, val
-
end
-
-
1
def content_location=( val )
-
header[:content_location] = val
-
end
-
-
1
def content_transfer_encoding( val = nil )
-
default :content_transfer_encoding, val
-
end
-
-
1
def content_transfer_encoding=( val )
-
header[:content_transfer_encoding] = val
-
end
-
-
1
def content_type( val = nil )
-
default :content_type, val
-
end
-
-
1
def content_type=( val )
-
header[:content_type] = val
-
end
-
-
1
def date( val = nil )
-
default :date, val
-
end
-
-
1
def date=( val )
-
header[:date] = val
-
end
-
-
1
def transport_encoding( val = nil)
-
if val
-
self.transport_encoding = val
-
else
-
@transport_encoding
-
end
-
end
-
-
1
def transport_encoding=( val )
-
@transport_encoding = Mail::Encodings.get_encoding(val)
-
end
-
-
# Returns the From value of the mail object as an array of strings of
-
# address specs.
-
#
-
# Example:
-
#
-
# mail.from = 'Mikel <mikel@test.lindsaar.net>'
-
# mail.from #=> ['mikel@test.lindsaar.net']
-
# mail.from = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.from #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
#
-
# Also allows you to set the value by passing a value as a parameter
-
#
-
# Example:
-
#
-
# mail.from 'Mikel <mikel@test.lindsaar.net>'
-
# mail.from #=> ['mikel@test.lindsaar.net']
-
#
-
# Additionally, you can append new addresses to the returned Array like
-
# object.
-
#
-
# Example:
-
#
-
# mail.from 'Mikel <mikel@test.lindsaar.net>'
-
# mail.from << 'ada@test.lindsaar.net'
-
# mail.from #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
1
def from( val = nil )
-
default :from, val
-
end
-
-
# Sets the From value of the mail object, pass in a string of the field
-
#
-
# Example:
-
#
-
# mail.from = 'Mikel <mikel@test.lindsaar.net>'
-
# mail.from #=> ['mikel@test.lindsaar.net']
-
# mail.from = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.from #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
1
def from=( val )
-
header[:from] = val
-
end
-
-
1
def in_reply_to( val = nil )
-
default :in_reply_to, val
-
end
-
-
1
def in_reply_to=( val )
-
header[:in_reply_to] = val
-
end
-
-
1
def keywords( val = nil )
-
default :keywords, val
-
end
-
-
1
def keywords=( val )
-
header[:keywords] = val
-
end
-
-
# Returns the Message-ID of the mail object. Note, per RFC 2822 the Message ID
-
# consists of what is INSIDE the < > usually seen in the mail header, so this method
-
# will return only what is inside.
-
#
-
# Example:
-
#
-
# mail.message_id = '<1234@message.id>'
-
# mail.message_id #=> '1234@message.id'
-
#
-
# Also allows you to set the Message-ID by passing a string as a parameter
-
#
-
# mail.message_id '<1234@message.id>'
-
# mail.message_id #=> '1234@message.id'
-
1
def message_id( val = nil )
-
default :message_id, val
-
end
-
-
# Sets the Message-ID. Note, per RFC 2822 the Message ID consists of what is INSIDE
-
# the < > usually seen in the mail header, so this method will return only what is inside.
-
#
-
# mail.message_id = '<1234@message.id>'
-
# mail.message_id #=> '1234@message.id'
-
1
def message_id=( val )
-
header[:message_id] = val
-
end
-
-
# Returns the MIME version of the email as a string
-
#
-
# Example:
-
#
-
# mail.mime_version = '1.0'
-
# mail.mime_version #=> '1.0'
-
#
-
# Also allows you to set the MIME version by passing a string as a parameter.
-
#
-
# Example:
-
#
-
# mail.mime_version '1.0'
-
# mail.mime_version #=> '1.0'
-
1
def mime_version( val = nil )
-
default :mime_version, val
-
end
-
-
# Sets the MIME version of the email by accepting a string
-
#
-
# Example:
-
#
-
# mail.mime_version = '1.0'
-
# mail.mime_version #=> '1.0'
-
1
def mime_version=( val )
-
header[:mime_version] = val
-
end
-
-
1
def received( val = nil )
-
if val
-
header[:received] = val
-
else
-
header[:received]
-
end
-
end
-
-
1
def received=( val )
-
header[:received] = val
-
end
-
-
1
def references( val = nil )
-
default :references, val
-
end
-
-
1
def references=( val )
-
header[:references] = val
-
end
-
-
# Returns the Reply-To value of the mail object as an array of strings of
-
# address specs.
-
#
-
# Example:
-
#
-
# mail.reply_to = 'Mikel <mikel@test.lindsaar.net>'
-
# mail.reply_to #=> ['mikel@test.lindsaar.net']
-
# mail.reply_to = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.reply_to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
#
-
# Also allows you to set the value by passing a value as a parameter
-
#
-
# Example:
-
#
-
# mail.reply_to 'Mikel <mikel@test.lindsaar.net>'
-
# mail.reply_to #=> ['mikel@test.lindsaar.net']
-
#
-
# Additionally, you can append new addresses to the returned Array like
-
# object.
-
#
-
# Example:
-
#
-
# mail.reply_to 'Mikel <mikel@test.lindsaar.net>'
-
# mail.reply_to << 'ada@test.lindsaar.net'
-
# mail.reply_to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
1
def reply_to( val = nil )
-
default :reply_to, val
-
end
-
-
# Sets the Reply-To value of the mail object, pass in a string of the field
-
#
-
# Example:
-
#
-
# mail.reply_to = 'Mikel <mikel@test.lindsaar.net>'
-
# mail.reply_to #=> ['mikel@test.lindsaar.net']
-
# mail.reply_to = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.reply_to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
1
def reply_to=( val )
-
header[:reply_to] = val
-
end
-
-
# Returns the Resent-Bcc value of the mail object as an array of strings of
-
# address specs.
-
#
-
# Example:
-
#
-
# mail.resent_bcc = 'Mikel <mikel@test.lindsaar.net>'
-
# mail.resent_bcc #=> ['mikel@test.lindsaar.net']
-
# mail.resent_bcc = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.resent_bcc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
#
-
# Also allows you to set the value by passing a value as a parameter
-
#
-
# Example:
-
#
-
# mail.resent_bcc 'Mikel <mikel@test.lindsaar.net>'
-
# mail.resent_bcc #=> ['mikel@test.lindsaar.net']
-
#
-
# Additionally, you can append new addresses to the returned Array like
-
# object.
-
#
-
# Example:
-
#
-
# mail.resent_bcc 'Mikel <mikel@test.lindsaar.net>'
-
# mail.resent_bcc << 'ada@test.lindsaar.net'
-
# mail.resent_bcc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
1
def resent_bcc( val = nil )
-
default :resent_bcc, val
-
end
-
-
# Sets the Resent-Bcc value of the mail object, pass in a string of the field
-
#
-
# Example:
-
#
-
# mail.resent_bcc = 'Mikel <mikel@test.lindsaar.net>'
-
# mail.resent_bcc #=> ['mikel@test.lindsaar.net']
-
# mail.resent_bcc = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.resent_bcc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
1
def resent_bcc=( val )
-
header[:resent_bcc] = val
-
end
-
-
# Returns the Resent-Cc value of the mail object as an array of strings of
-
# address specs.
-
#
-
# Example:
-
#
-
# mail.resent_cc = 'Mikel <mikel@test.lindsaar.net>'
-
# mail.resent_cc #=> ['mikel@test.lindsaar.net']
-
# mail.resent_cc = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.resent_cc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
#
-
# Also allows you to set the value by passing a value as a parameter
-
#
-
# Example:
-
#
-
# mail.resent_cc 'Mikel <mikel@test.lindsaar.net>'
-
# mail.resent_cc #=> ['mikel@test.lindsaar.net']
-
#
-
# Additionally, you can append new addresses to the returned Array like
-
# object.
-
#
-
# Example:
-
#
-
# mail.resent_cc 'Mikel <mikel@test.lindsaar.net>'
-
# mail.resent_cc << 'ada@test.lindsaar.net'
-
# mail.resent_cc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
1
def resent_cc( val = nil )
-
default :resent_cc, val
-
end
-
-
# Sets the Resent-Cc value of the mail object, pass in a string of the field
-
#
-
# Example:
-
#
-
# mail.resent_cc = 'Mikel <mikel@test.lindsaar.net>'
-
# mail.resent_cc #=> ['mikel@test.lindsaar.net']
-
# mail.resent_cc = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.resent_cc #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
1
def resent_cc=( val )
-
header[:resent_cc] = val
-
end
-
-
1
def resent_date( val = nil )
-
default :resent_date, val
-
end
-
-
1
def resent_date=( val )
-
header[:resent_date] = val
-
end
-
-
# Returns the Resent-From value of the mail object as an array of strings of
-
# address specs.
-
#
-
# Example:
-
#
-
# mail.resent_from = 'Mikel <mikel@test.lindsaar.net>'
-
# mail.resent_from #=> ['mikel@test.lindsaar.net']
-
# mail.resent_from = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.resent_from #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
#
-
# Also allows you to set the value by passing a value as a parameter
-
#
-
# Example:
-
#
-
# mail.resent_from ['Mikel <mikel@test.lindsaar.net>']
-
# mail.resent_from #=> 'mikel@test.lindsaar.net'
-
#
-
# Additionally, you can append new addresses to the returned Array like
-
# object.
-
#
-
# Example:
-
#
-
# mail.resent_from 'Mikel <mikel@test.lindsaar.net>'
-
# mail.resent_from << 'ada@test.lindsaar.net'
-
# mail.resent_from #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
1
def resent_from( val = nil )
-
default :resent_from, val
-
end
-
-
# Sets the Resent-From value of the mail object, pass in a string of the field
-
#
-
# Example:
-
#
-
# mail.resent_from = 'Mikel <mikel@test.lindsaar.net>'
-
# mail.resent_from #=> ['mikel@test.lindsaar.net']
-
# mail.resent_from = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.resent_from #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
1
def resent_from=( val )
-
header[:resent_from] = val
-
end
-
-
1
def resent_message_id( val = nil )
-
default :resent_message_id, val
-
end
-
-
1
def resent_message_id=( val )
-
header[:resent_message_id] = val
-
end
-
-
# Returns the Resent-Sender value of the mail object, as a single string of an address
-
# spec. A sender per RFC 2822 must be a single address, so you can not append to
-
# this address.
-
#
-
# Example:
-
#
-
# mail.resent_sender = 'Mikel <mikel@test.lindsaar.net>'
-
# mail.resent_sender #=> 'mikel@test.lindsaar.net'
-
#
-
# Also allows you to set the value by passing a value as a parameter
-
#
-
# Example:
-
#
-
# mail.resent_sender 'Mikel <mikel@test.lindsaar.net>'
-
# mail.resent_sender #=> 'mikel@test.lindsaar.net'
-
1
def resent_sender( val = nil )
-
default :resent_sender, val
-
end
-
-
# Sets the Resent-Sender value of the mail object, pass in a string of the field
-
#
-
# Example:
-
#
-
# mail.resent_sender = 'Mikel <mikel@test.lindsaar.net>'
-
# mail.resent_sender #=> 'mikel@test.lindsaar.net'
-
1
def resent_sender=( val )
-
header[:resent_sender] = val
-
end
-
-
# Returns the Resent-To value of the mail object as an array of strings of
-
# address specs.
-
#
-
# Example:
-
#
-
# mail.resent_to = 'Mikel <mikel@test.lindsaar.net>'
-
# mail.resent_to #=> ['mikel@test.lindsaar.net']
-
# mail.resent_to = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.resent_to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
#
-
# Also allows you to set the value by passing a value as a parameter
-
#
-
# Example:
-
#
-
# mail.resent_to 'Mikel <mikel@test.lindsaar.net>'
-
# mail.resent_to #=> ['mikel@test.lindsaar.net']
-
#
-
# Additionally, you can append new addresses to the returned Array like
-
# object.
-
#
-
# Example:
-
#
-
# mail.resent_to 'Mikel <mikel@test.lindsaar.net>'
-
# mail.resent_to << 'ada@test.lindsaar.net'
-
# mail.resent_to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
1
def resent_to( val = nil )
-
default :resent_to, val
-
end
-
-
# Sets the Resent-To value of the mail object, pass in a string of the field
-
#
-
# Example:
-
#
-
# mail.resent_to = 'Mikel <mikel@test.lindsaar.net>'
-
# mail.resent_to #=> ['mikel@test.lindsaar.net']
-
# mail.resent_to = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.resent_to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
1
def resent_to=( val )
-
header[:resent_to] = val
-
end
-
-
# Returns the return path of the mail object, or sets it if you pass a string
-
1
def return_path( val = nil )
-
default :return_path, val
-
end
-
-
# Sets the return path of the object
-
1
def return_path=( val )
-
header[:return_path] = val
-
end
-
-
# Returns the Sender value of the mail object, as a single string of an address
-
# spec. A sender per RFC 2822 must be a single address.
-
#
-
# Example:
-
#
-
# mail.sender = 'Mikel <mikel@test.lindsaar.net>'
-
# mail.sender #=> 'mikel@test.lindsaar.net'
-
#
-
# Also allows you to set the value by passing a value as a parameter
-
#
-
# Example:
-
#
-
# mail.sender 'Mikel <mikel@test.lindsaar.net>'
-
# mail.sender #=> 'mikel@test.lindsaar.net'
-
1
def sender( val = nil )
-
default :sender, val
-
end
-
-
# Sets the Sender value of the mail object, pass in a string of the field
-
#
-
# Example:
-
#
-
# mail.sender = 'Mikel <mikel@test.lindsaar.net>'
-
# mail.sender #=> 'mikel@test.lindsaar.net'
-
1
def sender=( val )
-
header[:sender] = val
-
end
-
-
# Returns the SMTP Envelope From value of the mail object, as a single
-
# string of an address spec.
-
#
-
# Defaults to Return-Path, Sender, or the first From address.
-
#
-
# Example:
-
#
-
# mail.smtp_envelope_from = 'Mikel <mikel@test.lindsaar.net>'
-
# mail.smtp_envelope_from #=> 'mikel@test.lindsaar.net'
-
#
-
# Also allows you to set the value by passing a value as a parameter
-
#
-
# Example:
-
#
-
# mail.smtp_envelope_from 'Mikel <mikel@test.lindsaar.net>'
-
# mail.smtp_envelope_from #=> 'mikel@test.lindsaar.net'
-
1
def smtp_envelope_from( val = nil )
-
if val
-
self.smtp_envelope_from = val
-
else
-
@smtp_envelope_from || return_path || sender || from_addrs.first
-
end
-
end
-
-
# Sets the From address on the SMTP Envelope.
-
#
-
# Example:
-
#
-
# mail.smtp_envelope_from = 'Mikel <mikel@test.lindsaar.net>'
-
# mail.smtp_envelope_from #=> 'mikel@test.lindsaar.net'
-
1
def smtp_envelope_from=( val )
-
@smtp_envelope_from = val
-
end
-
-
# Returns the SMTP Envelope To value of the mail object.
-
#
-
# Defaults to #destinations: To, Cc, and Bcc addresses.
-
#
-
# Example:
-
#
-
# mail.smtp_envelope_to = 'Mikel <mikel@test.lindsaar.net>'
-
# mail.smtp_envelope_to #=> 'mikel@test.lindsaar.net'
-
#
-
# Also allows you to set the value by passing a value as a parameter
-
#
-
# Example:
-
#
-
# mail.smtp_envelope_to ['Mikel <mikel@test.lindsaar.net>', 'Lindsaar <lindsaar@test.lindsaar.net>']
-
# mail.smtp_envelope_to #=> ['mikel@test.lindsaar.net', 'lindsaar@test.lindsaar.net']
-
1
def smtp_envelope_to( val = nil )
-
if val
-
self.smtp_envelope_to = val
-
else
-
@smtp_envelope_to || destinations
-
end
-
end
-
-
# Sets the To addresses on the SMTP Envelope.
-
#
-
# Example:
-
#
-
# mail.smtp_envelope_to = 'Mikel <mikel@test.lindsaar.net>'
-
# mail.smtp_envelope_to #=> 'mikel@test.lindsaar.net'
-
#
-
# mail.smtp_envelope_to = ['Mikel <mikel@test.lindsaar.net>', 'Lindsaar <lindsaar@test.lindsaar.net>']
-
# mail.smtp_envelope_to #=> ['mikel@test.lindsaar.net', 'lindsaar@test.lindsaar.net']
-
1
def smtp_envelope_to=( val )
-
@smtp_envelope_to =
-
case val
-
when Array, NilClass
-
val
-
else
-
[val]
-
end
-
end
-
-
# Returns the decoded value of the subject field, as a single string.
-
#
-
# Example:
-
#
-
# mail.subject = "G'Day mate"
-
# mail.subject #=> "G'Day mate"
-
# mail.subject = '=?UTF-8?Q?This_is_=E3=81=82_string?='
-
# mail.subject #=> "This is あ string"
-
#
-
# Also allows you to set the value by passing a value as a parameter
-
#
-
# Example:
-
#
-
# mail.subject "G'Day mate"
-
# mail.subject #=> "G'Day mate"
-
1
def subject( val = nil )
-
default :subject, val
-
end
-
-
# Sets the Subject value of the mail object, pass in a string of the field
-
#
-
# Example:
-
#
-
# mail.subject = '=?UTF-8?Q?This_is_=E3=81=82_string?='
-
# mail.subject #=> "This is あ string"
-
1
def subject=( val )
-
header[:subject] = val
-
end
-
-
# Returns the To value of the mail object as an array of strings of
-
# address specs.
-
#
-
# Example:
-
#
-
# mail.to = 'Mikel <mikel@test.lindsaar.net>'
-
# mail.to #=> ['mikel@test.lindsaar.net']
-
# mail.to = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
#
-
# Also allows you to set the value by passing a value as a parameter
-
#
-
# Example:
-
#
-
# mail.to 'Mikel <mikel@test.lindsaar.net>'
-
# mail.to #=> ['mikel@test.lindsaar.net']
-
#
-
# Additionally, you can append new addresses to the returned Array like
-
# object.
-
#
-
# Example:
-
#
-
# mail.to 'Mikel <mikel@test.lindsaar.net>'
-
# mail.to << 'ada@test.lindsaar.net'
-
# mail.to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
1
def to( val = nil )
-
default :to, val
-
end
-
-
# Sets the To value of the mail object, pass in a string of the field
-
#
-
# Example:
-
#
-
# mail.to = 'Mikel <mikel@test.lindsaar.net>'
-
# mail.to #=> ['mikel@test.lindsaar.net']
-
# mail.to = 'Mikel <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
-
# mail.to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
-
1
def to=( val )
-
header[:to] = val
-
end
-
-
# Returns the default value of the field requested as a symbol.
-
#
-
# Each header field has a :default method which returns the most common use case for
-
# that field, for example, the date field types will return a DateTime object when
-
# sent :default, the subject, or unstructured fields will return a decoded string of
-
# their value, the address field types will return a single addr_spec or an array of
-
# addr_specs if there is more than one.
-
1
def default( sym, val = nil )
-
if val
-
header[sym] = val
-
else
-
header[sym].default if header[sym]
-
end
-
end
-
-
# Sets the body object of the message object.
-
#
-
# Example:
-
#
-
# mail.body = 'This is the body'
-
# mail.body #=> #<Mail::Body:0x13919c @raw_source="This is the bo...
-
#
-
# You can also reset the body of an Message object by setting body to nil
-
#
-
# Example:
-
#
-
# mail.body = 'this is the body'
-
# mail.body.encoded #=> 'this is the body'
-
# mail.body = nil
-
# mail.body.encoded #=> ''
-
#
-
# If you try and set the body of an email that is a multipart email, then instead
-
# of deleting all the parts of your email, mail will add a text/plain part to
-
# your email:
-
#
-
# mail.add_file 'somefilename.png'
-
# mail.parts.length #=> 1
-
# mail.body = "This is a body"
-
# mail.parts.length #=> 2
-
# mail.parts.last.content_type.content_type #=> 'This is a body'
-
1
def body=(value)
-
body_lazy(value)
-
end
-
-
# Returns the body of the message object. Or, if passed
-
# a parameter sets the value.
-
#
-
# Example:
-
#
-
# mail = Mail::Message.new('To: mikel\r\n\r\nThis is the body')
-
# mail.body #=> #<Mail::Body:0x13919c @raw_source="This is the bo...
-
#
-
# mail.body 'This is another body'
-
# mail.body #=> #<Mail::Body:0x13919c @raw_source="This is anothe...
-
1
def body(value = nil)
-
if value
-
self.body = value
-
# add_encoding_to_body
-
else
-
process_body_raw if @body_raw
-
@body
-
end
-
end
-
-
1
def body_encoding(value)
-
if value.nil?
-
body.encoding
-
else
-
body.encoding = value
-
end
-
end
-
-
1
def body_encoding=(value)
-
body.encoding = value
-
end
-
-
# Returns the list of addresses this message should be sent to by
-
# collecting the addresses off the to, cc and bcc fields.
-
#
-
# Example:
-
#
-
# mail.to = 'mikel@test.lindsaar.net'
-
# mail.cc = 'sam@test.lindsaar.net'
-
# mail.bcc = 'bob@test.lindsaar.net'
-
# mail.destinations.length #=> 3
-
# mail.destinations.first #=> 'mikel@test.lindsaar.net'
-
1
def destinations
-
[to_addrs, cc_addrs, bcc_addrs].compact.flatten
-
end
-
-
# Returns an array of addresses (the encoded value) in the From field,
-
# if no From field, returns an empty array
-
1
def from_addrs
-
from ? [from].flatten : []
-
end
-
-
# Returns an array of addresses (the encoded value) in the To field,
-
# if no To field, returns an empty array
-
1
def to_addrs
-
to ? [to].flatten : []
-
end
-
-
# Returns an array of addresses (the encoded value) in the Cc field,
-
# if no Cc field, returns an empty array
-
1
def cc_addrs
-
cc ? [cc].flatten : []
-
end
-
-
# Returns an array of addresses (the encoded value) in the Bcc field,
-
# if no Bcc field, returns an empty array
-
1
def bcc_addrs
-
bcc ? [bcc].flatten : []
-
end
-
-
# Allows you to add an arbitrary header
-
#
-
# Example:
-
#
-
# mail['foo'] = '1234'
-
# mail['foo'].to_s #=> '1234'
-
1
def []=(name, value)
-
if name.to_s == 'body'
-
self.body = value
-
elsif name.to_s =~ /content[-_]type/i
-
header[name] = value
-
elsif name.to_s == 'charset'
-
self.charset = value
-
else
-
header[name] = value
-
end
-
end
-
-
# Allows you to read an arbitrary header
-
#
-
# Example:
-
#
-
# mail['foo'] = '1234'
-
# mail['foo'].to_s #=> '1234'
-
1
def [](name)
-
header[underscoreize(name)]
-
end
-
-
# Method Missing in this implementation allows you to set any of the
-
# standard fields directly as you would the "to", "subject" etc.
-
#
-
# Those fields used most often (to, subject et al) are given their
-
# own method for ease of documentation and also to avoid the hook
-
# call to method missing.
-
#
-
# This will only catch the known fields listed in:
-
#
-
# Mail::Field::KNOWN_FIELDS
-
#
-
# as per RFC 2822, any ruby string or method name could pretty much
-
# be a field name, so we don't want to just catch ANYTHING sent to
-
# a message object and interpret it as a header.
-
#
-
# This method provides all three types of header call to set, read
-
# and explicitly set with the = operator
-
#
-
# Examples:
-
#
-
# mail.comments = 'These are some comments'
-
# mail.comments #=> 'These are some comments'
-
#
-
# mail.comments 'These are other comments'
-
# mail.comments #=> 'These are other comments'
-
#
-
#
-
# mail.date = 'Tue, 1 Jul 2003 10:52:37 +0200'
-
# mail.date.to_s #=> 'Tue, 1 Jul 2003 10:52:37 +0200'
-
#
-
# mail.date 'Tue, 1 Jul 2003 10:52:37 +0200'
-
# mail.date.to_s #=> 'Tue, 1 Jul 2003 10:52:37 +0200'
-
#
-
#
-
# mail.resent_msg_id = '<1234@resent_msg_id.lindsaar.net>'
-
# mail.resent_msg_id #=> '<1234@resent_msg_id.lindsaar.net>'
-
#
-
# mail.resent_msg_id '<4567@resent_msg_id.lindsaar.net>'
-
# mail.resent_msg_id #=> '<4567@resent_msg_id.lindsaar.net>'
-
1
def method_missing(name, *args, &block)
-
#:nodoc:
-
# Only take the structured fields, as we could take _anything_ really
-
# as it could become an optional field... "but therin lies the dark side"
-
field_name = underscoreize(name).chomp("=")
-
if Mail::Field::KNOWN_FIELDS.include?(field_name)
-
if args.empty?
-
header[field_name]
-
else
-
header[field_name] = args.first
-
end
-
else
-
super # otherwise pass it on
-
end
-
#:startdoc:
-
end
-
-
# Returns an FieldList of all the fields in the header in the order that
-
# they appear in the header
-
1
def header_fields
-
header.fields
-
end
-
-
# Returns true if the message has a message ID field, the field may or may
-
# not have a value, but the field exists or not.
-
1
def has_message_id?
-
header.has_message_id?
-
end
-
-
# Returns true if the message has a Date field, the field may or may
-
# not have a value, but the field exists or not.
-
1
def has_date?
-
header.has_date?
-
end
-
-
# Returns true if the message has a Mime-Version field, the field may or may
-
# not have a value, but the field exists or not.
-
1
def has_mime_version?
-
header.has_mime_version?
-
end
-
-
1
def has_content_type?
-
tmp = header[:content_type].main_type rescue nil
-
!!tmp
-
end
-
-
1
def has_charset?
-
tmp = header[:content_type].parameters rescue nil
-
!!(has_content_type? && tmp && tmp['charset'])
-
end
-
-
1
def has_content_transfer_encoding?
-
header[:content_transfer_encoding] && header[:content_transfer_encoding].errors.blank?
-
end
-
-
1
def has_transfer_encoding? # :nodoc:
-
STDERR.puts(":has_transfer_encoding? is deprecated in Mail 1.4.3. Please use has_content_transfer_encoding?\n#{caller}")
-
has_content_transfer_encoding?
-
end
-
-
# Creates a new empty Message-ID field and inserts it in the correct order
-
# into the Header. The MessageIdField object will automatically generate
-
# a unique message ID if you try and encode it or output it to_s without
-
# specifying a message id.
-
#
-
# It will preserve the message ID you specify if you do.
-
1
def add_message_id(msg_id_val = '')
-
header['message-id'] = msg_id_val
-
end
-
-
# Creates a new empty Date field and inserts it in the correct order
-
# into the Header. The DateField object will automatically generate
-
# DateTime.now's date if you try and encode it or output it to_s without
-
# specifying a date yourself.
-
#
-
# It will preserve any date you specify if you do.
-
1
def add_date(date_val = '')
-
header['date'] = date_val
-
end
-
-
# Creates a new empty Mime Version field and inserts it in the correct order
-
# into the Header. The MimeVersion object will automatically generate
-
# set itself to '1.0' if you try and encode it or output it to_s without
-
# specifying a version yourself.
-
#
-
# It will preserve any date you specify if you do.
-
1
def add_mime_version(ver_val = '')
-
header['mime-version'] = ver_val
-
end
-
-
# Adds a content type and charset if the body is US-ASCII
-
#
-
# Otherwise raises a warning
-
1
def add_content_type
-
header[:content_type] = 'text/plain'
-
end
-
-
# Adds a content type and charset if the body is US-ASCII
-
#
-
# Otherwise raises a warning
-
1
def add_charset
-
if !body.empty?
-
# Only give a warning if this isn't an attachment, has non US-ASCII and the user
-
# has not specified an encoding explicitly.
-
if @defaulted_charset && body.raw_source.not_ascii_only? && !self.attachment?
-
warning = "Non US-ASCII detected and no charset defined.\nDefaulting to UTF-8, set your own if this is incorrect.\n"
-
STDERR.puts(warning)
-
end
-
header[:content_type].parameters['charset'] = @charset
-
end
-
end
-
-
# Adds a content transfer encoding
-
#
-
# Otherwise raises a warning
-
1
def add_content_transfer_encoding
-
if body.only_us_ascii?
-
header[:content_transfer_encoding] = '7bit'
-
else
-
warning = "Non US-ASCII detected and no content-transfer-encoding defined.\nDefaulting to 8bit, set your own if this is incorrect.\n"
-
STDERR.puts(warning)
-
header[:content_transfer_encoding] = '8bit'
-
end
-
end
-
-
1
def add_transfer_encoding # :nodoc:
-
STDERR.puts(":add_transfer_encoding is deprecated in Mail 1.4.3. Please use add_content_transfer_encoding\n#{caller}")
-
add_content_transfer_encoding
-
end
-
-
1
def transfer_encoding # :nodoc:
-
STDERR.puts(":transfer_encoding is deprecated in Mail 1.4.3. Please use content_transfer_encoding\n#{caller}")
-
content_transfer_encoding
-
end
-
-
# Returns the MIME media type of part we are on, this is taken from the content-type header
-
1
def mime_type
-
has_content_type? ? header[:content_type].string : nil rescue nil
-
end
-
-
1
def message_content_type
-
STDERR.puts(":message_content_type is deprecated in Mail 1.4.3. Please use mime_type\n#{caller}")
-
mime_type
-
end
-
-
# Returns the character set defined in the content type field
-
1
def charset
-
if @header
-
has_content_type? ? content_type_parameters['charset'] : @charset
-
else
-
@charset
-
end
-
end
-
-
# Sets the charset to the supplied value.
-
1
def charset=(value)
-
@defaulted_charset = false
-
@charset = value
-
@header.charset = value
-
end
-
-
# Returns the main content type
-
1
def main_type
-
has_content_type? ? header[:content_type].main_type : nil rescue nil
-
end
-
-
# Returns the sub content type
-
1
def sub_type
-
has_content_type? ? header[:content_type].sub_type : nil rescue nil
-
end
-
-
# Returns the content type parameters
-
1
def mime_parameters
-
STDERR.puts(':mime_parameters is deprecated in Mail 1.4.3, please use :content_type_parameters instead')
-
content_type_parameters
-
end
-
-
# Returns the content type parameters
-
1
def content_type_parameters
-
has_content_type? ? header[:content_type].parameters : nil rescue nil
-
end
-
-
# Returns true if the message is multipart
-
1
def multipart?
-
has_content_type? ? !!(main_type =~ /^multipart$/i) : false
-
end
-
-
# Returns true if the message is a multipart/report
-
1
def multipart_report?
-
multipart? && sub_type =~ /^report$/i
-
end
-
-
# Returns true if the message is a multipart/report; report-type=delivery-status;
-
1
def delivery_status_report?
-
multipart_report? && content_type_parameters['report-type'] =~ /^delivery-status$/i
-
end
-
-
# returns the part in a multipart/report email that has the content-type delivery-status
-
1
def delivery_status_part
-
@delivery_stats_part ||= parts.select { |p| p.delivery_status_report_part? }.first
-
end
-
-
1
def bounced?
-
delivery_status_part and delivery_status_part.bounced?
-
end
-
-
1
def action
-
delivery_status_part and delivery_status_part.action
-
end
-
-
1
def final_recipient
-
delivery_status_part and delivery_status_part.final_recipient
-
end
-
-
1
def error_status
-
delivery_status_part and delivery_status_part.error_status
-
end
-
-
1
def diagnostic_code
-
delivery_status_part and delivery_status_part.diagnostic_code
-
end
-
-
1
def remote_mta
-
delivery_status_part and delivery_status_part.remote_mta
-
end
-
-
1
def retryable?
-
delivery_status_part and delivery_status_part.retryable?
-
end
-
-
# Returns the current boundary for this message part
-
1
def boundary
-
content_type_parameters ? content_type_parameters['boundary'] : nil
-
end
-
-
# Returns a parts list object of all the parts in the message
-
1
def parts
-
body.parts
-
end
-
-
# Returns an AttachmentsList object, which holds all of the attachments in
-
# the receiver object (either the entire email or a part within) and all
-
# of its descendants.
-
#
-
# It also allows you to add attachments to the mail object directly, like so:
-
#
-
# mail.attachments['filename.jpg'] = File.read('/path/to/filename.jpg')
-
#
-
# If you do this, then Mail will take the file name and work out the MIME media type
-
# set the Content-Type, Content-Disposition, Content-Transfer-Encoding and
-
# base64 encode the contents of the attachment all for you.
-
#
-
# You can also specify overrides if you want by passing a hash instead of a string:
-
#
-
# mail.attachments['filename.jpg'] = {:mime_type => 'application/x-gzip',
-
# :content => File.read('/path/to/filename.jpg')}
-
#
-
# If you want to use a different encoding than Base64, you can pass an encoding in,
-
# but then it is up to you to pass in the content pre-encoded, and don't expect
-
# Mail to know how to decode this data:
-
#
-
# file_content = SpecialEncode(File.read('/path/to/filename.jpg'))
-
# mail.attachments['filename.jpg'] = {:mime_type => 'application/x-gzip',
-
# :encoding => 'SpecialEncoding',
-
# :content => file_content }
-
#
-
# You can also search for specific attachments:
-
#
-
# # By Filename
-
# mail.attachments['filename.jpg'] #=> Mail::Part object or nil
-
#
-
# # or by index
-
# mail.attachments[0] #=> Mail::Part (first attachment)
-
#
-
1
def attachments
-
parts.attachments
-
end
-
-
1
def has_attachments?
-
!attachments.empty?
-
end
-
-
# Accessor for html_part
-
1
def html_part(&block)
-
if block_given?
-
self.html_part = Mail::Part.new(:content_type => 'text/html', &block)
-
else
-
@html_part || find_first_mime_type('text/html')
-
end
-
end
-
-
# Accessor for text_part
-
1
def text_part(&block)
-
if block_given?
-
self.text_part = Mail::Part.new(:content_type => 'text/plain', &block)
-
else
-
@text_part || find_first_mime_type('text/plain')
-
end
-
end
-
-
# Helper to add a html part to a multipart/alternative email. If this and
-
# text_part are both defined in a message, then it will be a multipart/alternative
-
# message and set itself that way.
-
1
def html_part=(msg)
-
# Assign the html part and set multipart/alternative if there's a text part.
-
if msg
-
@html_part = msg
-
@html_part.content_type = 'text/html' unless @html_part.has_content_type?
-
add_multipart_alternate_header if text_part
-
add_part @html_part
-
-
# If nil, delete the html part and back out of multipart/alternative.
-
elsif @html_part
-
parts.delete_if { |p| p.object_id == @html_part.object_id }
-
@html_part = nil
-
if text_part
-
self.content_type = nil
-
body.boundary = nil
-
end
-
end
-
end
-
-
# Helper to add a text part to a multipart/alternative email. If this and
-
# html_part are both defined in a message, then it will be a multipart/alternative
-
# message and set itself that way.
-
1
def text_part=(msg)
-
# Assign the text part and set multipart/alternative if there's an html part.
-
if msg
-
@text_part = msg
-
@text_part.content_type = 'text/plain' unless @text_part.has_content_type?
-
add_multipart_alternate_header if html_part
-
add_part @text_part
-
-
# If nil, delete the text part and back out of multipart/alternative.
-
elsif @text_part
-
parts.delete_if { |p| p.object_id == @text_part.object_id }
-
@text_part = nil
-
if html_part
-
self.content_type = nil
-
body.boundary = nil
-
end
-
end
-
end
-
-
# Adds a part to the parts list or creates the part list
-
1
def add_part(part)
-
if !body.multipart? && !self.body.decoded.blank?
-
@text_part = Mail::Part.new('Content-Type: text/plain;')
-
@text_part.body = body.decoded
-
self.body << @text_part
-
add_multipart_alternate_header
-
end
-
add_boundary
-
self.body << part
-
end
-
-
# Allows you to add a part in block form to an existing mail message object
-
#
-
# Example:
-
#
-
# mail = Mail.new do
-
# part :content_type => "multipart/alternative", :content_disposition => "inline" do |p|
-
# p.part :content_type => "text/plain", :body => "test text\nline #2"
-
# p.part :content_type => "text/html", :body => "<b>test</b> HTML<br/>\nline #2"
-
# end
-
# end
-
1
def part(params = {})
-
new_part = Part.new(params)
-
yield new_part if block_given?
-
add_part(new_part)
-
end
-
-
# Adds a file to the message. You have two options with this method, you can
-
# just pass in the absolute path to the file you want and Mail will read the file,
-
# get the filename from the path you pass in and guess the MIME media type, or you
-
# can pass in the filename as a string, and pass in the file content as a blob.
-
#
-
# Example:
-
#
-
# m = Mail.new
-
# m.add_file('/path/to/filename.png')
-
#
-
# m = Mail.new
-
# m.add_file(:filename => 'filename.png', :content => File.read('/path/to/file.jpg'))
-
#
-
# Note also that if you add a file to an existing message, Mail will convert that message
-
# to a MIME multipart email, moving whatever plain text body you had into its own text
-
# plain part.
-
#
-
# Example:
-
#
-
# m = Mail.new do
-
# body 'this is some text'
-
# end
-
# m.multipart? #=> false
-
# m.add_file('/path/to/filename.png')
-
# m.multipart? #=> true
-
# m.parts.first.content_type.content_type #=> 'text/plain'
-
# m.parts.last.content_type.content_type #=> 'image/png'
-
#
-
# See also #attachments
-
1
def add_file(values)
-
convert_to_multipart unless self.multipart? || self.body.decoded.blank?
-
add_multipart_mixed_header
-
if values.is_a?(String)
-
basename = File.basename(values)
-
filedata = File.open(values, 'rb') { |f| f.read }
-
else
-
basename = values[:filename]
-
filedata = values[:content] || File.open(values[:filename], 'rb') { |f| f.read }
-
end
-
self.attachments[basename] = filedata
-
end
-
-
1
def convert_to_multipart
-
text = body.decoded
-
self.body = ''
-
text_part = Mail::Part.new({:content_type => 'text/plain;',
-
:body => text})
-
text_part.charset = charset unless @defaulted_charset
-
self.body << text_part
-
end
-
-
# Encodes the message, calls encode on all its parts, gets an email message
-
# ready to send
-
1
def ready_to_send!
-
identify_and_set_transfer_encoding
-
parts.sort!([ "text/plain", "text/enriched", "text/html", "multipart/alternative" ])
-
parts.each do |part|
-
part.transport_encoding = transport_encoding
-
part.ready_to_send!
-
end
-
add_required_fields
-
end
-
-
1
def encode!
-
STDERR.puts("Deprecated in 1.1.0 in favour of :ready_to_send! as it is less confusing with encoding and decoding.")
-
ready_to_send!
-
end
-
-
# Outputs an encoded string representation of the mail message including
-
# all headers, attachments, etc. This is an encoded email in US-ASCII,
-
# so it is able to be directly sent to an email server.
-
1
def encoded
-
ready_to_send!
-
buffer = header.encoded
-
buffer << "\r\n"
-
buffer << body.encoded(content_transfer_encoding)
-
buffer
-
end
-
-
1
def without_attachments!
-
return self unless has_attachments?
-
-
parts.delete_if { |p| p.attachment? }
-
body_raw = if parts.empty?
-
''
-
else
-
body.encoded
-
end
-
-
@body = Mail::Body.new(body_raw)
-
-
self
-
end
-
-
1
def to_yaml(opts = {})
-
hash = {}
-
hash['headers'] = {}
-
header.fields.each do |field|
-
hash['headers'][field.name] = field.value
-
end
-
hash['delivery_handler'] = delivery_handler.to_s if delivery_handler
-
hash['transport_encoding'] = transport_encoding.to_s
-
special_variables = [:@header, :@delivery_handler, :@transport_encoding]
-
if multipart?
-
hash['multipart_body'] = []
-
body.parts.map { |part| hash['multipart_body'] << part.to_yaml }
-
special_variables.push(:@body, :@text_part, :@html_part)
-
end
-
(instance_variables.map(&:to_sym) - special_variables).each do |var|
-
hash[var.to_s] = instance_variable_get(var)
-
end
-
hash.to_yaml(opts)
-
end
-
-
1
def self.from_yaml(str)
-
hash = YAML.load(str)
-
m = self.new(:headers => hash['headers'])
-
hash.delete('headers')
-
hash.each do |k,v|
-
case
-
when k == 'delivery_handler'
-
begin
-
m.delivery_handler = Object.const_get(v) unless v.blank?
-
rescue NameError
-
end
-
when k == 'transport_encoding'
-
m.transport_encoding(v)
-
when k == 'multipart_body'
-
v.map {|part| m.add_part Mail::Part.from_yaml(part) }
-
when k =~ /^@/
-
m.instance_variable_set(k.to_sym, v)
-
end
-
end
-
m
-
end
-
-
1
def self.from_hash(hash)
-
Mail::Message.new(hash)
-
end
-
-
1
def to_s
-
encoded
-
end
-
-
1
def inspect
-
"#<#{self.class}:#{self.object_id}, Multipart: #{multipart?}, Headers: #{header.field_summary}>"
-
end
-
-
1
def decoded
-
case
-
when self.text?
-
decode_body_as_text
-
when self.attachment?
-
decode_body
-
when !self.multipart?
-
body.decoded
-
else
-
raise NoMethodError, 'Can not decode an entire message, try calling #decoded on the various fields and body or parts if it is a multipart message.'
-
end
-
end
-
-
1
def read
-
if self.attachment?
-
decode_body
-
else
-
raise NoMethodError, 'Can not call read on a part unless it is an attachment.'
-
end
-
end
-
-
1
def decode_body
-
body.decoded
-
end
-
-
# Returns true if this part is an attachment,
-
# false otherwise.
-
1
def attachment?
-
!!find_attachment
-
end
-
-
# Returns the attachment data if there is any
-
1
def attachment
-
@attachment
-
end
-
-
# Returns the filename of the attachment
-
1
def filename
-
find_attachment
-
end
-
-
1
def all_parts
-
parts.map { |p| [p, p.all_parts] }.flatten
-
end
-
-
1
def find_first_mime_type(mt)
-
all_parts.detect { |p| p.mime_type == mt && !p.attachment? }
-
end
-
-
# Skips the deletion of this message. All other messages
-
# flagged for delete still will be deleted at session close (i.e. when
-
# #find exits). Only has an effect if you're using #find_and_delete
-
# or #find with :delete_after_find set to true.
-
1
def skip_deletion
-
@mark_for_delete = false
-
end
-
-
# Sets whether this message should be deleted at session close (i.e.
-
# after #find). Message will only be deleted if messages are retrieved
-
# using the #find_and_delete method, or by calling #find with
-
# :delete_after_find set to true.
-
1
def mark_for_delete=(value = true)
-
@mark_for_delete = value
-
end
-
-
# Returns whether message will be marked for deletion.
-
# If so, the message will be deleted at session close (i.e. after #find
-
# exits), but only if also using the #find_and_delete method, or by
-
# calling #find with :delete_after_find set to true.
-
#
-
# Side-note: Just to be clear, this method will return true even if
-
# the message hasn't yet been marked for delete on the mail server.
-
# However, if this method returns true, it *will be* marked on the
-
# server after each block yields back to #find or #find_and_delete.
-
1
def is_marked_for_delete?
-
return @mark_for_delete
-
end
-
-
1
def text?
-
has_content_type? ? !!(main_type =~ /^text$/i) : false
-
end
-
-
1
private
-
-
# 2.1. General Description
-
# A message consists of header fields (collectively called "the header
-
# of the message") followed, optionally, by a body. The header is a
-
# sequence of lines of characters with special syntax as defined in
-
# this standard. The body is simply a sequence of characters that
-
# follows the header and is separated from the header by an empty line
-
# (i.e., a line with nothing preceding the CRLF).
-
#
-
# Additionally, I allow for the case where someone might have put whitespace
-
# on the "gap line"
-
1
def parse_message
-
header_part, body_part = raw_source.lstrip.split(/#{CRLF}#{CRLF}|#{CRLF}#{WSP}*#{CRLF}(?!#{WSP})/m, 2)
-
self.header = header_part
-
self.body = body_part
-
end
-
-
1
def raw_source=(value)
-
@raw_source = value.to_crlf
-
@raw_source.force_encoding("binary") if RUBY_VERSION >= "1.9.1"
-
@raw_source
-
end
-
-
# see comments to body=. We take data and process it lazily
-
1
def body_lazy(value)
-
process_body_raw if @body_raw && value
-
case
-
when value == nil || value.length<=0
-
@body = Mail::Body.new('')
-
@body_raw = nil
-
add_encoding_to_body
-
when @body && @body.multipart?
-
@body << Mail::Part.new(value)
-
add_encoding_to_body
-
else
-
@body_raw = value
-
# process_body_raw
-
end
-
end
-
-
-
1
def process_body_raw
-
@body = Mail::Body.new(@body_raw)
-
@body_raw = nil
-
separate_parts if @separate_parts
-
-
add_encoding_to_body
-
end
-
-
1
def set_envelope_header
-
raw_string = raw_source.to_s
-
if match_data = raw_source.to_s.match(/\AFrom\s(#{TEXT}+)#{CRLF}/m)
-
set_envelope(match_data[1])
-
self.raw_source = raw_string.sub(match_data[0], "")
-
end
-
end
-
-
1
def separate_parts
-
body.split!(boundary)
-
end
-
-
1
def add_encoding_to_body
-
if has_content_transfer_encoding?
-
@body.encoding = content_transfer_encoding
-
end
-
end
-
-
1
def identify_and_set_transfer_encoding
-
if body && body.multipart?
-
self.content_transfer_encoding = @transport_encoding
-
else
-
self.content_transfer_encoding = body.get_best_encoding(@transport_encoding)
-
end
-
end
-
-
1
def add_required_fields
-
add_required_message_fields
-
add_multipart_mixed_header if body.multipart?
-
add_content_type unless has_content_type?
-
add_charset unless has_charset?
-
add_content_transfer_encoding unless has_content_transfer_encoding?
-
end
-
-
1
def add_required_message_fields
-
add_date unless has_date?
-
add_mime_version unless has_mime_version?
-
add_message_id unless has_message_id?
-
end
-
-
1
def add_multipart_alternate_header
-
header['content-type'] = ContentTypeField.with_boundary('multipart/alternative').value
-
header['content_type'].parameters[:charset] = @charset
-
body.boundary = boundary
-
end
-
-
1
def add_boundary
-
unless body.boundary && boundary
-
header['content-type'] = 'multipart/mixed' unless header['content-type']
-
header['content-type'].parameters[:boundary] = ContentTypeField.generate_boundary
-
header['content_type'].parameters[:charset] = @charset
-
body.boundary = boundary
-
end
-
end
-
-
1
def add_multipart_mixed_header
-
unless header['content-type']
-
header['content-type'] = ContentTypeField.with_boundary('multipart/mixed').value
-
header['content_type'].parameters[:charset] = @charset
-
body.boundary = boundary
-
end
-
end
-
-
1
def init_with_hash(hash)
-
passed_in_options = IndifferentHash.new(hash)
-
self.raw_source = ''
-
-
@header = Mail::Header.new
-
@body = Mail::Body.new
-
@body_raw = nil
-
-
# We need to store the body until last, as we need all headers added first
-
body_content = nil
-
-
passed_in_options.each_pair do |k,v|
-
k = underscoreize(k).to_sym if k.class == String
-
if k == :headers
-
self.headers(v)
-
elsif k == :body
-
body_content = v
-
else
-
self[k] = v
-
end
-
end
-
-
if body_content
-
self.body = body_content
-
if has_content_transfer_encoding?
-
body.encoding = content_transfer_encoding
-
end
-
end
-
end
-
-
1
def init_with_string(string)
-
self.raw_source = string
-
set_envelope_header
-
parse_message
-
@separate_parts = multipart?
-
end
-
-
# Returns the filename of the attachment (if it exists) or returns nil
-
1
def find_attachment
-
content_type_name = header[:content_type].filename rescue nil
-
content_disp_name = header[:content_disposition].filename rescue nil
-
content_loc_name = header[:content_location].location rescue nil
-
case
-
when content_type && content_type_name
-
filename = content_type_name
-
when content_disposition && content_disp_name
-
filename = content_disp_name
-
when content_location && content_loc_name
-
filename = content_loc_name
-
else
-
filename = nil
-
end
-
filename = Mail::Encodings.decode_encode(filename, :decode) if filename rescue filename
-
filename
-
end
-
-
1
def do_delivery
-
begin
-
if perform_deliveries
-
delivery_method.deliver!(self)
-
end
-
rescue => e # Net::SMTP errors or sendmail pipe errors
-
raise e if raise_delivery_errors
-
end
-
end
-
-
1
def decode_body_as_text
-
body_text = decode_body
-
if charset
-
if RUBY_VERSION < '1.9'
-
require 'iconv'
-
return Iconv.conv("UTF-8//TRANSLIT//IGNORE", charset, body_text)
-
else
-
if encoding = Encoding.find(charset) rescue nil
-
body_text.force_encoding(encoding)
-
return body_text.encode(Encoding::UTF_8, :undef => :replace, :invalid => :replace, :replace => '')
-
end
-
end
-
end
-
body_text
-
end
-
-
end
-
end
-
1
require 'mail/network/retriever_methods/base'
-
-
1
module Mail
-
1
register_autoload :SMTP, 'mail/network/delivery_methods/smtp'
-
1
register_autoload :FileDelivery, 'mail/network/delivery_methods/file_delivery'
-
1
register_autoload :Sendmail, 'mail/network/delivery_methods/sendmail'
-
1
register_autoload :Exim, 'mail/network/delivery_methods/exim'
-
1
register_autoload :SMTPConnection, 'mail/network/delivery_methods/smtp_connection'
-
1
register_autoload :TestMailer, 'mail/network/delivery_methods/test_mailer'
-
-
1
register_autoload :POP3, 'mail/network/retriever_methods/pop3'
-
1
register_autoload :IMAP, 'mail/network/retriever_methods/imap'
-
1
register_autoload :TestRetriever, 'mail/network/retriever_methods/test_retriever'
-
end
-
1
require 'mail/check_delivery_params'
-
-
1
module Mail
-
-
# FileDelivery class delivers emails into multiple files based on the destination
-
# address. Each file is appended to if it already exists.
-
#
-
# So if you have an email going to fred@test, bob@test, joe@anothertest, and you
-
# set your location path to /path/to/mails then FileDelivery will create the directory
-
# if it does not exist, and put one copy of the email in three files, called
-
# by their message id
-
#
-
# Make sure the path you specify with :location is writable by the Ruby process
-
# running Mail.
-
1
class FileDelivery
-
1
include Mail::CheckDeliveryParams
-
-
1
if RUBY_VERSION >= '1.9.1'
-
1
require 'fileutils'
-
else
-
require 'ftools'
-
end
-
-
1
def initialize(values)
-
self.settings = { :location => './mails' }.merge!(values)
-
end
-
-
1
attr_accessor :settings
-
-
1
def deliver!(mail)
-
check_delivery_params(mail)
-
-
if ::File.respond_to?(:makedirs)
-
::File.makedirs settings[:location]
-
else
-
::FileUtils.mkdir_p settings[:location]
-
end
-
-
mail.destinations.uniq.each do |to|
-
::File.open(::File.join(settings[:location], File.basename(to.to_s)), 'a') { |f| "#{f.write(mail.encoded)}\r\n\r\n" }
-
end
-
end
-
-
end
-
end
-
1
require 'mail/check_delivery_params'
-
-
1
module Mail
-
# A delivery method implementation which sends via sendmail.
-
#
-
# To use this, first find out where the sendmail binary is on your computer,
-
# if you are on a mac or unix box, it is usually in /usr/sbin/sendmail, this will
-
# be your sendmail location.
-
#
-
# Mail.defaults do
-
# delivery_method :sendmail
-
# end
-
#
-
# Or if your sendmail binary is not at '/usr/sbin/sendmail'
-
#
-
# Mail.defaults do
-
# delivery_method :sendmail, :location => '/absolute/path/to/your/sendmail'
-
# end
-
#
-
# Then just deliver the email as normal:
-
#
-
# Mail.deliver do
-
# to 'mikel@test.lindsaar.net'
-
# from 'ada@test.lindsaar.net'
-
# subject 'testing sendmail'
-
# body 'testing sendmail'
-
# end
-
#
-
# Or by calling deliver on a Mail message
-
#
-
# mail = Mail.new do
-
# to 'mikel@test.lindsaar.net'
-
# from 'ada@test.lindsaar.net'
-
# subject 'testing sendmail'
-
# body 'testing sendmail'
-
# end
-
#
-
# mail.deliver!
-
1
class Sendmail
-
1
include Mail::CheckDeliveryParams
-
-
1
def initialize(values)
-
self.settings = { :location => '/usr/sbin/sendmail',
-
:arguments => '-i' }.merge(values)
-
end
-
-
1
attr_accessor :settings
-
-
1
def deliver!(mail)
-
smtp_from, smtp_to, message = check_delivery_params(mail)
-
-
from = "-f #{self.class.shellquote(smtp_from)}"
-
to = smtp_to.map { |_to| self.class.shellquote(_to) }.join(' ')
-
-
arguments = "#{settings[:arguments]} #{from} --"
-
self.class.call(settings[:location], arguments, to, message)
-
end
-
-
1
def self.call(path, arguments, destinations, encoded_message)
-
popen "#{path} #{arguments} #{destinations}" do |io|
-
io.puts encoded_message.to_lf
-
io.flush
-
end
-
end
-
-
1
if RUBY_VERSION < '1.9.0'
-
def self.popen(command, &block)
-
IO.popen "#{command} 2>&1", 'w+', &block
-
end
-
else
-
1
def self.popen(command, &block)
-
IO.popen command, 'w+', :err => :out, &block
-
end
-
end
-
-
# The following is an adaptation of ruby 1.9.2's shellwords.rb file,
-
# it is modified to include '+' in the allowed list to allow for
-
# sendmail to accept email addresses as the sender with a + in them.
-
1
def self.shellquote(address)
-
# Process as a single byte sequence because not all shell
-
# implementations are multibyte aware.
-
#
-
# A LF cannot be escaped with a backslash because a backslash + LF
-
# combo is regarded as line continuation and simply ignored. Strip it.
-
escaped = address.gsub(/([^A-Za-z0-9_\s\+\-.,:\/@])/n, "\\\\\\1").gsub("\n", '')
-
%("#{escaped}")
-
end
-
end
-
end
-
1
require 'mail/check_delivery_params'
-
-
1
module Mail
-
# == Sending Email with SMTP
-
#
-
# Mail allows you to send emails using SMTP. This is done by wrapping Net::SMTP in
-
# an easy to use manner.
-
#
-
# === Sending via SMTP server on Localhost
-
#
-
# Sending locally (to a postfix or sendmail server running on localhost) requires
-
# no special setup. Just to Mail.deliver &block or message.deliver! and it will
-
# be sent in this method.
-
#
-
# === Sending via MobileMe
-
#
-
# Mail.defaults do
-
# delivery_method :smtp, { :address => "smtp.me.com",
-
# :port => 587,
-
# :domain => 'your.host.name',
-
# :user_name => '<username>',
-
# :password => '<password>',
-
# :authentication => 'plain',
-
# :enable_starttls_auto => true }
-
# end
-
#
-
# === Sending via GMail
-
#
-
# Mail.defaults do
-
# delivery_method :smtp, { :address => "smtp.gmail.com",
-
# :port => 587,
-
# :domain => 'your.host.name',
-
# :user_name => '<username>',
-
# :password => '<password>',
-
# :authentication => 'plain',
-
# :enable_starttls_auto => true }
-
# end
-
#
-
# === Certificate verification
-
#
-
# When using TLS, some mail servers provide certificates that are self-signed
-
# or whose names do not exactly match the hostname given in the address.
-
# OpenSSL will reject these by default. The best remedy is to use the correct
-
# hostname or update the certificate authorities trusted by your ruby. If
-
# that isn't possible, you can control this behavior with
-
# an :openssl_verify_mode setting. Its value may be either an OpenSSL
-
# verify mode constant (OpenSSL::SSL::VERIFY_NONE), or a string containing
-
# the name of an OpenSSL verify mode (none, peer, client_once,
-
# fail_if_no_peer_cert).
-
#
-
# === Others
-
#
-
# Feel free to send me other examples that were tricky
-
#
-
# === Delivering the email
-
#
-
# Once you have the settings right, sending the email is done by:
-
#
-
# Mail.deliver do
-
# to 'mikel@test.lindsaar.net'
-
# from 'ada@test.lindsaar.net'
-
# subject 'testing sendmail'
-
# body 'testing sendmail'
-
# end
-
#
-
# Or by calling deliver on a Mail message
-
#
-
# mail = Mail.new do
-
# to 'mikel@test.lindsaar.net'
-
# from 'ada@test.lindsaar.net'
-
# subject 'testing sendmail'
-
# body 'testing sendmail'
-
# end
-
#
-
# mail.deliver!
-
1
class SMTP
-
1
include Mail::CheckDeliveryParams
-
-
1
def initialize(values)
-
self.settings = { :address => "localhost",
-
:port => 25,
-
:domain => 'localhost.localdomain',
-
:user_name => nil,
-
:password => nil,
-
:authentication => nil,
-
:enable_starttls_auto => true,
-
:openssl_verify_mode => nil,
-
:ssl => nil,
-
:tls => nil
-
}.merge!(values)
-
end
-
-
1
attr_accessor :settings
-
-
# Send the message via SMTP.
-
# The from and to attributes are optional. If not set, they are retrieve from the Message.
-
1
def deliver!(mail)
-
smtp_from, smtp_to, message = check_delivery_params(mail)
-
-
smtp = Net::SMTP.new(settings[:address], settings[:port])
-
if settings[:tls] || settings[:ssl]
-
if smtp.respond_to?(:enable_tls)
-
smtp.enable_tls(ssl_context)
-
end
-
elsif settings[:enable_starttls_auto]
-
if smtp.respond_to?(:enable_starttls_auto)
-
smtp.enable_starttls_auto(ssl_context)
-
end
-
end
-
-
response = nil
-
smtp.start(settings[:domain], settings[:user_name], settings[:password], settings[:authentication]) do |smtp_obj|
-
response = smtp_obj.sendmail(message, smtp_from, smtp_to)
-
end
-
-
if settings[:return_response]
-
response
-
else
-
self
-
end
-
end
-
-
-
1
private
-
-
# Allow SSL context to be configured via settings, for Ruby >= 1.9
-
# Just returns openssl verify mode for Ruby 1.8.x
-
1
def ssl_context
-
openssl_verify_mode = settings[:openssl_verify_mode]
-
-
if openssl_verify_mode.kind_of?(String)
-
openssl_verify_mode = "OpenSSL::SSL::VERIFY_#{openssl_verify_mode.upcase}".constantize
-
end
-
-
context = Net::SMTP.default_ssl_context
-
context.verify_mode = openssl_verify_mode
-
context.ca_path = settings[:ca_path] if settings[:ca_path]
-
context.ca_file = settings[:ca_file] if settings[:ca_file]
-
context
-
end
-
end
-
end
-
1
require 'mail/check_delivery_params'
-
-
1
module Mail
-
# The TestMailer is a bare bones mailer that does nothing. It is useful
-
# when you are testing.
-
#
-
# It also provides a template of the minimum methods you require to implement
-
# if you want to make a custom mailer for Mail
-
1
class TestMailer
-
1
include Mail::CheckDeliveryParams
-
-
# Provides a store of all the emails sent with the TestMailer so you can check them.
-
1
def TestMailer.deliveries
-
@@deliveries ||= []
-
end
-
-
# Allows you to over write the default deliveries store from an array to some
-
# other object. If you just want to clear the store,
-
# call TestMailer.deliveries.clear.
-
#
-
# If you place another object here, please make sure it responds to:
-
#
-
# * << (message)
-
# * clear
-
# * length
-
# * size
-
# * and other common Array methods
-
1
def TestMailer.deliveries=(val)
-
@@deliveries = val
-
end
-
-
1
def initialize(values)
-
@settings = values.dup
-
end
-
-
1
attr_accessor :settings
-
-
1
def deliver!(mail)
-
check_delivery_params(mail)
-
Mail::TestMailer.deliveries << mail
-
end
-
-
end
-
end
-
# encoding: utf-8
-
-
1
module Mail
-
-
1
class Retriever
-
-
# Get the oldest received email(s)
-
#
-
# Possible options:
-
# count: number of emails to retrieve. The default value is 1.
-
# order: order of emails returned. Possible values are :asc or :desc. Default value is :asc.
-
#
-
1
def first(options = {}, &block)
-
options ||= {}
-
options[:what] = :first
-
options[:count] ||= 1
-
find(options, &block)
-
end
-
-
# Get the most recent received email(s)
-
#
-
# Possible options:
-
# count: number of emails to retrieve. The default value is 1.
-
# order: order of emails returned. Possible values are :asc or :desc. Default value is :asc.
-
#
-
1
def last(options = {}, &block)
-
options ||= {}
-
options[:what] = :last
-
options[:count] ||= 1
-
find(options, &block)
-
end
-
-
# Get all emails.
-
#
-
# Possible options:
-
# order: order of emails returned. Possible values are :asc or :desc. Default value is :asc.
-
#
-
1
def all(options = {}, &block)
-
options ||= {}
-
options[:count] = :all
-
find(options, &block)
-
end
-
-
# Find emails in the mailbox, and then deletes them. Without any options, the
-
# five last received emails are returned.
-
#
-
# Possible options:
-
# what: last or first emails. The default is :first.
-
# order: order of emails returned. Possible values are :asc or :desc. Default value is :asc.
-
# count: number of emails to retrieve. The default value is 10. A value of 1 returns an
-
# instance of Message, not an array of Message instances.
-
# delete_after_find: flag for whether to delete each retreived email after find. Default
-
# is true. Call #find if you would like this to default to false.
-
#
-
1
def find_and_delete(options = {}, &block)
-
options ||= {}
-
options[:delete_after_find] ||= true
-
find(options, &block)
-
end
-
-
end
-
-
end
-
1
module Mail
-
1
module Parsers
-
-
# Low-level ragel based parsers
-
1
require 'mail/parsers/ragel'
-
-
1
AddressListStruct = Struct.new(:addresses, :group_names, :error)
-
1
AddressStruct = Struct.new(:raw, :domain, :comments, :local,
-
:obs_domain_list, :display_name, :group, :error)
-
1
ContentDispositionStruct = Struct.new(:disposition_type, :parameters, :error)
-
1
ContentLocationStruct = Struct.new(:location, :error)
-
1
ContentTransferEncodingStruct = Struct.new(:encoding, :error)
-
1
ContentTypeStruct = Struct.new(:main_type, :sub_type, :parameters, :error)
-
1
DateTimeStruct = Struct.new(:date_string, :time_string, :error)
-
1
EnvelopeFromStruct = Struct.new(:address, :ctime_date, :error)
-
1
MessageIdsStruct = Struct.new(:message_ids, :error)
-
1
MimeVersionStruct = Struct.new(:major, :minor, :error)
-
1
PhraseListsStruct = Struct.new(:phrases, :error)
-
1
ReceivedStruct = Struct.new(:date, :time, :info, :error)
-
-
1
require 'mail/parsers/ragel/parser_info'
-
1
Ragel::FIELD_PARSERS.each do |field_parser|
-
11
require "mail/parsers/#{field_parser}_parser"
-
end
-
end
-
end
-
1
module Mail::Parsers
-
1
class AddressListsParser
-
1
include Mail::Utilities
-
-
1
def parse(s)
-
address_list = AddressListStruct.new([],[])
-
-
if s.blank?
-
return address_list
-
end
-
-
actions, error = Ragel.parse(:address_lists, s)
-
if error
-
raise Mail::Field::ParseError.new(Mail::AddressList, s, error)
-
end
-
-
-
phrase_s = phrase_e = qstr_s = qstr = comment_s = nil
-
group_name_s = domain_s = group_name = nil
-
local_dot_atom_s = local_dot_atom_e = nil
-
local_dot_atom_pre_comment_e = nil
-
obs_domain_list_s = nil
-
-
address_s = 0
-
address = AddressStruct.new(nil, nil, [], nil, nil, nil, nil)
-
actions.each_slice(2) do |action_id, p|
-
action = Mail::Parsers::Ragel::ACTIONS[action_id]
-
case action
-
-
# Phrase
-
when :phrase_s then phrase_s = p
-
when :phrase_e then phrase_e = p-1
-
-
# Quoted String.
-
when :qstr_s then qstr_s = p
-
when :qstr_e then qstr = s[qstr_s..(p-1)]
-
-
# Comment
-
when :comment_s
-
comment_s = p unless comment_s
-
when :comment_e
-
if address
-
address.comments << s[comment_s..(p-2)]
-
end
-
comment_s = nil
-
-
# Group Name
-
when :group_name_s then group_name_s = p
-
when :group_name_e
-
if qstr
-
group = qstr
-
qstr = nil
-
else
-
group = s[group_name_s..(p-1)]
-
group_name_s = nil
-
end
-
address_list.group_names << group
-
group_name = group
-
-
# Start next address
-
address = AddressStruct.new(nil, nil, [], nil, nil, nil, nil)
-
address_s = p
-
address.group = group_name
-
-
# Address
-
when :address_s
-
address_s = p
-
when :address_e
-
# Ignore address end events if they don't have
-
# a matching address start event.
-
next if address_s.nil?
-
if address.local.nil? && local_dot_atom_pre_comment_e && local_dot_atom_s && local_dot_atom_e
-
if address.domain
-
address.local = s[local_dot_atom_s..local_dot_atom_e] if address
-
else
-
address.local = s[local_dot_atom_s..local_dot_atom_pre_comment_e] if address
-
end
-
end
-
address.raw = s[address_s..(p-1)]
-
address_list.addresses << address if address
-
-
# Start next address
-
address = AddressStruct.new(nil, nil, [], nil, nil, nil, nil)
-
address.group = group_name
-
address_s = nil
-
-
# Don't set the display name until the
-
# address has actually started. This allows
-
# us to choose quoted_s version if it
-
# exists and always use the 'full' phrase
-
# version.
-
when :angle_addr_s
-
if qstr
-
address.display_name = qstr
-
qstr = nil
-
elsif phrase_e
-
address.display_name = s[phrase_s..phrase_e].strip
-
phrase_e = phrase_s = nil
-
end
-
-
# Domain
-
when :domain_s
-
domain_s = p
-
when :domain_e
-
address.domain = s[domain_s..(p-1)].rstrip if address
-
-
# Local
-
when :local_dot_atom_s then local_dot_atom_s = p
-
when :local_dot_atom_e then local_dot_atom_e = p-1
-
when :local_dot_atom_pre_comment_e
-
local_dot_atom_pre_comment_e = p-1
-
when :local_quoted_string_e
-
address.local = '"' + qstr + '"' if address
-
-
# obs_domain_list
-
when :obs_domain_list_s then obs_domain_list_s = p
-
when :obs_domain_list_e
-
address.obs_domain_list = s[obs_domain_list_s..(p-1)]
-
-
else
-
raise Mail::Field::ParseError.new(Mail::AddressList, s, "Failed to process unknown action: #{action}")
-
end
-
end
-
-
if address_list.addresses.empty? && address_list.group_names.empty?
-
raise Mail::Field::ParseError.new(Mail::AddressList, s, "Didn't find any addresses or groups")
-
end
-
-
address_list
-
end
-
end
-
end
-
1
module Mail::Parsers
-
1
class ContentDispositionParser
-
1
include Mail::Utilities
-
-
1
def parse(s)
-
content_disposition = ContentDispositionStruct.new("", nil)
-
if s.blank?
-
return content_disposition
-
end
-
-
actions, error = Ragel.parse(:content_disposition, s)
-
if error
-
raise Mail::Field::ParseError.new(Mail::ContentDispositionElement, s, error)
-
end
-
-
content_disposition.parameters = []
-
-
disp_type_s = param_attr_s = param_attr = qstr_s = qstr = param_val_s = nil
-
actions.each_slice(2) do |action_id, p|
-
action = Mail::Parsers::Ragel::ACTIONS[action_id]
-
case action
-
-
# Disposition Type
-
when :disp_type_s then disp_type_s = p
-
when :disp_type_e
-
content_disposition.disposition_type = s[disp_type_s..(p-1)].downcase
-
-
# Parameter Attribute
-
when :param_attr_s then param_attr_s = p
-
when :param_attr_e then param_attr = s[param_attr_s..(p-1)]
-
-
# Quoted String.
-
when :qstr_s then qstr_s = p
-
when :qstr_e then qstr = s[qstr_s..(p-1)]
-
-
# Parameter Value
-
when :param_val_s then param_val_s = p
-
when :param_val_e
-
if param_attr.nil?
-
raise Mail::Field::ParseError.new(Mail::ContentDispositionElement, s, "no attribute for value")
-
end
-
-
# Use quoted string value if one exists, otherwise use parameter value
-
if qstr
-
value = qstr
-
else
-
value = s[param_val_s..(p-1)]
-
end
-
-
content_disposition.parameters << { param_attr => value }
-
param_attr = nil
-
qstr = nil
-
-
else
-
raise Mail::Field::ParseError.new(Mail::ContentDispositionElement, s, "Failed to process unknown action: #{action}")
-
end
-
end
-
-
content_disposition
-
end
-
-
1
private
-
1
def cleaned(string)
-
string =~ /(.+);\s*$/ ? $1 : string
-
end
-
end
-
end
-
1
module Mail::Parsers
-
1
class ContentLocationParser
-
1
def parse(s)
-
content_location = ContentLocationStruct.new(nil)
-
if s.blank?
-
return content_location
-
end
-
-
actions, error = Ragel.parse(:content_location, s)
-
if error
-
raise Mail::Field::ParseError.new(Mail::ContentLocationElement, s, error)
-
end
-
-
qstr_s = token_string_s = nil
-
actions.each_slice(2) do |action_id, p|
-
action = Mail::Parsers::Ragel::ACTIONS[action_id]
-
case action
-
-
# Quoted String.
-
when :qstr_s then qstr_s = p
-
when :qstr_e then content_location.location = s[qstr_s..(p-1)]
-
-
# Token String
-
when :token_string_s then token_string_s = p
-
when :token_string_e
-
content_location.location = s[token_string_s..(p-1)]
-
-
else
-
raise Mail::Field::ParseError.new(Mail::ContentLocationElement, s, "Failed to process unknown action: #{action}")
-
end
-
end
-
content_location
-
end
-
end
-
end
-
1
module Mail::Parsers
-
1
class ContentTransferEncodingParser
-
-
1
def parse(s)
-
content_transfer_encoding = ContentTransferEncodingStruct.new("")
-
if s.blank?
-
return content_transfer_encoding
-
end
-
-
actions, error = Ragel.parse(:content_transfer_encoding, s)
-
if error
-
raise Mail::Field::ParseError.new(Mail::ContentTransferEncodingElement, s, error)
-
end
-
-
encoding_s = nil
-
actions.each_slice(2) do |action_id, p|
-
action = Mail::Parsers::Ragel::ACTIONS[action_id]
-
case action
-
-
# Encoding
-
when :encoding_s then encoding_s = p
-
when :encoding_e
-
content_transfer_encoding.encoding = s[encoding_s..(p-1)].downcase
-
-
else
-
raise Mail::Field::ParseError.new(Mail::ContentTransferEncodingElement, s, "Failed to process unknown action: #{action}")
-
end
-
end
-
-
content_transfer_encoding
-
end
-
end
-
end
-
1
module Mail::Parsers
-
1
class ContentTypeParser
-
1
include Mail::Utilities
-
-
1
def parse(s)
-
actions, error = Ragel.parse(:content_type, s)
-
if error
-
raise Mail::Field::ParseError.new(Mail::ContentTypeElement, s, error)
-
end
-
-
content_type = ContentTypeStruct.new(nil,nil,[])
-
-
content_type.parameters = []
-
-
main_type_s = sub_type_s = param_attr_s = param_attr = nil
-
qstr_s = qstr = param_val_s = nil
-
actions.each_slice(2) do |action_id, p|
-
action = Mail::Parsers::Ragel::ACTIONS[action_id]
-
case action
-
-
# Main Type
-
when :main_type_s then main_type_s = p
-
when :main_type_e then
-
content_type.main_type = s[main_type_s..(p-1)].downcase
-
-
# Sub Type
-
when :sub_type_s then sub_type_s = p
-
when :sub_type_e
-
content_type.sub_type = s[sub_type_s..(p-1)].downcase
-
-
# Parameter Attribute
-
when :param_attr_s then param_attr_s = p
-
when :param_attr_e then param_attr = s[param_attr_s..(p-1)]
-
-
# Quoted String.
-
when :qstr_s then qstr_s = p
-
when :qstr_e then qstr = s[qstr_s..(p-1)]
-
-
# Parameter Value
-
when :param_val_s then param_val_s = p
-
when :param_val_e
-
if param_attr.nil?
-
raise Mail::Field::ParseError.new(Mail::ContentTypeElement, s, "no attribute for value")
-
end
-
-
# Use quoted s value if one exists, otherwise use parameter value
-
if qstr
-
value = qstr
-
else
-
value = s[param_val_s..(p-1)]
-
end
-
-
content_type.parameters << { param_attr => value }
-
param_attr = nil
-
qstr = nil
-
-
else
-
raise Mail::Field::ParseError.new(Mail::ContentTypeElement, s, "Failed to process unknown action: #{action}")
-
end
-
end
-
content_type
-
end
-
end
-
end
-
1
module Mail::Parsers
-
1
class DateTimeParser
-
1
include Mail::Utilities
-
-
1
def parse(s)
-
date_time = DateTimeStruct.new([])
-
-
actions, error = Ragel.parse(:date_time, s)
-
if error
-
raise Mail::Field::ParseError.new(Mail::DateTimeElement, s, error)
-
end
-
-
date_s = time_s = nil
-
actions.each_slice(2) do |action_id, p|
-
action = Mail::Parsers::Ragel::ACTIONS[action_id]
-
case action
-
-
# Date
-
when :date_s then date_s = p
-
when :date_e
-
date_time.date_string = s[date_s..(p-1)]
-
-
# Time
-
when :time_s then time_s = p
-
when :time_e
-
date_time.time_string = s[time_s..(p-1)]
-
-
else
-
raise Mail::Field::ParseError.new(Mail::DateTimeElement, s, "Failed to process unknown action: #{action}")
-
end
-
end
-
-
date_time
-
end
-
end
-
end
-
1
module Mail::Parsers
-
1
class EnvelopeFromParser
-
1
def parse(s)
-
envelope_from = EnvelopeFromStruct.new
-
if s.blank?
-
return envelope_from
-
end
-
-
actions, error = Ragel.parse(:envelope_from, s)
-
if error
-
raise Mail::Field::ParseError.new(Mail::EnvelopeFromElement, s, error)
-
end
-
-
address_s = ctime_date_s = nil
-
envelope_from = EnvelopeFromStruct.new
-
actions.each_slice(2) do |action_id, p|
-
action = Mail::Parsers::Ragel::ACTIONS[action_id]
-
case action
-
-
# Address
-
when :address_s then address_s = p
-
when :address_e
-
envelope_from.address = s[address_s..(p-1)].rstrip
-
-
# ctime_date
-
when :ctime_date_s then ctime_date_s = p
-
when :ctime_date_e
-
envelope_from.ctime_date = s[ctime_date_s..(p-1)]
-
-
# ignored actions
-
when :angle_addr_s, :comment_e, :comment_s,
-
:domain_e, :domain_s, :local_dot_atom_e,
-
:local_dot_atom_pre_comment_e,
-
:local_dot_atom_pre_comment_s,
-
:local_dot_atom_s, :qstr_e, :qstr_s
-
nil
-
-
else
-
raise Mail::Field::ParseError.new(Mail::EnvelopeFromElement, s, "Failed to process unknown action: #{action}")
-
end
-
end
-
envelope_from
-
end
-
end
-
end
-
1
module Mail::Parsers
-
1
class MessageIdsParser
-
1
def parse(s)
-
if s.blank?
-
return MessageIdsStruct.new
-
end
-
-
message_ids = MessageIdsStruct.new([])
-
-
actions, error = Ragel.parse(:message_ids, s)
-
if error
-
raise Mail::Field::ParseError.new(Mail::MessageIdsElement, s, error)
-
end
-
-
msg_id_s = nil
-
actions.each_slice(2) do |action_id, p|
-
action = Mail::Parsers::Ragel::ACTIONS[action_id]
-
case action
-
-
# Message Ids
-
when :msg_id_s then msg_id_s = p
-
when :msg_id_e
-
message_ids.message_ids << s[msg_id_s..(p-1)].rstrip
-
-
when :domain_e, :domain_s, :local_dot_atom_e,
-
:local_dot_atom_pre_comment_e,
-
:local_dot_atom_pre_comment_s,
-
:local_dot_atom_s
-
-
# ignored actions
-
-
else
-
raise Mail::Field::ParseError.new(Mail::MessageIdsElement, s, "Failed to process unknown action: #{action}")
-
end
-
end
-
message_ids
-
end
-
end
-
end
-
1
module Mail::Parsers
-
1
class MimeVersionParser
-
1
include Mail::Utilities
-
-
1
def parse(s)
-
if s.blank?
-
return MimeVersionStruct.new("", nil)
-
end
-
-
mime_version = MimeVersionStruct.new
-
-
actions, error = Ragel.parse(:mime_version, s)
-
if error
-
raise Mail::Field::ParseError.new(Mail::MimeVersionElement, s, error)
-
end
-
-
major_digits_s = minor_digits_s = nil
-
actions.each_slice(2) do |action_id, p|
-
action = Mail::Parsers::Ragel::ACTIONS[action_id]
-
case action
-
-
# Major Digits
-
when :major_digits_s then major_digits_s = p
-
when :major_digits_e
-
mime_version.major = s[major_digits_s..(p-1)]
-
-
# Minor Digits
-
when :minor_digits_s then minor_digits_s = p
-
when :minor_digits_e
-
mime_version.minor = s[minor_digits_s..(p-1)]
-
-
when :comment_e, :comment_s then nil
-
-
else
-
raise Mail::Field::ParseError.new(Mail::MimeVersionElement, s, "Failed to process unknown action: #{action}")
-
end
-
end
-
mime_version
-
end
-
end
-
end
-
1
module Mail::Parsers
-
1
class PhraseListsParser
-
-
1
def parse(s)
-
actions, error = Ragel.parse(:phrase_lists, s)
-
if error
-
raise Mail::Field::ParseError.new(Mail::PhraseList, s, error)
-
end
-
-
phrase_lists = PhraseListsStruct.new([])
-
-
phrase_s = nil
-
actions.each_slice(2) do |action_id, p|
-
action = Mail::Parsers::Ragel::ACTIONS[action_id]
-
case action
-
-
# Phrase
-
when :phrase_s then phrase_s = p
-
when :phrase_e
-
phrase_lists.phrases << s[phrase_s..(p-1)] if phrase_s
-
phrase_s = nil
-
-
when :comment_e, :comment_s, :qstr_s, :qstr_e then nil
-
-
else
-
raise Mail::Field::ParseError.new(Mail::PhraseList, s, "Failed to process unknown action: #{action}")
-
end
-
end
-
-
phrase_lists
-
end
-
end
-
end
-
1
module Mail
-
1
module Parsers
-
1
module Ragel
-
1
require 'mail/parsers/ragel/parser_info'
-
1
require "mail/parsers/ragel/ruby"
-
-
1
def self.parse(machine, string)
-
@machine_module ||= Ruby
-
@machine_module.parse(machine, string)
-
end
-
-
1
def self.machine_module=(m)
-
@machine_module = m
-
end
-
end
-
end
-
end
-
1
module Mail
-
1
module Parsers
-
1
module Ragel
-
1
ACTIONS = [
-
:addr_spec,
-
:address_e,
-
:address_s,
-
:angle_addr_s,
-
:comment_e,
-
:comment_s,
-
:ctime_date_e,
-
:ctime_date_s,
-
:date_e,
-
:date_s,
-
:disp_type_e,
-
:disp_type_s,
-
:domain_e,
-
:domain_s,
-
:encoding_e,
-
:encoding_s,
-
:group_name_e,
-
:group_name_s,
-
:local_dot_atom_e,
-
:local_dot_atom_pre_comment_e,
-
:local_dot_atom_s,
-
:local_quoted_string_e,
-
:main_type_e,
-
:main_type_s,
-
:major_digits_e,
-
:major_digits_s,
-
:minor_digits_e,
-
:minor_digits_s,
-
:msg_id_e,
-
:msg_id_s,
-
:obs_domain_list_e,
-
:obs_domain_list_s,
-
:param_attr_e,
-
:param_attr_s,
-
:param_val_e,
-
:param_val_s,
-
:phrase_e,
-
:phrase_s,
-
:qstr_e,
-
:qstr_s,
-
:received_tokens_e,
-
:received_tokens_s,
-
:sub_type_e,
-
:sub_type_s,
-
:time_e,
-
:time_s,
-
:token_string_e,
-
:token_string_s
-
]
-
-
1
FIELD_PARSERS = %w[ address_lists phrase_lists
-
date_time received message_ids envelope_from
-
mime_version content_type content_disposition
-
content_transfer_encoding content_location ]
-
end
-
end
-
end
-
1
module Mail
-
1
module Parsers
-
1
module Ragel
-
1
module Ruby
-
1
def self.silence_warnings
-
1
original_verbose = $VERBOSE
-
1
$VERBOSE = nil
-
1
yield
-
1
$VERBOSE = original_verbose
-
end
-
# Ragel-generated parsers give a lot of warnings
-
# and may cause logs to balloon in size
-
1
silence_warnings do
-
1
Mail::Parsers::Ragel::FIELD_PARSERS.each do |field_parser|
-
11
require "mail/parsers/ragel/ruby/machines/#{field_parser}_machine"
-
end
-
end
-
-
1
MACHINE_LIST = {
-
:address_lists => AddressListsMachine,
-
:phrase_lists => PhraseListsMachine,
-
:date_time => DateTimeMachine,
-
:received => ReceivedMachine,
-
:message_ids => MessageIdsMachine,
-
:envelope_from => EnvelopeFromMachine,
-
:mime_version => MimeVersionMachine,
-
:content_type => ContentTypeMachine,
-
:content_disposition => ContentDispositionMachine,
-
:content_transfer_encoding => ContentTransferEncodingMachine,
-
:content_location => ContentLocationMachine
-
}
-
-
1
def self.parse(machine, string)
-
MACHINE_LIST[machine].parse(string)
-
end
-
end
-
end
-
end
-
end
-
-
# line 1 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb.rl"
-
-
# line 10 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb.rl"
-
-
-
1
module Mail
-
1
module Parsers
-
1
module Ragel
-
1
module AddressListsMachine
-
-
# line 13 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb"
-
1
class << self
-
1
attr_accessor :_address_lists_trans_keys
-
1
private :_address_lists_trans_keys, :_address_lists_trans_keys=
-
end
-
1
self._address_lists_trans_keys = [
-
0, 0, 9, 126, 10, 10,
-
9, 32, 10, 10, 9,
-
32, 9, 126, 10, 10,
-
9, 32, 1, 127, 1, 127,
-
10, 10, 9, 32, -128,
-
-1, 9, 126, 9, 126,
-
9, 126, 10, 10, 9, 32,
-
9, 126, 9, 126, 9,
-
126, 10, 10, 9, 32,
-
9, 126, 9, 64, 10, 10,
-
9, 32, 9, 64, 9,
-
126, 10, 10, 9, 32,
-
9, 126, 9, 64, 10, 10,
-
9, 32, 9, 64, 10,
-
10, 9, 32, 10, 10,
-
9, 32, 10, 10, 9, 32,
-
9, 126, 9, 126, 10,
-
10, 9, 32, 10, 10,
-
9, 32, 9, 126, 9, 126,
-
10, 10, 9, 32, 9,
-
126, 9, 126, 10, 10,
-
9, 32, 9, 126, 10, 10,
-
9, 32, 1, 127, 1,
-
127, 10, 10, 9, 32,
-
0, 127, 9, 126, 9, 126,
-
9, 126, 10, 10, 9,
-
32, 9, 126, 9, 126,
-
9, 126, 10, 10, 9, 32,
-
9, 126, 9, 64, 10,
-
10, 9, 32, 9, 64,
-
9, 126, 10, 10, 9, 32,
-
9, 126, 9, 64, 10,
-
10, 9, 32, 9, 64,
-
9, 59, 10, 10, 9, 32,
-
9, 59, 9, 126, 9,
-
126, 10, 10, 9, 32,
-
1, 127, 1, 127, 10, 10,
-
9, 32, 9, 126, 9,
-
126, 10, 10, 9, 32,
-
9, 126, 9, 126, 9, 126,
-
10, 10, 9, 32, 9,
-
126, 9, 126, 10, 10,
-
9, 32, 9, 126, 10, 10,
-
9, 32, 9, 126, 9,
-
126, 10, 10, 9, 32,
-
9, 126, 10, 10, 9, 32,
-
1, 127, 1, 127, 10,
-
10, 9, 32, -128, -1,
-
9, 126, 9, 126, 9, 126,
-
10, 10, 9, 32, 9,
-
126, 9, 126, 9, 126,
-
10, 10, 9, 32, 9, 126,
-
9, 64, 10, 10, 9,
-
32, 9, 64, 9, 126,
-
10, 10, 9, 32, 9, 126,
-
9, 64, 10, 10, 9,
-
32, 9, 64, 9, 59,
-
10, 10, 9, 32, 9, 59,
-
9, 126, 9, 126, 10,
-
10, 9, 32, 1, 127,
-
1, 127, 10, 10, 9, 32,
-
9, 126, 9, 126, 10,
-
10, 9, 32, 9, 126,
-
9, 126, 9, 126, 10, 10,
-
9, 32, 9, 126, 9,
-
126, 10, 10, 9, 32,
-
9, 126, 9, 126, 9, 126,
-
10, 10, 9, 32, 9,
-
126, 9, 126, 10, 10,
-
9, 32, 9, 126, 9, 126,
-
9, 126, 10, 10, 9,
-
32, 9, 126, 9, 126,
-
1, 127, 1, 127, 10, 10,
-
9, 32, 9, 126, 9,
-
126, 10, 10, 9, 32,
-
9, 126, 9, 126, 0, 127,
-
1, 127, 10, 10, 9,
-
32, 9, 126, 9, 126,
-
33, 126, 9, 126, 9, 59,
-
10, 10, 9, 32, 9,
-
59, 1, 127, 1, 127,
-
10, 10, 9, 32, 0, 127,
-
10, 10, 9, 32, 1,
-
127, 10, 10, 9, 32,
-
-128, -1, 1, 127, 1, 127,
-
10, 10, 9, 32, 0,
-
127, 9, 126, 9, 126,
-
9, 126, 10, 10, 9, 32,
-
9, 126, 1, 127, 1,
-
127, 10, 10, 9, 32,
-
-128, -1, 1, 127, 10, 10,
-
9, 32, 9, 126, 9,
-
126, 10, 10, 9, 32,
-
9, 126, 9, 126, 9, 126,
-
10, 10, 9, 32, 9,
-
126, 9, 59, 10, 10,
-
9, 32, 9, 59, 9, 126,
-
10, 10, 9, 32, 9,
-
126, 9, 126, 9, 126,
-
9, 126, 9, 126, 9, 126,
-
10, 10, 9, 32, 9,
-
126, 1, 127, 1, 127,
-
10, 10, 9, 32, 9, 126,
-
9, 126, 0, 127, 1,
-
127, 10, 10, 9, 32,
-
-128, -1, 1, 127, 10, 10,
-
9, 32, 9, 126, 10,
-
10, 9, 32, 9, 126,
-
9, 126, 33, 126, 9, 126,
-
9, 64, 10, 10, 9,
-
32, 9, 64, 1, 127,
-
1, 127, 10, 10, 9, 32,
-
-128, -1, 10, 10, 9,
-
32, 9, 126, 9, 126,
-
9, 126, 10, 10, 9, 32,
-
9, 126, 9, 62, 10,
-
10, 9, 32, 9, 62,
-
9, 126, 10, 10, 9, 32,
-
9, 126, 9, 126, 1,
-
127, 1, 127, 10, 10,
-
9, 32, 9, 126, 9, 62,
-
10, 10, 9, 32, 9,
-
62, 33, 126, 0, 127,
-
10, 10, 9, 32, 9, 126,
-
9, 126, 1, 127, 10,
-
10, 9, 32, -128, -1,
-
1, 127, 1, 127, 10, 10,
-
9, 32, 0, 127, 9,
-
126, 1, 127, 1, 127,
-
10, 10, 9, 32, 9, 126,
-
9, 64, 10, 10, 9,
-
32, 9, 64, 0, 127,
-
10, 10, 9, 32, 9, 126,
-
9, 126, 10, 10, 9,
-
32, 9, 126, 1, 127,
-
1, 127, 10, 10, 9, 32,
-
9, 126, -128, -1, 1,
-
127, 10, 10, 9, 32,
-
1, 127, 1, 127, 10, 10,
-
9, 32, 9, 126, 9,
-
64, 10, 10, 9, 32,
-
9, 64, 0, 127, 1, 127,
-
10, 10, 9, 32, 9,
-
64, 10, 10, 9, 32,
-
9, 64, 9, 126, 9, 64,
-
10, 10, 9, 32, 9,
-
64, 9, 126, 9, 126,
-
10, 10, 9, 32, 9, 126,
-
9, 58, 10, 10, 9,
-
32, 9, 58, 9, 64,
-
10, 10, 9, 32, 9, 64,
-
9, 126, 9, 126, 10,
-
10, 9, 32, 9, 126,
-
33, 126, 9, 126, 10, 10,
-
9, 32, 9, 126, 9,
-
126, 1, 127, 1, 127,
-
10, 10, 9, 32, 9, 126,
-
9, 58, 10, 10, 9,
-
32, 9, 58, 33, 126,
-
-128, -1, 10, 10, 9, 32,
-
9, 126, 9, 126, 1,
-
127, 10, 10, 9, 32,
-
0, 127, 9, 126, 9, 126,
-
10, 10, 9, 32, 9,
-
126, 9, 126, 10, 10,
-
9, 32, 9, 126, 9, 126,
-
9, 126, 10, 10, 9,
-
32, 9, 126, 9, 126,
-
1, 127, 1, 127, 10, 10,
-
9, 32, 9, 126, 9,
-
126, 10, 10, 9, 32,
-
9, 126, 9, 126, -128, -1,
-
1, 127, 10, 10, 9,
-
32, 9, 126, 9, 126,
-
33, 126, 9, 126, 9, 59,
-
10, 10, 9, 32, 9,
-
59, 1, 127, 1, 127,
-
10, 10, 9, 32, -128, -1,
-
10, 10, 9, 32, 1,
-
127, 10, 10, 9, 32,
-
-128, -1, 1, 127, 1, 127,
-
10, 10, 9, 32, 0,
-
127, 9, 126, 9, 126,
-
9, 126, 10, 10, 9, 32,
-
9, 126, 1, 127, 1,
-
127, 10, 10, 9, 32,
-
-128, -1, 1, 127, 10, 10,
-
9, 32, 9, 126, 9,
-
126, 10, 10, 9, 32,
-
9, 126, 9, 126, 9, 126,
-
10, 10, 9, 32, 9,
-
126, 9, 59, 10, 10,
-
9, 32, 9, 59, 9, 126,
-
10, 10, 9, 32, 9,
-
126, 9, 126, 9, 126,
-
9, 126, 9, 126, 9, 126,
-
10, 10, 9, 32, 9,
-
126, 1, 127, 1, 127,
-
10, 10, 9, 32, 9, 126,
-
9, 126, 0, 127, 1,
-
127, 10, 10, 9, 32,
-
0, 127, 1, 127, 10, 10,
-
9, 32, 9, 126, 10,
-
10, 9, 32, 9, 126,
-
9, 126, 9, 126, 9, 126,
-
10, 10, 9, 32, 9,
-
126, 9, 126, 10, 10,
-
9, 32, 9, 126, 10, 10,
-
9, 32, 1, 127, 1,
-
127, 10, 10, 9, 32,
-
-128, -1, 9, 126, 9, 126,
-
9, 126, 10, 10, 9,
-
32, 9, 126, 9, 126,
-
9, 126, 10, 10, 9, 32,
-
9, 126, 9, 64, 10,
-
10, 9, 32, 9, 64,
-
9, 126, 10, 10, 9, 32,
-
9, 126, 9, 64, 10,
-
10, 9, 32, 9, 64,
-
9, 59, 10, 10, 9, 32,
-
9, 59, 10, 10, 9,
-
32, 9, 126, 9, 126,
-
10, 10, 9, 32, 9, 126,
-
9, 62, 10, 10, 9,
-
32, 9, 62, 9, 126,
-
10, 10, 9, 32, 9, 126,
-
9, 126, 1, 127, 1,
-
127, 10, 10, 9, 32,
-
9, 126, 9, 62, 10, 10,
-
9, 32, 9, 62, 33,
-
126, -128, -1, 10, 10,
-
9, 32, 9, 126, 9, 126,
-
1, 127, 10, 10, 9,
-
32, -128, -1, 1, 127,
-
1, 127, 10, 10, 9, 32,
-
-128, -1, 9, 126, 1,
-
127, 1, 127, 10, 10,
-
9, 32, 9, 126, 9, 64,
-
10, 10, 9, 32, 9,
-
64, 0, 127, 10, 10,
-
9, 32, 9, 126, 9, 126,
-
10, 10, 9, 32, 9,
-
126, 1, 127, 1, 127,
-
10, 10, 9, 32, 9, 126,
-
0, 127, 1, 127, 10,
-
10, 9, 32, 1, 127,
-
1, 127, 10, 10, 9, 32,
-
9, 126, 9, 64, 10,
-
10, 9, 32, 9, 64,
-
-128, -1, 1, 127, 10, 10,
-
9, 32, 9, 64, 10,
-
10, 9, 32, 9, 64,
-
9, 126, 9, 64, 10, 10,
-
9, 32, 9, 64, 9,
-
126, 9, 126, 10, 10,
-
9, 32, 9, 126, 9, 58,
-
10, 10, 9, 32, 9,
-
58, 9, 64, 10, 10,
-
9, 32, 9, 64, 9, 126,
-
9, 126, 10, 10, 9,
-
32, 9, 126, 33, 126,
-
9, 126, 10, 10, 9, 32,
-
9, 126, 9, 126, 1,
-
127, 1, 127, 10, 10,
-
9, 32, 9, 126, 9, 58,
-
10, 10, 9, 32, 9,
-
58, 33, 126, 0, 127,
-
10, 10, 9, 32, 9, 126,
-
9, 126, 1, 127, 10,
-
10, 9, 32, 0, 127,
-
9, 126, 9, 126, 9, 126,
-
10, 10, 9, 32, 9,
-
126, 9, 126, 10, 10,
-
9, 32, 9, 126, 9, 126,
-
9, 126, 10, 10, 9,
-
32, 9, 126, 9, 126,
-
10, 10, 9, 32, 9, 126,
-
9, 126, 9, 126, 10,
-
10, 9, 32, 9, 126,
-
9, 126, 1, 127, 1, 127,
-
10, 10, 9, 32, 9,
-
126, 9, 126, 10, 10,
-
9, 32, 9, 126, 9, 126,
-
-128, -1, 1, 127, 10,
-
10, 9, 32, 9, 126,
-
9, 126, 33, 126, 9, 126,
-
9, 59, 10, 10, 9,
-
32, 9, 59, 1, 127,
-
1, 127, 10, 10, 9, 32,
-
0, 127, 10, 10, 9,
-
32, 1, 127, 10, 10,
-
9, 32, -128, -1, 1, 127,
-
1, 127, 10, 10, 9,
-
32, -128, -1, 9, 126,
-
1, 127, 1, 127, 10, 10,
-
9, 32, 9, 126, 9,
-
126, 10, 10, 9, 32,
-
9, 126, 9, 126, 9, 126,
-
10, 10, 9, 32, 9,
-
126, 9, 126, 9, 126,
-
10, 10, 9, 32, 9, 126,
-
9, 59, 10, 10, 9,
-
32, 9, 59, 9, 126,
-
10, 10, 9, 32, 9, 126,
-
9, 126, 9, 126, 9,
-
126, -128, -1, 1, 127,
-
10, 10, 9, 32, 9, 126,
-
9, 126, 10, 10, 9,
-
32, 9, 126, 1, 127,
-
1, 127, 10, 10, 9, 32,
-
9, 126, -128, -1, 1,
-
127, 10, 10, 9, 32,
-
1, 127, 1, 127, 10, 10,
-
9, 32, 9, 126, 9,
-
126, 10, 10, 9, 32,
-
9, 126, -128, -1, 1, 127,
-
10, 10, 9, 32, 9,
-
126, 10, 10, 9, 32,
-
9, 126, 9, 126, 33, 126,
-
9, 126, 9, 64, 10,
-
10, 9, 32, 9, 64,
-
1, 127, 1, 127, 10, 10,
-
9, 32, -128, -1, 10,
-
10, 9, 32, 9, 126,
-
33, 126, 9, 126, 9, 64,
-
10, 10, 9, 32, 9,
-
64, 1, 127, 1, 127,
-
10, 10, 9, 32, 0, 127,
-
10, 10, 9, 32, 9,
-
126, 9, 126, 9, 126,
-
10, 10, 9, 32, 9, 126,
-
9, 62, 10, 10, 9,
-
32, 9, 62, 9, 126,
-
10, 10, 9, 32, 9, 126,
-
9, 126, 1, 127, 1,
-
127, 10, 10, 9, 32,
-
9, 126, 9, 62, 10, 10,
-
9, 32, 9, 62, 33,
-
126, -128, -1, 10, 10,
-
9, 32, 9, 126, 9, 126,
-
1, 127, 10, 10, 9,
-
32, -128, -1, 1, 127,
-
1, 127, 10, 10, 9, 32,
-
-128, -1, 9, 126, 1,
-
127, 1, 127, 10, 10,
-
9, 32, 9, 126, 9, 64,
-
10, 10, 9, 32, 9,
-
64, -128, -1, 10, 10,
-
9, 32, 9, 126, 9, 126,
-
10, 10, 9, 32, 9,
-
126, 1, 127, 1, 127,
-
10, 10, 9, 32, 9, 126,
-
-128, -1, 1, 127, 10,
-
10, 9, 32, 1, 127,
-
1, 127, 10, 10, 9, 32,
-
9, 126, 9, 64, 10,
-
10, 9, 32, 9, 64,
-
0, 127, 1, 127, 10, 10,
-
9, 32, 9, 64, 10,
-
10, 9, 32, 9, 64,
-
9, 126, 9, 64, 10, 10,
-
9, 32, 9, 64, 9,
-
126, 9, 126, 10, 10,
-
9, 32, 9, 126, 9, 58,
-
10, 10, 9, 32, 9,
-
58, 9, 64, 10, 10,
-
9, 32, 9, 64, 9, 126,
-
9, 126, 10, 10, 9,
-
32, 9, 126, 33, 126,
-
9, 126, 10, 10, 9, 32,
-
9, 126, 9, 126, 1,
-
127, 1, 127, 10, 10,
-
9, 32, 9, 126, 9, 58,
-
10, 10, 9, 32, 9,
-
58, 33, 126, -128, -1,
-
10, 10, 9, 32, 9, 126,
-
9, 126, 1, 127, 10,
-
10, 9, 32, 0, 127,
-
9, 126, 9, 126, 9, 126,
-
10, 10, 9, 32, 9,
-
126, 9, 126, 9, 126,
-
10, 10, 9, 32, 10, 10,
-
9, 32, 9, 126, 9,
-
126, 10, 10, 9, 32,
-
9, 126, 1, 127, 1, 127,
-
10, 10, 9, 32, 10,
-
10, 9, 32, 9, 126,
-
-128, -1, 1, 127, 10, 10,
-
9, 32, 9, 126, 9,
-
126, 33, 126, 10, 10,
-
9, 32, 1, 127, 1, 127,
-
10, 10, 9, 32, -128,
-
-1, 10, 10, 9, 32,
-
1, 127, 10, 10, 9, 32,
-
0, 127, 1, 127, 1,
-
127, 10, 10, 9, 32,
-
0, 127, 9, 126, 1, 127,
-
1, 127, 10, 10, 9,
-
32, 10, 10, 9, 32,
-
10, 10, 9, 32, 9, 126,
-
9, 126, 10, 10, 9,
-
32, 10, 10, 9, 32,
-
9, 126, 10, 10, 9, 32,
-
9, 126, 9, 126, 9,
-
126, -128, -1, 1, 127,
-
10, 10, 9, 32, 10, 10,
-
9, 32, 1, 127, 1,
-
127, 10, 10, 9, 32,
-
-128, -1, 1, 127, 10, 10,
-
9, 32, 1, 127, 1,
-
127, 10, 10, 9, 32,
-
10, 10, 9, 32, -128, -1,
-
1, 127, 10, 10, 9,
-
32, 10, 10, 9, 32,
-
9, 126, 9, 126, 10, 10,
-
9, 32, 33, 126, 10,
-
10, 9, 32, 1, 127,
-
1, 127, 10, 10, 9, 32,
-
-128, -1, 10, 10, 9,
-
32, 9, 126, 9, 126,
-
9, 126, 10, 10, 9, 32,
-
9, 126, 9, 62, 10,
-
10, 9, 32, 9, 62,
-
9, 126, 10, 10, 9, 32,
-
9, 126, 9, 126, 1,
-
127, 1, 127, 10, 10,
-
9, 32, 9, 126, 9, 62,
-
10, 10, 9, 32, 9,
-
62, 33, 126, 0, 127,
-
10, 10, 9, 32, 9, 126,
-
9, 126, 1, 127, 10,
-
10, 9, 32, -128, -1,
-
1, 127, 1, 127, 10, 10,
-
9, 32, -128, -1, 9,
-
126, 1, 127, 1, 127,
-
10, 10, 9, 32, 9, 126,
-
9, 64, 10, 10, 9,
-
32, 9, 64, -128, -1,
-
10, 10, 9, 32, 9, 126,
-
9, 126, 10, 10, 9,
-
32, 9, 126, 1, 127,
-
1, 127, 10, 10, 9, 32,
-
9, 126, 0, 127, 1,
-
127, 10, 10, 9, 32,
-
1, 127, 1, 127, 10, 10,
-
9, 32, 9, 126, 9,
-
64, 10, 10, 9, 32,
-
9, 64, 0, 127, 1, 127,
-
10, 10, 9, 32, 9,
-
64, 10, 10, 9, 32,
-
9, 64, 9, 126, 9, 64,
-
10, 10, 9, 32, 9,
-
64, 9, 126, 9, 126,
-
10, 10, 9, 32, 9, 126,
-
9, 58, 10, 10, 9,
-
32, 9, 58, 9, 64,
-
10, 10, 9, 32, 9, 64,
-
9, 126, 9, 126, 10,
-
10, 9, 32, 9, 126,
-
33, 126, 9, 126, 10, 10,
-
9, 32, 9, 126, 9,
-
126, 1, 127, 1, 127,
-
10, 10, 9, 32, 9, 126,
-
9, 58, 10, 10, 9,
-
32, 9, 58, 33, 126,
-
0, 127, 10, 10, 9, 32,
-
9, 126, 9, 126, 1,
-
127, 10, 10, 9, 32,
-
0, 127, 9, 126, 9, 126,
-
10, 10, 9, 32, 10,
-
10, 9, 32, 9, 126,
-
9, 126, 10, 10, 9, 32,
-
9, 126, 33, 126, 1,
-
127, 1, 127, 10, 10,
-
9, 32, 0, 127, 10, 10,
-
9, 32, 1, 127, 1,
-
127, 10, 10, 9, 32,
-
-128, -1, 9, 126, 1, 127,
-
1, 127, 10, 10, 9,
-
32, 10, 10, 9, 32,
-
10, 10, 9, 32, 9, 126,
-
9, 126, 10, 10, 9,
-
32, 9, 126, 9, 126,
-
-128, -1, 1, 127, 10, 10,
-
9, 32, 10, 10, 9,
-
32, 1, 127, 1, 127,
-
10, 10, 9, 32, -128, -1,
-
1, 127, 10, 10, 9,
-
32, 1, 127, 1, 127,
-
10, 10, 9, 32, 10, 10,
-
9, 32, -128, -1, 1,
-
127, 10, 10, 9, 32,
-
10, 10, 9, 32, 9, 126,
-
9, 126, 10, 10, 9,
-
32, 33, 126, 10, 10,
-
9, 32, 1, 127, 1, 127,
-
10, 10, 9, 32, -128,
-
-1, 10, 10, 9, 32,
-
9, 126, 1, 127, 1, 127,
-
10, 10, 9, 32, -128,
-
-1, 9, 126, 9, 126,
-
9, 126, 9, 59, 9, 59,
-
9, 126, 9, 126, 9,
-
126, 9, 126, 9, 126,
-
9, 126, 9, 126, 9, 126,
-
9, 126, 9, 126, 9,
-
59, 9, 59, 9, 126,
-
9, 126, 9, 126, 9, 126,
-
9, 126, 9, 126, 9,
-
126, 9, 126, 9, 59,
-
9, 59, 9, 126, 9, 126,
-
9, 126, 9, 126, 9,
-
126, 9, 126, 9, 126,
-
9, 59, 9, 59, 9, 126,
-
9, 126, 9, 126, 9,
-
126, 9, 126, 9, 126,
-
9, 126, 9, 126, 9, 126,
-
9, 126, 9, 126, 9,
-
64, 9, 64, 9, 126,
-
9, 126, 9, 126, 9, 126,
-
9, 126, 9, 126, 9,
-
126, 9, 126, 9, 126,
-
9, 126, 9, 126, 9, 126,
-
9, 126, 9, 126, 9,
-
126, 9, 126, 9, 126,
-
9, 126, 9, 126, 9, 126,
-
9, 126, 9, 126, 9,
-
64, 9, 64, 0, 0,
-
0
-
]
-
-
1
class << self
-
1
attr_accessor :_address_lists_key_spans
-
1
private :_address_lists_key_spans, :_address_lists_key_spans=
-
end
-
1
self._address_lists_key_spans = [
-
0, 118, 1, 24, 1, 24, 118, 1,
-
24, 127, 127, 1, 24, 128, 118, 118,
-
118, 1, 24, 118, 118, 118, 1, 24,
-
118, 56, 1, 24, 56, 118, 1, 24,
-
118, 56, 1, 24, 56, 1, 24, 1,
-
24, 1, 24, 118, 118, 1, 24, 1,
-
24, 118, 118, 1, 24, 118, 118, 1,
-
24, 118, 1, 24, 127, 127, 1, 24,
-
128, 118, 118, 118, 1, 24, 118, 118,
-
118, 1, 24, 118, 56, 1, 24, 56,
-
118, 1, 24, 118, 56, 1, 24, 56,
-
51, 1, 24, 51, 118, 118, 1, 24,
-
127, 127, 1, 24, 118, 118, 1, 24,
-
118, 118, 118, 1, 24, 118, 118, 1,
-
24, 118, 1, 24, 118, 118, 1, 24,
-
118, 1, 24, 127, 127, 1, 24, 128,
-
118, 118, 118, 1, 24, 118, 118, 118,
-
1, 24, 118, 56, 1, 24, 56, 118,
-
1, 24, 118, 56, 1, 24, 56, 51,
-
1, 24, 51, 118, 118, 1, 24, 127,
-
127, 1, 24, 118, 118, 1, 24, 118,
-
118, 118, 1, 24, 118, 118, 1, 24,
-
118, 118, 118, 1, 24, 118, 118, 1,
-
24, 118, 118, 118, 1, 24, 118, 118,
-
127, 127, 1, 24, 118, 118, 1, 24,
-
118, 118, 128, 127, 1, 24, 118, 118,
-
94, 118, 51, 1, 24, 51, 127, 127,
-
1, 24, 128, 1, 24, 127, 1, 24,
-
128, 127, 127, 1, 24, 128, 118, 118,
-
118, 1, 24, 118, 127, 127, 1, 24,
-
128, 127, 1, 24, 118, 118, 1, 24,
-
118, 118, 118, 1, 24, 118, 51, 1,
-
24, 51, 118, 1, 24, 118, 118, 118,
-
118, 118, 118, 1, 24, 118, 127, 127,
-
1, 24, 118, 118, 128, 127, 1, 24,
-
128, 127, 1, 24, 118, 1, 24, 118,
-
118, 94, 118, 56, 1, 24, 56, 127,
-
127, 1, 24, 128, 1, 24, 118, 118,
-
118, 1, 24, 118, 54, 1, 24, 54,
-
118, 1, 24, 118, 118, 127, 127, 1,
-
24, 118, 54, 1, 24, 54, 94, 128,
-
1, 24, 118, 118, 127, 1, 24, 128,
-
127, 127, 1, 24, 128, 118, 127, 127,
-
1, 24, 118, 56, 1, 24, 56, 128,
-
1, 24, 118, 118, 1, 24, 118, 127,
-
127, 1, 24, 118, 128, 127, 1, 24,
-
127, 127, 1, 24, 118, 56, 1, 24,
-
56, 128, 127, 1, 24, 56, 1, 24,
-
56, 118, 56, 1, 24, 56, 118, 118,
-
1, 24, 118, 50, 1, 24, 50, 56,
-
1, 24, 56, 118, 118, 1, 24, 118,
-
94, 118, 1, 24, 118, 118, 127, 127,
-
1, 24, 118, 50, 1, 24, 50, 94,
-
128, 1, 24, 118, 118, 127, 1, 24,
-
128, 118, 118, 1, 24, 118, 118, 1,
-
24, 118, 118, 118, 1, 24, 118, 118,
-
127, 127, 1, 24, 118, 118, 1, 24,
-
118, 118, 128, 127, 1, 24, 118, 118,
-
94, 118, 51, 1, 24, 51, 127, 127,
-
1, 24, 128, 1, 24, 127, 1, 24,
-
128, 127, 127, 1, 24, 128, 118, 118,
-
118, 1, 24, 118, 127, 127, 1, 24,
-
128, 127, 1, 24, 118, 118, 1, 24,
-
118, 118, 118, 1, 24, 118, 51, 1,
-
24, 51, 118, 1, 24, 118, 118, 118,
-
118, 118, 118, 1, 24, 118, 127, 127,
-
1, 24, 118, 118, 128, 127, 1, 24,
-
128, 127, 1, 24, 118, 1, 24, 118,
-
118, 118, 118, 1, 24, 118, 118, 1,
-
24, 118, 1, 24, 127, 127, 1, 24,
-
128, 118, 118, 118, 1, 24, 118, 118,
-
118, 1, 24, 118, 56, 1, 24, 56,
-
118, 1, 24, 118, 56, 1, 24, 56,
-
51, 1, 24, 51, 1, 24, 118, 118,
-
1, 24, 118, 54, 1, 24, 54, 118,
-
1, 24, 118, 118, 127, 127, 1, 24,
-
118, 54, 1, 24, 54, 94, 128, 1,
-
24, 118, 118, 127, 1, 24, 128, 127,
-
127, 1, 24, 128, 118, 127, 127, 1,
-
24, 118, 56, 1, 24, 56, 128, 1,
-
24, 118, 118, 1, 24, 118, 127, 127,
-
1, 24, 118, 128, 127, 1, 24, 127,
-
127, 1, 24, 118, 56, 1, 24, 56,
-
128, 127, 1, 24, 56, 1, 24, 56,
-
118, 56, 1, 24, 56, 118, 118, 1,
-
24, 118, 50, 1, 24, 50, 56, 1,
-
24, 56, 118, 118, 1, 24, 118, 94,
-
118, 1, 24, 118, 118, 127, 127, 1,
-
24, 118, 50, 1, 24, 50, 94, 128,
-
1, 24, 118, 118, 127, 1, 24, 128,
-
118, 118, 118, 1, 24, 118, 118, 1,
-
24, 118, 118, 118, 1, 24, 118, 118,
-
1, 24, 118, 118, 118, 1, 24, 118,
-
118, 127, 127, 1, 24, 118, 118, 1,
-
24, 118, 118, 128, 127, 1, 24, 118,
-
118, 94, 118, 51, 1, 24, 51, 127,
-
127, 1, 24, 128, 1, 24, 127, 1,
-
24, 128, 127, 127, 1, 24, 128, 118,
-
127, 127, 1, 24, 118, 118, 1, 24,
-
118, 118, 118, 1, 24, 118, 118, 118,
-
1, 24, 118, 51, 1, 24, 51, 118,
-
1, 24, 118, 118, 118, 118, 128, 127,
-
1, 24, 118, 118, 1, 24, 118, 127,
-
127, 1, 24, 118, 128, 127, 1, 24,
-
127, 127, 1, 24, 118, 118, 1, 24,
-
118, 128, 127, 1, 24, 118, 1, 24,
-
118, 118, 94, 118, 56, 1, 24, 56,
-
127, 127, 1, 24, 128, 1, 24, 118,
-
94, 118, 56, 1, 24, 56, 127, 127,
-
1, 24, 128, 1, 24, 118, 118, 118,
-
1, 24, 118, 54, 1, 24, 54, 118,
-
1, 24, 118, 118, 127, 127, 1, 24,
-
118, 54, 1, 24, 54, 94, 128, 1,
-
24, 118, 118, 127, 1, 24, 128, 127,
-
127, 1, 24, 128, 118, 127, 127, 1,
-
24, 118, 56, 1, 24, 56, 128, 1,
-
24, 118, 118, 1, 24, 118, 127, 127,
-
1, 24, 118, 128, 127, 1, 24, 127,
-
127, 1, 24, 118, 56, 1, 24, 56,
-
128, 127, 1, 24, 56, 1, 24, 56,
-
118, 56, 1, 24, 56, 118, 118, 1,
-
24, 118, 50, 1, 24, 50, 56, 1,
-
24, 56, 118, 118, 1, 24, 118, 94,
-
118, 1, 24, 118, 118, 127, 127, 1,
-
24, 118, 50, 1, 24, 50, 94, 128,
-
1, 24, 118, 118, 127, 1, 24, 128,
-
118, 118, 118, 1, 24, 118, 118, 118,
-
1, 24, 1, 24, 118, 118, 1, 24,
-
118, 127, 127, 1, 24, 1, 24, 118,
-
128, 127, 1, 24, 118, 118, 94, 1,
-
24, 127, 127, 1, 24, 128, 1, 24,
-
127, 1, 24, 128, 127, 127, 1, 24,
-
128, 118, 127, 127, 1, 24, 1, 24,
-
1, 24, 118, 118, 1, 24, 1, 24,
-
118, 1, 24, 118, 118, 118, 128, 127,
-
1, 24, 1, 24, 127, 127, 1, 24,
-
128, 127, 1, 24, 127, 127, 1, 24,
-
1, 24, 128, 127, 1, 24, 1, 24,
-
118, 118, 1, 24, 94, 1, 24, 127,
-
127, 1, 24, 128, 1, 24, 118, 118,
-
118, 1, 24, 118, 54, 1, 24, 54,
-
118, 1, 24, 118, 118, 127, 127, 1,
-
24, 118, 54, 1, 24, 54, 94, 128,
-
1, 24, 118, 118, 127, 1, 24, 128,
-
127, 127, 1, 24, 128, 118, 127, 127,
-
1, 24, 118, 56, 1, 24, 56, 128,
-
1, 24, 118, 118, 1, 24, 118, 127,
-
127, 1, 24, 118, 128, 127, 1, 24,
-
127, 127, 1, 24, 118, 56, 1, 24,
-
56, 128, 127, 1, 24, 56, 1, 24,
-
56, 118, 56, 1, 24, 56, 118, 118,
-
1, 24, 118, 50, 1, 24, 50, 56,
-
1, 24, 56, 118, 118, 1, 24, 118,
-
94, 118, 1, 24, 118, 118, 127, 127,
-
1, 24, 118, 50, 1, 24, 50, 94,
-
128, 1, 24, 118, 118, 127, 1, 24,
-
128, 118, 118, 1, 24, 1, 24, 118,
-
118, 1, 24, 118, 94, 127, 127, 1,
-
24, 128, 1, 24, 127, 127, 1, 24,
-
128, 118, 127, 127, 1, 24, 1, 24,
-
1, 24, 118, 118, 1, 24, 118, 118,
-
128, 127, 1, 24, 1, 24, 127, 127,
-
1, 24, 128, 127, 1, 24, 127, 127,
-
1, 24, 1, 24, 128, 127, 1, 24,
-
1, 24, 118, 118, 1, 24, 94, 1,
-
24, 127, 127, 1, 24, 128, 1, 24,
-
118, 127, 127, 1, 24, 128, 118, 118,
-
118, 51, 51, 118, 118, 118, 118, 118,
-
118, 118, 118, 118, 118, 51, 51, 118,
-
118, 118, 118, 118, 118, 118, 118, 51,
-
51, 118, 118, 118, 118, 118, 118, 118,
-
51, 51, 118, 118, 118, 118, 118, 118,
-
118, 118, 118, 118, 118, 56, 56, 118,
-
118, 118, 118, 118, 118, 118, 118, 118,
-
118, 118, 118, 118, 118, 118, 118, 118,
-
118, 118, 118, 118, 118, 56, 56, 0
-
]
-
-
1
class << self
-
1
attr_accessor :_address_lists_index_offsets
-
1
private :_address_lists_index_offsets, :_address_lists_index_offsets=
-
end
-
1
self._address_lists_index_offsets = [
-
0, 0, 119, 121, 146, 148, 173, 292,
-
294, 319, 447, 575, 577, 602, 731, 850,
-
969, 1088, 1090, 1115, 1234, 1353, 1472, 1474,
-
1499, 1618, 1675, 1677, 1702, 1759, 1878, 1880,
-
1905, 2024, 2081, 2083, 2108, 2165, 2167, 2192,
-
2194, 2219, 2221, 2246, 2365, 2484, 2486, 2511,
-
2513, 2538, 2657, 2776, 2778, 2803, 2922, 3041,
-
3043, 3068, 3187, 3189, 3214, 3342, 3470, 3472,
-
3497, 3626, 3745, 3864, 3983, 3985, 4010, 4129,
-
4248, 4367, 4369, 4394, 4513, 4570, 4572, 4597,
-
4654, 4773, 4775, 4800, 4919, 4976, 4978, 5003,
-
5060, 5112, 5114, 5139, 5191, 5310, 5429, 5431,
-
5456, 5584, 5712, 5714, 5739, 5858, 5977, 5979,
-
6004, 6123, 6242, 6361, 6363, 6388, 6507, 6626,
-
6628, 6653, 6772, 6774, 6799, 6918, 7037, 7039,
-
7064, 7183, 7185, 7210, 7338, 7466, 7468, 7493,
-
7622, 7741, 7860, 7979, 7981, 8006, 8125, 8244,
-
8363, 8365, 8390, 8509, 8566, 8568, 8593, 8650,
-
8769, 8771, 8796, 8915, 8972, 8974, 8999, 9056,
-
9108, 9110, 9135, 9187, 9306, 9425, 9427, 9452,
-
9580, 9708, 9710, 9735, 9854, 9973, 9975, 10000,
-
10119, 10238, 10357, 10359, 10384, 10503, 10622, 10624,
-
10649, 10768, 10887, 11006, 11008, 11033, 11152, 11271,
-
11273, 11298, 11417, 11536, 11655, 11657, 11682, 11801,
-
11920, 12048, 12176, 12178, 12203, 12322, 12441, 12443,
-
12468, 12587, 12706, 12835, 12963, 12965, 12990, 13109,
-
13228, 13323, 13442, 13494, 13496, 13521, 13573, 13701,
-
13829, 13831, 13856, 13985, 13987, 14012, 14140, 14142,
-
14167, 14296, 14424, 14552, 14554, 14579, 14708, 14827,
-
14946, 15065, 15067, 15092, 15211, 15339, 15467, 15469,
-
15494, 15623, 15751, 15753, 15778, 15897, 16016, 16018,
-
16043, 16162, 16281, 16400, 16402, 16427, 16546, 16598,
-
16600, 16625, 16677, 16796, 16798, 16823, 16942, 17061,
-
17180, 17299, 17418, 17537, 17539, 17564, 17683, 17811,
-
17939, 17941, 17966, 18085, 18204, 18333, 18461, 18463,
-
18488, 18617, 18745, 18747, 18772, 18891, 18893, 18918,
-
19037, 19156, 19251, 19370, 19427, 19429, 19454, 19511,
-
19639, 19767, 19769, 19794, 19923, 19925, 19950, 20069,
-
20188, 20307, 20309, 20334, 20453, 20508, 20510, 20535,
-
20590, 20709, 20711, 20736, 20855, 20974, 21102, 21230,
-
21232, 21257, 21376, 21431, 21433, 21458, 21513, 21608,
-
21737, 21739, 21764, 21883, 22002, 22130, 22132, 22157,
-
22286, 22414, 22542, 22544, 22569, 22698, 22817, 22945,
-
23073, 23075, 23100, 23219, 23276, 23278, 23303, 23360,
-
23489, 23491, 23516, 23635, 23754, 23756, 23781, 23900,
-
24028, 24156, 24158, 24183, 24302, 24431, 24559, 24561,
-
24586, 24714, 24842, 24844, 24869, 24988, 25045, 25047,
-
25072, 25129, 25258, 25386, 25388, 25413, 25470, 25472,
-
25497, 25554, 25673, 25730, 25732, 25757, 25814, 25933,
-
26052, 26054, 26079, 26198, 26249, 26251, 26276, 26327,
-
26384, 26386, 26411, 26468, 26587, 26706, 26708, 26733,
-
26852, 26947, 27066, 27068, 27093, 27212, 27331, 27459,
-
27587, 27589, 27614, 27733, 27784, 27786, 27811, 27862,
-
27957, 28086, 28088, 28113, 28232, 28351, 28479, 28481,
-
28506, 28635, 28754, 28873, 28875, 28900, 29019, 29138,
-
29140, 29165, 29284, 29403, 29522, 29524, 29549, 29668,
-
29787, 29915, 30043, 30045, 30070, 30189, 30308, 30310,
-
30335, 30454, 30573, 30702, 30830, 30832, 30857, 30976,
-
31095, 31190, 31309, 31361, 31363, 31388, 31440, 31568,
-
31696, 31698, 31723, 31852, 31854, 31879, 32007, 32009,
-
32034, 32163, 32291, 32419, 32421, 32446, 32575, 32694,
-
32813, 32932, 32934, 32959, 33078, 33206, 33334, 33336,
-
33361, 33490, 33618, 33620, 33645, 33764, 33883, 33885,
-
33910, 34029, 34148, 34267, 34269, 34294, 34413, 34465,
-
34467, 34492, 34544, 34663, 34665, 34690, 34809, 34928,
-
35047, 35166, 35285, 35404, 35406, 35431, 35550, 35678,
-
35806, 35808, 35833, 35952, 36071, 36200, 36328, 36330,
-
36355, 36484, 36612, 36614, 36639, 36758, 36760, 36785,
-
36904, 37023, 37142, 37261, 37263, 37288, 37407, 37526,
-
37528, 37553, 37672, 37674, 37699, 37827, 37955, 37957,
-
37982, 38111, 38230, 38349, 38468, 38470, 38495, 38614,
-
38733, 38852, 38854, 38879, 38998, 39055, 39057, 39082,
-
39139, 39258, 39260, 39285, 39404, 39461, 39463, 39488,
-
39545, 39597, 39599, 39624, 39676, 39678, 39703, 39822,
-
39941, 39943, 39968, 40087, 40142, 40144, 40169, 40224,
-
40343, 40345, 40370, 40489, 40608, 40736, 40864, 40866,
-
40891, 41010, 41065, 41067, 41092, 41147, 41242, 41371,
-
41373, 41398, 41517, 41636, 41764, 41766, 41791, 41920,
-
42048, 42176, 42178, 42203, 42332, 42451, 42579, 42707,
-
42709, 42734, 42853, 42910, 42912, 42937, 42994, 43123,
-
43125, 43150, 43269, 43388, 43390, 43415, 43534, 43662,
-
43790, 43792, 43817, 43936, 44065, 44193, 44195, 44220,
-
44348, 44476, 44478, 44503, 44622, 44679, 44681, 44706,
-
44763, 44892, 45020, 45022, 45047, 45104, 45106, 45131,
-
45188, 45307, 45364, 45366, 45391, 45448, 45567, 45686,
-
45688, 45713, 45832, 45883, 45885, 45910, 45961, 46018,
-
46020, 46045, 46102, 46221, 46340, 46342, 46367, 46486,
-
46581, 46700, 46702, 46727, 46846, 46965, 47093, 47221,
-
47223, 47248, 47367, 47418, 47420, 47445, 47496, 47591,
-
47720, 47722, 47747, 47866, 47985, 48113, 48115, 48140,
-
48269, 48388, 48507, 48626, 48628, 48653, 48772, 48891,
-
48893, 48918, 49037, 49156, 49275, 49277, 49302, 49421,
-
49540, 49542, 49567, 49686, 49805, 49924, 49926, 49951,
-
50070, 50189, 50317, 50445, 50447, 50472, 50591, 50710,
-
50712, 50737, 50856, 50975, 51104, 51232, 51234, 51259,
-
51378, 51497, 51592, 51711, 51763, 51765, 51790, 51842,
-
51970, 52098, 52100, 52125, 52254, 52256, 52281, 52409,
-
52411, 52436, 52565, 52693, 52821, 52823, 52848, 52977,
-
53096, 53224, 53352, 53354, 53379, 53498, 53617, 53619,
-
53644, 53763, 53882, 54001, 54003, 54028, 54147, 54266,
-
54385, 54387, 54412, 54531, 54583, 54585, 54610, 54662,
-
54781, 54783, 54808, 54927, 55046, 55165, 55284, 55413,
-
55541, 55543, 55568, 55687, 55806, 55808, 55833, 55952,
-
56080, 56208, 56210, 56235, 56354, 56483, 56611, 56613,
-
56638, 56766, 56894, 56896, 56921, 57040, 57159, 57161,
-
57186, 57305, 57434, 57562, 57564, 57589, 57708, 57710,
-
57735, 57854, 57973, 58068, 58187, 58244, 58246, 58271,
-
58328, 58456, 58584, 58586, 58611, 58740, 58742, 58767,
-
58886, 58981, 59100, 59157, 59159, 59184, 59241, 59369,
-
59497, 59499, 59524, 59653, 59655, 59680, 59799, 59918,
-
60037, 60039, 60064, 60183, 60238, 60240, 60265, 60320,
-
60439, 60441, 60466, 60585, 60704, 60832, 60960, 60962,
-
60987, 61106, 61161, 61163, 61188, 61243, 61338, 61467,
-
61469, 61494, 61613, 61732, 61860, 61862, 61887, 62016,
-
62144, 62272, 62274, 62299, 62428, 62547, 62675, 62803,
-
62805, 62830, 62949, 63006, 63008, 63033, 63090, 63219,
-
63221, 63246, 63365, 63484, 63486, 63511, 63630, 63758,
-
63886, 63888, 63913, 64032, 64161, 64289, 64291, 64316,
-
64444, 64572, 64574, 64599, 64718, 64775, 64777, 64802,
-
64859, 64988, 65116, 65118, 65143, 65200, 65202, 65227,
-
65284, 65403, 65460, 65462, 65487, 65544, 65663, 65782,
-
65784, 65809, 65928, 65979, 65981, 66006, 66057, 66114,
-
66116, 66141, 66198, 66317, 66436, 66438, 66463, 66582,
-
66677, 66796, 66798, 66823, 66942, 67061, 67189, 67317,
-
67319, 67344, 67463, 67514, 67516, 67541, 67592, 67687,
-
67816, 67818, 67843, 67962, 68081, 68209, 68211, 68236,
-
68365, 68484, 68603, 68722, 68724, 68749, 68868, 68987,
-
69106, 69108, 69133, 69135, 69160, 69279, 69398, 69400,
-
69425, 69544, 69672, 69800, 69802, 69827, 69829, 69854,
-
69973, 70102, 70230, 70232, 70257, 70376, 70495, 70590,
-
70592, 70617, 70745, 70873, 70875, 70900, 71029, 71031,
-
71056, 71184, 71186, 71211, 71340, 71468, 71596, 71598,
-
71623, 71752, 71871, 71999, 72127, 72129, 72154, 72156,
-
72181, 72183, 72208, 72327, 72446, 72448, 72473, 72475,
-
72500, 72619, 72621, 72646, 72765, 72884, 73003, 73132,
-
73260, 73262, 73287, 73289, 73314, 73442, 73570, 73572,
-
73597, 73726, 73854, 73856, 73881, 74009, 74137, 74139,
-
74164, 74166, 74191, 74320, 74448, 74450, 74475, 74477,
-
74502, 74621, 74740, 74742, 74767, 74862, 74864, 74889,
-
75017, 75145, 75147, 75172, 75301, 75303, 75328, 75447,
-
75566, 75685, 75687, 75712, 75831, 75886, 75888, 75913,
-
75968, 76087, 76089, 76114, 76233, 76352, 76480, 76608,
-
76610, 76635, 76754, 76809, 76811, 76836, 76891, 76986,
-
77115, 77117, 77142, 77261, 77380, 77508, 77510, 77535,
-
77664, 77792, 77920, 77922, 77947, 78076, 78195, 78323,
-
78451, 78453, 78478, 78597, 78654, 78656, 78681, 78738,
-
78867, 78869, 78894, 79013, 79132, 79134, 79159, 79278,
-
79406, 79534, 79536, 79561, 79680, 79809, 79937, 79939,
-
79964, 80092, 80220, 80222, 80247, 80366, 80423, 80425,
-
80450, 80507, 80636, 80764, 80766, 80791, 80848, 80850,
-
80875, 80932, 81051, 81108, 81110, 81135, 81192, 81311,
-
81430, 81432, 81457, 81576, 81627, 81629, 81654, 81705,
-
81762, 81764, 81789, 81846, 81965, 82084, 82086, 82111,
-
82230, 82325, 82444, 82446, 82471, 82590, 82709, 82837,
-
82965, 82967, 82992, 83111, 83162, 83164, 83189, 83240,
-
83335, 83464, 83466, 83491, 83610, 83729, 83857, 83859,
-
83884, 84013, 84132, 84251, 84253, 84278, 84280, 84305,
-
84424, 84543, 84545, 84570, 84689, 84784, 84912, 85040,
-
85042, 85067, 85196, 85198, 85223, 85351, 85479, 85481,
-
85506, 85635, 85754, 85882, 86010, 86012, 86037, 86039,
-
86064, 86066, 86091, 86210, 86329, 86331, 86356, 86475,
-
86594, 86723, 86851, 86853, 86878, 86880, 86905, 87033,
-
87161, 87163, 87188, 87317, 87445, 87447, 87472, 87600,
-
87728, 87730, 87755, 87757, 87782, 87911, 88039, 88041,
-
88066, 88068, 88093, 88212, 88331, 88333, 88358, 88453,
-
88455, 88480, 88608, 88736, 88738, 88763, 88892, 88894,
-
88919, 89038, 89166, 89294, 89296, 89321, 89450, 89569,
-
89688, 89807, 89859, 89911, 90030, 90149, 90268, 90387,
-
90506, 90625, 90744, 90863, 90982, 91101, 91153, 91205,
-
91324, 91443, 91562, 91681, 91800, 91919, 92038, 92157,
-
92209, 92261, 92380, 92499, 92618, 92737, 92856, 92975,
-
93094, 93146, 93198, 93317, 93436, 93555, 93674, 93793,
-
93912, 94031, 94150, 94269, 94388, 94507, 94564, 94621,
-
94740, 94859, 94978, 95097, 95216, 95335, 95454, 95573,
-
95692, 95811, 95930, 96049, 96168, 96287, 96406, 96525,
-
96644, 96763, 96882, 97001, 97120, 97239, 97296, 97353
-
]
-
-
1
class << self
-
1
attr_accessor :_address_lists_indicies
-
1
private :_address_lists_indicies, :_address_lists_indicies=
-
end
-
1
self._address_lists_indicies = [
-
0, 1, 1, 1, 2, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 0,
-
3, 4, 3, 3, 3, 3, 3, 5,
-
1, 3, 3, 6, 3, 7, 3, 3,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 1, 6, 8, 3, 1, 3, 1,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 3, 1, 1, 1, 3, 3, 3,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 3, 3, 3, 3, 3, 1, 9,
-
1, 0, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
0, 1, 10, 1, 11, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 11, 1, 12, 1, 1,
-
1, 13, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 12, 14, 15, 14,
-
14, 14, 14, 14, 16, 1, 14, 14,
-
1, 14, 17, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 18, 1,
-
19, 14, 1, 14, 17, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 1,
-
1, 1, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 1, 20, 1, 14, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 14, 1, 21,
-
21, 21, 21, 21, 21, 21, 21, 21,
-
1, 21, 21, 22, 21, 21, 21, 21,
-
21, 21, 21, 21, 21, 21, 21, 21,
-
21, 21, 21, 21, 21, 21, 21, 21,
-
23, 21, 21, 21, 21, 21, 21, 21,
-
21, 21, 21, 21, 21, 21, 21, 21,
-
21, 21, 21, 21, 21, 21, 21, 21,
-
21, 21, 21, 21, 21, 21, 21, 21,
-
21, 21, 21, 21, 21, 21, 21, 21,
-
21, 21, 21, 21, 21, 21, 21, 21,
-
21, 21, 21, 21, 21, 21, 21, 21,
-
21, 21, 24, 21, 21, 21, 21, 21,
-
21, 21, 21, 21, 21, 21, 21, 21,
-
21, 21, 21, 21, 21, 21, 21, 21,
-
21, 21, 21, 21, 21, 21, 21, 21,
-
21, 21, 21, 21, 21, 21, 1, 25,
-
25, 25, 25, 25, 25, 25, 25, 25,
-
1, 25, 25, 26, 25, 25, 25, 25,
-
25, 25, 25, 25, 25, 25, 25, 25,
-
25, 25, 25, 25, 25, 25, 25, 25,
-
27, 25, 25, 25, 25, 25, 25, 25,
-
25, 25, 25, 25, 25, 25, 25, 25,
-
25, 25, 25, 25, 25, 25, 25, 25,
-
25, 25, 25, 25, 25, 25, 25, 25,
-
25, 25, 25, 25, 25, 25, 25, 25,
-
25, 25, 25, 25, 25, 25, 25, 25,
-
25, 25, 25, 25, 25, 25, 25, 25,
-
25, 25, 28, 25, 25, 25, 25, 25,
-
25, 25, 25, 25, 25, 25, 25, 25,
-
25, 25, 25, 25, 25, 25, 25, 25,
-
25, 25, 25, 25, 25, 25, 25, 25,
-
25, 25, 25, 25, 25, 25, 1, 29,
-
1, 25, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
25, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 25, 30, 1, 1, 1, 31,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 30, 32, 33, 32, 32, 32,
-
32, 32, 34, 1, 32, 32, 1, 32,
-
35, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 36, 1, 37, 32,
-
1, 32, 35, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 1, 1, 1,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 1, 38, 1, 1, 1, 39, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 38, 14, 15, 14, 14, 14, 14,
-
14, 40, 1, 14, 14, 1, 14, 17,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 18, 1, 19, 14, 1,
-
14, 17, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 1, 1, 1, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
1, 41, 1, 1, 1, 42, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
41, 14, 15, 14, 14, 14, 14, 14,
-
43, 1, 14, 14, 1, 14, 1, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 1, 1, 8, 14, 1, 14,
-
1, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 1, 1, 1, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 1,
-
44, 1, 41, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 41, 1, 45, 1, 1, 1, 46,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 45, 32, 33, 32, 32, 32,
-
32, 32, 47, 1, 32, 32, 1, 32,
-
1, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 1, 1, 48, 32,
-
1, 32, 1, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 1, 1, 1,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 1, 49, 1, 1, 1, 50, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 49, 51, 52, 51, 51, 51, 51,
-
51, 53, 1, 51, 51, 54, 51, 55,
-
51, 51, 51, 51, 51, 51, 51, 51,
-
51, 51, 51, 1, 1, 1, 51, 1,
-
51, 56, 51, 51, 51, 51, 51, 51,
-
51, 51, 51, 51, 51, 51, 51, 51,
-
51, 51, 51, 51, 51, 51, 51, 51,
-
51, 51, 51, 51, 1, 1, 1, 51,
-
51, 51, 51, 51, 51, 51, 51, 51,
-
51, 51, 51, 51, 51, 51, 51, 51,
-
51, 51, 51, 51, 51, 51, 51, 51,
-
51, 51, 51, 51, 51, 51, 51, 51,
-
1, 57, 1, 1, 1, 58, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
57, 51, 52, 51, 51, 51, 51, 51,
-
59, 1, 51, 51, 60, 51, 55, 51,
-
51, 51, 51, 51, 51, 51, 51, 51,
-
51, 51, 1, 1, 1, 51, 1, 51,
-
61, 51, 51, 51, 51, 51, 51, 51,
-
51, 51, 51, 51, 51, 51, 51, 51,
-
51, 51, 51, 51, 51, 51, 51, 51,
-
51, 51, 51, 1, 1, 1, 51, 51,
-
51, 51, 51, 51, 51, 51, 51, 51,
-
51, 51, 51, 51, 51, 51, 51, 51,
-
51, 51, 51, 51, 51, 51, 51, 51,
-
51, 51, 51, 51, 51, 51, 51, 1,
-
62, 1, 57, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 57, 1, 63, 1, 1, 1, 64,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 63, 65, 66, 65, 65, 65,
-
65, 65, 67, 1, 65, 65, 1, 65,
-
68, 65, 65, 65, 65, 65, 65, 65,
-
65, 65, 65, 65, 1, 1, 1, 65,
-
69, 65, 70, 65, 65, 65, 65, 65,
-
65, 65, 65, 65, 65, 65, 65, 65,
-
65, 65, 65, 65, 65, 65, 65, 65,
-
65, 65, 65, 65, 65, 1, 1, 1,
-
65, 65, 65, 65, 65, 65, 65, 65,
-
65, 65, 65, 65, 65, 65, 65, 65,
-
65, 65, 65, 65, 65, 65, 65, 65,
-
65, 65, 65, 65, 65, 65, 65, 65,
-
65, 1, 71, 1, 1, 1, 72, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 71, 1, 1, 1, 1, 1, 1,
-
1, 73, 1, 1, 1, 1, 1, 74,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 75,
-
1, 76, 1, 77, 1, 71, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 71, 1, 78, 1,
-
1, 1, 79, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 78, 1, 1,
-
1, 1, 1, 1, 1, 80, 1, 1,
-
1, 1, 1, 81, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 82, 1, 83, 1, 74,
-
1, 1, 1, 84, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 74, 85,
-
86, 85, 85, 85, 85, 85, 87, 1,
-
85, 85, 1, 85, 1, 85, 85, 85,
-
85, 85, 85, 85, 85, 85, 85, 85,
-
1, 1, 1, 85, 1, 85, 1, 85,
-
85, 85, 85, 85, 85, 85, 85, 85,
-
85, 85, 85, 85, 85, 85, 85, 85,
-
85, 85, 85, 85, 85, 85, 85, 85,
-
85, 1, 1, 1, 85, 85, 85, 85,
-
85, 85, 85, 85, 85, 85, 85, 85,
-
85, 85, 85, 85, 85, 85, 85, 85,
-
85, 85, 85, 85, 85, 85, 85, 85,
-
85, 85, 85, 85, 85, 1, 88, 1,
-
74, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 74,
-
1, 89, 1, 1, 1, 90, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
89, 85, 1, 85, 85, 85, 85, 85,
-
91, 1, 85, 85, 1, 85, 74, 85,
-
85, 85, 85, 85, 85, 85, 85, 85,
-
85, 85, 1, 1, 1, 85, 75, 85,
-
92, 85, 85, 85, 85, 85, 85, 85,
-
85, 85, 85, 85, 85, 85, 85, 85,
-
85, 85, 85, 85, 85, 85, 85, 85,
-
85, 85, 85, 1, 1, 1, 85, 85,
-
85, 85, 85, 85, 85, 85, 85, 85,
-
85, 85, 85, 85, 85, 85, 85, 85,
-
85, 85, 85, 85, 85, 85, 85, 85,
-
85, 85, 85, 85, 85, 85, 85, 1,
-
89, 1, 1, 1, 90, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 89,
-
1, 1, 1, 1, 1, 1, 1, 91,
-
1, 1, 1, 1, 1, 74, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 75, 1, 92,
-
1, 93, 1, 89, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 89, 1, 94, 1, 1, 1,
-
95, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 94, 1, 1, 1, 1,
-
1, 1, 1, 96, 1, 1, 1, 1,
-
1, 81, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 82, 1, 97, 1, 98, 1, 75,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 75, 1,
-
99, 1, 6, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 6, 1, 100, 1, 101, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 101, 1, 102, 1,
-
1, 1, 103, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 102, 104, 105,
-
104, 104, 104, 104, 104, 106, 1, 104,
-
104, 1, 104, 17, 104, 104, 104, 104,
-
104, 104, 104, 104, 104, 104, 104, 18,
-
1, 19, 104, 1, 104, 17, 104, 104,
-
104, 104, 104, 104, 104, 104, 104, 104,
-
104, 104, 104, 104, 104, 104, 104, 104,
-
104, 104, 104, 104, 104, 104, 104, 104,
-
1, 1, 1, 104, 104, 104, 104, 104,
-
104, 104, 104, 104, 104, 104, 104, 104,
-
104, 104, 104, 104, 104, 104, 104, 104,
-
104, 104, 104, 104, 104, 104, 104, 104,
-
104, 104, 104, 104, 1, 107, 1, 1,
-
1, 108, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 107, 104, 105, 104,
-
104, 104, 104, 104, 109, 1, 104, 104,
-
1, 104, 1, 104, 104, 104, 104, 104,
-
104, 104, 104, 104, 104, 104, 1, 1,
-
8, 104, 1, 104, 1, 104, 104, 104,
-
104, 104, 104, 104, 104, 104, 104, 104,
-
104, 104, 104, 104, 104, 104, 104, 104,
-
104, 104, 104, 104, 104, 104, 104, 1,
-
1, 1, 104, 104, 104, 104, 104, 104,
-
104, 104, 104, 104, 104, 104, 104, 104,
-
104, 104, 104, 104, 104, 104, 104, 104,
-
104, 104, 104, 104, 104, 104, 104, 104,
-
104, 104, 104, 1, 110, 1, 107, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 107, 1, 111,
-
1, 112, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
112, 1, 113, 1, 1, 1, 114, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 113, 115, 116, 115, 115, 115, 115,
-
115, 117, 1, 115, 115, 118, 115, 119,
-
115, 115, 115, 115, 115, 115, 115, 115,
-
115, 115, 115, 1, 120, 121, 115, 1,
-
115, 122, 115, 115, 115, 115, 115, 115,
-
115, 115, 115, 115, 115, 115, 115, 115,
-
115, 115, 115, 115, 115, 115, 115, 115,
-
115, 115, 115, 115, 1, 1, 1, 115,
-
115, 115, 115, 115, 115, 115, 115, 115,
-
115, 115, 115, 115, 115, 115, 115, 115,
-
115, 115, 115, 115, 115, 115, 115, 115,
-
115, 115, 115, 115, 115, 115, 115, 115,
-
1, 123, 1, 1, 1, 124, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
123, 125, 126, 125, 125, 125, 125, 125,
-
127, 1, 125, 125, 118, 125, 128, 125,
-
125, 125, 125, 125, 125, 125, 125, 125,
-
125, 125, 1, 120, 129, 125, 1, 125,
-
1, 125, 125, 125, 125, 125, 125, 125,
-
125, 125, 125, 125, 125, 125, 125, 125,
-
125, 125, 125, 125, 125, 125, 125, 125,
-
125, 125, 125, 1, 1, 1, 125, 125,
-
125, 125, 125, 125, 125, 125, 125, 125,
-
125, 125, 125, 125, 125, 125, 125, 125,
-
125, 125, 125, 125, 125, 125, 125, 125,
-
125, 125, 125, 125, 125, 125, 125, 1,
-
130, 1, 123, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 123, 1, 131, 1, 1, 1, 132,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 131, 133, 134, 133, 133, 133,
-
133, 133, 135, 1, 133, 133, 136, 133,
-
137, 133, 133, 133, 133, 133, 133, 133,
-
133, 133, 133, 133, 1, 138, 139, 133,
-
1, 133, 140, 133, 133, 133, 133, 133,
-
133, 133, 133, 133, 133, 133, 133, 133,
-
133, 133, 133, 133, 133, 133, 133, 133,
-
133, 133, 133, 133, 133, 1, 1, 1,
-
133, 133, 133, 133, 133, 133, 133, 133,
-
133, 133, 133, 133, 133, 133, 133, 133,
-
133, 133, 133, 133, 133, 133, 133, 133,
-
133, 133, 133, 133, 133, 133, 133, 133,
-
133, 1, 141, 1, 1, 1, 142, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 141, 143, 144, 143, 143, 143, 143,
-
143, 145, 1, 143, 143, 146, 143, 147,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 1, 148, 139, 143, 1,
-
143, 149, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 1, 1, 1, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
1, 150, 1, 151, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 151, 1, 152, 1, 1, 1,
-
153, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 152, 143, 144, 143, 143,
-
143, 143, 143, 154, 1, 143, 143, 1,
-
143, 155, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 1, 1, 139,
-
143, 1, 143, 155, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 1, 1,
-
1, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 1, 156, 1, 143, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 143, 1, 157, 157,
-
157, 157, 157, 157, 157, 157, 157, 1,
-
157, 157, 158, 157, 157, 157, 157, 157,
-
157, 157, 157, 157, 157, 157, 157, 157,
-
157, 157, 157, 157, 157, 157, 157, 159,
-
157, 157, 157, 157, 157, 157, 157, 157,
-
157, 157, 157, 157, 157, 157, 157, 157,
-
157, 157, 157, 157, 157, 157, 157, 157,
-
157, 157, 157, 157, 157, 157, 157, 157,
-
157, 157, 157, 157, 157, 157, 157, 157,
-
157, 157, 157, 157, 157, 157, 157, 157,
-
157, 157, 157, 157, 157, 157, 157, 157,
-
157, 160, 157, 157, 157, 157, 157, 157,
-
157, 157, 157, 157, 157, 157, 157, 157,
-
157, 157, 157, 157, 157, 157, 157, 157,
-
157, 157, 157, 157, 157, 157, 157, 157,
-
157, 157, 157, 157, 157, 1, 161, 161,
-
161, 161, 161, 161, 161, 161, 161, 1,
-
161, 161, 162, 161, 161, 161, 161, 161,
-
161, 161, 161, 161, 161, 161, 161, 161,
-
161, 161, 161, 161, 161, 161, 161, 163,
-
161, 161, 161, 161, 161, 161, 161, 161,
-
161, 161, 161, 161, 161, 161, 161, 161,
-
161, 161, 161, 161, 161, 161, 161, 161,
-
161, 161, 161, 161, 161, 161, 161, 161,
-
161, 161, 161, 161, 161, 161, 161, 161,
-
161, 161, 161, 161, 161, 161, 161, 161,
-
161, 161, 161, 161, 161, 161, 161, 161,
-
161, 164, 161, 161, 161, 161, 161, 161,
-
161, 161, 161, 161, 161, 161, 161, 161,
-
161, 161, 161, 161, 161, 161, 161, 161,
-
161, 161, 161, 161, 161, 161, 161, 161,
-
161, 161, 161, 161, 161, 1, 165, 1,
-
161, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 161,
-
1, 161, 161, 161, 161, 161, 161, 161,
-
161, 161, 161, 161, 161, 161, 161, 161,
-
161, 161, 161, 161, 161, 161, 161, 161,
-
161, 161, 161, 161, 161, 161, 161, 161,
-
161, 161, 161, 161, 161, 161, 161, 161,
-
161, 161, 161, 161, 161, 161, 161, 161,
-
161, 161, 161, 161, 161, 161, 161, 161,
-
161, 161, 161, 161, 161, 161, 161, 161,
-
161, 161, 161, 161, 161, 161, 161, 161,
-
161, 161, 161, 161, 161, 161, 161, 161,
-
161, 161, 161, 161, 161, 161, 161, 161,
-
161, 161, 161, 161, 161, 161, 161, 161,
-
161, 161, 161, 161, 161, 161, 161, 161,
-
161, 161, 161, 161, 161, 161, 161, 161,
-
161, 161, 161, 161, 161, 161, 161, 161,
-
161, 161, 161, 161, 161, 161, 161, 161,
-
161, 1, 166, 1, 1, 1, 167, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 166, 168, 169, 168, 168, 168, 168,
-
168, 170, 1, 168, 168, 1, 168, 171,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 1, 1, 172, 168, 1,
-
168, 171, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 1, 1, 1, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
1, 173, 1, 1, 1, 174, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
173, 143, 144, 143, 143, 143, 143, 143,
-
175, 1, 143, 143, 1, 143, 155, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 1, 1, 139, 143, 1, 143,
-
155, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 1, 1, 1, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 1,
-
176, 1, 1, 1, 177, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 176,
-
143, 144, 143, 143, 143, 143, 143, 178,
-
1, 143, 143, 1, 143, 1, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 1, 1, 129, 143, 1, 143, 1,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 1, 1, 1, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 1, 179,
-
1, 176, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
176, 1, 180, 1, 1, 1, 181, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 180, 168, 169, 168, 168, 168, 168,
-
168, 182, 1, 168, 168, 1, 168, 1,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 1, 1, 183, 168, 1,
-
168, 1, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 1, 1, 1, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
1, 184, 1, 1, 1, 185, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
184, 186, 187, 186, 186, 186, 186, 186,
-
188, 1, 186, 186, 189, 186, 190, 186,
-
186, 186, 186, 186, 186, 186, 186, 186,
-
186, 186, 1, 1, 1, 186, 1, 186,
-
191, 186, 186, 186, 186, 186, 186, 186,
-
186, 186, 186, 186, 186, 186, 186, 186,
-
186, 186, 186, 186, 186, 186, 186, 186,
-
186, 186, 186, 1, 1, 1, 186, 186,
-
186, 186, 186, 186, 186, 186, 186, 186,
-
186, 186, 186, 186, 186, 186, 186, 186,
-
186, 186, 186, 186, 186, 186, 186, 186,
-
186, 186, 186, 186, 186, 186, 186, 1,
-
192, 1, 1, 1, 193, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 192,
-
186, 187, 186, 186, 186, 186, 186, 194,
-
1, 186, 186, 195, 186, 190, 186, 186,
-
186, 186, 186, 186, 186, 186, 186, 186,
-
186, 1, 1, 1, 186, 1, 186, 196,
-
186, 186, 186, 186, 186, 186, 186, 186,
-
186, 186, 186, 186, 186, 186, 186, 186,
-
186, 186, 186, 186, 186, 186, 186, 186,
-
186, 186, 1, 1, 1, 186, 186, 186,
-
186, 186, 186, 186, 186, 186, 186, 186,
-
186, 186, 186, 186, 186, 186, 186, 186,
-
186, 186, 186, 186, 186, 186, 186, 186,
-
186, 186, 186, 186, 186, 186, 1, 197,
-
1, 192, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
192, 1, 198, 1, 1, 1, 199, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 198, 200, 201, 200, 200, 200, 200,
-
200, 202, 1, 200, 200, 1, 200, 203,
-
200, 200, 200, 200, 200, 200, 200, 200,
-
200, 200, 200, 1, 1, 1, 200, 204,
-
200, 205, 200, 200, 200, 200, 200, 200,
-
200, 200, 200, 200, 200, 200, 200, 200,
-
200, 200, 200, 200, 200, 200, 200, 200,
-
200, 200, 200, 200, 1, 1, 1, 200,
-
200, 200, 200, 200, 200, 200, 200, 200,
-
200, 200, 200, 200, 200, 200, 200, 200,
-
200, 200, 200, 200, 200, 200, 200, 200,
-
200, 200, 200, 200, 200, 200, 200, 200,
-
1, 206, 1, 1, 1, 207, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
206, 1, 1, 1, 1, 1, 1, 1,
-
208, 1, 1, 1, 1, 1, 209, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 210, 1,
-
211, 1, 212, 1, 206, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 206, 1, 213, 1, 1,
-
1, 214, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 213, 1, 1, 1,
-
1, 1, 1, 1, 215, 1, 1, 1,
-
1, 1, 216, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 217, 1, 218, 1, 209, 1,
-
1, 1, 219, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 209, 220, 221,
-
220, 220, 220, 220, 220, 222, 1, 220,
-
220, 1, 220, 1, 220, 220, 220, 220,
-
220, 220, 220, 220, 220, 220, 220, 1,
-
1, 1, 220, 1, 220, 1, 220, 220,
-
220, 220, 220, 220, 220, 220, 220, 220,
-
220, 220, 220, 220, 220, 220, 220, 220,
-
220, 220, 220, 220, 220, 220, 220, 220,
-
1, 1, 1, 220, 220, 220, 220, 220,
-
220, 220, 220, 220, 220, 220, 220, 220,
-
220, 220, 220, 220, 220, 220, 220, 220,
-
220, 220, 220, 220, 220, 220, 220, 220,
-
220, 220, 220, 220, 1, 223, 1, 209,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 209, 1,
-
224, 1, 1, 1, 225, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 224,
-
220, 1, 220, 220, 220, 220, 220, 226,
-
1, 220, 220, 1, 220, 209, 220, 220,
-
220, 220, 220, 220, 220, 220, 220, 220,
-
220, 1, 1, 1, 220, 210, 220, 227,
-
220, 220, 220, 220, 220, 220, 220, 220,
-
220, 220, 220, 220, 220, 220, 220, 220,
-
220, 220, 220, 220, 220, 220, 220, 220,
-
220, 220, 1, 1, 1, 220, 220, 220,
-
220, 220, 220, 220, 220, 220, 220, 220,
-
220, 220, 220, 220, 220, 220, 220, 220,
-
220, 220, 220, 220, 220, 220, 220, 220,
-
220, 220, 220, 220, 220, 220, 1, 224,
-
1, 1, 1, 225, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 224, 1,
-
1, 1, 1, 1, 1, 1, 226, 1,
-
1, 1, 1, 1, 209, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 210, 1, 227, 1,
-
228, 1, 224, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 224, 1, 229, 1, 1, 1, 230,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 229, 1, 1, 1, 1, 1,
-
1, 1, 231, 1, 1, 1, 1, 1,
-
216, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
217, 1, 232, 1, 210, 1, 1, 1,
-
233, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 210, 1, 1, 1, 1,
-
1, 1, 1, 234, 1, 1, 1, 235,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 236, 1,
-
237, 1, 210, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 210, 1, 217, 1, 1, 1, 238,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 217, 1, 1, 1, 1, 1,
-
1, 1, 239, 1, 1, 1, 240, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 241, 1, 242,
-
1, 1, 1, 243, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 242, 115,
-
116, 115, 115, 115, 115, 115, 244, 1,
-
115, 115, 245, 115, 119, 115, 115, 115,
-
115, 115, 115, 115, 115, 115, 115, 115,
-
1, 120, 121, 115, 1, 115, 122, 115,
-
115, 115, 115, 115, 115, 115, 115, 115,
-
115, 115, 115, 115, 115, 115, 115, 115,
-
115, 115, 115, 115, 115, 115, 115, 115,
-
115, 1, 1, 1, 115, 115, 115, 115,
-
115, 115, 115, 115, 115, 115, 115, 115,
-
115, 115, 115, 115, 115, 115, 115, 115,
-
115, 115, 115, 115, 115, 115, 115, 115,
-
115, 115, 115, 115, 115, 1, 246, 1,
-
1, 1, 247, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 246, 125, 126,
-
125, 125, 125, 125, 125, 248, 1, 125,
-
125, 245, 125, 128, 125, 125, 125, 125,
-
125, 125, 125, 125, 125, 125, 125, 1,
-
120, 129, 125, 1, 125, 1, 125, 125,
-
125, 125, 125, 125, 125, 125, 125, 125,
-
125, 125, 125, 125, 125, 125, 125, 125,
-
125, 125, 125, 125, 125, 125, 125, 125,
-
1, 1, 1, 125, 125, 125, 125, 125,
-
125, 125, 125, 125, 125, 125, 125, 125,
-
125, 125, 125, 125, 125, 125, 125, 125,
-
125, 125, 125, 125, 125, 125, 125, 125,
-
125, 125, 125, 125, 1, 249, 1, 246,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 246, 1,
-
250, 250, 250, 250, 250, 250, 250, 250,
-
251, 1, 250, 250, 252, 250, 250, 250,
-
250, 250, 250, 250, 250, 250, 250, 250,
-
250, 250, 250, 250, 250, 250, 250, 251,
-
250, 253, 250, 250, 250, 250, 250, 250,
-
250, 250, 250, 250, 250, 250, 250, 250,
-
250, 250, 250, 250, 250, 250, 250, 250,
-
250, 250, 250, 250, 250, 250, 250, 250,
-
250, 250, 250, 250, 250, 250, 250, 250,
-
250, 250, 250, 250, 250, 250, 250, 250,
-
250, 250, 250, 250, 250, 250, 250, 250,
-
250, 250, 250, 254, 250, 250, 250, 250,
-
250, 250, 250, 250, 250, 250, 250, 250,
-
250, 250, 250, 250, 250, 250, 250, 250,
-
250, 250, 250, 250, 250, 250, 250, 250,
-
250, 250, 250, 250, 250, 250, 250, 1,
-
255, 255, 255, 255, 255, 255, 255, 255,
-
255, 1, 255, 255, 256, 255, 255, 255,
-
255, 255, 255, 255, 255, 255, 255, 255,
-
255, 255, 255, 255, 255, 255, 255, 255,
-
255, 257, 255, 255, 255, 255, 255, 255,
-
255, 255, 255, 255, 255, 255, 255, 255,
-
255, 255, 255, 255, 255, 255, 255, 255,
-
255, 255, 255, 255, 255, 255, 255, 255,
-
255, 255, 255, 255, 255, 255, 255, 255,
-
255, 255, 255, 255, 255, 255, 255, 255,
-
255, 255, 255, 255, 255, 255, 255, 255,
-
255, 255, 255, 258, 255, 255, 255, 255,
-
255, 255, 255, 255, 255, 255, 255, 255,
-
255, 255, 255, 255, 255, 255, 255, 255,
-
255, 255, 255, 255, 255, 255, 255, 255,
-
255, 255, 255, 255, 255, 255, 255, 1,
-
259, 1, 255, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 255, 1, 260, 1, 1, 1, 261,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 260, 262, 134, 262, 262, 262,
-
262, 262, 263, 1, 262, 262, 264, 262,
-
137, 262, 262, 262, 262, 262, 262, 262,
-
262, 262, 262, 262, 1, 265, 139, 262,
-
1, 262, 266, 262, 262, 262, 262, 262,
-
262, 262, 262, 262, 262, 262, 262, 262,
-
262, 262, 262, 262, 262, 262, 262, 262,
-
262, 262, 262, 262, 262, 1, 1, 1,
-
262, 262, 262, 262, 262, 262, 262, 262,
-
262, 262, 262, 262, 262, 262, 262, 262,
-
262, 262, 262, 262, 262, 262, 262, 262,
-
262, 262, 262, 262, 262, 262, 262, 262,
-
262, 1, 267, 1, 1, 1, 268, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 267, 143, 144, 143, 143, 143, 143,
-
143, 269, 1, 143, 143, 270, 143, 147,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 1, 271, 139, 143, 1,
-
143, 272, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 1, 1, 1, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
1, 273, 1, 274, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 274, 1, 275, 1, 1, 1,
-
276, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 275, 168, 169, 168, 168,
-
168, 168, 168, 277, 1, 168, 168, 278,
-
168, 279, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 1, 280, 172,
-
168, 1, 168, 281, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 1, 1,
-
1, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 1, 282, 1, 1, 1, 283,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 282, 284, 285, 284, 284, 284,
-
284, 284, 286, 1, 284, 284, 1, 284,
-
155, 284, 284, 284, 284, 284, 284, 284,
-
284, 284, 284, 284, 1, 1, 139, 284,
-
1, 284, 155, 284, 284, 284, 284, 284,
-
284, 284, 284, 284, 284, 284, 284, 284,
-
284, 284, 284, 284, 284, 284, 284, 284,
-
284, 284, 284, 284, 284, 1, 1, 1,
-
284, 284, 284, 284, 284, 284, 284, 284,
-
284, 284, 284, 284, 284, 284, 284, 284,
-
284, 284, 284, 284, 284, 284, 284, 284,
-
284, 284, 284, 284, 284, 284, 284, 284,
-
284, 1, 287, 1, 1, 1, 288, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 287, 284, 285, 284, 284, 284, 284,
-
284, 289, 1, 284, 284, 1, 284, 1,
-
284, 284, 284, 284, 284, 284, 284, 284,
-
284, 284, 284, 1, 1, 129, 284, 1,
-
284, 1, 284, 284, 284, 284, 284, 284,
-
284, 284, 284, 284, 284, 284, 284, 284,
-
284, 284, 284, 284, 284, 284, 284, 284,
-
284, 284, 284, 284, 1, 1, 1, 284,
-
284, 284, 284, 284, 284, 284, 284, 284,
-
284, 284, 284, 284, 284, 284, 284, 284,
-
284, 284, 284, 284, 284, 284, 284, 284,
-
284, 284, 284, 284, 284, 284, 284, 284,
-
1, 290, 1, 287, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 287, 1, 291, 1, 1, 1,
-
292, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 291, 284, 144, 284, 284,
-
284, 284, 284, 293, 1, 284, 284, 235,
-
284, 147, 284, 284, 284, 284, 284, 284,
-
284, 284, 284, 284, 284, 1, 236, 139,
-
284, 1, 284, 294, 284, 284, 284, 284,
-
284, 284, 284, 284, 284, 284, 284, 284,
-
284, 284, 284, 284, 284, 284, 284, 284,
-
284, 284, 284, 284, 284, 284, 1, 1,
-
1, 284, 284, 284, 284, 284, 284, 284,
-
284, 284, 284, 284, 284, 284, 284, 284,
-
284, 284, 284, 284, 284, 284, 284, 284,
-
284, 284, 284, 284, 284, 284, 284, 284,
-
284, 284, 1, 291, 1, 1, 1, 292,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 291, 143, 144, 143, 143, 143,
-
143, 143, 293, 1, 143, 143, 235, 143,
-
147, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 1, 236, 139, 143,
-
1, 143, 294, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 1, 1, 1,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 1, 295, 1, 296, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 296, 1, 297, 1, 1,
-
1, 298, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 297, 168, 169, 168,
-
168, 168, 168, 168, 299, 1, 168, 168,
-
240, 168, 279, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 1, 241,
-
172, 168, 1, 168, 300, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 1,
-
1, 1, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 1, 301, 1, 302, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 302, 1, 303,
-
1, 1, 1, 304, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 303, 305,
-
306, 305, 305, 305, 305, 305, 307, 1,
-
305, 305, 308, 305, 309, 305, 305, 305,
-
305, 305, 305, 305, 305, 305, 305, 305,
-
1, 138, 310, 305, 1, 305, 311, 305,
-
305, 305, 305, 305, 305, 305, 305, 305,
-
305, 305, 305, 305, 305, 305, 305, 305,
-
305, 305, 305, 305, 305, 305, 305, 305,
-
305, 1, 1, 1, 305, 305, 305, 305,
-
305, 305, 305, 305, 305, 305, 305, 305,
-
305, 305, 305, 305, 305, 305, 305, 305,
-
305, 305, 305, 305, 305, 305, 305, 305,
-
305, 305, 305, 305, 305, 1, 312, 1,
-
1, 1, 313, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 312, 314, 315,
-
314, 314, 314, 314, 314, 316, 1, 314,
-
314, 317, 314, 318, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 1,
-
148, 310, 314, 1, 314, 319, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
1, 1, 1, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 1, 320, 1, 321,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 321, 1,
-
322, 1, 1, 1, 323, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 322,
-
314, 315, 314, 314, 314, 314, 314, 324,
-
1, 314, 314, 1, 314, 325, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 1, 1, 310, 314, 1, 314, 325,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 1, 1, 1, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 1, 326,
-
1, 314, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
314, 1, 327, 327, 327, 327, 327, 327,
-
327, 327, 327, 1, 327, 327, 328, 327,
-
327, 327, 327, 327, 327, 327, 327, 327,
-
327, 327, 327, 327, 327, 327, 327, 327,
-
327, 327, 327, 329, 327, 327, 327, 327,
-
327, 327, 327, 327, 327, 327, 327, 327,
-
327, 327, 327, 327, 327, 327, 327, 327,
-
327, 327, 327, 327, 327, 327, 327, 327,
-
327, 327, 327, 327, 327, 327, 327, 327,
-
327, 327, 327, 327, 327, 327, 327, 327,
-
327, 327, 327, 327, 327, 327, 327, 327,
-
327, 327, 327, 327, 327, 330, 327, 327,
-
327, 327, 327, 327, 327, 327, 327, 327,
-
327, 327, 327, 327, 327, 327, 327, 327,
-
327, 327, 327, 327, 327, 327, 327, 327,
-
327, 327, 327, 327, 327, 327, 327, 327,
-
327, 1, 331, 331, 331, 331, 331, 331,
-
331, 331, 331, 1, 331, 331, 332, 331,
-
331, 331, 331, 331, 331, 331, 331, 331,
-
331, 331, 331, 331, 331, 331, 331, 331,
-
331, 331, 331, 333, 331, 331, 331, 331,
-
331, 331, 331, 331, 331, 331, 331, 331,
-
331, 331, 331, 331, 331, 331, 331, 331,
-
331, 331, 331, 331, 331, 331, 331, 331,
-
331, 331, 331, 331, 331, 331, 331, 331,
-
331, 331, 331, 331, 331, 331, 331, 331,
-
331, 331, 331, 331, 331, 331, 331, 331,
-
331, 331, 331, 331, 331, 334, 331, 331,
-
331, 331, 331, 331, 331, 331, 331, 331,
-
331, 331, 331, 331, 331, 331, 331, 331,
-
331, 331, 331, 331, 331, 331, 331, 331,
-
331, 331, 331, 331, 331, 331, 331, 331,
-
331, 1, 335, 1, 331, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 331, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 331, 336, 1,
-
1, 1, 337, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 336, 338, 339,
-
338, 338, 338, 338, 338, 340, 1, 338,
-
338, 1, 338, 341, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 1,
-
1, 342, 338, 1, 338, 341, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
1, 1, 1, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 1, 343, 1, 1,
-
1, 344, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 343, 314, 315, 314,
-
314, 314, 314, 314, 345, 1, 314, 314,
-
1, 314, 325, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 1, 1,
-
310, 314, 1, 314, 325, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 1,
-
1, 1, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 1, 346, 1, 1, 1,
-
347, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 346, 314, 315, 314, 314,
-
314, 314, 314, 348, 1, 314, 314, 1,
-
314, 1, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 1, 1, 349,
-
314, 1, 314, 1, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 1, 1,
-
1, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 1, 350, 1, 346, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 346, 1, 351, 1,
-
1, 1, 352, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 351, 338, 339,
-
338, 338, 338, 338, 338, 353, 1, 338,
-
338, 1, 338, 1, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 1,
-
1, 354, 338, 1, 338, 1, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
1, 1, 1, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 1, 355, 1, 1,
-
1, 356, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 355, 357, 358, 357,
-
357, 357, 357, 357, 359, 1, 357, 357,
-
360, 357, 361, 357, 357, 357, 357, 357,
-
357, 357, 357, 357, 357, 357, 1, 1,
-
1, 357, 1, 357, 362, 357, 357, 357,
-
357, 357, 357, 357, 357, 357, 357, 357,
-
357, 357, 357, 357, 357, 357, 357, 357,
-
357, 357, 357, 357, 357, 357, 357, 1,
-
1, 1, 357, 357, 357, 357, 357, 357,
-
357, 357, 357, 357, 357, 357, 357, 357,
-
357, 357, 357, 357, 357, 357, 357, 357,
-
357, 357, 357, 357, 357, 357, 357, 357,
-
357, 357, 357, 1, 363, 1, 1, 1,
-
364, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 363, 357, 358, 357, 357,
-
357, 357, 357, 365, 1, 357, 357, 366,
-
357, 361, 357, 357, 357, 357, 357, 357,
-
357, 357, 357, 357, 357, 1, 1, 1,
-
357, 1, 357, 367, 357, 357, 357, 357,
-
357, 357, 357, 357, 357, 357, 357, 357,
-
357, 357, 357, 357, 357, 357, 357, 357,
-
357, 357, 357, 357, 357, 357, 1, 1,
-
1, 357, 357, 357, 357, 357, 357, 357,
-
357, 357, 357, 357, 357, 357, 357, 357,
-
357, 357, 357, 357, 357, 357, 357, 357,
-
357, 357, 357, 357, 357, 357, 357, 357,
-
357, 357, 1, 368, 1, 363, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 363, 1, 369, 1,
-
1, 1, 370, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 369, 371, 372,
-
371, 371, 371, 371, 371, 373, 1, 371,
-
371, 1, 371, 374, 371, 371, 371, 371,
-
371, 371, 371, 371, 371, 371, 371, 1,
-
1, 1, 371, 375, 371, 376, 371, 371,
-
371, 371, 371, 371, 371, 371, 371, 371,
-
371, 371, 371, 371, 371, 371, 371, 371,
-
371, 371, 371, 371, 371, 371, 371, 371,
-
1, 1, 1, 371, 371, 371, 371, 371,
-
371, 371, 371, 371, 371, 371, 371, 371,
-
371, 371, 371, 371, 371, 371, 371, 371,
-
371, 371, 371, 371, 371, 371, 371, 371,
-
371, 371, 371, 371, 1, 377, 1, 1,
-
1, 378, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 377, 1, 1, 1,
-
1, 1, 1, 1, 379, 1, 1, 1,
-
1, 1, 380, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 381, 1, 382, 1, 383, 1,
-
377, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 377,
-
1, 384, 1, 1, 1, 385, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
384, 1, 1, 1, 1, 1, 1, 1,
-
386, 1, 1, 1, 1, 1, 387, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 388, 1,
-
389, 1, 380, 1, 1, 1, 390, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 380, 391, 392, 391, 391, 391, 391,
-
391, 393, 1, 391, 391, 1, 391, 1,
-
391, 391, 391, 391, 391, 391, 391, 391,
-
391, 391, 391, 1, 1, 1, 391, 1,
-
391, 1, 391, 391, 391, 391, 391, 391,
-
391, 391, 391, 391, 391, 391, 391, 391,
-
391, 391, 391, 391, 391, 391, 391, 391,
-
391, 391, 391, 391, 1, 1, 1, 391,
-
391, 391, 391, 391, 391, 391, 391, 391,
-
391, 391, 391, 391, 391, 391, 391, 391,
-
391, 391, 391, 391, 391, 391, 391, 391,
-
391, 391, 391, 391, 391, 391, 391, 391,
-
1, 394, 1, 380, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 380, 1, 395, 1, 1, 1,
-
396, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 395, 391, 1, 391, 391,
-
391, 391, 391, 397, 1, 391, 391, 1,
-
391, 380, 391, 391, 391, 391, 391, 391,
-
391, 391, 391, 391, 391, 1, 1, 1,
-
391, 381, 391, 398, 391, 391, 391, 391,
-
391, 391, 391, 391, 391, 391, 391, 391,
-
391, 391, 391, 391, 391, 391, 391, 391,
-
391, 391, 391, 391, 391, 391, 1, 1,
-
1, 391, 391, 391, 391, 391, 391, 391,
-
391, 391, 391, 391, 391, 391, 391, 391,
-
391, 391, 391, 391, 391, 391, 391, 391,
-
391, 391, 391, 391, 391, 391, 391, 391,
-
391, 391, 1, 395, 1, 1, 1, 396,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 395, 1, 1, 1, 1, 1,
-
1, 1, 397, 1, 1, 1, 1, 1,
-
380, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
381, 1, 398, 1, 399, 1, 395, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 395, 1, 400,
-
1, 1, 1, 401, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 400, 1,
-
1, 1, 1, 1, 1, 1, 402, 1,
-
1, 1, 1, 1, 387, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 388, 1, 403, 1,
-
381, 1, 1, 1, 404, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 381,
-
1, 1, 1, 1, 1, 1, 1, 405,
-
1, 1, 1, 406, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 236, 1, 407, 1, 381, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 381, 1, 388,
-
1, 1, 1, 408, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 388, 1,
-
1, 1, 1, 1, 1, 1, 409, 1,
-
1, 1, 410, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 241, 1, 411, 1, 1, 1, 412,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 411, 413, 414, 413, 413, 413,
-
413, 413, 415, 1, 413, 413, 1, 413,
-
416, 413, 413, 413, 413, 413, 413, 413,
-
413, 413, 413, 413, 1, 1, 417, 413,
-
1, 413, 418, 413, 413, 413, 413, 413,
-
413, 413, 413, 413, 413, 413, 413, 413,
-
413, 413, 413, 413, 413, 413, 413, 413,
-
413, 413, 413, 413, 413, 1, 1, 1,
-
413, 413, 413, 413, 413, 413, 413, 413,
-
413, 413, 413, 413, 413, 413, 413, 413,
-
413, 413, 413, 413, 413, 413, 413, 413,
-
413, 413, 413, 413, 413, 413, 413, 413,
-
413, 1, 419, 1, 1, 1, 420, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 419, 421, 422, 421, 421, 421, 421,
-
421, 423, 1, 421, 421, 1, 421, 424,
-
421, 421, 421, 421, 421, 421, 421, 421,
-
421, 421, 421, 1, 1, 349, 421, 1,
-
421, 1, 421, 421, 421, 421, 421, 421,
-
421, 421, 421, 421, 421, 421, 421, 421,
-
421, 421, 421, 421, 421, 421, 421, 421,
-
421, 421, 421, 421, 1, 1, 1, 421,
-
421, 421, 421, 421, 421, 421, 421, 421,
-
421, 421, 421, 421, 421, 421, 421, 421,
-
421, 421, 421, 421, 421, 421, 421, 421,
-
421, 421, 421, 421, 421, 421, 421, 421,
-
1, 425, 1, 419, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 419, 1, 426, 426, 426, 426,
-
426, 426, 426, 426, 427, 1, 426, 426,
-
428, 426, 426, 426, 426, 426, 426, 426,
-
426, 426, 426, 426, 426, 426, 426, 426,
-
426, 426, 426, 427, 426, 429, 426, 426,
-
426, 426, 426, 426, 426, 426, 426, 426,
-
426, 426, 426, 426, 426, 426, 426, 426,
-
426, 426, 426, 426, 426, 426, 426, 426,
-
426, 426, 426, 426, 426, 426, 426, 426,
-
426, 426, 426, 426, 426, 426, 426, 426,
-
426, 426, 426, 426, 426, 426, 426, 426,
-
426, 426, 426, 426, 426, 426, 426, 430,
-
426, 426, 426, 426, 426, 426, 426, 426,
-
426, 426, 426, 426, 426, 426, 426, 426,
-
426, 426, 426, 426, 426, 426, 426, 426,
-
426, 426, 426, 426, 426, 426, 426, 426,
-
426, 426, 426, 1, 431, 431, 431, 431,
-
431, 431, 431, 431, 431, 1, 431, 431,
-
432, 431, 431, 431, 431, 431, 431, 431,
-
431, 431, 431, 431, 431, 431, 431, 431,
-
431, 431, 431, 431, 431, 433, 431, 431,
-
431, 431, 431, 431, 431, 431, 431, 431,
-
431, 431, 431, 431, 431, 431, 431, 431,
-
431, 431, 431, 431, 431, 431, 431, 431,
-
431, 431, 431, 431, 431, 431, 431, 431,
-
431, 431, 431, 431, 431, 431, 431, 431,
-
431, 431, 431, 431, 431, 431, 431, 431,
-
431, 431, 431, 431, 431, 431, 431, 434,
-
431, 431, 431, 431, 431, 431, 431, 431,
-
431, 431, 431, 431, 431, 431, 431, 431,
-
431, 431, 431, 431, 431, 431, 431, 431,
-
431, 431, 431, 431, 431, 431, 431, 431,
-
431, 431, 431, 1, 435, 1, 431, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 431, 1, 436,
-
1, 1, 1, 437, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 436, 438,
-
306, 438, 438, 438, 438, 438, 439, 1,
-
438, 438, 440, 438, 309, 438, 438, 438,
-
438, 438, 438, 438, 438, 438, 438, 438,
-
1, 265, 310, 438, 1, 438, 441, 438,
-
438, 438, 438, 438, 438, 438, 438, 438,
-
438, 438, 438, 438, 438, 438, 438, 438,
-
438, 438, 438, 438, 438, 438, 438, 438,
-
438, 1, 1, 1, 438, 438, 438, 438,
-
438, 438, 438, 438, 438, 438, 438, 438,
-
438, 438, 438, 438, 438, 438, 438, 438,
-
438, 438, 438, 438, 438, 438, 438, 438,
-
438, 438, 438, 438, 438, 1, 442, 1,
-
1, 1, 443, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 442, 314, 315,
-
314, 314, 314, 314, 314, 444, 1, 314,
-
314, 445, 314, 318, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 1,
-
271, 310, 314, 1, 314, 446, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
1, 1, 1, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 1, 447, 1, 448,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 448, 1,
-
449, 1, 1, 1, 450, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 449,
-
338, 339, 338, 338, 338, 338, 338, 451,
-
1, 338, 338, 452, 338, 453, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 1, 280, 342, 338, 1, 338, 454,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 1, 1, 1, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 1, 455,
-
1, 1, 1, 456, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 455, 457,
-
458, 457, 457, 457, 457, 457, 459, 1,
-
457, 457, 1, 457, 325, 457, 457, 457,
-
457, 457, 457, 457, 457, 457, 457, 457,
-
1, 1, 310, 457, 1, 457, 325, 457,
-
457, 457, 457, 457, 457, 457, 457, 457,
-
457, 457, 457, 457, 457, 457, 457, 457,
-
457, 457, 457, 457, 457, 457, 457, 457,
-
457, 1, 1, 1, 457, 457, 457, 457,
-
457, 457, 457, 457, 457, 457, 457, 457,
-
457, 457, 457, 457, 457, 457, 457, 457,
-
457, 457, 457, 457, 457, 457, 457, 457,
-
457, 457, 457, 457, 457, 1, 460, 1,
-
1, 1, 461, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 460, 457, 458,
-
457, 457, 457, 457, 457, 462, 1, 457,
-
457, 1, 457, 1, 457, 457, 457, 457,
-
457, 457, 457, 457, 457, 457, 457, 1,
-
1, 349, 457, 1, 457, 1, 457, 457,
-
457, 457, 457, 457, 457, 457, 457, 457,
-
457, 457, 457, 457, 457, 457, 457, 457,
-
457, 457, 457, 457, 457, 457, 457, 457,
-
1, 1, 1, 457, 457, 457, 457, 457,
-
457, 457, 457, 457, 457, 457, 457, 457,
-
457, 457, 457, 457, 457, 457, 457, 457,
-
457, 457, 457, 457, 457, 457, 457, 457,
-
457, 457, 457, 457, 1, 463, 1, 460,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 460, 1,
-
464, 1, 1, 1, 465, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 464,
-
457, 315, 457, 457, 457, 457, 457, 466,
-
1, 457, 457, 406, 457, 318, 457, 457,
-
457, 457, 457, 457, 457, 457, 457, 457,
-
457, 1, 236, 310, 457, 1, 457, 467,
-
457, 457, 457, 457, 457, 457, 457, 457,
-
457, 457, 457, 457, 457, 457, 457, 457,
-
457, 457, 457, 457, 457, 457, 457, 457,
-
457, 457, 1, 1, 1, 457, 457, 457,
-
457, 457, 457, 457, 457, 457, 457, 457,
-
457, 457, 457, 457, 457, 457, 457, 457,
-
457, 457, 457, 457, 457, 457, 457, 457,
-
457, 457, 457, 457, 457, 457, 1, 464,
-
1, 1, 1, 465, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 464, 314,
-
315, 314, 314, 314, 314, 314, 466, 1,
-
314, 314, 406, 314, 318, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
1, 236, 310, 314, 1, 314, 467, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 1, 1, 1, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 1, 468, 1,
-
469, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 469,
-
1, 470, 1, 1, 1, 471, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
470, 338, 339, 338, 338, 338, 338, 338,
-
472, 1, 338, 338, 410, 338, 453, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 1, 241, 342, 338, 1, 338,
-
473, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 1, 1, 1, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 1,
-
474, 1, 1, 1, 475, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 474,
-
476, 477, 476, 476, 476, 476, 476, 478,
-
1, 476, 476, 1, 476, 479, 476, 476,
-
476, 476, 476, 476, 476, 476, 476, 476,
-
476, 1, 1, 310, 476, 1, 476, 325,
-
476, 476, 476, 476, 476, 476, 476, 476,
-
476, 476, 476, 476, 476, 476, 476, 476,
-
476, 476, 476, 476, 476, 476, 476, 476,
-
476, 476, 480, 1, 1, 476, 476, 476,
-
476, 476, 476, 476, 476, 476, 476, 476,
-
476, 476, 476, 476, 476, 476, 476, 476,
-
476, 476, 476, 476, 476, 476, 476, 476,
-
476, 476, 476, 476, 476, 476, 1, 481,
-
1, 1, 1, 482, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 481, 483,
-
484, 483, 483, 483, 483, 483, 485, 1,
-
483, 483, 1, 483, 486, 483, 483, 483,
-
483, 483, 483, 483, 483, 483, 483, 483,
-
1, 1, 349, 483, 1, 483, 1, 483,
-
483, 483, 483, 483, 483, 483, 483, 483,
-
483, 483, 483, 483, 483, 483, 483, 483,
-
483, 483, 483, 483, 483, 483, 483, 483,
-
483, 487, 1, 1, 483, 483, 483, 483,
-
483, 483, 483, 483, 483, 483, 483, 483,
-
483, 483, 483, 483, 483, 483, 483, 483,
-
483, 483, 483, 483, 483, 483, 483, 483,
-
483, 483, 483, 483, 483, 1, 488, 1,
-
481, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 481,
-
1, 489, 1, 1, 1, 490, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
489, 483, 484, 483, 483, 483, 483, 483,
-
491, 1, 483, 483, 492, 483, 493, 483,
-
483, 483, 483, 483, 483, 483, 483, 483,
-
483, 483, 1, 494, 310, 483, 1, 483,
-
325, 483, 483, 483, 483, 483, 483, 483,
-
483, 483, 483, 483, 483, 483, 483, 483,
-
483, 483, 483, 483, 483, 483, 483, 483,
-
483, 483, 483, 1, 1, 1, 483, 483,
-
483, 483, 483, 483, 483, 483, 483, 483,
-
483, 483, 483, 483, 483, 483, 483, 483,
-
483, 483, 483, 483, 483, 483, 483, 483,
-
483, 483, 483, 483, 483, 483, 483, 1,
-
489, 1, 1, 1, 490, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 489,
-
314, 315, 314, 314, 314, 314, 314, 491,
-
1, 314, 314, 492, 314, 495, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 1, 494, 310, 314, 1, 314, 325,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 1, 1, 1, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 1, 496,
-
1, 497, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
497, 1, 498, 1, 1, 1, 499, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 498, 338, 339, 338, 338, 338, 338,
-
338, 500, 1, 338, 338, 501, 338, 502,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 1, 503, 342, 338, 1,
-
338, 341, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 1, 1, 1, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
1, 504, 1, 1, 1, 505, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
504, 506, 315, 506, 506, 506, 506, 506,
-
507, 1, 506, 506, 1, 506, 325, 506,
-
506, 506, 506, 506, 506, 506, 506, 506,
-
506, 506, 1, 1, 310, 506, 1, 506,
-
325, 506, 506, 506, 506, 506, 506, 506,
-
506, 506, 506, 506, 506, 506, 506, 506,
-
506, 506, 506, 506, 506, 506, 506, 506,
-
506, 506, 506, 1, 1, 1, 506, 506,
-
506, 506, 506, 506, 506, 506, 506, 506,
-
506, 506, 506, 506, 506, 506, 506, 506,
-
506, 506, 506, 506, 506, 506, 506, 506,
-
506, 506, 506, 506, 506, 506, 506, 1,
-
508, 1, 1, 1, 509, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 508,
-
506, 315, 506, 506, 506, 506, 506, 510,
-
1, 506, 506, 1, 506, 1, 506, 506,
-
506, 506, 506, 506, 506, 506, 506, 506,
-
506, 1, 1, 349, 506, 1, 506, 1,
-
506, 506, 506, 506, 506, 506, 506, 506,
-
506, 506, 506, 506, 506, 506, 506, 506,
-
506, 506, 506, 506, 506, 506, 506, 506,
-
506, 506, 1, 1, 1, 506, 506, 506,
-
506, 506, 506, 506, 506, 506, 506, 506,
-
506, 506, 506, 506, 506, 506, 506, 506,
-
506, 506, 506, 506, 506, 506, 506, 506,
-
506, 506, 506, 506, 506, 506, 1, 511,
-
1, 508, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
508, 1, 489, 1, 1, 1, 490, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 489, 506, 315, 506, 506, 506, 506,
-
506, 491, 1, 506, 506, 492, 506, 495,
-
506, 506, 506, 506, 506, 506, 506, 506,
-
506, 506, 506, 1, 494, 310, 506, 1,
-
506, 325, 506, 506, 506, 506, 506, 506,
-
506, 506, 506, 506, 506, 506, 506, 506,
-
506, 506, 506, 506, 506, 506, 506, 506,
-
506, 506, 506, 506, 1, 1, 1, 506,
-
506, 506, 506, 506, 506, 506, 506, 506,
-
506, 506, 506, 506, 506, 506, 506, 506,
-
506, 506, 506, 506, 506, 506, 506, 506,
-
506, 506, 506, 506, 506, 506, 506, 506,
-
1, 512, 1, 1, 1, 513, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
512, 514, 339, 514, 514, 514, 514, 514,
-
515, 1, 514, 514, 1, 514, 1, 514,
-
514, 514, 514, 514, 514, 514, 514, 514,
-
514, 514, 1, 1, 354, 514, 1, 514,
-
1, 514, 514, 514, 514, 514, 514, 514,
-
514, 514, 514, 514, 514, 514, 514, 514,
-
514, 514, 514, 514, 514, 514, 514, 514,
-
514, 514, 514, 1, 1, 1, 514, 514,
-
514, 514, 514, 514, 514, 514, 514, 514,
-
514, 514, 514, 514, 514, 514, 514, 514,
-
514, 514, 514, 514, 514, 514, 514, 514,
-
514, 514, 514, 514, 514, 514, 514, 1,
-
516, 516, 516, 516, 516, 516, 516, 516,
-
517, 1, 516, 516, 518, 516, 516, 516,
-
516, 516, 516, 516, 516, 516, 516, 516,
-
516, 516, 516, 516, 516, 516, 516, 517,
-
516, 329, 516, 516, 516, 516, 516, 516,
-
516, 516, 516, 516, 516, 516, 516, 516,
-
516, 516, 516, 516, 516, 516, 516, 516,
-
516, 516, 516, 516, 516, 516, 516, 516,
-
516, 516, 516, 516, 516, 516, 516, 516,
-
516, 516, 516, 516, 516, 516, 516, 516,
-
516, 516, 516, 516, 516, 516, 516, 516,
-
516, 516, 516, 519, 516, 516, 516, 516,
-
516, 516, 516, 516, 516, 516, 516, 516,
-
516, 516, 516, 516, 516, 516, 516, 516,
-
516, 516, 516, 516, 516, 516, 516, 516,
-
516, 516, 516, 516, 516, 516, 516, 1,
-
520, 520, 520, 520, 520, 520, 520, 520,
-
520, 1, 520, 520, 521, 520, 520, 520,
-
520, 520, 520, 520, 520, 520, 520, 520,
-
520, 520, 520, 520, 520, 520, 520, 520,
-
520, 522, 520, 520, 520, 520, 520, 520,
-
520, 520, 520, 520, 520, 520, 520, 520,
-
520, 520, 520, 520, 520, 520, 520, 520,
-
520, 520, 520, 520, 520, 520, 520, 520,
-
520, 520, 520, 520, 520, 520, 520, 520,
-
520, 520, 520, 520, 520, 520, 520, 520,
-
520, 520, 520, 520, 520, 520, 520, 520,
-
520, 520, 520, 523, 520, 520, 520, 520,
-
520, 520, 520, 520, 520, 520, 520, 520,
-
520, 520, 520, 520, 520, 520, 520, 520,
-
520, 520, 520, 520, 520, 520, 520, 520,
-
520, 520, 520, 520, 520, 520, 520, 1,
-
524, 1, 520, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 520, 1, 525, 1, 1, 1, 526,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 525, 527, 484, 527, 527, 527,
-
527, 527, 528, 1, 527, 527, 492, 527,
-
529, 527, 527, 527, 527, 527, 527, 527,
-
527, 527, 527, 527, 1, 494, 310, 527,
-
1, 527, 325, 527, 527, 527, 527, 527,
-
527, 527, 527, 527, 527, 527, 527, 527,
-
527, 527, 527, 527, 527, 527, 527, 527,
-
527, 527, 527, 527, 527, 1, 1, 1,
-
527, 527, 527, 527, 527, 527, 527, 527,
-
527, 527, 527, 527, 527, 527, 527, 527,
-
527, 527, 527, 527, 527, 527, 527, 527,
-
527, 527, 527, 527, 527, 527, 527, 527,
-
527, 1, 525, 1, 1, 1, 526, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 525, 314, 315, 314, 314, 314, 314,
-
314, 528, 1, 314, 314, 492, 314, 325,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 1, 494, 310, 314, 1,
-
314, 325, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 1, 1, 1, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
1, 530, 1, 531, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 531, 1, 532, 1, 1, 1,
-
533, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 532, 338, 339, 338, 338,
-
338, 338, 338, 534, 1, 338, 338, 501,
-
338, 341, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 1, 503, 342,
-
338, 1, 338, 341, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 1, 1,
-
1, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 1, 343, 1, 1, 1, 344,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 343, 527, 484, 527, 527, 527,
-
527, 527, 345, 1, 527, 527, 1, 527,
-
529, 527, 527, 527, 527, 527, 527, 527,
-
527, 527, 527, 527, 1, 1, 310, 527,
-
1, 527, 325, 527, 527, 527, 527, 527,
-
527, 527, 527, 527, 527, 527, 527, 527,
-
527, 527, 527, 527, 527, 527, 527, 527,
-
527, 527, 527, 527, 527, 1, 1, 1,
-
527, 527, 527, 527, 527, 527, 527, 527,
-
527, 527, 527, 527, 527, 527, 527, 527,
-
527, 527, 527, 527, 527, 527, 527, 527,
-
527, 527, 527, 527, 527, 527, 527, 527,
-
527, 1, 520, 520, 520, 520, 520, 520,
-
520, 520, 520, 520, 520, 520, 520, 520,
-
520, 520, 520, 520, 520, 520, 520, 520,
-
520, 520, 520, 520, 520, 520, 520, 520,
-
520, 520, 520, 520, 520, 520, 520, 520,
-
520, 520, 520, 520, 520, 520, 520, 520,
-
520, 520, 520, 520, 520, 520, 520, 520,
-
520, 520, 520, 520, 520, 520, 520, 520,
-
520, 520, 520, 520, 520, 520, 520, 520,
-
520, 520, 520, 520, 520, 520, 520, 520,
-
520, 520, 520, 520, 520, 520, 520, 520,
-
520, 520, 520, 520, 520, 520, 520, 520,
-
520, 520, 520, 520, 520, 520, 520, 520,
-
520, 520, 520, 520, 520, 520, 520, 520,
-
520, 520, 520, 520, 520, 520, 520, 520,
-
520, 520, 520, 520, 520, 520, 520, 520,
-
520, 520, 1, 520, 520, 520, 520, 520,
-
520, 520, 520, 535, 1, 520, 520, 536,
-
520, 520, 520, 520, 520, 520, 520, 520,
-
520, 520, 520, 520, 520, 520, 520, 520,
-
520, 520, 535, 520, 333, 520, 520, 520,
-
520, 520, 520, 520, 520, 520, 520, 520,
-
520, 520, 520, 520, 520, 520, 520, 520,
-
520, 520, 520, 520, 520, 520, 520, 520,
-
520, 520, 520, 520, 520, 520, 520, 520,
-
520, 520, 520, 520, 520, 520, 520, 520,
-
520, 520, 520, 520, 520, 520, 520, 520,
-
520, 520, 520, 520, 520, 520, 523, 520,
-
520, 520, 520, 520, 520, 520, 520, 520,
-
520, 520, 520, 520, 520, 520, 520, 520,
-
520, 520, 520, 520, 520, 520, 520, 520,
-
520, 520, 520, 520, 520, 520, 520, 520,
-
520, 520, 1, 537, 1, 535, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 535, 1, 504, 1,
-
1, 1, 505, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 504, 483, 484,
-
483, 483, 483, 483, 483, 507, 1, 483,
-
483, 1, 483, 529, 483, 483, 483, 483,
-
483, 483, 483, 483, 483, 483, 483, 1,
-
1, 310, 483, 1, 483, 325, 483, 483,
-
483, 483, 483, 483, 483, 483, 483, 483,
-
483, 483, 483, 483, 483, 483, 483, 483,
-
483, 483, 483, 483, 483, 483, 483, 483,
-
1, 1, 1, 483, 483, 483, 483, 483,
-
483, 483, 483, 483, 483, 483, 483, 483,
-
483, 483, 483, 483, 483, 483, 483, 483,
-
483, 483, 483, 483, 483, 483, 483, 483,
-
483, 483, 483, 483, 1, 538, 1, 1,
-
1, 539, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 538, 540, 541, 540,
-
540, 540, 540, 540, 542, 1, 540, 540,
-
1, 540, 543, 540, 540, 540, 540, 540,
-
540, 540, 540, 540, 540, 540, 1, 1,
-
354, 540, 1, 540, 1, 540, 540, 540,
-
540, 540, 540, 540, 540, 540, 540, 540,
-
540, 540, 540, 540, 540, 540, 540, 540,
-
540, 540, 540, 540, 540, 540, 540, 544,
-
1, 1, 540, 540, 540, 540, 540, 540,
-
540, 540, 540, 540, 540, 540, 540, 540,
-
540, 540, 540, 540, 540, 540, 540, 540,
-
540, 540, 540, 540, 540, 540, 540, 540,
-
540, 540, 540, 1, 545, 546, 545, 545,
-
545, 545, 545, 1, 1, 545, 545, 1,
-
545, 486, 545, 545, 545, 545, 545, 545,
-
545, 545, 545, 545, 545, 1, 1, 1,
-
545, 1, 545, 1, 545, 545, 545, 545,
-
545, 545, 545, 545, 545, 545, 545, 545,
-
545, 545, 545, 545, 545, 545, 545, 545,
-
545, 545, 545, 545, 545, 545, 1, 1,
-
1, 545, 545, 545, 545, 545, 545, 545,
-
545, 545, 545, 545, 545, 545, 545, 545,
-
545, 545, 545, 545, 545, 545, 545, 545,
-
545, 545, 545, 545, 545, 545, 545, 545,
-
545, 545, 1, 547, 1, 1, 1, 548,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 547, 545, 546, 545, 545, 545,
-
545, 545, 549, 1, 545, 545, 492, 545,
-
486, 545, 545, 545, 545, 545, 545, 545,
-
545, 545, 545, 545, 1, 494, 1, 545,
-
1, 545, 1, 545, 545, 545, 545, 545,
-
545, 545, 545, 545, 545, 545, 545, 545,
-
545, 545, 545, 545, 545, 545, 545, 545,
-
545, 545, 545, 545, 545, 1, 1, 1,
-
545, 545, 545, 545, 545, 545, 545, 545,
-
545, 545, 545, 545, 545, 545, 545, 545,
-
545, 545, 545, 545, 545, 545, 545, 545,
-
545, 545, 545, 545, 545, 545, 545, 545,
-
545, 1, 547, 1, 1, 1, 548, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 547, 1, 1, 1, 1, 1, 1,
-
1, 549, 1, 1, 1, 492, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 494, 1, 550, 1,
-
547, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 547,
-
1, 551, 1, 1, 1, 552, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
551, 1, 1, 1, 1, 1, 1, 1,
-
553, 1, 1, 1, 501, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 503, 1, 554, 554, 554,
-
554, 554, 554, 554, 554, 546, 1, 554,
-
554, 555, 554, 554, 554, 554, 554, 554,
-
554, 554, 554, 554, 554, 554, 554, 554,
-
554, 554, 554, 554, 546, 554, 1, 554,
-
554, 554, 554, 554, 554, 554, 554, 554,
-
554, 554, 554, 554, 554, 554, 554, 554,
-
554, 554, 554, 554, 554, 554, 554, 554,
-
554, 554, 554, 554, 554, 554, 554, 554,
-
554, 554, 554, 554, 554, 554, 554, 554,
-
554, 554, 554, 554, 554, 554, 554, 554,
-
554, 554, 554, 554, 554, 554, 554, 554,
-
556, 554, 554, 554, 554, 554, 554, 554,
-
554, 554, 554, 554, 554, 554, 554, 554,
-
554, 554, 554, 554, 554, 554, 554, 554,
-
554, 554, 554, 554, 554, 554, 554, 554,
-
554, 554, 554, 554, 1, 554, 554, 554,
-
554, 554, 554, 554, 554, 554, 1, 554,
-
554, 557, 554, 554, 554, 554, 554, 554,
-
554, 554, 554, 554, 554, 554, 554, 554,
-
554, 554, 554, 554, 554, 554, 545, 554,
-
554, 554, 554, 554, 554, 554, 554, 554,
-
554, 554, 554, 554, 554, 554, 554, 554,
-
554, 554, 554, 554, 554, 554, 554, 554,
-
554, 554, 554, 554, 554, 554, 554, 554,
-
554, 554, 554, 554, 554, 554, 554, 554,
-
554, 554, 554, 554, 554, 554, 554, 554,
-
554, 554, 554, 554, 554, 554, 554, 554,
-
556, 554, 554, 554, 554, 554, 554, 554,
-
554, 554, 554, 554, 554, 554, 554, 554,
-
554, 554, 554, 554, 554, 554, 554, 554,
-
554, 554, 554, 554, 554, 554, 554, 554,
-
554, 554, 554, 554, 1, 558, 1, 554,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 554, 1,
-
554, 554, 554, 554, 554, 554, 554, 554,
-
554, 554, 554, 554, 554, 554, 554, 554,
-
554, 554, 554, 554, 554, 554, 554, 554,
-
554, 554, 554, 554, 554, 554, 554, 554,
-
554, 554, 554, 554, 554, 554, 554, 554,
-
554, 554, 554, 554, 554, 554, 554, 554,
-
554, 554, 554, 554, 554, 554, 554, 554,
-
554, 554, 554, 554, 554, 554, 554, 554,
-
554, 554, 554, 554, 554, 554, 554, 554,
-
554, 554, 554, 554, 554, 554, 554, 554,
-
554, 554, 554, 554, 554, 554, 554, 554,
-
554, 554, 554, 554, 554, 554, 554, 554,
-
554, 554, 554, 554, 554, 554, 554, 554,
-
554, 554, 554, 554, 554, 554, 554, 554,
-
554, 554, 554, 554, 554, 554, 554, 554,
-
554, 554, 554, 554, 554, 554, 554, 554,
-
1, 559, 1, 546, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 546, 1, 487, 487, 487, 487,
-
487, 487, 487, 487, 487, 1, 487, 487,
-
560, 487, 487, 487, 487, 487, 487, 487,
-
487, 487, 487, 487, 487, 487, 487, 487,
-
487, 487, 487, 487, 487, 487, 487, 487,
-
487, 487, 487, 487, 487, 487, 487, 487,
-
487, 487, 487, 487, 487, 487, 487, 487,
-
487, 487, 487, 487, 487, 487, 487, 487,
-
487, 487, 487, 487, 487, 487, 487, 487,
-
487, 487, 487, 487, 487, 487, 487, 487,
-
487, 487, 487, 487, 487, 487, 487, 487,
-
487, 487, 487, 487, 487, 487, 1, 561,
-
547, 487, 487, 487, 487, 487, 487, 487,
-
487, 487, 487, 487, 487, 487, 487, 487,
-
487, 487, 487, 487, 487, 487, 487, 487,
-
487, 487, 487, 487, 487, 487, 487, 487,
-
487, 487, 487, 1, 562, 1, 487, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 487, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 487,
-
563, 563, 563, 563, 563, 563, 563, 563,
-
563, 1, 563, 563, 564, 563, 563, 563,
-
563, 563, 563, 563, 563, 563, 563, 563,
-
563, 563, 563, 563, 563, 563, 563, 563,
-
563, 565, 563, 563, 563, 563, 563, 563,
-
563, 563, 563, 563, 563, 563, 563, 563,
-
563, 563, 563, 563, 563, 563, 563, 563,
-
563, 563, 563, 563, 563, 563, 563, 563,
-
563, 563, 563, 563, 563, 563, 563, 563,
-
563, 563, 563, 563, 563, 563, 563, 563,
-
563, 563, 563, 563, 563, 563, 563, 563,
-
563, 563, 563, 566, 563, 563, 563, 563,
-
563, 563, 563, 563, 563, 563, 563, 563,
-
563, 563, 563, 563, 563, 563, 563, 563,
-
563, 563, 563, 563, 563, 563, 563, 563,
-
563, 563, 563, 563, 563, 563, 563, 1,
-
567, 567, 567, 567, 567, 567, 567, 567,
-
567, 1, 567, 567, 568, 567, 567, 567,
-
567, 567, 567, 567, 567, 567, 567, 567,
-
567, 567, 567, 567, 567, 567, 567, 567,
-
567, 569, 567, 567, 567, 567, 567, 567,
-
567, 567, 567, 567, 567, 567, 567, 567,
-
567, 567, 567, 567, 567, 567, 567, 567,
-
567, 567, 567, 567, 567, 567, 567, 567,
-
567, 567, 567, 567, 567, 567, 567, 567,
-
567, 567, 567, 567, 567, 567, 567, 567,
-
567, 567, 567, 567, 567, 567, 567, 567,
-
567, 567, 567, 570, 567, 567, 567, 567,
-
567, 567, 567, 567, 567, 567, 567, 567,
-
567, 567, 567, 567, 567, 567, 567, 567,
-
567, 567, 567, 567, 567, 567, 567, 567,
-
567, 567, 567, 567, 567, 567, 567, 1,
-
571, 1, 567, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 567, 1, 567, 567, 567, 567, 567,
-
567, 567, 567, 567, 567, 567, 567, 567,
-
567, 567, 567, 567, 567, 567, 567, 567,
-
567, 567, 567, 567, 567, 567, 567, 567,
-
567, 567, 567, 567, 567, 567, 567, 567,
-
567, 567, 567, 567, 567, 567, 567, 567,
-
567, 567, 567, 567, 567, 567, 567, 567,
-
567, 567, 567, 567, 567, 567, 567, 567,
-
567, 567, 567, 567, 567, 567, 567, 567,
-
567, 567, 567, 567, 567, 567, 567, 567,
-
567, 567, 567, 567, 567, 567, 567, 567,
-
567, 567, 567, 567, 567, 567, 567, 567,
-
567, 567, 567, 567, 567, 567, 567, 567,
-
567, 567, 567, 567, 567, 567, 567, 567,
-
567, 567, 567, 567, 567, 567, 567, 567,
-
567, 567, 567, 567, 567, 567, 567, 567,
-
567, 567, 567, 1, 572, 1, 1, 1,
-
573, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 572, 574, 575, 574, 574,
-
574, 574, 574, 576, 1, 574, 574, 1,
-
574, 1, 574, 574, 574, 574, 574, 574,
-
574, 574, 574, 574, 574, 1, 1, 354,
-
574, 1, 574, 1, 574, 574, 574, 574,
-
574, 574, 574, 574, 574, 574, 574, 574,
-
574, 574, 574, 574, 574, 574, 574, 574,
-
574, 574, 574, 574, 574, 574, 1, 1,
-
1, 574, 574, 574, 574, 574, 574, 574,
-
574, 574, 574, 574, 574, 574, 574, 574,
-
574, 574, 574, 574, 574, 574, 574, 574,
-
574, 574, 574, 574, 574, 574, 574, 574,
-
574, 574, 1, 577, 1, 1, 1, 578,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 577, 438, 306, 438, 438, 438,
-
438, 438, 579, 1, 438, 438, 308, 438,
-
580, 438, 438, 438, 438, 438, 438, 438,
-
438, 438, 438, 438, 1, 138, 310, 438,
-
1, 438, 311, 438, 438, 438, 438, 438,
-
438, 438, 438, 438, 438, 438, 438, 438,
-
438, 438, 438, 438, 438, 438, 438, 438,
-
438, 438, 438, 438, 438, 1, 1, 1,
-
438, 438, 438, 438, 438, 438, 438, 438,
-
438, 438, 438, 438, 438, 438, 438, 438,
-
438, 438, 438, 438, 438, 438, 438, 438,
-
438, 438, 438, 438, 438, 438, 438, 438,
-
438, 1, 581, 1, 1, 1, 582, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 581, 314, 315, 314, 314, 314, 314,
-
314, 583, 1, 314, 314, 317, 314, 325,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 1, 148, 310, 314, 1,
-
314, 319, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 1, 1, 1, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
1, 584, 1, 585, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 585, 1, 586, 1, 1, 1,
-
587, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 586, 338, 339, 338, 338,
-
338, 338, 338, 588, 1, 338, 338, 589,
-
338, 341, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 1, 590, 342,
-
338, 1, 338, 591, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 1, 1,
-
1, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 1, 592, 592, 592, 592, 592,
-
592, 592, 592, 593, 1, 592, 592, 594,
-
592, 592, 592, 592, 592, 592, 592, 592,
-
592, 592, 592, 592, 592, 592, 592, 592,
-
592, 592, 593, 592, 329, 592, 592, 592,
-
592, 592, 592, 592, 592, 592, 592, 592,
-
592, 592, 592, 592, 592, 592, 592, 592,
-
592, 592, 592, 592, 592, 592, 592, 592,
-
592, 592, 592, 592, 592, 592, 592, 592,
-
592, 592, 592, 592, 592, 592, 592, 592,
-
592, 592, 592, 592, 592, 592, 592, 592,
-
592, 592, 592, 592, 592, 592, 595, 592,
-
592, 592, 592, 592, 592, 592, 592, 592,
-
592, 592, 592, 592, 592, 592, 592, 592,
-
592, 592, 592, 592, 592, 592, 592, 592,
-
592, 592, 592, 592, 592, 592, 592, 592,
-
592, 592, 1, 596, 596, 596, 596, 596,
-
596, 596, 596, 596, 1, 596, 596, 597,
-
596, 596, 596, 596, 596, 596, 596, 596,
-
596, 596, 596, 596, 596, 596, 596, 596,
-
596, 596, 596, 596, 598, 596, 596, 596,
-
596, 596, 596, 596, 596, 596, 596, 596,
-
596, 596, 596, 596, 596, 596, 596, 596,
-
596, 596, 596, 596, 596, 596, 596, 596,
-
596, 596, 596, 596, 596, 596, 596, 596,
-
596, 596, 596, 596, 596, 596, 596, 596,
-
596, 596, 596, 596, 596, 596, 596, 596,
-
596, 596, 596, 596, 596, 596, 599, 596,
-
596, 596, 596, 596, 596, 596, 596, 596,
-
596, 596, 596, 596, 596, 596, 596, 596,
-
596, 596, 596, 596, 596, 596, 596, 596,
-
596, 596, 596, 596, 596, 596, 596, 596,
-
596, 596, 1, 600, 1, 596, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 596, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 596, 596,
-
596, 596, 596, 596, 596, 596, 596, 601,
-
1, 596, 596, 602, 596, 596, 596, 596,
-
596, 596, 596, 596, 596, 596, 596, 596,
-
596, 596, 596, 596, 596, 596, 601, 596,
-
333, 596, 596, 596, 596, 596, 596, 596,
-
596, 596, 596, 596, 596, 596, 596, 596,
-
596, 596, 596, 596, 596, 596, 596, 596,
-
596, 596, 596, 596, 596, 596, 596, 596,
-
596, 596, 596, 596, 596, 596, 596, 596,
-
596, 596, 596, 596, 596, 596, 596, 596,
-
596, 596, 596, 596, 596, 596, 596, 596,
-
596, 596, 599, 596, 596, 596, 596, 596,
-
596, 596, 596, 596, 596, 596, 596, 596,
-
596, 596, 596, 596, 596, 596, 596, 596,
-
596, 596, 596, 596, 596, 596, 596, 596,
-
596, 596, 596, 596, 596, 596, 1, 603,
-
1, 601, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
601, 1, 604, 1, 1, 1, 605, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 604, 438, 306, 438, 438, 438, 438,
-
438, 606, 1, 438, 438, 308, 438, 580,
-
438, 438, 438, 438, 438, 438, 438, 438,
-
438, 438, 438, 1, 138, 310, 438, 1,
-
438, 311, 438, 438, 438, 438, 438, 438,
-
438, 438, 438, 438, 438, 438, 438, 438,
-
438, 438, 438, 438, 438, 438, 438, 438,
-
438, 438, 438, 438, 1, 1, 1, 438,
-
438, 438, 438, 438, 438, 438, 438, 438,
-
438, 438, 438, 438, 438, 438, 438, 438,
-
438, 438, 438, 438, 438, 438, 438, 438,
-
438, 438, 438, 438, 438, 438, 438, 438,
-
1, 607, 1, 1, 1, 608, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
607, 314, 315, 314, 314, 314, 314, 314,
-
609, 1, 314, 314, 317, 314, 1, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 1, 148, 349, 314, 1, 314,
-
610, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 1, 1, 1, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 1,
-
611, 1, 607, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 607, 1, 612, 1, 1, 1, 613,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 612, 338, 339, 338, 338, 338,
-
338, 338, 614, 1, 338, 338, 589, 338,
-
1, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 1, 590, 354, 338,
-
1, 338, 615, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 1, 1, 1,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 1, 616, 1, 1, 1, 617, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 616, 618, 619, 618, 618, 618, 618,
-
618, 620, 1, 618, 618, 1, 618, 621,
-
618, 618, 618, 618, 618, 618, 618, 618,
-
618, 618, 618, 1, 1, 1, 618, 1,
-
618, 1, 618, 618, 618, 618, 618, 618,
-
618, 618, 618, 618, 618, 618, 618, 618,
-
618, 618, 618, 618, 618, 618, 618, 618,
-
618, 618, 618, 618, 480, 1, 1, 618,
-
618, 618, 618, 618, 618, 618, 618, 618,
-
618, 618, 618, 618, 618, 618, 618, 618,
-
618, 618, 618, 618, 618, 618, 618, 618,
-
618, 618, 618, 618, 618, 618, 618, 618,
-
1, 622, 1, 1, 1, 623, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
622, 624, 546, 624, 624, 624, 624, 624,
-
625, 1, 624, 624, 1, 624, 486, 624,
-
624, 624, 624, 624, 624, 624, 624, 624,
-
624, 624, 1, 1, 1, 624, 1, 624,
-
1, 624, 624, 624, 624, 624, 624, 624,
-
624, 624, 624, 624, 624, 624, 624, 624,
-
624, 624, 624, 624, 624, 624, 624, 624,
-
624, 624, 624, 487, 1, 1, 624, 624,
-
624, 624, 624, 624, 624, 624, 624, 624,
-
624, 624, 624, 624, 624, 624, 624, 624,
-
624, 624, 624, 624, 624, 624, 624, 624,
-
624, 624, 624, 624, 624, 624, 624, 1,
-
626, 1, 622, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 622, 1, 627, 1, 1, 1, 628,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 627, 624, 546, 624, 624, 624,
-
624, 624, 629, 1, 624, 624, 492, 624,
-
630, 624, 624, 624, 624, 624, 624, 624,
-
624, 624, 624, 624, 1, 494, 1, 624,
-
1, 624, 1, 624, 624, 624, 624, 624,
-
624, 624, 624, 624, 624, 624, 624, 624,
-
624, 624, 624, 624, 624, 624, 624, 624,
-
624, 624, 624, 624, 624, 1, 1, 1,
-
624, 624, 624, 624, 624, 624, 624, 624,
-
624, 624, 624, 624, 624, 624, 624, 624,
-
624, 624, 624, 624, 624, 624, 624, 624,
-
624, 624, 624, 624, 624, 624, 624, 624,
-
624, 1, 627, 1, 1, 1, 628, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 627, 1, 1, 1, 1, 1, 1,
-
1, 629, 1, 1, 1, 492, 1, 631,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 494, 1, 632, 1,
-
627, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 627,
-
1, 633, 1, 1, 1, 634, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
633, 1, 1, 1, 1, 1, 1, 1,
-
635, 1, 1, 1, 501, 1, 636, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 503, 1, 631, 1, 1,
-
1, 637, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 631, 638, 1, 638,
-
638, 638, 638, 638, 639, 1, 638, 638,
-
1, 638, 1, 638, 638, 638, 638, 638,
-
638, 638, 638, 638, 638, 638, 1, 1,
-
1, 638, 1, 638, 1, 638, 638, 638,
-
638, 638, 638, 638, 638, 638, 638, 638,
-
638, 638, 638, 638, 638, 638, 638, 638,
-
638, 638, 638, 638, 638, 638, 638, 1,
-
1, 1, 638, 638, 638, 638, 638, 638,
-
638, 638, 638, 638, 638, 638, 638, 638,
-
638, 638, 638, 638, 638, 638, 638, 638,
-
638, 638, 638, 638, 638, 638, 638, 638,
-
638, 638, 638, 1, 640, 1, 631, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 631, 1, 627,
-
1, 1, 1, 628, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 627, 638,
-
1, 638, 638, 638, 638, 638, 629, 1,
-
638, 638, 492, 638, 631, 638, 638, 638,
-
638, 638, 638, 638, 638, 638, 638, 638,
-
1, 494, 1, 638, 1, 638, 1, 638,
-
638, 638, 638, 638, 638, 638, 638, 638,
-
638, 638, 638, 638, 638, 638, 638, 638,
-
638, 638, 638, 638, 638, 638, 638, 638,
-
638, 1, 1, 1, 638, 638, 638, 638,
-
638, 638, 638, 638, 638, 638, 638, 638,
-
638, 638, 638, 638, 638, 638, 638, 638,
-
638, 638, 638, 638, 638, 638, 638, 638,
-
638, 638, 638, 638, 638, 1, 636, 1,
-
1, 1, 641, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 636, 642, 1,
-
642, 642, 642, 642, 642, 643, 1, 642,
-
642, 1, 642, 1, 642, 642, 642, 642,
-
642, 642, 642, 642, 642, 642, 642, 1,
-
1, 1, 642, 1, 642, 1, 642, 642,
-
642, 642, 642, 642, 642, 642, 642, 642,
-
642, 642, 642, 642, 642, 642, 642, 642,
-
642, 642, 642, 642, 642, 642, 642, 642,
-
1, 1, 1, 642, 642, 642, 642, 642,
-
642, 642, 642, 642, 642, 642, 642, 642,
-
642, 642, 642, 642, 642, 642, 642, 642,
-
642, 642, 642, 642, 642, 642, 642, 642,
-
642, 642, 642, 642, 1, 631, 1, 1,
-
1, 637, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 631, 624, 546, 624,
-
624, 624, 624, 624, 639, 1, 624, 624,
-
1, 624, 486, 624, 624, 624, 624, 624,
-
624, 624, 624, 624, 624, 624, 1, 1,
-
1, 624, 1, 624, 1, 624, 624, 624,
-
624, 624, 624, 624, 624, 624, 624, 624,
-
624, 624, 624, 624, 624, 624, 624, 624,
-
624, 624, 624, 624, 624, 624, 624, 1,
-
1, 1, 624, 624, 624, 624, 624, 624,
-
624, 624, 624, 624, 624, 624, 624, 624,
-
624, 624, 624, 624, 624, 624, 624, 624,
-
624, 624, 624, 624, 624, 624, 624, 624,
-
624, 624, 624, 1, 644, 1, 1, 1,
-
645, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 644, 646, 647, 646, 646,
-
646, 646, 646, 648, 1, 646, 646, 1,
-
646, 543, 646, 646, 646, 646, 646, 646,
-
646, 646, 646, 646, 646, 1, 1, 1,
-
646, 1, 646, 1, 646, 646, 646, 646,
-
646, 646, 646, 646, 646, 646, 646, 646,
-
646, 646, 646, 646, 646, 646, 646, 646,
-
646, 646, 646, 646, 646, 646, 544, 1,
-
1, 646, 646, 646, 646, 646, 646, 646,
-
646, 646, 646, 646, 646, 646, 646, 646,
-
646, 646, 646, 646, 646, 646, 646, 646,
-
646, 646, 646, 646, 646, 646, 646, 646,
-
646, 646, 1, 649, 1, 1, 1, 650,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 649, 305, 651, 305, 305, 305,
-
305, 305, 652, 1, 305, 305, 308, 305,
-
580, 305, 305, 305, 305, 305, 305, 305,
-
305, 305, 305, 305, 1, 138, 310, 305,
-
1, 305, 311, 305, 305, 305, 305, 305,
-
305, 305, 305, 305, 305, 305, 305, 305,
-
305, 305, 305, 305, 305, 305, 305, 305,
-
305, 305, 305, 305, 305, 1, 1, 1,
-
305, 305, 305, 305, 305, 305, 305, 305,
-
305, 305, 305, 305, 305, 305, 305, 305,
-
305, 305, 305, 305, 305, 305, 305, 305,
-
305, 305, 305, 305, 305, 305, 305, 305,
-
305, 1, 653, 1, 1, 1, 654, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 653, 457, 458, 457, 457, 457, 457,
-
457, 655, 1, 457, 457, 317, 457, 1,
-
457, 457, 457, 457, 457, 457, 457, 457,
-
457, 457, 457, 1, 148, 349, 457, 1,
-
457, 610, 457, 457, 457, 457, 457, 457,
-
457, 457, 457, 457, 457, 457, 457, 457,
-
457, 457, 457, 457, 457, 457, 457, 457,
-
457, 457, 457, 457, 1, 1, 1, 457,
-
457, 457, 457, 457, 457, 457, 457, 457,
-
457, 457, 457, 457, 457, 457, 457, 457,
-
457, 457, 457, 457, 457, 457, 457, 457,
-
457, 457, 457, 457, 457, 457, 457, 457,
-
1, 656, 1, 653, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 653, 1, 657, 1, 1, 1,
-
658, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 657, 574, 575, 574, 574,
-
574, 574, 574, 659, 1, 574, 574, 589,
-
574, 1, 574, 574, 574, 574, 574, 574,
-
574, 574, 574, 574, 574, 1, 590, 354,
-
574, 1, 574, 615, 574, 574, 574, 574,
-
574, 574, 574, 574, 574, 574, 574, 574,
-
574, 574, 574, 574, 574, 574, 574, 574,
-
574, 574, 574, 574, 574, 574, 1, 1,
-
1, 574, 574, 574, 574, 574, 574, 574,
-
574, 574, 574, 574, 574, 574, 574, 574,
-
574, 574, 574, 574, 574, 574, 574, 574,
-
574, 574, 574, 574, 574, 574, 574, 574,
-
574, 574, 1, 660, 660, 660, 660, 660,
-
660, 660, 660, 661, 1, 660, 660, 662,
-
660, 660, 660, 660, 660, 660, 660, 660,
-
660, 660, 660, 660, 660, 660, 660, 660,
-
660, 660, 661, 660, 565, 660, 660, 660,
-
660, 660, 660, 660, 660, 660, 660, 660,
-
660, 660, 660, 660, 660, 660, 660, 660,
-
660, 660, 660, 660, 660, 660, 660, 660,
-
660, 660, 660, 660, 660, 660, 660, 660,
-
660, 660, 660, 660, 660, 660, 660, 660,
-
660, 660, 660, 660, 660, 660, 660, 660,
-
660, 660, 660, 660, 660, 660, 663, 660,
-
660, 660, 660, 660, 660, 660, 660, 660,
-
660, 660, 660, 660, 660, 660, 660, 660,
-
660, 660, 660, 660, 660, 660, 660, 660,
-
660, 660, 660, 660, 660, 660, 660, 660,
-
660, 660, 1, 664, 664, 664, 664, 664,
-
664, 664, 664, 664, 1, 664, 664, 665,
-
664, 664, 664, 664, 664, 664, 664, 664,
-
664, 664, 664, 664, 664, 664, 664, 664,
-
664, 664, 664, 664, 666, 664, 664, 664,
-
664, 664, 664, 664, 664, 664, 664, 664,
-
664, 664, 664, 664, 664, 664, 664, 664,
-
664, 664, 664, 664, 664, 664, 664, 664,
-
664, 664, 664, 664, 664, 664, 664, 664,
-
664, 664, 664, 664, 664, 664, 664, 664,
-
664, 664, 664, 664, 664, 664, 664, 664,
-
664, 664, 664, 664, 664, 664, 667, 664,
-
664, 664, 664, 664, 664, 664, 664, 664,
-
664, 664, 664, 664, 664, 664, 664, 664,
-
664, 664, 664, 664, 664, 664, 664, 664,
-
664, 664, 664, 664, 664, 664, 664, 664,
-
664, 664, 1, 668, 1, 664, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 664, 1, 303, 1,
-
1, 1, 304, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 303, 438, 306,
-
438, 438, 438, 438, 438, 307, 1, 438,
-
438, 308, 438, 309, 438, 438, 438, 438,
-
438, 438, 438, 438, 438, 438, 438, 1,
-
138, 310, 438, 1, 438, 311, 438, 438,
-
438, 438, 438, 438, 438, 438, 438, 438,
-
438, 438, 438, 438, 438, 438, 438, 438,
-
438, 438, 438, 438, 438, 438, 438, 438,
-
1, 1, 1, 438, 438, 438, 438, 438,
-
438, 438, 438, 438, 438, 438, 438, 438,
-
438, 438, 438, 438, 438, 438, 438, 438,
-
438, 438, 438, 438, 438, 438, 438, 438,
-
438, 438, 438, 438, 1, 669, 1, 1,
-
1, 670, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 669, 338, 339, 338,
-
338, 338, 338, 338, 671, 1, 338, 338,
-
589, 338, 453, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 1, 590,
-
342, 338, 1, 338, 591, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 1,
-
1, 1, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 1, 664, 664, 664, 664,
-
664, 664, 664, 664, 664, 664, 664, 664,
-
664, 664, 664, 664, 664, 664, 664, 664,
-
664, 664, 664, 664, 664, 664, 664, 664,
-
664, 664, 664, 664, 664, 664, 664, 664,
-
664, 664, 664, 664, 664, 664, 664, 664,
-
664, 664, 664, 664, 664, 664, 664, 664,
-
664, 664, 664, 664, 664, 664, 664, 664,
-
664, 664, 664, 664, 664, 664, 664, 664,
-
664, 664, 664, 664, 664, 664, 664, 664,
-
664, 664, 664, 664, 664, 664, 664, 664,
-
664, 664, 664, 664, 664, 664, 664, 664,
-
664, 664, 664, 664, 664, 664, 664, 664,
-
664, 664, 664, 664, 664, 664, 664, 664,
-
664, 664, 664, 664, 664, 664, 664, 664,
-
664, 664, 664, 664, 664, 664, 664, 664,
-
664, 664, 664, 664, 1, 664, 664, 664,
-
664, 664, 664, 664, 664, 672, 1, 664,
-
664, 673, 664, 664, 664, 664, 664, 664,
-
664, 664, 664, 664, 664, 664, 664, 664,
-
664, 664, 664, 664, 672, 664, 569, 664,
-
664, 664, 664, 664, 664, 664, 664, 664,
-
664, 664, 664, 664, 664, 664, 664, 664,
-
664, 664, 664, 664, 664, 664, 664, 664,
-
664, 664, 664, 664, 664, 664, 664, 664,
-
664, 664, 664, 664, 664, 664, 664, 664,
-
664, 664, 664, 664, 664, 664, 664, 664,
-
664, 664, 664, 664, 664, 664, 664, 664,
-
667, 664, 664, 664, 664, 664, 664, 664,
-
664, 664, 664, 664, 664, 664, 664, 664,
-
664, 664, 664, 664, 664, 664, 664, 664,
-
664, 664, 664, 664, 664, 664, 664, 664,
-
664, 664, 664, 664, 1, 674, 1, 672,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 672, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
431, 431, 431, 431, 431, 431, 431, 431,
-
431, 675, 1, 431, 431, 676, 431, 431,
-
431, 431, 431, 431, 431, 431, 431, 431,
-
431, 431, 431, 431, 431, 431, 431, 431,
-
675, 431, 677, 431, 431, 431, 431, 431,
-
431, 431, 431, 431, 431, 431, 431, 431,
-
431, 431, 431, 431, 431, 431, 431, 431,
-
431, 431, 431, 431, 431, 431, 431, 431,
-
431, 431, 431, 431, 431, 431, 431, 431,
-
431, 431, 431, 431, 431, 431, 431, 431,
-
431, 431, 431, 431, 431, 431, 431, 431,
-
431, 431, 431, 431, 434, 431, 431, 431,
-
431, 431, 431, 431, 431, 431, 431, 431,
-
431, 431, 431, 431, 431, 431, 431, 431,
-
431, 431, 431, 431, 431, 431, 431, 431,
-
431, 431, 431, 431, 431, 431, 431, 431,
-
1, 678, 1, 675, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 675, 1, 679, 1, 1, 1,
-
680, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 679, 314, 315, 314, 314,
-
314, 314, 314, 681, 1, 314, 314, 682,
-
314, 318, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 1, 683, 310,
-
314, 1, 314, 684, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 1, 1,
-
1, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 314, 314, 314, 314, 314, 314,
-
314, 314, 1, 685, 1, 686, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 686, 1, 687, 1,
-
1, 1, 688, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 687, 338, 339,
-
338, 338, 338, 338, 338, 689, 1, 338,
-
338, 690, 338, 453, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 1,
-
691, 342, 338, 1, 338, 692, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
1, 1, 1, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 338, 338, 338, 338,
-
338, 338, 338, 338, 1, 693, 1, 1,
-
1, 694, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 693, 695, 696, 695,
-
695, 695, 695, 695, 697, 1, 695, 695,
-
1, 695, 698, 695, 695, 695, 695, 695,
-
695, 695, 695, 695, 695, 695, 1, 1,
-
354, 695, 1, 695, 1, 695, 695, 695,
-
695, 695, 695, 695, 695, 695, 695, 695,
-
695, 695, 695, 695, 695, 695, 695, 695,
-
695, 695, 695, 695, 695, 695, 695, 1,
-
1, 1, 695, 695, 695, 695, 695, 695,
-
695, 695, 695, 695, 695, 695, 695, 695,
-
695, 695, 695, 695, 695, 695, 695, 695,
-
695, 695, 695, 695, 695, 695, 695, 695,
-
695, 695, 695, 1, 699, 700, 699, 699,
-
699, 699, 699, 1, 1, 699, 699, 1,
-
699, 701, 699, 699, 699, 699, 699, 699,
-
699, 699, 699, 699, 699, 1, 1, 1,
-
699, 1, 699, 1, 699, 699, 699, 699,
-
699, 699, 699, 699, 699, 699, 699, 699,
-
699, 699, 699, 699, 699, 699, 699, 699,
-
699, 699, 699, 699, 699, 699, 1, 1,
-
1, 699, 699, 699, 699, 699, 699, 699,
-
699, 699, 699, 699, 699, 699, 699, 699,
-
699, 699, 699, 699, 699, 699, 699, 699,
-
699, 699, 699, 699, 699, 699, 699, 699,
-
699, 699, 1, 702, 1, 1, 1, 703,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 702, 699, 700, 699, 699, 699,
-
699, 699, 704, 1, 699, 699, 308, 699,
-
699, 699, 699, 699, 699, 699, 699, 699,
-
699, 699, 699, 699, 1, 138, 1, 699,
-
1, 699, 705, 699, 699, 699, 699, 699,
-
699, 699, 699, 699, 699, 699, 699, 699,
-
699, 699, 699, 699, 699, 699, 699, 699,
-
699, 699, 699, 699, 699, 1, 1, 1,
-
699, 699, 699, 699, 699, 699, 699, 699,
-
699, 699, 699, 699, 699, 699, 699, 699,
-
699, 699, 699, 699, 699, 699, 699, 699,
-
699, 699, 699, 699, 699, 699, 699, 699,
-
699, 1, 706, 1, 1, 1, 707, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 706, 1, 1, 1, 1, 1, 1,
-
1, 708, 1, 1, 1, 317, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 148, 1, 1, 1,
-
1, 610, 1, 709, 1, 706, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 706, 1, 710, 1,
-
1, 1, 711, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 710, 1, 1,
-
1, 1, 1, 1, 1, 712, 1, 1,
-
1, 589, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
590, 1, 1, 1, 1, 615, 1, 713,
-
713, 713, 713, 713, 713, 713, 713, 700,
-
1, 713, 713, 714, 713, 713, 713, 713,
-
713, 713, 713, 713, 713, 713, 713, 713,
-
713, 713, 713, 713, 713, 713, 700, 713,
-
1, 713, 713, 713, 713, 713, 713, 713,
-
713, 713, 713, 713, 713, 713, 713, 713,
-
713, 713, 713, 713, 713, 713, 713, 713,
-
713, 713, 713, 713, 713, 713, 713, 713,
-
713, 713, 713, 713, 713, 713, 713, 713,
-
713, 713, 713, 713, 713, 713, 713, 713,
-
713, 713, 713, 713, 713, 713, 713, 713,
-
713, 713, 715, 713, 713, 713, 713, 713,
-
713, 713, 713, 713, 713, 713, 713, 713,
-
713, 713, 713, 713, 713, 713, 713, 713,
-
713, 713, 713, 713, 713, 713, 713, 713,
-
713, 713, 713, 713, 713, 713, 1, 713,
-
713, 713, 713, 713, 713, 713, 713, 713,
-
1, 713, 713, 716, 713, 713, 713, 713,
-
713, 713, 713, 713, 713, 713, 713, 713,
-
713, 713, 713, 713, 713, 713, 713, 713,
-
699, 713, 713, 713, 713, 713, 713, 713,
-
713, 713, 713, 713, 713, 713, 713, 713,
-
713, 713, 713, 713, 713, 713, 713, 713,
-
713, 713, 713, 713, 713, 713, 713, 713,
-
713, 713, 713, 713, 713, 713, 713, 713,
-
713, 713, 713, 713, 713, 713, 713, 713,
-
713, 713, 713, 713, 713, 713, 713, 713,
-
713, 713, 715, 713, 713, 713, 713, 713,
-
713, 713, 713, 713, 713, 713, 713, 713,
-
713, 713, 713, 713, 713, 713, 713, 713,
-
713, 713, 713, 713, 713, 713, 713, 713,
-
713, 713, 713, 713, 713, 713, 1, 717,
-
1, 713, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
713, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 713, 718, 1, 700, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 700, 1, 343, 1,
-
1, 1, 344, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 343, 438, 306,
-
438, 438, 438, 438, 438, 345, 1, 438,
-
438, 1, 438, 719, 438, 438, 438, 438,
-
438, 438, 438, 438, 438, 438, 438, 1,
-
1, 310, 438, 1, 438, 325, 438, 438,
-
438, 438, 438, 438, 438, 438, 438, 438,
-
438, 438, 438, 438, 438, 438, 438, 438,
-
438, 438, 438, 438, 438, 438, 438, 438,
-
1, 1, 1, 438, 438, 438, 438, 438,
-
438, 438, 438, 438, 438, 438, 438, 438,
-
438, 438, 438, 438, 438, 438, 438, 438,
-
438, 438, 438, 438, 438, 438, 438, 438,
-
438, 438, 438, 438, 1, 720, 1, 1,
-
1, 721, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 720, 722, 723, 722,
-
722, 722, 722, 722, 724, 1, 722, 722,
-
1, 722, 725, 722, 722, 722, 722, 722,
-
722, 722, 722, 722, 722, 722, 1, 1,
-
1, 722, 1, 722, 1, 722, 722, 722,
-
722, 722, 722, 722, 722, 722, 722, 722,
-
722, 722, 722, 722, 722, 722, 722, 722,
-
722, 722, 722, 722, 722, 722, 722, 726,
-
1, 1, 722, 722, 722, 722, 722, 722,
-
722, 722, 722, 722, 722, 722, 722, 722,
-
722, 722, 722, 722, 722, 722, 722, 722,
-
722, 722, 722, 722, 722, 722, 722, 722,
-
722, 722, 722, 1, 727, 1, 1, 1,
-
728, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 727, 729, 730, 729, 729,
-
729, 729, 729, 731, 1, 729, 729, 1,
-
729, 732, 729, 729, 729, 729, 729, 729,
-
729, 729, 729, 729, 729, 1, 1, 1,
-
729, 1, 729, 1, 729, 729, 729, 729,
-
729, 729, 729, 729, 729, 729, 729, 729,
-
729, 729, 729, 729, 729, 729, 729, 729,
-
729, 729, 729, 729, 729, 729, 733, 1,
-
1, 729, 729, 729, 729, 729, 729, 729,
-
729, 729, 729, 729, 729, 729, 729, 729,
-
729, 729, 729, 729, 729, 729, 729, 729,
-
729, 729, 729, 729, 729, 729, 729, 729,
-
729, 729, 1, 734, 1, 727, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 727, 1, 735, 1,
-
1, 1, 736, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 735, 729, 730,
-
729, 729, 729, 729, 729, 737, 1, 729,
-
729, 1, 729, 738, 729, 729, 729, 729,
-
729, 729, 729, 729, 729, 729, 729, 1,
-
1, 1, 729, 739, 729, 1, 729, 729,
-
729, 729, 729, 729, 729, 729, 729, 729,
-
729, 729, 729, 729, 729, 729, 729, 729,
-
729, 729, 729, 729, 729, 729, 729, 729,
-
1, 1, 1, 729, 729, 729, 729, 729,
-
729, 729, 729, 729, 729, 729, 729, 729,
-
729, 729, 729, 729, 729, 729, 729, 729,
-
729, 729, 729, 729, 729, 729, 729, 729,
-
729, 729, 729, 729, 1, 735, 1, 1,
-
1, 736, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 735, 1, 1, 1,
-
1, 1, 1, 1, 737, 1, 1, 1,
-
1, 1, 740, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 739, 1, 741, 1, 735, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 735, 1, 742,
-
1, 1, 1, 743, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 742, 1,
-
1, 1, 1, 1, 1, 1, 744, 1,
-
1, 1, 1, 1, 745, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 746, 1, 740, 1,
-
1, 1, 747, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 740, 748, 1,
-
748, 748, 748, 748, 748, 749, 1, 748,
-
748, 1, 748, 1, 748, 748, 748, 748,
-
748, 748, 748, 748, 748, 748, 748, 1,
-
1, 1, 748, 1, 748, 1, 748, 748,
-
748, 748, 748, 748, 748, 748, 748, 748,
-
748, 748, 748, 748, 748, 748, 748, 748,
-
748, 748, 748, 748, 748, 748, 748, 748,
-
1, 1, 1, 748, 748, 748, 748, 748,
-
748, 748, 748, 748, 748, 748, 748, 748,
-
748, 748, 748, 748, 748, 748, 748, 748,
-
748, 748, 748, 748, 748, 748, 748, 748,
-
748, 748, 748, 748, 1, 750, 1, 740,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 740, 1,
-
735, 1, 1, 1, 736, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 735,
-
748, 1, 748, 748, 748, 748, 748, 737,
-
1, 748, 748, 1, 748, 740, 748, 748,
-
748, 748, 748, 748, 748, 748, 748, 748,
-
748, 1, 1, 1, 748, 739, 748, 1,
-
748, 748, 748, 748, 748, 748, 748, 748,
-
748, 748, 748, 748, 748, 748, 748, 748,
-
748, 748, 748, 748, 748, 748, 748, 748,
-
748, 748, 1, 1, 1, 748, 748, 748,
-
748, 748, 748, 748, 748, 748, 748, 748,
-
748, 748, 748, 748, 748, 748, 748, 748,
-
748, 748, 748, 748, 748, 748, 748, 748,
-
748, 748, 748, 748, 748, 748, 1, 745,
-
1, 1, 1, 751, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 745, 752,
-
1, 752, 752, 752, 752, 752, 753, 1,
-
752, 752, 1, 752, 1, 752, 752, 752,
-
752, 752, 752, 752, 752, 752, 752, 752,
-
1, 1, 1, 752, 1, 752, 1, 752,
-
752, 752, 752, 752, 752, 752, 752, 752,
-
752, 752, 752, 752, 752, 752, 752, 752,
-
752, 752, 752, 752, 752, 752, 752, 752,
-
752, 1, 1, 1, 752, 752, 752, 752,
-
752, 752, 752, 752, 752, 752, 752, 752,
-
752, 752, 752, 752, 752, 752, 752, 752,
-
752, 752, 752, 752, 752, 752, 752, 752,
-
752, 752, 752, 752, 752, 1, 754, 754,
-
754, 754, 754, 754, 754, 754, 730, 1,
-
754, 754, 755, 754, 754, 754, 754, 754,
-
754, 754, 754, 754, 754, 754, 754, 754,
-
754, 754, 754, 754, 754, 730, 754, 1,
-
754, 754, 754, 754, 754, 754, 754, 754,
-
754, 754, 754, 754, 754, 754, 754, 754,
-
754, 754, 754, 754, 754, 754, 754, 754,
-
754, 754, 754, 754, 754, 754, 754, 754,
-
754, 754, 754, 754, 754, 754, 754, 754,
-
754, 754, 754, 754, 754, 754, 754, 754,
-
754, 754, 754, 754, 754, 754, 754, 754,
-
754, 756, 754, 754, 754, 754, 754, 754,
-
754, 754, 754, 754, 754, 754, 754, 754,
-
754, 754, 754, 754, 754, 754, 754, 754,
-
754, 754, 754, 754, 754, 754, 754, 754,
-
754, 754, 754, 754, 754, 1, 754, 754,
-
754, 754, 754, 754, 754, 754, 754, 1,
-
754, 754, 757, 754, 754, 754, 754, 754,
-
754, 754, 754, 754, 754, 754, 754, 754,
-
754, 754, 754, 754, 754, 754, 754, 758,
-
754, 754, 754, 754, 754, 754, 754, 754,
-
754, 754, 754, 754, 754, 754, 754, 754,
-
754, 754, 754, 754, 754, 754, 754, 754,
-
754, 754, 754, 754, 754, 754, 754, 754,
-
754, 754, 754, 754, 754, 754, 754, 754,
-
754, 754, 754, 754, 754, 754, 754, 754,
-
754, 754, 754, 754, 754, 754, 754, 754,
-
754, 756, 754, 754, 754, 754, 754, 754,
-
754, 754, 754, 754, 754, 754, 754, 754,
-
754, 754, 754, 754, 754, 754, 754, 754,
-
754, 754, 754, 754, 754, 754, 754, 754,
-
754, 754, 754, 754, 754, 1, 759, 1,
-
754, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 754,
-
1, 760, 1, 1, 1, 761, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
760, 758, 730, 758, 758, 758, 758, 758,
-
762, 1, 758, 758, 1, 758, 732, 758,
-
758, 758, 758, 758, 758, 758, 758, 758,
-
758, 758, 1, 1, 1, 758, 739, 758,
-
1, 758, 758, 758, 758, 758, 758, 758,
-
758, 758, 758, 758, 758, 758, 758, 758,
-
758, 758, 758, 758, 758, 758, 758, 758,
-
758, 758, 758, 1, 1, 1, 758, 758,
-
758, 758, 758, 758, 758, 758, 758, 758,
-
758, 758, 758, 758, 758, 758, 758, 758,
-
758, 758, 758, 758, 758, 758, 758, 758,
-
758, 758, 758, 758, 758, 758, 758, 1,
-
760, 1, 1, 1, 761, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 760,
-
1, 1, 1, 1, 1, 1, 1, 762,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 739, 1, 763,
-
1, 760, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
760, 1, 764, 1, 1, 1, 765, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 764, 1, 1, 1, 1, 1, 1,
-
1, 766, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 746,
-
1, 758, 730, 758, 758, 758, 758, 758,
-
1, 1, 758, 758, 1, 758, 732, 758,
-
758, 758, 758, 758, 758, 758, 758, 758,
-
758, 758, 1, 1, 1, 758, 1, 758,
-
1, 758, 758, 758, 758, 758, 758, 758,
-
758, 758, 758, 758, 758, 758, 758, 758,
-
758, 758, 758, 758, 758, 758, 758, 758,
-
758, 758, 758, 1, 1, 1, 758, 758,
-
758, 758, 758, 758, 758, 758, 758, 758,
-
758, 758, 758, 758, 758, 758, 758, 758,
-
758, 758, 758, 758, 758, 758, 758, 758,
-
758, 758, 758, 758, 758, 758, 758, 1,
-
754, 754, 754, 754, 754, 754, 754, 754,
-
754, 754, 754, 754, 754, 754, 754, 754,
-
754, 754, 754, 754, 754, 754, 754, 754,
-
754, 754, 754, 754, 754, 754, 754, 754,
-
754, 754, 754, 754, 754, 754, 754, 754,
-
754, 754, 754, 754, 754, 754, 754, 754,
-
754, 754, 754, 754, 754, 754, 754, 754,
-
754, 754, 754, 754, 754, 754, 754, 754,
-
754, 754, 754, 754, 754, 754, 754, 754,
-
754, 754, 754, 754, 754, 754, 754, 754,
-
754, 754, 754, 754, 754, 754, 754, 754,
-
754, 754, 754, 754, 754, 754, 754, 754,
-
754, 754, 754, 754, 754, 754, 754, 754,
-
754, 754, 754, 754, 754, 754, 754, 754,
-
754, 754, 754, 754, 754, 754, 754, 754,
-
754, 754, 754, 754, 754, 754, 754, 754,
-
1, 767, 1, 730, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 730, 1, 740, 1, 1, 1,
-
747, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 740, 729, 730, 729, 729,
-
729, 729, 729, 749, 1, 729, 729, 1,
-
729, 732, 729, 729, 729, 729, 729, 729,
-
729, 729, 729, 729, 729, 1, 1, 1,
-
729, 1, 729, 1, 729, 729, 729, 729,
-
729, 729, 729, 729, 729, 729, 729, 729,
-
729, 729, 729, 729, 729, 729, 729, 729,
-
729, 729, 729, 729, 729, 729, 1, 1,
-
1, 729, 729, 729, 729, 729, 729, 729,
-
729, 729, 729, 729, 729, 729, 729, 729,
-
729, 729, 729, 729, 729, 729, 729, 729,
-
729, 729, 729, 729, 729, 729, 729, 729,
-
729, 729, 1, 768, 1, 1, 1, 769,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 768, 770, 771, 770, 770, 770,
-
770, 770, 772, 1, 770, 770, 1, 770,
-
773, 770, 770, 770, 770, 770, 770, 770,
-
770, 770, 770, 770, 1, 1, 1, 770,
-
1, 770, 1, 770, 770, 770, 770, 770,
-
770, 770, 770, 770, 770, 770, 770, 770,
-
770, 770, 770, 770, 770, 770, 770, 770,
-
770, 770, 770, 770, 770, 774, 1, 1,
-
770, 770, 770, 770, 770, 770, 770, 770,
-
770, 770, 770, 770, 770, 770, 770, 770,
-
770, 770, 770, 770, 770, 770, 770, 770,
-
770, 770, 770, 770, 770, 770, 770, 770,
-
770, 1, 733, 733, 733, 733, 733, 733,
-
733, 733, 733, 1, 733, 733, 775, 733,
-
733, 733, 733, 733, 733, 733, 733, 733,
-
733, 733, 733, 733, 733, 733, 733, 733,
-
733, 733, 733, 733, 733, 733, 733, 733,
-
733, 733, 733, 733, 733, 733, 733, 733,
-
733, 733, 733, 733, 733, 733, 733, 733,
-
733, 733, 733, 733, 733, 733, 733, 733,
-
733, 733, 733, 733, 733, 733, 733, 733,
-
733, 733, 733, 733, 733, 733, 733, 733,
-
733, 733, 733, 733, 733, 733, 733, 733,
-
733, 733, 733, 733, 1, 776, 760, 733,
-
733, 733, 733, 733, 733, 733, 733, 733,
-
733, 733, 733, 733, 733, 733, 733, 733,
-
733, 733, 733, 733, 733, 733, 733, 733,
-
733, 733, 733, 733, 733, 733, 733, 733,
-
733, 1, 777, 1, 733, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 733, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 733, 778, 778,
-
778, 778, 778, 778, 778, 778, 778, 1,
-
778, 778, 779, 778, 778, 778, 778, 778,
-
778, 778, 778, 778, 778, 778, 778, 778,
-
778, 778, 778, 778, 778, 778, 778, 780,
-
778, 778, 778, 778, 778, 778, 778, 778,
-
778, 778, 778, 778, 778, 778, 778, 778,
-
778, 778, 778, 778, 778, 778, 778, 778,
-
778, 778, 778, 778, 778, 778, 778, 778,
-
778, 778, 778, 778, 778, 778, 778, 778,
-
778, 778, 778, 778, 778, 778, 778, 778,
-
778, 778, 778, 778, 778, 778, 778, 778,
-
778, 781, 778, 778, 778, 778, 778, 778,
-
778, 778, 778, 778, 778, 778, 778, 778,
-
778, 778, 778, 778, 778, 778, 778, 778,
-
778, 778, 778, 778, 778, 778, 778, 778,
-
778, 778, 778, 778, 778, 1, 782, 782,
-
782, 782, 782, 782, 782, 782, 782, 1,
-
782, 782, 783, 782, 782, 782, 782, 782,
-
782, 782, 782, 782, 782, 782, 782, 782,
-
782, 782, 782, 782, 782, 782, 782, 784,
-
782, 782, 782, 782, 782, 782, 782, 782,
-
782, 782, 782, 782, 782, 782, 782, 782,
-
782, 782, 782, 782, 782, 782, 782, 782,
-
782, 782, 782, 782, 782, 782, 782, 782,
-
782, 782, 782, 782, 782, 782, 782, 782,
-
782, 782, 782, 782, 782, 782, 782, 782,
-
782, 782, 782, 782, 782, 782, 782, 782,
-
782, 785, 782, 782, 782, 782, 782, 782,
-
782, 782, 782, 782, 782, 782, 782, 782,
-
782, 782, 782, 782, 782, 782, 782, 782,
-
782, 782, 782, 782, 782, 782, 782, 782,
-
782, 782, 782, 782, 782, 1, 786, 1,
-
782, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 782,
-
1, 782, 782, 782, 782, 782, 782, 782,
-
782, 782, 782, 782, 782, 782, 782, 782,
-
782, 782, 782, 782, 782, 782, 782, 782,
-
782, 782, 782, 782, 782, 782, 782, 782,
-
782, 782, 782, 782, 782, 782, 782, 782,
-
782, 782, 782, 782, 782, 782, 782, 782,
-
782, 782, 782, 782, 782, 782, 782, 782,
-
782, 782, 782, 782, 782, 782, 782, 782,
-
782, 782, 782, 782, 782, 782, 782, 782,
-
782, 782, 782, 782, 782, 782, 782, 782,
-
782, 782, 782, 782, 782, 782, 782, 782,
-
782, 782, 782, 782, 782, 782, 782, 782,
-
782, 782, 782, 782, 782, 782, 782, 782,
-
782, 782, 782, 782, 782, 782, 782, 782,
-
782, 782, 782, 782, 782, 782, 782, 782,
-
782, 782, 782, 782, 782, 782, 782, 782,
-
782, 1, 387, 1, 1, 1, 787, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 387, 788, 789, 788, 788, 788, 788,
-
788, 790, 1, 788, 788, 1, 788, 1,
-
788, 788, 788, 788, 788, 788, 788, 788,
-
788, 788, 788, 1, 1, 1, 788, 1,
-
788, 1, 788, 788, 788, 788, 788, 788,
-
788, 788, 788, 788, 788, 788, 788, 788,
-
788, 788, 788, 788, 788, 788, 788, 788,
-
788, 788, 788, 788, 1, 1, 1, 788,
-
788, 788, 788, 788, 788, 788, 788, 788,
-
788, 788, 788, 788, 788, 788, 788, 788,
-
788, 788, 788, 788, 788, 788, 788, 788,
-
788, 788, 788, 788, 788, 788, 788, 788,
-
1, 791, 791, 791, 791, 791, 791, 791,
-
791, 372, 1, 791, 791, 792, 791, 791,
-
791, 791, 791, 791, 791, 791, 791, 791,
-
791, 791, 791, 791, 791, 791, 791, 791,
-
372, 791, 1, 791, 791, 791, 791, 791,
-
791, 791, 791, 791, 791, 791, 791, 791,
-
791, 791, 791, 791, 791, 791, 791, 791,
-
791, 791, 791, 791, 791, 791, 791, 791,
-
791, 791, 791, 791, 791, 791, 791, 791,
-
791, 791, 791, 791, 791, 791, 791, 791,
-
791, 791, 791, 791, 791, 791, 791, 791,
-
791, 791, 791, 791, 793, 791, 791, 791,
-
791, 791, 791, 791, 791, 791, 791, 791,
-
791, 791, 791, 791, 791, 791, 791, 791,
-
791, 791, 791, 791, 791, 791, 791, 791,
-
791, 791, 791, 791, 791, 791, 791, 791,
-
1, 791, 791, 791, 791, 791, 791, 791,
-
791, 791, 1, 791, 791, 794, 791, 791,
-
791, 791, 791, 791, 791, 791, 791, 791,
-
791, 791, 791, 791, 791, 791, 791, 791,
-
791, 791, 795, 791, 791, 791, 791, 791,
-
791, 791, 791, 791, 791, 791, 791, 791,
-
791, 791, 791, 791, 791, 791, 791, 791,
-
791, 791, 791, 791, 791, 791, 791, 791,
-
791, 791, 791, 791, 791, 791, 791, 791,
-
791, 791, 791, 791, 791, 791, 791, 791,
-
791, 791, 791, 791, 791, 791, 791, 791,
-
791, 791, 791, 791, 793, 791, 791, 791,
-
791, 791, 791, 791, 791, 791, 791, 791,
-
791, 791, 791, 791, 791, 791, 791, 791,
-
791, 791, 791, 791, 791, 791, 791, 791,
-
791, 791, 791, 791, 791, 791, 791, 791,
-
1, 796, 1, 791, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 791, 1, 797, 1, 1, 1,
-
798, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 797, 795, 372, 795, 795,
-
795, 795, 795, 799, 1, 795, 795, 1,
-
795, 795, 795, 795, 795, 795, 795, 795,
-
795, 795, 795, 795, 795, 1, 1, 1,
-
795, 375, 795, 376, 795, 795, 795, 795,
-
795, 795, 795, 795, 795, 795, 795, 795,
-
795, 795, 795, 795, 795, 795, 795, 795,
-
795, 795, 795, 795, 795, 795, 1, 1,
-
1, 795, 795, 795, 795, 795, 795, 795,
-
795, 795, 795, 795, 795, 795, 795, 795,
-
795, 795, 795, 795, 795, 795, 795, 795,
-
795, 795, 795, 795, 795, 795, 795, 795,
-
795, 795, 1, 800, 1, 1, 1, 801,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 800, 1, 1, 1, 1, 1,
-
1, 1, 802, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
381, 1, 382, 1, 803, 1, 800, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 800, 1, 804,
-
1, 1, 1, 805, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 804, 1,
-
1, 1, 1, 1, 1, 1, 806, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 388, 1, 389, 1,
-
791, 791, 791, 791, 791, 791, 791, 791,
-
791, 791, 791, 791, 791, 791, 791, 791,
-
791, 791, 791, 791, 791, 791, 791, 791,
-
791, 791, 791, 791, 791, 791, 791, 791,
-
791, 791, 791, 791, 791, 791, 791, 791,
-
791, 791, 791, 791, 791, 791, 791, 791,
-
791, 791, 791, 791, 791, 791, 791, 791,
-
791, 791, 791, 791, 791, 791, 791, 791,
-
791, 791, 791, 791, 791, 791, 791, 791,
-
791, 791, 791, 791, 791, 791, 791, 791,
-
791, 791, 791, 791, 791, 791, 791, 791,
-
791, 791, 791, 791, 791, 791, 791, 791,
-
791, 791, 791, 791, 791, 791, 791, 791,
-
791, 791, 791, 791, 791, 791, 791, 791,
-
791, 791, 791, 791, 791, 791, 791, 791,
-
791, 791, 791, 791, 791, 791, 791, 791,
-
1, 807, 1, 372, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 372, 1, 808, 1, 1, 1,
-
809, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 808, 371, 810, 371, 371,
-
371, 371, 371, 811, 1, 371, 371, 1,
-
371, 795, 371, 371, 371, 371, 371, 371,
-
371, 371, 371, 371, 371, 1, 1, 1,
-
371, 375, 371, 376, 371, 371, 371, 371,
-
371, 371, 371, 371, 371, 371, 371, 371,
-
371, 371, 371, 371, 371, 371, 371, 371,
-
371, 371, 371, 371, 371, 371, 1, 1,
-
1, 371, 371, 371, 371, 371, 371, 371,
-
371, 371, 371, 371, 371, 371, 371, 371,
-
371, 371, 371, 371, 371, 371, 371, 371,
-
371, 371, 371, 371, 371, 371, 371, 371,
-
371, 371, 1, 812, 1, 1, 1, 813,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 812, 391, 392, 391, 391, 391,
-
391, 391, 814, 1, 391, 391, 1, 391,
-
1, 391, 391, 391, 391, 391, 391, 391,
-
391, 391, 391, 391, 1, 1, 1, 391,
-
381, 391, 382, 391, 391, 391, 391, 391,
-
391, 391, 391, 391, 391, 391, 391, 391,
-
391, 391, 391, 391, 391, 391, 391, 391,
-
391, 391, 391, 391, 391, 1, 1, 1,
-
391, 391, 391, 391, 391, 391, 391, 391,
-
391, 391, 391, 391, 391, 391, 391, 391,
-
391, 391, 391, 391, 391, 391, 391, 391,
-
391, 391, 391, 391, 391, 391, 391, 391,
-
391, 1, 815, 1, 812, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 812, 1, 816, 1, 1,
-
1, 817, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 816, 788, 789, 788,
-
788, 788, 788, 788, 818, 1, 788, 788,
-
1, 788, 1, 788, 788, 788, 788, 788,
-
788, 788, 788, 788, 788, 788, 1, 1,
-
1, 788, 388, 788, 389, 788, 788, 788,
-
788, 788, 788, 788, 788, 788, 788, 788,
-
788, 788, 788, 788, 788, 788, 788, 788,
-
788, 788, 788, 788, 788, 788, 788, 1,
-
1, 1, 788, 788, 788, 788, 788, 788,
-
788, 788, 788, 788, 788, 788, 788, 788,
-
788, 788, 788, 788, 788, 788, 788, 788,
-
788, 788, 788, 788, 788, 788, 788, 788,
-
788, 788, 788, 1, 819, 819, 819, 819,
-
819, 819, 819, 819, 820, 1, 819, 819,
-
821, 819, 819, 819, 819, 819, 819, 819,
-
819, 819, 819, 819, 819, 819, 819, 819,
-
819, 819, 819, 820, 819, 780, 819, 819,
-
819, 819, 819, 819, 819, 819, 819, 819,
-
819, 819, 819, 819, 819, 819, 819, 819,
-
819, 819, 819, 819, 819, 819, 819, 819,
-
819, 819, 819, 819, 819, 819, 819, 819,
-
819, 819, 819, 819, 819, 819, 819, 819,
-
819, 819, 819, 819, 819, 819, 819, 819,
-
819, 819, 819, 819, 819, 819, 819, 822,
-
819, 819, 819, 819, 819, 819, 819, 819,
-
819, 819, 819, 819, 819, 819, 819, 819,
-
819, 819, 819, 819, 819, 819, 819, 819,
-
819, 819, 819, 819, 819, 819, 819, 819,
-
819, 819, 819, 1, 823, 823, 823, 823,
-
823, 823, 823, 823, 823, 1, 823, 823,
-
824, 823, 823, 823, 823, 823, 823, 823,
-
823, 823, 823, 823, 823, 823, 823, 823,
-
823, 823, 823, 823, 823, 825, 823, 823,
-
823, 823, 823, 823, 823, 823, 823, 823,
-
823, 823, 823, 823, 823, 823, 823, 823,
-
823, 823, 823, 823, 823, 823, 823, 823,
-
823, 823, 823, 823, 823, 823, 823, 823,
-
823, 823, 823, 823, 823, 823, 823, 823,
-
823, 823, 823, 823, 823, 823, 823, 823,
-
823, 823, 823, 823, 823, 823, 823, 826,
-
823, 823, 823, 823, 823, 823, 823, 823,
-
823, 823, 823, 823, 823, 823, 823, 823,
-
823, 823, 823, 823, 823, 823, 823, 823,
-
823, 823, 823, 823, 823, 823, 823, 823,
-
823, 823, 823, 1, 827, 1, 823, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 823, 1, 369,
-
1, 1, 1, 370, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 369, 795,
-
372, 795, 795, 795, 795, 795, 373, 1,
-
795, 795, 1, 795, 374, 795, 795, 795,
-
795, 795, 795, 795, 795, 795, 795, 795,
-
1, 1, 1, 795, 375, 795, 376, 795,
-
795, 795, 795, 795, 795, 795, 795, 795,
-
795, 795, 795, 795, 795, 795, 795, 795,
-
795, 795, 795, 795, 795, 795, 795, 795,
-
795, 1, 1, 1, 795, 795, 795, 795,
-
795, 795, 795, 795, 795, 795, 795, 795,
-
795, 795, 795, 795, 795, 795, 795, 795,
-
795, 795, 795, 795, 795, 795, 795, 795,
-
795, 795, 795, 795, 795, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 823, 823,
-
823, 823, 823, 823, 823, 823, 823, 828,
-
1, 823, 823, 829, 823, 823, 823, 823,
-
823, 823, 823, 823, 823, 823, 823, 823,
-
823, 823, 823, 823, 823, 823, 828, 823,
-
784, 823, 823, 823, 823, 823, 823, 823,
-
823, 823, 823, 823, 823, 823, 823, 823,
-
823, 823, 823, 823, 823, 823, 823, 823,
-
823, 823, 823, 823, 823, 823, 823, 823,
-
823, 823, 823, 823, 823, 823, 823, 823,
-
823, 823, 823, 823, 823, 823, 823, 823,
-
823, 823, 823, 823, 823, 823, 823, 823,
-
823, 823, 826, 823, 823, 823, 823, 823,
-
823, 823, 823, 823, 823, 823, 823, 823,
-
823, 823, 823, 823, 823, 823, 823, 823,
-
823, 823, 823, 823, 823, 823, 823, 823,
-
823, 823, 823, 823, 823, 823, 1, 830,
-
1, 828, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
828, 1, 831, 831, 831, 831, 831, 831,
-
831, 831, 832, 1, 831, 831, 833, 831,
-
831, 831, 831, 831, 831, 831, 831, 831,
-
831, 831, 831, 831, 831, 831, 831, 831,
-
831, 832, 831, 834, 831, 831, 831, 831,
-
831, 831, 831, 831, 831, 831, 831, 831,
-
831, 831, 831, 831, 831, 831, 831, 831,
-
831, 831, 831, 831, 831, 831, 831, 831,
-
831, 831, 831, 831, 831, 831, 831, 831,
-
831, 831, 831, 831, 831, 831, 831, 831,
-
831, 831, 831, 831, 831, 831, 831, 831,
-
831, 831, 831, 831, 831, 835, 831, 831,
-
831, 831, 831, 831, 831, 831, 831, 831,
-
831, 831, 831, 831, 831, 831, 831, 831,
-
831, 831, 831, 831, 831, 831, 831, 831,
-
831, 831, 831, 831, 831, 831, 831, 831,
-
831, 1, 836, 836, 836, 836, 836, 836,
-
836, 836, 836, 1, 836, 836, 837, 836,
-
836, 836, 836, 836, 836, 836, 836, 836,
-
836, 836, 836, 836, 836, 836, 836, 836,
-
836, 836, 836, 838, 836, 836, 836, 836,
-
836, 836, 836, 836, 836, 836, 836, 836,
-
836, 836, 836, 836, 836, 836, 836, 836,
-
836, 836, 836, 836, 836, 836, 836, 836,
-
836, 836, 836, 836, 836, 836, 836, 836,
-
836, 836, 836, 836, 836, 836, 836, 836,
-
836, 836, 836, 836, 836, 836, 836, 836,
-
836, 836, 836, 836, 836, 839, 836, 836,
-
836, 836, 836, 836, 836, 836, 836, 836,
-
836, 836, 836, 836, 836, 836, 836, 836,
-
836, 836, 836, 836, 836, 836, 836, 836,
-
836, 836, 836, 836, 836, 836, 836, 836,
-
836, 1, 840, 1, 836, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 836, 1, 841, 1, 1,
-
1, 842, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 841, 795, 372, 795,
-
795, 795, 795, 795, 843, 1, 795, 795,
-
1, 795, 374, 795, 795, 795, 795, 795,
-
795, 795, 795, 795, 795, 795, 1, 1,
-
1, 795, 375, 795, 844, 795, 795, 795,
-
795, 795, 795, 795, 795, 795, 795, 795,
-
795, 795, 795, 795, 795, 795, 795, 795,
-
795, 795, 795, 795, 795, 795, 795, 1,
-
1, 1, 795, 795, 795, 795, 795, 795,
-
795, 795, 795, 795, 795, 795, 795, 795,
-
795, 795, 795, 795, 795, 795, 795, 795,
-
795, 795, 795, 795, 795, 795, 795, 795,
-
795, 795, 795, 1, 845, 1, 1, 1,
-
846, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 845, 1, 1, 1, 1,
-
1, 1, 1, 847, 1, 1, 1, 1,
-
1, 380, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 381, 1, 848, 1, 849, 1, 845,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 845, 1,
-
850, 1, 1, 1, 851, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 850,
-
1, 1, 1, 1, 1, 1, 1, 852,
-
1, 1, 1, 1, 1, 387, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 388, 1, 853,
-
1, 836, 836, 836, 836, 836, 836, 836,
-
836, 836, 836, 836, 836, 836, 836, 836,
-
836, 836, 836, 836, 836, 836, 836, 836,
-
836, 836, 836, 836, 836, 836, 836, 836,
-
836, 836, 836, 836, 836, 836, 836, 836,
-
836, 836, 836, 836, 836, 836, 836, 836,
-
836, 836, 836, 836, 836, 836, 836, 836,
-
836, 836, 836, 836, 836, 836, 836, 836,
-
836, 836, 836, 836, 836, 836, 836, 836,
-
836, 836, 836, 836, 836, 836, 836, 836,
-
836, 836, 836, 836, 836, 836, 836, 836,
-
836, 836, 836, 836, 836, 836, 836, 836,
-
836, 836, 836, 836, 836, 836, 836, 836,
-
836, 836, 836, 836, 836, 836, 836, 836,
-
836, 836, 836, 836, 836, 836, 836, 836,
-
836, 836, 836, 836, 836, 836, 836, 836,
-
836, 1, 836, 836, 836, 836, 836, 836,
-
836, 836, 854, 1, 836, 836, 855, 836,
-
836, 836, 836, 836, 836, 836, 836, 836,
-
836, 836, 836, 836, 836, 836, 836, 836,
-
836, 854, 836, 856, 836, 836, 836, 836,
-
836, 836, 836, 836, 836, 836, 836, 836,
-
836, 836, 836, 836, 836, 836, 836, 836,
-
836, 836, 836, 836, 836, 836, 836, 836,
-
836, 836, 836, 836, 836, 836, 836, 836,
-
836, 836, 836, 836, 836, 836, 836, 836,
-
836, 836, 836, 836, 836, 836, 836, 836,
-
836, 836, 836, 836, 836, 839, 836, 836,
-
836, 836, 836, 836, 836, 836, 836, 836,
-
836, 836, 836, 836, 836, 836, 836, 836,
-
836, 836, 836, 836, 836, 836, 836, 836,
-
836, 836, 836, 836, 836, 836, 836, 836,
-
836, 1, 857, 1, 854, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 854, 1, 858, 1, 1,
-
1, 859, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 858, 1, 1, 1,
-
1, 1, 1, 1, 860, 1, 1, 1,
-
1, 1, 380, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 381, 1, 861, 1, 862, 1,
-
858, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 858,
-
1, 863, 1, 1, 1, 864, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
863, 1, 1, 1, 1, 1, 1, 1,
-
865, 1, 1, 1, 1, 1, 387, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 388, 1,
-
866, 1, 867, 1, 1, 1, 868, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 867, 869, 870, 869, 869, 869, 869,
-
869, 871, 1, 869, 869, 872, 869, 873,
-
869, 869, 869, 869, 869, 869, 869, 869,
-
869, 869, 869, 1, 1, 1, 869, 1,
-
869, 874, 869, 869, 869, 869, 869, 869,
-
869, 869, 869, 869, 869, 869, 869, 869,
-
869, 869, 869, 869, 869, 869, 869, 869,
-
869, 869, 869, 869, 1, 1, 1, 869,
-
869, 869, 869, 869, 869, 869, 869, 869,
-
869, 869, 869, 869, 869, 869, 869, 869,
-
869, 869, 869, 869, 869, 869, 869, 869,
-
869, 869, 869, 869, 869, 869, 869, 869,
-
1, 366, 1, 1, 1, 875, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
366, 1, 1, 1, 1, 1, 1, 1,
-
876, 1, 1, 1, 366, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
367, 1, 877, 1, 366, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 366, 1, 872, 1, 1,
-
1, 878, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 872, 1, 1, 1,
-
1, 1, 1, 1, 879, 1, 1, 1,
-
872, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 874, 1, 880, 1,
-
1, 1, 881, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 880, 882, 883,
-
882, 882, 882, 882, 882, 884, 1, 882,
-
882, 1, 882, 885, 882, 882, 882, 882,
-
882, 882, 882, 882, 882, 882, 882, 1,
-
1, 1, 882, 1, 882, 1, 882, 882,
-
882, 882, 882, 882, 882, 882, 882, 882,
-
882, 882, 882, 882, 882, 882, 882, 882,
-
882, 882, 882, 882, 882, 882, 882, 882,
-
886, 1, 1, 882, 882, 882, 882, 882,
-
882, 882, 882, 882, 882, 882, 882, 882,
-
882, 882, 882, 882, 882, 882, 882, 882,
-
882, 882, 882, 882, 882, 882, 882, 882,
-
882, 882, 882, 882, 1, 887, 1, 1,
-
1, 888, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 887, 889, 890, 889,
-
889, 889, 889, 889, 891, 1, 889, 889,
-
1, 889, 892, 889, 889, 889, 889, 889,
-
889, 889, 889, 889, 889, 889, 1, 1,
-
1, 889, 1, 889, 1, 889, 889, 889,
-
889, 889, 889, 889, 889, 889, 889, 889,
-
889, 889, 889, 889, 889, 889, 889, 889,
-
889, 889, 889, 889, 889, 889, 889, 893,
-
1, 1, 889, 889, 889, 889, 889, 889,
-
889, 889, 889, 889, 889, 889, 889, 889,
-
889, 889, 889, 889, 889, 889, 889, 889,
-
889, 889, 889, 889, 889, 889, 889, 889,
-
889, 889, 889, 1, 894, 1, 887, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 887, 1, 895,
-
1, 1, 1, 896, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 895, 889,
-
890, 889, 889, 889, 889, 889, 897, 1,
-
889, 889, 898, 889, 899, 889, 889, 889,
-
889, 889, 889, 889, 889, 889, 889, 889,
-
900, 1, 1, 889, 1, 889, 1, 889,
-
889, 889, 889, 889, 889, 889, 889, 889,
-
889, 889, 889, 889, 889, 889, 889, 889,
-
889, 889, 889, 889, 889, 889, 889, 889,
-
889, 1, 1, 1, 889, 889, 889, 889,
-
889, 889, 889, 889, 889, 889, 889, 889,
-
889, 889, 889, 889, 889, 889, 889, 889,
-
889, 889, 889, 889, 889, 889, 889, 889,
-
889, 889, 889, 889, 889, 1, 895, 1,
-
1, 1, 896, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 895, 1, 1,
-
1, 1, 1, 1, 1, 897, 1, 1,
-
1, 898, 1, 901, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 900,
-
1, 902, 1, 895, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 895, 1, 903, 1, 1, 1,
-
904, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 903, 1, 1, 1, 1,
-
1, 1, 1, 905, 1, 1, 1, 906,
-
1, 907, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 908, 1, 909,
-
1, 1, 1, 910, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 909, 1,
-
1, 1, 1, 1, 1, 1, 911, 1,
-
1, 1, 909, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
912, 1, 1, 1, 1, 1, 367, 1,
-
913, 1, 909, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 909, 1, 914, 1, 1, 1, 915,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 914, 1, 1, 1, 1, 1,
-
1, 1, 916, 1, 1, 1, 914, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 917, 1, 1, 1,
-
1, 1, 874, 1, 918, 1, 1, 1,
-
919, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 918, 920, 921, 920, 920,
-
920, 920, 920, 922, 1, 920, 920, 1,
-
920, 923, 920, 920, 920, 920, 920, 920,
-
920, 920, 920, 920, 920, 1, 1, 1,
-
920, 1, 920, 1, 920, 920, 920, 920,
-
920, 920, 920, 920, 920, 920, 920, 920,
-
920, 920, 920, 920, 920, 920, 920, 920,
-
920, 920, 920, 920, 920, 920, 1, 1,
-
1, 920, 920, 920, 920, 920, 920, 920,
-
920, 920, 920, 920, 920, 920, 920, 920,
-
920, 920, 920, 920, 920, 920, 920, 920,
-
920, 920, 920, 920, 920, 920, 920, 920,
-
920, 920, 1, 924, 1, 1, 1, 925,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 924, 357, 358, 357, 357, 357,
-
357, 357, 926, 1, 357, 357, 1, 357,
-
361, 357, 357, 357, 357, 357, 357, 357,
-
357, 357, 357, 357, 1, 1, 1, 357,
-
1, 357, 1, 357, 357, 357, 357, 357,
-
357, 357, 357, 357, 357, 357, 357, 357,
-
357, 357, 357, 357, 357, 357, 357, 357,
-
357, 357, 357, 357, 357, 1, 1, 1,
-
357, 357, 357, 357, 357, 357, 357, 357,
-
357, 357, 357, 357, 357, 357, 357, 357,
-
357, 357, 357, 357, 357, 357, 357, 357,
-
357, 357, 357, 357, 357, 357, 357, 357,
-
357, 1, 927, 1, 924, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 924, 1, 928, 1, 1,
-
1, 929, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 928, 869, 870, 869,
-
869, 869, 869, 869, 930, 1, 869, 869,
-
1, 869, 873, 869, 869, 869, 869, 869,
-
869, 869, 869, 869, 869, 869, 1, 1,
-
1, 869, 1, 869, 1, 869, 869, 869,
-
869, 869, 869, 869, 869, 869, 869, 869,
-
869, 869, 869, 869, 869, 869, 869, 869,
-
869, 869, 869, 869, 869, 869, 869, 1,
-
1, 1, 869, 869, 869, 869, 869, 869,
-
869, 869, 869, 869, 869, 869, 869, 869,
-
869, 869, 869, 869, 869, 869, 869, 869,
-
869, 869, 869, 869, 869, 869, 869, 869,
-
869, 869, 869, 1, 795, 372, 795, 795,
-
795, 795, 795, 1, 1, 795, 795, 1,
-
795, 931, 795, 795, 795, 795, 795, 795,
-
795, 795, 795, 795, 795, 1, 1, 1,
-
795, 1, 795, 1, 795, 795, 795, 795,
-
795, 795, 795, 795, 795, 795, 795, 795,
-
795, 795, 795, 795, 795, 795, 795, 795,
-
795, 795, 795, 795, 795, 795, 1, 1,
-
1, 795, 795, 795, 795, 795, 795, 795,
-
795, 795, 795, 795, 795, 795, 795, 795,
-
795, 795, 795, 795, 795, 795, 795, 795,
-
795, 795, 795, 795, 795, 795, 795, 795,
-
795, 795, 1, 901, 1, 1, 1, 932,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 901, 933, 1, 933, 933, 933,
-
933, 933, 934, 1, 933, 933, 1, 933,
-
1, 933, 933, 933, 933, 933, 933, 933,
-
933, 933, 933, 933, 1, 1, 1, 933,
-
1, 933, 1, 933, 933, 933, 933, 933,
-
933, 933, 933, 933, 933, 933, 933, 933,
-
933, 933, 933, 933, 933, 933, 933, 933,
-
933, 933, 933, 933, 933, 1, 1, 1,
-
933, 933, 933, 933, 933, 933, 933, 933,
-
933, 933, 933, 933, 933, 933, 933, 933,
-
933, 933, 933, 933, 933, 933, 933, 933,
-
933, 933, 933, 933, 933, 933, 933, 933,
-
933, 1, 935, 1, 901, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 901, 1, 895, 1, 1,
-
1, 896, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 895, 933, 1, 933,
-
933, 933, 933, 933, 897, 1, 933, 933,
-
898, 933, 901, 933, 933, 933, 933, 933,
-
933, 933, 933, 933, 933, 933, 900, 1,
-
1, 933, 1, 933, 1, 933, 933, 933,
-
933, 933, 933, 933, 933, 933, 933, 933,
-
933, 933, 933, 933, 933, 933, 933, 933,
-
933, 933, 933, 933, 933, 933, 933, 1,
-
1, 1, 933, 933, 933, 933, 933, 933,
-
933, 933, 933, 933, 933, 933, 933, 933,
-
933, 933, 933, 933, 933, 933, 933, 933,
-
933, 933, 933, 933, 933, 933, 933, 933,
-
933, 933, 933, 1, 907, 1, 1, 1,
-
936, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 907, 937, 1, 937, 937,
-
937, 937, 937, 938, 1, 937, 937, 1,
-
937, 1, 937, 937, 937, 937, 937, 937,
-
937, 937, 937, 937, 937, 1, 1, 1,
-
937, 1, 937, 1, 937, 937, 937, 937,
-
937, 937, 937, 937, 937, 937, 937, 937,
-
937, 937, 937, 937, 937, 937, 937, 937,
-
937, 937, 937, 937, 937, 937, 1, 1,
-
1, 937, 937, 937, 937, 937, 937, 937,
-
937, 937, 937, 937, 937, 937, 937, 937,
-
937, 937, 937, 937, 937, 937, 937, 937,
-
937, 937, 937, 937, 937, 937, 937, 937,
-
937, 937, 1, 939, 939, 939, 939, 939,
-
939, 939, 939, 890, 1, 939, 939, 940,
-
939, 939, 939, 939, 939, 939, 939, 939,
-
939, 939, 939, 939, 939, 939, 939, 939,
-
939, 939, 890, 939, 1, 939, 939, 939,
-
939, 939, 939, 939, 939, 939, 939, 939,
-
939, 939, 939, 939, 939, 939, 939, 939,
-
939, 939, 939, 939, 939, 939, 939, 939,
-
939, 939, 939, 939, 939, 939, 939, 939,
-
939, 939, 939, 939, 939, 939, 939, 939,
-
939, 939, 939, 939, 939, 939, 939, 939,
-
939, 939, 939, 939, 939, 939, 941, 939,
-
939, 939, 939, 939, 939, 939, 939, 939,
-
939, 939, 939, 939, 939, 939, 939, 939,
-
939, 939, 939, 939, 939, 939, 939, 939,
-
939, 939, 939, 939, 939, 939, 939, 939,
-
939, 939, 1, 939, 939, 939, 939, 939,
-
939, 939, 939, 939, 1, 939, 939, 942,
-
939, 939, 939, 939, 939, 939, 939, 939,
-
939, 939, 939, 939, 939, 939, 939, 939,
-
939, 939, 939, 939, 943, 939, 939, 939,
-
939, 939, 939, 939, 939, 939, 939, 939,
-
939, 939, 939, 939, 939, 939, 939, 939,
-
939, 939, 939, 939, 939, 939, 939, 939,
-
939, 939, 939, 939, 939, 939, 939, 939,
-
939, 939, 939, 939, 939, 939, 939, 939,
-
939, 939, 939, 939, 939, 939, 939, 939,
-
939, 939, 939, 939, 939, 939, 941, 939,
-
939, 939, 939, 939, 939, 939, 939, 939,
-
939, 939, 939, 939, 939, 939, 939, 939,
-
939, 939, 939, 939, 939, 939, 939, 939,
-
939, 939, 939, 939, 939, 939, 939, 939,
-
939, 939, 1, 944, 1, 939, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 939, 1, 945, 1,
-
1, 1, 946, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 945, 943, 890,
-
943, 943, 943, 943, 943, 947, 1, 943,
-
943, 898, 943, 892, 943, 943, 943, 943,
-
943, 943, 943, 943, 943, 943, 943, 900,
-
1, 1, 943, 1, 943, 1, 943, 943,
-
943, 943, 943, 943, 943, 943, 943, 943,
-
943, 943, 943, 943, 943, 943, 943, 943,
-
943, 943, 943, 943, 943, 943, 943, 943,
-
1, 1, 1, 943, 943, 943, 943, 943,
-
943, 943, 943, 943, 943, 943, 943, 943,
-
943, 943, 943, 943, 943, 943, 943, 943,
-
943, 943, 943, 943, 943, 943, 943, 943,
-
943, 943, 943, 943, 1, 945, 1, 1,
-
1, 946, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 945, 1, 1, 1,
-
1, 1, 1, 1, 947, 1, 1, 1,
-
898, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 900, 1,
-
948, 1, 945, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 945, 1, 949, 1, 1, 1, 950,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 949, 1, 1, 1, 1, 1,
-
1, 1, 951, 1, 1, 1, 906, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 908, 1, 943, 890,
-
943, 943, 943, 943, 943, 1, 1, 943,
-
943, 1, 943, 892, 943, 943, 943, 943,
-
943, 943, 943, 943, 943, 943, 943, 1,
-
1, 1, 943, 1, 943, 1, 943, 943,
-
943, 943, 943, 943, 943, 943, 943, 943,
-
943, 943, 943, 943, 943, 943, 943, 943,
-
943, 943, 943, 943, 943, 943, 943, 943,
-
1, 1, 1, 943, 943, 943, 943, 943,
-
943, 943, 943, 943, 943, 943, 943, 943,
-
943, 943, 943, 943, 943, 943, 943, 943,
-
943, 943, 943, 943, 943, 943, 943, 943,
-
943, 943, 943, 943, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 939, 952, 1,
-
890, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 890,
-
1, 901, 1, 1, 1, 932, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
901, 889, 890, 889, 889, 889, 889, 889,
-
934, 1, 889, 889, 1, 889, 892, 889,
-
889, 889, 889, 889, 889, 889, 889, 889,
-
889, 889, 1, 1, 1, 889, 1, 889,
-
1, 889, 889, 889, 889, 889, 889, 889,
-
889, 889, 889, 889, 889, 889, 889, 889,
-
889, 889, 889, 889, 889, 889, 889, 889,
-
889, 889, 889, 1, 1, 1, 889, 889,
-
889, 889, 889, 889, 889, 889, 889, 889,
-
889, 889, 889, 889, 889, 889, 889, 889,
-
889, 889, 889, 889, 889, 889, 889, 889,
-
889, 889, 889, 889, 889, 889, 889, 1,
-
953, 1, 1, 1, 954, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 953,
-
955, 956, 955, 955, 955, 955, 955, 957,
-
1, 955, 955, 1, 955, 958, 955, 955,
-
955, 955, 955, 955, 955, 955, 955, 955,
-
955, 1, 1, 1, 955, 1, 955, 1,
-
955, 955, 955, 955, 955, 955, 955, 955,
-
955, 955, 955, 955, 955, 955, 955, 955,
-
955, 955, 955, 955, 955, 955, 955, 955,
-
955, 955, 959, 1, 1, 955, 955, 955,
-
955, 955, 955, 955, 955, 955, 955, 955,
-
955, 955, 955, 955, 955, 955, 955, 955,
-
955, 955, 955, 955, 955, 955, 955, 955,
-
955, 955, 955, 955, 955, 955, 1, 893,
-
893, 893, 893, 893, 893, 893, 893, 893,
-
1, 893, 893, 960, 893, 893, 893, 893,
-
893, 893, 893, 893, 893, 893, 893, 893,
-
893, 893, 893, 893, 893, 893, 893, 893,
-
893, 893, 893, 893, 893, 893, 893, 893,
-
893, 893, 893, 893, 893, 893, 893, 893,
-
893, 893, 893, 893, 893, 893, 893, 893,
-
893, 893, 893, 893, 893, 893, 893, 893,
-
893, 893, 893, 893, 893, 893, 893, 893,
-
893, 893, 893, 893, 893, 893, 893, 893,
-
893, 893, 893, 893, 893, 893, 893, 893,
-
893, 1, 961, 945, 893, 893, 893, 893,
-
893, 893, 893, 893, 893, 893, 893, 893,
-
893, 893, 893, 893, 893, 893, 893, 893,
-
893, 893, 893, 893, 893, 893, 893, 893,
-
893, 893, 893, 893, 893, 893, 1, 962,
-
1, 893, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
893, 1, 893, 893, 893, 893, 893, 893,
-
893, 893, 893, 893, 893, 893, 893, 893,
-
893, 893, 893, 893, 893, 893, 893, 893,
-
893, 893, 893, 893, 893, 893, 893, 893,
-
893, 893, 893, 893, 893, 893, 893, 893,
-
893, 893, 893, 893, 893, 893, 893, 893,
-
893, 893, 893, 893, 893, 893, 893, 893,
-
893, 893, 893, 893, 893, 893, 893, 893,
-
893, 893, 893, 893, 893, 893, 893, 893,
-
893, 893, 893, 893, 893, 893, 893, 893,
-
893, 893, 893, 893, 893, 893, 893, 893,
-
893, 893, 893, 893, 893, 893, 893, 893,
-
893, 893, 893, 893, 893, 893, 893, 893,
-
893, 893, 893, 893, 893, 893, 893, 893,
-
893, 893, 893, 893, 893, 893, 893, 893,
-
893, 893, 893, 893, 893, 893, 893, 893,
-
893, 893, 1, 963, 1, 1, 1, 964,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 963, 965, 966, 965, 965, 965,
-
965, 965, 967, 1, 965, 965, 1, 965,
-
968, 965, 965, 965, 965, 965, 965, 965,
-
965, 965, 965, 965, 1, 1, 139, 965,
-
1, 965, 155, 965, 965, 965, 965, 965,
-
965, 965, 965, 965, 965, 965, 965, 965,
-
965, 965, 965, 965, 965, 965, 965, 965,
-
965, 965, 965, 965, 965, 969, 1, 1,
-
965, 965, 965, 965, 965, 965, 965, 965,
-
965, 965, 965, 965, 965, 965, 965, 965,
-
965, 965, 965, 965, 965, 965, 965, 965,
-
965, 965, 965, 965, 965, 965, 965, 965,
-
965, 1, 970, 1, 1, 1, 971, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 970, 972, 973, 972, 972, 972, 972,
-
972, 974, 1, 972, 972, 1, 972, 975,
-
972, 972, 972, 972, 972, 972, 972, 972,
-
972, 972, 972, 1, 1, 129, 972, 1,
-
972, 1, 972, 972, 972, 972, 972, 972,
-
972, 972, 972, 972, 972, 972, 972, 972,
-
972, 972, 972, 972, 972, 972, 972, 972,
-
972, 972, 972, 972, 976, 1, 1, 972,
-
972, 972, 972, 972, 972, 972, 972, 972,
-
972, 972, 972, 972, 972, 972, 972, 972,
-
972, 972, 972, 972, 972, 972, 972, 972,
-
972, 972, 972, 972, 972, 972, 972, 972,
-
1, 977, 1, 970, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 970, 1, 978, 1, 1, 1,
-
979, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 978, 972, 973, 972, 972,
-
972, 972, 972, 980, 1, 972, 972, 981,
-
972, 982, 972, 972, 972, 972, 972, 972,
-
972, 972, 972, 972, 972, 1, 494, 139,
-
972, 1, 972, 155, 972, 972, 972, 972,
-
972, 972, 972, 972, 972, 972, 972, 972,
-
972, 972, 972, 972, 972, 972, 972, 972,
-
972, 972, 972, 972, 972, 972, 1, 1,
-
1, 972, 972, 972, 972, 972, 972, 972,
-
972, 972, 972, 972, 972, 972, 972, 972,
-
972, 972, 972, 972, 972, 972, 972, 972,
-
972, 972, 972, 972, 972, 972, 972, 972,
-
972, 972, 1, 978, 1, 1, 1, 979,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 978, 143, 144, 143, 143, 143,
-
143, 143, 980, 1, 143, 143, 981, 143,
-
983, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 1, 494, 139, 143,
-
1, 143, 155, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 1, 1, 1,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 1, 984, 1, 985, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 985, 1, 986, 1, 1,
-
1, 987, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 986, 168, 169, 168,
-
168, 168, 168, 168, 988, 1, 168, 168,
-
989, 168, 990, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 1, 503,
-
172, 168, 1, 168, 171, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 1,
-
1, 1, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 1, 991, 1, 1, 1,
-
992, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 991, 993, 144, 993, 993,
-
993, 993, 993, 994, 1, 993, 993, 1,
-
993, 155, 993, 993, 993, 993, 993, 993,
-
993, 993, 993, 993, 993, 1, 1, 139,
-
993, 1, 993, 155, 993, 993, 993, 993,
-
993, 993, 993, 993, 993, 993, 993, 993,
-
993, 993, 993, 993, 993, 993, 993, 993,
-
993, 993, 993, 993, 993, 993, 1, 1,
-
1, 993, 993, 993, 993, 993, 993, 993,
-
993, 993, 993, 993, 993, 993, 993, 993,
-
993, 993, 993, 993, 993, 993, 993, 993,
-
993, 993, 993, 993, 993, 993, 993, 993,
-
993, 993, 1, 995, 1, 1, 1, 996,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 995, 993, 144, 993, 993, 993,
-
993, 993, 997, 1, 993, 993, 1, 993,
-
1, 993, 993, 993, 993, 993, 993, 993,
-
993, 993, 993, 993, 1, 1, 129, 993,
-
1, 993, 1, 993, 993, 993, 993, 993,
-
993, 993, 993, 993, 993, 993, 993, 993,
-
993, 993, 993, 993, 993, 993, 993, 993,
-
993, 993, 993, 993, 993, 1, 1, 1,
-
993, 993, 993, 993, 993, 993, 993, 993,
-
993, 993, 993, 993, 993, 993, 993, 993,
-
993, 993, 993, 993, 993, 993, 993, 993,
-
993, 993, 993, 993, 993, 993, 993, 993,
-
993, 1, 998, 1, 995, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 995, 1, 978, 1, 1,
-
1, 979, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 978, 993, 144, 993,
-
993, 993, 993, 993, 980, 1, 993, 993,
-
981, 993, 983, 993, 993, 993, 993, 993,
-
993, 993, 993, 993, 993, 993, 1, 494,
-
139, 993, 1, 993, 155, 993, 993, 993,
-
993, 993, 993, 993, 993, 993, 993, 993,
-
993, 993, 993, 993, 993, 993, 993, 993,
-
993, 993, 993, 993, 993, 993, 993, 1,
-
1, 1, 993, 993, 993, 993, 993, 993,
-
993, 993, 993, 993, 993, 993, 993, 993,
-
993, 993, 993, 993, 993, 993, 993, 993,
-
993, 993, 993, 993, 993, 993, 993, 993,
-
993, 993, 993, 1, 999, 1, 1, 1,
-
1000, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 999, 1001, 169, 1001, 1001,
-
1001, 1001, 1001, 1002, 1, 1001, 1001, 1,
-
1001, 1, 1001, 1001, 1001, 1001, 1001, 1001,
-
1001, 1001, 1001, 1001, 1001, 1, 1, 183,
-
1001, 1, 1001, 1, 1001, 1001, 1001, 1001,
-
1001, 1001, 1001, 1001, 1001, 1001, 1001, 1001,
-
1001, 1001, 1001, 1001, 1001, 1001, 1001, 1001,
-
1001, 1001, 1001, 1001, 1001, 1001, 1, 1,
-
1, 1001, 1001, 1001, 1001, 1001, 1001, 1001,
-
1001, 1001, 1001, 1001, 1001, 1001, 1001, 1001,
-
1001, 1001, 1001, 1001, 1001, 1001, 1001, 1001,
-
1001, 1001, 1001, 1001, 1001, 1001, 1001, 1001,
-
1001, 1001, 1, 1003, 1003, 1003, 1003, 1003,
-
1003, 1003, 1003, 1004, 1, 1003, 1003, 1005,
-
1003, 1003, 1003, 1003, 1003, 1003, 1003, 1003,
-
1003, 1003, 1003, 1003, 1003, 1003, 1003, 1003,
-
1003, 1003, 1004, 1003, 159, 1003, 1003, 1003,
-
1003, 1003, 1003, 1003, 1003, 1003, 1003, 1003,
-
1003, 1003, 1003, 1003, 1003, 1003, 1003, 1003,
-
1003, 1003, 1003, 1003, 1003, 1003, 1003, 1003,
-
1003, 1003, 1003, 1003, 1003, 1003, 1003, 1003,
-
1003, 1003, 1003, 1003, 1003, 1003, 1003, 1003,
-
1003, 1003, 1003, 1003, 1003, 1003, 1003, 1003,
-
1003, 1003, 1003, 1003, 1003, 1003, 1006, 1003,
-
1003, 1003, 1003, 1003, 1003, 1003, 1003, 1003,
-
1003, 1003, 1003, 1003, 1003, 1003, 1003, 1003,
-
1003, 1003, 1003, 1003, 1003, 1003, 1003, 1003,
-
1003, 1003, 1003, 1003, 1003, 1003, 1003, 1003,
-
1003, 1003, 1, 1007, 1007, 1007, 1007, 1007,
-
1007, 1007, 1007, 1007, 1, 1007, 1007, 1008,
-
1007, 1007, 1007, 1007, 1007, 1007, 1007, 1007,
-
1007, 1007, 1007, 1007, 1007, 1007, 1007, 1007,
-
1007, 1007, 1007, 1007, 1009, 1007, 1007, 1007,
-
1007, 1007, 1007, 1007, 1007, 1007, 1007, 1007,
-
1007, 1007, 1007, 1007, 1007, 1007, 1007, 1007,
-
1007, 1007, 1007, 1007, 1007, 1007, 1007, 1007,
-
1007, 1007, 1007, 1007, 1007, 1007, 1007, 1007,
-
1007, 1007, 1007, 1007, 1007, 1007, 1007, 1007,
-
1007, 1007, 1007, 1007, 1007, 1007, 1007, 1007,
-
1007, 1007, 1007, 1007, 1007, 1007, 1010, 1007,
-
1007, 1007, 1007, 1007, 1007, 1007, 1007, 1007,
-
1007, 1007, 1007, 1007, 1007, 1007, 1007, 1007,
-
1007, 1007, 1007, 1007, 1007, 1007, 1007, 1007,
-
1007, 1007, 1007, 1007, 1007, 1007, 1007, 1007,
-
1007, 1007, 1, 1011, 1, 1007, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1007, 1, 1012, 1,
-
1, 1, 1013, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1012, 1014, 973,
-
1014, 1014, 1014, 1014, 1014, 1015, 1, 1014,
-
1014, 981, 1014, 1016, 1014, 1014, 1014, 1014,
-
1014, 1014, 1014, 1014, 1014, 1014, 1014, 1,
-
494, 139, 1014, 1, 1014, 155, 1014, 1014,
-
1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014,
-
1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014,
-
1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014,
-
1, 1, 1, 1014, 1014, 1014, 1014, 1014,
-
1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014,
-
1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014,
-
1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014,
-
1014, 1014, 1014, 1014, 1, 1012, 1, 1,
-
1, 1013, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1012, 143, 144, 143,
-
143, 143, 143, 143, 1015, 1, 143, 143,
-
981, 143, 155, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 1, 494,
-
139, 143, 1, 143, 155, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 1,
-
1, 1, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 1, 1017, 1, 1018, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1018, 1, 1019,
-
1, 1, 1, 1020, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1019, 168,
-
169, 168, 168, 168, 168, 168, 1021, 1,
-
168, 168, 989, 168, 171, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
1, 503, 172, 168, 1, 168, 171, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 1, 1, 1, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 1, 173, 1,
-
1, 1, 174, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 173, 1014, 973,
-
1014, 1014, 1014, 1014, 1014, 175, 1, 1014,
-
1014, 1, 1014, 1016, 1014, 1014, 1014, 1014,
-
1014, 1014, 1014, 1014, 1014, 1014, 1014, 1,
-
1, 139, 1014, 1, 1014, 155, 1014, 1014,
-
1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014,
-
1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014,
-
1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014,
-
1, 1, 1, 1014, 1014, 1014, 1014, 1014,
-
1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014,
-
1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014,
-
1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014,
-
1014, 1014, 1014, 1014, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1007, 1007, 1007,
-
1007, 1007, 1007, 1007, 1007, 1007, 1022, 1,
-
1007, 1007, 1023, 1007, 1007, 1007, 1007, 1007,
-
1007, 1007, 1007, 1007, 1007, 1007, 1007, 1007,
-
1007, 1007, 1007, 1007, 1007, 1022, 1007, 163,
-
1007, 1007, 1007, 1007, 1007, 1007, 1007, 1007,
-
1007, 1007, 1007, 1007, 1007, 1007, 1007, 1007,
-
1007, 1007, 1007, 1007, 1007, 1007, 1007, 1007,
-
1007, 1007, 1007, 1007, 1007, 1007, 1007, 1007,
-
1007, 1007, 1007, 1007, 1007, 1007, 1007, 1007,
-
1007, 1007, 1007, 1007, 1007, 1007, 1007, 1007,
-
1007, 1007, 1007, 1007, 1007, 1007, 1007, 1007,
-
1007, 1010, 1007, 1007, 1007, 1007, 1007, 1007,
-
1007, 1007, 1007, 1007, 1007, 1007, 1007, 1007,
-
1007, 1007, 1007, 1007, 1007, 1007, 1007, 1007,
-
1007, 1007, 1007, 1007, 1007, 1007, 1007, 1007,
-
1007, 1007, 1007, 1007, 1007, 1, 1024, 1,
-
1022, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1022,
-
1, 991, 1, 1, 1, 992, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
991, 972, 973, 972, 972, 972, 972, 972,
-
994, 1, 972, 972, 1, 972, 1016, 972,
-
972, 972, 972, 972, 972, 972, 972, 972,
-
972, 972, 1, 1, 139, 972, 1, 972,
-
155, 972, 972, 972, 972, 972, 972, 972,
-
972, 972, 972, 972, 972, 972, 972, 972,
-
972, 972, 972, 972, 972, 972, 972, 972,
-
972, 972, 972, 1, 1, 1, 972, 972,
-
972, 972, 972, 972, 972, 972, 972, 972,
-
972, 972, 972, 972, 972, 972, 972, 972,
-
972, 972, 972, 972, 972, 972, 972, 972,
-
972, 972, 972, 972, 972, 972, 972, 1,
-
1025, 1, 1, 1, 1026, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1025,
-
1027, 1028, 1027, 1027, 1027, 1027, 1027, 1029,
-
1, 1027, 1027, 1, 1027, 1030, 1027, 1027,
-
1027, 1027, 1027, 1027, 1027, 1027, 1027, 1027,
-
1027, 1, 1, 183, 1027, 1, 1027, 1,
-
1027, 1027, 1027, 1027, 1027, 1027, 1027, 1027,
-
1027, 1027, 1027, 1027, 1027, 1027, 1027, 1027,
-
1027, 1027, 1027, 1027, 1027, 1027, 1027, 1027,
-
1027, 1027, 1031, 1, 1, 1027, 1027, 1027,
-
1027, 1027, 1027, 1027, 1027, 1027, 1027, 1027,
-
1027, 1027, 1027, 1027, 1027, 1027, 1027, 1027,
-
1027, 1027, 1027, 1027, 1027, 1027, 1027, 1027,
-
1027, 1027, 1027, 1027, 1027, 1027, 1, 1032,
-
1033, 1032, 1032, 1032, 1032, 1032, 1, 1,
-
1032, 1032, 1, 1032, 975, 1032, 1032, 1032,
-
1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032,
-
1, 1, 1, 1032, 1, 1032, 1, 1032,
-
1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032,
-
1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032,
-
1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032,
-
1032, 1, 1, 1, 1032, 1032, 1032, 1032,
-
1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032,
-
1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032,
-
1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032,
-
1032, 1032, 1032, 1032, 1032, 1, 1034, 1,
-
1, 1, 1035, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1034, 1032, 1033,
-
1032, 1032, 1032, 1032, 1032, 1036, 1, 1032,
-
1032, 981, 1032, 975, 1032, 1032, 1032, 1032,
-
1032, 1032, 1032, 1032, 1032, 1032, 1032, 1,
-
494, 1, 1032, 1, 1032, 1, 1032, 1032,
-
1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032,
-
1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032,
-
1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032,
-
1, 1, 1, 1032, 1032, 1032, 1032, 1032,
-
1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032,
-
1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032,
-
1032, 1032, 1032, 1032, 1032, 1032, 1032, 1032,
-
1032, 1032, 1032, 1032, 1, 1034, 1, 1,
-
1, 1035, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1034, 1, 1, 1,
-
1, 1, 1, 1, 1036, 1, 1, 1,
-
981, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 494,
-
1, 1037, 1, 1034, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1034, 1, 1038, 1, 1, 1,
-
1039, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1038, 1, 1, 1, 1,
-
1, 1, 1, 1040, 1, 1, 1, 989,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 503, 1,
-
1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041,
-
1033, 1, 1041, 1041, 1042, 1041, 1041, 1041,
-
1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041,
-
1041, 1041, 1041, 1041, 1041, 1041, 1041, 1033,
-
1041, 1, 1041, 1041, 1041, 1041, 1041, 1041,
-
1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041,
-
1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041,
-
1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041,
-
1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041,
-
1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041,
-
1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041,
-
1041, 1041, 1041, 1043, 1041, 1041, 1041, 1041,
-
1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041,
-
1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041,
-
1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041,
-
1041, 1041, 1041, 1041, 1041, 1041, 1041, 1,
-
1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041,
-
1041, 1, 1041, 1041, 1044, 1041, 1041, 1041,
-
1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041,
-
1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041,
-
1041, 1032, 1041, 1041, 1041, 1041, 1041, 1041,
-
1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041,
-
1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041,
-
1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041,
-
1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041,
-
1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041,
-
1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041,
-
1041, 1041, 1041, 1043, 1041, 1041, 1041, 1041,
-
1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041,
-
1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041,
-
1041, 1041, 1041, 1041, 1041, 1041, 1041, 1041,
-
1041, 1041, 1041, 1041, 1041, 1041, 1041, 1,
-
1045, 1, 1041, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1041, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1041, 1046, 1, 1033, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1033, 1, 976,
-
976, 976, 976, 976, 976, 976, 976, 976,
-
1, 976, 976, 1047, 976, 976, 976, 976,
-
976, 976, 976, 976, 976, 976, 976, 976,
-
976, 976, 976, 976, 976, 976, 976, 976,
-
976, 976, 976, 976, 976, 976, 976, 976,
-
976, 976, 976, 976, 976, 976, 976, 976,
-
976, 976, 976, 976, 976, 976, 976, 976,
-
976, 976, 976, 976, 976, 976, 976, 976,
-
976, 976, 976, 976, 976, 976, 976, 976,
-
976, 976, 976, 976, 976, 976, 976, 976,
-
976, 976, 976, 976, 976, 976, 976, 976,
-
976, 1, 1048, 1034, 976, 976, 976, 976,
-
976, 976, 976, 976, 976, 976, 976, 976,
-
976, 976, 976, 976, 976, 976, 976, 976,
-
976, 976, 976, 976, 976, 976, 976, 976,
-
976, 976, 976, 976, 976, 976, 1, 1049,
-
1, 976, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
976, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 976, 1050, 1050, 1050, 1050, 1050,
-
1050, 1050, 1050, 1050, 1, 1050, 1050, 1051,
-
1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050,
-
1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050,
-
1050, 1050, 1050, 1050, 1052, 1050, 1050, 1050,
-
1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050,
-
1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050,
-
1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050,
-
1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050,
-
1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050,
-
1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050,
-
1050, 1050, 1050, 1050, 1050, 1050, 1053, 1050,
-
1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050,
-
1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050,
-
1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050,
-
1050, 1050, 1050, 1050, 1050, 1050, 1050, 1050,
-
1050, 1050, 1, 1054, 1054, 1054, 1054, 1054,
-
1054, 1054, 1054, 1054, 1, 1054, 1054, 1055,
-
1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054,
-
1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054,
-
1054, 1054, 1054, 1054, 1056, 1054, 1054, 1054,
-
1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054,
-
1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054,
-
1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054,
-
1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054,
-
1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054,
-
1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054,
-
1054, 1054, 1054, 1054, 1054, 1054, 1057, 1054,
-
1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054,
-
1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054,
-
1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054,
-
1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054,
-
1054, 1054, 1, 1058, 1, 1054, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1054, 1, 1054, 1054,
-
1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054,
-
1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054,
-
1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054,
-
1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054,
-
1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054,
-
1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054,
-
1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054,
-
1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054,
-
1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054,
-
1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054,
-
1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054,
-
1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054,
-
1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054,
-
1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054,
-
1054, 1054, 1054, 1054, 1054, 1054, 1054, 1054,
-
1054, 1054, 1054, 1054, 1054, 1054, 1, 1059,
-
1, 1, 1, 1060, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1059, 1061,
-
1062, 1061, 1061, 1061, 1061, 1061, 1063, 1,
-
1061, 1061, 1, 1061, 1, 1061, 1061, 1061,
-
1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061,
-
1, 1, 183, 1061, 1, 1061, 1, 1061,
-
1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061,
-
1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061,
-
1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061,
-
1061, 1, 1, 1, 1061, 1061, 1061, 1061,
-
1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061,
-
1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061,
-
1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061,
-
1061, 1061, 1061, 1061, 1061, 1, 1064, 1,
-
1, 1, 1065, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1064, 262, 134,
-
262, 262, 262, 262, 262, 1066, 1, 262,
-
262, 136, 262, 1067, 262, 262, 262, 262,
-
262, 262, 262, 262, 262, 262, 262, 1,
-
138, 139, 262, 1, 262, 140, 262, 262,
-
262, 262, 262, 262, 262, 262, 262, 262,
-
262, 262, 262, 262, 262, 262, 262, 262,
-
262, 262, 262, 262, 262, 262, 262, 262,
-
1, 1, 1, 262, 262, 262, 262, 262,
-
262, 262, 262, 262, 262, 262, 262, 262,
-
262, 262, 262, 262, 262, 262, 262, 262,
-
262, 262, 262, 262, 262, 262, 262, 262,
-
262, 262, 262, 262, 1, 1068, 1, 1,
-
1, 1069, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1068, 143, 144, 143,
-
143, 143, 143, 143, 1070, 1, 143, 143,
-
146, 143, 155, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 1, 148,
-
139, 143, 1, 143, 149, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 1,
-
1, 1, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 1, 1071, 1, 1072, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1072, 1, 1073,
-
1, 1, 1, 1074, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1073, 168,
-
169, 168, 168, 168, 168, 168, 1075, 1,
-
168, 168, 1076, 168, 171, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
1, 590, 172, 168, 1, 168, 1077, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 1, 1, 1, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 1, 1078, 1078,
-
1078, 1078, 1078, 1078, 1078, 1078, 1079, 1,
-
1078, 1078, 1080, 1078, 1078, 1078, 1078, 1078,
-
1078, 1078, 1078, 1078, 1078, 1078, 1078, 1078,
-
1078, 1078, 1078, 1078, 1078, 1079, 1078, 159,
-
1078, 1078, 1078, 1078, 1078, 1078, 1078, 1078,
-
1078, 1078, 1078, 1078, 1078, 1078, 1078, 1078,
-
1078, 1078, 1078, 1078, 1078, 1078, 1078, 1078,
-
1078, 1078, 1078, 1078, 1078, 1078, 1078, 1078,
-
1078, 1078, 1078, 1078, 1078, 1078, 1078, 1078,
-
1078, 1078, 1078, 1078, 1078, 1078, 1078, 1078,
-
1078, 1078, 1078, 1078, 1078, 1078, 1078, 1078,
-
1078, 1081, 1078, 1078, 1078, 1078, 1078, 1078,
-
1078, 1078, 1078, 1078, 1078, 1078, 1078, 1078,
-
1078, 1078, 1078, 1078, 1078, 1078, 1078, 1078,
-
1078, 1078, 1078, 1078, 1078, 1078, 1078, 1078,
-
1078, 1078, 1078, 1078, 1078, 1, 1082, 1082,
-
1082, 1082, 1082, 1082, 1082, 1082, 1082, 1,
-
1082, 1082, 1083, 1082, 1082, 1082, 1082, 1082,
-
1082, 1082, 1082, 1082, 1082, 1082, 1082, 1082,
-
1082, 1082, 1082, 1082, 1082, 1082, 1082, 1084,
-
1082, 1082, 1082, 1082, 1082, 1082, 1082, 1082,
-
1082, 1082, 1082, 1082, 1082, 1082, 1082, 1082,
-
1082, 1082, 1082, 1082, 1082, 1082, 1082, 1082,
-
1082, 1082, 1082, 1082, 1082, 1082, 1082, 1082,
-
1082, 1082, 1082, 1082, 1082, 1082, 1082, 1082,
-
1082, 1082, 1082, 1082, 1082, 1082, 1082, 1082,
-
1082, 1082, 1082, 1082, 1082, 1082, 1082, 1082,
-
1082, 1085, 1082, 1082, 1082, 1082, 1082, 1082,
-
1082, 1082, 1082, 1082, 1082, 1082, 1082, 1082,
-
1082, 1082, 1082, 1082, 1082, 1082, 1082, 1082,
-
1082, 1082, 1082, 1082, 1082, 1082, 1082, 1082,
-
1082, 1082, 1082, 1082, 1082, 1, 1086, 1,
-
1082, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1082,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1082, 1082, 1082, 1082, 1082, 1082, 1082,
-
1082, 1082, 1087, 1, 1082, 1082, 1088, 1082,
-
1082, 1082, 1082, 1082, 1082, 1082, 1082, 1082,
-
1082, 1082, 1082, 1082, 1082, 1082, 1082, 1082,
-
1082, 1087, 1082, 163, 1082, 1082, 1082, 1082,
-
1082, 1082, 1082, 1082, 1082, 1082, 1082, 1082,
-
1082, 1082, 1082, 1082, 1082, 1082, 1082, 1082,
-
1082, 1082, 1082, 1082, 1082, 1082, 1082, 1082,
-
1082, 1082, 1082, 1082, 1082, 1082, 1082, 1082,
-
1082, 1082, 1082, 1082, 1082, 1082, 1082, 1082,
-
1082, 1082, 1082, 1082, 1082, 1082, 1082, 1082,
-
1082, 1082, 1082, 1082, 1082, 1085, 1082, 1082,
-
1082, 1082, 1082, 1082, 1082, 1082, 1082, 1082,
-
1082, 1082, 1082, 1082, 1082, 1082, 1082, 1082,
-
1082, 1082, 1082, 1082, 1082, 1082, 1082, 1082,
-
1082, 1082, 1082, 1082, 1082, 1082, 1082, 1082,
-
1082, 1, 1089, 1, 1087, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1087, 1, 1090, 1, 1,
-
1, 1091, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1090, 262, 134, 262,
-
262, 262, 262, 262, 1092, 1, 262, 262,
-
136, 262, 1067, 262, 262, 262, 262, 262,
-
262, 262, 262, 262, 262, 262, 1, 138,
-
139, 262, 1, 262, 140, 262, 262, 262,
-
262, 262, 262, 262, 262, 262, 262, 262,
-
262, 262, 262, 262, 262, 262, 262, 262,
-
262, 262, 262, 262, 262, 262, 262, 1,
-
1, 1, 262, 262, 262, 262, 262, 262,
-
262, 262, 262, 262, 262, 262, 262, 262,
-
262, 262, 262, 262, 262, 262, 262, 262,
-
262, 262, 262, 262, 262, 262, 262, 262,
-
262, 262, 262, 1, 1093, 1, 1, 1,
-
1094, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1093, 143, 144, 143, 143,
-
143, 143, 143, 1095, 1, 143, 143, 146,
-
143, 1, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 1, 148, 129,
-
143, 1, 143, 1096, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 1, 1,
-
1, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 1, 1097, 1, 1093, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1093, 1, 1098, 1,
-
1, 1, 1099, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1098, 168, 169,
-
168, 168, 168, 168, 168, 1100, 1, 168,
-
168, 1076, 168, 1, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 1,
-
590, 183, 168, 1, 168, 1101, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
1, 1, 1, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 1, 1102, 1, 1,
-
1, 1103, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1102, 1104, 1105, 1104,
-
1104, 1104, 1104, 1104, 1106, 1, 1104, 1104,
-
1, 1104, 1107, 1104, 1104, 1104, 1104, 1104,
-
1104, 1104, 1104, 1104, 1104, 1104, 1, 1,
-
1, 1104, 1, 1104, 1, 1104, 1104, 1104,
-
1104, 1104, 1104, 1104, 1104, 1104, 1104, 1104,
-
1104, 1104, 1104, 1104, 1104, 1104, 1104, 1104,
-
1104, 1104, 1104, 1104, 1104, 1104, 1104, 969,
-
1, 1, 1104, 1104, 1104, 1104, 1104, 1104,
-
1104, 1104, 1104, 1104, 1104, 1104, 1104, 1104,
-
1104, 1104, 1104, 1104, 1104, 1104, 1104, 1104,
-
1104, 1104, 1104, 1104, 1104, 1104, 1104, 1104,
-
1104, 1104, 1104, 1, 1108, 1, 1, 1,
-
1109, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1108, 1110, 1033, 1110, 1110,
-
1110, 1110, 1110, 1111, 1, 1110, 1110, 1,
-
1110, 975, 1110, 1110, 1110, 1110, 1110, 1110,
-
1110, 1110, 1110, 1110, 1110, 1, 1, 1,
-
1110, 1, 1110, 1, 1110, 1110, 1110, 1110,
-
1110, 1110, 1110, 1110, 1110, 1110, 1110, 1110,
-
1110, 1110, 1110, 1110, 1110, 1110, 1110, 1110,
-
1110, 1110, 1110, 1110, 1110, 1110, 976, 1,
-
1, 1110, 1110, 1110, 1110, 1110, 1110, 1110,
-
1110, 1110, 1110, 1110, 1110, 1110, 1110, 1110,
-
1110, 1110, 1110, 1110, 1110, 1110, 1110, 1110,
-
1110, 1110, 1110, 1110, 1110, 1110, 1110, 1110,
-
1110, 1110, 1, 1112, 1, 1108, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1108, 1, 1113, 1,
-
1, 1, 1114, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1113, 1110, 1033,
-
1110, 1110, 1110, 1110, 1110, 1115, 1, 1110,
-
1110, 981, 1110, 1116, 1110, 1110, 1110, 1110,
-
1110, 1110, 1110, 1110, 1110, 1110, 1110, 1,
-
494, 1, 1110, 1, 1110, 1, 1110, 1110,
-
1110, 1110, 1110, 1110, 1110, 1110, 1110, 1110,
-
1110, 1110, 1110, 1110, 1110, 1110, 1110, 1110,
-
1110, 1110, 1110, 1110, 1110, 1110, 1110, 1110,
-
1, 1, 1, 1110, 1110, 1110, 1110, 1110,
-
1110, 1110, 1110, 1110, 1110, 1110, 1110, 1110,
-
1110, 1110, 1110, 1110, 1110, 1110, 1110, 1110,
-
1110, 1110, 1110, 1110, 1110, 1110, 1110, 1110,
-
1110, 1110, 1110, 1110, 1, 1113, 1, 1,
-
1, 1114, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1113, 1, 1, 1,
-
1, 1, 1, 1, 1115, 1, 1, 1,
-
981, 1, 1117, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 494,
-
1, 1118, 1, 1113, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1113, 1, 1119, 1, 1, 1,
-
1120, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1119, 1, 1, 1, 1,
-
1, 1, 1, 1121, 1, 1, 1, 989,
-
1, 1122, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 503, 1,
-
1117, 1, 1, 1, 1123, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1117,
-
1124, 1, 1124, 1124, 1124, 1124, 1124, 1125,
-
1, 1124, 1124, 1, 1124, 1, 1124, 1124,
-
1124, 1124, 1124, 1124, 1124, 1124, 1124, 1124,
-
1124, 1, 1, 1, 1124, 1, 1124, 1,
-
1124, 1124, 1124, 1124, 1124, 1124, 1124, 1124,
-
1124, 1124, 1124, 1124, 1124, 1124, 1124, 1124,
-
1124, 1124, 1124, 1124, 1124, 1124, 1124, 1124,
-
1124, 1124, 1, 1, 1, 1124, 1124, 1124,
-
1124, 1124, 1124, 1124, 1124, 1124, 1124, 1124,
-
1124, 1124, 1124, 1124, 1124, 1124, 1124, 1124,
-
1124, 1124, 1124, 1124, 1124, 1124, 1124, 1124,
-
1124, 1124, 1124, 1124, 1124, 1124, 1, 1126,
-
1, 1117, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1117, 1, 1113, 1, 1, 1, 1114, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1113, 1124, 1, 1124, 1124, 1124, 1124,
-
1124, 1115, 1, 1124, 1124, 981, 1124, 1117,
-
1124, 1124, 1124, 1124, 1124, 1124, 1124, 1124,
-
1124, 1124, 1124, 1, 494, 1, 1124, 1,
-
1124, 1, 1124, 1124, 1124, 1124, 1124, 1124,
-
1124, 1124, 1124, 1124, 1124, 1124, 1124, 1124,
-
1124, 1124, 1124, 1124, 1124, 1124, 1124, 1124,
-
1124, 1124, 1124, 1124, 1, 1, 1, 1124,
-
1124, 1124, 1124, 1124, 1124, 1124, 1124, 1124,
-
1124, 1124, 1124, 1124, 1124, 1124, 1124, 1124,
-
1124, 1124, 1124, 1124, 1124, 1124, 1124, 1124,
-
1124, 1124, 1124, 1124, 1124, 1124, 1124, 1124,
-
1, 1122, 1, 1, 1, 1127, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1122, 1128, 1, 1128, 1128, 1128, 1128, 1128,
-
1129, 1, 1128, 1128, 1, 1128, 1, 1128,
-
1128, 1128, 1128, 1128, 1128, 1128, 1128, 1128,
-
1128, 1128, 1, 1, 1, 1128, 1, 1128,
-
1, 1128, 1128, 1128, 1128, 1128, 1128, 1128,
-
1128, 1128, 1128, 1128, 1128, 1128, 1128, 1128,
-
1128, 1128, 1128, 1128, 1128, 1128, 1128, 1128,
-
1128, 1128, 1128, 1, 1, 1, 1128, 1128,
-
1128, 1128, 1128, 1128, 1128, 1128, 1128, 1128,
-
1128, 1128, 1128, 1128, 1128, 1128, 1128, 1128,
-
1128, 1128, 1128, 1128, 1128, 1128, 1128, 1128,
-
1128, 1128, 1128, 1128, 1128, 1128, 1128, 1,
-
1117, 1, 1, 1, 1123, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1117,
-
1110, 1033, 1110, 1110, 1110, 1110, 1110, 1125,
-
1, 1110, 1110, 1, 1110, 975, 1110, 1110,
-
1110, 1110, 1110, 1110, 1110, 1110, 1110, 1110,
-
1110, 1, 1, 1, 1110, 1, 1110, 1,
-
1110, 1110, 1110, 1110, 1110, 1110, 1110, 1110,
-
1110, 1110, 1110, 1110, 1110, 1110, 1110, 1110,
-
1110, 1110, 1110, 1110, 1110, 1110, 1110, 1110,
-
1110, 1110, 1, 1, 1, 1110, 1110, 1110,
-
1110, 1110, 1110, 1110, 1110, 1110, 1110, 1110,
-
1110, 1110, 1110, 1110, 1110, 1110, 1110, 1110,
-
1110, 1110, 1110, 1110, 1110, 1110, 1110, 1110,
-
1110, 1110, 1110, 1110, 1110, 1110, 1, 1130,
-
1, 1, 1, 1131, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1130, 1132,
-
1133, 1132, 1132, 1132, 1132, 1132, 1134, 1,
-
1132, 1132, 1, 1132, 1030, 1132, 1132, 1132,
-
1132, 1132, 1132, 1132, 1132, 1132, 1132, 1132,
-
1, 1, 1, 1132, 1, 1132, 1, 1132,
-
1132, 1132, 1132, 1132, 1132, 1132, 1132, 1132,
-
1132, 1132, 1132, 1132, 1132, 1132, 1132, 1132,
-
1132, 1132, 1132, 1132, 1132, 1132, 1132, 1132,
-
1132, 1031, 1, 1, 1132, 1132, 1132, 1132,
-
1132, 1132, 1132, 1132, 1132, 1132, 1132, 1132,
-
1132, 1132, 1132, 1132, 1132, 1132, 1132, 1132,
-
1132, 1132, 1132, 1132, 1132, 1132, 1132, 1132,
-
1132, 1132, 1132, 1132, 1132, 1, 1135, 1,
-
1, 1, 1136, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1135, 133, 1137,
-
133, 133, 133, 133, 133, 1138, 1, 133,
-
133, 136, 133, 1067, 133, 133, 133, 133,
-
133, 133, 133, 133, 133, 133, 133, 1,
-
138, 139, 133, 1, 133, 140, 133, 133,
-
133, 133, 133, 133, 133, 133, 133, 133,
-
133, 133, 133, 133, 133, 133, 133, 133,
-
133, 133, 133, 133, 133, 133, 133, 133,
-
1, 1, 1, 133, 133, 133, 133, 133,
-
133, 133, 133, 133, 133, 133, 133, 133,
-
133, 133, 133, 133, 133, 133, 133, 133,
-
133, 133, 133, 133, 133, 133, 133, 133,
-
133, 133, 133, 133, 1, 1139, 1, 1,
-
1, 1140, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1139, 284, 285, 284,
-
284, 284, 284, 284, 1141, 1, 284, 284,
-
146, 284, 1, 284, 284, 284, 284, 284,
-
284, 284, 284, 284, 284, 284, 1, 148,
-
129, 284, 1, 284, 1096, 284, 284, 284,
-
284, 284, 284, 284, 284, 284, 284, 284,
-
284, 284, 284, 284, 284, 284, 284, 284,
-
284, 284, 284, 284, 284, 284, 284, 1,
-
1, 1, 284, 284, 284, 284, 284, 284,
-
284, 284, 284, 284, 284, 284, 284, 284,
-
284, 284, 284, 284, 284, 284, 284, 284,
-
284, 284, 284, 284, 284, 284, 284, 284,
-
284, 284, 284, 1, 1142, 1, 1139, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1139, 1, 1143,
-
1, 1, 1, 1144, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1143, 1061,
-
1062, 1061, 1061, 1061, 1061, 1061, 1145, 1,
-
1061, 1061, 1076, 1061, 1, 1061, 1061, 1061,
-
1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061,
-
1, 590, 183, 1061, 1, 1061, 1101, 1061,
-
1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061,
-
1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061,
-
1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061,
-
1061, 1, 1, 1, 1061, 1061, 1061, 1061,
-
1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061,
-
1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061,
-
1061, 1061, 1061, 1061, 1061, 1061, 1061, 1061,
-
1061, 1061, 1061, 1061, 1061, 1, 1146, 1146,
-
1146, 1146, 1146, 1146, 1146, 1146, 1147, 1,
-
1146, 1146, 1148, 1146, 1146, 1146, 1146, 1146,
-
1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146,
-
1146, 1146, 1146, 1146, 1146, 1147, 1146, 1052,
-
1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146,
-
1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146,
-
1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146,
-
1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146,
-
1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146,
-
1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146,
-
1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146,
-
1146, 1149, 1146, 1146, 1146, 1146, 1146, 1146,
-
1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146,
-
1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146,
-
1146, 1146, 1146, 1146, 1146, 1146, 1146, 1146,
-
1146, 1146, 1146, 1146, 1146, 1, 1150, 1150,
-
1150, 1150, 1150, 1150, 1150, 1150, 1150, 1,
-
1150, 1150, 1151, 1150, 1150, 1150, 1150, 1150,
-
1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150,
-
1150, 1150, 1150, 1150, 1150, 1150, 1150, 1152,
-
1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150,
-
1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150,
-
1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150,
-
1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150,
-
1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150,
-
1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150,
-
1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150,
-
1150, 1153, 1150, 1150, 1150, 1150, 1150, 1150,
-
1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150,
-
1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150,
-
1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150,
-
1150, 1150, 1150, 1150, 1150, 1, 1154, 1,
-
1150, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1150,
-
1, 131, 1, 1, 1, 132, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
131, 262, 134, 262, 262, 262, 262, 262,
-
135, 1, 262, 262, 136, 262, 137, 262,
-
262, 262, 262, 262, 262, 262, 262, 262,
-
262, 262, 1, 138, 139, 262, 1, 262,
-
140, 262, 262, 262, 262, 262, 262, 262,
-
262, 262, 262, 262, 262, 262, 262, 262,
-
262, 262, 262, 262, 262, 262, 262, 262,
-
262, 262, 262, 1, 1, 1, 262, 262,
-
262, 262, 262, 262, 262, 262, 262, 262,
-
262, 262, 262, 262, 262, 262, 262, 262,
-
262, 262, 262, 262, 262, 262, 262, 262,
-
262, 262, 262, 262, 262, 262, 262, 1,
-
1155, 1, 1, 1, 1156, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1155,
-
168, 169, 168, 168, 168, 168, 168, 1157,
-
1, 168, 168, 1076, 168, 279, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 1, 590, 172, 168, 1, 168, 1077,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 1, 1, 1, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 1, 1150,
-
1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150,
-
1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150,
-
1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150,
-
1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150,
-
1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150,
-
1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150,
-
1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150,
-
1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150,
-
1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150,
-
1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150,
-
1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150,
-
1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150,
-
1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150,
-
1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150,
-
1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150,
-
1150, 1150, 1150, 1150, 1150, 1150, 1150, 1,
-
1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150,
-
1158, 1, 1150, 1150, 1159, 1150, 1150, 1150,
-
1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150,
-
1150, 1150, 1150, 1150, 1150, 1150, 1150, 1158,
-
1150, 1056, 1150, 1150, 1150, 1150, 1150, 1150,
-
1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150,
-
1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150,
-
1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150,
-
1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150,
-
1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150,
-
1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150,
-
1150, 1150, 1150, 1153, 1150, 1150, 1150, 1150,
-
1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150,
-
1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150,
-
1150, 1150, 1150, 1150, 1150, 1150, 1150, 1150,
-
1150, 1150, 1150, 1150, 1150, 1150, 1150, 1,
-
1160, 1, 1158, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1158, 1, 255, 255, 255, 255, 255,
-
255, 255, 255, 255, 255, 255, 255, 255,
-
255, 255, 255, 255, 255, 255, 255, 255,
-
255, 255, 255, 255, 255, 255, 255, 255,
-
255, 255, 255, 255, 255, 255, 255, 255,
-
255, 255, 255, 255, 255, 255, 255, 255,
-
255, 255, 255, 255, 255, 255, 255, 255,
-
255, 255, 255, 255, 255, 255, 255, 255,
-
255, 255, 255, 255, 255, 255, 255, 255,
-
255, 255, 255, 255, 255, 255, 255, 255,
-
255, 255, 255, 255, 255, 255, 255, 255,
-
255, 255, 255, 255, 255, 255, 255, 255,
-
255, 255, 255, 255, 255, 255, 255, 255,
-
255, 255, 255, 255, 255, 255, 255, 255,
-
255, 255, 255, 255, 255, 255, 255, 255,
-
255, 255, 255, 255, 255, 255, 255, 255,
-
255, 255, 255, 1, 255, 255, 255, 255,
-
255, 255, 255, 255, 1161, 1, 255, 255,
-
1162, 255, 255, 255, 255, 255, 255, 255,
-
255, 255, 255, 255, 255, 255, 255, 255,
-
255, 255, 255, 1161, 255, 1163, 255, 255,
-
255, 255, 255, 255, 255, 255, 255, 255,
-
255, 255, 255, 255, 255, 255, 255, 255,
-
255, 255, 255, 255, 255, 255, 255, 255,
-
255, 255, 255, 255, 255, 255, 255, 255,
-
255, 255, 255, 255, 255, 255, 255, 255,
-
255, 255, 255, 255, 255, 255, 255, 255,
-
255, 255, 255, 255, 255, 255, 255, 258,
-
255, 255, 255, 255, 255, 255, 255, 255,
-
255, 255, 255, 255, 255, 255, 255, 255,
-
255, 255, 255, 255, 255, 255, 255, 255,
-
255, 255, 255, 255, 255, 255, 255, 255,
-
255, 255, 255, 1, 1164, 1, 1161, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1161, 1, 1165,
-
1, 1, 1, 1166, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1165, 143,
-
144, 143, 143, 143, 143, 143, 1167, 1,
-
143, 143, 1168, 143, 147, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
1, 683, 139, 143, 1, 143, 1169, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 1, 1, 1, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 143, 143, 143,
-
143, 143, 143, 143, 143, 1, 1170, 1,
-
1171, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1171,
-
1, 1172, 1, 1, 1, 1173, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1172, 168, 169, 168, 168, 168, 168, 168,
-
1174, 1, 168, 168, 1175, 168, 279, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 1, 691, 172, 168, 1, 168,
-
1176, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 1, 1, 1, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 168,
-
168, 168, 168, 168, 168, 168, 168, 1,
-
1177, 1, 1, 1, 1178, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1177,
-
1179, 1180, 1179, 1179, 1179, 1179, 1179, 1181,
-
1, 1179, 1179, 1182, 1179, 1183, 1179, 1179,
-
1179, 1179, 1179, 1179, 1179, 1179, 1179, 1179,
-
1179, 1, 1184, 183, 1179, 1, 1179, 1,
-
1179, 1179, 1179, 1179, 1179, 1179, 1179, 1179,
-
1179, 1179, 1179, 1179, 1179, 1179, 1179, 1179,
-
1179, 1179, 1179, 1179, 1179, 1179, 1179, 1179,
-
1179, 1179, 1, 1, 1, 1179, 1179, 1179,
-
1179, 1179, 1179, 1179, 1179, 1179, 1179, 1179,
-
1179, 1179, 1179, 1179, 1179, 1179, 1179, 1179,
-
1179, 1179, 1179, 1179, 1179, 1179, 1179, 1179,
-
1179, 1179, 1179, 1179, 1179, 1179, 1, 1185,
-
1, 1, 1, 1186, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1185, 1187,
-
1188, 1187, 1187, 1187, 1187, 1187, 1189, 1,
-
1187, 1187, 245, 1187, 1190, 1187, 1187, 1187,
-
1187, 1187, 1187, 1187, 1187, 1187, 1187, 1187,
-
1, 120, 1191, 1187, 1, 1187, 1192, 1187,
-
1187, 1187, 1187, 1187, 1187, 1187, 1187, 1187,
-
1187, 1187, 1187, 1187, 1187, 1187, 1187, 1187,
-
1187, 1187, 1187, 1187, 1187, 1187, 1187, 1187,
-
1187, 1, 1, 1, 1187, 1187, 1187, 1187,
-
1187, 1187, 1187, 1187, 1187, 1187, 1187, 1187,
-
1187, 1187, 1187, 1187, 1187, 1187, 1187, 1187,
-
1187, 1187, 1187, 1187, 1187, 1187, 1187, 1187,
-
1187, 1187, 1187, 1187, 1187, 1, 1193, 1,
-
1, 1, 1194, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1193, 1195, 1196,
-
1195, 1195, 1195, 1195, 1195, 1197, 1, 1195,
-
1195, 245, 1195, 1198, 1195, 1195, 1195, 1195,
-
1195, 1195, 1195, 1195, 1195, 1195, 1195, 1,
-
120, 1199, 1195, 1, 1195, 1, 1195, 1195,
-
1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195,
-
1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195,
-
1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195,
-
1, 1, 1, 1195, 1195, 1195, 1195, 1195,
-
1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195,
-
1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195,
-
1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195,
-
1195, 1195, 1195, 1195, 1, 1200, 1, 1193,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1193, 1,
-
1201, 1, 1, 1, 1202, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1201,
-
1203, 1204, 1203, 1203, 1203, 1203, 1203, 1205,
-
1, 1203, 1203, 1206, 1203, 1207, 1203, 1203,
-
1203, 1203, 1203, 1203, 1203, 1203, 1203, 1203,
-
1203, 1, 1208, 1209, 1203, 1, 1203, 1210,
-
1203, 1203, 1203, 1203, 1203, 1203, 1203, 1203,
-
1203, 1203, 1203, 1203, 1203, 1203, 1203, 1203,
-
1203, 1203, 1203, 1203, 1203, 1203, 1203, 1203,
-
1203, 1203, 1, 1, 1, 1203, 1203, 1203,
-
1203, 1203, 1203, 1203, 1203, 1203, 1203, 1203,
-
1203, 1203, 1203, 1203, 1203, 1203, 1203, 1203,
-
1203, 1203, 1203, 1203, 1203, 1203, 1203, 1203,
-
1203, 1203, 1203, 1203, 1203, 1203, 1, 1211,
-
1, 1, 1, 1212, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1211, 1213,
-
1214, 1213, 1213, 1213, 1213, 1213, 1215, 1,
-
1213, 1213, 1216, 1213, 1217, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1, 1218, 1209, 1213, 1, 1213, 1219, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1, 1, 1, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1, 1220, 1,
-
1221, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1221,
-
1, 1222, 1, 1, 1, 1223, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1222, 1213, 1214, 1213, 1213, 1213, 1213, 1213,
-
1224, 1, 1213, 1213, 1, 1213, 1225, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1, 1, 1209, 1213, 1, 1213,
-
1225, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1, 1, 1, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1,
-
1226, 1, 1213, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1213, 1, 1227, 1227, 1227, 1227, 1227,
-
1227, 1227, 1227, 1227, 1, 1227, 1227, 1228,
-
1227, 1227, 1227, 1227, 1227, 1227, 1227, 1227,
-
1227, 1227, 1227, 1227, 1227, 1227, 1227, 1227,
-
1227, 1227, 1227, 1227, 1229, 1227, 1227, 1227,
-
1227, 1227, 1227, 1227, 1227, 1227, 1227, 1227,
-
1227, 1227, 1227, 1227, 1227, 1227, 1227, 1227,
-
1227, 1227, 1227, 1227, 1227, 1227, 1227, 1227,
-
1227, 1227, 1227, 1227, 1227, 1227, 1227, 1227,
-
1227, 1227, 1227, 1227, 1227, 1227, 1227, 1227,
-
1227, 1227, 1227, 1227, 1227, 1227, 1227, 1227,
-
1227, 1227, 1227, 1227, 1227, 1227, 1230, 1227,
-
1227, 1227, 1227, 1227, 1227, 1227, 1227, 1227,
-
1227, 1227, 1227, 1227, 1227, 1227, 1227, 1227,
-
1227, 1227, 1227, 1227, 1227, 1227, 1227, 1227,
-
1227, 1227, 1227, 1227, 1227, 1227, 1227, 1227,
-
1227, 1227, 1, 1231, 1231, 1231, 1231, 1231,
-
1231, 1231, 1231, 1231, 1, 1231, 1231, 1232,
-
1231, 1231, 1231, 1231, 1231, 1231, 1231, 1231,
-
1231, 1231, 1231, 1231, 1231, 1231, 1231, 1231,
-
1231, 1231, 1231, 1231, 1233, 1231, 1231, 1231,
-
1231, 1231, 1231, 1231, 1231, 1231, 1231, 1231,
-
1231, 1231, 1231, 1231, 1231, 1231, 1231, 1231,
-
1231, 1231, 1231, 1231, 1231, 1231, 1231, 1231,
-
1231, 1231, 1231, 1231, 1231, 1231, 1231, 1231,
-
1231, 1231, 1231, 1231, 1231, 1231, 1231, 1231,
-
1231, 1231, 1231, 1231, 1231, 1231, 1231, 1231,
-
1231, 1231, 1231, 1231, 1231, 1231, 1234, 1231,
-
1231, 1231, 1231, 1231, 1231, 1231, 1231, 1231,
-
1231, 1231, 1231, 1231, 1231, 1231, 1231, 1231,
-
1231, 1231, 1231, 1231, 1231, 1231, 1231, 1231,
-
1231, 1231, 1231, 1231, 1231, 1231, 1231, 1231,
-
1231, 1231, 1, 1235, 1, 1231, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1231, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1231, 1236,
-
1, 1, 1, 1237, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1236, 1238,
-
1239, 1238, 1238, 1238, 1238, 1238, 1240, 1,
-
1238, 1238, 1, 1238, 1241, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1, 1, 1242, 1238, 1, 1238, 1241, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1, 1, 1, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1, 1243, 1,
-
1, 1, 1244, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1243, 1213, 1214,
-
1213, 1213, 1213, 1213, 1213, 1245, 1, 1213,
-
1213, 1, 1213, 1225, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1,
-
1, 1209, 1213, 1, 1213, 1225, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1, 1, 1, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1, 1246, 1, 1,
-
1, 1247, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1246, 1213, 1214, 1213,
-
1213, 1213, 1213, 1213, 1248, 1, 1213, 1213,
-
1, 1213, 1, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1, 1,
-
1199, 1213, 1, 1213, 1, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1,
-
1, 1, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1, 1249, 1, 1246, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1246, 1, 1250,
-
1, 1, 1, 1251, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1250, 1238,
-
1239, 1238, 1238, 1238, 1238, 1238, 1252, 1,
-
1238, 1238, 1, 1238, 1, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1, 1, 1253, 1238, 1, 1238, 1, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1, 1, 1, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1, 1254, 1,
-
1, 1, 1255, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1254, 1256, 1257,
-
1256, 1256, 1256, 1256, 1256, 1258, 1, 1256,
-
1256, 1259, 1256, 1260, 1256, 1256, 1256, 1256,
-
1256, 1256, 1256, 1256, 1256, 1256, 1256, 1,
-
1, 1, 1256, 1, 1256, 1261, 1256, 1256,
-
1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256,
-
1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256,
-
1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256,
-
1, 1, 1, 1256, 1256, 1256, 1256, 1256,
-
1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256,
-
1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256,
-
1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256,
-
1256, 1256, 1256, 1256, 1, 1262, 1, 1,
-
1, 1263, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1262, 1256, 1257, 1256,
-
1256, 1256, 1256, 1256, 1264, 1, 1256, 1256,
-
1265, 1256, 1260, 1256, 1256, 1256, 1256, 1256,
-
1256, 1256, 1256, 1256, 1256, 1256, 1, 1,
-
1, 1256, 1, 1256, 1266, 1256, 1256, 1256,
-
1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256,
-
1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256,
-
1256, 1256, 1256, 1256, 1256, 1256, 1256, 1,
-
1, 1, 1256, 1256, 1256, 1256, 1256, 1256,
-
1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256,
-
1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256,
-
1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256,
-
1256, 1256, 1256, 1, 1267, 1, 1262, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1262, 1, 1268,
-
1, 1, 1, 1269, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1268, 1270,
-
1271, 1270, 1270, 1270, 1270, 1270, 1272, 1,
-
1270, 1270, 1, 1270, 1273, 1270, 1270, 1270,
-
1270, 1270, 1270, 1270, 1270, 1270, 1270, 1270,
-
1, 1, 1, 1270, 1274, 1270, 1275, 1270,
-
1270, 1270, 1270, 1270, 1270, 1270, 1270, 1270,
-
1270, 1270, 1270, 1270, 1270, 1270, 1270, 1270,
-
1270, 1270, 1270, 1270, 1270, 1270, 1270, 1270,
-
1270, 1, 1, 1, 1270, 1270, 1270, 1270,
-
1270, 1270, 1270, 1270, 1270, 1270, 1270, 1270,
-
1270, 1270, 1270, 1270, 1270, 1270, 1270, 1270,
-
1270, 1270, 1270, 1270, 1270, 1270, 1270, 1270,
-
1270, 1270, 1270, 1270, 1270, 1, 1276, 1,
-
1, 1, 1277, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1276, 1, 1,
-
1, 1, 1, 1, 1, 1278, 1, 1,
-
1, 1, 1, 1279, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1280, 1, 1281, 1, 1282,
-
1, 1276, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1276, 1, 1283, 1, 1, 1, 1284, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1283, 1, 1, 1, 1, 1, 1,
-
1, 1285, 1, 1, 1, 1, 1, 1286,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1287,
-
1, 1288, 1, 1279, 1, 1, 1, 1289,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1279, 1290, 1291, 1290, 1290, 1290,
-
1290, 1290, 1292, 1, 1290, 1290, 1, 1290,
-
1, 1290, 1290, 1290, 1290, 1290, 1290, 1290,
-
1290, 1290, 1290, 1290, 1, 1, 1, 1290,
-
1, 1290, 1, 1290, 1290, 1290, 1290, 1290,
-
1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290,
-
1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290,
-
1290, 1290, 1290, 1290, 1290, 1, 1, 1,
-
1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290,
-
1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290,
-
1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290,
-
1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290,
-
1290, 1, 1293, 1, 1279, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1279, 1, 1294, 1, 1,
-
1, 1295, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1294, 1290, 1, 1290,
-
1290, 1290, 1290, 1290, 1296, 1, 1290, 1290,
-
1, 1290, 1279, 1290, 1290, 1290, 1290, 1290,
-
1290, 1290, 1290, 1290, 1290, 1290, 1, 1,
-
1, 1290, 1280, 1290, 1297, 1290, 1290, 1290,
-
1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290,
-
1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290,
-
1290, 1290, 1290, 1290, 1290, 1290, 1290, 1,
-
1, 1, 1290, 1290, 1290, 1290, 1290, 1290,
-
1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290,
-
1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290,
-
1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290,
-
1290, 1290, 1290, 1, 1294, 1, 1, 1,
-
1295, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1294, 1, 1, 1, 1,
-
1, 1, 1, 1296, 1, 1, 1, 1,
-
1, 1279, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1280, 1, 1297, 1, 1298, 1, 1294,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1294, 1,
-
1299, 1, 1, 1, 1300, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1299,
-
1, 1, 1, 1, 1, 1, 1, 1301,
-
1, 1, 1, 1, 1, 1286, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1287, 1, 1302,
-
1, 1280, 1, 1, 1, 1303, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1280, 1, 1, 1, 1, 1, 1, 1,
-
1304, 1, 1, 1, 1305, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1306, 1, 1307, 1, 1280,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1280, 1,
-
1287, 1, 1, 1, 1308, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1287,
-
1, 1, 1, 1, 1, 1, 1, 1309,
-
1, 1, 1, 1310, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1311, 1, 1312, 1, 120, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 120, 1, 1313,
-
1, 1, 1, 1314, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1313, 1315,
-
1316, 1315, 1315, 1315, 1315, 1315, 1317, 1,
-
1315, 1315, 1, 1315, 1318, 1315, 1315, 1315,
-
1315, 1315, 1315, 1315, 1315, 1315, 1315, 1315,
-
1, 1, 1, 1315, 1, 1315, 1, 1315,
-
1315, 1315, 1315, 1315, 1315, 1315, 1315, 1315,
-
1315, 1315, 1315, 1315, 1315, 1315, 1315, 1315,
-
1315, 1315, 1315, 1315, 1315, 1315, 1315, 1315,
-
1315, 1319, 1, 1, 1315, 1315, 1315, 1315,
-
1315, 1315, 1315, 1315, 1315, 1315, 1315, 1315,
-
1315, 1315, 1315, 1315, 1315, 1315, 1315, 1315,
-
1315, 1315, 1315, 1315, 1315, 1315, 1315, 1315,
-
1315, 1315, 1315, 1315, 1315, 1, 1320, 1,
-
1, 1, 1321, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1320, 1322, 1323,
-
1322, 1322, 1322, 1322, 1322, 1324, 1, 1322,
-
1322, 1, 1322, 1325, 1322, 1322, 1322, 1322,
-
1322, 1322, 1322, 1322, 1322, 1322, 1322, 1,
-
1, 1, 1322, 1, 1322, 1, 1322, 1322,
-
1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322,
-
1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322,
-
1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322,
-
1326, 1, 1, 1322, 1322, 1322, 1322, 1322,
-
1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322,
-
1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322,
-
1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322,
-
1322, 1322, 1322, 1322, 1, 1327, 1, 1320,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1320, 1,
-
1328, 1, 1, 1, 1329, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1328,
-
1322, 1323, 1322, 1322, 1322, 1322, 1322, 1330,
-
1, 1322, 1322, 1, 1322, 1331, 1322, 1322,
-
1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322,
-
1322, 1, 1, 1, 1322, 1332, 1322, 1,
-
1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322,
-
1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322,
-
1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322,
-
1322, 1322, 1, 1, 1, 1322, 1322, 1322,
-
1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322,
-
1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322,
-
1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322,
-
1322, 1322, 1322, 1322, 1322, 1322, 1, 1328,
-
1, 1, 1, 1329, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1328, 1,
-
1, 1, 1, 1, 1, 1, 1330, 1,
-
1, 1, 1, 1, 1333, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1332, 1, 1334, 1,
-
1328, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1328,
-
1, 1335, 1, 1, 1, 1336, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1335, 1, 1, 1, 1, 1, 1, 1,
-
1337, 1, 1, 1, 1, 1, 1338, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1339, 1,
-
1333, 1, 1, 1, 1340, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1333,
-
1341, 1, 1341, 1341, 1341, 1341, 1341, 1342,
-
1, 1341, 1341, 1, 1341, 1, 1341, 1341,
-
1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341,
-
1341, 1, 1, 1, 1341, 1, 1341, 1,
-
1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341,
-
1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341,
-
1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341,
-
1341, 1341, 1, 1, 1, 1341, 1341, 1341,
-
1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341,
-
1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341,
-
1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341,
-
1341, 1341, 1341, 1341, 1341, 1341, 1, 1343,
-
1, 1333, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1333, 1, 1328, 1, 1, 1, 1329, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1328, 1341, 1, 1341, 1341, 1341, 1341,
-
1341, 1330, 1, 1341, 1341, 1, 1341, 1333,
-
1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341,
-
1341, 1341, 1341, 1, 1, 1, 1341, 1332,
-
1341, 1, 1341, 1341, 1341, 1341, 1341, 1341,
-
1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341,
-
1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341,
-
1341, 1341, 1341, 1341, 1, 1, 1, 1341,
-
1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341,
-
1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341,
-
1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341,
-
1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341,
-
1, 1338, 1, 1, 1, 1344, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1338, 1345, 1, 1345, 1345, 1345, 1345, 1345,
-
1346, 1, 1345, 1345, 1, 1345, 1, 1345,
-
1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345,
-
1345, 1345, 1, 1, 1, 1345, 1, 1345,
-
1, 1345, 1345, 1345, 1345, 1345, 1345, 1345,
-
1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345,
-
1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345,
-
1345, 1345, 1345, 1, 1, 1, 1345, 1345,
-
1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345,
-
1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345,
-
1345, 1345, 1345, 1345, 1345, 1345, 1345, 1345,
-
1345, 1345, 1345, 1345, 1345, 1345, 1345, 1,
-
1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347,
-
1323, 1, 1347, 1347, 1348, 1347, 1347, 1347,
-
1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347,
-
1347, 1347, 1347, 1347, 1347, 1347, 1347, 1323,
-
1347, 1, 1347, 1347, 1347, 1347, 1347, 1347,
-
1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347,
-
1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347,
-
1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347,
-
1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347,
-
1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347,
-
1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347,
-
1347, 1347, 1347, 1349, 1347, 1347, 1347, 1347,
-
1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347,
-
1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347,
-
1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347,
-
1347, 1347, 1347, 1347, 1347, 1347, 1347, 1,
-
1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347,
-
1347, 1, 1347, 1347, 1350, 1347, 1347, 1347,
-
1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347,
-
1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347,
-
1347, 1351, 1347, 1347, 1347, 1347, 1347, 1347,
-
1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347,
-
1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347,
-
1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347,
-
1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347,
-
1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347,
-
1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347,
-
1347, 1347, 1347, 1349, 1347, 1347, 1347, 1347,
-
1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347,
-
1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347,
-
1347, 1347, 1347, 1347, 1347, 1347, 1347, 1347,
-
1347, 1347, 1347, 1347, 1347, 1347, 1347, 1,
-
1352, 1, 1347, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1347, 1, 1353, 1, 1, 1, 1354,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1353, 1351, 1323, 1351, 1351, 1351,
-
1351, 1351, 1355, 1, 1351, 1351, 1, 1351,
-
1325, 1351, 1351, 1351, 1351, 1351, 1351, 1351,
-
1351, 1351, 1351, 1351, 1, 1, 1, 1351,
-
1332, 1351, 1, 1351, 1351, 1351, 1351, 1351,
-
1351, 1351, 1351, 1351, 1351, 1351, 1351, 1351,
-
1351, 1351, 1351, 1351, 1351, 1351, 1351, 1351,
-
1351, 1351, 1351, 1351, 1351, 1, 1, 1,
-
1351, 1351, 1351, 1351, 1351, 1351, 1351, 1351,
-
1351, 1351, 1351, 1351, 1351, 1351, 1351, 1351,
-
1351, 1351, 1351, 1351, 1351, 1351, 1351, 1351,
-
1351, 1351, 1351, 1351, 1351, 1351, 1351, 1351,
-
1351, 1, 1353, 1, 1, 1, 1354, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1353, 1, 1, 1, 1, 1, 1,
-
1, 1355, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1332,
-
1, 1356, 1, 1353, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1353, 1, 1357, 1, 1, 1,
-
1358, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1357, 1, 1, 1, 1,
-
1, 1, 1, 1359, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1339, 1, 1351, 1323, 1351, 1351, 1351,
-
1351, 1351, 1, 1, 1351, 1351, 1, 1351,
-
1325, 1351, 1351, 1351, 1351, 1351, 1351, 1351,
-
1351, 1351, 1351, 1351, 1, 1, 1, 1351,
-
1, 1351, 1, 1351, 1351, 1351, 1351, 1351,
-
1351, 1351, 1351, 1351, 1351, 1351, 1351, 1351,
-
1351, 1351, 1351, 1351, 1351, 1351, 1351, 1351,
-
1351, 1351, 1351, 1351, 1351, 1, 1, 1,
-
1351, 1351, 1351, 1351, 1351, 1351, 1351, 1351,
-
1351, 1351, 1351, 1351, 1351, 1351, 1351, 1351,
-
1351, 1351, 1351, 1351, 1351, 1351, 1351, 1351,
-
1351, 1351, 1351, 1351, 1351, 1351, 1351, 1351,
-
1351, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1347, 1360, 1, 1323, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1323, 1, 1333, 1,
-
1, 1, 1340, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1333, 1322, 1323,
-
1322, 1322, 1322, 1322, 1322, 1342, 1, 1322,
-
1322, 1, 1322, 1325, 1322, 1322, 1322, 1322,
-
1322, 1322, 1322, 1322, 1322, 1322, 1322, 1,
-
1, 1, 1322, 1, 1322, 1, 1322, 1322,
-
1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322,
-
1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322,
-
1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322,
-
1, 1, 1, 1322, 1322, 1322, 1322, 1322,
-
1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322,
-
1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322,
-
1322, 1322, 1322, 1322, 1322, 1322, 1322, 1322,
-
1322, 1322, 1322, 1322, 1, 1361, 1, 1,
-
1, 1362, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1361, 1363, 1364, 1363,
-
1363, 1363, 1363, 1363, 1365, 1, 1363, 1363,
-
1, 1363, 1366, 1363, 1363, 1363, 1363, 1363,
-
1363, 1363, 1363, 1363, 1363, 1363, 1, 1,
-
1, 1363, 1, 1363, 1, 1363, 1363, 1363,
-
1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363,
-
1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363,
-
1363, 1363, 1363, 1363, 1363, 1363, 1363, 1367,
-
1, 1, 1363, 1363, 1363, 1363, 1363, 1363,
-
1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363,
-
1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363,
-
1363, 1363, 1363, 1363, 1363, 1363, 1363, 1363,
-
1363, 1363, 1363, 1, 1326, 1326, 1326, 1326,
-
1326, 1326, 1326, 1326, 1326, 1, 1326, 1326,
-
1368, 1326, 1326, 1326, 1326, 1326, 1326, 1326,
-
1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326,
-
1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326,
-
1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326,
-
1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326,
-
1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326,
-
1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326,
-
1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326,
-
1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326,
-
1326, 1326, 1326, 1326, 1326, 1326, 1, 1369,
-
1353, 1326, 1326, 1326, 1326, 1326, 1326, 1326,
-
1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326,
-
1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326,
-
1326, 1326, 1326, 1326, 1326, 1326, 1326, 1326,
-
1326, 1326, 1326, 1, 1370, 1, 1326, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1326, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1326,
-
1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371,
-
1371, 1, 1371, 1371, 1372, 1371, 1371, 1371,
-
1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371,
-
1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371,
-
1371, 1373, 1371, 1371, 1371, 1371, 1371, 1371,
-
1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371,
-
1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371,
-
1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371,
-
1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371,
-
1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371,
-
1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371,
-
1371, 1371, 1371, 1374, 1371, 1371, 1371, 1371,
-
1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371,
-
1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371,
-
1371, 1371, 1371, 1371, 1371, 1371, 1371, 1371,
-
1371, 1371, 1371, 1371, 1371, 1371, 1371, 1,
-
1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375,
-
1375, 1, 1375, 1375, 1376, 1375, 1375, 1375,
-
1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375,
-
1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375,
-
1375, 1377, 1375, 1375, 1375, 1375, 1375, 1375,
-
1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375,
-
1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375,
-
1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375,
-
1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375,
-
1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375,
-
1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375,
-
1375, 1375, 1375, 1378, 1375, 1375, 1375, 1375,
-
1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375,
-
1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375,
-
1375, 1375, 1375, 1375, 1375, 1375, 1375, 1375,
-
1375, 1375, 1375, 1375, 1375, 1375, 1375, 1,
-
1379, 1, 1375, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1375, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1375, 1286, 1, 1, 1,
-
1380, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1286, 1381, 1382, 1381, 1381,
-
1381, 1381, 1381, 1383, 1, 1381, 1381, 1,
-
1381, 1, 1381, 1381, 1381, 1381, 1381, 1381,
-
1381, 1381, 1381, 1381, 1381, 1, 1, 1,
-
1381, 1, 1381, 1, 1381, 1381, 1381, 1381,
-
1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381,
-
1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381,
-
1381, 1381, 1381, 1381, 1381, 1381, 1, 1,
-
1, 1381, 1381, 1381, 1381, 1381, 1381, 1381,
-
1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381,
-
1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381,
-
1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381,
-
1381, 1381, 1, 1384, 1384, 1384, 1384, 1384,
-
1384, 1384, 1384, 1271, 1, 1384, 1384, 1385,
-
1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384,
-
1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384,
-
1384, 1384, 1271, 1384, 1, 1384, 1384, 1384,
-
1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384,
-
1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384,
-
1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384,
-
1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384,
-
1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384,
-
1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384,
-
1384, 1384, 1384, 1384, 1384, 1384, 1386, 1384,
-
1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384,
-
1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384,
-
1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384,
-
1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384,
-
1384, 1384, 1, 1384, 1384, 1384, 1384, 1384,
-
1384, 1384, 1384, 1384, 1, 1384, 1384, 1387,
-
1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384,
-
1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384,
-
1384, 1384, 1384, 1384, 1388, 1384, 1384, 1384,
-
1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384,
-
1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384,
-
1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384,
-
1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384,
-
1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384,
-
1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384,
-
1384, 1384, 1384, 1384, 1384, 1384, 1386, 1384,
-
1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384,
-
1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384,
-
1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384,
-
1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384,
-
1384, 1384, 1, 1389, 1, 1384, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1384, 1, 1390, 1,
-
1, 1, 1391, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1390, 1388, 1271,
-
1388, 1388, 1388, 1388, 1388, 1392, 1, 1388,
-
1388, 1, 1388, 1388, 1388, 1388, 1388, 1388,
-
1388, 1388, 1388, 1388, 1388, 1388, 1388, 1,
-
1, 1, 1388, 1274, 1388, 1275, 1388, 1388,
-
1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388,
-
1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388,
-
1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388,
-
1, 1, 1, 1388, 1388, 1388, 1388, 1388,
-
1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388,
-
1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388,
-
1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388,
-
1388, 1388, 1388, 1388, 1, 1393, 1, 1,
-
1, 1394, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1393, 1, 1, 1,
-
1, 1, 1, 1, 1395, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1280, 1, 1281, 1, 1396, 1,
-
1393, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1393,
-
1, 1397, 1, 1, 1, 1398, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1397, 1, 1, 1, 1, 1, 1, 1,
-
1399, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1287, 1,
-
1288, 1, 1384, 1384, 1384, 1384, 1384, 1384,
-
1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384,
-
1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384,
-
1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384,
-
1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384,
-
1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384,
-
1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384,
-
1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384,
-
1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384,
-
1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384,
-
1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384,
-
1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384,
-
1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384,
-
1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384,
-
1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384,
-
1384, 1384, 1384, 1384, 1384, 1384, 1384, 1384,
-
1384, 1384, 1, 1400, 1, 1271, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1271, 1, 1401, 1,
-
1, 1, 1402, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1401, 1270, 1403,
-
1270, 1270, 1270, 1270, 1270, 1404, 1, 1270,
-
1270, 1, 1270, 1388, 1270, 1270, 1270, 1270,
-
1270, 1270, 1270, 1270, 1270, 1270, 1270, 1,
-
1, 1, 1270, 1274, 1270, 1275, 1270, 1270,
-
1270, 1270, 1270, 1270, 1270, 1270, 1270, 1270,
-
1270, 1270, 1270, 1270, 1270, 1270, 1270, 1270,
-
1270, 1270, 1270, 1270, 1270, 1270, 1270, 1270,
-
1, 1, 1, 1270, 1270, 1270, 1270, 1270,
-
1270, 1270, 1270, 1270, 1270, 1270, 1270, 1270,
-
1270, 1270, 1270, 1270, 1270, 1270, 1270, 1270,
-
1270, 1270, 1270, 1270, 1270, 1270, 1270, 1270,
-
1270, 1270, 1270, 1270, 1, 1405, 1, 1,
-
1, 1406, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1405, 1290, 1291, 1290,
-
1290, 1290, 1290, 1290, 1407, 1, 1290, 1290,
-
1, 1290, 1, 1290, 1290, 1290, 1290, 1290,
-
1290, 1290, 1290, 1290, 1290, 1290, 1, 1,
-
1, 1290, 1280, 1290, 1281, 1290, 1290, 1290,
-
1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290,
-
1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290,
-
1290, 1290, 1290, 1290, 1290, 1290, 1290, 1,
-
1, 1, 1290, 1290, 1290, 1290, 1290, 1290,
-
1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290,
-
1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290,
-
1290, 1290, 1290, 1290, 1290, 1290, 1290, 1290,
-
1290, 1290, 1290, 1, 1408, 1, 1405, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1405, 1, 1409,
-
1, 1, 1, 1410, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1409, 1381,
-
1382, 1381, 1381, 1381, 1381, 1381, 1411, 1,
-
1381, 1381, 1, 1381, 1, 1381, 1381, 1381,
-
1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381,
-
1, 1, 1, 1381, 1287, 1381, 1288, 1381,
-
1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381,
-
1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381,
-
1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381,
-
1381, 1, 1, 1, 1381, 1381, 1381, 1381,
-
1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381,
-
1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381,
-
1381, 1381, 1381, 1381, 1381, 1381, 1381, 1381,
-
1381, 1381, 1381, 1381, 1381, 1, 1412, 1412,
-
1412, 1412, 1412, 1412, 1412, 1412, 1413, 1,
-
1412, 1412, 1414, 1412, 1412, 1412, 1412, 1412,
-
1412, 1412, 1412, 1412, 1412, 1412, 1412, 1412,
-
1412, 1412, 1412, 1412, 1412, 1413, 1412, 1373,
-
1412, 1412, 1412, 1412, 1412, 1412, 1412, 1412,
-
1412, 1412, 1412, 1412, 1412, 1412, 1412, 1412,
-
1412, 1412, 1412, 1412, 1412, 1412, 1412, 1412,
-
1412, 1412, 1412, 1412, 1412, 1412, 1412, 1412,
-
1412, 1412, 1412, 1412, 1412, 1412, 1412, 1412,
-
1412, 1412, 1412, 1412, 1412, 1412, 1412, 1412,
-
1412, 1412, 1412, 1412, 1412, 1412, 1412, 1412,
-
1412, 1415, 1412, 1412, 1412, 1412, 1412, 1412,
-
1412, 1412, 1412, 1412, 1412, 1412, 1412, 1412,
-
1412, 1412, 1412, 1412, 1412, 1412, 1412, 1412,
-
1412, 1412, 1412, 1412, 1412, 1412, 1412, 1412,
-
1412, 1412, 1412, 1412, 1412, 1, 1416, 1416,
-
1416, 1416, 1416, 1416, 1416, 1416, 1416, 1,
-
1416, 1416, 1417, 1416, 1416, 1416, 1416, 1416,
-
1416, 1416, 1416, 1416, 1416, 1416, 1416, 1416,
-
1416, 1416, 1416, 1416, 1416, 1416, 1416, 1418,
-
1416, 1416, 1416, 1416, 1416, 1416, 1416, 1416,
-
1416, 1416, 1416, 1416, 1416, 1416, 1416, 1416,
-
1416, 1416, 1416, 1416, 1416, 1416, 1416, 1416,
-
1416, 1416, 1416, 1416, 1416, 1416, 1416, 1416,
-
1416, 1416, 1416, 1416, 1416, 1416, 1416, 1416,
-
1416, 1416, 1416, 1416, 1416, 1416, 1416, 1416,
-
1416, 1416, 1416, 1416, 1416, 1416, 1416, 1416,
-
1416, 1419, 1416, 1416, 1416, 1416, 1416, 1416,
-
1416, 1416, 1416, 1416, 1416, 1416, 1416, 1416,
-
1416, 1416, 1416, 1416, 1416, 1416, 1416, 1416,
-
1416, 1416, 1416, 1416, 1416, 1416, 1416, 1416,
-
1416, 1416, 1416, 1416, 1416, 1, 1420, 1,
-
1416, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1416,
-
1, 1268, 1, 1, 1, 1269, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1268, 1388, 1271, 1388, 1388, 1388, 1388, 1388,
-
1272, 1, 1388, 1388, 1, 1388, 1273, 1388,
-
1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388,
-
1388, 1388, 1, 1, 1, 1388, 1274, 1388,
-
1275, 1388, 1388, 1388, 1388, 1388, 1388, 1388,
-
1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388,
-
1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388,
-
1388, 1388, 1388, 1, 1, 1, 1388, 1388,
-
1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388,
-
1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388,
-
1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388,
-
1388, 1388, 1388, 1388, 1388, 1388, 1388, 1,
-
1416, 1416, 1416, 1416, 1416, 1416, 1416, 1416,
-
1416, 1416, 1416, 1416, 1416, 1416, 1416, 1416,
-
1416, 1416, 1416, 1416, 1416, 1416, 1416, 1416,
-
1416, 1416, 1416, 1416, 1416, 1416, 1416, 1416,
-
1416, 1416, 1416, 1416, 1416, 1416, 1416, 1416,
-
1416, 1416, 1416, 1416, 1416, 1416, 1416, 1416,
-
1416, 1416, 1416, 1416, 1416, 1416, 1416, 1416,
-
1416, 1416, 1416, 1416, 1416, 1416, 1416, 1416,
-
1416, 1416, 1416, 1416, 1416, 1416, 1416, 1416,
-
1416, 1416, 1416, 1416, 1416, 1416, 1416, 1416,
-
1416, 1416, 1416, 1416, 1416, 1416, 1416, 1416,
-
1416, 1416, 1416, 1416, 1416, 1416, 1416, 1416,
-
1416, 1416, 1416, 1416, 1416, 1416, 1416, 1416,
-
1416, 1416, 1416, 1416, 1416, 1416, 1416, 1416,
-
1416, 1416, 1416, 1416, 1416, 1416, 1416, 1416,
-
1416, 1416, 1416, 1416, 1416, 1416, 1416, 1416,
-
1, 1416, 1416, 1416, 1416, 1416, 1416, 1416,
-
1416, 1421, 1, 1416, 1416, 1422, 1416, 1416,
-
1416, 1416, 1416, 1416, 1416, 1416, 1416, 1416,
-
1416, 1416, 1416, 1416, 1416, 1416, 1416, 1416,
-
1421, 1416, 1377, 1416, 1416, 1416, 1416, 1416,
-
1416, 1416, 1416, 1416, 1416, 1416, 1416, 1416,
-
1416, 1416, 1416, 1416, 1416, 1416, 1416, 1416,
-
1416, 1416, 1416, 1416, 1416, 1416, 1416, 1416,
-
1416, 1416, 1416, 1416, 1416, 1416, 1416, 1416,
-
1416, 1416, 1416, 1416, 1416, 1416, 1416, 1416,
-
1416, 1416, 1416, 1416, 1416, 1416, 1416, 1416,
-
1416, 1416, 1416, 1416, 1419, 1416, 1416, 1416,
-
1416, 1416, 1416, 1416, 1416, 1416, 1416, 1416,
-
1416, 1416, 1416, 1416, 1416, 1416, 1416, 1416,
-
1416, 1416, 1416, 1416, 1416, 1416, 1416, 1416,
-
1416, 1416, 1416, 1416, 1416, 1416, 1416, 1416,
-
1, 1423, 1, 1421, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1421, 1, 1424, 1424, 1424, 1424,
-
1424, 1424, 1424, 1424, 1425, 1, 1424, 1424,
-
1426, 1424, 1424, 1424, 1424, 1424, 1424, 1424,
-
1424, 1424, 1424, 1424, 1424, 1424, 1424, 1424,
-
1424, 1424, 1424, 1425, 1424, 1427, 1424, 1424,
-
1424, 1424, 1424, 1424, 1424, 1424, 1424, 1424,
-
1424, 1424, 1424, 1424, 1424, 1424, 1424, 1424,
-
1424, 1424, 1424, 1424, 1424, 1424, 1424, 1424,
-
1424, 1424, 1424, 1424, 1424, 1424, 1424, 1424,
-
1424, 1424, 1424, 1424, 1424, 1424, 1424, 1424,
-
1424, 1424, 1424, 1424, 1424, 1424, 1424, 1424,
-
1424, 1424, 1424, 1424, 1424, 1424, 1424, 1428,
-
1424, 1424, 1424, 1424, 1424, 1424, 1424, 1424,
-
1424, 1424, 1424, 1424, 1424, 1424, 1424, 1424,
-
1424, 1424, 1424, 1424, 1424, 1424, 1424, 1424,
-
1424, 1424, 1424, 1424, 1424, 1424, 1424, 1424,
-
1424, 1424, 1424, 1, 1429, 1429, 1429, 1429,
-
1429, 1429, 1429, 1429, 1429, 1, 1429, 1429,
-
1430, 1429, 1429, 1429, 1429, 1429, 1429, 1429,
-
1429, 1429, 1429, 1429, 1429, 1429, 1429, 1429,
-
1429, 1429, 1429, 1429, 1429, 1431, 1429, 1429,
-
1429, 1429, 1429, 1429, 1429, 1429, 1429, 1429,
-
1429, 1429, 1429, 1429, 1429, 1429, 1429, 1429,
-
1429, 1429, 1429, 1429, 1429, 1429, 1429, 1429,
-
1429, 1429, 1429, 1429, 1429, 1429, 1429, 1429,
-
1429, 1429, 1429, 1429, 1429, 1429, 1429, 1429,
-
1429, 1429, 1429, 1429, 1429, 1429, 1429, 1429,
-
1429, 1429, 1429, 1429, 1429, 1429, 1429, 1432,
-
1429, 1429, 1429, 1429, 1429, 1429, 1429, 1429,
-
1429, 1429, 1429, 1429, 1429, 1429, 1429, 1429,
-
1429, 1429, 1429, 1429, 1429, 1429, 1429, 1429,
-
1429, 1429, 1429, 1429, 1429, 1429, 1429, 1429,
-
1429, 1429, 1429, 1, 1433, 1, 1429, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1429, 1, 1434,
-
1, 1, 1, 1435, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1434, 1388,
-
1271, 1388, 1388, 1388, 1388, 1388, 1436, 1,
-
1388, 1388, 1, 1388, 1273, 1388, 1388, 1388,
-
1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388,
-
1, 1, 1, 1388, 1274, 1388, 1437, 1388,
-
1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388,
-
1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388,
-
1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388,
-
1388, 1, 1, 1, 1388, 1388, 1388, 1388,
-
1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388,
-
1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388,
-
1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388,
-
1388, 1388, 1388, 1388, 1388, 1, 1438, 1,
-
1, 1, 1439, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1438, 1, 1,
-
1, 1, 1, 1, 1, 1440, 1, 1,
-
1, 1, 1, 1279, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1280, 1, 1441, 1, 1442,
-
1, 1438, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1438, 1, 1443, 1, 1, 1, 1444, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1443, 1, 1, 1, 1, 1, 1,
-
1, 1445, 1, 1, 1, 1, 1, 1286,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1287,
-
1, 1446, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1429, 1429, 1429, 1429, 1429,
-
1429, 1429, 1429, 1429, 1447, 1, 1429, 1429,
-
1448, 1429, 1429, 1429, 1429, 1429, 1429, 1429,
-
1429, 1429, 1429, 1429, 1429, 1429, 1429, 1429,
-
1429, 1429, 1429, 1447, 1429, 1449, 1429, 1429,
-
1429, 1429, 1429, 1429, 1429, 1429, 1429, 1429,
-
1429, 1429, 1429, 1429, 1429, 1429, 1429, 1429,
-
1429, 1429, 1429, 1429, 1429, 1429, 1429, 1429,
-
1429, 1429, 1429, 1429, 1429, 1429, 1429, 1429,
-
1429, 1429, 1429, 1429, 1429, 1429, 1429, 1429,
-
1429, 1429, 1429, 1429, 1429, 1429, 1429, 1429,
-
1429, 1429, 1429, 1429, 1429, 1429, 1429, 1432,
-
1429, 1429, 1429, 1429, 1429, 1429, 1429, 1429,
-
1429, 1429, 1429, 1429, 1429, 1429, 1429, 1429,
-
1429, 1429, 1429, 1429, 1429, 1429, 1429, 1429,
-
1429, 1429, 1429, 1429, 1429, 1429, 1429, 1429,
-
1429, 1429, 1429, 1, 1450, 1, 1447, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1447, 1, 1451,
-
1, 1, 1, 1452, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1451, 1,
-
1, 1, 1, 1, 1, 1, 1453, 1,
-
1, 1, 1, 1, 1279, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1280, 1, 1454, 1,
-
1455, 1, 1451, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1451, 1, 1456, 1, 1, 1, 1457,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1456, 1, 1, 1, 1, 1,
-
1, 1, 1458, 1, 1, 1, 1, 1,
-
1286, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1287, 1, 1459, 1, 1460, 1, 1, 1,
-
1461, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1460, 1462, 1463, 1462, 1462,
-
1462, 1462, 1462, 1464, 1, 1462, 1462, 1465,
-
1462, 1466, 1462, 1462, 1462, 1462, 1462, 1462,
-
1462, 1462, 1462, 1462, 1462, 1, 1, 1,
-
1462, 1, 1462, 1467, 1462, 1462, 1462, 1462,
-
1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462,
-
1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462,
-
1462, 1462, 1462, 1462, 1462, 1462, 1, 1,
-
1, 1462, 1462, 1462, 1462, 1462, 1462, 1462,
-
1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462,
-
1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462,
-
1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462,
-
1462, 1462, 1, 1265, 1, 1, 1, 1468,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1265, 1, 1, 1, 1, 1,
-
1, 1, 1469, 1, 1, 1, 1265, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1266, 1, 1470, 1, 1265, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1265, 1, 1465,
-
1, 1, 1, 1471, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1465, 1,
-
1, 1, 1, 1, 1, 1, 1472, 1,
-
1, 1, 1465, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1467, 1,
-
1473, 1, 1, 1, 1474, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1473,
-
1475, 1476, 1475, 1475, 1475, 1475, 1475, 1477,
-
1, 1475, 1475, 1, 1475, 1478, 1475, 1475,
-
1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475,
-
1475, 1, 1, 1, 1475, 1, 1475, 1,
-
1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475,
-
1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475,
-
1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475,
-
1475, 1475, 1479, 1, 1, 1475, 1475, 1475,
-
1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475,
-
1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475,
-
1475, 1475, 1475, 1475, 1475, 1475, 1475, 1475,
-
1475, 1475, 1475, 1475, 1475, 1475, 1, 1480,
-
1, 1, 1, 1481, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1480, 1482,
-
1483, 1482, 1482, 1482, 1482, 1482, 1484, 1,
-
1482, 1482, 1, 1482, 1485, 1482, 1482, 1482,
-
1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482,
-
1, 1, 1, 1482, 1, 1482, 1, 1482,
-
1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482,
-
1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482,
-
1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482,
-
1482, 1486, 1, 1, 1482, 1482, 1482, 1482,
-
1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482,
-
1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482,
-
1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482,
-
1482, 1482, 1482, 1482, 1482, 1, 1487, 1,
-
1480, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1480,
-
1, 1488, 1, 1, 1, 1489, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1488, 1482, 1483, 1482, 1482, 1482, 1482, 1482,
-
1490, 1, 1482, 1482, 1491, 1482, 1492, 1482,
-
1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482,
-
1482, 1482, 1493, 1, 1, 1482, 1, 1482,
-
1, 1482, 1482, 1482, 1482, 1482, 1482, 1482,
-
1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482,
-
1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482,
-
1482, 1482, 1482, 1, 1, 1, 1482, 1482,
-
1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482,
-
1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482,
-
1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482,
-
1482, 1482, 1482, 1482, 1482, 1482, 1482, 1,
-
1488, 1, 1, 1, 1489, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1488,
-
1, 1, 1, 1, 1, 1, 1, 1490,
-
1, 1, 1, 1491, 1, 1494, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1493, 1, 1495, 1, 1488, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1488, 1, 1496, 1,
-
1, 1, 1497, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1496, 1, 1,
-
1, 1, 1, 1, 1, 1498, 1, 1,
-
1, 1499, 1, 1500, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1501,
-
1, 1502, 1, 1, 1, 1503, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1502, 1, 1, 1, 1, 1, 1, 1,
-
1504, 1, 1, 1, 1502, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1505, 1, 1, 1, 1, 1,
-
1266, 1, 1506, 1, 1502, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1502, 1, 1507, 1, 1,
-
1, 1508, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1507, 1, 1, 1,
-
1, 1, 1, 1, 1509, 1, 1, 1,
-
1507, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1510, 1,
-
1, 1, 1, 1, 1467, 1, 1511, 1,
-
1, 1, 1512, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1511, 1513, 1514,
-
1513, 1513, 1513, 1513, 1513, 1515, 1, 1513,
-
1513, 1, 1513, 1516, 1513, 1513, 1513, 1513,
-
1513, 1513, 1513, 1513, 1513, 1513, 1513, 1,
-
1, 1, 1513, 1, 1513, 1, 1513, 1513,
-
1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513,
-
1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513,
-
1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513,
-
1, 1, 1, 1513, 1513, 1513, 1513, 1513,
-
1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513,
-
1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513,
-
1513, 1513, 1513, 1513, 1513, 1513, 1513, 1513,
-
1513, 1513, 1513, 1513, 1, 1517, 1, 1,
-
1, 1518, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1517, 1256, 1257, 1256,
-
1256, 1256, 1256, 1256, 1519, 1, 1256, 1256,
-
1, 1256, 1260, 1256, 1256, 1256, 1256, 1256,
-
1256, 1256, 1256, 1256, 1256, 1256, 1, 1,
-
1, 1256, 1, 1256, 1, 1256, 1256, 1256,
-
1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256,
-
1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256,
-
1256, 1256, 1256, 1256, 1256, 1256, 1256, 1,
-
1, 1, 1256, 1256, 1256, 1256, 1256, 1256,
-
1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256,
-
1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256,
-
1256, 1256, 1256, 1256, 1256, 1256, 1256, 1256,
-
1256, 1256, 1256, 1, 1520, 1, 1517, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1517, 1, 1521,
-
1, 1, 1, 1522, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1521, 1462,
-
1463, 1462, 1462, 1462, 1462, 1462, 1523, 1,
-
1462, 1462, 1, 1462, 1466, 1462, 1462, 1462,
-
1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462,
-
1, 1, 1, 1462, 1, 1462, 1, 1462,
-
1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462,
-
1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462,
-
1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462,
-
1462, 1, 1, 1, 1462, 1462, 1462, 1462,
-
1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462,
-
1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462,
-
1462, 1462, 1462, 1462, 1462, 1462, 1462, 1462,
-
1462, 1462, 1462, 1462, 1462, 1, 1388, 1271,
-
1388, 1388, 1388, 1388, 1388, 1, 1, 1388,
-
1388, 1, 1388, 1524, 1388, 1388, 1388, 1388,
-
1388, 1388, 1388, 1388, 1388, 1388, 1388, 1,
-
1, 1, 1388, 1, 1388, 1, 1388, 1388,
-
1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388,
-
1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388,
-
1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388,
-
1, 1, 1, 1388, 1388, 1388, 1388, 1388,
-
1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388,
-
1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388,
-
1388, 1388, 1388, 1388, 1388, 1388, 1388, 1388,
-
1388, 1388, 1388, 1388, 1, 1494, 1, 1,
-
1, 1525, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1494, 1526, 1, 1526,
-
1526, 1526, 1526, 1526, 1527, 1, 1526, 1526,
-
1, 1526, 1, 1526, 1526, 1526, 1526, 1526,
-
1526, 1526, 1526, 1526, 1526, 1526, 1, 1,
-
1, 1526, 1, 1526, 1, 1526, 1526, 1526,
-
1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526,
-
1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526,
-
1526, 1526, 1526, 1526, 1526, 1526, 1526, 1,
-
1, 1, 1526, 1526, 1526, 1526, 1526, 1526,
-
1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526,
-
1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526,
-
1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526,
-
1526, 1526, 1526, 1, 1528, 1, 1494, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1494, 1, 1488,
-
1, 1, 1, 1489, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1488, 1526,
-
1, 1526, 1526, 1526, 1526, 1526, 1490, 1,
-
1526, 1526, 1491, 1526, 1494, 1526, 1526, 1526,
-
1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526,
-
1493, 1, 1, 1526, 1, 1526, 1, 1526,
-
1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526,
-
1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526,
-
1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526,
-
1526, 1, 1, 1, 1526, 1526, 1526, 1526,
-
1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526,
-
1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526,
-
1526, 1526, 1526, 1526, 1526, 1526, 1526, 1526,
-
1526, 1526, 1526, 1526, 1526, 1, 1500, 1,
-
1, 1, 1529, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1500, 1530, 1,
-
1530, 1530, 1530, 1530, 1530, 1531, 1, 1530,
-
1530, 1, 1530, 1, 1530, 1530, 1530, 1530,
-
1530, 1530, 1530, 1530, 1530, 1530, 1530, 1,
-
1, 1, 1530, 1, 1530, 1, 1530, 1530,
-
1530, 1530, 1530, 1530, 1530, 1530, 1530, 1530,
-
1530, 1530, 1530, 1530, 1530, 1530, 1530, 1530,
-
1530, 1530, 1530, 1530, 1530, 1530, 1530, 1530,
-
1, 1, 1, 1530, 1530, 1530, 1530, 1530,
-
1530, 1530, 1530, 1530, 1530, 1530, 1530, 1530,
-
1530, 1530, 1530, 1530, 1530, 1530, 1530, 1530,
-
1530, 1530, 1530, 1530, 1530, 1530, 1530, 1530,
-
1530, 1530, 1530, 1530, 1, 1532, 1532, 1532,
-
1532, 1532, 1532, 1532, 1532, 1483, 1, 1532,
-
1532, 1533, 1532, 1532, 1532, 1532, 1532, 1532,
-
1532, 1532, 1532, 1532, 1532, 1532, 1532, 1532,
-
1532, 1532, 1532, 1532, 1483, 1532, 1, 1532,
-
1532, 1532, 1532, 1532, 1532, 1532, 1532, 1532,
-
1532, 1532, 1532, 1532, 1532, 1532, 1532, 1532,
-
1532, 1532, 1532, 1532, 1532, 1532, 1532, 1532,
-
1532, 1532, 1532, 1532, 1532, 1532, 1532, 1532,
-
1532, 1532, 1532, 1532, 1532, 1532, 1532, 1532,
-
1532, 1532, 1532, 1532, 1532, 1532, 1532, 1532,
-
1532, 1532, 1532, 1532, 1532, 1532, 1532, 1532,
-
1534, 1532, 1532, 1532, 1532, 1532, 1532, 1532,
-
1532, 1532, 1532, 1532, 1532, 1532, 1532, 1532,
-
1532, 1532, 1532, 1532, 1532, 1532, 1532, 1532,
-
1532, 1532, 1532, 1532, 1532, 1532, 1532, 1532,
-
1532, 1532, 1532, 1532, 1, 1532, 1532, 1532,
-
1532, 1532, 1532, 1532, 1532, 1532, 1, 1532,
-
1532, 1535, 1532, 1532, 1532, 1532, 1532, 1532,
-
1532, 1532, 1532, 1532, 1532, 1532, 1532, 1532,
-
1532, 1532, 1532, 1532, 1532, 1532, 1536, 1532,
-
1532, 1532, 1532, 1532, 1532, 1532, 1532, 1532,
-
1532, 1532, 1532, 1532, 1532, 1532, 1532, 1532,
-
1532, 1532, 1532, 1532, 1532, 1532, 1532, 1532,
-
1532, 1532, 1532, 1532, 1532, 1532, 1532, 1532,
-
1532, 1532, 1532, 1532, 1532, 1532, 1532, 1532,
-
1532, 1532, 1532, 1532, 1532, 1532, 1532, 1532,
-
1532, 1532, 1532, 1532, 1532, 1532, 1532, 1532,
-
1534, 1532, 1532, 1532, 1532, 1532, 1532, 1532,
-
1532, 1532, 1532, 1532, 1532, 1532, 1532, 1532,
-
1532, 1532, 1532, 1532, 1532, 1532, 1532, 1532,
-
1532, 1532, 1532, 1532, 1532, 1532, 1532, 1532,
-
1532, 1532, 1532, 1532, 1, 1537, 1, 1532,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1532, 1,
-
1538, 1, 1, 1, 1539, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1538,
-
1536, 1483, 1536, 1536, 1536, 1536, 1536, 1540,
-
1, 1536, 1536, 1491, 1536, 1485, 1536, 1536,
-
1536, 1536, 1536, 1536, 1536, 1536, 1536, 1536,
-
1536, 1493, 1, 1, 1536, 1, 1536, 1,
-
1536, 1536, 1536, 1536, 1536, 1536, 1536, 1536,
-
1536, 1536, 1536, 1536, 1536, 1536, 1536, 1536,
-
1536, 1536, 1536, 1536, 1536, 1536, 1536, 1536,
-
1536, 1536, 1, 1, 1, 1536, 1536, 1536,
-
1536, 1536, 1536, 1536, 1536, 1536, 1536, 1536,
-
1536, 1536, 1536, 1536, 1536, 1536, 1536, 1536,
-
1536, 1536, 1536, 1536, 1536, 1536, 1536, 1536,
-
1536, 1536, 1536, 1536, 1536, 1536, 1, 1538,
-
1, 1, 1, 1539, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1538, 1,
-
1, 1, 1, 1, 1, 1, 1540, 1,
-
1, 1, 1491, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1493, 1, 1541, 1, 1538, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1538, 1, 1542, 1, 1,
-
1, 1543, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1542, 1, 1, 1,
-
1, 1, 1, 1, 1544, 1, 1, 1,
-
1499, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1501, 1,
-
1536, 1483, 1536, 1536, 1536, 1536, 1536, 1,
-
1, 1536, 1536, 1, 1536, 1485, 1536, 1536,
-
1536, 1536, 1536, 1536, 1536, 1536, 1536, 1536,
-
1536, 1, 1, 1, 1536, 1, 1536, 1,
-
1536, 1536, 1536, 1536, 1536, 1536, 1536, 1536,
-
1536, 1536, 1536, 1536, 1536, 1536, 1536, 1536,
-
1536, 1536, 1536, 1536, 1536, 1536, 1536, 1536,
-
1536, 1536, 1, 1, 1, 1536, 1536, 1536,
-
1536, 1536, 1536, 1536, 1536, 1536, 1536, 1536,
-
1536, 1536, 1536, 1536, 1536, 1536, 1536, 1536,
-
1536, 1536, 1536, 1536, 1536, 1536, 1536, 1536,
-
1536, 1536, 1536, 1536, 1536, 1536, 1, 1532,
-
1532, 1532, 1532, 1532, 1532, 1532, 1532, 1532,
-
1532, 1532, 1532, 1532, 1532, 1532, 1532, 1532,
-
1532, 1532, 1532, 1532, 1532, 1532, 1532, 1532,
-
1532, 1532, 1532, 1532, 1532, 1532, 1532, 1532,
-
1532, 1532, 1532, 1532, 1532, 1532, 1532, 1532,
-
1532, 1532, 1532, 1532, 1532, 1532, 1532, 1532,
-
1532, 1532, 1532, 1532, 1532, 1532, 1532, 1532,
-
1532, 1532, 1532, 1532, 1532, 1532, 1532, 1532,
-
1532, 1532, 1532, 1532, 1532, 1532, 1532, 1532,
-
1532, 1532, 1532, 1532, 1532, 1532, 1532, 1532,
-
1532, 1532, 1532, 1532, 1532, 1532, 1532, 1532,
-
1532, 1532, 1532, 1532, 1532, 1532, 1532, 1532,
-
1532, 1532, 1532, 1532, 1532, 1532, 1532, 1532,
-
1532, 1532, 1532, 1532, 1532, 1532, 1532, 1532,
-
1532, 1532, 1532, 1532, 1532, 1532, 1532, 1532,
-
1532, 1532, 1532, 1532, 1532, 1532, 1532, 1,
-
1545, 1, 1483, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1483, 1, 1494, 1, 1, 1, 1525,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1494, 1482, 1483, 1482, 1482, 1482,
-
1482, 1482, 1527, 1, 1482, 1482, 1, 1482,
-
1485, 1482, 1482, 1482, 1482, 1482, 1482, 1482,
-
1482, 1482, 1482, 1482, 1, 1, 1, 1482,
-
1, 1482, 1, 1482, 1482, 1482, 1482, 1482,
-
1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482,
-
1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482,
-
1482, 1482, 1482, 1482, 1482, 1, 1, 1,
-
1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482,
-
1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482,
-
1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482,
-
1482, 1482, 1482, 1482, 1482, 1482, 1482, 1482,
-
1482, 1, 1546, 1, 1, 1, 1547, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1546, 1548, 1549, 1548, 1548, 1548, 1548,
-
1548, 1550, 1, 1548, 1548, 1, 1548, 1551,
-
1548, 1548, 1548, 1548, 1548, 1548, 1548, 1548,
-
1548, 1548, 1548, 1, 1, 1, 1548, 1,
-
1548, 1, 1548, 1548, 1548, 1548, 1548, 1548,
-
1548, 1548, 1548, 1548, 1548, 1548, 1548, 1548,
-
1548, 1548, 1548, 1548, 1548, 1548, 1548, 1548,
-
1548, 1548, 1548, 1548, 1552, 1, 1, 1548,
-
1548, 1548, 1548, 1548, 1548, 1548, 1548, 1548,
-
1548, 1548, 1548, 1548, 1548, 1548, 1548, 1548,
-
1548, 1548, 1548, 1548, 1548, 1548, 1548, 1548,
-
1548, 1548, 1548, 1548, 1548, 1548, 1548, 1548,
-
1, 1486, 1486, 1486, 1486, 1486, 1486, 1486,
-
1486, 1486, 1, 1486, 1486, 1553, 1486, 1486,
-
1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486,
-
1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486,
-
1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486,
-
1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486,
-
1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486,
-
1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486,
-
1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486,
-
1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486,
-
1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486,
-
1486, 1486, 1486, 1, 1554, 1538, 1486, 1486,
-
1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486,
-
1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486,
-
1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486,
-
1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486,
-
1, 1555, 1, 1486, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1486, 1, 1486, 1486, 1486, 1486,
-
1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486,
-
1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486,
-
1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486,
-
1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486,
-
1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486,
-
1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486,
-
1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486,
-
1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486,
-
1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486,
-
1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486,
-
1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486,
-
1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486,
-
1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486,
-
1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486,
-
1486, 1486, 1486, 1486, 1486, 1486, 1486, 1486,
-
1486, 1486, 1486, 1486, 1, 1556, 1, 1,
-
1, 1557, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1556, 1238, 1239, 1238,
-
1238, 1238, 1238, 1238, 1558, 1, 1238, 1238,
-
1559, 1238, 1560, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1, 1561,
-
1242, 1238, 1, 1238, 1562, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1,
-
1, 1, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1, 1563, 1, 1, 1,
-
1564, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1563, 1565, 1566, 1565, 1565,
-
1565, 1565, 1565, 1567, 1, 1565, 1565, 1,
-
1565, 1225, 1565, 1565, 1565, 1565, 1565, 1565,
-
1565, 1565, 1565, 1565, 1565, 1, 1, 1209,
-
1565, 1, 1565, 1225, 1565, 1565, 1565, 1565,
-
1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565,
-
1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565,
-
1565, 1565, 1565, 1565, 1565, 1565, 1, 1,
-
1, 1565, 1565, 1565, 1565, 1565, 1565, 1565,
-
1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565,
-
1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565,
-
1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565,
-
1565, 1565, 1, 1568, 1, 1, 1, 1569,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1568, 1565, 1566, 1565, 1565, 1565,
-
1565, 1565, 1570, 1, 1565, 1565, 1, 1565,
-
1, 1565, 1565, 1565, 1565, 1565, 1565, 1565,
-
1565, 1565, 1565, 1565, 1, 1, 1199, 1565,
-
1, 1565, 1, 1565, 1565, 1565, 1565, 1565,
-
1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565,
-
1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565,
-
1565, 1565, 1565, 1565, 1565, 1, 1, 1,
-
1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565,
-
1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565,
-
1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565,
-
1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565,
-
1565, 1, 1571, 1, 1568, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1568, 1, 1572, 1, 1,
-
1, 1573, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1572, 1565, 1214, 1565,
-
1565, 1565, 1565, 1565, 1574, 1, 1565, 1565,
-
1305, 1565, 1217, 1565, 1565, 1565, 1565, 1565,
-
1565, 1565, 1565, 1565, 1565, 1565, 1, 1306,
-
1209, 1565, 1, 1565, 1575, 1565, 1565, 1565,
-
1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565,
-
1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565,
-
1565, 1565, 1565, 1565, 1565, 1565, 1565, 1,
-
1, 1, 1565, 1565, 1565, 1565, 1565, 1565,
-
1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565,
-
1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565,
-
1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565,
-
1565, 1565, 1565, 1, 1572, 1, 1, 1,
-
1573, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1572, 1213, 1214, 1213, 1213,
-
1213, 1213, 1213, 1574, 1, 1213, 1213, 1305,
-
1213, 1217, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1, 1306, 1209,
-
1213, 1, 1213, 1575, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1, 1,
-
1, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1, 1576, 1, 1577, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1577, 1, 1578, 1,
-
1, 1, 1579, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1578, 1238, 1239,
-
1238, 1238, 1238, 1238, 1238, 1580, 1, 1238,
-
1238, 1310, 1238, 1560, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1,
-
1311, 1242, 1238, 1, 1238, 1581, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1, 1, 1, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1, 1582, 1, 1,
-
1, 1583, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1582, 1584, 1585, 1584,
-
1584, 1584, 1584, 1584, 1586, 1, 1584, 1584,
-
1, 1584, 1587, 1584, 1584, 1584, 1584, 1584,
-
1584, 1584, 1584, 1584, 1584, 1584, 1, 1,
-
1209, 1584, 1, 1584, 1225, 1584, 1584, 1584,
-
1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584,
-
1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584,
-
1584, 1584, 1584, 1584, 1584, 1584, 1584, 1588,
-
1, 1, 1584, 1584, 1584, 1584, 1584, 1584,
-
1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584,
-
1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584,
-
1584, 1584, 1584, 1584, 1584, 1584, 1584, 1584,
-
1584, 1584, 1584, 1, 1589, 1, 1, 1,
-
1590, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1589, 1591, 1592, 1591, 1591,
-
1591, 1591, 1591, 1593, 1, 1591, 1591, 1,
-
1591, 1594, 1591, 1591, 1591, 1591, 1591, 1591,
-
1591, 1591, 1591, 1591, 1591, 1, 1, 1199,
-
1591, 1, 1591, 1, 1591, 1591, 1591, 1591,
-
1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591,
-
1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591,
-
1591, 1591, 1591, 1591, 1591, 1591, 1595, 1,
-
1, 1591, 1591, 1591, 1591, 1591, 1591, 1591,
-
1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591,
-
1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591,
-
1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591,
-
1591, 1591, 1, 1596, 1, 1589, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1589, 1, 1597, 1,
-
1, 1, 1598, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1597, 1591, 1592,
-
1591, 1591, 1591, 1591, 1591, 1599, 1, 1591,
-
1591, 1600, 1591, 1601, 1591, 1591, 1591, 1591,
-
1591, 1591, 1591, 1591, 1591, 1591, 1591, 1,
-
1602, 1209, 1591, 1, 1591, 1225, 1591, 1591,
-
1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591,
-
1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591,
-
1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591,
-
1, 1, 1, 1591, 1591, 1591, 1591, 1591,
-
1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591,
-
1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591,
-
1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591,
-
1591, 1591, 1591, 1591, 1, 1597, 1, 1,
-
1, 1598, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1597, 1213, 1214, 1213,
-
1213, 1213, 1213, 1213, 1599, 1, 1213, 1213,
-
1600, 1213, 1603, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1, 1602,
-
1209, 1213, 1, 1213, 1225, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1,
-
1, 1, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1, 1604, 1, 1605, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1605, 1, 1606,
-
1, 1, 1, 1607, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1606, 1238,
-
1239, 1238, 1238, 1238, 1238, 1238, 1608, 1,
-
1238, 1238, 1609, 1238, 1610, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1, 1611, 1242, 1238, 1, 1238, 1241, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1, 1, 1, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1, 1612, 1,
-
1, 1, 1613, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1612, 1614, 1214,
-
1614, 1614, 1614, 1614, 1614, 1615, 1, 1614,
-
1614, 1, 1614, 1225, 1614, 1614, 1614, 1614,
-
1614, 1614, 1614, 1614, 1614, 1614, 1614, 1,
-
1, 1209, 1614, 1, 1614, 1225, 1614, 1614,
-
1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614,
-
1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614,
-
1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614,
-
1, 1, 1, 1614, 1614, 1614, 1614, 1614,
-
1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614,
-
1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614,
-
1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614,
-
1614, 1614, 1614, 1614, 1, 1616, 1, 1,
-
1, 1617, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1616, 1614, 1214, 1614,
-
1614, 1614, 1614, 1614, 1618, 1, 1614, 1614,
-
1, 1614, 1, 1614, 1614, 1614, 1614, 1614,
-
1614, 1614, 1614, 1614, 1614, 1614, 1, 1,
-
1199, 1614, 1, 1614, 1, 1614, 1614, 1614,
-
1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614,
-
1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614,
-
1614, 1614, 1614, 1614, 1614, 1614, 1614, 1,
-
1, 1, 1614, 1614, 1614, 1614, 1614, 1614,
-
1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614,
-
1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614,
-
1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614,
-
1614, 1614, 1614, 1, 1619, 1, 1616, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1616, 1, 1597,
-
1, 1, 1, 1598, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1597, 1614,
-
1214, 1614, 1614, 1614, 1614, 1614, 1599, 1,
-
1614, 1614, 1600, 1614, 1603, 1614, 1614, 1614,
-
1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614,
-
1, 1602, 1209, 1614, 1, 1614, 1225, 1614,
-
1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614,
-
1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614,
-
1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614,
-
1614, 1, 1, 1, 1614, 1614, 1614, 1614,
-
1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614,
-
1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614,
-
1614, 1614, 1614, 1614, 1614, 1614, 1614, 1614,
-
1614, 1614, 1614, 1614, 1614, 1, 1620, 1,
-
1, 1, 1621, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1620, 1622, 1239,
-
1622, 1622, 1622, 1622, 1622, 1623, 1, 1622,
-
1622, 1, 1622, 1, 1622, 1622, 1622, 1622,
-
1622, 1622, 1622, 1622, 1622, 1622, 1622, 1,
-
1, 1253, 1622, 1, 1622, 1, 1622, 1622,
-
1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622,
-
1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622,
-
1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622,
-
1, 1, 1, 1622, 1622, 1622, 1622, 1622,
-
1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622,
-
1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622,
-
1622, 1622, 1622, 1622, 1622, 1622, 1622, 1622,
-
1622, 1622, 1622, 1622, 1, 1624, 1624, 1624,
-
1624, 1624, 1624, 1624, 1624, 1625, 1, 1624,
-
1624, 1626, 1624, 1624, 1624, 1624, 1624, 1624,
-
1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624,
-
1624, 1624, 1624, 1624, 1625, 1624, 1229, 1624,
-
1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624,
-
1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624,
-
1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624,
-
1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624,
-
1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624,
-
1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624,
-
1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624,
-
1627, 1624, 1624, 1624, 1624, 1624, 1624, 1624,
-
1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624,
-
1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624,
-
1624, 1624, 1624, 1624, 1624, 1624, 1624, 1624,
-
1624, 1624, 1624, 1624, 1, 1628, 1628, 1628,
-
1628, 1628, 1628, 1628, 1628, 1628, 1, 1628,
-
1628, 1629, 1628, 1628, 1628, 1628, 1628, 1628,
-
1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628,
-
1628, 1628, 1628, 1628, 1628, 1628, 1630, 1628,
-
1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628,
-
1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628,
-
1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628,
-
1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628,
-
1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628,
-
1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628,
-
1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628,
-
1631, 1628, 1628, 1628, 1628, 1628, 1628, 1628,
-
1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628,
-
1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628,
-
1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628,
-
1628, 1628, 1628, 1628, 1, 1632, 1, 1628,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1628, 1,
-
1633, 1, 1, 1, 1634, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1633,
-
1635, 1592, 1635, 1635, 1635, 1635, 1635, 1636,
-
1, 1635, 1635, 1600, 1635, 1637, 1635, 1635,
-
1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635,
-
1635, 1, 1602, 1209, 1635, 1, 1635, 1225,
-
1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635,
-
1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635,
-
1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635,
-
1635, 1635, 1, 1, 1, 1635, 1635, 1635,
-
1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635,
-
1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635,
-
1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635,
-
1635, 1635, 1635, 1635, 1635, 1635, 1, 1633,
-
1, 1, 1, 1634, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1633, 1213,
-
1214, 1213, 1213, 1213, 1213, 1213, 1636, 1,
-
1213, 1213, 1600, 1213, 1225, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1, 1602, 1209, 1213, 1, 1213, 1225, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1, 1, 1, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1, 1638, 1,
-
1639, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1639,
-
1, 1640, 1, 1, 1, 1641, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1640, 1238, 1239, 1238, 1238, 1238, 1238, 1238,
-
1642, 1, 1238, 1238, 1609, 1238, 1241, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1, 1611, 1242, 1238, 1, 1238,
-
1241, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1, 1, 1, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1,
-
1243, 1, 1, 1, 1244, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1243,
-
1635, 1592, 1635, 1635, 1635, 1635, 1635, 1245,
-
1, 1635, 1635, 1, 1635, 1637, 1635, 1635,
-
1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635,
-
1635, 1, 1, 1209, 1635, 1, 1635, 1225,
-
1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635,
-
1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635,
-
1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635,
-
1635, 1635, 1, 1, 1, 1635, 1635, 1635,
-
1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635,
-
1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635,
-
1635, 1635, 1635, 1635, 1635, 1635, 1635, 1635,
-
1635, 1635, 1635, 1635, 1635, 1635, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1628,
-
1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628,
-
1643, 1, 1628, 1628, 1644, 1628, 1628, 1628,
-
1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628,
-
1628, 1628, 1628, 1628, 1628, 1628, 1628, 1643,
-
1628, 1233, 1628, 1628, 1628, 1628, 1628, 1628,
-
1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628,
-
1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628,
-
1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628,
-
1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628,
-
1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628,
-
1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628,
-
1628, 1628, 1628, 1631, 1628, 1628, 1628, 1628,
-
1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628,
-
1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628,
-
1628, 1628, 1628, 1628, 1628, 1628, 1628, 1628,
-
1628, 1628, 1628, 1628, 1628, 1628, 1628, 1,
-
1645, 1, 1643, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1643, 1, 1612, 1, 1, 1, 1613,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1612, 1591, 1592, 1591, 1591, 1591,
-
1591, 1591, 1615, 1, 1591, 1591, 1, 1591,
-
1637, 1591, 1591, 1591, 1591, 1591, 1591, 1591,
-
1591, 1591, 1591, 1591, 1, 1, 1209, 1591,
-
1, 1591, 1225, 1591, 1591, 1591, 1591, 1591,
-
1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591,
-
1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591,
-
1591, 1591, 1591, 1591, 1591, 1, 1, 1,
-
1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591,
-
1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591,
-
1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591,
-
1591, 1591, 1591, 1591, 1591, 1591, 1591, 1591,
-
1591, 1, 1646, 1, 1, 1, 1647, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1646, 1648, 1649, 1648, 1648, 1648, 1648,
-
1648, 1650, 1, 1648, 1648, 1, 1648, 1651,
-
1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648,
-
1648, 1648, 1648, 1, 1, 1253, 1648, 1,
-
1648, 1, 1648, 1648, 1648, 1648, 1648, 1648,
-
1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648,
-
1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648,
-
1648, 1648, 1648, 1648, 1652, 1, 1, 1648,
-
1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648,
-
1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648,
-
1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648,
-
1648, 1648, 1648, 1648, 1648, 1648, 1648, 1648,
-
1, 1653, 1654, 1653, 1653, 1653, 1653, 1653,
-
1, 1, 1653, 1653, 1, 1653, 1594, 1653,
-
1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653,
-
1653, 1653, 1, 1, 1, 1653, 1, 1653,
-
1, 1653, 1653, 1653, 1653, 1653, 1653, 1653,
-
1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653,
-
1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653,
-
1653, 1653, 1653, 1, 1, 1, 1653, 1653,
-
1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653,
-
1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653,
-
1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653,
-
1653, 1653, 1653, 1653, 1653, 1653, 1653, 1,
-
1655, 1, 1, 1, 1656, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1655,
-
1653, 1654, 1653, 1653, 1653, 1653, 1653, 1657,
-
1, 1653, 1653, 1600, 1653, 1594, 1653, 1653,
-
1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653,
-
1653, 1, 1602, 1, 1653, 1, 1653, 1,
-
1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653,
-
1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653,
-
1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653,
-
1653, 1653, 1, 1, 1, 1653, 1653, 1653,
-
1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653,
-
1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653,
-
1653, 1653, 1653, 1653, 1653, 1653, 1653, 1653,
-
1653, 1653, 1653, 1653, 1653, 1653, 1, 1655,
-
1, 1, 1, 1656, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1655, 1,
-
1, 1, 1, 1, 1, 1, 1657, 1,
-
1, 1, 1600, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1602, 1, 1658, 1, 1655, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1655, 1, 1659, 1,
-
1, 1, 1660, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1659, 1, 1,
-
1, 1, 1, 1, 1, 1661, 1, 1,
-
1, 1609, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1611, 1, 1662, 1662, 1662, 1662, 1662, 1662,
-
1662, 1662, 1654, 1, 1662, 1662, 1663, 1662,
-
1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662,
-
1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662,
-
1662, 1654, 1662, 1, 1662, 1662, 1662, 1662,
-
1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662,
-
1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662,
-
1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662,
-
1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662,
-
1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662,
-
1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662,
-
1662, 1662, 1662, 1662, 1662, 1664, 1662, 1662,
-
1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662,
-
1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662,
-
1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662,
-
1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662,
-
1662, 1, 1662, 1662, 1662, 1662, 1662, 1662,
-
1662, 1662, 1662, 1, 1662, 1662, 1665, 1662,
-
1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662,
-
1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662,
-
1662, 1662, 1662, 1653, 1662, 1662, 1662, 1662,
-
1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662,
-
1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662,
-
1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662,
-
1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662,
-
1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662,
-
1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662,
-
1662, 1662, 1662, 1662, 1662, 1664, 1662, 1662,
-
1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662,
-
1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662,
-
1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662,
-
1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662,
-
1662, 1, 1666, 1, 1662, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1662, 1, 1662, 1662, 1662,
-
1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662,
-
1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662,
-
1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662,
-
1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662,
-
1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662,
-
1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662,
-
1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662,
-
1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662,
-
1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662,
-
1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662,
-
1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662,
-
1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662,
-
1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662,
-
1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662,
-
1662, 1662, 1662, 1662, 1662, 1662, 1662, 1662,
-
1662, 1662, 1662, 1662, 1662, 1, 1667, 1,
-
1654, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1654,
-
1, 1595, 1595, 1595, 1595, 1595, 1595, 1595,
-
1595, 1595, 1, 1595, 1595, 1668, 1595, 1595,
-
1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595,
-
1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595,
-
1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595,
-
1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595,
-
1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595,
-
1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595,
-
1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595,
-
1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595,
-
1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595,
-
1595, 1595, 1595, 1, 1669, 1655, 1595, 1595,
-
1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595,
-
1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595,
-
1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595,
-
1595, 1595, 1595, 1595, 1595, 1595, 1595, 1595,
-
1, 1670, 1, 1595, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1595, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1595, 1671, 1671, 1671,
-
1671, 1671, 1671, 1671, 1671, 1671, 1, 1671,
-
1671, 1672, 1671, 1671, 1671, 1671, 1671, 1671,
-
1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671,
-
1671, 1671, 1671, 1671, 1671, 1671, 1673, 1671,
-
1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671,
-
1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671,
-
1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671,
-
1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671,
-
1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671,
-
1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671,
-
1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671,
-
1674, 1671, 1671, 1671, 1671, 1671, 1671, 1671,
-
1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671,
-
1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671,
-
1671, 1671, 1671, 1671, 1671, 1671, 1671, 1671,
-
1671, 1671, 1671, 1671, 1, 1675, 1675, 1675,
-
1675, 1675, 1675, 1675, 1675, 1675, 1, 1675,
-
1675, 1676, 1675, 1675, 1675, 1675, 1675, 1675,
-
1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675,
-
1675, 1675, 1675, 1675, 1675, 1675, 1677, 1675,
-
1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675,
-
1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675,
-
1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675,
-
1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675,
-
1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675,
-
1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675,
-
1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675,
-
1678, 1675, 1675, 1675, 1675, 1675, 1675, 1675,
-
1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675,
-
1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675,
-
1675, 1675, 1675, 1675, 1675, 1675, 1675, 1675,
-
1675, 1675, 1675, 1675, 1, 1679, 1, 1675,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1675, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1675, 1680, 1, 1, 1, 1681, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1680, 1682, 1683, 1682, 1682, 1682, 1682, 1682,
-
1684, 1, 1682, 1682, 1, 1682, 1, 1682,
-
1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682,
-
1682, 1682, 1, 1, 1253, 1682, 1, 1682,
-
1, 1682, 1682, 1682, 1682, 1682, 1682, 1682,
-
1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682,
-
1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682,
-
1682, 1682, 1682, 1, 1, 1, 1682, 1682,
-
1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682,
-
1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682,
-
1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682,
-
1682, 1682, 1682, 1682, 1682, 1682, 1682, 1,
-
1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685,
-
1686, 1, 1685, 1685, 1687, 1685, 1685, 1685,
-
1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685,
-
1685, 1685, 1685, 1685, 1685, 1685, 1685, 1686,
-
1685, 1229, 1685, 1685, 1685, 1685, 1685, 1685,
-
1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685,
-
1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685,
-
1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685,
-
1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685,
-
1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685,
-
1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685,
-
1685, 1685, 1685, 1688, 1685, 1685, 1685, 1685,
-
1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685,
-
1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685,
-
1685, 1685, 1685, 1685, 1685, 1685, 1685, 1685,
-
1685, 1685, 1685, 1685, 1685, 1685, 1685, 1,
-
1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689,
-
1689, 1, 1689, 1689, 1690, 1689, 1689, 1689,
-
1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689,
-
1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689,
-
1689, 1691, 1689, 1689, 1689, 1689, 1689, 1689,
-
1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689,
-
1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689,
-
1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689,
-
1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689,
-
1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689,
-
1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689,
-
1689, 1689, 1689, 1692, 1689, 1689, 1689, 1689,
-
1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689,
-
1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689,
-
1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689,
-
1689, 1689, 1689, 1689, 1689, 1689, 1689, 1,
-
1693, 1, 1689, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1689, 1, 1694, 1, 1, 1, 1695,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1694, 1696, 1204, 1696, 1696, 1696,
-
1696, 1696, 1697, 1, 1696, 1696, 1206, 1696,
-
1698, 1696, 1696, 1696, 1696, 1696, 1696, 1696,
-
1696, 1696, 1696, 1696, 1, 1208, 1209, 1696,
-
1, 1696, 1210, 1696, 1696, 1696, 1696, 1696,
-
1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696,
-
1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696,
-
1696, 1696, 1696, 1696, 1696, 1, 1, 1,
-
1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696,
-
1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696,
-
1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696,
-
1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696,
-
1696, 1, 1699, 1, 1, 1, 1700, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1699, 1213, 1214, 1213, 1213, 1213, 1213,
-
1213, 1701, 1, 1213, 1213, 1216, 1213, 1225,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1, 1218, 1209, 1213, 1,
-
1213, 1219, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1, 1, 1, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1, 1702, 1, 1703, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1703, 1, 1704, 1, 1, 1,
-
1705, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1704, 1238, 1239, 1238, 1238,
-
1238, 1238, 1238, 1706, 1, 1238, 1238, 1559,
-
1238, 1241, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1, 1561, 1242,
-
1238, 1, 1238, 1562, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1, 1,
-
1, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1, 1707, 1, 1, 1, 1708,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1707, 1696, 1204, 1696, 1696, 1696,
-
1696, 1696, 1709, 1, 1696, 1696, 1206, 1696,
-
1698, 1696, 1696, 1696, 1696, 1696, 1696, 1696,
-
1696, 1696, 1696, 1696, 1, 1208, 1209, 1696,
-
1, 1696, 1210, 1696, 1696, 1696, 1696, 1696,
-
1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696,
-
1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696,
-
1696, 1696, 1696, 1696, 1696, 1, 1, 1,
-
1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696,
-
1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696,
-
1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696,
-
1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696,
-
1696, 1, 1710, 1, 1, 1, 1711, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1710, 1213, 1214, 1213, 1213, 1213, 1213,
-
1213, 1712, 1, 1213, 1213, 1216, 1213, 1,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1, 1218, 1199, 1213, 1,
-
1213, 1713, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1, 1, 1, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1, 1714, 1, 1710, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1710, 1, 1715, 1, 1, 1,
-
1716, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1715, 1238, 1239, 1238, 1238,
-
1238, 1238, 1238, 1717, 1, 1238, 1238, 1559,
-
1238, 1, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1, 1561, 1253,
-
1238, 1, 1238, 1718, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1, 1,
-
1, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1, 1719, 1, 1, 1, 1720,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1719, 1721, 1722, 1721, 1721, 1721,
-
1721, 1721, 1723, 1, 1721, 1721, 1, 1721,
-
1724, 1721, 1721, 1721, 1721, 1721, 1721, 1721,
-
1721, 1721, 1721, 1721, 1, 1, 1, 1721,
-
1, 1721, 1, 1721, 1721, 1721, 1721, 1721,
-
1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721,
-
1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721,
-
1721, 1721, 1721, 1721, 1721, 1588, 1, 1,
-
1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721,
-
1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721,
-
1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721,
-
1721, 1721, 1721, 1721, 1721, 1721, 1721, 1721,
-
1721, 1, 1725, 1, 1, 1, 1726, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1725, 1727, 1654, 1727, 1727, 1727, 1727,
-
1727, 1728, 1, 1727, 1727, 1, 1727, 1594,
-
1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727,
-
1727, 1727, 1727, 1, 1, 1, 1727, 1,
-
1727, 1, 1727, 1727, 1727, 1727, 1727, 1727,
-
1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727,
-
1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727,
-
1727, 1727, 1727, 1727, 1595, 1, 1, 1727,
-
1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727,
-
1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727,
-
1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727,
-
1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727,
-
1, 1729, 1, 1725, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1725, 1, 1730, 1, 1, 1,
-
1731, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1730, 1727, 1654, 1727, 1727,
-
1727, 1727, 1727, 1732, 1, 1727, 1727, 1600,
-
1727, 1733, 1727, 1727, 1727, 1727, 1727, 1727,
-
1727, 1727, 1727, 1727, 1727, 1, 1602, 1,
-
1727, 1, 1727, 1, 1727, 1727, 1727, 1727,
-
1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727,
-
1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727,
-
1727, 1727, 1727, 1727, 1727, 1727, 1, 1,
-
1, 1727, 1727, 1727, 1727, 1727, 1727, 1727,
-
1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727,
-
1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727,
-
1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727,
-
1727, 1727, 1, 1730, 1, 1, 1, 1731,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1730, 1, 1, 1, 1, 1,
-
1, 1, 1732, 1, 1, 1, 1600, 1,
-
1734, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1602, 1, 1735,
-
1, 1730, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1730, 1, 1736, 1, 1, 1, 1737, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1736, 1, 1, 1, 1, 1, 1,
-
1, 1738, 1, 1, 1, 1609, 1, 1739,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1611, 1, 1734, 1,
-
1, 1, 1740, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1734, 1741, 1,
-
1741, 1741, 1741, 1741, 1741, 1742, 1, 1741,
-
1741, 1, 1741, 1, 1741, 1741, 1741, 1741,
-
1741, 1741, 1741, 1741, 1741, 1741, 1741, 1,
-
1, 1, 1741, 1, 1741, 1, 1741, 1741,
-
1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741,
-
1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741,
-
1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741,
-
1, 1, 1, 1741, 1741, 1741, 1741, 1741,
-
1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741,
-
1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741,
-
1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741,
-
1741, 1741, 1741, 1741, 1, 1743, 1, 1734,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1734, 1,
-
1730, 1, 1, 1, 1731, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1730,
-
1741, 1, 1741, 1741, 1741, 1741, 1741, 1732,
-
1, 1741, 1741, 1600, 1741, 1734, 1741, 1741,
-
1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741,
-
1741, 1, 1602, 1, 1741, 1, 1741, 1,
-
1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741,
-
1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741,
-
1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741,
-
1741, 1741, 1, 1, 1, 1741, 1741, 1741,
-
1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741,
-
1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741,
-
1741, 1741, 1741, 1741, 1741, 1741, 1741, 1741,
-
1741, 1741, 1741, 1741, 1741, 1741, 1, 1739,
-
1, 1, 1, 1744, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1739, 1745,
-
1, 1745, 1745, 1745, 1745, 1745, 1746, 1,
-
1745, 1745, 1, 1745, 1, 1745, 1745, 1745,
-
1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745,
-
1, 1, 1, 1745, 1, 1745, 1, 1745,
-
1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745,
-
1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745,
-
1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745,
-
1745, 1, 1, 1, 1745, 1745, 1745, 1745,
-
1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745,
-
1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745,
-
1745, 1745, 1745, 1745, 1745, 1745, 1745, 1745,
-
1745, 1745, 1745, 1745, 1745, 1, 1734, 1,
-
1, 1, 1740, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1734, 1727, 1654,
-
1727, 1727, 1727, 1727, 1727, 1742, 1, 1727,
-
1727, 1, 1727, 1594, 1727, 1727, 1727, 1727,
-
1727, 1727, 1727, 1727, 1727, 1727, 1727, 1,
-
1, 1, 1727, 1, 1727, 1, 1727, 1727,
-
1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727,
-
1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727,
-
1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727,
-
1, 1, 1, 1727, 1727, 1727, 1727, 1727,
-
1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727,
-
1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727,
-
1727, 1727, 1727, 1727, 1727, 1727, 1727, 1727,
-
1727, 1727, 1727, 1727, 1, 1747, 1, 1,
-
1, 1748, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1747, 1749, 1750, 1749,
-
1749, 1749, 1749, 1749, 1751, 1, 1749, 1749,
-
1, 1749, 1651, 1749, 1749, 1749, 1749, 1749,
-
1749, 1749, 1749, 1749, 1749, 1749, 1, 1,
-
1, 1749, 1, 1749, 1, 1749, 1749, 1749,
-
1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749,
-
1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749,
-
1749, 1749, 1749, 1749, 1749, 1749, 1749, 1652,
-
1, 1, 1749, 1749, 1749, 1749, 1749, 1749,
-
1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749,
-
1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749,
-
1749, 1749, 1749, 1749, 1749, 1749, 1749, 1749,
-
1749, 1749, 1749, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1689, 1689, 1689, 1689,
-
1689, 1689, 1689, 1689, 1689, 1752, 1, 1689,
-
1689, 1753, 1689, 1689, 1689, 1689, 1689, 1689,
-
1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689,
-
1689, 1689, 1689, 1689, 1752, 1689, 1233, 1689,
-
1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689,
-
1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689,
-
1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689,
-
1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689,
-
1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689,
-
1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689,
-
1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689,
-
1692, 1689, 1689, 1689, 1689, 1689, 1689, 1689,
-
1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689,
-
1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689,
-
1689, 1689, 1689, 1689, 1689, 1689, 1689, 1689,
-
1689, 1689, 1689, 1689, 1, 1754, 1, 1752,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1752, 1,
-
1755, 1, 1, 1, 1756, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1755,
-
1203, 1757, 1203, 1203, 1203, 1203, 1203, 1758,
-
1, 1203, 1203, 1206, 1203, 1698, 1203, 1203,
-
1203, 1203, 1203, 1203, 1203, 1203, 1203, 1203,
-
1203, 1, 1208, 1209, 1203, 1, 1203, 1210,
-
1203, 1203, 1203, 1203, 1203, 1203, 1203, 1203,
-
1203, 1203, 1203, 1203, 1203, 1203, 1203, 1203,
-
1203, 1203, 1203, 1203, 1203, 1203, 1203, 1203,
-
1203, 1203, 1, 1, 1, 1203, 1203, 1203,
-
1203, 1203, 1203, 1203, 1203, 1203, 1203, 1203,
-
1203, 1203, 1203, 1203, 1203, 1203, 1203, 1203,
-
1203, 1203, 1203, 1203, 1203, 1203, 1203, 1203,
-
1203, 1203, 1203, 1203, 1203, 1203, 1, 1759,
-
1, 1, 1, 1760, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1759, 1565,
-
1566, 1565, 1565, 1565, 1565, 1565, 1761, 1,
-
1565, 1565, 1216, 1565, 1, 1565, 1565, 1565,
-
1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565,
-
1, 1218, 1199, 1565, 1, 1565, 1713, 1565,
-
1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565,
-
1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565,
-
1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565,
-
1565, 1, 1, 1, 1565, 1565, 1565, 1565,
-
1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565,
-
1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565,
-
1565, 1565, 1565, 1565, 1565, 1565, 1565, 1565,
-
1565, 1565, 1565, 1565, 1565, 1, 1762, 1,
-
1759, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1759,
-
1, 1763, 1, 1, 1, 1764, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1763, 1682, 1683, 1682, 1682, 1682, 1682, 1682,
-
1765, 1, 1682, 1682, 1559, 1682, 1, 1682,
-
1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682,
-
1682, 1682, 1, 1561, 1253, 1682, 1, 1682,
-
1718, 1682, 1682, 1682, 1682, 1682, 1682, 1682,
-
1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682,
-
1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682,
-
1682, 1682, 1682, 1, 1, 1, 1682, 1682,
-
1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682,
-
1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682,
-
1682, 1682, 1682, 1682, 1682, 1682, 1682, 1682,
-
1682, 1682, 1682, 1682, 1682, 1682, 1682, 1,
-
1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766,
-
1767, 1, 1766, 1766, 1768, 1766, 1766, 1766,
-
1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766,
-
1766, 1766, 1766, 1766, 1766, 1766, 1766, 1767,
-
1766, 1673, 1766, 1766, 1766, 1766, 1766, 1766,
-
1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766,
-
1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766,
-
1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766,
-
1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766,
-
1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766,
-
1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766,
-
1766, 1766, 1766, 1769, 1766, 1766, 1766, 1766,
-
1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766,
-
1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766,
-
1766, 1766, 1766, 1766, 1766, 1766, 1766, 1766,
-
1766, 1766, 1766, 1766, 1766, 1766, 1766, 1,
-
1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770,
-
1770, 1, 1770, 1770, 1771, 1770, 1770, 1770,
-
1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770,
-
1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770,
-
1770, 1772, 1770, 1770, 1770, 1770, 1770, 1770,
-
1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770,
-
1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770,
-
1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770,
-
1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770,
-
1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770,
-
1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770,
-
1770, 1770, 1770, 1773, 1770, 1770, 1770, 1770,
-
1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770,
-
1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770,
-
1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770,
-
1770, 1770, 1770, 1770, 1770, 1770, 1770, 1,
-
1774, 1, 1770, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1770, 1, 1201, 1, 1, 1, 1202,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1201, 1696, 1204, 1696, 1696, 1696,
-
1696, 1696, 1205, 1, 1696, 1696, 1206, 1696,
-
1207, 1696, 1696, 1696, 1696, 1696, 1696, 1696,
-
1696, 1696, 1696, 1696, 1, 1208, 1209, 1696,
-
1, 1696, 1210, 1696, 1696, 1696, 1696, 1696,
-
1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696,
-
1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696,
-
1696, 1696, 1696, 1696, 1696, 1, 1, 1,
-
1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696,
-
1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696,
-
1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696,
-
1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696,
-
1696, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1770, 1770, 1770, 1770, 1770, 1770,
-
1770, 1770, 1770, 1775, 1, 1770, 1770, 1776,
-
1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770,
-
1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770,
-
1770, 1770, 1775, 1770, 1677, 1770, 1770, 1770,
-
1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770,
-
1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770,
-
1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770,
-
1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770,
-
1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770,
-
1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770,
-
1770, 1770, 1770, 1770, 1770, 1770, 1773, 1770,
-
1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770,
-
1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770,
-
1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770,
-
1770, 1770, 1770, 1770, 1770, 1770, 1770, 1770,
-
1770, 1770, 1, 1777, 1, 1775, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1775, 1, 1778, 1778,
-
1778, 1778, 1778, 1778, 1778, 1778, 1779, 1,
-
1778, 1778, 1780, 1778, 1778, 1778, 1778, 1778,
-
1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778,
-
1778, 1778, 1778, 1778, 1778, 1779, 1778, 1781,
-
1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778,
-
1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778,
-
1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778,
-
1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778,
-
1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778,
-
1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778,
-
1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778,
-
1778, 1782, 1778, 1778, 1778, 1778, 1778, 1778,
-
1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778,
-
1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778,
-
1778, 1778, 1778, 1778, 1778, 1778, 1778, 1778,
-
1778, 1778, 1778, 1778, 1778, 1, 1783, 1783,
-
1783, 1783, 1783, 1783, 1783, 1783, 1783, 1,
-
1783, 1783, 1784, 1783, 1783, 1783, 1783, 1783,
-
1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783,
-
1783, 1783, 1783, 1783, 1783, 1783, 1783, 1785,
-
1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783,
-
1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783,
-
1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783,
-
1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783,
-
1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783,
-
1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783,
-
1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783,
-
1783, 1786, 1783, 1783, 1783, 1783, 1783, 1783,
-
1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783,
-
1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783,
-
1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783,
-
1783, 1783, 1783, 1783, 1783, 1, 1787, 1,
-
1783, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1783,
-
1, 1788, 1, 1, 1, 1789, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1788, 1696, 1204, 1696, 1696, 1696, 1696, 1696,
-
1790, 1, 1696, 1696, 1791, 1696, 1207, 1696,
-
1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696,
-
1696, 1696, 1, 1792, 1209, 1696, 1, 1696,
-
1793, 1696, 1696, 1696, 1696, 1696, 1696, 1696,
-
1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696,
-
1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696,
-
1696, 1696, 1696, 1, 1, 1, 1696, 1696,
-
1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696,
-
1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696,
-
1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696,
-
1696, 1696, 1696, 1696, 1696, 1696, 1696, 1,
-
1794, 1, 1, 1, 1795, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1794,
-
1213, 1214, 1213, 1213, 1213, 1213, 1213, 1796,
-
1, 1213, 1213, 1797, 1213, 1217, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1, 1798, 1209, 1213, 1, 1213, 1799,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1, 1, 1, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1, 1800,
-
1, 1801, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1801, 1, 1802, 1, 1, 1, 1803, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1802, 1238, 1239, 1238, 1238, 1238, 1238,
-
1238, 1804, 1, 1238, 1238, 1805, 1238, 1560,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1, 1806, 1242, 1238, 1,
-
1238, 1807, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1, 1, 1, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1783, 1783, 1783, 1783, 1783, 1783, 1783,
-
1783, 1783, 1808, 1, 1783, 1783, 1809, 1783,
-
1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783,
-
1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783,
-
1783, 1808, 1783, 1810, 1783, 1783, 1783, 1783,
-
1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783,
-
1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783,
-
1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783,
-
1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783,
-
1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783,
-
1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783,
-
1783, 1783, 1783, 1783, 1783, 1786, 1783, 1783,
-
1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783,
-
1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783,
-
1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783,
-
1783, 1783, 1783, 1783, 1783, 1783, 1783, 1783,
-
1783, 1, 1811, 1, 1808, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1808, 1, 1812, 1, 1,
-
1, 1813, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1812, 1213, 1214, 1213,
-
1213, 1213, 1213, 1213, 1814, 1, 1213, 1213,
-
1815, 1213, 1217, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1, 1816,
-
1209, 1213, 1, 1213, 1817, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1,
-
1, 1, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
-
1213, 1213, 1213, 1, 1818, 1, 1819, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1819, 1, 1820,
-
1, 1, 1, 1821, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1820, 1238,
-
1239, 1238, 1238, 1238, 1238, 1238, 1822, 1,
-
1238, 1238, 1823, 1238, 1560, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1, 1824, 1242, 1238, 1, 1238, 1825, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1, 1, 1, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
-
1238, 1238, 1238, 1238, 1238, 1, 1826, 1,
-
1, 1, 1827, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1826, 1828, 1829,
-
1828, 1828, 1828, 1828, 1828, 1830, 1, 1828,
-
1828, 1182, 1828, 1831, 1828, 1828, 1828, 1828,
-
1828, 1828, 1828, 1828, 1828, 1828, 1828, 1,
-
1184, 1253, 1828, 1, 1828, 1, 1828, 1828,
-
1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828,
-
1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828,
-
1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828,
-
1, 1, 1, 1828, 1828, 1828, 1828, 1828,
-
1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828,
-
1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828,
-
1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828,
-
1828, 1828, 1828, 1828, 1, 1832, 1833, 1832,
-
1832, 1832, 1832, 1832, 1, 1, 1832, 1832,
-
1, 1832, 1834, 1832, 1832, 1832, 1832, 1832,
-
1832, 1832, 1832, 1832, 1832, 1832, 1, 1,
-
1, 1832, 1, 1832, 1, 1832, 1832, 1832,
-
1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832,
-
1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832,
-
1832, 1832, 1832, 1832, 1832, 1832, 1832, 1,
-
1, 1, 1832, 1832, 1832, 1832, 1832, 1832,
-
1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832,
-
1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832,
-
1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832,
-
1832, 1832, 1832, 1, 1835, 1, 1, 1,
-
1836, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1835, 1832, 1833, 1832, 1832,
-
1832, 1832, 1832, 1837, 1, 1832, 1832, 1206,
-
1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832,
-
1832, 1832, 1832, 1832, 1832, 1, 1208, 1,
-
1832, 1, 1832, 1838, 1832, 1832, 1832, 1832,
-
1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832,
-
1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832,
-
1832, 1832, 1832, 1832, 1832, 1832, 1, 1,
-
1, 1832, 1832, 1832, 1832, 1832, 1832, 1832,
-
1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832,
-
1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832,
-
1832, 1832, 1832, 1832, 1832, 1832, 1832, 1832,
-
1832, 1832, 1, 1839, 1, 1, 1, 1840,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1839, 1, 1, 1, 1, 1,
-
1, 1, 1841, 1, 1, 1, 1216, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1218, 1, 1,
-
1, 1, 1713, 1, 1842, 1, 1839, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1839, 1, 1843,
-
1, 1, 1, 1844, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1843, 1,
-
1, 1, 1, 1, 1, 1, 1845, 1,
-
1, 1, 1559, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1561, 1, 1, 1, 1, 1718, 1,
-
1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846,
-
1833, 1, 1846, 1846, 1847, 1846, 1846, 1846,
-
1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846,
-
1846, 1846, 1846, 1846, 1846, 1846, 1846, 1833,
-
1846, 1, 1846, 1846, 1846, 1846, 1846, 1846,
-
1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846,
-
1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846,
-
1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846,
-
1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846,
-
1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846,
-
1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846,
-
1846, 1846, 1846, 1848, 1846, 1846, 1846, 1846,
-
1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846,
-
1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846,
-
1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846,
-
1846, 1846, 1846, 1846, 1846, 1846, 1846, 1,
-
1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846,
-
1846, 1, 1846, 1846, 1849, 1846, 1846, 1846,
-
1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846,
-
1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846,
-
1846, 1832, 1846, 1846, 1846, 1846, 1846, 1846,
-
1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846,
-
1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846,
-
1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846,
-
1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846,
-
1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846,
-
1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846,
-
1846, 1846, 1846, 1848, 1846, 1846, 1846, 1846,
-
1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846,
-
1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846,
-
1846, 1846, 1846, 1846, 1846, 1846, 1846, 1846,
-
1846, 1846, 1846, 1846, 1846, 1846, 1846, 1,
-
1850, 1, 1846, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1846, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1846, 1851, 1, 1833, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1833, 1, 1243,
-
1, 1, 1, 1244, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1243, 1696,
-
1204, 1696, 1696, 1696, 1696, 1696, 1245, 1,
-
1696, 1696, 1, 1696, 1852, 1696, 1696, 1696,
-
1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696,
-
1, 1, 1209, 1696, 1, 1696, 1225, 1696,
-
1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696,
-
1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696,
-
1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696,
-
1696, 1, 1, 1, 1696, 1696, 1696, 1696,
-
1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696,
-
1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696,
-
1696, 1696, 1696, 1696, 1696, 1696, 1696, 1696,
-
1696, 1696, 1696, 1696, 1696, 1, 1853, 1854,
-
1853, 1853, 1853, 1853, 1853, 1, 1, 1853,
-
1853, 1, 1853, 1855, 1853, 1853, 1853, 1853,
-
1853, 1853, 1853, 1853, 1853, 1853, 1853, 1,
-
1, 1, 1853, 1, 1853, 1, 1853, 1853,
-
1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853,
-
1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853,
-
1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853,
-
1, 1, 1, 1853, 1853, 1853, 1853, 1853,
-
1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853,
-
1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853,
-
1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853,
-
1853, 1853, 1853, 1853, 1, 1856, 1, 1,
-
1, 1857, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1856, 1853, 1854, 1853,
-
1853, 1853, 1853, 1853, 1858, 1, 1853, 1853,
-
136, 1853, 1853, 1853, 1853, 1853, 1853, 1853,
-
1853, 1853, 1853, 1853, 1853, 1853, 1, 138,
-
1, 1853, 1, 1853, 1859, 1853, 1853, 1853,
-
1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853,
-
1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853,
-
1853, 1853, 1853, 1853, 1853, 1853, 1853, 1,
-
1, 1, 1853, 1853, 1853, 1853, 1853, 1853,
-
1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853,
-
1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853,
-
1853, 1853, 1853, 1853, 1853, 1853, 1853, 1853,
-
1853, 1853, 1853, 1, 1860, 1, 1, 1,
-
1861, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1860, 1, 1, 1, 1,
-
1, 1, 1, 1862, 1, 1, 1, 146,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 148, 1,
-
1, 1, 1, 1096, 1, 1863, 1, 1860,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1860, 1,
-
1864, 1, 1, 1, 1865, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1864,
-
1, 1, 1, 1, 1, 1, 1, 1866,
-
1, 1, 1, 1076, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 590, 1, 1, 1, 1, 1101,
-
1, 1867, 1867, 1867, 1867, 1867, 1867, 1867,
-
1867, 1854, 1, 1867, 1867, 1868, 1867, 1867,
-
1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867,
-
1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867,
-
1854, 1867, 1, 1867, 1867, 1867, 1867, 1867,
-
1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867,
-
1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867,
-
1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867,
-
1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867,
-
1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867,
-
1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867,
-
1867, 1867, 1867, 1867, 1869, 1867, 1867, 1867,
-
1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867,
-
1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867,
-
1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867,
-
1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867,
-
1, 1867, 1867, 1867, 1867, 1867, 1867, 1867,
-
1867, 1867, 1, 1867, 1867, 1870, 1867, 1867,
-
1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867,
-
1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867,
-
1867, 1867, 1853, 1867, 1867, 1867, 1867, 1867,
-
1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867,
-
1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867,
-
1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867,
-
1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867,
-
1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867,
-
1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867,
-
1867, 1867, 1867, 1867, 1869, 1867, 1867, 1867,
-
1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867,
-
1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867,
-
1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867,
-
1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867,
-
1, 1871, 1, 1867, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1867, 1, 1867, 1867, 1867, 1867,
-
1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867,
-
1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867,
-
1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867,
-
1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867,
-
1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867,
-
1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867,
-
1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867,
-
1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867,
-
1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867,
-
1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867,
-
1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867,
-
1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867,
-
1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867,
-
1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867,
-
1867, 1867, 1867, 1867, 1867, 1867, 1867, 1867,
-
1867, 1867, 1867, 1867, 1, 1872, 1, 1854,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1854, 1,
-
173, 1, 1, 1, 174, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 173,
-
262, 134, 262, 262, 262, 262, 262, 175,
-
1, 262, 262, 1, 262, 1873, 262, 262,
-
262, 262, 262, 262, 262, 262, 262, 262,
-
262, 1, 1, 139, 262, 1, 262, 155,
-
262, 262, 262, 262, 262, 262, 262, 262,
-
262, 262, 262, 262, 262, 262, 262, 262,
-
262, 262, 262, 262, 262, 262, 262, 262,
-
262, 262, 1, 1, 1, 262, 262, 262,
-
262, 262, 262, 262, 262, 262, 262, 262,
-
262, 262, 262, 262, 262, 262, 262, 262,
-
262, 262, 262, 262, 262, 262, 262, 262,
-
262, 262, 262, 262, 262, 262, 1, 1874,
-
1, 1, 1, 1875, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1874, 1876,
-
1877, 1876, 1876, 1876, 1876, 1876, 1878, 1,
-
1876, 1876, 1, 1876, 1879, 1876, 1876, 1876,
-
1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876,
-
1, 1, 1, 1876, 1, 1876, 1, 1876,
-
1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876,
-
1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876,
-
1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876,
-
1876, 1880, 1, 1, 1876, 1876, 1876, 1876,
-
1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876,
-
1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876,
-
1876, 1876, 1876, 1876, 1876, 1876, 1876, 1876,
-
1876, 1876, 1876, 1876, 1876, 1, 1881, 1,
-
1, 1, 1882, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1881, 1883, 1884,
-
1883, 1883, 1883, 1883, 1883, 1885, 1, 1883,
-
1883, 1, 1883, 1886, 1883, 1883, 1883, 1883,
-
1883, 1883, 1883, 1883, 1883, 1883, 1883, 1,
-
1, 1, 1883, 1, 1883, 1, 1883, 1883,
-
1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883,
-
1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883,
-
1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883,
-
1887, 1, 1, 1883, 1883, 1883, 1883, 1883,
-
1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883,
-
1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883,
-
1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883,
-
1883, 1883, 1883, 1883, 1, 1888, 1, 1881,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1881, 1,
-
1889, 1, 1, 1, 1890, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1889,
-
1883, 1884, 1883, 1883, 1883, 1883, 1883, 1891,
-
1, 1883, 1883, 1, 1883, 1892, 1883, 1883,
-
1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883,
-
1883, 1, 1, 1, 1883, 1893, 1883, 1,
-
1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883,
-
1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883,
-
1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883,
-
1883, 1883, 1, 1, 1, 1883, 1883, 1883,
-
1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883,
-
1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883,
-
1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883,
-
1883, 1883, 1883, 1883, 1883, 1883, 1, 1889,
-
1, 1, 1, 1890, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1889, 1,
-
1, 1, 1, 1, 1, 1, 1891, 1,
-
1, 1, 1, 1, 1894, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1893, 1, 1895, 1,
-
1889, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1889,
-
1, 1896, 1, 1, 1, 1897, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1896, 1, 1, 1, 1, 1, 1, 1,
-
1898, 1, 1, 1, 1, 1, 1899, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1900, 1,
-
1894, 1, 1, 1, 1901, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1894,
-
1902, 1, 1902, 1902, 1902, 1902, 1902, 1903,
-
1, 1902, 1902, 1, 1902, 1, 1902, 1902,
-
1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902,
-
1902, 1, 1, 1, 1902, 1, 1902, 1,
-
1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902,
-
1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902,
-
1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902,
-
1902, 1902, 1, 1, 1, 1902, 1902, 1902,
-
1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902,
-
1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902,
-
1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902,
-
1902, 1902, 1902, 1902, 1902, 1902, 1, 1904,
-
1, 1894, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1894, 1, 1889, 1, 1, 1, 1890, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1889, 1902, 1, 1902, 1902, 1902, 1902,
-
1902, 1891, 1, 1902, 1902, 1, 1902, 1894,
-
1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902,
-
1902, 1902, 1902, 1, 1, 1, 1902, 1893,
-
1902, 1, 1902, 1902, 1902, 1902, 1902, 1902,
-
1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902,
-
1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902,
-
1902, 1902, 1902, 1902, 1, 1, 1, 1902,
-
1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902,
-
1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902,
-
1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902,
-
1902, 1902, 1902, 1902, 1902, 1902, 1902, 1902,
-
1, 1899, 1, 1, 1, 1905, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1899, 1906, 1, 1906, 1906, 1906, 1906, 1906,
-
1907, 1, 1906, 1906, 1, 1906, 1, 1906,
-
1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906,
-
1906, 1906, 1, 1, 1, 1906, 1, 1906,
-
1, 1906, 1906, 1906, 1906, 1906, 1906, 1906,
-
1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906,
-
1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906,
-
1906, 1906, 1906, 1, 1, 1, 1906, 1906,
-
1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906,
-
1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906,
-
1906, 1906, 1906, 1906, 1906, 1906, 1906, 1906,
-
1906, 1906, 1906, 1906, 1906, 1906, 1906, 1,
-
1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908,
-
1884, 1, 1908, 1908, 1909, 1908, 1908, 1908,
-
1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908,
-
1908, 1908, 1908, 1908, 1908, 1908, 1908, 1884,
-
1908, 1, 1908, 1908, 1908, 1908, 1908, 1908,
-
1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908,
-
1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908,
-
1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908,
-
1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908,
-
1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908,
-
1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908,
-
1908, 1908, 1908, 1910, 1908, 1908, 1908, 1908,
-
1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908,
-
1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908,
-
1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908,
-
1908, 1908, 1908, 1908, 1908, 1908, 1908, 1,
-
1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908,
-
1908, 1, 1908, 1908, 1911, 1908, 1908, 1908,
-
1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908,
-
1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908,
-
1908, 1912, 1908, 1908, 1908, 1908, 1908, 1908,
-
1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908,
-
1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908,
-
1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908,
-
1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908,
-
1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908,
-
1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908,
-
1908, 1908, 1908, 1910, 1908, 1908, 1908, 1908,
-
1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908,
-
1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908,
-
1908, 1908, 1908, 1908, 1908, 1908, 1908, 1908,
-
1908, 1908, 1908, 1908, 1908, 1908, 1908, 1,
-
1913, 1, 1908, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1908, 1, 1914, 1, 1, 1, 1915,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1914, 1912, 1884, 1912, 1912, 1912,
-
1912, 1912, 1916, 1, 1912, 1912, 1, 1912,
-
1886, 1912, 1912, 1912, 1912, 1912, 1912, 1912,
-
1912, 1912, 1912, 1912, 1, 1, 1, 1912,
-
1893, 1912, 1, 1912, 1912, 1912, 1912, 1912,
-
1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912,
-
1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912,
-
1912, 1912, 1912, 1912, 1912, 1, 1, 1,
-
1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912,
-
1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912,
-
1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912,
-
1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912,
-
1912, 1, 1914, 1, 1, 1, 1915, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1914, 1, 1, 1, 1, 1, 1,
-
1, 1916, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1893,
-
1, 1917, 1, 1914, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1914, 1, 1918, 1, 1, 1,
-
1919, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1918, 1, 1, 1, 1,
-
1, 1, 1, 1920, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1900, 1, 1912, 1884, 1912, 1912, 1912,
-
1912, 1912, 1, 1, 1912, 1912, 1, 1912,
-
1886, 1912, 1912, 1912, 1912, 1912, 1912, 1912,
-
1912, 1912, 1912, 1912, 1, 1, 1, 1912,
-
1, 1912, 1, 1912, 1912, 1912, 1912, 1912,
-
1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912,
-
1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912,
-
1912, 1912, 1912, 1912, 1912, 1, 1, 1,
-
1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912,
-
1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912,
-
1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912,
-
1912, 1912, 1912, 1912, 1912, 1912, 1912, 1912,
-
1912, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1908, 1921, 1, 1884, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1884, 1, 1894, 1,
-
1, 1, 1901, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1894, 1883, 1884,
-
1883, 1883, 1883, 1883, 1883, 1903, 1, 1883,
-
1883, 1, 1883, 1886, 1883, 1883, 1883, 1883,
-
1883, 1883, 1883, 1883, 1883, 1883, 1883, 1,
-
1, 1, 1883, 1, 1883, 1, 1883, 1883,
-
1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883,
-
1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883,
-
1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883,
-
1, 1, 1, 1883, 1883, 1883, 1883, 1883,
-
1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883,
-
1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883,
-
1883, 1883, 1883, 1883, 1883, 1883, 1883, 1883,
-
1883, 1883, 1883, 1883, 1, 1922, 1, 1,
-
1, 1923, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1922, 1924, 1925, 1924,
-
1924, 1924, 1924, 1924, 1926, 1, 1924, 1924,
-
1, 1924, 1927, 1924, 1924, 1924, 1924, 1924,
-
1924, 1924, 1924, 1924, 1924, 1924, 1, 1,
-
1, 1924, 1, 1924, 1, 1924, 1924, 1924,
-
1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924,
-
1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924,
-
1924, 1924, 1924, 1924, 1924, 1924, 1924, 1928,
-
1, 1, 1924, 1924, 1924, 1924, 1924, 1924,
-
1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924,
-
1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924,
-
1924, 1924, 1924, 1924, 1924, 1924, 1924, 1924,
-
1924, 1924, 1924, 1, 1887, 1887, 1887, 1887,
-
1887, 1887, 1887, 1887, 1887, 1, 1887, 1887,
-
1929, 1887, 1887, 1887, 1887, 1887, 1887, 1887,
-
1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887,
-
1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887,
-
1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887,
-
1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887,
-
1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887,
-
1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887,
-
1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887,
-
1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887,
-
1887, 1887, 1887, 1887, 1887, 1887, 1, 1930,
-
1914, 1887, 1887, 1887, 1887, 1887, 1887, 1887,
-
1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887,
-
1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887,
-
1887, 1887, 1887, 1887, 1887, 1887, 1887, 1887,
-
1887, 1887, 1887, 1, 1931, 1, 1887, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1887, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1887,
-
1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932,
-
1932, 1, 1932, 1932, 1933, 1932, 1932, 1932,
-
1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932,
-
1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932,
-
1932, 1934, 1932, 1932, 1932, 1932, 1932, 1932,
-
1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932,
-
1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932,
-
1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932,
-
1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932,
-
1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932,
-
1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932,
-
1932, 1932, 1932, 1935, 1932, 1932, 1932, 1932,
-
1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932,
-
1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932,
-
1932, 1932, 1932, 1932, 1932, 1932, 1932, 1932,
-
1932, 1932, 1932, 1932, 1932, 1932, 1932, 1,
-
1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936,
-
1936, 1, 1936, 1936, 1937, 1936, 1936, 1936,
-
1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936,
-
1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936,
-
1936, 1938, 1936, 1936, 1936, 1936, 1936, 1936,
-
1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936,
-
1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936,
-
1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936,
-
1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936,
-
1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936,
-
1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936,
-
1936, 1936, 1936, 1939, 1936, 1936, 1936, 1936,
-
1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936,
-
1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936,
-
1936, 1936, 1936, 1936, 1936, 1936, 1936, 1936,
-
1936, 1936, 1936, 1936, 1936, 1936, 1936, 1,
-
1940, 1, 1936, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1936, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1936, 216, 1, 1, 1,
-
1941, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 216, 1942, 1943, 1942, 1942,
-
1942, 1942, 1942, 1944, 1, 1942, 1942, 1,
-
1942, 1, 1942, 1942, 1942, 1942, 1942, 1942,
-
1942, 1942, 1942, 1942, 1942, 1, 1, 1,
-
1942, 1, 1942, 1, 1942, 1942, 1942, 1942,
-
1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942,
-
1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942,
-
1942, 1942, 1942, 1942, 1942, 1942, 1, 1,
-
1, 1942, 1942, 1942, 1942, 1942, 1942, 1942,
-
1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942,
-
1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942,
-
1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942,
-
1942, 1942, 1, 1945, 1945, 1945, 1945, 1945,
-
1945, 1945, 1945, 201, 1, 1945, 1945, 1946,
-
1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945,
-
1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945,
-
1945, 1945, 201, 1945, 1, 1945, 1945, 1945,
-
1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945,
-
1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945,
-
1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945,
-
1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945,
-
1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945,
-
1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945,
-
1945, 1945, 1945, 1945, 1945, 1945, 1947, 1945,
-
1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945,
-
1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945,
-
1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945,
-
1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945,
-
1945, 1945, 1, 1945, 1945, 1945, 1945, 1945,
-
1945, 1945, 1945, 1945, 1, 1945, 1945, 1948,
-
1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945,
-
1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945,
-
1945, 1945, 1945, 1945, 1949, 1945, 1945, 1945,
-
1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945,
-
1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945,
-
1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945,
-
1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945,
-
1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945,
-
1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945,
-
1945, 1945, 1945, 1945, 1945, 1945, 1947, 1945,
-
1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945,
-
1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945,
-
1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945,
-
1945, 1945, 1945, 1945, 1945, 1945, 1945, 1945,
-
1945, 1945, 1, 1950, 1, 1945, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1945, 1, 1951, 1,
-
1, 1, 1952, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1951, 1949, 201,
-
1949, 1949, 1949, 1949, 1949, 1953, 1, 1949,
-
1949, 1, 1949, 1949, 1949, 1949, 1949, 1949,
-
1949, 1949, 1949, 1949, 1949, 1949, 1949, 1,
-
1, 1, 1949, 204, 1949, 205, 1949, 1949,
-
1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949,
-
1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949,
-
1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949,
-
1, 1, 1, 1949, 1949, 1949, 1949, 1949,
-
1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949,
-
1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949,
-
1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949,
-
1949, 1949, 1949, 1949, 1, 1954, 1, 1,
-
1, 1955, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1954, 1, 1, 1,
-
1, 1, 1, 1, 1956, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 210, 1, 211, 1, 1957, 1,
-
1954, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1954,
-
1, 1958, 1, 1, 1, 1959, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1958, 1, 1, 1, 1, 1, 1, 1,
-
1960, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 217, 1,
-
218, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1945, 1961, 1, 201, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 201, 1, 1962, 1,
-
1, 1, 1963, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1962, 200, 1964,
-
200, 200, 200, 200, 200, 1965, 1, 200,
-
200, 1, 200, 1949, 200, 200, 200, 200,
-
200, 200, 200, 200, 200, 200, 200, 1,
-
1, 1, 200, 204, 200, 205, 200, 200,
-
200, 200, 200, 200, 200, 200, 200, 200,
-
200, 200, 200, 200, 200, 200, 200, 200,
-
200, 200, 200, 200, 200, 200, 200, 200,
-
1, 1, 1, 200, 200, 200, 200, 200,
-
200, 200, 200, 200, 200, 200, 200, 200,
-
200, 200, 200, 200, 200, 200, 200, 200,
-
200, 200, 200, 200, 200, 200, 200, 200,
-
200, 200, 200, 200, 1, 1966, 1, 1,
-
1, 1967, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1966, 220, 221, 220,
-
220, 220, 220, 220, 1968, 1, 220, 220,
-
1, 220, 1, 220, 220, 220, 220, 220,
-
220, 220, 220, 220, 220, 220, 1, 1,
-
1, 220, 210, 220, 211, 220, 220, 220,
-
220, 220, 220, 220, 220, 220, 220, 220,
-
220, 220, 220, 220, 220, 220, 220, 220,
-
220, 220, 220, 220, 220, 220, 220, 1,
-
1, 1, 220, 220, 220, 220, 220, 220,
-
220, 220, 220, 220, 220, 220, 220, 220,
-
220, 220, 220, 220, 220, 220, 220, 220,
-
220, 220, 220, 220, 220, 220, 220, 220,
-
220, 220, 220, 1, 1969, 1, 1966, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1966, 1, 1970,
-
1, 1, 1, 1971, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1970, 1942,
-
1943, 1942, 1942, 1942, 1942, 1942, 1972, 1,
-
1942, 1942, 1, 1942, 1, 1942, 1942, 1942,
-
1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942,
-
1, 1, 1, 1942, 217, 1942, 218, 1942,
-
1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942,
-
1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942,
-
1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942,
-
1942, 1, 1, 1, 1942, 1942, 1942, 1942,
-
1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942,
-
1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942,
-
1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942,
-
1942, 1942, 1942, 1942, 1942, 1, 1973, 1973,
-
1973, 1973, 1973, 1973, 1973, 1973, 1974, 1,
-
1973, 1973, 1975, 1973, 1973, 1973, 1973, 1973,
-
1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973,
-
1973, 1973, 1973, 1973, 1973, 1974, 1973, 1934,
-
1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973,
-
1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973,
-
1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973,
-
1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973,
-
1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973,
-
1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973,
-
1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973,
-
1973, 1976, 1973, 1973, 1973, 1973, 1973, 1973,
-
1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973,
-
1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973,
-
1973, 1973, 1973, 1973, 1973, 1973, 1973, 1973,
-
1973, 1973, 1973, 1973, 1973, 1, 1977, 1977,
-
1977, 1977, 1977, 1977, 1977, 1977, 1977, 1,
-
1977, 1977, 1978, 1977, 1977, 1977, 1977, 1977,
-
1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977,
-
1977, 1977, 1977, 1977, 1977, 1977, 1977, 1979,
-
1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977,
-
1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977,
-
1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977,
-
1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977,
-
1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977,
-
1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977,
-
1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977,
-
1977, 1980, 1977, 1977, 1977, 1977, 1977, 1977,
-
1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977,
-
1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977,
-
1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977,
-
1977, 1977, 1977, 1977, 1977, 1, 1981, 1,
-
1977, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1977,
-
1, 198, 1, 1, 1, 199, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
198, 1949, 201, 1949, 1949, 1949, 1949, 1949,
-
202, 1, 1949, 1949, 1, 1949, 203, 1949,
-
1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949,
-
1949, 1949, 1, 1, 1, 1949, 204, 1949,
-
205, 1949, 1949, 1949, 1949, 1949, 1949, 1949,
-
1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949,
-
1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949,
-
1949, 1949, 1949, 1, 1, 1, 1949, 1949,
-
1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949,
-
1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949,
-
1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949,
-
1949, 1949, 1949, 1949, 1949, 1949, 1949, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977,
-
1977, 1982, 1, 1977, 1977, 1983, 1977, 1977,
-
1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977,
-
1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977,
-
1982, 1977, 1938, 1977, 1977, 1977, 1977, 1977,
-
1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977,
-
1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977,
-
1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977,
-
1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977,
-
1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977,
-
1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977,
-
1977, 1977, 1977, 1977, 1980, 1977, 1977, 1977,
-
1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977,
-
1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977,
-
1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977,
-
1977, 1977, 1977, 1977, 1977, 1977, 1977, 1977,
-
1, 1984, 1, 1982, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1982, 1, 1985, 1985, 1985, 1985,
-
1985, 1985, 1985, 1985, 1986, 1, 1985, 1985,
-
1987, 1985, 1985, 1985, 1985, 1985, 1985, 1985,
-
1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985,
-
1985, 1985, 1985, 1986, 1985, 1988, 1985, 1985,
-
1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985,
-
1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985,
-
1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985,
-
1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985,
-
1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985,
-
1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985,
-
1985, 1985, 1985, 1985, 1985, 1985, 1985, 1989,
-
1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985,
-
1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985,
-
1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985,
-
1985, 1985, 1985, 1985, 1985, 1985, 1985, 1985,
-
1985, 1985, 1985, 1, 1990, 1990, 1990, 1990,
-
1990, 1990, 1990, 1990, 1990, 1, 1990, 1990,
-
1991, 1990, 1990, 1990, 1990, 1990, 1990, 1990,
-
1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990,
-
1990, 1990, 1990, 1990, 1990, 1992, 1990, 1990,
-
1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990,
-
1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990,
-
1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990,
-
1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990,
-
1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990,
-
1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990,
-
1990, 1990, 1990, 1990, 1990, 1990, 1990, 1993,
-
1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990,
-
1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990,
-
1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990,
-
1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990,
-
1990, 1990, 1990, 1, 1994, 1, 1990, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1990, 1, 1995,
-
1, 1, 1, 1996, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1995, 1949,
-
201, 1949, 1949, 1949, 1949, 1949, 1997, 1,
-
1949, 1949, 1, 1949, 203, 1949, 1949, 1949,
-
1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949,
-
1, 1, 1, 1949, 204, 1949, 1998, 1949,
-
1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949,
-
1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949,
-
1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949,
-
1949, 1, 1, 1, 1949, 1949, 1949, 1949,
-
1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949,
-
1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949,
-
1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949,
-
1949, 1949, 1949, 1949, 1949, 1, 1999, 1,
-
1, 1, 2000, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1999, 1, 1,
-
1, 1, 1, 1, 1, 2001, 1, 1,
-
1, 1, 1, 209, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 210, 1, 2002, 1, 2003,
-
1, 1999, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1999, 1, 2004, 1, 1, 1, 2005, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 2004, 1, 1, 1, 1, 1, 1,
-
1, 2006, 1, 1, 1, 1, 1, 216,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 217,
-
1, 2007, 1, 1990, 1990, 1990, 1990, 1990,
-
1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990,
-
1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990,
-
1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990,
-
1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990,
-
1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990,
-
1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990,
-
1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990,
-
1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990,
-
1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990,
-
1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990,
-
1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990,
-
1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990,
-
1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990,
-
1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990,
-
1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990,
-
1990, 1990, 1990, 1, 1990, 1990, 1990, 1990,
-
1990, 1990, 1990, 1990, 2008, 1, 1990, 1990,
-
2009, 1990, 1990, 1990, 1990, 1990, 1990, 1990,
-
1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990,
-
1990, 1990, 1990, 2008, 1990, 2010, 1990, 1990,
-
1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990,
-
1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990,
-
1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990,
-
1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990,
-
1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990,
-
1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990,
-
1990, 1990, 1990, 1990, 1990, 1990, 1990, 1993,
-
1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990,
-
1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990,
-
1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990,
-
1990, 1990, 1990, 1990, 1990, 1990, 1990, 1990,
-
1990, 1990, 1990, 1, 2011, 1, 2008, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 2008, 1, 2012,
-
1, 1, 1, 2013, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 2012, 1,
-
1, 1, 1, 1, 1, 1, 2014, 1,
-
1, 1, 1, 1, 209, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 210, 1, 2015, 1,
-
2016, 1, 2012, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 2012, 1, 2017, 1, 1, 1, 2018,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 2017, 1, 1, 1, 1, 1,
-
1, 1, 2019, 1, 1, 1, 1, 1,
-
216, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
217, 1, 2020, 1, 2021, 1, 1, 1,
-
2022, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 2021, 2023, 2024, 2023, 2023,
-
2023, 2023, 2023, 2025, 1, 2023, 2023, 2026,
-
2023, 2027, 2023, 2023, 2023, 2023, 2023, 2023,
-
2023, 2023, 2023, 2023, 2023, 1, 1, 1,
-
2023, 1, 2023, 2028, 2023, 2023, 2023, 2023,
-
2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023,
-
2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023,
-
2023, 2023, 2023, 2023, 2023, 2023, 1, 1,
-
1, 2023, 2023, 2023, 2023, 2023, 2023, 2023,
-
2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023,
-
2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023,
-
2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023,
-
2023, 2023, 1, 195, 1, 1, 1, 2029,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 195, 1, 1, 1, 1, 1,
-
1, 1, 2030, 1, 1, 1, 195, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 196, 1, 2031, 1, 195, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 195, 1, 2026,
-
1, 1, 1, 2032, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 2026, 1,
-
1, 1, 1, 1, 1, 1, 2033, 1,
-
1, 1, 2026, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 2028, 1,
-
2034, 1, 1, 1, 2035, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 2034,
-
2036, 2037, 2036, 2036, 2036, 2036, 2036, 2038,
-
1, 2036, 2036, 1, 2036, 2039, 2036, 2036,
-
2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036,
-
2036, 1, 1, 1, 2036, 1, 2036, 1,
-
2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036,
-
2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036,
-
2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036,
-
2036, 2036, 2040, 1, 1, 2036, 2036, 2036,
-
2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036,
-
2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036,
-
2036, 2036, 2036, 2036, 2036, 2036, 2036, 2036,
-
2036, 2036, 2036, 2036, 2036, 2036, 1, 2041,
-
1, 1, 1, 2042, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 2041, 2043,
-
2044, 2043, 2043, 2043, 2043, 2043, 2045, 1,
-
2043, 2043, 1, 2043, 2046, 2043, 2043, 2043,
-
2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043,
-
1, 1, 1, 2043, 1, 2043, 1, 2043,
-
2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043,
-
2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043,
-
2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043,
-
2043, 2047, 1, 1, 2043, 2043, 2043, 2043,
-
2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043,
-
2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043,
-
2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043,
-
2043, 2043, 2043, 2043, 2043, 1, 2048, 1,
-
2041, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 2041,
-
1, 2049, 1, 1, 1, 2050, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
2049, 2043, 2044, 2043, 2043, 2043, 2043, 2043,
-
2051, 1, 2043, 2043, 2052, 2043, 2053, 2043,
-
2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043,
-
2043, 2043, 2054, 1, 1, 2043, 1, 2043,
-
1, 2043, 2043, 2043, 2043, 2043, 2043, 2043,
-
2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043,
-
2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043,
-
2043, 2043, 2043, 1, 1, 1, 2043, 2043,
-
2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043,
-
2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043,
-
2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043,
-
2043, 2043, 2043, 2043, 2043, 2043, 2043, 1,
-
2049, 1, 1, 1, 2050, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 2049,
-
1, 1, 1, 1, 1, 1, 1, 2051,
-
1, 1, 1, 2052, 1, 2055, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 2054, 1, 2056, 1, 2049, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 2049, 1, 2057, 1,
-
1, 1, 2058, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 2057, 1, 1,
-
1, 1, 1, 1, 1, 2059, 1, 1,
-
1, 2060, 1, 2061, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 2062,
-
1, 2063, 1, 1, 1, 2064, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
2063, 1, 1, 1, 1, 1, 1, 1,
-
2065, 1, 1, 1, 2063, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 2066, 1, 1, 1, 1, 1,
-
196, 1, 2067, 1, 2063, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 2063, 1, 2068, 1, 1,
-
1, 2069, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 2068, 1, 1, 1,
-
1, 1, 1, 1, 2070, 1, 1, 1,
-
2068, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 2071, 1,
-
1, 1, 1, 1, 2028, 1, 2072, 1,
-
1, 1, 2073, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 2072, 2074, 2075,
-
2074, 2074, 2074, 2074, 2074, 2076, 1, 2074,
-
2074, 1, 2074, 2077, 2074, 2074, 2074, 2074,
-
2074, 2074, 2074, 2074, 2074, 2074, 2074, 1,
-
1, 1, 2074, 1, 2074, 1, 2074, 2074,
-
2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074,
-
2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074,
-
2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074,
-
1, 1, 1, 2074, 2074, 2074, 2074, 2074,
-
2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074,
-
2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074,
-
2074, 2074, 2074, 2074, 2074, 2074, 2074, 2074,
-
2074, 2074, 2074, 2074, 1, 2078, 1, 1,
-
1, 2079, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 2078, 186, 187, 186,
-
186, 186, 186, 186, 2080, 1, 186, 186,
-
1, 186, 190, 186, 186, 186, 186, 186,
-
186, 186, 186, 186, 186, 186, 1, 1,
-
1, 186, 1, 186, 1, 186, 186, 186,
-
186, 186, 186, 186, 186, 186, 186, 186,
-
186, 186, 186, 186, 186, 186, 186, 186,
-
186, 186, 186, 186, 186, 186, 186, 1,
-
1, 1, 186, 186, 186, 186, 186, 186,
-
186, 186, 186, 186, 186, 186, 186, 186,
-
186, 186, 186, 186, 186, 186, 186, 186,
-
186, 186, 186, 186, 186, 186, 186, 186,
-
186, 186, 186, 1, 2081, 1, 2078, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 2078, 1, 2082,
-
1, 1, 1, 2083, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 2082, 2023,
-
2024, 2023, 2023, 2023, 2023, 2023, 2084, 1,
-
2023, 2023, 1, 2023, 2027, 2023, 2023, 2023,
-
2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023,
-
1, 1, 1, 2023, 1, 2023, 1, 2023,
-
2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023,
-
2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023,
-
2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023,
-
2023, 1, 1, 1, 2023, 2023, 2023, 2023,
-
2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023,
-
2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023,
-
2023, 2023, 2023, 2023, 2023, 2023, 2023, 2023,
-
2023, 2023, 2023, 2023, 2023, 1, 1949, 201,
-
1949, 1949, 1949, 1949, 1949, 1, 1, 1949,
-
1949, 1, 1949, 2085, 1949, 1949, 1949, 1949,
-
1949, 1949, 1949, 1949, 1949, 1949, 1949, 1,
-
1, 1, 1949, 1, 1949, 1, 1949, 1949,
-
1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949,
-
1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949,
-
1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949,
-
1, 1, 1, 1949, 1949, 1949, 1949, 1949,
-
1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949,
-
1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949,
-
1949, 1949, 1949, 1949, 1949, 1949, 1949, 1949,
-
1949, 1949, 1949, 1949, 1, 2055, 1, 1,
-
1, 2086, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 2055, 2087, 1, 2087,
-
2087, 2087, 2087, 2087, 2088, 1, 2087, 2087,
-
1, 2087, 1, 2087, 2087, 2087, 2087, 2087,
-
2087, 2087, 2087, 2087, 2087, 2087, 1, 1,
-
1, 2087, 1, 2087, 1, 2087, 2087, 2087,
-
2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087,
-
2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087,
-
2087, 2087, 2087, 2087, 2087, 2087, 2087, 1,
-
1, 1, 2087, 2087, 2087, 2087, 2087, 2087,
-
2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087,
-
2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087,
-
2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087,
-
2087, 2087, 2087, 1, 2089, 1, 2055, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 2055, 1, 2049,
-
1, 1, 1, 2050, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 2049, 2087,
-
1, 2087, 2087, 2087, 2087, 2087, 2051, 1,
-
2087, 2087, 2052, 2087, 2055, 2087, 2087, 2087,
-
2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087,
-
2054, 1, 1, 2087, 1, 2087, 1, 2087,
-
2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087,
-
2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087,
-
2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087,
-
2087, 1, 1, 1, 2087, 2087, 2087, 2087,
-
2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087,
-
2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087,
-
2087, 2087, 2087, 2087, 2087, 2087, 2087, 2087,
-
2087, 2087, 2087, 2087, 2087, 1, 2061, 1,
-
1, 1, 2090, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 2061, 2091, 1,
-
2091, 2091, 2091, 2091, 2091, 2092, 1, 2091,
-
2091, 1, 2091, 1, 2091, 2091, 2091, 2091,
-
2091, 2091, 2091, 2091, 2091, 2091, 2091, 1,
-
1, 1, 2091, 1, 2091, 1, 2091, 2091,
-
2091, 2091, 2091, 2091, 2091, 2091, 2091, 2091,
-
2091, 2091, 2091, 2091, 2091, 2091, 2091, 2091,
-
2091, 2091, 2091, 2091, 2091, 2091, 2091, 2091,
-
1, 1, 1, 2091, 2091, 2091, 2091, 2091,
-
2091, 2091, 2091, 2091, 2091, 2091, 2091, 2091,
-
2091, 2091, 2091, 2091, 2091, 2091, 2091, 2091,
-
2091, 2091, 2091, 2091, 2091, 2091, 2091, 2091,
-
2091, 2091, 2091, 2091, 1, 2093, 2093, 2093,
-
2093, 2093, 2093, 2093, 2093, 2044, 1, 2093,
-
2093, 2094, 2093, 2093, 2093, 2093, 2093, 2093,
-
2093, 2093, 2093, 2093, 2093, 2093, 2093, 2093,
-
2093, 2093, 2093, 2093, 2044, 2093, 1, 2093,
-
2093, 2093, 2093, 2093, 2093, 2093, 2093, 2093,
-
2093, 2093, 2093, 2093, 2093, 2093, 2093, 2093,
-
2093, 2093, 2093, 2093, 2093, 2093, 2093, 2093,
-
2093, 2093, 2093, 2093, 2093, 2093, 2093, 2093,
-
2093, 2093, 2093, 2093, 2093, 2093, 2093, 2093,
-
2093, 2093, 2093, 2093, 2093, 2093, 2093, 2093,
-
2093, 2093, 2093, 2093, 2093, 2093, 2093, 2093,
-
2095, 2093, 2093, 2093, 2093, 2093, 2093, 2093,
-
2093, 2093, 2093, 2093, 2093, 2093, 2093, 2093,
-
2093, 2093, 2093, 2093, 2093, 2093, 2093, 2093,
-
2093, 2093, 2093, 2093, 2093, 2093, 2093, 2093,
-
2093, 2093, 2093, 2093, 1, 2093, 2093, 2093,
-
2093, 2093, 2093, 2093, 2093, 2093, 1, 2093,
-
2093, 2096, 2093, 2093, 2093, 2093, 2093, 2093,
-
2093, 2093, 2093, 2093, 2093, 2093, 2093, 2093,
-
2093, 2093, 2093, 2093, 2093, 2093, 2097, 2093,
-
2093, 2093, 2093, 2093, 2093, 2093, 2093, 2093,
-
2093, 2093, 2093, 2093, 2093, 2093, 2093, 2093,
-
2093, 2093, 2093, 2093, 2093, 2093, 2093, 2093,
-
2093, 2093, 2093, 2093, 2093, 2093, 2093, 2093,
-
2093, 2093, 2093, 2093, 2093, 2093, 2093, 2093,
-
2093, 2093, 2093, 2093, 2093, 2093, 2093, 2093,
-
2093, 2093, 2093, 2093, 2093, 2093, 2093, 2093,
-
2095, 2093, 2093, 2093, 2093, 2093, 2093, 2093,
-
2093, 2093, 2093, 2093, 2093, 2093, 2093, 2093,
-
2093, 2093, 2093, 2093, 2093, 2093, 2093, 2093,
-
2093, 2093, 2093, 2093, 2093, 2093, 2093, 2093,
-
2093, 2093, 2093, 2093, 1, 2098, 1, 2093,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 2093, 1,
-
2099, 1, 1, 1, 2100, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 2099,
-
2097, 2044, 2097, 2097, 2097, 2097, 2097, 2101,
-
1, 2097, 2097, 2052, 2097, 2046, 2097, 2097,
-
2097, 2097, 2097, 2097, 2097, 2097, 2097, 2097,
-
2097, 2054, 1, 1, 2097, 1, 2097, 1,
-
2097, 2097, 2097, 2097, 2097, 2097, 2097, 2097,
-
2097, 2097, 2097, 2097, 2097, 2097, 2097, 2097,
-
2097, 2097, 2097, 2097, 2097, 2097, 2097, 2097,
-
2097, 2097, 1, 1, 1, 2097, 2097, 2097,
-
2097, 2097, 2097, 2097, 2097, 2097, 2097, 2097,
-
2097, 2097, 2097, 2097, 2097, 2097, 2097, 2097,
-
2097, 2097, 2097, 2097, 2097, 2097, 2097, 2097,
-
2097, 2097, 2097, 2097, 2097, 2097, 1, 2099,
-
1, 1, 1, 2100, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 2099, 1,
-
1, 1, 1, 1, 1, 1, 2101, 1,
-
1, 1, 2052, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
2054, 1, 2102, 1, 2099, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 2099, 1, 2103, 1, 1,
-
1, 2104, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 2103, 1, 1, 1,
-
1, 1, 1, 1, 2105, 1, 1, 1,
-
2060, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 2062, 1,
-
2097, 2044, 2097, 2097, 2097, 2097, 2097, 1,
-
1, 2097, 2097, 1, 2097, 2046, 2097, 2097,
-
2097, 2097, 2097, 2097, 2097, 2097, 2097, 2097,
-
2097, 1, 1, 1, 2097, 1, 2097, 1,
-
2097, 2097, 2097, 2097, 2097, 2097, 2097, 2097,
-
2097, 2097, 2097, 2097, 2097, 2097, 2097, 2097,
-
2097, 2097, 2097, 2097, 2097, 2097, 2097, 2097,
-
2097, 2097, 1, 1, 1, 2097, 2097, 2097,
-
2097, 2097, 2097, 2097, 2097, 2097, 2097, 2097,
-
2097, 2097, 2097, 2097, 2097, 2097, 2097, 2097,
-
2097, 2097, 2097, 2097, 2097, 2097, 2097, 2097,
-
2097, 2097, 2097, 2097, 2097, 2097, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 2093,
-
2106, 1, 2044, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 2044, 1, 2055, 1, 1, 1, 2086,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 2055, 2043, 2044, 2043, 2043, 2043,
-
2043, 2043, 2088, 1, 2043, 2043, 1, 2043,
-
2046, 2043, 2043, 2043, 2043, 2043, 2043, 2043,
-
2043, 2043, 2043, 2043, 1, 1, 1, 2043,
-
1, 2043, 1, 2043, 2043, 2043, 2043, 2043,
-
2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043,
-
2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043,
-
2043, 2043, 2043, 2043, 2043, 1, 1, 1,
-
2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043,
-
2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043,
-
2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043,
-
2043, 2043, 2043, 2043, 2043, 2043, 2043, 2043,
-
2043, 1, 2107, 1, 1, 1, 2108, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 2107, 2109, 2110, 2109, 2109, 2109, 2109,
-
2109, 2111, 1, 2109, 2109, 1, 2109, 2112,
-
2109, 2109, 2109, 2109, 2109, 2109, 2109, 2109,
-
2109, 2109, 2109, 1, 1, 1, 2109, 1,
-
2109, 1, 2109, 2109, 2109, 2109, 2109, 2109,
-
2109, 2109, 2109, 2109, 2109, 2109, 2109, 2109,
-
2109, 2109, 2109, 2109, 2109, 2109, 2109, 2109,
-
2109, 2109, 2109, 2109, 2113, 1, 1, 2109,
-
2109, 2109, 2109, 2109, 2109, 2109, 2109, 2109,
-
2109, 2109, 2109, 2109, 2109, 2109, 2109, 2109,
-
2109, 2109, 2109, 2109, 2109, 2109, 2109, 2109,
-
2109, 2109, 2109, 2109, 2109, 2109, 2109, 2109,
-
1, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-
2047, 2047, 1, 2047, 2047, 2114, 2047, 2047,
-
2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-
2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-
2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-
2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-
2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-
2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-
2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-
2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-
2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-
2047, 2047, 2047, 1, 2115, 2099, 2047, 2047,
-
2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-
2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-
2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-
2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-
1, 2116, 1, 2047, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 2047, 1, 2047, 2047, 2047, 2047,
-
2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-
2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-
2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-
2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-
2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-
2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-
2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-
2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-
2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-
2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-
2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-
2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-
2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-
2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-
2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-
2047, 2047, 2047, 2047, 1, 2117, 1, 1,
-
1, 2118, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 2117, 1179, 1180, 1179,
-
1179, 1179, 1179, 1179, 2119, 1, 1179, 1179,
-
2120, 1179, 1183, 1179, 1179, 1179, 1179, 1179,
-
1179, 1179, 1179, 1179, 1179, 1179, 1, 1184,
-
183, 1179, 1, 1179, 1, 1179, 1179, 1179,
-
1179, 1179, 1179, 1179, 1179, 1179, 1179, 1179,
-
1179, 1179, 1179, 1179, 1179, 1179, 1179, 1179,
-
1179, 1179, 1179, 1179, 1179, 1179, 1179, 1,
-
1, 1, 1179, 1179, 1179, 1179, 1179, 1179,
-
1179, 1179, 1179, 1179, 1179, 1179, 1179, 1179,
-
1179, 1179, 1179, 1179, 1179, 1179, 1179, 1179,
-
1179, 1179, 1179, 1179, 1179, 1179, 1179, 1179,
-
1179, 1179, 1179, 1, 2121, 1, 1, 1,
-
2122, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 2121, 1187, 1188, 1187, 1187,
-
1187, 1187, 1187, 2123, 1, 1187, 1187, 118,
-
1187, 1190, 1187, 1187, 1187, 1187, 1187, 1187,
-
1187, 1187, 1187, 1187, 1187, 1, 120, 1191,
-
1187, 1, 1187, 1192, 1187, 1187, 1187, 1187,
-
1187, 1187, 1187, 1187, 1187, 1187, 1187, 1187,
-
1187, 1187, 1187, 1187, 1187, 1187, 1187, 1187,
-
1187, 1187, 1187, 1187, 1187, 1187, 1, 1,
-
1, 1187, 1187, 1187, 1187, 1187, 1187, 1187,
-
1187, 1187, 1187, 1187, 1187, 1187, 1187, 1187,
-
1187, 1187, 1187, 1187, 1187, 1187, 1187, 1187,
-
1187, 1187, 1187, 1187, 1187, 1187, 1187, 1187,
-
1187, 1187, 1, 2124, 1, 1, 1, 2125,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 2124, 1195, 1196, 1195, 1195, 1195,
-
1195, 1195, 2126, 1, 1195, 1195, 118, 1195,
-
1198, 1195, 1195, 1195, 1195, 1195, 1195, 1195,
-
1195, 1195, 1195, 1195, 1, 120, 1199, 1195,
-
1, 1195, 1, 1195, 1195, 1195, 1195, 1195,
-
1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195,
-
1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195,
-
1195, 1195, 1195, 1195, 1195, 1, 1, 1,
-
1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195,
-
1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195,
-
1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195,
-
1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195,
-
1195, 1, 2127, 1, 2124, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 2124, 1, 2128, 1, 1,
-
1, 2129, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 2128, 1828, 1829, 1828,
-
1828, 1828, 1828, 1828, 2130, 1, 1828, 1828,
-
2120, 1828, 1831, 1828, 1828, 1828, 1828, 1828,
-
1828, 1828, 1828, 1828, 1828, 1828, 1, 1184,
-
1253, 1828, 1, 1828, 1, 1828, 1828, 1828,
-
1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828,
-
1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828,
-
1828, 1828, 1828, 1828, 1828, 1828, 1828, 1,
-
1, 1, 1828, 1828, 1828, 1828, 1828, 1828,
-
1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828,
-
1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828,
-
1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828,
-
1828, 1828, 1828, 1, 2131, 1, 1, 1,
-
2132, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 2131, 2133, 2134, 2133, 2133,
-
2133, 2133, 2133, 2135, 1, 2133, 2133, 1,
-
2133, 2136, 2133, 2133, 2133, 2133, 2133, 2133,
-
2133, 2133, 2133, 2133, 2133, 18, 1, 19,
-
2133, 1, 2133, 17, 2133, 2133, 2133, 2133,
-
2133, 2133, 2133, 2133, 2133, 2133, 2133, 2133,
-
2133, 2133, 2133, 2133, 2133, 2133, 2133, 2133,
-
2133, 2133, 2133, 2133, 2133, 2133, 2137, 1,
-
1, 2133, 2133, 2133, 2133, 2133, 2133, 2133,
-
2133, 2133, 2133, 2133, 2133, 2133, 2133, 2133,
-
2133, 2133, 2133, 2133, 2133, 2133, 2133, 2133,
-
2133, 2133, 2133, 2133, 2133, 2133, 2133, 2133,
-
2133, 2133, 1, 2138, 1, 1, 1, 2139,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 2138, 2140, 2141, 2140, 2140, 2140,
-
2140, 2140, 2142, 1, 2140, 2140, 1, 2140,
-
2143, 2140, 2140, 2140, 2140, 2140, 2140, 2140,
-
2140, 2140, 2140, 2140, 1, 1, 8, 2140,
-
1, 2140, 1, 2140, 2140, 2140, 2140, 2140,
-
2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140,
-
2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140,
-
2140, 2140, 2140, 2140, 2140, 2144, 1, 1,
-
2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140,
-
2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140,
-
2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140,
-
2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140,
-
2140, 1, 2145, 1, 2138, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 2138, 1, 2146, 1, 2147,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 2147, 1,
-
2148, 1, 1, 1, 2149, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 2148,
-
2150, 15, 2150, 2150, 2150, 2150, 2150, 2151,
-
1, 2150, 2150, 1, 2150, 17, 2150, 2150,
-
2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150,
-
2150, 18, 1, 19, 2150, 1, 2150, 17,
-
2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150,
-
2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150,
-
2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150,
-
2150, 2150, 1, 1, 1, 2150, 2150, 2150,
-
2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150,
-
2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150,
-
2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150,
-
2150, 2150, 2150, 2150, 2150, 2150, 1, 2152,
-
1, 1, 1, 2153, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 2152, 2150,
-
15, 2150, 2150, 2150, 2150, 2150, 2154, 1,
-
2150, 2150, 1, 2150, 1, 2150, 2150, 2150,
-
2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150,
-
1, 1, 8, 2150, 1, 2150, 1, 2150,
-
2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150,
-
2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150,
-
2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150,
-
2150, 1, 1, 1, 2150, 2150, 2150, 2150,
-
2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150,
-
2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150,
-
2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150,
-
2150, 2150, 2150, 2150, 2150, 1, 2155, 1,
-
2152, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 2152,
-
1, 2156, 1, 1, 1, 2157, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
2156, 2158, 33, 2158, 2158, 2158, 2158, 2158,
-
2159, 1, 2158, 2158, 1, 2158, 1, 2158,
-
2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158,
-
2158, 2158, 1, 1, 48, 2158, 1, 2158,
-
1, 2158, 2158, 2158, 2158, 2158, 2158, 2158,
-
2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158,
-
2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158,
-
2158, 2158, 2158, 1, 1, 1, 2158, 2158,
-
2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158,
-
2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158,
-
2158, 2158, 2158, 2158, 2158, 2158, 2158, 2158,
-
2158, 2158, 2158, 2158, 2158, 2158, 2158, 1,
-
2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160,
-
2161, 1, 2160, 2160, 2162, 2160, 2160, 2160,
-
2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160,
-
2160, 2160, 2160, 2160, 2160, 2160, 2160, 2161,
-
2160, 23, 2160, 2160, 2160, 2160, 2160, 2160,
-
2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160,
-
2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160,
-
2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160,
-
2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160,
-
2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160,
-
2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160,
-
2160, 2160, 2160, 2163, 2160, 2160, 2160, 2160,
-
2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160,
-
2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160,
-
2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160,
-
2160, 2160, 2160, 2160, 2160, 2160, 2160, 1,
-
2164, 2164, 2164, 2164, 2164, 2164, 2164, 2164,
-
2164, 1, 2164, 2164, 2165, 2164, 2164, 2164,
-
2164, 2164, 2164, 2164, 2164, 2164, 2164, 2164,
-
2164, 2164, 2164, 2164, 2164, 2164, 2164, 2164,
-
2164, 2166, 2164, 2164, 2164, 2164, 2164, 2164,
-
2164, 2164, 2164, 2164, 2164, 2164, 2164, 2164,
-
2164, 2164, 2164, 2164, 2164, 2164, 2164, 2164,
-
2164, 2164, 2164, 2164, 2164, 2164, 2164, 2164,
-
2164, 2164, 2164, 2164, 2164, 2164, 2164, 2164,
-
2164, 2164, 2164, 2164, 2164, 2164, 2164, 2164,
-
2164, 2164, 2164, 2164, 2164, 2164, 2164, 2164,
-
2164, 2164, 2164, 2167, 2164, 2164, 2164, 2164,
-
2164, 2164, 2164, 2164, 2164, 2164, 2164, 2164,
-
2164, 2164, 2164, 2164, 2164, 2164, 2164, 2164,
-
2164, 2164, 2164, 2164, 2164, 2164, 2164, 2164,
-
2164, 2164, 2164, 2164, 2164, 2164, 2164, 1,
-
2168, 1, 2164, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 2164, 1, 2169, 1, 2170, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 2170, 1, 38, 1,
-
1, 1, 39, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 38, 2171, 2141,
-
2171, 2171, 2171, 2171, 2171, 40, 1, 2171,
-
2171, 1, 2171, 2172, 2171, 2171, 2171, 2171,
-
2171, 2171, 2171, 2171, 2171, 2171, 2171, 18,
-
1, 19, 2171, 1, 2171, 17, 2171, 2171,
-
2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171,
-
2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171,
-
2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171,
-
1, 1, 1, 2171, 2171, 2171, 2171, 2171,
-
2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171,
-
2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171,
-
2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171,
-
2171, 2171, 2171, 2171, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 2164, 2164, 2164,
-
2164, 2164, 2164, 2164, 2164, 2164, 2173, 1,
-
2164, 2164, 2174, 2164, 2164, 2164, 2164, 2164,
-
2164, 2164, 2164, 2164, 2164, 2164, 2164, 2164,
-
2164, 2164, 2164, 2164, 2164, 2173, 2164, 27,
-
2164, 2164, 2164, 2164, 2164, 2164, 2164, 2164,
-
2164, 2164, 2164, 2164, 2164, 2164, 2164, 2164,
-
2164, 2164, 2164, 2164, 2164, 2164, 2164, 2164,
-
2164, 2164, 2164, 2164, 2164, 2164, 2164, 2164,
-
2164, 2164, 2164, 2164, 2164, 2164, 2164, 2164,
-
2164, 2164, 2164, 2164, 2164, 2164, 2164, 2164,
-
2164, 2164, 2164, 2164, 2164, 2164, 2164, 2164,
-
2164, 2167, 2164, 2164, 2164, 2164, 2164, 2164,
-
2164, 2164, 2164, 2164, 2164, 2164, 2164, 2164,
-
2164, 2164, 2164, 2164, 2164, 2164, 2164, 2164,
-
2164, 2164, 2164, 2164, 2164, 2164, 2164, 2164,
-
2164, 2164, 2164, 2164, 2164, 1, 2175, 1,
-
2173, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 2173,
-
1, 2148, 1, 1, 1, 2149, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
2148, 2140, 2141, 2140, 2140, 2140, 2140, 2140,
-
2151, 1, 2140, 2140, 1, 2140, 2172, 2140,
-
2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140,
-
2140, 2140, 18, 1, 19, 2140, 1, 2140,
-
17, 2140, 2140, 2140, 2140, 2140, 2140, 2140,
-
2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140,
-
2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140,
-
2140, 2140, 2140, 1, 1, 1, 2140, 2140,
-
2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140,
-
2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140,
-
2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140,
-
2140, 2140, 2140, 2140, 2140, 2140, 2140, 1,
-
2176, 1, 1, 1, 2177, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 2176,
-
2178, 2179, 2178, 2178, 2178, 2178, 2178, 2180,
-
1, 2178, 2178, 1, 2178, 2181, 2178, 2178,
-
2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178,
-
2178, 1, 1, 48, 2178, 1, 2178, 1,
-
2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178,
-
2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178,
-
2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178,
-
2178, 2178, 2182, 1, 1, 2178, 2178, 2178,
-
2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178,
-
2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178,
-
2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178,
-
2178, 2178, 2178, 2178, 2178, 2178, 1, 2183,
-
2184, 2183, 2183, 2183, 2183, 2183, 1, 1,
-
2183, 2183, 1, 2183, 2143, 2183, 2183, 2183,
-
2183, 2183, 2183, 2183, 2183, 2183, 2183, 2183,
-
1, 1, 1, 2183, 1, 2183, 1, 2183,
-
2183, 2183, 2183, 2183, 2183, 2183, 2183, 2183,
-
2183, 2183, 2183, 2183, 2183, 2183, 2183, 2183,
-
2183, 2183, 2183, 2183, 2183, 2183, 2183, 2183,
-
2183, 1, 1, 1, 2183, 2183, 2183, 2183,
-
2183, 2183, 2183, 2183, 2183, 2183, 2183, 2183,
-
2183, 2183, 2183, 2183, 2183, 2183, 2183, 2183,
-
2183, 2183, 2183, 2183, 2183, 2183, 2183, 2183,
-
2183, 2183, 2183, 2183, 2183, 1, 2185, 1,
-
2186, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 2186,
-
1, 2187, 2187, 2187, 2187, 2187, 2187, 2187,
-
2187, 2184, 1, 2187, 2187, 2188, 2187, 2187,
-
2187, 2187, 2187, 2187, 2187, 2187, 2187, 2187,
-
2187, 2187, 2187, 2187, 2187, 2187, 2187, 2187,
-
2184, 2187, 1, 2187, 2187, 2187, 2187, 2187,
-
2187, 2187, 2187, 2187, 2187, 2187, 2187, 2187,
-
2187, 2187, 2187, 2187, 2187, 2187, 2187, 2187,
-
2187, 2187, 2187, 2187, 2187, 2187, 2187, 2187,
-
2187, 2187, 2187, 2187, 2187, 2187, 2187, 2187,
-
2187, 2187, 2187, 2187, 2187, 2187, 2187, 2187,
-
2187, 2187, 2187, 2187, 2187, 2187, 2187, 2187,
-
2187, 2187, 2187, 2187, 2189, 2187, 2187, 2187,
-
2187, 2187, 2187, 2187, 2187, 2187, 2187, 2187,
-
2187, 2187, 2187, 2187, 2187, 2187, 2187, 2187,
-
2187, 2187, 2187, 2187, 2187, 2187, 2187, 2187,
-
2187, 2187, 2187, 2187, 2187, 2187, 2187, 2187,
-
1, 2187, 2187, 2187, 2187, 2187, 2187, 2187,
-
2187, 2187, 1, 2187, 2187, 2190, 2187, 2187,
-
2187, 2187, 2187, 2187, 2187, 2187, 2187, 2187,
-
2187, 2187, 2187, 2187, 2187, 2187, 2187, 2187,
-
2187, 2187, 2183, 2187, 2187, 2187, 2187, 2187,
-
2187, 2187, 2187, 2187, 2187, 2187, 2187, 2187,
-
2187, 2187, 2187, 2187, 2187, 2187, 2187, 2187,
-
2187, 2187, 2187, 2187, 2187, 2187, 2187, 2187,
-
2187, 2187, 2187, 2187, 2187, 2187, 2187, 2187,
-
2187, 2187, 2187, 2187, 2187, 2187, 2187, 2187,
-
2187, 2187, 2187, 2187, 2187, 2187, 2187, 2187,
-
2187, 2187, 2187, 2187, 2189, 2187, 2187, 2187,
-
2187, 2187, 2187, 2187, 2187, 2187, 2187, 2187,
-
2187, 2187, 2187, 2187, 2187, 2187, 2187, 2187,
-
2187, 2187, 2187, 2187, 2187, 2187, 2187, 2187,
-
2187, 2187, 2187, 2187, 2187, 2187, 2187, 2187,
-
1, 2191, 1, 2187, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 2187, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 2187, 2192, 1, 2184,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 2184, 1,
-
2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144,
-
2144, 1, 2144, 2144, 2193, 2144, 2144, 2144,
-
2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144,
-
2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144,
-
2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144,
-
2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144,
-
2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144,
-
2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144,
-
2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144,
-
2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144,
-
2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144,
-
2144, 2144, 1, 2194, 2186, 2144, 2144, 2144,
-
2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144,
-
2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144,
-
2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144,
-
2144, 2144, 2144, 2144, 2144, 2144, 2144, 1,
-
2195, 1, 2144, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 2144, 1, 2144, 2144, 2144, 2144, 2144,
-
2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144,
-
2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144,
-
2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144,
-
2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144,
-
2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144,
-
2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144,
-
2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144,
-
2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144,
-
2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144,
-
2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144,
-
2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144,
-
2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144,
-
2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144,
-
2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144,
-
2144, 2144, 2144, 2144, 2144, 2144, 2144, 2144,
-
2144, 2144, 2144, 1, 2196, 2196, 2196, 2196,
-
2196, 2196, 2196, 2196, 2196, 1, 2196, 2196,
-
2197, 2196, 2196, 2196, 2196, 2196, 2196, 2196,
-
2196, 2196, 2196, 2196, 2196, 2196, 2196, 2196,
-
2196, 2196, 2196, 2196, 2196, 2198, 2196, 2196,
-
2196, 2196, 2196, 2196, 2196, 2196, 2196, 2196,
-
2196, 2196, 2196, 2196, 2196, 2196, 2196, 2196,
-
2196, 2196, 2196, 2196, 2196, 2196, 2196, 2196,
-
2196, 2196, 2196, 2196, 2196, 2196, 2196, 2196,
-
2196, 2196, 2196, 2196, 2196, 2196, 2196, 2196,
-
2196, 2196, 2196, 2196, 2196, 2196, 2196, 2196,
-
2196, 2196, 2196, 2196, 2196, 2196, 2196, 2199,
-
2196, 2196, 2196, 2196, 2196, 2196, 2196, 2196,
-
2196, 2196, 2196, 2196, 2196, 2196, 2196, 2196,
-
2196, 2196, 2196, 2196, 2196, 2196, 2196, 2196,
-
2196, 2196, 2196, 2196, 2196, 2196, 2196, 2196,
-
2196, 2196, 2196, 1, 2200, 2200, 2200, 2200,
-
2200, 2200, 2200, 2200, 2200, 1, 2200, 2200,
-
2201, 2200, 2200, 2200, 2200, 2200, 2200, 2200,
-
2200, 2200, 2200, 2200, 2200, 2200, 2200, 2200,
-
2200, 2200, 2200, 2200, 2200, 2202, 2200, 2200,
-
2200, 2200, 2200, 2200, 2200, 2200, 2200, 2200,
-
2200, 2200, 2200, 2200, 2200, 2200, 2200, 2200,
-
2200, 2200, 2200, 2200, 2200, 2200, 2200, 2200,
-
2200, 2200, 2200, 2200, 2200, 2200, 2200, 2200,
-
2200, 2200, 2200, 2200, 2200, 2200, 2200, 2200,
-
2200, 2200, 2200, 2200, 2200, 2200, 2200, 2200,
-
2200, 2200, 2200, 2200, 2200, 2200, 2200, 2203,
-
2200, 2200, 2200, 2200, 2200, 2200, 2200, 2200,
-
2200, 2200, 2200, 2200, 2200, 2200, 2200, 2200,
-
2200, 2200, 2200, 2200, 2200, 2200, 2200, 2200,
-
2200, 2200, 2200, 2200, 2200, 2200, 2200, 2200,
-
2200, 2200, 2200, 1, 2204, 1, 2200, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 2200, 1, 2200,
-
2200, 2200, 2200, 2200, 2200, 2200, 2200, 2200,
-
2200, 2200, 2200, 2200, 2200, 2200, 2200, 2200,
-
2200, 2200, 2200, 2200, 2200, 2200, 2200, 2200,
-
2200, 2200, 2200, 2200, 2200, 2200, 2200, 2200,
-
2200, 2200, 2200, 2200, 2200, 2200, 2200, 2200,
-
2200, 2200, 2200, 2200, 2200, 2200, 2200, 2200,
-
2200, 2200, 2200, 2200, 2200, 2200, 2200, 2200,
-
2200, 2200, 2200, 2200, 2200, 2200, 2200, 2200,
-
2200, 2200, 2200, 2200, 2200, 2200, 2200, 2200,
-
2200, 2200, 2200, 2200, 2200, 2200, 2200, 2200,
-
2200, 2200, 2200, 2200, 2200, 2200, 2200, 2200,
-
2200, 2200, 2200, 2200, 2200, 2200, 2200, 2200,
-
2200, 2200, 2200, 2200, 2200, 2200, 2200, 2200,
-
2200, 2200, 2200, 2200, 2200, 2200, 2200, 2200,
-
2200, 2200, 2200, 2200, 2200, 2200, 2200, 2200,
-
2200, 2200, 2200, 2200, 2200, 2200, 2200, 1,
-
2205, 1, 1, 1, 2206, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 2205,
-
2207, 2208, 2207, 2207, 2207, 2207, 2207, 2209,
-
1, 2207, 2207, 1, 2207, 1, 2207, 2207,
-
2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207,
-
2207, 1, 1, 48, 2207, 1, 2207, 1,
-
2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207,
-
2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207,
-
2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207,
-
2207, 2207, 1, 1, 1, 2207, 2207, 2207,
-
2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207,
-
2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207,
-
2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207,
-
2207, 2207, 2207, 2207, 2207, 2207, 1, 2210,
-
2210, 2210, 2210, 2210, 2210, 2210, 2210, 2211,
-
1, 2210, 2210, 2212, 2210, 2210, 2210, 2210,
-
2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210,
-
2210, 2210, 2210, 2210, 2210, 2210, 2211, 2210,
-
23, 2210, 2210, 2210, 2210, 2210, 2210, 2210,
-
2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210,
-
2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210,
-
2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210,
-
2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210,
-
2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210,
-
2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210,
-
2210, 2210, 2213, 2210, 2210, 2210, 2210, 2210,
-
2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210,
-
2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210,
-
2210, 2210, 2210, 2210, 2210, 2210, 2210, 2210,
-
2210, 2210, 2210, 2210, 2210, 2210, 1, 2214,
-
2214, 2214, 2214, 2214, 2214, 2214, 2214, 2214,
-
1, 2214, 2214, 2215, 2214, 2214, 2214, 2214,
-
2214, 2214, 2214, 2214, 2214, 2214, 2214, 2214,
-
2214, 2214, 2214, 2214, 2214, 2214, 2214, 2214,
-
2216, 2214, 2214, 2214, 2214, 2214, 2214, 2214,
-
2214, 2214, 2214, 2214, 2214, 2214, 2214, 2214,
-
2214, 2214, 2214, 2214, 2214, 2214, 2214, 2214,
-
2214, 2214, 2214, 2214, 2214, 2214, 2214, 2214,
-
2214, 2214, 2214, 2214, 2214, 2214, 2214, 2214,
-
2214, 2214, 2214, 2214, 2214, 2214, 2214, 2214,
-
2214, 2214, 2214, 2214, 2214, 2214, 2214, 2214,
-
2214, 2214, 2217, 2214, 2214, 2214, 2214, 2214,
-
2214, 2214, 2214, 2214, 2214, 2214, 2214, 2214,
-
2214, 2214, 2214, 2214, 2214, 2214, 2214, 2214,
-
2214, 2214, 2214, 2214, 2214, 2214, 2214, 2214,
-
2214, 2214, 2214, 2214, 2214, 2214, 1, 2218,
-
1, 2214, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
2214, 1, 2219, 1, 2220, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 2220, 1, 2221, 1, 2222,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 2222, 1,
-
2223, 1, 1, 1, 2224, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 2223,
-
2225, 2226, 2225, 2225, 2225, 2225, 2225, 2227,
-
1, 2225, 2225, 1, 2225, 2228, 2225, 2225,
-
2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225,
-
2225, 1, 1, 1, 2225, 1, 2225, 1,
-
2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225,
-
2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225,
-
2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225,
-
2225, 2225, 2137, 1, 1, 2225, 2225, 2225,
-
2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225,
-
2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225,
-
2225, 2225, 2225, 2225, 2225, 2225, 2225, 2225,
-
2225, 2225, 2225, 2225, 2225, 2225, 1, 2229,
-
1, 1, 1, 2230, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 2229, 2231,
-
2184, 2231, 2231, 2231, 2231, 2231, 2232, 1,
-
2231, 2231, 1, 2231, 2143, 2231, 2231, 2231,
-
2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231,
-
1, 1, 1, 2231, 1, 2231, 1, 2231,
-
2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231,
-
2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231,
-
2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231,
-
2231, 2144, 1, 1, 2231, 2231, 2231, 2231,
-
2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231,
-
2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231,
-
2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231,
-
2231, 2231, 2231, 2231, 2231, 1, 2233, 1,
-
2229, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 2229,
-
1, 2234, 1, 2235, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 2235, 1, 2236, 1, 1, 1,
-
2237, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 2236, 2238, 1, 2238, 2238,
-
2238, 2238, 2238, 2239, 1, 2238, 2238, 1,
-
2238, 1, 2238, 2238, 2238, 2238, 2238, 2238,
-
2238, 2238, 2238, 2238, 2238, 1, 1, 1,
-
2238, 1, 2238, 1, 2238, 2238, 2238, 2238,
-
2238, 2238, 2238, 2238, 2238, 2238, 2238, 2238,
-
2238, 2238, 2238, 2238, 2238, 2238, 2238, 2238,
-
2238, 2238, 2238, 2238, 2238, 2238, 1, 1,
-
1, 2238, 2238, 2238, 2238, 2238, 2238, 2238,
-
2238, 2238, 2238, 2238, 2238, 2238, 2238, 2238,
-
2238, 2238, 2238, 2238, 2238, 2238, 2238, 2238,
-
2238, 2238, 2238, 2238, 2238, 2238, 2238, 2238,
-
2238, 2238, 1, 2240, 1, 2236, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 2236, 1, 2241, 1,
-
1, 1, 2242, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 2241, 2243, 1,
-
2243, 2243, 2243, 2243, 2243, 2244, 1, 2243,
-
2243, 1, 2243, 1, 2243, 2243, 2243, 2243,
-
2243, 2243, 2243, 2243, 2243, 2243, 2243, 1,
-
1, 1, 2243, 1, 2243, 1, 2243, 2243,
-
2243, 2243, 2243, 2243, 2243, 2243, 2243, 2243,
-
2243, 2243, 2243, 2243, 2243, 2243, 2243, 2243,
-
2243, 2243, 2243, 2243, 2243, 2243, 2243, 2243,
-
1, 1, 1, 2243, 2243, 2243, 2243, 2243,
-
2243, 2243, 2243, 2243, 2243, 2243, 2243, 2243,
-
2243, 2243, 2243, 2243, 2243, 2243, 2243, 2243,
-
2243, 2243, 2243, 2243, 2243, 2243, 2243, 2243,
-
2243, 2243, 2243, 2243, 1, 2236, 1, 1,
-
1, 2237, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 2236, 2231, 2184, 2231,
-
2231, 2231, 2231, 2231, 2239, 1, 2231, 2231,
-
1, 2231, 2143, 2231, 2231, 2231, 2231, 2231,
-
2231, 2231, 2231, 2231, 2231, 2231, 1, 1,
-
1, 2231, 1, 2231, 1, 2231, 2231, 2231,
-
2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231,
-
2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231,
-
2231, 2231, 2231, 2231, 2231, 2231, 2231, 1,
-
1, 1, 2231, 2231, 2231, 2231, 2231, 2231,
-
2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231,
-
2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231,
-
2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231,
-
2231, 2231, 2231, 1, 2245, 1, 1, 1,
-
2246, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 2245, 2247, 2248, 2247, 2247,
-
2247, 2247, 2247, 2249, 1, 2247, 2247, 1,
-
2247, 2181, 2247, 2247, 2247, 2247, 2247, 2247,
-
2247, 2247, 2247, 2247, 2247, 1, 1, 1,
-
2247, 1, 2247, 1, 2247, 2247, 2247, 2247,
-
2247, 2247, 2247, 2247, 2247, 2247, 2247, 2247,
-
2247, 2247, 2247, 2247, 2247, 2247, 2247, 2247,
-
2247, 2247, 2247, 2247, 2247, 2247, 2182, 1,
-
1, 2247, 2247, 2247, 2247, 2247, 2247, 2247,
-
2247, 2247, 2247, 2247, 2247, 2247, 2247, 2247,
-
2247, 2247, 2247, 2247, 2247, 2247, 2247, 2247,
-
2247, 2247, 2247, 2247, 2247, 2247, 2247, 2247,
-
2247, 2247, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 2214, 2214, 2214, 2214, 2214,
-
2214, 2214, 2214, 2214, 2250, 1, 2214, 2214,
-
2251, 2214, 2214, 2214, 2214, 2214, 2214, 2214,
-
2214, 2214, 2214, 2214, 2214, 2214, 2214, 2214,
-
2214, 2214, 2214, 2250, 2214, 27, 2214, 2214,
-
2214, 2214, 2214, 2214, 2214, 2214, 2214, 2214,
-
2214, 2214, 2214, 2214, 2214, 2214, 2214, 2214,
-
2214, 2214, 2214, 2214, 2214, 2214, 2214, 2214,
-
2214, 2214, 2214, 2214, 2214, 2214, 2214, 2214,
-
2214, 2214, 2214, 2214, 2214, 2214, 2214, 2214,
-
2214, 2214, 2214, 2214, 2214, 2214, 2214, 2214,
-
2214, 2214, 2214, 2214, 2214, 2214, 2214, 2217,
-
2214, 2214, 2214, 2214, 2214, 2214, 2214, 2214,
-
2214, 2214, 2214, 2214, 2214, 2214, 2214, 2214,
-
2214, 2214, 2214, 2214, 2214, 2214, 2214, 2214,
-
2214, 2214, 2214, 2214, 2214, 2214, 2214, 2214,
-
2214, 2214, 2214, 1, 2252, 1, 2250, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 2250, 1, 2253,
-
1, 2254, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
2254, 1, 2255, 2255, 2255, 2255, 2255, 2255,
-
2255, 2255, 2256, 1, 2255, 2255, 2257, 2255,
-
2255, 2255, 2255, 2255, 2255, 2255, 2255, 2255,
-
2255, 2255, 2255, 2255, 2255, 2255, 2255, 2255,
-
2255, 2256, 2255, 2198, 2255, 2255, 2255, 2255,
-
2255, 2255, 2255, 2255, 2255, 2255, 2255, 2255,
-
2255, 2255, 2255, 2255, 2255, 2255, 2255, 2255,
-
2255, 2255, 2255, 2255, 2255, 2255, 2255, 2255,
-
2255, 2255, 2255, 2255, 2255, 2255, 2255, 2255,
-
2255, 2255, 2255, 2255, 2255, 2255, 2255, 2255,
-
2255, 2255, 2255, 2255, 2255, 2255, 2255, 2255,
-
2255, 2255, 2255, 2255, 2255, 2258, 2255, 2255,
-
2255, 2255, 2255, 2255, 2255, 2255, 2255, 2255,
-
2255, 2255, 2255, 2255, 2255, 2255, 2255, 2255,
-
2255, 2255, 2255, 2255, 2255, 2255, 2255, 2255,
-
2255, 2255, 2255, 2255, 2255, 2255, 2255, 2255,
-
2255, 1, 2259, 2259, 2259, 2259, 2259, 2259,
-
2259, 2259, 2259, 1, 2259, 2259, 2260, 2259,
-
2259, 2259, 2259, 2259, 2259, 2259, 2259, 2259,
-
2259, 2259, 2259, 2259, 2259, 2259, 2259, 2259,
-
2259, 2259, 2259, 2261, 2259, 2259, 2259, 2259,
-
2259, 2259, 2259, 2259, 2259, 2259, 2259, 2259,
-
2259, 2259, 2259, 2259, 2259, 2259, 2259, 2259,
-
2259, 2259, 2259, 2259, 2259, 2259, 2259, 2259,
-
2259, 2259, 2259, 2259, 2259, 2259, 2259, 2259,
-
2259, 2259, 2259, 2259, 2259, 2259, 2259, 2259,
-
2259, 2259, 2259, 2259, 2259, 2259, 2259, 2259,
-
2259, 2259, 2259, 2259, 2259, 2262, 2259, 2259,
-
2259, 2259, 2259, 2259, 2259, 2259, 2259, 2259,
-
2259, 2259, 2259, 2259, 2259, 2259, 2259, 2259,
-
2259, 2259, 2259, 2259, 2259, 2259, 2259, 2259,
-
2259, 2259, 2259, 2259, 2259, 2259, 2259, 2259,
-
2259, 1, 2263, 1, 2259, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 2259, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 2259, 2259, 2259,
-
2259, 2259, 2259, 2259, 2259, 2259, 2264, 1,
-
2259, 2259, 2265, 2259, 2259, 2259, 2259, 2259,
-
2259, 2259, 2259, 2259, 2259, 2259, 2259, 2259,
-
2259, 2259, 2259, 2259, 2259, 2264, 2259, 2202,
-
2259, 2259, 2259, 2259, 2259, 2259, 2259, 2259,
-
2259, 2259, 2259, 2259, 2259, 2259, 2259, 2259,
-
2259, 2259, 2259, 2259, 2259, 2259, 2259, 2259,
-
2259, 2259, 2259, 2259, 2259, 2259, 2259, 2259,
-
2259, 2259, 2259, 2259, 2259, 2259, 2259, 2259,
-
2259, 2259, 2259, 2259, 2259, 2259, 2259, 2259,
-
2259, 2259, 2259, 2259, 2259, 2259, 2259, 2259,
-
2259, 2262, 2259, 2259, 2259, 2259, 2259, 2259,
-
2259, 2259, 2259, 2259, 2259, 2259, 2259, 2259,
-
2259, 2259, 2259, 2259, 2259, 2259, 2259, 2259,
-
2259, 2259, 2259, 2259, 2259, 2259, 2259, 2259,
-
2259, 2259, 2259, 2259, 2259, 1, 2266, 1,
-
2264, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 2264,
-
1, 2267, 2267, 2267, 2267, 2267, 2267, 2267,
-
2267, 2268, 1, 2267, 2267, 2269, 2267, 2267,
-
2267, 2267, 2267, 2267, 2267, 2267, 2267, 2267,
-
2267, 2267, 2267, 2267, 2267, 2267, 2267, 2267,
-
2268, 2267, 2270, 2267, 2267, 2267, 2267, 2267,
-
2267, 2267, 2267, 2267, 2267, 2267, 2267, 2267,
-
2267, 2267, 2267, 2267, 2267, 2267, 2267, 2267,
-
2267, 2267, 2267, 2267, 2267, 2267, 2267, 2267,
-
2267, 2267, 2267, 2267, 2267, 2267, 2267, 2267,
-
2267, 2267, 2267, 2267, 2267, 2267, 2267, 2267,
-
2267, 2267, 2267, 2267, 2267, 2267, 2267, 2267,
-
2267, 2267, 2267, 2267, 2271, 2267, 2267, 2267,
-
2267, 2267, 2267, 2267, 2267, 2267, 2267, 2267,
-
2267, 2267, 2267, 2267, 2267, 2267, 2267, 2267,
-
2267, 2267, 2267, 2267, 2267, 2267, 2267, 2267,
-
2267, 2267, 2267, 2267, 2267, 2267, 2267, 2267,
-
1, 2272, 2272, 2272, 2272, 2272, 2272, 2272,
-
2272, 2272, 1, 2272, 2272, 2273, 2272, 2272,
-
2272, 2272, 2272, 2272, 2272, 2272, 2272, 2272,
-
2272, 2272, 2272, 2272, 2272, 2272, 2272, 2272,
-
2272, 2272, 2274, 2272, 2272, 2272, 2272, 2272,
-
2272, 2272, 2272, 2272, 2272, 2272, 2272, 2272,
-
2272, 2272, 2272, 2272, 2272, 2272, 2272, 2272,
-
2272, 2272, 2272, 2272, 2272, 2272, 2272, 2272,
-
2272, 2272, 2272, 2272, 2272, 2272, 2272, 2272,
-
2272, 2272, 2272, 2272, 2272, 2272, 2272, 2272,
-
2272, 2272, 2272, 2272, 2272, 2272, 2272, 2272,
-
2272, 2272, 2272, 2272, 2275, 2272, 2272, 2272,
-
2272, 2272, 2272, 2272, 2272, 2272, 2272, 2272,
-
2272, 2272, 2272, 2272, 2272, 2272, 2272, 2272,
-
2272, 2272, 2272, 2272, 2272, 2272, 2272, 2272,
-
2272, 2272, 2272, 2272, 2272, 2272, 2272, 2272,
-
1, 2276, 1, 2272, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 2272, 1, 2277, 1, 2278, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 2278, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 2272,
-
2272, 2272, 2272, 2272, 2272, 2272, 2272, 2272,
-
2279, 1, 2272, 2272, 2280, 2272, 2272, 2272,
-
2272, 2272, 2272, 2272, 2272, 2272, 2272, 2272,
-
2272, 2272, 2272, 2272, 2272, 2272, 2272, 2279,
-
2272, 2281, 2272, 2272, 2272, 2272, 2272, 2272,
-
2272, 2272, 2272, 2272, 2272, 2272, 2272, 2272,
-
2272, 2272, 2272, 2272, 2272, 2272, 2272, 2272,
-
2272, 2272, 2272, 2272, 2272, 2272, 2272, 2272,
-
2272, 2272, 2272, 2272, 2272, 2272, 2272, 2272,
-
2272, 2272, 2272, 2272, 2272, 2272, 2272, 2272,
-
2272, 2272, 2272, 2272, 2272, 2272, 2272, 2272,
-
2272, 2272, 2272, 2275, 2272, 2272, 2272, 2272,
-
2272, 2272, 2272, 2272, 2272, 2272, 2272, 2272,
-
2272, 2272, 2272, 2272, 2272, 2272, 2272, 2272,
-
2272, 2272, 2272, 2272, 2272, 2272, 2272, 2272,
-
2272, 2272, 2272, 2272, 2272, 2272, 2272, 1,
-
2282, 1, 2279, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 2279, 1, 2283, 1, 2284, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 2284, 1, 2285, 1,
-
1, 1, 2286, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 2285, 2287, 2288,
-
2287, 2287, 2287, 2287, 2287, 2289, 1, 2287,
-
2287, 1, 2287, 2290, 2287, 2287, 2287, 2287,
-
2287, 2287, 2287, 2287, 2287, 2287, 2287, 1,
-
1, 48, 2287, 1, 2287, 1, 2287, 2287,
-
2287, 2287, 2287, 2287, 2287, 2287, 2287, 2287,
-
2287, 2287, 2287, 2287, 2287, 2287, 2287, 2287,
-
2287, 2287, 2287, 2287, 2287, 2287, 2287, 2287,
-
1, 1, 1, 2287, 2287, 2287, 2287, 2287,
-
2287, 2287, 2287, 2287, 2287, 2287, 2287, 2287,
-
2287, 2287, 2287, 2287, 2287, 2287, 2287, 2287,
-
2287, 2287, 2287, 2287, 2287, 2287, 2287, 2287,
-
2287, 2287, 2287, 2287, 1, 2291, 1, 1,
-
1, 2292, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 2291, 2293, 2294, 2293,
-
2293, 2293, 2293, 2293, 2295, 1, 2293, 2293,
-
1, 2293, 2296, 2293, 2293, 2293, 2293, 2293,
-
2293, 2293, 2293, 2293, 2293, 2293, 1, 1,
-
8, 2293, 1, 2293, 1, 2293, 2293, 2293,
-
2293, 2293, 2293, 2293, 2293, 2293, 2293, 2293,
-
2293, 2293, 2293, 2293, 2293, 2293, 2293, 2293,
-
2293, 2293, 2293, 2293, 2293, 2293, 2293, 1,
-
1, 1, 2293, 2293, 2293, 2293, 2293, 2293,
-
2293, 2293, 2293, 2293, 2293, 2293, 2293, 2293,
-
2293, 2293, 2293, 2293, 2293, 2293, 2293, 2293,
-
2293, 2293, 2293, 2293, 2293, 2293, 2293, 2293,
-
2293, 2293, 2293, 1, 2297, 1, 2291, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 2291, 1, 2298,
-
2299, 2298, 2298, 2298, 2298, 2298, 1, 1,
-
2298, 2298, 1, 2298, 2300, 2298, 2298, 2298,
-
2298, 2298, 2298, 2298, 2298, 2298, 2298, 2298,
-
1, 1, 1, 2298, 1, 2298, 1, 2298,
-
2298, 2298, 2298, 2298, 2298, 2298, 2298, 2298,
-
2298, 2298, 2298, 2298, 2298, 2298, 2298, 2298,
-
2298, 2298, 2298, 2298, 2298, 2298, 2298, 2298,
-
2298, 1, 1, 1, 2298, 2298, 2298, 2298,
-
2298, 2298, 2298, 2298, 2298, 2298, 2298, 2298,
-
2298, 2298, 2298, 2298, 2298, 2298, 2298, 2298,
-
2298, 2298, 2298, 2298, 2298, 2298, 2298, 2298,
-
2298, 2298, 2298, 2298, 2298, 1, 2301, 1,
-
2302, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 2302,
-
1, 2303, 2303, 2303, 2303, 2303, 2303, 2303,
-
2303, 2299, 1, 2303, 2303, 2304, 2303, 2303,
-
2303, 2303, 2303, 2303, 2303, 2303, 2303, 2303,
-
2303, 2303, 2303, 2303, 2303, 2303, 2303, 2303,
-
2299, 2303, 1, 2303, 2303, 2303, 2303, 2303,
-
2303, 2303, 2303, 2303, 2303, 2303, 2303, 2303,
-
2303, 2303, 2303, 2303, 2303, 2303, 2303, 2303,
-
2303, 2303, 2303, 2303, 2303, 2303, 2303, 2303,
-
2303, 2303, 2303, 2303, 2303, 2303, 2303, 2303,
-
2303, 2303, 2303, 2303, 2303, 2303, 2303, 2303,
-
2303, 2303, 2303, 2303, 2303, 2303, 2303, 2303,
-
2303, 2303, 2303, 2303, 2305, 2303, 2303, 2303,
-
2303, 2303, 2303, 2303, 2303, 2303, 2303, 2303,
-
2303, 2303, 2303, 2303, 2303, 2303, 2303, 2303,
-
2303, 2303, 2303, 2303, 2303, 2303, 2303, 2303,
-
2303, 2303, 2303, 2303, 2303, 2303, 2303, 2303,
-
1, 2303, 2303, 2303, 2303, 2303, 2303, 2303,
-
2303, 2303, 1, 2303, 2303, 2306, 2303, 2303,
-
2303, 2303, 2303, 2303, 2303, 2303, 2303, 2303,
-
2303, 2303, 2303, 2303, 2303, 2303, 2303, 2303,
-
2303, 2303, 2298, 2303, 2303, 2303, 2303, 2303,
-
2303, 2303, 2303, 2303, 2303, 2303, 2303, 2303,
-
2303, 2303, 2303, 2303, 2303, 2303, 2303, 2303,
-
2303, 2303, 2303, 2303, 2303, 2303, 2303, 2303,
-
2303, 2303, 2303, 2303, 2303, 2303, 2303, 2303,
-
2303, 2303, 2303, 2303, 2303, 2303, 2303, 2303,
-
2303, 2303, 2303, 2303, 2303, 2303, 2303, 2303,
-
2303, 2303, 2303, 2303, 2305, 2303, 2303, 2303,
-
2303, 2303, 2303, 2303, 2303, 2303, 2303, 2303,
-
2303, 2303, 2303, 2303, 2303, 2303, 2303, 2303,
-
2303, 2303, 2303, 2303, 2303, 2303, 2303, 2303,
-
2303, 2303, 2303, 2303, 2303, 2303, 2303, 2303,
-
1, 2307, 1, 2303, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 2303, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 2303, 2308, 1, 2299,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 2299, 1,
-
38, 1, 1, 1, 39, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 38,
-
2309, 2310, 2309, 2309, 2309, 2309, 2309, 40,
-
1, 2309, 2309, 1, 2309, 2311, 2309, 2309,
-
2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309,
-
2309, 18, 1, 19, 2309, 1, 2309, 17,
-
2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309,
-
2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309,
-
2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309,
-
2309, 2309, 1, 1, 1, 2309, 2309, 2309,
-
2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309,
-
2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309,
-
2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309,
-
2309, 2309, 2309, 2309, 2309, 2309, 1, 2312,
-
1, 1, 1, 2313, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 2312, 2314,
-
2315, 2314, 2314, 2314, 2314, 2314, 2316, 1,
-
2314, 2314, 1, 2314, 2317, 2314, 2314, 2314,
-
2314, 2314, 2314, 2314, 2314, 2314, 2314, 2314,
-
1, 1, 1, 2314, 1, 2314, 1, 2314,
-
2314, 2314, 2314, 2314, 2314, 2314, 2314, 2314,
-
2314, 2314, 2314, 2314, 2314, 2314, 2314, 2314,
-
2314, 2314, 2314, 2314, 2314, 2314, 2314, 2314,
-
2314, 2318, 1, 1, 2314, 2314, 2314, 2314,
-
2314, 2314, 2314, 2314, 2314, 2314, 2314, 2314,
-
2314, 2314, 2314, 2314, 2314, 2314, 2314, 2314,
-
2314, 2314, 2314, 2314, 2314, 2314, 2314, 2314,
-
2314, 2314, 2314, 2314, 2314, 1, 2319, 1,
-
1, 1, 2320, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 2319, 2321, 2322,
-
2321, 2321, 2321, 2321, 2321, 2323, 1, 2321,
-
2321, 1, 2321, 2324, 2321, 2321, 2321, 2321,
-
2321, 2321, 2321, 2321, 2321, 2321, 2321, 1,
-
1, 1, 2321, 1, 2321, 1, 2321, 2321,
-
2321, 2321, 2321, 2321, 2321, 2321, 2321, 2321,
-
2321, 2321, 2321, 2321, 2321, 2321, 2321, 2321,
-
2321, 2321, 2321, 2321, 2321, 2321, 2321, 2321,
-
2325, 1, 1, 2321, 2321, 2321, 2321, 2321,
-
2321, 2321, 2321, 2321, 2321, 2321, 2321, 2321,
-
2321, 2321, 2321, 2321, 2321, 2321, 2321, 2321,
-
2321, 2321, 2321, 2321, 2321, 2321, 2321, 2321,
-
2321, 2321, 2321, 2321, 1, 2326, 1, 2319,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 2319, 1,
-
2327, 1, 1, 1, 2328, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 2327,
-
2321, 2322, 2321, 2321, 2321, 2321, 2321, 2329,
-
1, 2321, 2321, 1, 2321, 2330, 2321, 2321,
-
2321, 2321, 2321, 2321, 2321, 2321, 2321, 2321,
-
2321, 1, 1, 1, 2321, 2331, 2321, 1,
-
2321, 2321, 2321, 2321, 2321, 2321, 2321, 2321,
-
2321, 2321, 2321, 2321, 2321, 2321, 2321, 2321,
-
2321, 2321, 2321, 2321, 2321, 2321, 2321, 2321,
-
2321, 2321, 1, 1, 1, 2321, 2321, 2321,
-
2321, 2321, 2321, 2321, 2321, 2321, 2321, 2321,
-
2321, 2321, 2321, 2321, 2321, 2321, 2321, 2321,
-
2321, 2321, 2321, 2321, 2321, 2321, 2321, 2321,
-
2321, 2321, 2321, 2321, 2321, 2321, 1, 2327,
-
1, 1, 1, 2328, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 2327, 1,
-
1, 1, 1, 1, 1, 1, 2329, 1,
-
1, 1, 1, 1, 2332, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 2331, 1, 2333, 1,
-
2327, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 2327,
-
1, 2334, 1, 1, 1, 2335, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
2334, 1, 1, 1, 1, 1, 1, 1,
-
2336, 1, 1, 1, 1, 1, 2337, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 2338, 1,
-
2332, 1, 1, 1, 2339, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 2332,
-
2340, 1, 2340, 2340, 2340, 2340, 2340, 2341,
-
1, 2340, 2340, 1, 2340, 1, 2340, 2340,
-
2340, 2340, 2340, 2340, 2340, 2340, 2340, 2340,
-
2340, 1, 1, 1, 2340, 1, 2340, 1,
-
2340, 2340, 2340, 2340, 2340, 2340, 2340, 2340,
-
2340, 2340, 2340, 2340, 2340, 2340, 2340, 2340,
-
2340, 2340, 2340, 2340, 2340, 2340, 2340, 2340,
-
2340, 2340, 1, 1, 1, 2340, 2340, 2340,
-
2340, 2340, 2340, 2340, 2340, 2340, 2340, 2340,
-
2340, 2340, 2340, 2340, 2340, 2340, 2340, 2340,
-
2340, 2340, 2340, 2340, 2340, 2340, 2340, 2340,
-
2340, 2340, 2340, 2340, 2340, 2340, 1, 2342,
-
1, 2332, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
2332, 1, 2327, 1, 1, 1, 2328, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 2327, 2340, 1, 2340, 2340, 2340, 2340,
-
2340, 2329, 1, 2340, 2340, 1, 2340, 2332,
-
2340, 2340, 2340, 2340, 2340, 2340, 2340, 2340,
-
2340, 2340, 2340, 1, 1, 1, 2340, 2331,
-
2340, 1, 2340, 2340, 2340, 2340, 2340, 2340,
-
2340, 2340, 2340, 2340, 2340, 2340, 2340, 2340,
-
2340, 2340, 2340, 2340, 2340, 2340, 2340, 2340,
-
2340, 2340, 2340, 2340, 1, 1, 1, 2340,
-
2340, 2340, 2340, 2340, 2340, 2340, 2340, 2340,
-
2340, 2340, 2340, 2340, 2340, 2340, 2340, 2340,
-
2340, 2340, 2340, 2340, 2340, 2340, 2340, 2340,
-
2340, 2340, 2340, 2340, 2340, 2340, 2340, 2340,
-
1, 2337, 1, 1, 1, 2343, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
2337, 2344, 1, 2344, 2344, 2344, 2344, 2344,
-
2345, 1, 2344, 2344, 1, 2344, 1, 2344,
-
2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344,
-
2344, 2344, 1, 1, 1, 2344, 1, 2344,
-
1, 2344, 2344, 2344, 2344, 2344, 2344, 2344,
-
2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344,
-
2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344,
-
2344, 2344, 2344, 1, 1, 1, 2344, 2344,
-
2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344,
-
2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344,
-
2344, 2344, 2344, 2344, 2344, 2344, 2344, 2344,
-
2344, 2344, 2344, 2344, 2344, 2344, 2344, 1,
-
2346, 2346, 2346, 2346, 2346, 2346, 2346, 2346,
-
2322, 1, 2346, 2346, 2347, 2346, 2346, 2346,
-
2346, 2346, 2346, 2346, 2346, 2346, 2346, 2346,
-
2346, 2346, 2346, 2346, 2346, 2346, 2346, 2322,
-
2346, 1, 2346, 2346, 2346, 2346, 2346, 2346,
-
2346, 2346, 2346, 2346, 2346, 2346, 2346, 2346,
-
2346, 2346, 2346, 2346, 2346, 2346, 2346, 2346,
-
2346, 2346, 2346, 2346, 2346, 2346, 2346, 2346,
-
2346, 2346, 2346, 2346, 2346, 2346, 2346, 2346,
-
2346, 2346, 2346, 2346, 2346, 2346, 2346, 2346,
-
2346, 2346, 2346, 2346, 2346, 2346, 2346, 2346,
-
2346, 2346, 2346, 2348, 2346, 2346, 2346, 2346,
-
2346, 2346, 2346, 2346, 2346, 2346, 2346, 2346,
-
2346, 2346, 2346, 2346, 2346, 2346, 2346, 2346,
-
2346, 2346, 2346, 2346, 2346, 2346, 2346, 2346,
-
2346, 2346, 2346, 2346, 2346, 2346, 2346, 1,
-
2346, 2346, 2346, 2346, 2346, 2346, 2346, 2346,
-
2346, 1, 2346, 2346, 2349, 2346, 2346, 2346,
-
2346, 2346, 2346, 2346, 2346, 2346, 2346, 2346,
-
2346, 2346, 2346, 2346, 2346, 2346, 2346, 2346,
-
2346, 2350, 2346, 2346, 2346, 2346, 2346, 2346,
-
2346, 2346, 2346, 2346, 2346, 2346, 2346, 2346,
-
2346, 2346, 2346, 2346, 2346, 2346, 2346, 2346,
-
2346, 2346, 2346, 2346, 2346, 2346, 2346, 2346,
-
2346, 2346, 2346, 2346, 2346, 2346, 2346, 2346,
-
2346, 2346, 2346, 2346, 2346, 2346, 2346, 2346,
-
2346, 2346, 2346, 2346, 2346, 2346, 2346, 2346,
-
2346, 2346, 2346, 2348, 2346, 2346, 2346, 2346,
-
2346, 2346, 2346, 2346, 2346, 2346, 2346, 2346,
-
2346, 2346, 2346, 2346, 2346, 2346, 2346, 2346,
-
2346, 2346, 2346, 2346, 2346, 2346, 2346, 2346,
-
2346, 2346, 2346, 2346, 2346, 2346, 2346, 1,
-
2351, 1, 2346, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 2346, 1, 2352, 1, 1, 1, 2353,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 2352, 2350, 2322, 2350, 2350, 2350,
-
2350, 2350, 2354, 1, 2350, 2350, 1, 2350,
-
2324, 2350, 2350, 2350, 2350, 2350, 2350, 2350,
-
2350, 2350, 2350, 2350, 1, 1, 1, 2350,
-
2331, 2350, 1, 2350, 2350, 2350, 2350, 2350,
-
2350, 2350, 2350, 2350, 2350, 2350, 2350, 2350,
-
2350, 2350, 2350, 2350, 2350, 2350, 2350, 2350,
-
2350, 2350, 2350, 2350, 2350, 1, 1, 1,
-
2350, 2350, 2350, 2350, 2350, 2350, 2350, 2350,
-
2350, 2350, 2350, 2350, 2350, 2350, 2350, 2350,
-
2350, 2350, 2350, 2350, 2350, 2350, 2350, 2350,
-
2350, 2350, 2350, 2350, 2350, 2350, 2350, 2350,
-
2350, 1, 2352, 1, 1, 1, 2353, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 2352, 1, 1, 1, 1, 1, 1,
-
1, 2354, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 2331,
-
1, 2355, 1, 2352, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 2352, 1, 2356, 1, 1, 1,
-
2357, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 2356, 1, 1, 1, 1,
-
1, 1, 1, 2358, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 2338, 1, 2350, 2322, 2350, 2350, 2350,
-
2350, 2350, 1, 1, 2350, 2350, 1, 2350,
-
2324, 2350, 2350, 2350, 2350, 2350, 2350, 2350,
-
2350, 2350, 2350, 2350, 1, 1, 1, 2350,
-
1, 2350, 1, 2350, 2350, 2350, 2350, 2350,
-
2350, 2350, 2350, 2350, 2350, 2350, 2350, 2350,
-
2350, 2350, 2350, 2350, 2350, 2350, 2350, 2350,
-
2350, 2350, 2350, 2350, 2350, 1, 1, 1,
-
2350, 2350, 2350, 2350, 2350, 2350, 2350, 2350,
-
2350, 2350, 2350, 2350, 2350, 2350, 2350, 2350,
-
2350, 2350, 2350, 2350, 2350, 2350, 2350, 2350,
-
2350, 2350, 2350, 2350, 2350, 2350, 2350, 2350,
-
2350, 1, 2346, 2346, 2346, 2346, 2346, 2346,
-
2346, 2346, 2346, 2346, 2346, 2346, 2346, 2346,
-
2346, 2346, 2346, 2346, 2346, 2346, 2346, 2346,
-
2346, 2346, 2346, 2346, 2346, 2346, 2346, 2346,
-
2346, 2346, 2346, 2346, 2346, 2346, 2346, 2346,
-
2346, 2346, 2346, 2346, 2346, 2346, 2346, 2346,
-
2346, 2346, 2346, 2346, 2346, 2346, 2346, 2346,
-
2346, 2346, 2346, 2346, 2346, 2346, 2346, 2346,
-
2346, 2346, 2346, 2346, 2346, 2346, 2346, 2346,
-
2346, 2346, 2346, 2346, 2346, 2346, 2346, 2346,
-
2346, 2346, 2346, 2346, 2346, 2346, 2346, 2346,
-
2346, 2346, 2346, 2346, 2346, 2346, 2346, 2346,
-
2346, 2346, 2346, 2346, 2346, 2346, 2346, 2346,
-
2346, 2346, 2346, 2346, 2346, 2346, 2346, 2346,
-
2346, 2346, 2346, 2346, 2346, 2346, 2346, 2346,
-
2346, 2346, 2346, 2346, 2346, 2346, 2346, 2346,
-
2346, 2346, 1, 2359, 1, 2322, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 2322, 1, 2332, 1,
-
1, 1, 2339, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 2332, 2321, 2322,
-
2321, 2321, 2321, 2321, 2321, 2341, 1, 2321,
-
2321, 1, 2321, 2324, 2321, 2321, 2321, 2321,
-
2321, 2321, 2321, 2321, 2321, 2321, 2321, 1,
-
1, 1, 2321, 1, 2321, 1, 2321, 2321,
-
2321, 2321, 2321, 2321, 2321, 2321, 2321, 2321,
-
2321, 2321, 2321, 2321, 2321, 2321, 2321, 2321,
-
2321, 2321, 2321, 2321, 2321, 2321, 2321, 2321,
-
1, 1, 1, 2321, 2321, 2321, 2321, 2321,
-
2321, 2321, 2321, 2321, 2321, 2321, 2321, 2321,
-
2321, 2321, 2321, 2321, 2321, 2321, 2321, 2321,
-
2321, 2321, 2321, 2321, 2321, 2321, 2321, 2321,
-
2321, 2321, 2321, 2321, 1, 2360, 1, 1,
-
1, 2361, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 2360, 2362, 2363, 2362,
-
2362, 2362, 2362, 2362, 2364, 1, 2362, 2362,
-
1, 2362, 2365, 2362, 2362, 2362, 2362, 2362,
-
2362, 2362, 2362, 2362, 2362, 2362, 1, 1,
-
1, 2362, 1, 2362, 1, 2362, 2362, 2362,
-
2362, 2362, 2362, 2362, 2362, 2362, 2362, 2362,
-
2362, 2362, 2362, 2362, 2362, 2362, 2362, 2362,
-
2362, 2362, 2362, 2362, 2362, 2362, 2362, 2366,
-
1, 1, 2362, 2362, 2362, 2362, 2362, 2362,
-
2362, 2362, 2362, 2362, 2362, 2362, 2362, 2362,
-
2362, 2362, 2362, 2362, 2362, 2362, 2362, 2362,
-
2362, 2362, 2362, 2362, 2362, 2362, 2362, 2362,
-
2362, 2362, 2362, 1, 2325, 2325, 2325, 2325,
-
2325, 2325, 2325, 2325, 2325, 1, 2325, 2325,
-
2367, 2325, 2325, 2325, 2325, 2325, 2325, 2325,
-
2325, 2325, 2325, 2325, 2325, 2325, 2325, 2325,
-
2325, 2325, 2325, 2325, 2325, 2325, 2325, 2325,
-
2325, 2325, 2325, 2325, 2325, 2325, 2325, 2325,
-
2325, 2325, 2325, 2325, 2325, 2325, 2325, 2325,
-
2325, 2325, 2325, 2325, 2325, 2325, 2325, 2325,
-
2325, 2325, 2325, 2325, 2325, 2325, 2325, 2325,
-
2325, 2325, 2325, 2325, 2325, 2325, 2325, 2325,
-
2325, 2325, 2325, 2325, 2325, 2325, 2325, 2325,
-
2325, 2325, 2325, 2325, 2325, 2325, 1, 2368,
-
2352, 2325, 2325, 2325, 2325, 2325, 2325, 2325,
-
2325, 2325, 2325, 2325, 2325, 2325, 2325, 2325,
-
2325, 2325, 2325, 2325, 2325, 2325, 2325, 2325,
-
2325, 2325, 2325, 2325, 2325, 2325, 2325, 2325,
-
2325, 2325, 2325, 1, 2369, 1, 2325, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 2325, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 2325,
-
2370, 2370, 2370, 2370, 2370, 2370, 2370, 2370,
-
2370, 1, 2370, 2370, 2371, 2370, 2370, 2370,
-
2370, 2370, 2370, 2370, 2370, 2370, 2370, 2370,
-
2370, 2370, 2370, 2370, 2370, 2370, 2370, 2370,
-
2370, 2372, 2370, 2370, 2370, 2370, 2370, 2370,
-
2370, 2370, 2370, 2370, 2370, 2370, 2370, 2370,
-
2370, 2370, 2370, 2370, 2370, 2370, 2370, 2370,
-
2370, 2370, 2370, 2370, 2370, 2370, 2370, 2370,
-
2370, 2370, 2370, 2370, 2370, 2370, 2370, 2370,
-
2370, 2370, 2370, 2370, 2370, 2370, 2370, 2370,
-
2370, 2370, 2370, 2370, 2370, 2370, 2370, 2370,
-
2370, 2370, 2370, 2373, 2370, 2370, 2370, 2370,
-
2370, 2370, 2370, 2370, 2370, 2370, 2370, 2370,
-
2370, 2370, 2370, 2370, 2370, 2370, 2370, 2370,
-
2370, 2370, 2370, 2370, 2370, 2370, 2370, 2370,
-
2370, 2370, 2370, 2370, 2370, 2370, 2370, 1,
-
2374, 2374, 2374, 2374, 2374, 2374, 2374, 2374,
-
2374, 1, 2374, 2374, 2375, 2374, 2374, 2374,
-
2374, 2374, 2374, 2374, 2374, 2374, 2374, 2374,
-
2374, 2374, 2374, 2374, 2374, 2374, 2374, 2374,
-
2374, 2376, 2374, 2374, 2374, 2374, 2374, 2374,
-
2374, 2374, 2374, 2374, 2374, 2374, 2374, 2374,
-
2374, 2374, 2374, 2374, 2374, 2374, 2374, 2374,
-
2374, 2374, 2374, 2374, 2374, 2374, 2374, 2374,
-
2374, 2374, 2374, 2374, 2374, 2374, 2374, 2374,
-
2374, 2374, 2374, 2374, 2374, 2374, 2374, 2374,
-
2374, 2374, 2374, 2374, 2374, 2374, 2374, 2374,
-
2374, 2374, 2374, 2377, 2374, 2374, 2374, 2374,
-
2374, 2374, 2374, 2374, 2374, 2374, 2374, 2374,
-
2374, 2374, 2374, 2374, 2374, 2374, 2374, 2374,
-
2374, 2374, 2374, 2374, 2374, 2374, 2374, 2374,
-
2374, 2374, 2374, 2374, 2374, 2374, 2374, 1,
-
2378, 1, 2374, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 2374, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 2374, 81, 1, 1, 1,
-
2379, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 81, 2380, 2381, 2380, 2380,
-
2380, 2380, 2380, 2382, 1, 2380, 2380, 1,
-
2380, 1, 2380, 2380, 2380, 2380, 2380, 2380,
-
2380, 2380, 2380, 2380, 2380, 1, 1, 1,
-
2380, 1, 2380, 1, 2380, 2380, 2380, 2380,
-
2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380,
-
2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380,
-
2380, 2380, 2380, 2380, 2380, 2380, 1, 1,
-
1, 2380, 2380, 2380, 2380, 2380, 2380, 2380,
-
2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380,
-
2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380,
-
2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380,
-
2380, 2380, 1, 2383, 2383, 2383, 2383, 2383,
-
2383, 2383, 2383, 66, 1, 2383, 2383, 2384,
-
2383, 2383, 2383, 2383, 2383, 2383, 2383, 2383,
-
2383, 2383, 2383, 2383, 2383, 2383, 2383, 2383,
-
2383, 2383, 66, 2383, 1, 2383, 2383, 2383,
-
2383, 2383, 2383, 2383, 2383, 2383, 2383, 2383,
-
2383, 2383, 2383, 2383, 2383, 2383, 2383, 2383,
-
2383, 2383, 2383, 2383, 2383, 2383, 2383, 2383,
-
2383, 2383, 2383, 2383, 2383, 2383, 2383, 2383,
-
2383, 2383, 2383, 2383, 2383, 2383, 2383, 2383,
-
2383, 2383, 2383, 2383, 2383, 2383, 2383, 2383,
-
2383, 2383, 2383, 2383, 2383, 2383, 2385, 2383,
-
2383, 2383, 2383, 2383, 2383, 2383, 2383, 2383,
-
2383, 2383, 2383, 2383, 2383, 2383, 2383, 2383,
-
2383, 2383, 2383, 2383, 2383, 2383, 2383, 2383,
-
2383, 2383, 2383, 2383, 2383, 2383, 2383, 2383,
-
2383, 2383, 1, 2383, 2383, 2383, 2383, 2383,
-
2383, 2383, 2383, 2383, 1, 2383, 2383, 2386,
-
2383, 2383, 2383, 2383, 2383, 2383, 2383, 2383,
-
2383, 2383, 2383, 2383, 2383, 2383, 2383, 2383,
-
2383, 2383, 2383, 2383, 2387, 2383, 2383, 2383,
-
2383, 2383, 2383, 2383, 2383, 2383, 2383, 2383,
-
2383, 2383, 2383, 2383, 2383, 2383, 2383, 2383,
-
2383, 2383, 2383, 2383, 2383, 2383, 2383, 2383,
-
2383, 2383, 2383, 2383, 2383, 2383, 2383, 2383,
-
2383, 2383, 2383, 2383, 2383, 2383, 2383, 2383,
-
2383, 2383, 2383, 2383, 2383, 2383, 2383, 2383,
-
2383, 2383, 2383, 2383, 2383, 2383, 2385, 2383,
-
2383, 2383, 2383, 2383, 2383, 2383, 2383, 2383,
-
2383, 2383, 2383, 2383, 2383, 2383, 2383, 2383,
-
2383, 2383, 2383, 2383, 2383, 2383, 2383, 2383,
-
2383, 2383, 2383, 2383, 2383, 2383, 2383, 2383,
-
2383, 2383, 1, 2388, 1, 2383, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 2383, 1, 2389, 1,
-
1, 1, 2390, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 2389, 2387, 66,
-
2387, 2387, 2387, 2387, 2387, 2391, 1, 2387,
-
2387, 1, 2387, 2387, 2387, 2387, 2387, 2387,
-
2387, 2387, 2387, 2387, 2387, 2387, 2387, 1,
-
1, 1, 2387, 69, 2387, 70, 2387, 2387,
-
2387, 2387, 2387, 2387, 2387, 2387, 2387, 2387,
-
2387, 2387, 2387, 2387, 2387, 2387, 2387, 2387,
-
2387, 2387, 2387, 2387, 2387, 2387, 2387, 2387,
-
1, 1, 1, 2387, 2387, 2387, 2387, 2387,
-
2387, 2387, 2387, 2387, 2387, 2387, 2387, 2387,
-
2387, 2387, 2387, 2387, 2387, 2387, 2387, 2387,
-
2387, 2387, 2387, 2387, 2387, 2387, 2387, 2387,
-
2387, 2387, 2387, 2387, 1, 2392, 1, 1,
-
1, 2393, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 2392, 1, 1, 1,
-
1, 1, 1, 1, 2394, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 75, 1, 76, 1, 2395, 1,
-
2392, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 2392,
-
1, 2396, 1, 1, 1, 2397, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
2396, 1, 1, 1, 1, 1, 1, 1,
-
2398, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 82, 1,
-
83, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 2383, 2399, 1, 66, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 66, 1, 2400, 1,
-
1, 1, 2401, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 2400, 65, 2402,
-
65, 65, 65, 65, 65, 2403, 1, 65,
-
65, 1, 65, 2387, 65, 65, 65, 65,
-
65, 65, 65, 65, 65, 65, 65, 1,
-
1, 1, 65, 69, 65, 70, 65, 65,
-
65, 65, 65, 65, 65, 65, 65, 65,
-
65, 65, 65, 65, 65, 65, 65, 65,
-
65, 65, 65, 65, 65, 65, 65, 65,
-
1, 1, 1, 65, 65, 65, 65, 65,
-
65, 65, 65, 65, 65, 65, 65, 65,
-
65, 65, 65, 65, 65, 65, 65, 65,
-
65, 65, 65, 65, 65, 65, 65, 65,
-
65, 65, 65, 65, 1, 2404, 1, 1,
-
1, 2405, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 2404, 85, 86, 85,
-
85, 85, 85, 85, 2406, 1, 85, 85,
-
1, 85, 1, 85, 85, 85, 85, 85,
-
85, 85, 85, 85, 85, 85, 1, 1,
-
1, 85, 75, 85, 76, 85, 85, 85,
-
85, 85, 85, 85, 85, 85, 85, 85,
-
85, 85, 85, 85, 85, 85, 85, 85,
-
85, 85, 85, 85, 85, 85, 85, 1,
-
1, 1, 85, 85, 85, 85, 85, 85,
-
85, 85, 85, 85, 85, 85, 85, 85,
-
85, 85, 85, 85, 85, 85, 85, 85,
-
85, 85, 85, 85, 85, 85, 85, 85,
-
85, 85, 85, 1, 2407, 1, 2404, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 2404, 1, 2408,
-
1, 1, 1, 2409, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 2408, 2380,
-
2381, 2380, 2380, 2380, 2380, 2380, 2410, 1,
-
2380, 2380, 1, 2380, 1, 2380, 2380, 2380,
-
2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380,
-
1, 1, 1, 2380, 82, 2380, 83, 2380,
-
2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380,
-
2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380,
-
2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380,
-
2380, 1, 1, 1, 2380, 2380, 2380, 2380,
-
2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380,
-
2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380,
-
2380, 2380, 2380, 2380, 2380, 2380, 2380, 2380,
-
2380, 2380, 2380, 2380, 2380, 1, 2411, 2411,
-
2411, 2411, 2411, 2411, 2411, 2411, 2412, 1,
-
2411, 2411, 2413, 2411, 2411, 2411, 2411, 2411,
-
2411, 2411, 2411, 2411, 2411, 2411, 2411, 2411,
-
2411, 2411, 2411, 2411, 2411, 2412, 2411, 2372,
-
2411, 2411, 2411, 2411, 2411, 2411, 2411, 2411,
-
2411, 2411, 2411, 2411, 2411, 2411, 2411, 2411,
-
2411, 2411, 2411, 2411, 2411, 2411, 2411, 2411,
-
2411, 2411, 2411, 2411, 2411, 2411, 2411, 2411,
-
2411, 2411, 2411, 2411, 2411, 2411, 2411, 2411,
-
2411, 2411, 2411, 2411, 2411, 2411, 2411, 2411,
-
2411, 2411, 2411, 2411, 2411, 2411, 2411, 2411,
-
2411, 2414, 2411, 2411, 2411, 2411, 2411, 2411,
-
2411, 2411, 2411, 2411, 2411, 2411, 2411, 2411,
-
2411, 2411, 2411, 2411, 2411, 2411, 2411, 2411,
-
2411, 2411, 2411, 2411, 2411, 2411, 2411, 2411,
-
2411, 2411, 2411, 2411, 2411, 1, 2415, 2415,
-
2415, 2415, 2415, 2415, 2415, 2415, 2415, 1,
-
2415, 2415, 2416, 2415, 2415, 2415, 2415, 2415,
-
2415, 2415, 2415, 2415, 2415, 2415, 2415, 2415,
-
2415, 2415, 2415, 2415, 2415, 2415, 2415, 2417,
-
2415, 2415, 2415, 2415, 2415, 2415, 2415, 2415,
-
2415, 2415, 2415, 2415, 2415, 2415, 2415, 2415,
-
2415, 2415, 2415, 2415, 2415, 2415, 2415, 2415,
-
2415, 2415, 2415, 2415, 2415, 2415, 2415, 2415,
-
2415, 2415, 2415, 2415, 2415, 2415, 2415, 2415,
-
2415, 2415, 2415, 2415, 2415, 2415, 2415, 2415,
-
2415, 2415, 2415, 2415, 2415, 2415, 2415, 2415,
-
2415, 2418, 2415, 2415, 2415, 2415, 2415, 2415,
-
2415, 2415, 2415, 2415, 2415, 2415, 2415, 2415,
-
2415, 2415, 2415, 2415, 2415, 2415, 2415, 2415,
-
2415, 2415, 2415, 2415, 2415, 2415, 2415, 2415,
-
2415, 2415, 2415, 2415, 2415, 1, 2419, 1,
-
2415, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 2415,
-
1, 63, 1, 1, 1, 64, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
63, 2387, 66, 2387, 2387, 2387, 2387, 2387,
-
67, 1, 2387, 2387, 1, 2387, 68, 2387,
-
2387, 2387, 2387, 2387, 2387, 2387, 2387, 2387,
-
2387, 2387, 1, 1, 1, 2387, 69, 2387,
-
70, 2387, 2387, 2387, 2387, 2387, 2387, 2387,
-
2387, 2387, 2387, 2387, 2387, 2387, 2387, 2387,
-
2387, 2387, 2387, 2387, 2387, 2387, 2387, 2387,
-
2387, 2387, 2387, 1, 1, 1, 2387, 2387,
-
2387, 2387, 2387, 2387, 2387, 2387, 2387, 2387,
-
2387, 2387, 2387, 2387, 2387, 2387, 2387, 2387,
-
2387, 2387, 2387, 2387, 2387, 2387, 2387, 2387,
-
2387, 2387, 2387, 2387, 2387, 2387, 2387, 1,
-
2415, 2415, 2415, 2415, 2415, 2415, 2415, 2415,
-
2415, 2415, 2415, 2415, 2415, 2415, 2415, 2415,
-
2415, 2415, 2415, 2415, 2415, 2415, 2415, 2415,
-
2415, 2415, 2415, 2415, 2415, 2415, 2415, 2415,
-
2415, 2415, 2415, 2415, 2415, 2415, 2415, 2415,
-
2415, 2415, 2415, 2415, 2415, 2415, 2415, 2415,
-
2415, 2415, 2415, 2415, 2415, 2415, 2415, 2415,
-
2415, 2415, 2415, 2415, 2415, 2415, 2415, 2415,
-
2415, 2415, 2415, 2415, 2415, 2415, 2415, 2415,
-
2415, 2415, 2415, 2415, 2415, 2415, 2415, 2415,
-
2415, 2415, 2415, 2415, 2415, 2415, 2415, 2415,
-
2415, 2415, 2415, 2415, 2415, 2415, 2415, 2415,
-
2415, 2415, 2415, 2415, 2415, 2415, 2415, 2415,
-
2415, 2415, 2415, 2415, 2415, 2415, 2415, 2415,
-
2415, 2415, 2415, 2415, 2415, 2415, 2415, 2415,
-
2415, 2415, 2415, 2415, 2415, 2415, 2415, 2415,
-
1, 2415, 2415, 2415, 2415, 2415, 2415, 2415,
-
2415, 2420, 1, 2415, 2415, 2421, 2415, 2415,
-
2415, 2415, 2415, 2415, 2415, 2415, 2415, 2415,
-
2415, 2415, 2415, 2415, 2415, 2415, 2415, 2415,
-
2420, 2415, 2376, 2415, 2415, 2415, 2415, 2415,
-
2415, 2415, 2415, 2415, 2415, 2415, 2415, 2415,
-
2415, 2415, 2415, 2415, 2415, 2415, 2415, 2415,
-
2415, 2415, 2415, 2415, 2415, 2415, 2415, 2415,
-
2415, 2415, 2415, 2415, 2415, 2415, 2415, 2415,
-
2415, 2415, 2415, 2415, 2415, 2415, 2415, 2415,
-
2415, 2415, 2415, 2415, 2415, 2415, 2415, 2415,
-
2415, 2415, 2415, 2415, 2418, 2415, 2415, 2415,
-
2415, 2415, 2415, 2415, 2415, 2415, 2415, 2415,
-
2415, 2415, 2415, 2415, 2415, 2415, 2415, 2415,
-
2415, 2415, 2415, 2415, 2415, 2415, 2415, 2415,
-
2415, 2415, 2415, 2415, 2415, 2415, 2415, 2415,
-
1, 2422, 1, 2420, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 2420, 1, 2423, 2423, 2423, 2423,
-
2423, 2423, 2423, 2423, 2424, 1, 2423, 2423,
-
2425, 2423, 2423, 2423, 2423, 2423, 2423, 2423,
-
2423, 2423, 2423, 2423, 2423, 2423, 2423, 2423,
-
2423, 2423, 2423, 2424, 2423, 2426, 2423, 2423,
-
2423, 2423, 2423, 2423, 2423, 2423, 2423, 2423,
-
2423, 2423, 2423, 2423, 2423, 2423, 2423, 2423,
-
2423, 2423, 2423, 2423, 2423, 2423, 2423, 2423,
-
2423, 2423, 2423, 2423, 2423, 2423, 2423, 2423,
-
2423, 2423, 2423, 2423, 2423, 2423, 2423, 2423,
-
2423, 2423, 2423, 2423, 2423, 2423, 2423, 2423,
-
2423, 2423, 2423, 2423, 2423, 2423, 2423, 2427,
-
2423, 2423, 2423, 2423, 2423, 2423, 2423, 2423,
-
2423, 2423, 2423, 2423, 2423, 2423, 2423, 2423,
-
2423, 2423, 2423, 2423, 2423, 2423, 2423, 2423,
-
2423, 2423, 2423, 2423, 2423, 2423, 2423, 2423,
-
2423, 2423, 2423, 1, 2428, 2428, 2428, 2428,
-
2428, 2428, 2428, 2428, 2428, 1, 2428, 2428,
-
2429, 2428, 2428, 2428, 2428, 2428, 2428, 2428,
-
2428, 2428, 2428, 2428, 2428, 2428, 2428, 2428,
-
2428, 2428, 2428, 2428, 2428, 2430, 2428, 2428,
-
2428, 2428, 2428, 2428, 2428, 2428, 2428, 2428,
-
2428, 2428, 2428, 2428, 2428, 2428, 2428, 2428,
-
2428, 2428, 2428, 2428, 2428, 2428, 2428, 2428,
-
2428, 2428, 2428, 2428, 2428, 2428, 2428, 2428,
-
2428, 2428, 2428, 2428, 2428, 2428, 2428, 2428,
-
2428, 2428, 2428, 2428, 2428, 2428, 2428, 2428,
-
2428, 2428, 2428, 2428, 2428, 2428, 2428, 2431,
-
2428, 2428, 2428, 2428, 2428, 2428, 2428, 2428,
-
2428, 2428, 2428, 2428, 2428, 2428, 2428, 2428,
-
2428, 2428, 2428, 2428, 2428, 2428, 2428, 2428,
-
2428, 2428, 2428, 2428, 2428, 2428, 2428, 2428,
-
2428, 2428, 2428, 1, 2432, 1, 2428, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 2428, 1, 2433,
-
1, 1, 1, 2434, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 2433, 2387,
-
66, 2387, 2387, 2387, 2387, 2387, 2435, 1,
-
2387, 2387, 1, 2387, 68, 2387, 2387, 2387,
-
2387, 2387, 2387, 2387, 2387, 2387, 2387, 2387,
-
1, 1, 1, 2387, 69, 2387, 2436, 2387,
-
2387, 2387, 2387, 2387, 2387, 2387, 2387, 2387,
-
2387, 2387, 2387, 2387, 2387, 2387, 2387, 2387,
-
2387, 2387, 2387, 2387, 2387, 2387, 2387, 2387,
-
2387, 1, 1, 1, 2387, 2387, 2387, 2387,
-
2387, 2387, 2387, 2387, 2387, 2387, 2387, 2387,
-
2387, 2387, 2387, 2387, 2387, 2387, 2387, 2387,
-
2387, 2387, 2387, 2387, 2387, 2387, 2387, 2387,
-
2387, 2387, 2387, 2387, 2387, 1, 2437, 1,
-
1, 1, 2438, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 2437, 1, 1,
-
1, 1, 1, 1, 1, 2439, 1, 1,
-
1, 1, 1, 74, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 75, 1, 2440, 1, 2441,
-
1, 2437, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
2437, 1, 2442, 1, 1, 1, 2443, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 2442, 1, 1, 1, 1, 1, 1,
-
1, 2444, 1, 1, 1, 1, 1, 81,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 82,
-
1, 2445, 1, 2428, 2428, 2428, 2428, 2428,
-
2428, 2428, 2428, 2428, 2428, 2428, 2428, 2428,
-
2428, 2428, 2428, 2428, 2428, 2428, 2428, 2428,
-
2428, 2428, 2428, 2428, 2428, 2428, 2428, 2428,
-
2428, 2428, 2428, 2428, 2428, 2428, 2428, 2428,
-
2428, 2428, 2428, 2428, 2428, 2428, 2428, 2428,
-
2428, 2428, 2428, 2428, 2428, 2428, 2428, 2428,
-
2428, 2428, 2428, 2428, 2428, 2428, 2428, 2428,
-
2428, 2428, 2428, 2428, 2428, 2428, 2428, 2428,
-
2428, 2428, 2428, 2428, 2428, 2428, 2428, 2428,
-
2428, 2428, 2428, 2428, 2428, 2428, 2428, 2428,
-
2428, 2428, 2428, 2428, 2428, 2428, 2428, 2428,
-
2428, 2428, 2428, 2428, 2428, 2428, 2428, 2428,
-
2428, 2428, 2428, 2428, 2428, 2428, 2428, 2428,
-
2428, 2428, 2428, 2428, 2428, 2428, 2428, 2428,
-
2428, 2428, 2428, 2428, 2428, 2428, 2428, 2428,
-
2428, 2428, 2428, 1, 2428, 2428, 2428, 2428,
-
2428, 2428, 2428, 2428, 2446, 1, 2428, 2428,
-
2447, 2428, 2428, 2428, 2428, 2428, 2428, 2428,
-
2428, 2428, 2428, 2428, 2428, 2428, 2428, 2428,
-
2428, 2428, 2428, 2446, 2428, 2448, 2428, 2428,
-
2428, 2428, 2428, 2428, 2428, 2428, 2428, 2428,
-
2428, 2428, 2428, 2428, 2428, 2428, 2428, 2428,
-
2428, 2428, 2428, 2428, 2428, 2428, 2428, 2428,
-
2428, 2428, 2428, 2428, 2428, 2428, 2428, 2428,
-
2428, 2428, 2428, 2428, 2428, 2428, 2428, 2428,
-
2428, 2428, 2428, 2428, 2428, 2428, 2428, 2428,
-
2428, 2428, 2428, 2428, 2428, 2428, 2428, 2431,
-
2428, 2428, 2428, 2428, 2428, 2428, 2428, 2428,
-
2428, 2428, 2428, 2428, 2428, 2428, 2428, 2428,
-
2428, 2428, 2428, 2428, 2428, 2428, 2428, 2428,
-
2428, 2428, 2428, 2428, 2428, 2428, 2428, 2428,
-
2428, 2428, 2428, 1, 2449, 1, 2446, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 2446, 1, 2450,
-
1, 1, 1, 2451, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 2450, 1,
-
1, 1, 1, 1, 1, 1, 2452, 1,
-
1, 1, 1, 1, 74, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 75, 1, 2453, 1,
-
2454, 1, 2450, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 2450, 1, 2455, 1, 1, 1, 2456,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 2455, 1, 1, 1, 1, 1,
-
1, 1, 2457, 1, 1, 1, 1, 1,
-
81, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
82, 1, 2458, 1, 2459, 1, 1, 1,
-
2460, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 2459, 2461, 2462, 2461, 2461,
-
2461, 2461, 2461, 2463, 1, 2461, 2461, 2464,
-
2461, 2465, 2461, 2461, 2461, 2461, 2461, 2461,
-
2461, 2461, 2461, 2461, 2461, 1, 1, 1,
-
2461, 1, 2461, 2466, 2461, 2461, 2461, 2461,
-
2461, 2461, 2461, 2461, 2461, 2461, 2461, 2461,
-
2461, 2461, 2461, 2461, 2461, 2461, 2461, 2461,
-
2461, 2461, 2461, 2461, 2461, 2461, 1, 1,
-
1, 2461, 2461, 2461, 2461, 2461, 2461, 2461,
-
2461, 2461, 2461, 2461, 2461, 2461, 2461, 2461,
-
2461, 2461, 2461, 2461, 2461, 2461, 2461, 2461,
-
2461, 2461, 2461, 2461, 2461, 2461, 2461, 2461,
-
2461, 2461, 1, 60, 1, 1, 1, 2467,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 60, 1, 1, 1, 1, 1,
-
1, 1, 2468, 1, 1, 1, 60, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 61, 1, 2469, 1, 60, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 60, 1, 2464,
-
1, 1, 1, 2470, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 2464, 1,
-
1, 1, 1, 1, 1, 1, 2471, 1,
-
1, 1, 2464, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 2466, 1,
-
2472, 1, 1, 1, 2473, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 2472,
-
2474, 2475, 2474, 2474, 2474, 2474, 2474, 2476,
-
1, 2474, 2474, 1, 2474, 2477, 2474, 2474,
-
2474, 2474, 2474, 2474, 2474, 2474, 2474, 2474,
-
2474, 1, 1, 1, 2474, 1, 2474, 1,
-
2474, 2474, 2474, 2474, 2474, 2474, 2474, 2474,
-
2474, 2474, 2474, 2474, 2474, 2474, 2474, 2474,
-
2474, 2474, 2474, 2474, 2474, 2474, 2474, 2474,
-
2474, 2474, 2478, 1, 1, 2474, 2474, 2474,
-
2474, 2474, 2474, 2474, 2474, 2474, 2474, 2474,
-
2474, 2474, 2474, 2474, 2474, 2474, 2474, 2474,
-
2474, 2474, 2474, 2474, 2474, 2474, 2474, 2474,
-
2474, 2474, 2474, 2474, 2474, 2474, 1, 2479,
-
1, 1, 1, 2480, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 2479, 2481,
-
2482, 2481, 2481, 2481, 2481, 2481, 2483, 1,
-
2481, 2481, 1, 2481, 2484, 2481, 2481, 2481,
-
2481, 2481, 2481, 2481, 2481, 2481, 2481, 2481,
-
1, 1, 1, 2481, 1, 2481, 1, 2481,
-
2481, 2481, 2481, 2481, 2481, 2481, 2481, 2481,
-
2481, 2481, 2481, 2481, 2481, 2481, 2481, 2481,
-
2481, 2481, 2481, 2481, 2481, 2481, 2481, 2481,
-
2481, 2485, 1, 1, 2481, 2481, 2481, 2481,
-
2481, 2481, 2481, 2481, 2481, 2481, 2481, 2481,
-
2481, 2481, 2481, 2481, 2481, 2481, 2481, 2481,
-
2481, 2481, 2481, 2481, 2481, 2481, 2481, 2481,
-
2481, 2481, 2481, 2481, 2481, 1, 2486, 1,
-
2479, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 2479,
-
1, 2487, 1, 1, 1, 2488, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
2487, 2481, 2482, 2481, 2481, 2481, 2481, 2481,
-
2489, 1, 2481, 2481, 2490, 2481, 2491, 2481,
-
2481, 2481, 2481, 2481, 2481, 2481, 2481, 2481,
-
2481, 2481, 2492, 1, 1, 2481, 1, 2481,
-
1, 2481, 2481, 2481, 2481, 2481, 2481, 2481,
-
2481, 2481, 2481, 2481, 2481, 2481, 2481, 2481,
-
2481, 2481, 2481, 2481, 2481, 2481, 2481, 2481,
-
2481, 2481, 2481, 1, 1, 1, 2481, 2481,
-
2481, 2481, 2481, 2481, 2481, 2481, 2481, 2481,
-
2481, 2481, 2481, 2481, 2481, 2481, 2481, 2481,
-
2481, 2481, 2481, 2481, 2481, 2481, 2481, 2481,
-
2481, 2481, 2481, 2481, 2481, 2481, 2481, 1,
-
2487, 1, 1, 1, 2488, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 2487,
-
1, 1, 1, 1, 1, 1, 1, 2489,
-
1, 1, 1, 2490, 1, 2493, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 2492, 1, 2494, 1, 2487, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 2487, 1, 2495, 1,
-
1, 1, 2496, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 2495, 1, 1,
-
1, 1, 1, 1, 1, 2497, 1, 1,
-
1, 2498, 1, 2499, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 2500,
-
1, 2501, 1, 1, 1, 2502, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
2501, 1, 1, 1, 1, 1, 1, 1,
-
2503, 1, 1, 1, 2501, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 2504, 1, 1, 1, 1, 1,
-
61, 1, 2505, 1, 2501, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 2501, 1, 2506, 1, 1,
-
1, 2507, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 2506, 1, 1, 1,
-
1, 1, 1, 1, 2508, 1, 1, 1,
-
2506, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 2509, 1,
-
1, 1, 1, 1, 2466, 1, 2510, 1,
-
1, 1, 2511, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 2510, 2512, 2513,
-
2512, 2512, 2512, 2512, 2512, 2514, 1, 2512,
-
2512, 1, 2512, 2515, 2512, 2512, 2512, 2512,
-
2512, 2512, 2512, 2512, 2512, 2512, 2512, 1,
-
1, 1, 2512, 1, 2512, 1, 2512, 2512,
-
2512, 2512, 2512, 2512, 2512, 2512, 2512, 2512,
-
2512, 2512, 2512, 2512, 2512, 2512, 2512, 2512,
-
2512, 2512, 2512, 2512, 2512, 2512, 2512, 2512,
-
1, 1, 1, 2512, 2512, 2512, 2512, 2512,
-
2512, 2512, 2512, 2512, 2512, 2512, 2512, 2512,
-
2512, 2512, 2512, 2512, 2512, 2512, 2512, 2512,
-
2512, 2512, 2512, 2512, 2512, 2512, 2512, 2512,
-
2512, 2512, 2512, 2512, 1, 2516, 1, 1,
-
1, 2517, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 2516, 51, 52, 51,
-
51, 51, 51, 51, 2518, 1, 51, 51,
-
1, 51, 55, 51, 51, 51, 51, 51,
-
51, 51, 51, 51, 51, 51, 1, 1,
-
1, 51, 1, 51, 1, 51, 51, 51,
-
51, 51, 51, 51, 51, 51, 51, 51,
-
51, 51, 51, 51, 51, 51, 51, 51,
-
51, 51, 51, 51, 51, 51, 51, 1,
-
1, 1, 51, 51, 51, 51, 51, 51,
-
51, 51, 51, 51, 51, 51, 51, 51,
-
51, 51, 51, 51, 51, 51, 51, 51,
-
51, 51, 51, 51, 51, 51, 51, 51,
-
51, 51, 51, 1, 2519, 1, 2516, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 2516, 1, 2520,
-
1, 1, 1, 2521, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 2520, 2461,
-
2462, 2461, 2461, 2461, 2461, 2461, 2522, 1,
-
2461, 2461, 1, 2461, 2465, 2461, 2461, 2461,
-
2461, 2461, 2461, 2461, 2461, 2461, 2461, 2461,
-
1, 1, 1, 2461, 1, 2461, 1, 2461,
-
2461, 2461, 2461, 2461, 2461, 2461, 2461, 2461,
-
2461, 2461, 2461, 2461, 2461, 2461, 2461, 2461,
-
2461, 2461, 2461, 2461, 2461, 2461, 2461, 2461,
-
2461, 1, 1, 1, 2461, 2461, 2461, 2461,
-
2461, 2461, 2461, 2461, 2461, 2461, 2461, 2461,
-
2461, 2461, 2461, 2461, 2461, 2461, 2461, 2461,
-
2461, 2461, 2461, 2461, 2461, 2461, 2461, 2461,
-
2461, 2461, 2461, 2461, 2461, 1, 2387, 66,
-
2387, 2387, 2387, 2387, 2387, 1, 1, 2387,
-
2387, 1, 2387, 2523, 2387, 2387, 2387, 2387,
-
2387, 2387, 2387, 2387, 2387, 2387, 2387, 1,
-
1, 1, 2387, 1, 2387, 1, 2387, 2387,
-
2387, 2387, 2387, 2387, 2387, 2387, 2387, 2387,
-
2387, 2387, 2387, 2387, 2387, 2387, 2387, 2387,
-
2387, 2387, 2387, 2387, 2387, 2387, 2387, 2387,
-
1, 1, 1, 2387, 2387, 2387, 2387, 2387,
-
2387, 2387, 2387, 2387, 2387, 2387, 2387, 2387,
-
2387, 2387, 2387, 2387, 2387, 2387, 2387, 2387,
-
2387, 2387, 2387, 2387, 2387, 2387, 2387, 2387,
-
2387, 2387, 2387, 2387, 1, 2493, 1, 1,
-
1, 2524, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 2493, 2525, 1, 2525,
-
2525, 2525, 2525, 2525, 2526, 1, 2525, 2525,
-
1, 2525, 1, 2525, 2525, 2525, 2525, 2525,
-
2525, 2525, 2525, 2525, 2525, 2525, 1, 1,
-
1, 2525, 1, 2525, 1, 2525, 2525, 2525,
-
2525, 2525, 2525, 2525, 2525, 2525, 2525, 2525,
-
2525, 2525, 2525, 2525, 2525, 2525, 2525, 2525,
-
2525, 2525, 2525, 2525, 2525, 2525, 2525, 1,
-
1, 1, 2525, 2525, 2525, 2525, 2525, 2525,
-
2525, 2525, 2525, 2525, 2525, 2525, 2525, 2525,
-
2525, 2525, 2525, 2525, 2525, 2525, 2525, 2525,
-
2525, 2525, 2525, 2525, 2525, 2525, 2525, 2525,
-
2525, 2525, 2525, 1, 2527, 1, 2493, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 2493, 1, 2487,
-
1, 1, 1, 2488, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 2487, 2525,
-
1, 2525, 2525, 2525, 2525, 2525, 2489, 1,
-
2525, 2525, 2490, 2525, 2493, 2525, 2525, 2525,
-
2525, 2525, 2525, 2525, 2525, 2525, 2525, 2525,
-
2492, 1, 1, 2525, 1, 2525, 1, 2525,
-
2525, 2525, 2525, 2525, 2525, 2525, 2525, 2525,
-
2525, 2525, 2525, 2525, 2525, 2525, 2525, 2525,
-
2525, 2525, 2525, 2525, 2525, 2525, 2525, 2525,
-
2525, 1, 1, 1, 2525, 2525, 2525, 2525,
-
2525, 2525, 2525, 2525, 2525, 2525, 2525, 2525,
-
2525, 2525, 2525, 2525, 2525, 2525, 2525, 2525,
-
2525, 2525, 2525, 2525, 2525, 2525, 2525, 2525,
-
2525, 2525, 2525, 2525, 2525, 1, 2499, 1,
-
1, 1, 2528, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 2499, 2529, 1,
-
2529, 2529, 2529, 2529, 2529, 2530, 1, 2529,
-
2529, 1, 2529, 1, 2529, 2529, 2529, 2529,
-
2529, 2529, 2529, 2529, 2529, 2529, 2529, 1,
-
1, 1, 2529, 1, 2529, 1, 2529, 2529,
-
2529, 2529, 2529, 2529, 2529, 2529, 2529, 2529,
-
2529, 2529, 2529, 2529, 2529, 2529, 2529, 2529,
-
2529, 2529, 2529, 2529, 2529, 2529, 2529, 2529,
-
1, 1, 1, 2529, 2529, 2529, 2529, 2529,
-
2529, 2529, 2529, 2529, 2529, 2529, 2529, 2529,
-
2529, 2529, 2529, 2529, 2529, 2529, 2529, 2529,
-
2529, 2529, 2529, 2529, 2529, 2529, 2529, 2529,
-
2529, 2529, 2529, 2529, 1, 2531, 2531, 2531,
-
2531, 2531, 2531, 2531, 2531, 2482, 1, 2531,
-
2531, 2532, 2531, 2531, 2531, 2531, 2531, 2531,
-
2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531,
-
2531, 2531, 2531, 2531, 2482, 2531, 1, 2531,
-
2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531,
-
2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531,
-
2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531,
-
2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531,
-
2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531,
-
2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531,
-
2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531,
-
2533, 2531, 2531, 2531, 2531, 2531, 2531, 2531,
-
2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531,
-
2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531,
-
2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531,
-
2531, 2531, 2531, 2531, 1, 2531, 2531, 2531,
-
2531, 2531, 2531, 2531, 2531, 2531, 1, 2531,
-
2531, 2534, 2531, 2531, 2531, 2531, 2531, 2531,
-
2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531,
-
2531, 2531, 2531, 2531, 2531, 2531, 2535, 2531,
-
2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531,
-
2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531,
-
2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531,
-
2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531,
-
2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531,
-
2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531,
-
2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531,
-
2533, 2531, 2531, 2531, 2531, 2531, 2531, 2531,
-
2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531,
-
2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531,
-
2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531,
-
2531, 2531, 2531, 2531, 1, 2536, 1, 2531,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 2531, 1,
-
2537, 1, 1, 1, 2538, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 2537,
-
2535, 2482, 2535, 2535, 2535, 2535, 2535, 2539,
-
1, 2535, 2535, 2490, 2535, 2484, 2535, 2535,
-
2535, 2535, 2535, 2535, 2535, 2535, 2535, 2535,
-
2535, 2492, 1, 1, 2535, 1, 2535, 1,
-
2535, 2535, 2535, 2535, 2535, 2535, 2535, 2535,
-
2535, 2535, 2535, 2535, 2535, 2535, 2535, 2535,
-
2535, 2535, 2535, 2535, 2535, 2535, 2535, 2535,
-
2535, 2535, 1, 1, 1, 2535, 2535, 2535,
-
2535, 2535, 2535, 2535, 2535, 2535, 2535, 2535,
-
2535, 2535, 2535, 2535, 2535, 2535, 2535, 2535,
-
2535, 2535, 2535, 2535, 2535, 2535, 2535, 2535,
-
2535, 2535, 2535, 2535, 2535, 2535, 1, 2537,
-
1, 1, 1, 2538, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 2537, 1,
-
1, 1, 1, 1, 1, 1, 2539, 1,
-
1, 1, 2490, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
2492, 1, 2540, 1, 2537, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 2537, 1, 2541, 1, 1,
-
1, 2542, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 2541, 1, 1, 1,
-
1, 1, 1, 1, 2543, 1, 1, 1,
-
2498, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 2500, 1,
-
2535, 2482, 2535, 2535, 2535, 2535, 2535, 1,
-
1, 2535, 2535, 1, 2535, 2484, 2535, 2535,
-
2535, 2535, 2535, 2535, 2535, 2535, 2535, 2535,
-
2535, 1, 1, 1, 2535, 1, 2535, 1,
-
2535, 2535, 2535, 2535, 2535, 2535, 2535, 2535,
-
2535, 2535, 2535, 2535, 2535, 2535, 2535, 2535,
-
2535, 2535, 2535, 2535, 2535, 2535, 2535, 2535,
-
2535, 2535, 1, 1, 1, 2535, 2535, 2535,
-
2535, 2535, 2535, 2535, 2535, 2535, 2535, 2535,
-
2535, 2535, 2535, 2535, 2535, 2535, 2535, 2535,
-
2535, 2535, 2535, 2535, 2535, 2535, 2535, 2535,
-
2535, 2535, 2535, 2535, 2535, 2535, 1, 2531,
-
2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531,
-
2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531,
-
2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531,
-
2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531,
-
2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531,
-
2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531,
-
2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531,
-
2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531,
-
2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531,
-
2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531,
-
2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531,
-
2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531,
-
2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531,
-
2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531,
-
2531, 2531, 2531, 2531, 2531, 2531, 2531, 2531,
-
2531, 2531, 2531, 2531, 2531, 2531, 2531, 1,
-
2544, 1, 2482, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 2482, 1, 2493, 1, 1, 1, 2524,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 2493, 2481, 2482, 2481, 2481, 2481,
-
2481, 2481, 2526, 1, 2481, 2481, 1, 2481,
-
2484, 2481, 2481, 2481, 2481, 2481, 2481, 2481,
-
2481, 2481, 2481, 2481, 1, 1, 1, 2481,
-
1, 2481, 1, 2481, 2481, 2481, 2481, 2481,
-
2481, 2481, 2481, 2481, 2481, 2481, 2481, 2481,
-
2481, 2481, 2481, 2481, 2481, 2481, 2481, 2481,
-
2481, 2481, 2481, 2481, 2481, 1, 1, 1,
-
2481, 2481, 2481, 2481, 2481, 2481, 2481, 2481,
-
2481, 2481, 2481, 2481, 2481, 2481, 2481, 2481,
-
2481, 2481, 2481, 2481, 2481, 2481, 2481, 2481,
-
2481, 2481, 2481, 2481, 2481, 2481, 2481, 2481,
-
2481, 1, 2545, 1, 1, 1, 2546, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 2545, 2547, 2548, 2547, 2547, 2547, 2547,
-
2547, 2549, 1, 2547, 2547, 1, 2547, 2550,
-
2547, 2547, 2547, 2547, 2547, 2547, 2547, 2547,
-
2547, 2547, 2547, 1, 1, 1, 2547, 1,
-
2547, 1, 2547, 2547, 2547, 2547, 2547, 2547,
-
2547, 2547, 2547, 2547, 2547, 2547, 2547, 2547,
-
2547, 2547, 2547, 2547, 2547, 2547, 2547, 2547,
-
2547, 2547, 2547, 2547, 2551, 1, 1, 2547,
-
2547, 2547, 2547, 2547, 2547, 2547, 2547, 2547,
-
2547, 2547, 2547, 2547, 2547, 2547, 2547, 2547,
-
2547, 2547, 2547, 2547, 2547, 2547, 2547, 2547,
-
2547, 2547, 2547, 2547, 2547, 2547, 2547, 2547,
-
1, 2485, 2485, 2485, 2485, 2485, 2485, 2485,
-
2485, 2485, 1, 2485, 2485, 2552, 2485, 2485,
-
2485, 2485, 2485, 2485, 2485, 2485, 2485, 2485,
-
2485, 2485, 2485, 2485, 2485, 2485, 2485, 2485,
-
2485, 2485, 2485, 2485, 2485, 2485, 2485, 2485,
-
2485, 2485, 2485, 2485, 2485, 2485, 2485, 2485,
-
2485, 2485, 2485, 2485, 2485, 2485, 2485, 2485,
-
2485, 2485, 2485, 2485, 2485, 2485, 2485, 2485,
-
2485, 2485, 2485, 2485, 2485, 2485, 2485, 2485,
-
2485, 2485, 2485, 2485, 2485, 2485, 2485, 2485,
-
2485, 2485, 2485, 2485, 2485, 2485, 2485, 2485,
-
2485, 2485, 2485, 1, 2553, 2537, 2485, 2485,
-
2485, 2485, 2485, 2485, 2485, 2485, 2485, 2485,
-
2485, 2485, 2485, 2485, 2485, 2485, 2485, 2485,
-
2485, 2485, 2485, 2485, 2485, 2485, 2485, 2485,
-
2485, 2485, 2485, 2485, 2485, 2485, 2485, 2485,
-
1, 2554, 1, 2485, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 2485, 1, 2485, 2485, 2485, 2485,
-
2485, 2485, 2485, 2485, 2485, 2485, 2485, 2485,
-
2485, 2485, 2485, 2485, 2485, 2485, 2485, 2485,
-
2485, 2485, 2485, 2485, 2485, 2485, 2485, 2485,
-
2485, 2485, 2485, 2485, 2485, 2485, 2485, 2485,
-
2485, 2485, 2485, 2485, 2485, 2485, 2485, 2485,
-
2485, 2485, 2485, 2485, 2485, 2485, 2485, 2485,
-
2485, 2485, 2485, 2485, 2485, 2485, 2485, 2485,
-
2485, 2485, 2485, 2485, 2485, 2485, 2485, 2485,
-
2485, 2485, 2485, 2485, 2485, 2485, 2485, 2485,
-
2485, 2485, 2485, 2485, 2485, 2485, 2485, 2485,
-
2485, 2485, 2485, 2485, 2485, 2485, 2485, 2485,
-
2485, 2485, 2485, 2485, 2485, 2485, 2485, 2485,
-
2485, 2485, 2485, 2485, 2485, 2485, 2485, 2485,
-
2485, 2485, 2485, 2485, 2485, 2485, 2485, 2485,
-
2485, 2485, 2485, 2485, 2485, 2485, 2485, 2485,
-
2485, 2485, 2485, 2485, 1, 2555, 1, 1,
-
1, 2556, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 2555, 2557, 2558, 2557,
-
2557, 2557, 2557, 2557, 2559, 1, 2557, 2557,
-
1, 2557, 17, 2557, 2557, 2557, 2557, 2557,
-
2557, 2557, 2557, 2557, 2557, 2557, 18, 1,
-
19, 2557, 1, 2557, 17, 2557, 2557, 2557,
-
2557, 2557, 2557, 2557, 2557, 2557, 2557, 2557,
-
2557, 2557, 2557, 2557, 2557, 2557, 2557, 2557,
-
2557, 2557, 2557, 2557, 2557, 2557, 2557, 1,
-
1, 1, 2557, 2557, 2557, 2557, 2557, 2557,
-
2557, 2557, 2557, 2557, 2557, 2557, 2557, 2557,
-
2557, 2557, 2557, 2557, 2557, 2557, 2557, 2557,
-
2557, 2557, 2557, 2557, 2557, 2557, 2557, 2557,
-
2557, 2557, 2557, 1, 2560, 1, 1, 1,
-
2561, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 2560, 2557, 2558, 2557, 2557,
-
2557, 2557, 2557, 2562, 1, 2557, 2557, 1,
-
2557, 1, 2557, 2557, 2557, 2557, 2557, 2557,
-
2557, 2557, 2557, 2557, 2557, 1, 1, 8,
-
2557, 1, 2557, 1, 2557, 2557, 2557, 2557,
-
2557, 2557, 2557, 2557, 2557, 2557, 2557, 2557,
-
2557, 2557, 2557, 2557, 2557, 2557, 2557, 2557,
-
2557, 2557, 2557, 2557, 2557, 2557, 1, 1,
-
1, 2557, 2557, 2557, 2557, 2557, 2557, 2557,
-
2557, 2557, 2557, 2557, 2557, 2557, 2557, 2557,
-
2557, 2557, 2557, 2557, 2557, 2557, 2557, 2557,
-
2557, 2557, 2557, 2557, 2557, 2557, 2557, 2557,
-
2557, 2557, 1, 2563, 1, 2560, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 2560, 1, 2564, 1,
-
2565, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 2565,
-
1, 2566, 1, 1, 1, 2567, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
2566, 2133, 2134, 2133, 2133, 2133, 2133, 2133,
-
2568, 1, 2133, 2133, 1, 2133, 2136, 2133,
-
2133, 2133, 2133, 2133, 2133, 2133, 2133, 2133,
-
2133, 2133, 18, 1, 19, 2133, 1, 2133,
-
17, 2133, 2133, 2133, 2133, 2133, 2133, 2133,
-
2133, 2133, 2133, 2133, 2133, 2133, 2133, 2133,
-
2133, 2133, 2133, 2133, 2133, 2133, 2133, 2133,
-
2133, 2133, 2133, 2137, 1, 1, 2133, 2133,
-
2133, 2133, 2133, 2133, 2133, 2133, 2133, 2133,
-
2133, 2133, 2133, 2133, 2133, 2133, 2133, 2133,
-
2133, 2133, 2133, 2133, 2133, 2133, 2133, 2133,
-
2133, 2133, 2133, 2133, 2133, 2133, 2133, 1,
-
2569, 1, 1, 1, 2570, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 2569,
-
2140, 2141, 2140, 2140, 2140, 2140, 2140, 2571,
-
1, 2140, 2140, 1, 2140, 2572, 2140, 2140,
-
2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140,
-
2140, 1, 1, 8, 2140, 1, 2140, 1,
-
2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140,
-
2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140,
-
2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140,
-
2140, 2140, 2144, 1, 1, 2140, 2140, 2140,
-
2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140,
-
2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140,
-
2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140,
-
2140, 2140, 2140, 2140, 2140, 2140, 1, 2573,
-
1, 2569, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
2569, 1, 2574, 1, 1, 1, 2575, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 2574, 2178, 2179, 2178, 2178, 2178, 2178,
-
2178, 2576, 1, 2178, 2178, 1, 2178, 2577,
-
2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178,
-
2178, 2178, 2178, 1, 1, 48, 2178, 1,
-
2178, 1, 2178, 2178, 2178, 2178, 2178, 2178,
-
2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178,
-
2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178,
-
2178, 2178, 2178, 2178, 2182, 1, 1, 2178,
-
2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178,
-
2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178,
-
2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178,
-
2178, 2178, 2178, 2178, 2178, 2178, 2178, 2178,
-
1, 2578, 2579, 2578, 2578, 2578, 2578, 2578,
-
1, 1, 2578, 2578, 1, 2578, 2572, 2578,
-
2578, 2578, 2578, 2578, 2578, 2578, 2578, 2578,
-
2578, 2578, 1, 1, 1, 2578, 1, 2578,
-
1, 2578, 2578, 2578, 2578, 2578, 2578, 2578,
-
2578, 2578, 2578, 2578, 2578, 2578, 2578, 2578,
-
2578, 2578, 2578, 2578, 2578, 2578, 2578, 2578,
-
2578, 2578, 2578, 1, 1, 1, 2578, 2578,
-
2578, 2578, 2578, 2578, 2578, 2578, 2578, 2578,
-
2578, 2578, 2578, 2578, 2578, 2578, 2578, 2578,
-
2578, 2578, 2578, 2578, 2578, 2578, 2578, 2578,
-
2578, 2578, 2578, 2578, 2578, 2578, 2578, 1,
-
2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580,
-
2579, 1, 2580, 2580, 2581, 2580, 2580, 2580,
-
2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580,
-
2580, 2580, 2580, 2580, 2580, 2580, 2580, 2579,
-
2580, 1, 2580, 2580, 2580, 2580, 2580, 2580,
-
2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580,
-
2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580,
-
2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580,
-
2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580,
-
2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580,
-
2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580,
-
2580, 2580, 2580, 2582, 2580, 2580, 2580, 2580,
-
2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580,
-
2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580,
-
2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580,
-
2580, 2580, 2580, 2580, 2580, 2580, 2580, 1,
-
2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580,
-
2580, 1, 2580, 2580, 2583, 2580, 2580, 2580,
-
2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580,
-
2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580,
-
2580, 2578, 2580, 2580, 2580, 2580, 2580, 2580,
-
2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580,
-
2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580,
-
2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580,
-
2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580,
-
2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580,
-
2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580,
-
2580, 2580, 2580, 2582, 2580, 2580, 2580, 2580,
-
2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580,
-
2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580,
-
2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580,
-
2580, 2580, 2580, 2580, 2580, 2580, 2580, 1,
-
2584, 1, 2580, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 2580, 1, 2580, 2580, 2580, 2580, 2580,
-
2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580,
-
2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580,
-
2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580,
-
2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580,
-
2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580,
-
2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580,
-
2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580,
-
2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580,
-
2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580,
-
2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580,
-
2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580,
-
2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580,
-
2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580,
-
2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580,
-
2580, 2580, 2580, 2580, 2580, 2580, 2580, 2580,
-
2580, 2580, 2580, 1, 2585, 1, 2579, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 2579, 1, 2586,
-
2586, 2586, 2586, 2586, 2586, 2586, 2586, 2586,
-
1, 2586, 2586, 2587, 2586, 2586, 2586, 2586,
-
2586, 2586, 2586, 2586, 2586, 2586, 2586, 2586,
-
2586, 2586, 2586, 2586, 2586, 2586, 2586, 2586,
-
2588, 2586, 2586, 2586, 2586, 2586, 2586, 2586,
-
2586, 2586, 2586, 2586, 2586, 2586, 2586, 2586,
-
2586, 2586, 2586, 2586, 2586, 2586, 2586, 2586,
-
2586, 2586, 2586, 2586, 2586, 2586, 2586, 2586,
-
2586, 2586, 2586, 2586, 2586, 2586, 2586, 2586,
-
2586, 2586, 2586, 2586, 2586, 2586, 2586, 2586,
-
2586, 2586, 2586, 2586, 2586, 2586, 2586, 2586,
-
2586, 2586, 2589, 2586, 2586, 2586, 2586, 2586,
-
2586, 2586, 2586, 2586, 2586, 2586, 2586, 2586,
-
2586, 2586, 2586, 2586, 2586, 2586, 2586, 2586,
-
2586, 2586, 2586, 2586, 2586, 2586, 2586, 2586,
-
2586, 2586, 2586, 2586, 2586, 2586, 1, 2590,
-
2590, 2590, 2590, 2590, 2590, 2590, 2590, 2590,
-
1, 2590, 2590, 2591, 2590, 2590, 2590, 2590,
-
2590, 2590, 2590, 2590, 2590, 2590, 2590, 2590,
-
2590, 2590, 2590, 2590, 2590, 2590, 2590, 2590,
-
2592, 2590, 2590, 2590, 2590, 2590, 2590, 2590,
-
2590, 2590, 2590, 2590, 2590, 2590, 2590, 2590,
-
2590, 2590, 2590, 2590, 2590, 2590, 2590, 2590,
-
2590, 2590, 2590, 2590, 2590, 2590, 2590, 2590,
-
2590, 2590, 2590, 2590, 2590, 2590, 2590, 2590,
-
2590, 2590, 2590, 2590, 2590, 2590, 2590, 2590,
-
2590, 2590, 2590, 2590, 2590, 2590, 2590, 2590,
-
2590, 2590, 2593, 2590, 2590, 2590, 2590, 2590,
-
2590, 2590, 2590, 2590, 2590, 2590, 2590, 2590,
-
2590, 2590, 2590, 2590, 2590, 2590, 2590, 2590,
-
2590, 2590, 2590, 2590, 2590, 2590, 2590, 2590,
-
2590, 2590, 2590, 2590, 2590, 2590, 1, 2594,
-
1, 2590, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
2590, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 2590, 2595, 1, 1, 1, 2596,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 2595, 2597, 2598, 2597, 2597, 2597,
-
2597, 2597, 2599, 1, 2597, 2597, 1, 2597,
-
1, 2597, 2597, 2597, 2597, 2597, 2597, 2597,
-
2597, 2597, 2597, 2597, 1, 1, 48, 2597,
-
1, 2597, 1, 2597, 2597, 2597, 2597, 2597,
-
2597, 2597, 2597, 2597, 2597, 2597, 2597, 2597,
-
2597, 2597, 2597, 2597, 2597, 2597, 2597, 2597,
-
2597, 2597, 2597, 2597, 2597, 1, 1, 1,
-
2597, 2597, 2597, 2597, 2597, 2597, 2597, 2597,
-
2597, 2597, 2597, 2597, 2597, 2597, 2597, 2597,
-
2597, 2597, 2597, 2597, 2597, 2597, 2597, 2597,
-
2597, 2597, 2597, 2597, 2597, 2597, 2597, 2597,
-
2597, 1, 2600, 2600, 2600, 2600, 2600, 2600,
-
2600, 2600, 2601, 1, 2600, 2600, 2602, 2600,
-
2600, 2600, 2600, 2600, 2600, 2600, 2600, 2600,
-
2600, 2600, 2600, 2600, 2600, 2600, 2600, 2600,
-
2600, 2601, 2600, 23, 2600, 2600, 2600, 2600,
-
2600, 2600, 2600, 2600, 2600, 2600, 2600, 2600,
-
2600, 2600, 2600, 2600, 2600, 2600, 2600, 2600,
-
2600, 2600, 2600, 2600, 2600, 2600, 2600, 2600,
-
2600, 2600, 2600, 2600, 2600, 2600, 2600, 2600,
-
2600, 2600, 2600, 2600, 2600, 2600, 2600, 2600,
-
2600, 2600, 2600, 2600, 2600, 2600, 2600, 2600,
-
2600, 2600, 2600, 2600, 2600, 2603, 2600, 2600,
-
2600, 2600, 2600, 2600, 2600, 2600, 2600, 2600,
-
2600, 2600, 2600, 2600, 2600, 2600, 2600, 2600,
-
2600, 2600, 2600, 2600, 2600, 2600, 2600, 2600,
-
2600, 2600, 2600, 2600, 2600, 2600, 2600, 2600,
-
2600, 1, 2604, 2604, 2604, 2604, 2604, 2604,
-
2604, 2604, 2604, 1, 2604, 2604, 2605, 2604,
-
2604, 2604, 2604, 2604, 2604, 2604, 2604, 2604,
-
2604, 2604, 2604, 2604, 2604, 2604, 2604, 2604,
-
2604, 2604, 2604, 2606, 2604, 2604, 2604, 2604,
-
2604, 2604, 2604, 2604, 2604, 2604, 2604, 2604,
-
2604, 2604, 2604, 2604, 2604, 2604, 2604, 2604,
-
2604, 2604, 2604, 2604, 2604, 2604, 2604, 2604,
-
2604, 2604, 2604, 2604, 2604, 2604, 2604, 2604,
-
2604, 2604, 2604, 2604, 2604, 2604, 2604, 2604,
-
2604, 2604, 2604, 2604, 2604, 2604, 2604, 2604,
-
2604, 2604, 2604, 2604, 2604, 2607, 2604, 2604,
-
2604, 2604, 2604, 2604, 2604, 2604, 2604, 2604,
-
2604, 2604, 2604, 2604, 2604, 2604, 2604, 2604,
-
2604, 2604, 2604, 2604, 2604, 2604, 2604, 2604,
-
2604, 2604, 2604, 2604, 2604, 2604, 2604, 2604,
-
2604, 1, 2608, 1, 2604, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 2604, 1, 2609, 1, 2610,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 2610, 1,
-
2611, 1, 2612, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 2612, 1, 2613, 1, 1, 1, 2614,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 2613, 2615, 2616, 2615, 2615, 2615,
-
2615, 2615, 2617, 1, 2615, 2615, 1, 2615,
-
2618, 2615, 2615, 2615, 2615, 2615, 2615, 2615,
-
2615, 2615, 2615, 2615, 1, 1, 1, 2615,
-
1, 2615, 1, 2615, 2615, 2615, 2615, 2615,
-
2615, 2615, 2615, 2615, 2615, 2615, 2615, 2615,
-
2615, 2615, 2615, 2615, 2615, 2615, 2615, 2615,
-
2615, 2615, 2615, 2615, 2615, 2137, 1, 1,
-
2615, 2615, 2615, 2615, 2615, 2615, 2615, 2615,
-
2615, 2615, 2615, 2615, 2615, 2615, 2615, 2615,
-
2615, 2615, 2615, 2615, 2615, 2615, 2615, 2615,
-
2615, 2615, 2615, 2615, 2615, 2615, 2615, 2615,
-
2615, 1, 2619, 1, 1, 1, 2620, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 2619, 2621, 2579, 2621, 2621, 2621, 2621,
-
2621, 2622, 1, 2621, 2621, 1, 2621, 2572,
-
2621, 2621, 2621, 2621, 2621, 2621, 2621, 2621,
-
2621, 2621, 2621, 1, 1, 1, 2621, 1,
-
2621, 1, 2621, 2621, 2621, 2621, 2621, 2621,
-
2621, 2621, 2621, 2621, 2621, 2621, 2621, 2621,
-
2621, 2621, 2621, 2621, 2621, 2621, 2621, 2621,
-
2621, 2621, 2621, 2621, 2144, 1, 1, 2621,
-
2621, 2621, 2621, 2621, 2621, 2621, 2621, 2621,
-
2621, 2621, 2621, 2621, 2621, 2621, 2621, 2621,
-
2621, 2621, 2621, 2621, 2621, 2621, 2621, 2621,
-
2621, 2621, 2621, 2621, 2621, 2621, 2621, 2621,
-
1, 2623, 1, 2619, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 2619, 1, 2236, 1, 1, 1,
-
2237, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 2236, 2621, 2579, 2621, 2621,
-
2621, 2621, 2621, 2239, 1, 2621, 2621, 1,
-
2621, 2572, 2621, 2621, 2621, 2621, 2621, 2621,
-
2621, 2621, 2621, 2621, 2621, 1, 1, 1,
-
2621, 1, 2621, 1, 2621, 2621, 2621, 2621,
-
2621, 2621, 2621, 2621, 2621, 2621, 2621, 2621,
-
2621, 2621, 2621, 2621, 2621, 2621, 2621, 2621,
-
2621, 2621, 2621, 2621, 2621, 2621, 1, 1,
-
1, 2621, 2621, 2621, 2621, 2621, 2621, 2621,
-
2621, 2621, 2621, 2621, 2621, 2621, 2621, 2621,
-
2621, 2621, 2621, 2621, 2621, 2621, 2621, 2621,
-
2621, 2621, 2621, 2621, 2621, 2621, 2621, 2621,
-
2621, 2621, 1, 2624, 1, 1, 1, 2625,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 2624, 2626, 2627, 2626, 2626, 2626,
-
2626, 2626, 2628, 1, 2626, 2626, 1, 2626,
-
2577, 2626, 2626, 2626, 2626, 2626, 2626, 2626,
-
2626, 2626, 2626, 2626, 1, 1, 1, 2626,
-
1, 2626, 1, 2626, 2626, 2626, 2626, 2626,
-
2626, 2626, 2626, 2626, 2626, 2626, 2626, 2626,
-
2626, 2626, 2626, 2626, 2626, 2626, 2626, 2626,
-
2626, 2626, 2626, 2626, 2626, 2182, 1, 1,
-
2626, 2626, 2626, 2626, 2626, 2626, 2626, 2626,
-
2626, 2626, 2626, 2626, 2626, 2626, 2626, 2626,
-
2626, 2626, 2626, 2626, 2626, 2626, 2626, 2626,
-
2626, 2626, 2626, 2626, 2626, 2626, 2626, 2626,
-
2626, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 2604, 2604, 2604, 2604, 2604, 2604,
-
2604, 2604, 2604, 2629, 1, 2604, 2604, 2630,
-
2604, 2604, 2604, 2604, 2604, 2604, 2604, 2604,
-
2604, 2604, 2604, 2604, 2604, 2604, 2604, 2604,
-
2604, 2604, 2629, 2604, 27, 2604, 2604, 2604,
-
2604, 2604, 2604, 2604, 2604, 2604, 2604, 2604,
-
2604, 2604, 2604, 2604, 2604, 2604, 2604, 2604,
-
2604, 2604, 2604, 2604, 2604, 2604, 2604, 2604,
-
2604, 2604, 2604, 2604, 2604, 2604, 2604, 2604,
-
2604, 2604, 2604, 2604, 2604, 2604, 2604, 2604,
-
2604, 2604, 2604, 2604, 2604, 2604, 2604, 2604,
-
2604, 2604, 2604, 2604, 2604, 2604, 2607, 2604,
-
2604, 2604, 2604, 2604, 2604, 2604, 2604, 2604,
-
2604, 2604, 2604, 2604, 2604, 2604, 2604, 2604,
-
2604, 2604, 2604, 2604, 2604, 2604, 2604, 2604,
-
2604, 2604, 2604, 2604, 2604, 2604, 2604, 2604,
-
2604, 2604, 1, 2631, 1, 2629, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 2629, 1, 2632, 1,
-
2633, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 2633,
-
1, 2634, 2634, 2634, 2634, 2634, 2634, 2634,
-
2634, 2635, 1, 2634, 2634, 2636, 2634, 2634,
-
2634, 2634, 2634, 2634, 2634, 2634, 2634, 2634,
-
2634, 2634, 2634, 2634, 2634, 2634, 2634, 2634,
-
2635, 2634, 2588, 2634, 2634, 2634, 2634, 2634,
-
2634, 2634, 2634, 2634, 2634, 2634, 2634, 2634,
-
2634, 2634, 2634, 2634, 2634, 2634, 2634, 2634,
-
2634, 2634, 2634, 2634, 2634, 2634, 2634, 2634,
-
2634, 2634, 2634, 2634, 2634, 2634, 2634, 2634,
-
2634, 2634, 2634, 2634, 2634, 2634, 2634, 2634,
-
2634, 2634, 2634, 2634, 2634, 2634, 2634, 2634,
-
2634, 2634, 2634, 2634, 2637, 2634, 2634, 2634,
-
2634, 2634, 2634, 2634, 2634, 2634, 2634, 2634,
-
2634, 2634, 2634, 2634, 2634, 2634, 2634, 2634,
-
2634, 2634, 2634, 2634, 2634, 2634, 2634, 2634,
-
2634, 2634, 2634, 2634, 2634, 2634, 2634, 2634,
-
1, 2638, 2638, 2638, 2638, 2638, 2638, 2638,
-
2638, 2638, 1, 2638, 2638, 2639, 2638, 2638,
-
2638, 2638, 2638, 2638, 2638, 2638, 2638, 2638,
-
2638, 2638, 2638, 2638, 2638, 2638, 2638, 2638,
-
2638, 2638, 2640, 2638, 2638, 2638, 2638, 2638,
-
2638, 2638, 2638, 2638, 2638, 2638, 2638, 2638,
-
2638, 2638, 2638, 2638, 2638, 2638, 2638, 2638,
-
2638, 2638, 2638, 2638, 2638, 2638, 2638, 2638,
-
2638, 2638, 2638, 2638, 2638, 2638, 2638, 2638,
-
2638, 2638, 2638, 2638, 2638, 2638, 2638, 2638,
-
2638, 2638, 2638, 2638, 2638, 2638, 2638, 2638,
-
2638, 2638, 2638, 2638, 2641, 2638, 2638, 2638,
-
2638, 2638, 2638, 2638, 2638, 2638, 2638, 2638,
-
2638, 2638, 2638, 2638, 2638, 2638, 2638, 2638,
-
2638, 2638, 2638, 2638, 2638, 2638, 2638, 2638,
-
2638, 2638, 2638, 2638, 2638, 2638, 2638, 2638,
-
1, 2642, 1, 2638, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 2638, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 2638, 2638, 2638, 2638,
-
2638, 2638, 2638, 2638, 2638, 2643, 1, 2638,
-
2638, 2644, 2638, 2638, 2638, 2638, 2638, 2638,
-
2638, 2638, 2638, 2638, 2638, 2638, 2638, 2638,
-
2638, 2638, 2638, 2638, 2643, 2638, 2592, 2638,
-
2638, 2638, 2638, 2638, 2638, 2638, 2638, 2638,
-
2638, 2638, 2638, 2638, 2638, 2638, 2638, 2638,
-
2638, 2638, 2638, 2638, 2638, 2638, 2638, 2638,
-
2638, 2638, 2638, 2638, 2638, 2638, 2638, 2638,
-
2638, 2638, 2638, 2638, 2638, 2638, 2638, 2638,
-
2638, 2638, 2638, 2638, 2638, 2638, 2638, 2638,
-
2638, 2638, 2638, 2638, 2638, 2638, 2638, 2638,
-
2641, 2638, 2638, 2638, 2638, 2638, 2638, 2638,
-
2638, 2638, 2638, 2638, 2638, 2638, 2638, 2638,
-
2638, 2638, 2638, 2638, 2638, 2638, 2638, 2638,
-
2638, 2638, 2638, 2638, 2638, 2638, 2638, 2638,
-
2638, 2638, 2638, 2638, 1, 2645, 1, 2643,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 2643, 1,
-
2646, 2646, 2646, 2646, 2646, 2646, 2646, 2646,
-
2647, 1, 2646, 2646, 2648, 2646, 2646, 2646,
-
2646, 2646, 2646, 2646, 2646, 2646, 2646, 2646,
-
2646, 2646, 2646, 2646, 2646, 2646, 2646, 2647,
-
2646, 2649, 2646, 2646, 2646, 2646, 2646, 2646,
-
2646, 2646, 2646, 2646, 2646, 2646, 2646, 2646,
-
2646, 2646, 2646, 2646, 2646, 2646, 2646, 2646,
-
2646, 2646, 2646, 2646, 2646, 2646, 2646, 2646,
-
2646, 2646, 2646, 2646, 2646, 2646, 2646, 2646,
-
2646, 2646, 2646, 2646, 2646, 2646, 2646, 2646,
-
2646, 2646, 2646, 2646, 2646, 2646, 2646, 2646,
-
2646, 2646, 2646, 2650, 2646, 2646, 2646, 2646,
-
2646, 2646, 2646, 2646, 2646, 2646, 2646, 2646,
-
2646, 2646, 2646, 2646, 2646, 2646, 2646, 2646,
-
2646, 2646, 2646, 2646, 2646, 2646, 2646, 2646,
-
2646, 2646, 2646, 2646, 2646, 2646, 2646, 1,
-
2651, 2651, 2651, 2651, 2651, 2651, 2651, 2651,
-
2651, 1, 2651, 2651, 2652, 2651, 2651, 2651,
-
2651, 2651, 2651, 2651, 2651, 2651, 2651, 2651,
-
2651, 2651, 2651, 2651, 2651, 2651, 2651, 2651,
-
2651, 2653, 2651, 2651, 2651, 2651, 2651, 2651,
-
2651, 2651, 2651, 2651, 2651, 2651, 2651, 2651,
-
2651, 2651, 2651, 2651, 2651, 2651, 2651, 2651,
-
2651, 2651, 2651, 2651, 2651, 2651, 2651, 2651,
-
2651, 2651, 2651, 2651, 2651, 2651, 2651, 2651,
-
2651, 2651, 2651, 2651, 2651, 2651, 2651, 2651,
-
2651, 2651, 2651, 2651, 2651, 2651, 2651, 2651,
-
2651, 2651, 2651, 2654, 2651, 2651, 2651, 2651,
-
2651, 2651, 2651, 2651, 2651, 2651, 2651, 2651,
-
2651, 2651, 2651, 2651, 2651, 2651, 2651, 2651,
-
2651, 2651, 2651, 2651, 2651, 2651, 2651, 2651,
-
2651, 2651, 2651, 2651, 2651, 2651, 2651, 1,
-
2655, 1, 2651, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 2651, 1, 2656, 1, 2657, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 2657, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 2651, 2651,
-
2651, 2651, 2651, 2651, 2651, 2651, 2651, 2658,
-
1, 2651, 2651, 2659, 2651, 2651, 2651, 2651,
-
2651, 2651, 2651, 2651, 2651, 2651, 2651, 2651,
-
2651, 2651, 2651, 2651, 2651, 2651, 2658, 2651,
-
2660, 2651, 2651, 2651, 2651, 2651, 2651, 2651,
-
2651, 2651, 2651, 2651, 2651, 2651, 2651, 2651,
-
2651, 2651, 2651, 2651, 2651, 2651, 2651, 2651,
-
2651, 2651, 2651, 2651, 2651, 2651, 2651, 2651,
-
2651, 2651, 2651, 2651, 2651, 2651, 2651, 2651,
-
2651, 2651, 2651, 2651, 2651, 2651, 2651, 2651,
-
2651, 2651, 2651, 2651, 2651, 2651, 2651, 2651,
-
2651, 2651, 2654, 2651, 2651, 2651, 2651, 2651,
-
2651, 2651, 2651, 2651, 2651, 2651, 2651, 2651,
-
2651, 2651, 2651, 2651, 2651, 2651, 2651, 2651,
-
2651, 2651, 2651, 2651, 2651, 2651, 2651, 2651,
-
2651, 2651, 2651, 2651, 2651, 2651, 1, 2661,
-
1, 2658, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
2658, 1, 2662, 1, 2663, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 2663, 1, 2664, 1, 1,
-
1, 2665, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 2664, 2666, 2667, 2666,
-
2666, 2666, 2666, 2666, 2668, 1, 2666, 2666,
-
1, 2666, 2669, 2666, 2666, 2666, 2666, 2666,
-
2666, 2666, 2666, 2666, 2666, 2666, 1, 1,
-
48, 2666, 1, 2666, 1, 2666, 2666, 2666,
-
2666, 2666, 2666, 2666, 2666, 2666, 2666, 2666,
-
2666, 2666, 2666, 2666, 2666, 2666, 2666, 2666,
-
2666, 2666, 2666, 2666, 2666, 2666, 2666, 1,
-
1, 1, 2666, 2666, 2666, 2666, 2666, 2666,
-
2666, 2666, 2666, 2666, 2666, 2666, 2666, 2666,
-
2666, 2666, 2666, 2666, 2666, 2666, 2666, 2666,
-
2666, 2666, 2666, 2666, 2666, 2666, 2666, 2666,
-
2666, 2666, 2666, 1, 2670, 1, 1, 1,
-
2671, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 2670, 3, 4, 3, 3,
-
3, 3, 3, 5, 1, 3, 3, 1,
-
3, 7, 3, 3, 3, 3, 3, 3,
-
3, 3, 3, 3, 3, 1, 1, 8,
-
3, 1, 3, 1, 3, 3, 3, 3,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 3, 3, 3, 3, 3, 1, 1,
-
1, 3, 3, 3, 3, 3, 3, 3,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 3, 1, 2672, 1, 2670, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 2670, 1, 2673, 2674,
-
2673, 2673, 2673, 2673, 2673, 1, 1, 2673,
-
2673, 1, 2673, 2675, 2673, 2673, 2673, 2673,
-
2673, 2673, 2673, 2673, 2673, 2673, 2673, 1,
-
1, 1, 2673, 1, 2673, 1, 2673, 2673,
-
2673, 2673, 2673, 2673, 2673, 2673, 2673, 2673,
-
2673, 2673, 2673, 2673, 2673, 2673, 2673, 2673,
-
2673, 2673, 2673, 2673, 2673, 2673, 2673, 2673,
-
1, 1, 1, 2673, 2673, 2673, 2673, 2673,
-
2673, 2673, 2673, 2673, 2673, 2673, 2673, 2673,
-
2673, 2673, 2673, 2673, 2673, 2673, 2673, 2673,
-
2673, 2673, 2673, 2673, 2673, 2673, 2673, 2673,
-
2673, 2673, 2673, 2673, 1, 2676, 1, 2677,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 2677, 1,
-
2678, 2678, 2678, 2678, 2678, 2678, 2678, 2678,
-
2674, 1, 2678, 2678, 2679, 2678, 2678, 2678,
-
2678, 2678, 2678, 2678, 2678, 2678, 2678, 2678,
-
2678, 2678, 2678, 2678, 2678, 2678, 2678, 2674,
-
2678, 1, 2678, 2678, 2678, 2678, 2678, 2678,
-
2678, 2678, 2678, 2678, 2678, 2678, 2678, 2678,
-
2678, 2678, 2678, 2678, 2678, 2678, 2678, 2678,
-
2678, 2678, 2678, 2678, 2678, 2678, 2678, 2678,
-
2678, 2678, 2678, 2678, 2678, 2678, 2678, 2678,
-
2678, 2678, 2678, 2678, 2678, 2678, 2678, 2678,
-
2678, 2678, 2678, 2678, 2678, 2678, 2678, 2678,
-
2678, 2678, 2678, 2680, 2678, 2678, 2678, 2678,
-
2678, 2678, 2678, 2678, 2678, 2678, 2678, 2678,
-
2678, 2678, 2678, 2678, 2678, 2678, 2678, 2678,
-
2678, 2678, 2678, 2678, 2678, 2678, 2678, 2678,
-
2678, 2678, 2678, 2678, 2678, 2678, 2678, 1,
-
2678, 2678, 2678, 2678, 2678, 2678, 2678, 2678,
-
2678, 1, 2678, 2678, 2681, 2678, 2678, 2678,
-
2678, 2678, 2678, 2678, 2678, 2678, 2678, 2678,
-
2678, 2678, 2678, 2678, 2678, 2678, 2678, 2678,
-
2678, 2673, 2678, 2678, 2678, 2678, 2678, 2678,
-
2678, 2678, 2678, 2678, 2678, 2678, 2678, 2678,
-
2678, 2678, 2678, 2678, 2678, 2678, 2678, 2678,
-
2678, 2678, 2678, 2678, 2678, 2678, 2678, 2678,
-
2678, 2678, 2678, 2678, 2678, 2678, 2678, 2678,
-
2678, 2678, 2678, 2678, 2678, 2678, 2678, 2678,
-
2678, 2678, 2678, 2678, 2678, 2678, 2678, 2678,
-
2678, 2678, 2678, 2680, 2678, 2678, 2678, 2678,
-
2678, 2678, 2678, 2678, 2678, 2678, 2678, 2678,
-
2678, 2678, 2678, 2678, 2678, 2678, 2678, 2678,
-
2678, 2678, 2678, 2678, 2678, 2678, 2678, 2678,
-
2678, 2678, 2678, 2678, 2678, 2678, 2678, 1,
-
2682, 1, 2678, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 2678, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 2678, 2683, 1, 2674, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 2674, 1, 38,
-
1, 1, 1, 39, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 38, 2684,
-
2685, 2684, 2684, 2684, 2684, 2684, 40, 1,
-
2684, 2684, 1, 2684, 2686, 2684, 2684, 2684,
-
2684, 2684, 2684, 2684, 2684, 2684, 2684, 2684,
-
18, 1, 19, 2684, 1, 2684, 17, 2684,
-
2684, 2684, 2684, 2684, 2684, 2684, 2684, 2684,
-
2684, 2684, 2684, 2684, 2684, 2684, 2684, 2684,
-
2684, 2684, 2684, 2684, 2684, 2684, 2684, 2684,
-
2684, 1, 1, 1, 2684, 2684, 2684, 2684,
-
2684, 2684, 2684, 2684, 2684, 2684, 2684, 2684,
-
2684, 2684, 2684, 2684, 2684, 2684, 2684, 2684,
-
2684, 2684, 2684, 2684, 2684, 2684, 2684, 2684,
-
2684, 2684, 2684, 2684, 2684, 1, 2687, 2687,
-
2687, 2687, 2687, 2687, 2687, 2687, 2687, 1,
-
2687, 2687, 2688, 2687, 2687, 2687, 2687, 2687,
-
2687, 2687, 2687, 2687, 2687, 2687, 2687, 2687,
-
2687, 2687, 2687, 2687, 2687, 2687, 2687, 2687,
-
2687, 2687, 2687, 2687, 2687, 2689, 2690, 2687,
-
2687, 2687, 2687, 2687, 2687, 2687, 2687, 2687,
-
2687, 2687, 2687, 2687, 2687, 2687, 2687, 2687,
-
2687, 2687, 2687, 2687, 2687, 2687, 2687, 2687,
-
2687, 2687, 2687, 2687, 2687, 2687, 2687, 2687,
-
2687, 2687, 2687, 2687, 2687, 2687, 2687, 2687,
-
2687, 2687, 2687, 2687, 2687, 2687, 2687, 2687,
-
2687, 2691, 2687, 2687, 2687, 2687, 2687, 2687,
-
2687, 2687, 2687, 2687, 2687, 2687, 2687, 2687,
-
2687, 2687, 2687, 2687, 2687, 2687, 2687, 2687,
-
2687, 2687, 2687, 2687, 2687, 2687, 2687, 2687,
-
2687, 2687, 2687, 2687, 2687, 1, 2692, 2692,
-
2692, 2692, 2692, 2692, 2692, 2692, 2692, 1,
-
2692, 2692, 2693, 2692, 2692, 2692, 2692, 2692,
-
2692, 2692, 2692, 2692, 2692, 2692, 2692, 2692,
-
2692, 2692, 2692, 2692, 2692, 2692, 2692, 2692,
-
2692, 2692, 2692, 2692, 2692, 2694, 2695, 2692,
-
2692, 2692, 2692, 2692, 2692, 2692, 2692, 2692,
-
2692, 2692, 2692, 2692, 2692, 2692, 2692, 2692,
-
2692, 2692, 2692, 2692, 2692, 2692, 2692, 2692,
-
2692, 2692, 2692, 2692, 2692, 2692, 2692, 2692,
-
2692, 2692, 2692, 2692, 2692, 2692, 2692, 2692,
-
2692, 2692, 2692, 2692, 2692, 2692, 2692, 2692,
-
2692, 2696, 2692, 2692, 2692, 2692, 2692, 2692,
-
2692, 2692, 2692, 2692, 2692, 2692, 2692, 2692,
-
2692, 2692, 2692, 2692, 2692, 2692, 2692, 2692,
-
2692, 2692, 2692, 2692, 2692, 2692, 2692, 2692,
-
2692, 2692, 2692, 2692, 2692, 1, 2697, 1,
-
2692, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 2692,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 2692, 2698, 1, 1, 1, 2699, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 2698, 2700, 2701, 2700, 2700, 2700, 2700,
-
2700, 2702, 1, 2700, 2700, 6, 2700, 2703,
-
2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700,
-
2700, 2700, 2700, 1, 6, 2704, 2700, 1,
-
2700, 2705, 2700, 2700, 2700, 2700, 2700, 2700,
-
2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700,
-
2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700,
-
2700, 2700, 2700, 2700, 1, 1, 1, 2700,
-
2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700,
-
2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700,
-
2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700,
-
2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700,
-
1, 2706, 1, 1, 1, 2707, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
2706, 2708, 2685, 2708, 2708, 2708, 2708, 2708,
-
2709, 1, 2708, 2708, 2710, 2708, 2711, 2708,
-
2708, 2708, 2708, 2708, 2708, 2708, 2708, 2708,
-
2708, 2708, 18, 2710, 19, 2708, 1, 2708,
-
2712, 2708, 2708, 2708, 2708, 2708, 2708, 2708,
-
2708, 2708, 2708, 2708, 2708, 2708, 2708, 2708,
-
2708, 2708, 2708, 2708, 2708, 2708, 2708, 2708,
-
2708, 2708, 2708, 1, 1, 1, 2708, 2708,
-
2708, 2708, 2708, 2708, 2708, 2708, 2708, 2708,
-
2708, 2708, 2708, 2708, 2708, 2708, 2708, 2708,
-
2708, 2708, 2708, 2708, 2708, 2708, 2708, 2708,
-
2708, 2708, 2708, 2708, 2708, 2708, 2708, 1,
-
2713, 1, 1, 1, 2714, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 2713,
-
14, 15, 14, 14, 14, 14, 14, 2715,
-
1, 14, 14, 2716, 14, 2717, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 18, 2716, 19, 14, 1, 14, 2718,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 1, 1, 1, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 1, 2719,
-
1, 1, 1, 2720, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 2719, 1,
-
1, 1, 1, 1, 1, 1, 2721, 1,
-
1, 1, 2722, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 2722, 1, 2723, 1, 1, 1, 2724,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 2723, 1, 1, 1, 1, 1,
-
1, 1, 2725, 1, 1, 1, 2726, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 2726, 1, 2727,
-
1, 1, 1, 2728, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 2727, 2729,
-
2730, 2729, 2729, 2729, 2729, 2729, 2731, 1,
-
2729, 2729, 6, 2729, 2732, 2729, 2729, 2729,
-
2729, 2729, 2729, 2729, 2729, 2729, 2729, 2729,
-
1, 6, 2704, 2729, 1, 2729, 2705, 2729,
-
2729, 2729, 2729, 2729, 2729, 2729, 2729, 2729,
-
2729, 2729, 2729, 2729, 2729, 2729, 2729, 2729,
-
2729, 2729, 2729, 2729, 2729, 2729, 2729, 2729,
-
2729, 1, 1, 1, 2729, 2729, 2729, 2729,
-
2729, 2729, 2729, 2729, 2729, 2729, 2729, 2729,
-
2729, 2729, 2729, 2729, 2729, 2729, 2729, 2729,
-
2729, 2729, 2729, 2729, 2729, 2729, 2729, 2729,
-
2729, 2729, 2729, 2729, 2729, 1, 2733, 1,
-
1, 1, 2734, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 2733, 2735, 2310,
-
2735, 2735, 2735, 2735, 2735, 2736, 1, 2735,
-
2735, 2710, 2735, 2737, 2735, 2735, 2735, 2735,
-
2735, 2735, 2735, 2735, 2735, 2735, 2735, 18,
-
2710, 19, 2735, 1, 2735, 2738, 2735, 2735,
-
2735, 2735, 2735, 2735, 2735, 2735, 2735, 2735,
-
2735, 2735, 2735, 2735, 2735, 2735, 2735, 2735,
-
2735, 2735, 2735, 2735, 2735, 2735, 2735, 2735,
-
1, 1, 1, 2735, 2735, 2735, 2735, 2735,
-
2735, 2735, 2735, 2735, 2735, 2735, 2735, 2735,
-
2735, 2735, 2735, 2735, 2735, 2735, 2735, 2735,
-
2735, 2735, 2735, 2735, 2735, 2735, 2735, 2735,
-
2735, 2735, 2735, 2735, 1, 2739, 1, 1,
-
1, 2740, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 2739, 14, 15, 14,
-
14, 14, 14, 14, 2741, 1, 14, 14,
-
2716, 14, 2742, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 18, 2716,
-
19, 14, 1, 14, 2743, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 1,
-
1, 1, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 1, 2744, 1, 1, 1,
-
2745, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 2744, 32, 33, 32, 32,
-
32, 32, 32, 2746, 1, 32, 32, 2747,
-
32, 2748, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 36, 2747, 37,
-
32, 1, 32, 2749, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 1, 1,
-
1, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 1, 2750, 1, 1, 1, 2751,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 2750, 104, 15, 104, 104, 104,
-
104, 104, 2752, 1, 104, 104, 2722, 104,
-
2742, 104, 104, 104, 104, 104, 104, 104,
-
104, 104, 104, 104, 18, 2722, 19, 104,
-
1, 104, 2753, 104, 104, 104, 104, 104,
-
104, 104, 104, 104, 104, 104, 104, 104,
-
104, 104, 104, 104, 104, 104, 104, 104,
-
104, 104, 104, 104, 104, 1, 1, 1,
-
104, 104, 104, 104, 104, 104, 104, 104,
-
104, 104, 104, 104, 104, 104, 104, 104,
-
104, 104, 104, 104, 104, 104, 104, 104,
-
104, 104, 104, 104, 104, 104, 104, 104,
-
104, 1, 2750, 1, 1, 1, 2751, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 2750, 14, 15, 14, 14, 14, 14,
-
14, 2752, 1, 14, 14, 2722, 14, 2742,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 18, 2722, 19, 14, 1,
-
14, 2753, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 1, 1, 1, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
1, 2754, 1, 1, 1, 2755, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
2754, 32, 33, 32, 32, 32, 32, 32,
-
2756, 1, 32, 32, 2726, 32, 2748, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 36, 2726, 37, 32, 1, 32,
-
2757, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 1, 1, 1, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 1,
-
2758, 1, 1, 1, 2759, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 2758,
-
413, 414, 413, 413, 413, 413, 413, 2760,
-
1, 413, 413, 6, 413, 416, 413, 413,
-
413, 413, 413, 413, 413, 413, 413, 413,
-
413, 1, 6, 417, 413, 1, 413, 418,
-
413, 413, 413, 413, 413, 413, 413, 413,
-
413, 413, 413, 413, 413, 413, 413, 413,
-
413, 413, 413, 413, 413, 413, 413, 413,
-
413, 413, 1, 1, 1, 413, 413, 413,
-
413, 413, 413, 413, 413, 413, 413, 413,
-
413, 413, 413, 413, 413, 413, 413, 413,
-
413, 413, 413, 413, 413, 413, 413, 413,
-
413, 413, 413, 413, 413, 413, 1, 302,
-
1, 1, 1, 2761, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 302, 421,
-
422, 421, 421, 421, 421, 421, 2762, 1,
-
421, 421, 6, 421, 424, 421, 421, 421,
-
421, 421, 421, 421, 421, 421, 421, 421,
-
1, 6, 349, 421, 1, 421, 1, 421,
-
421, 421, 421, 421, 421, 421, 421, 421,
-
421, 421, 421, 421, 421, 421, 421, 421,
-
421, 421, 421, 421, 421, 421, 421, 421,
-
421, 1, 1, 1, 421, 421, 421, 421,
-
421, 421, 421, 421, 421, 421, 421, 421,
-
421, 421, 421, 421, 421, 421, 421, 421,
-
421, 421, 421, 421, 421, 421, 421, 421,
-
421, 421, 421, 421, 421, 1, 2763, 1,
-
1, 1, 2764, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 2763, 695, 696,
-
695, 695, 695, 695, 695, 2765, 1, 695,
-
695, 2766, 695, 698, 695, 695, 695, 695,
-
695, 695, 695, 695, 695, 695, 695, 1,
-
2766, 354, 695, 1, 695, 1, 695, 695,
-
695, 695, 695, 695, 695, 695, 695, 695,
-
695, 695, 695, 695, 695, 695, 695, 695,
-
695, 695, 695, 695, 695, 695, 695, 695,
-
1, 1, 1, 695, 695, 695, 695, 695,
-
695, 695, 695, 695, 695, 695, 695, 695,
-
695, 695, 695, 695, 695, 695, 695, 695,
-
695, 695, 695, 695, 695, 695, 695, 695,
-
695, 695, 695, 695, 1, 120, 1, 1,
-
1, 2767, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 120, 1, 1, 1,
-
1, 1, 1, 1, 2768, 1, 1, 1,
-
6, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 6,
-
1, 1184, 1, 1, 1, 2769, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1184, 1, 1, 1, 1, 1, 1, 1,
-
2770, 1, 1, 1, 2766, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 2766, 1, 2771, 1, 1,
-
1, 2772, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 2771, 2140, 2141, 2140,
-
2140, 2140, 2140, 2140, 2773, 1, 2140, 2140,
-
2774, 2140, 2775, 2140, 2140, 2140, 2140, 2140,
-
2140, 2140, 2140, 2140, 2140, 2140, 18, 2774,
-
19, 2140, 1, 2140, 17, 2140, 2140, 2140,
-
2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140,
-
2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140,
-
2140, 2140, 2140, 2140, 2140, 2140, 2140, 1,
-
1, 1, 2140, 2140, 2140, 2140, 2140, 2140,
-
2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140,
-
2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140,
-
2140, 2140, 2140, 2140, 2140, 2140, 2140, 2140,
-
2140, 2140, 2140, 1, 2771, 1, 1, 1,
-
2772, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 2771, 14, 15, 14, 14,
-
14, 14, 14, 2773, 1, 14, 14, 2774,
-
14, 2776, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 18, 2774, 19,
-
14, 1, 14, 17, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 1, 1,
-
1, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 1, 2777, 1, 1, 1, 2778,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 2777, 32, 33, 32, 32, 32,
-
32, 32, 2779, 1, 32, 32, 2780, 32,
-
2781, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 36, 2780, 37, 32,
-
1, 32, 35, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 1, 1, 1,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 1, 2771, 1, 1, 1, 2772, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 2771, 2150, 15, 2150, 2150, 2150, 2150,
-
2150, 2773, 1, 2150, 2150, 2774, 2150, 2776,
-
2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150,
-
2150, 2150, 2150, 18, 2774, 19, 2150, 1,
-
2150, 17, 2150, 2150, 2150, 2150, 2150, 2150,
-
2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150,
-
2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150,
-
2150, 2150, 2150, 2150, 1, 1, 1, 2150,
-
2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150,
-
2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150,
-
2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150,
-
2150, 2150, 2150, 2150, 2150, 2150, 2150, 2150,
-
1, 2782, 1, 1, 1, 2783, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
2782, 2171, 2141, 2171, 2171, 2171, 2171, 2171,
-
2784, 1, 2171, 2171, 2774, 2171, 2172, 2171,
-
2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171,
-
2171, 2171, 18, 2774, 19, 2171, 1, 2171,
-
17, 2171, 2171, 2171, 2171, 2171, 2171, 2171,
-
2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171,
-
2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171,
-
2171, 2171, 2171, 1, 1, 1, 2171, 2171,
-
2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171,
-
2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171,
-
2171, 2171, 2171, 2171, 2171, 2171, 2171, 2171,
-
2171, 2171, 2171, 2171, 2171, 2171, 2171, 1,
-
2782, 1, 1, 1, 2783, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 2782,
-
14, 15, 14, 14, 14, 14, 14, 2784,
-
1, 14, 14, 2774, 14, 17, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 18, 2774, 19, 14, 1, 14, 17,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 1, 1, 1, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 1, 2785,
-
1, 1, 1, 2786, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 2785, 32,
-
33, 32, 32, 32, 32, 32, 2787, 1,
-
32, 32, 2780, 32, 35, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
36, 2780, 37, 32, 1, 32, 35, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 1, 1, 1, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 1, 2788, 1,
-
1, 1, 2789, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 2788, 2183, 2184,
-
2183, 2183, 2183, 2183, 2183, 2790, 1, 2183,
-
2183, 2774, 2183, 2143, 2183, 2183, 2183, 2183,
-
2183, 2183, 2183, 2183, 2183, 2183, 2183, 1,
-
2774, 1, 2183, 1, 2183, 1, 2183, 2183,
-
2183, 2183, 2183, 2183, 2183, 2183, 2183, 2183,
-
2183, 2183, 2183, 2183, 2183, 2183, 2183, 2183,
-
2183, 2183, 2183, 2183, 2183, 2183, 2183, 2183,
-
1, 1, 1, 2183, 2183, 2183, 2183, 2183,
-
2183, 2183, 2183, 2183, 2183, 2183, 2183, 2183,
-
2183, 2183, 2183, 2183, 2183, 2183, 2183, 2183,
-
2183, 2183, 2183, 2183, 2183, 2183, 2183, 2183,
-
2183, 2183, 2183, 2183, 1, 2788, 1, 1,
-
1, 2789, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 2788, 1, 1, 1,
-
1, 1, 1, 1, 2790, 1, 1, 1,
-
2774, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 2774,
-
1, 2791, 1, 1, 1, 2792, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
2791, 1, 1, 1, 1, 1, 1, 1,
-
2793, 1, 1, 1, 2780, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 2780, 1, 2794, 1, 1,
-
1, 2795, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 2794, 2309, 2310, 2309,
-
2309, 2309, 2309, 2309, 2796, 1, 2309, 2309,
-
2710, 2309, 2797, 2309, 2309, 2309, 2309, 2309,
-
2309, 2309, 2309, 2309, 2309, 2309, 18, 2710,
-
19, 2309, 1, 2309, 2738, 2309, 2309, 2309,
-
2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309,
-
2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309,
-
2309, 2309, 2309, 2309, 2309, 2309, 2309, 1,
-
1, 1, 2309, 2309, 2309, 2309, 2309, 2309,
-
2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309,
-
2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309,
-
2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309,
-
2309, 2309, 2309, 1, 2798, 1, 1, 1,
-
2799, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 2798, 14, 15, 14, 14,
-
14, 14, 14, 2800, 1, 14, 14, 2716,
-
14, 17, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 18, 2716, 19,
-
14, 1, 14, 2743, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 1, 1,
-
1, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 1, 2801, 1, 1, 1, 2802,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 2801, 32, 33, 32, 32, 32,
-
32, 32, 2803, 1, 32, 32, 2747, 32,
-
35, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 36, 2747, 37, 32,
-
1, 32, 2749, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 1, 1, 1,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 1, 2804, 1, 1, 1, 2805, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 2804, 2309, 2310, 2309, 2309, 2309, 2309,
-
2309, 2806, 1, 2309, 2309, 2710, 2309, 2797,
-
2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309,
-
2309, 2309, 2309, 18, 2710, 19, 2309, 1,
-
2309, 2738, 2309, 2309, 2309, 2309, 2309, 2309,
-
2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309,
-
2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309,
-
2309, 2309, 2309, 2309, 1, 1, 1, 2309,
-
2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309,
-
2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309,
-
2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309,
-
2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309,
-
1, 2807, 1, 1, 1, 2808, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
2807, 14, 15, 14, 14, 14, 14, 14,
-
2809, 1, 14, 14, 2716, 14, 1, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 1, 2716, 8, 14, 1, 14,
-
2810, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 1, 1, 1, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 1,
-
2811, 1, 1, 1, 2812, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 2811,
-
32, 33, 32, 32, 32, 32, 32, 2813,
-
1, 32, 32, 2747, 32, 1, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 1, 2747, 48, 32, 1, 32, 2814,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 1, 1, 1, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 1, 2815,
-
1, 1, 1, 2816, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 2815, 2231,
-
2184, 2231, 2231, 2231, 2231, 2231, 2817, 1,
-
2231, 2231, 2774, 2231, 2818, 2231, 2231, 2231,
-
2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231,
-
1, 2774, 1, 2231, 1, 2231, 1, 2231,
-
2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231,
-
2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231,
-
2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231,
-
2231, 1, 1, 1, 2231, 2231, 2231, 2231,
-
2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231,
-
2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231,
-
2231, 2231, 2231, 2231, 2231, 2231, 2231, 2231,
-
2231, 2231, 2231, 2231, 2231, 1, 2815, 1,
-
1, 1, 2816, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 2815, 1, 1,
-
1, 1, 1, 1, 1, 2817, 1, 1,
-
1, 2774, 1, 2236, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
2774, 1, 2819, 1, 1, 1, 2820, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 2819, 1, 1, 1, 1, 1, 1,
-
1, 2821, 1, 1, 1, 2780, 1, 2241,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 2780, 1, 2815, 1,
-
1, 1, 2816, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 2815, 2238, 1,
-
2238, 2238, 2238, 2238, 2238, 2817, 1, 2238,
-
2238, 2774, 2238, 2236, 2238, 2238, 2238, 2238,
-
2238, 2238, 2238, 2238, 2238, 2238, 2238, 1,
-
2774, 1, 2238, 1, 2238, 1, 2238, 2238,
-
2238, 2238, 2238, 2238, 2238, 2238, 2238, 2238,
-
2238, 2238, 2238, 2238, 2238, 2238, 2238, 2238,
-
2238, 2238, 2238, 2238, 2238, 2238, 2238, 2238,
-
1, 1, 1, 2238, 2238, 2238, 2238, 2238,
-
2238, 2238, 2238, 2238, 2238, 2238, 2238, 2238,
-
2238, 2238, 2238, 2238, 2238, 2238, 2238, 2238,
-
2238, 2238, 2238, 2238, 2238, 2238, 2238, 2238,
-
2238, 2238, 2238, 2238, 1, 2822, 1, 1,
-
1, 2823, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 2822, 2735, 2824, 2735,
-
2735, 2735, 2735, 2735, 2825, 1, 2735, 2735,
-
2710, 2735, 2797, 2735, 2735, 2735, 2735, 2735,
-
2735, 2735, 2735, 2735, 2735, 2735, 18, 2710,
-
19, 2735, 1, 2735, 2738, 2735, 2735, 2735,
-
2735, 2735, 2735, 2735, 2735, 2735, 2735, 2735,
-
2735, 2735, 2735, 2735, 2735, 2735, 2735, 2735,
-
2735, 2735, 2735, 2735, 2735, 2735, 2735, 1,
-
1, 1, 2735, 2735, 2735, 2735, 2735, 2735,
-
2735, 2735, 2735, 2735, 2735, 2735, 2735, 2735,
-
2735, 2735, 2735, 2735, 2735, 2735, 2735, 2735,
-
2735, 2735, 2735, 2735, 2735, 2735, 2735, 2735,
-
2735, 2735, 2735, 1, 2826, 1, 1, 1,
-
2827, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 2826, 104, 105, 104, 104,
-
104, 104, 104, 2828, 1, 104, 104, 2716,
-
104, 1, 104, 104, 104, 104, 104, 104,
-
104, 104, 104, 104, 104, 1, 2716, 8,
-
104, 1, 104, 2810, 104, 104, 104, 104,
-
104, 104, 104, 104, 104, 104, 104, 104,
-
104, 104, 104, 104, 104, 104, 104, 104,
-
104, 104, 104, 104, 104, 104, 1, 1,
-
1, 104, 104, 104, 104, 104, 104, 104,
-
104, 104, 104, 104, 104, 104, 104, 104,
-
104, 104, 104, 104, 104, 104, 104, 104,
-
104, 104, 104, 104, 104, 104, 104, 104,
-
104, 104, 1, 2829, 1, 1, 1, 2830,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 2829, 2207, 2208, 2207, 2207, 2207,
-
2207, 2207, 2831, 1, 2207, 2207, 2747, 2207,
-
1, 2207, 2207, 2207, 2207, 2207, 2207, 2207,
-
2207, 2207, 2207, 2207, 1, 2747, 48, 2207,
-
1, 2207, 2814, 2207, 2207, 2207, 2207, 2207,
-
2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207,
-
2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207,
-
2207, 2207, 2207, 2207, 2207, 1, 1, 1,
-
2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207,
-
2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207,
-
2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207,
-
2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207,
-
2207, 1, 2733, 1, 1, 1, 2734, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 2733, 2309, 2310, 2309, 2309, 2309, 2309,
-
2309, 2736, 1, 2309, 2309, 2710, 2309, 2737,
-
2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309,
-
2309, 2309, 2309, 18, 2710, 19, 2309, 1,
-
2309, 2738, 2309, 2309, 2309, 2309, 2309, 2309,
-
2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309,
-
2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309,
-
2309, 2309, 2309, 2309, 1, 1, 1, 2309,
-
2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309,
-
2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309,
-
2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309,
-
2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309,
-
1, 2832, 1, 1, 1, 2833, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
2832, 2309, 2310, 2309, 2309, 2309, 2309, 2309,
-
2834, 1, 2309, 2309, 2835, 2309, 2737, 2309,
-
2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309,
-
2309, 2309, 18, 2835, 19, 2309, 1, 2309,
-
2836, 2309, 2309, 2309, 2309, 2309, 2309, 2309,
-
2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309,
-
2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309,
-
2309, 2309, 2309, 1, 1, 1, 2309, 2309,
-
2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309,
-
2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309,
-
2309, 2309, 2309, 2309, 2309, 2309, 2309, 2309,
-
2309, 2309, 2309, 2309, 2309, 2309, 2309, 1,
-
2837, 1, 1, 1, 2838, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 2837,
-
14, 15, 14, 14, 14, 14, 14, 2839,
-
1, 14, 14, 2840, 14, 2742, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 18, 2840, 19, 14, 1, 14, 2841,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 1, 1, 1, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 1, 2842,
-
1, 1, 1, 2843, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 2842, 32,
-
33, 32, 32, 32, 32, 32, 2844, 1,
-
32, 32, 2845, 32, 2748, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
36, 2845, 37, 32, 1, 32, 2846, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 1, 1, 1, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 1, 2847, 1,
-
1, 1, 2848, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 2847, 14, 15,
-
14, 14, 14, 14, 14, 2849, 1, 14,
-
14, 2850, 14, 2742, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 18,
-
2850, 19, 14, 1, 14, 2851, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
1, 1, 1, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 1, 2852, 1, 1,
-
1, 2853, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 2852, 32, 33, 32,
-
32, 32, 32, 32, 2854, 1, 32, 32,
-
2855, 32, 2748, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 36, 2855,
-
37, 32, 1, 32, 2856, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 1,
-
1, 1, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 1, 2857, 1, 1, 1,
-
2858, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 2857, 2298, 2299, 2298, 2298,
-
2298, 2298, 2298, 2859, 1, 2298, 2298, 2710,
-
2298, 2298, 2298, 2298, 2298, 2298, 2298, 2298,
-
2298, 2298, 2298, 2298, 2298, 1, 2710, 1,
-
2298, 1, 2298, 2860, 2298, 2298, 2298, 2298,
-
2298, 2298, 2298, 2298, 2298, 2298, 2298, 2298,
-
2298, 2298, 2298, 2298, 2298, 2298, 2298, 2298,
-
2298, 2298, 2298, 2298, 2298, 2298, 1, 1,
-
1, 2298, 2298, 2298, 2298, 2298, 2298, 2298,
-
2298, 2298, 2298, 2298, 2298, 2298, 2298, 2298,
-
2298, 2298, 2298, 2298, 2298, 2298, 2298, 2298,
-
2298, 2298, 2298, 2298, 2298, 2298, 2298, 2298,
-
2298, 2298, 1, 2861, 1, 1, 1, 2862,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 2861, 1, 1, 1, 1, 1,
-
1, 1, 2863, 1, 1, 1, 2716, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 2716, 1, 1,
-
1, 1, 2810, 1, 2864, 1, 1, 1,
-
2865, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 2864, 1, 1, 1, 1,
-
1, 1, 1, 2866, 1, 1, 1, 2747,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 2747, 1,
-
1, 1, 1, 2814, 1, 2867, 1, 1,
-
1, 2868, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 2867, 32, 33, 32,
-
32, 32, 32, 32, 2869, 1, 32, 32,
-
2747, 32, 2870, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 36, 2747,
-
37, 32, 1, 32, 2871, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 1,
-
1, 1, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 1, 2872, 1, 1, 1,
-
2873, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 2872, 2557, 15, 2557, 2557,
-
2557, 2557, 2557, 2874, 1, 2557, 2557, 2722,
-
2557, 2717, 2557, 2557, 2557, 2557, 2557, 2557,
-
2557, 2557, 2557, 2557, 2557, 18, 2722, 19,
-
2557, 1, 2557, 2875, 2557, 2557, 2557, 2557,
-
2557, 2557, 2557, 2557, 2557, 2557, 2557, 2557,
-
2557, 2557, 2557, 2557, 2557, 2557, 2557, 2557,
-
2557, 2557, 2557, 2557, 2557, 2557, 1, 1,
-
1, 2557, 2557, 2557, 2557, 2557, 2557, 2557,
-
2557, 2557, 2557, 2557, 2557, 2557, 2557, 2557,
-
2557, 2557, 2557, 2557, 2557, 2557, 2557, 2557,
-
2557, 2557, 2557, 2557, 2557, 2557, 2557, 2557,
-
2557, 2557, 1, 2872, 1, 1, 1, 2873,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 2872, 14, 15, 14, 14, 14,
-
14, 14, 2874, 1, 14, 14, 2722, 14,
-
2717, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 18, 2722, 19, 14,
-
1, 14, 2875, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 1, 1, 1,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 1, 2876, 1, 1, 1, 2877, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 2876, 32, 33, 32, 32, 32, 32,
-
32, 2878, 1, 32, 32, 2726, 32, 2870,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 36, 2726, 37, 32, 1,
-
32, 2879, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 1, 1, 1, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
1, 2186, 1, 1, 1, 2880, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
2186, 2578, 2579, 2578, 2578, 2578, 2578, 2578,
-
2790, 1, 2578, 2578, 2774, 2578, 2572, 2578,
-
2578, 2578, 2578, 2578, 2578, 2578, 2578, 2578,
-
2578, 2578, 1, 2774, 1, 2578, 1, 2578,
-
1, 2578, 2578, 2578, 2578, 2578, 2578, 2578,
-
2578, 2578, 2578, 2578, 2578, 2578, 2578, 2578,
-
2578, 2578, 2578, 2578, 2578, 2578, 2578, 2578,
-
2578, 2578, 2578, 1, 1, 1, 2578, 2578,
-
2578, 2578, 2578, 2578, 2578, 2578, 2578, 2578,
-
2578, 2578, 2578, 2578, 2578, 2578, 2578, 2578,
-
2578, 2578, 2578, 2578, 2578, 2578, 2578, 2578,
-
2578, 2578, 2578, 2578, 2578, 2578, 2578, 1,
-
2881, 1, 1, 1, 2882, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 2881,
-
2684, 2685, 2684, 2684, 2684, 2684, 2684, 2883,
-
1, 2684, 2684, 2710, 2684, 2884, 2684, 2684,
-
2684, 2684, 2684, 2684, 2684, 2684, 2684, 2684,
-
2684, 18, 2710, 19, 2684, 1, 2684, 2712,
-
2684, 2684, 2684, 2684, 2684, 2684, 2684, 2684,
-
2684, 2684, 2684, 2684, 2684, 2684, 2684, 2684,
-
2684, 2684, 2684, 2684, 2684, 2684, 2684, 2684,
-
2684, 2684, 1, 1, 1, 2684, 2684, 2684,
-
2684, 2684, 2684, 2684, 2684, 2684, 2684, 2684,
-
2684, 2684, 2684, 2684, 2684, 2684, 2684, 2684,
-
2684, 2684, 2684, 2684, 2684, 2684, 2684, 2684,
-
2684, 2684, 2684, 2684, 2684, 2684, 1, 2885,
-
1, 1, 1, 2886, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 2885, 14,
-
15, 14, 14, 14, 14, 14, 2887, 1,
-
14, 14, 2716, 14, 17, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
18, 2716, 19, 14, 1, 14, 2718, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 1, 1, 1, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 1, 2888, 1,
-
1, 1, 2889, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 2888, 32, 33,
-
32, 32, 32, 32, 32, 2890, 1, 32,
-
32, 2747, 32, 35, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 36,
-
2747, 37, 32, 1, 32, 2871, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
1, 1, 1, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 1, 2891, 1, 1,
-
1, 2892, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 2891, 2684, 2685, 2684,
-
2684, 2684, 2684, 2684, 2893, 1, 2684, 2684,
-
2710, 2684, 2884, 2684, 2684, 2684, 2684, 2684,
-
2684, 2684, 2684, 2684, 2684, 2684, 18, 2710,
-
19, 2684, 1, 2684, 2712, 2684, 2684, 2684,
-
2684, 2684, 2684, 2684, 2684, 2684, 2684, 2684,
-
2684, 2684, 2684, 2684, 2684, 2684, 2684, 2684,
-
2684, 2684, 2684, 2684, 2684, 2684, 2684, 1,
-
1, 1, 2684, 2684, 2684, 2684, 2684, 2684,
-
2684, 2684, 2684, 2684, 2684, 2684, 2684, 2684,
-
2684, 2684, 2684, 2684, 2684, 2684, 2684, 2684,
-
2684, 2684, 2684, 2684, 2684, 2684, 2684, 2684,
-
2684, 2684, 2684, 1, 2894, 1, 1, 1,
-
2895, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 2894, 14, 15, 14, 14,
-
14, 14, 14, 2896, 1, 14, 14, 2716,
-
14, 1, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 1, 2716, 8,
-
14, 1, 14, 2897, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 1, 1,
-
1, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 1, 2898, 1, 1, 1, 2899,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 2898, 32, 33, 32, 32, 32,
-
32, 32, 2900, 1, 32, 32, 2747, 32,
-
1, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 1, 2747, 48, 32,
-
1, 32, 2901, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 1, 1, 1,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 1, 2235, 1, 1, 1, 2902, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 2235, 2621, 2579, 2621, 2621, 2621, 2621,
-
2621, 2817, 1, 2621, 2621, 2774, 2621, 2903,
-
2621, 2621, 2621, 2621, 2621, 2621, 2621, 2621,
-
2621, 2621, 2621, 1, 2774, 1, 2621, 1,
-
2621, 1, 2621, 2621, 2621, 2621, 2621, 2621,
-
2621, 2621, 2621, 2621, 2621, 2621, 2621, 2621,
-
2621, 2621, 2621, 2621, 2621, 2621, 2621, 2621,
-
2621, 2621, 2621, 2621, 1, 1, 1, 2621,
-
2621, 2621, 2621, 2621, 2621, 2621, 2621, 2621,
-
2621, 2621, 2621, 2621, 2621, 2621, 2621, 2621,
-
2621, 2621, 2621, 2621, 2621, 2621, 2621, 2621,
-
2621, 2621, 2621, 2621, 2621, 2621, 2621, 2621,
-
1, 2904, 1, 1, 1, 2905, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
2904, 2708, 2906, 2708, 2708, 2708, 2708, 2708,
-
2907, 1, 2708, 2708, 2710, 2708, 2884, 2708,
-
2708, 2708, 2708, 2708, 2708, 2708, 2708, 2708,
-
2708, 2708, 18, 2710, 19, 2708, 1, 2708,
-
2712, 2708, 2708, 2708, 2708, 2708, 2708, 2708,
-
2708, 2708, 2708, 2708, 2708, 2708, 2708, 2708,
-
2708, 2708, 2708, 2708, 2708, 2708, 2708, 2708,
-
2708, 2708, 2708, 1, 1, 1, 2708, 2708,
-
2708, 2708, 2708, 2708, 2708, 2708, 2708, 2708,
-
2708, 2708, 2708, 2708, 2708, 2708, 2708, 2708,
-
2708, 2708, 2708, 2708, 2708, 2708, 2708, 2708,
-
2708, 2708, 2708, 2708, 2708, 2708, 2708, 1,
-
2908, 1, 1, 1, 2909, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 2908,
-
2557, 2558, 2557, 2557, 2557, 2557, 2557, 2910,
-
1, 2557, 2557, 2716, 2557, 1, 2557, 2557,
-
2557, 2557, 2557, 2557, 2557, 2557, 2557, 2557,
-
2557, 1, 2716, 8, 2557, 1, 2557, 2897,
-
2557, 2557, 2557, 2557, 2557, 2557, 2557, 2557,
-
2557, 2557, 2557, 2557, 2557, 2557, 2557, 2557,
-
2557, 2557, 2557, 2557, 2557, 2557, 2557, 2557,
-
2557, 2557, 1, 1, 1, 2557, 2557, 2557,
-
2557, 2557, 2557, 2557, 2557, 2557, 2557, 2557,
-
2557, 2557, 2557, 2557, 2557, 2557, 2557, 2557,
-
2557, 2557, 2557, 2557, 2557, 2557, 2557, 2557,
-
2557, 2557, 2557, 2557, 2557, 2557, 1, 2911,
-
1, 1, 1, 2912, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 2911, 2597,
-
2598, 2597, 2597, 2597, 2597, 2597, 2913, 1,
-
2597, 2597, 2747, 2597, 1, 2597, 2597, 2597,
-
2597, 2597, 2597, 2597, 2597, 2597, 2597, 2597,
-
1, 2747, 48, 2597, 1, 2597, 2901, 2597,
-
2597, 2597, 2597, 2597, 2597, 2597, 2597, 2597,
-
2597, 2597, 2597, 2597, 2597, 2597, 2597, 2597,
-
2597, 2597, 2597, 2597, 2597, 2597, 2597, 2597,
-
2597, 1, 1, 1, 2597, 2597, 2597, 2597,
-
2597, 2597, 2597, 2597, 2597, 2597, 2597, 2597,
-
2597, 2597, 2597, 2597, 2597, 2597, 2597, 2597,
-
2597, 2597, 2597, 2597, 2597, 2597, 2597, 2597,
-
2597, 2597, 2597, 2597, 2597, 1, 2706, 1,
-
1, 1, 2707, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 2706, 2684, 2685,
-
2684, 2684, 2684, 2684, 2684, 2709, 1, 2684,
-
2684, 2710, 2684, 2711, 2684, 2684, 2684, 2684,
-
2684, 2684, 2684, 2684, 2684, 2684, 2684, 18,
-
2710, 19, 2684, 1, 2684, 2712, 2684, 2684,
-
2684, 2684, 2684, 2684, 2684, 2684, 2684, 2684,
-
2684, 2684, 2684, 2684, 2684, 2684, 2684, 2684,
-
2684, 2684, 2684, 2684, 2684, 2684, 2684, 2684,
-
1, 1, 1, 2684, 2684, 2684, 2684, 2684,
-
2684, 2684, 2684, 2684, 2684, 2684, 2684, 2684,
-
2684, 2684, 2684, 2684, 2684, 2684, 2684, 2684,
-
2684, 2684, 2684, 2684, 2684, 2684, 2684, 2684,
-
2684, 2684, 2684, 2684, 1, 2914, 1, 1,
-
1, 2915, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 2914, 2684, 2685, 2684,
-
2684, 2684, 2684, 2684, 2916, 1, 2684, 2684,
-
2835, 2684, 2711, 2684, 2684, 2684, 2684, 2684,
-
2684, 2684, 2684, 2684, 2684, 2684, 18, 2835,
-
19, 2684, 1, 2684, 2917, 2684, 2684, 2684,
-
2684, 2684, 2684, 2684, 2684, 2684, 2684, 2684,
-
2684, 2684, 2684, 2684, 2684, 2684, 2684, 2684,
-
2684, 2684, 2684, 2684, 2684, 2684, 2684, 1,
-
1, 1, 2684, 2684, 2684, 2684, 2684, 2684,
-
2684, 2684, 2684, 2684, 2684, 2684, 2684, 2684,
-
2684, 2684, 2684, 2684, 2684, 2684, 2684, 2684,
-
2684, 2684, 2684, 2684, 2684, 2684, 2684, 2684,
-
2684, 2684, 2684, 1, 2918, 1, 1, 1,
-
2919, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 2918, 14, 15, 14, 14,
-
14, 14, 14, 2920, 1, 14, 14, 2840,
-
14, 2717, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 18, 2840, 19,
-
14, 1, 14, 2921, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 1, 1,
-
1, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 1, 2922, 1, 1, 1, 2923,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 2922, 32, 33, 32, 32, 32,
-
32, 32, 2924, 1, 32, 32, 2845, 32,
-
2870, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 36, 2845, 37, 32,
-
1, 32, 2925, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 1, 1, 1,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 1, 2926, 1, 1, 1, 2927, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 2926, 14, 15, 14, 14, 14, 14,
-
14, 2928, 1, 14, 14, 2850, 14, 2717,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 18, 2850, 19, 14, 1,
-
14, 2929, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 1, 1, 1, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
1, 2930, 1, 1, 1, 2931, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
2930, 32, 33, 32, 32, 32, 32, 32,
-
2932, 1, 32, 32, 2855, 32, 2870, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 36, 2855, 37, 32, 1, 32,
-
2933, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 1, 1, 1, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 1,
-
2934, 1, 1, 1, 2935, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 2934,
-
2673, 2674, 2673, 2673, 2673, 2673, 2673, 2936,
-
1, 2673, 2673, 2710, 2673, 2673, 2673, 2673,
-
2673, 2673, 2673, 2673, 2673, 2673, 2673, 2673,
-
2673, 1, 2710, 1, 2673, 1, 2673, 2937,
-
2673, 2673, 2673, 2673, 2673, 2673, 2673, 2673,
-
2673, 2673, 2673, 2673, 2673, 2673, 2673, 2673,
-
2673, 2673, 2673, 2673, 2673, 2673, 2673, 2673,
-
2673, 2673, 1, 1, 1, 2673, 2673, 2673,
-
2673, 2673, 2673, 2673, 2673, 2673, 2673, 2673,
-
2673, 2673, 2673, 2673, 2673, 2673, 2673, 2673,
-
2673, 2673, 2673, 2673, 2673, 2673, 2673, 2673,
-
2673, 2673, 2673, 2673, 2673, 2673, 1, 2938,
-
1, 1, 1, 2939, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 2938, 1,
-
1, 1, 1, 1, 1, 1, 2940, 1,
-
1, 1, 2716, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 2716, 1, 1, 1, 1, 2897, 1,
-
2941, 1, 1, 1, 2942, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 2941,
-
1, 1, 1, 1, 1, 1, 1, 2943,
-
1, 1, 1, 2747, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 2747, 1, 1, 1, 1, 2901,
-
1, 1, 0
-
]
-
-
1
class << self
-
1
attr_accessor :_address_lists_trans_targs
-
1
private :_address_lists_trans_targs, :_address_lists_trans_targs=
-
end
-
1
self._address_lists_trans_targs = [
-
1, 0, 2, 1335, 1302, 1314, 1339, 1318,
-
20, 3, 5, 1336, 6, 7, 6, 9,
-
14, 15, 49, 20, 8, 10, 11, 6,
-
13, 10, 11, 6, 13, 12, 6, 7,
-
6, 9, 14, 15, 49, 20, 16, 17,
-
19, 16, 17, 19, 18, 16, 17, 19,
-
20, 21, 22, 24, 1184, 1201, 1202, 1224,
-
1206, 21, 22, 1201, 1202, 1206, 23, 25,
-
26, 24, 1158, 28, 1170, 1337, 1119, 25,
-
26, 28, 29, 1337, 1119, 27, 25, 26,
-
28, 29, 1337, 1119, 30, 32, 1152, 1157,
-
31, 33, 34, 36, 1119, 35, 33, 34,
-
36, 1119, 38, 40, 42, 1341, 44, 45,
-
1343, 1052, 1057, 44, 45, 1057, 46, 48,
-
1344, 50, 51, 53, 96, 1008, 1009, 877,
-
1349, 71, 66, 50, 51, 53, 96, 1008,
-
864, 71, 52, 54, 55, 53, 492, 531,
-
92, 521, 1346, 71, 433, 54, 55, 57,
-
60, 531, 92, 105, 1346, 433, 56, 54,
-
57, 58, 65, 66, 59, 61, 62, 57,
-
64, 61, 62, 57, 64, 63, 57, 58,
-
57, 60, 65, 66, 71, 67, 68, 70,
-
67, 68, 70, 69, 67, 68, 70, 71,
-
72, 73, 75, 943, 960, 961, 983, 965,
-
72, 73, 960, 961, 965, 74, 76, 77,
-
75, 917, 79, 929, 88, 878, 76, 77,
-
79, 80, 88, 878, 78, 76, 77, 79,
-
80, 88, 878, 81, 83, 911, 916, 82,
-
84, 85, 87, 878, 86, 84, 85, 87,
-
878, 89, 91, 92, 1346, 90, 89, 91,
-
92, 1346, 93, 94, 544, 545, 93, 94,
-
544, 95, 97, 537, 538, 540, 536, 97,
-
98, 100, 536, 99, 101, 102, 487, 104,
-
92, 1346, 433, 101, 102, 104, 92, 1346,
-
433, 103, 101, 101, 102, 104, 92, 105,
-
1346, 433, 106, 107, 109, 481, 486, 106,
-
107, 486, 108, 110, 111, 113, 433, 112,
-
110, 110, 111, 113, 433, 115, 1347, 117,
-
118, 116, 236, 275, 155, 265, 134, 177,
-
117, 118, 120, 123, 275, 155, 168, 177,
-
119, 117, 120, 121, 128, 129, 122, 124,
-
125, 120, 127, 124, 125, 120, 127, 126,
-
120, 121, 120, 123, 128, 129, 134, 130,
-
131, 133, 130, 131, 133, 134, 132, 130,
-
131, 133, 134, 135, 136, 138, 368, 385,
-
386, 408, 390, 135, 136, 385, 386, 390,
-
137, 139, 140, 138, 342, 142, 354, 151,
-
303, 139, 140, 142, 143, 151, 303, 141,
-
139, 140, 142, 143, 151, 303, 144, 146,
-
336, 341, 145, 147, 148, 150, 303, 149,
-
147, 148, 150, 303, 152, 154, 155, 153,
-
152, 154, 155, 156, 157, 116, 159, 288,
-
302, 134, 129, 156, 157, 116, 159, 288,
-
289, 158, 160, 281, 282, 284, 280, 160,
-
161, 163, 280, 162, 164, 165, 231, 167,
-
155, 177, 164, 165, 167, 155, 177, 166,
-
164, 164, 165, 167, 155, 168, 177, 169,
-
170, 172, 225, 230, 169, 170, 230, 171,
-
173, 174, 176, 177, 175, 173, 173, 174,
-
176, 177, 178, 179, 181, 192, 207, 201,
-
221, 178, 179, 181, 192, 207, 208, 221,
-
180, 182, 183, 185, 155, 206, 1346, 186,
-
184, 182, 182, 183, 185, 155, 186, 1346,
-
187, 188, 190, 191, 187, 188, 191, 189,
-
187, 188, 190, 191, 193, 203, 204, 202,
-
193, 194, 196, 202, 195, 197, 198, 196,
-
200, 201, 199, 197, 197, 198, 200, 203,
-
204, 205, 178, 179, 181, 192, 207, 208,
-
221, 209, 214, 210, 211, 213, 212, 210,
-
211, 213, 215, 219, 218, 216, 217, 220,
-
222, 224, 223, 226, 227, 173, 229, 226,
-
227, 173, 229, 228, 169, 170, 172, 225,
-
230, 232, 233, 235, 244, 232, 233, 235,
-
234, 232, 232, 233, 235, 155, 1346, 177,
-
237, 241, 242, 240, 237, 238, 231, 240,
-
239, 241, 242, 243, 245, 246, 248, 245,
-
246, 248, 249, 247, 245, 246, 248, 249,
-
250, 251, 253, 214, 264, 208, 250, 251,
-
253, 264, 252, 254, 255, 257, 263, 258,
-
256, 254, 255, 257, 258, 259, 261, 262,
-
260, 259, 261, 262, 250, 251, 253, 214,
-
264, 266, 267, 270, 269, 266, 267, 269,
-
268, 266, 267, 269, 271, 277, 278, 276,
-
271, 272, 274, 276, 273, 117, 118, 275,
-
277, 278, 279, 281, 282, 284, 283, 284,
-
285, 287, 155, 1346, 177, 286, 284, 284,
-
285, 287, 155, 1346, 177, 156, 157, 116,
-
159, 288, 289, 290, 295, 289, 291, 292,
-
294, 249, 291, 292, 294, 293, 291, 292,
-
294, 296, 300, 299, 297, 298, 301, 302,
-
304, 305, 307, 317, 331, 326, 332, 304,
-
305, 307, 317, 331, 326, 332, 306, 308,
-
309, 311, 330, 151, 312, 310, 308, 309,
-
311, 312, 151, 313, 315, 316, 314, 313,
-
315, 316, 318, 328, 327, 319, 321, 320,
-
322, 323, 325, 324, 322, 323, 325, 329,
-
304, 305, 307, 317, 331, 326, 332, 333,
-
335, 334, 337, 338, 147, 340, 337, 338,
-
147, 340, 339, 144, 146, 336, 341, 343,
-
352, 351, 344, 346, 345, 347, 348, 350,
-
347, 348, 350, 349, 347, 348, 350, 353,
-
355, 356, 359, 358, 355, 356, 358, 357,
-
355, 356, 358, 360, 365, 366, 364, 360,
-
361, 363, 364, 362, 365, 366, 367, 369,
-
378, 379, 381, 377, 369, 370, 372, 377,
-
371, 373, 374, 376, 303, 373, 374, 376,
-
303, 375, 373, 374, 376, 303, 378, 379,
-
381, 380, 381, 382, 384, 303, 383, 381,
-
382, 384, 303, 135, 136, 138, 368, 385,
-
386, 408, 390, 387, 389, 388, 387, 389,
-
391, 392, 394, 414, 428, 423, 429, 391,
-
392, 394, 414, 428, 423, 429, 393, 395,
-
396, 398, 399, 427, 403, 409, 397, 395,
-
396, 398, 399, 409, 403, 399, 400, 402,
-
403, 401, 399, 400, 402, 403, 404, 405,
-
138, 368, 407, 408, 404, 405, 407, 406,
-
404, 405, 407, 408, 410, 412, 413, 411,
-
410, 412, 413, 415, 425, 424, 416, 418,
-
417, 419, 420, 422, 421, 419, 420, 422,
-
426, 391, 392, 394, 414, 428, 423, 429,
-
430, 432, 431, 434, 435, 437, 448, 463,
-
457, 477, 434, 435, 437, 448, 463, 464,
-
477, 436, 438, 439, 441, 92, 462, 442,
-
440, 438, 438, 439, 441, 92, 442, 443,
-
444, 446, 447, 443, 444, 447, 445, 443,
-
444, 446, 447, 449, 459, 460, 458, 449,
-
450, 452, 458, 451, 453, 454, 452, 456,
-
457, 455, 453, 453, 454, 456, 459, 460,
-
461, 434, 435, 437, 448, 463, 464, 477,
-
465, 470, 466, 467, 469, 468, 466, 467,
-
469, 471, 475, 474, 472, 473, 476, 478,
-
480, 479, 482, 483, 110, 485, 482, 483,
-
110, 485, 484, 106, 107, 109, 481, 486,
-
488, 489, 491, 500, 488, 489, 491, 490,
-
488, 488, 489, 491, 92, 433, 493, 497,
-
498, 496, 493, 494, 487, 496, 495, 497,
-
498, 499, 501, 502, 504, 501, 502, 504,
-
505, 503, 501, 502, 504, 505, 506, 507,
-
509, 470, 520, 464, 506, 507, 509, 520,
-
508, 510, 511, 513, 519, 514, 512, 510,
-
511, 513, 514, 515, 517, 518, 516, 515,
-
517, 518, 506, 507, 509, 470, 520, 522,
-
523, 526, 525, 522, 523, 525, 524, 522,
-
523, 525, 527, 533, 534, 532, 527, 528,
-
530, 532, 529, 54, 55, 531, 533, 534,
-
535, 537, 538, 540, 539, 540, 541, 543,
-
92, 433, 542, 540, 540, 541, 543, 92,
-
433, 93, 94, 53, 96, 544, 545, 864,
-
1349, 546, 547, 549, 832, 849, 863, 567,
-
562, 546, 547, 549, 832, 849, 850, 567,
-
548, 550, 551, 549, 784, 720, 545, 818,
-
1349, 567, 730, 550, 551, 553, 556, 720,
-
545, 721, 1349, 730, 552, 550, 553, 554,
-
561, 562, 555, 557, 558, 553, 560, 557,
-
558, 553, 560, 559, 553, 554, 553, 556,
-
561, 562, 567, 563, 564, 566, 563, 564,
-
566, 565, 563, 564, 566, 567, 568, 569,
-
571, 655, 672, 673, 695, 677, 568, 569,
-
672, 673, 677, 570, 572, 573, 571, 629,
-
575, 641, 584, 590, 572, 573, 575, 576,
-
584, 590, 574, 572, 573, 575, 576, 584,
-
590, 577, 579, 623, 628, 578, 580, 581,
-
583, 590, 582, 580, 581, 583, 590, 585,
-
587, 545, 1349, 586, 585, 587, 545, 1349,
-
589, 591, 592, 594, 604, 618, 613, 619,
-
591, 592, 594, 604, 618, 613, 619, 593,
-
595, 596, 598, 617, 584, 599, 597, 595,
-
596, 598, 599, 584, 600, 602, 603, 601,
-
600, 602, 603, 605, 615, 614, 606, 608,
-
607, 609, 610, 612, 611, 609, 610, 612,
-
616, 591, 592, 594, 604, 618, 613, 619,
-
620, 622, 621, 624, 625, 580, 627, 624,
-
625, 580, 627, 626, 577, 579, 623, 628,
-
630, 639, 638, 631, 633, 632, 634, 635,
-
637, 634, 635, 637, 636, 634, 635, 637,
-
640, 642, 643, 646, 645, 642, 643, 645,
-
644, 642, 643, 645, 647, 652, 653, 651,
-
647, 648, 650, 651, 649, 652, 653, 654,
-
656, 665, 666, 668, 664, 656, 657, 659,
-
664, 658, 660, 661, 663, 590, 660, 661,
-
663, 590, 662, 660, 661, 663, 590, 665,
-
666, 668, 667, 668, 669, 671, 590, 670,
-
668, 669, 671, 590, 568, 569, 571, 655,
-
672, 673, 695, 677, 674, 676, 675, 674,
-
676, 678, 679, 681, 701, 715, 710, 716,
-
678, 679, 681, 701, 715, 710, 716, 680,
-
682, 683, 685, 686, 714, 690, 696, 684,
-
682, 683, 685, 686, 696, 690, 686, 687,
-
689, 690, 688, 686, 687, 689, 690, 691,
-
692, 571, 655, 694, 695, 691, 692, 694,
-
693, 691, 692, 694, 695, 697, 699, 700,
-
698, 697, 699, 700, 702, 712, 711, 703,
-
705, 704, 706, 707, 709, 708, 706, 707,
-
709, 713, 678, 679, 681, 701, 715, 710,
-
716, 717, 719, 718, 550, 551, 720, 545,
-
721, 1349, 730, 722, 723, 725, 778, 783,
-
722, 723, 783, 724, 726, 727, 729, 730,
-
728, 726, 726, 727, 729, 730, 731, 732,
-
734, 745, 760, 754, 774, 731, 732, 734,
-
745, 760, 761, 774, 733, 735, 736, 738,
-
545, 759, 1349, 739, 737, 735, 735, 736,
-
738, 545, 739, 1349, 740, 741, 743, 744,
-
740, 741, 744, 742, 740, 741, 743, 744,
-
746, 756, 757, 755, 746, 747, 749, 755,
-
748, 750, 751, 749, 753, 754, 752, 750,
-
750, 751, 753, 756, 757, 758, 731, 732,
-
734, 745, 760, 761, 774, 762, 767, 763,
-
764, 766, 765, 763, 764, 766, 768, 772,
-
771, 769, 770, 773, 775, 777, 776, 779,
-
780, 726, 782, 779, 780, 726, 782, 781,
-
722, 723, 725, 778, 783, 785, 815, 816,
-
814, 785, 786, 788, 814, 787, 789, 790,
-
788, 792, 793, 789, 790, 792, 791, 789,
-
789, 790, 792, 794, 795, 797, 794, 795,
-
797, 798, 796, 794, 795, 797, 798, 799,
-
800, 802, 767, 813, 761, 799, 800, 802,
-
813, 801, 803, 804, 806, 812, 807, 805,
-
803, 804, 806, 807, 808, 810, 811, 809,
-
808, 810, 811, 799, 800, 802, 767, 813,
-
815, 816, 817, 819, 820, 823, 822, 819,
-
820, 822, 821, 819, 820, 822, 824, 829,
-
830, 828, 824, 825, 827, 828, 826, 829,
-
830, 831, 833, 842, 843, 845, 841, 833,
-
834, 836, 841, 835, 837, 838, 840, 545,
-
1349, 730, 837, 838, 840, 545, 1349, 730,
-
839, 837, 837, 838, 840, 545, 1349, 730,
-
842, 843, 845, 844, 845, 846, 848, 545,
-
1349, 730, 847, 845, 845, 846, 848, 545,
-
1349, 730, 546, 547, 549, 832, 849, 850,
-
851, 856, 850, 852, 853, 855, 798, 852,
-
853, 855, 854, 852, 853, 855, 857, 861,
-
860, 858, 859, 862, 863, 865, 870, 864,
-
866, 867, 869, 505, 866, 867, 869, 868,
-
866, 867, 869, 871, 875, 874, 872, 873,
-
876, 877, 879, 880, 882, 892, 906, 901,
-
907, 879, 880, 882, 892, 906, 901, 907,
-
881, 883, 884, 886, 905, 88, 887, 885,
-
883, 884, 886, 887, 88, 888, 890, 891,
-
889, 888, 890, 891, 893, 903, 902, 894,
-
896, 895, 897, 898, 900, 899, 897, 898,
-
900, 904, 879, 880, 882, 892, 906, 901,
-
907, 908, 910, 909, 912, 913, 84, 915,
-
912, 913, 84, 915, 914, 81, 83, 911,
-
916, 918, 927, 926, 919, 921, 920, 922,
-
923, 925, 922, 923, 925, 924, 922, 923,
-
925, 928, 930, 931, 934, 933, 930, 931,
-
933, 932, 930, 931, 933, 935, 940, 941,
-
939, 935, 936, 938, 939, 937, 940, 941,
-
942, 944, 953, 954, 956, 952, 944, 945,
-
947, 952, 946, 948, 949, 951, 878, 948,
-
949, 951, 878, 950, 948, 949, 951, 878,
-
953, 954, 956, 955, 956, 957, 959, 878,
-
958, 956, 957, 959, 878, 72, 73, 75,
-
943, 960, 961, 983, 965, 962, 964, 963,
-
962, 964, 966, 967, 969, 989, 1003, 998,
-
1004, 966, 967, 969, 989, 1003, 998, 1004,
-
968, 970, 971, 973, 974, 1002, 978, 984,
-
972, 970, 971, 973, 974, 984, 978, 974,
-
975, 977, 978, 976, 974, 975, 977, 978,
-
979, 980, 75, 943, 982, 983, 979, 980,
-
982, 981, 979, 980, 982, 983, 985, 987,
-
988, 986, 985, 987, 988, 990, 1000, 999,
-
991, 993, 992, 994, 995, 997, 996, 994,
-
995, 997, 1001, 966, 967, 969, 989, 1003,
-
998, 1004, 1005, 1007, 1006, 50, 51, 1008,
-
1009, 1010, 1011, 1013, 1010, 1011, 1013, 1012,
-
1010, 1011, 1013, 1015, 1016, 1351, 1025, 1037,
-
1031, 1048, 1015, 1016, 1351, 1025, 1037, 1038,
-
1048, 1017, 1019, 1352, 1021, 1022, 1354, 1024,
-
1021, 1022, 1024, 1023, 1021, 1022, 1354, 1024,
-
1026, 1033, 1034, 1032, 1026, 1027, 1355, 1032,
-
1028, 1030, 1356, 1355, 1031, 1033, 1034, 1035,
-
1015, 1016, 1351, 1025, 1037, 1038, 1048, 1358,
-
1041, 1040, 1359, 1042, 1046, 1045, 1043, 1044,
-
1047, 1049, 1051, 1050, 1053, 1054, 1344, 1056,
-
1053, 1054, 1344, 1056, 1055, 44, 45, 1343,
-
1052, 1057, 1059, 1079, 1080, 1078, 1059, 1060,
-
1361, 1078, 1061, 1063, 1362, 1065, 1365, 1067,
-
1068, 1367, 1041, 1077, 1038, 1067, 1068, 1367,
-
1077, 1069, 1071, 1368, 1072, 1073, 1370, 1075,
-
1074, 1072, 1073, 1370, 1075, 1067, 1068, 1367,
-
1041, 1077, 1079, 1080, 1081, 1083, 1372, 1085,
-
1089, 1090, 1088, 1085, 1086, 1374, 1088, 1087,
-
1089, 1090, 1091, 1093, 1099, 1100, 1378, 1098,
-
1093, 1094, 1375, 1098, 1095, 1097, 1376, 1099,
-
1100, 1378, 1101, 1103, 1378, 1105, 1106, 1340,
-
1092, 1104, 1108, 1105, 1106, 1340, 1092, 1104,
-
1108, 1107, 1380, 1111, 1108, 1110, 1381, 1112,
-
1116, 1115, 1113, 1114, 1117, 1361, 1058, 1118,
-
1120, 1121, 1123, 1133, 1147, 1142, 1148, 1120,
-
1121, 1123, 1133, 1147, 1142, 1148, 1122, 1124,
-
1125, 1127, 1146, 1337, 1128, 1126, 1124, 1125,
-
1127, 1128, 1337, 1129, 1131, 1132, 1130, 1129,
-
1131, 1132, 1134, 1144, 1143, 1135, 1137, 1136,
-
1138, 1139, 1141, 1140, 1138, 1139, 1141, 1145,
-
1120, 1121, 1123, 1133, 1147, 1142, 1148, 1149,
-
1151, 1150, 1153, 1154, 33, 1156, 1153, 1154,
-
33, 1156, 1155, 30, 32, 1152, 1157, 1159,
-
1168, 1167, 1160, 1162, 1161, 1163, 1164, 1166,
-
1163, 1164, 1166, 1165, 1163, 1164, 1166, 1169,
-
1171, 1172, 1175, 1174, 1171, 1172, 1174, 1173,
-
1171, 1172, 1174, 1176, 1181, 1182, 1180, 1176,
-
1177, 1179, 1180, 1178, 1181, 1182, 1183, 1185,
-
1194, 1195, 1197, 1193, 1185, 1186, 1188, 1193,
-
1187, 1189, 1190, 1192, 1119, 1189, 1190, 1192,
-
1119, 1191, 1189, 1190, 1192, 1119, 1194, 1195,
-
1197, 1196, 1197, 1198, 1200, 1119, 1199, 1197,
-
1198, 1200, 1119, 21, 22, 24, 1184, 1201,
-
1202, 1224, 1206, 1203, 1205, 1204, 1203, 1205,
-
1207, 1208, 1210, 1230, 1244, 1239, 1245, 1207,
-
1208, 1210, 1230, 1244, 1239, 1245, 1209, 1211,
-
1212, 1214, 1215, 1243, 1219, 1225, 1213, 1211,
-
1212, 1214, 1215, 1225, 1219, 1215, 1216, 1218,
-
1219, 1217, 1215, 1216, 1218, 1219, 1220, 1221,
-
24, 1184, 1223, 1224, 1220, 1221, 1223, 1222,
-
1220, 1221, 1223, 1224, 1226, 1228, 1229, 1227,
-
1226, 1228, 1229, 1231, 1241, 1240, 1232, 1234,
-
1233, 1235, 1236, 1238, 1237, 1235, 1236, 1238,
-
1242, 1207, 1208, 1210, 1230, 1244, 1239, 1245,
-
1246, 1248, 1247, 1250, 1251, 1384, 1268, 1273,
-
1250, 1251, 1273, 1252, 1254, 1385, 1256, 1257,
-
1259, 1256, 1257, 1259, 1260, 1258, 1256, 1257,
-
1259, 1260, 1387, 1261, 1262, 1266, 1265, 1263,
-
1264, 1267, 1269, 1270, 1385, 1272, 1269, 1270,
-
1385, 1272, 1271, 1250, 1251, 1384, 1268, 1273,
-
1275, 1289, 1290, 1288, 1275, 1276, 1388, 1288,
-
1277, 1279, 1389, 1281, 1392, 1283, 1284, 1394,
-
1261, 1287, 1260, 1283, 1284, 1394, 1287, 1285,
-
1283, 1284, 1394, 1261, 1287, 1289, 1290, 1291,
-
1293, 1396, 1295, 1299, 1300, 1298, 1295, 1296,
-
1398, 1298, 1297, 1299, 1300, 1301, 1303, 1309,
-
1310, 1402, 1308, 1303, 1304, 1399, 1308, 1305,
-
1307, 1400, 1309, 1310, 1402, 1311, 1313, 1402,
-
1315, 1316, 1335, 1302, 1314, 1318, 1315, 1316,
-
1317, 1404, 1321, 1318, 1320, 1405, 1322, 1326,
-
1325, 1323, 1324, 1327, 1388, 1274, 1328, 1330,
-
1331, 1330, 1407, 1333, 1330, 1331, 1330, 1407,
-
1333, 1332, 1, 2, 1335, 1302, 1314, 1328,
-
20, 15, 1336, 4, 1335, 1383, 1339, 1395,
-
1255, 1336, 4, 1383, 1339, 1249, 1255, 1337,
-
37, 1338, 1339, 1337, 37, 1338, 1339, 1339,
-
39, 1340, 1092, 1104, 1118, 1341, 41, 1340,
-
1342, 1371, 1014, 1341, 41, 1342, 43, 1014,
-
1341, 41, 1342, 1339, 43, 1014, 1344, 47,
-
1345, 1014, 1344, 47, 1345, 1014, 1347, 114,
-
1348, 114, 1348, 1347, 114, 1348, 1339, 588,
-
1350, 588, 1350, 1352, 1018, 1353, 1339, 1036,
-
1020, 1352, 1018, 1353, 1339, 1020, 1356, 1029,
-
1357, 1356, 1029, 1357, 1359, 1039, 1360, 1359,
-
1039, 1360, 1362, 1062, 1363, 1364, 1362, 1062,
-
1363, 1362, 1062, 1363, 1365, 1064, 1366, 1365,
-
1064, 1366, 1066, 1365, 1064, 1366, 1066, 1368,
-
1070, 1369, 1076, 1368, 1070, 1369, 1372, 1082,
-
1084, 1373, 1372, 1082, 1373, 1372, 1082, 1373,
-
1376, 1096, 1377, 1339, 1014, 1376, 1096, 1377,
-
1339, 1014, 1376, 1096, 1377, 1339, 1014, 1378,
-
1102, 1379, 1339, 1014, 1378, 1102, 1379, 1339,
-
1014, 1381, 1109, 1382, 1066, 1381, 1109, 1382,
-
1381, 1109, 1382, 1336, 4, 1383, 1249, 1255,
-
1385, 1253, 1386, 1255, 1385, 1253, 1386, 1255,
-
1039, 1389, 1278, 1390, 1391, 1389, 1278, 1390,
-
1389, 1278, 1390, 1392, 1280, 1393, 1392, 1280,
-
1393, 1282, 1392, 1280, 1393, 1282, 1070, 1286,
-
1396, 1292, 1294, 1397, 1396, 1292, 1397, 1396,
-
1292, 1397, 1400, 1306, 1401, 1255, 1400, 1306,
-
1401, 1255, 1400, 1306, 1401, 1255, 1402, 1312,
-
1403, 1255, 1402, 1312, 1403, 1255, 1405, 1319,
-
1406, 1282, 1405, 1319, 1406, 1405, 1319, 1406
-
]
-
-
1
class << self
-
1
attr_accessor :_address_lists_trans_actions
-
1
private :_address_lists_trans_actions, :_address_lists_trans_actions=
-
end
-
1
self._address_lists_trans_actions = [
-
0, 0, 0, 1, 1, 2, 0, 1,
-
3, 0, 0, 0, 4, 4, 0, 0,
-
5, 0, 6, 7, 0, 8, 8, 9,
-
8, 0, 0, 10, 0, 0, 11, 11,
-
12, 12, 13, 12, 14, 15, 4, 4,
-
5, 0, 0, 2, 0, 12, 12, 16,
-
17, 18, 18, 1, 1, 19, 20, 1,
-
20, 0, 0, 2, 0, 0, 0, 21,
-
21, 0, 0, 22, 0, 21, 23, 0,
-
0, 2, 0, 0, 24, 0, 12, 12,
-
16, 12, 12, 25, 0, 0, 0, 2,
-
0, 0, 0, 2, 0, 0, 12, 12,
-
16, 12, 0, 0, 0, 0, 4, 4,
-
0, 0, 5, 0, 0, 2, 0, 0,
-
0, 26, 26, 26, 26, 27, 0, 26,
-
0, 28, 29, 0, 0, 1, 1, 2,
-
1, 3, 0, 30, 30, 0, 0, 31,
-
32, 0, 32, 7, 23, 4, 4, 0,
-
0, 5, 33, 0, 33, 24, 0, 0,
-
4, 4, 5, 0, 0, 8, 8, 9,
-
8, 0, 0, 10, 0, 0, 11, 11,
-
12, 12, 13, 12, 15, 4, 4, 5,
-
0, 0, 2, 0, 12, 12, 16, 17,
-
18, 18, 1, 1, 19, 20, 1, 20,
-
0, 0, 2, 0, 0, 0, 21, 21,
-
0, 0, 22, 0, 21, 23, 0, 0,
-
2, 0, 0, 24, 0, 12, 12, 16,
-
12, 12, 25, 0, 0, 0, 2, 0,
-
0, 0, 2, 0, 0, 12, 12, 16,
-
12, 0, 2, 34, 34, 0, 12, 16,
-
35, 35, 26, 26, 27, 0, 0, 0,
-
2, 0, 8, 8, 8, 9, 8, 0,
-
0, 10, 0, 0, 30, 30, 0, 31,
-
36, 36, 37, 4, 4, 5, 38, 38,
-
39, 0, 0, 11, 11, 13, 40, 12,
-
40, 41, 4, 4, 0, 0, 5, 0,
-
0, 2, 0, 4, 4, 5, 0, 0,
-
0, 11, 11, 13, 12, 0, 0, 30,
-
30, 0, 0, 31, 32, 0, 7, 23,
-
4, 4, 0, 0, 5, 33, 0, 24,
-
0, 0, 4, 4, 5, 0, 0, 8,
-
8, 9, 8, 0, 0, 10, 0, 0,
-
11, 11, 12, 12, 13, 12, 15, 4,
-
4, 5, 0, 0, 2, 3, 0, 12,
-
12, 16, 17, 18, 18, 1, 1, 19,
-
20, 1, 20, 0, 0, 2, 0, 0,
-
0, 21, 21, 0, 0, 22, 0, 21,
-
23, 0, 0, 2, 0, 0, 24, 0,
-
12, 12, 16, 12, 12, 25, 0, 0,
-
0, 2, 0, 0, 0, 2, 0, 0,
-
12, 12, 16, 12, 0, 2, 34, 0,
-
12, 16, 35, 26, 26, 26, 26, 27,
-
26, 28, 29, 0, 0, 1, 1, 2,
-
1, 0, 8, 8, 8, 9, 8, 0,
-
0, 10, 0, 0, 30, 30, 0, 31,
-
36, 37, 4, 4, 5, 38, 39, 0,
-
0, 11, 11, 13, 40, 12, 41, 4,
-
4, 0, 0, 5, 0, 0, 2, 0,
-
4, 4, 5, 0, 0, 0, 11, 11,
-
13, 12, 42, 42, 43, 43, 44, 43,
-
43, 0, 0, 0, 0, 2, 0, 0,
-
0, 4, 4, 5, 45, 0, 45, 0,
-
0, 0, 11, 11, 13, 46, 12, 46,
-
4, 4, 0, 5, 0, 0, 2, 0,
-
12, 12, 12, 16, 8, 8, 8, 8,
-
0, 0, 10, 0, 0, 4, 4, 0,
-
5, 0, 0, 0, 11, 11, 13, 0,
-
0, 0, 12, 12, 12, 12, 16, 12,
-
12, 0, 0, 0, 0, 2, 0, 12,
-
12, 16, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 8, 8, 9, 8, 0,
-
0, 10, 0, 0, 12, 12, 12, 12,
-
16, 30, 30, 31, 0, 4, 4, 5,
-
0, 0, 11, 11, 13, 47, 47, 25,
-
8, 8, 8, 8, 0, 0, 10, 0,
-
0, 0, 0, 0, 30, 30, 31, 0,
-
0, 2, 24, 0, 12, 12, 16, 25,
-
43, 43, 43, 43, 48, 43, 0, 0,
-
0, 2, 0, 0, 0, 2, 0, 0,
-
0, 12, 12, 16, 12, 0, 0, 2,
-
0, 12, 12, 16, 12, 12, 12, 12,
-
16, 30, 30, 0, 31, 0, 0, 2,
-
0, 12, 12, 16, 8, 8, 8, 8,
-
0, 0, 10, 0, 0, 11, 11, 13,
-
0, 0, 0, 0, 0, 10, 0, 4,
-
4, 5, 49, 49, 50, 0, 0, 11,
-
11, 13, 51, 51, 52, 12, 12, 53,
-
53, 16, 53, 0, 0, 0, 21, 21,
-
22, 23, 0, 0, 2, 0, 12, 12,
-
16, 0, 0, 0, 0, 0, 0, 0,
-
43, 43, 43, 43, 48, 43, 43, 0,
-
0, 0, 0, 2, 0, 0, 0, 0,
-
0, 2, 0, 54, 0, 0, 12, 12,
-
16, 12, 55, 0, 0, 2, 0, 12,
-
12, 16, 0, 0, 0, 0, 0, 0,
-
0, 0, 2, 0, 12, 12, 16, 0,
-
12, 12, 12, 12, 16, 12, 12, 0,
-
0, 0, 8, 8, 9, 8, 0, 0,
-
10, 0, 0, 12, 12, 12, 16, 0,
-
0, 0, 0, 0, 0, 21, 21, 22,
-
0, 0, 2, 0, 12, 12, 16, 0,
-
21, 21, 0, 22, 0, 0, 2, 0,
-
12, 12, 16, 8, 8, 8, 8, 0,
-
0, 10, 0, 0, 0, 0, 0, 8,
-
8, 8, 9, 8, 0, 0, 10, 0,
-
0, 21, 21, 22, 37, 0, 0, 2,
-
39, 0, 12, 12, 16, 41, 0, 0,
-
10, 0, 0, 0, 2, 50, 0, 12,
-
12, 16, 52, 12, 12, 53, 53, 16,
-
12, 53, 12, 0, 2, 0, 12, 16,
-
43, 43, 43, 43, 48, 43, 43, 0,
-
0, 0, 0, 2, 0, 0, 0, 0,
-
0, 2, 54, 0, 54, 0, 0, 12,
-
12, 16, 55, 12, 55, 0, 0, 2,
-
0, 0, 12, 12, 16, 12, 56, 56,
-
56, 56, 57, 56, 0, 0, 2, 0,
-
12, 12, 16, 0, 0, 0, 2, 0,
-
12, 12, 16, 0, 0, 0, 0, 0,
-
0, 0, 0, 2, 0, 12, 12, 16,
-
0, 12, 12, 12, 12, 16, 12, 12,
-
0, 0, 0, 42, 42, 43, 43, 44,
-
43, 43, 0, 0, 0, 0, 2, 0,
-
0, 0, 4, 4, 5, 45, 0, 0,
-
0, 0, 11, 11, 13, 46, 12, 4,
-
4, 0, 5, 0, 0, 2, 0, 12,
-
12, 12, 16, 8, 8, 8, 8, 0,
-
0, 10, 0, 0, 4, 4, 0, 5,
-
0, 0, 0, 11, 11, 13, 0, 0,
-
0, 12, 12, 12, 12, 16, 12, 12,
-
0, 0, 0, 0, 2, 0, 12, 12,
-
16, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 8, 8, 9, 8, 0, 0,
-
10, 0, 0, 12, 12, 12, 12, 16,
-
30, 30, 31, 0, 4, 4, 5, 0,
-
0, 11, 11, 13, 47, 25, 8, 8,
-
8, 8, 0, 0, 10, 0, 0, 0,
-
0, 0, 30, 30, 31, 0, 0, 2,
-
24, 0, 12, 12, 16, 25, 43, 43,
-
43, 43, 48, 43, 0, 0, 0, 2,
-
0, 0, 0, 2, 0, 0, 0, 12,
-
12, 16, 12, 0, 0, 2, 0, 12,
-
12, 16, 12, 12, 12, 12, 16, 30,
-
30, 0, 31, 0, 0, 2, 0, 12,
-
12, 16, 8, 8, 8, 8, 0, 0,
-
10, 0, 0, 11, 11, 13, 0, 0,
-
0, 0, 0, 10, 0, 4, 4, 5,
-
49, 50, 0, 0, 11, 11, 13, 51,
-
52, 12, 12, 53, 53, 16, 12, 53,
-
12, 26, 26, 26, 26, 27, 26, 28,
-
29, 0, 0, 1, 1, 2, 1, 3,
-
0, 30, 30, 0, 0, 31, 32, 0,
-
32, 7, 23, 4, 4, 0, 0, 5,
-
33, 0, 33, 24, 0, 0, 4, 4,
-
5, 0, 0, 8, 8, 9, 8, 0,
-
0, 10, 0, 0, 11, 11, 12, 12,
-
13, 12, 15, 4, 4, 5, 0, 0,
-
2, 0, 12, 12, 16, 17, 18, 18,
-
1, 1, 19, 20, 1, 20, 0, 0,
-
2, 0, 0, 0, 21, 21, 0, 0,
-
22, 0, 21, 23, 0, 0, 2, 0,
-
0, 24, 0, 12, 12, 16, 12, 12,
-
25, 0, 0, 0, 2, 0, 0, 0,
-
2, 0, 0, 12, 12, 16, 12, 0,
-
2, 34, 34, 0, 12, 16, 35, 35,
-
0, 43, 43, 43, 43, 48, 43, 43,
-
0, 0, 0, 0, 2, 0, 0, 0,
-
0, 0, 2, 0, 54, 0, 0, 12,
-
12, 16, 12, 55, 0, 0, 2, 0,
-
12, 12, 16, 0, 0, 0, 0, 0,
-
0, 0, 0, 2, 0, 12, 12, 16,
-
0, 12, 12, 12, 12, 16, 12, 12,
-
0, 0, 0, 8, 8, 9, 8, 0,
-
0, 10, 0, 0, 12, 12, 12, 16,
-
0, 0, 0, 0, 0, 0, 21, 21,
-
22, 0, 0, 2, 0, 12, 12, 16,
-
0, 21, 21, 0, 22, 0, 0, 2,
-
0, 12, 12, 16, 8, 8, 8, 8,
-
0, 0, 10, 0, 0, 0, 0, 0,
-
8, 8, 8, 9, 8, 0, 0, 10,
-
0, 0, 21, 21, 22, 37, 0, 0,
-
2, 39, 0, 12, 12, 16, 41, 0,
-
0, 10, 0, 0, 0, 2, 50, 0,
-
12, 12, 16, 52, 12, 12, 53, 53,
-
16, 12, 53, 12, 0, 2, 0, 12,
-
16, 43, 43, 43, 43, 48, 43, 43,
-
0, 0, 0, 0, 2, 0, 0, 0,
-
0, 0, 2, 54, 0, 54, 0, 0,
-
12, 12, 16, 55, 12, 55, 0, 0,
-
2, 0, 0, 12, 12, 16, 12, 56,
-
56, 56, 56, 57, 56, 0, 0, 2,
-
0, 12, 12, 16, 0, 0, 0, 2,
-
0, 12, 12, 16, 0, 0, 0, 0,
-
0, 0, 0, 0, 2, 0, 12, 12,
-
16, 0, 12, 12, 12, 12, 16, 12,
-
12, 0, 0, 0, 11, 11, 13, 47,
-
12, 47, 25, 4, 4, 0, 0, 5,
-
0, 0, 2, 0, 4, 4, 5, 0,
-
0, 0, 11, 11, 13, 12, 42, 42,
-
43, 43, 44, 43, 43, 0, 0, 0,
-
0, 2, 0, 0, 0, 4, 4, 5,
-
45, 0, 45, 0, 0, 0, 11, 11,
-
13, 46, 12, 46, 4, 4, 0, 5,
-
0, 0, 2, 0, 12, 12, 12, 16,
-
8, 8, 8, 8, 0, 0, 10, 0,
-
0, 4, 4, 0, 5, 0, 0, 0,
-
11, 11, 13, 0, 0, 0, 12, 12,
-
12, 12, 16, 12, 12, 0, 0, 0,
-
0, 2, 0, 12, 12, 16, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 8,
-
8, 9, 8, 0, 0, 10, 0, 0,
-
12, 12, 12, 12, 16, 8, 8, 8,
-
8, 0, 0, 10, 0, 0, 30, 30,
-
0, 31, 0, 4, 4, 5, 0, 0,
-
11, 11, 13, 30, 30, 31, 0, 0,
-
2, 24, 0, 12, 12, 16, 25, 43,
-
43, 43, 43, 48, 43, 0, 0, 0,
-
2, 0, 0, 0, 2, 0, 0, 0,
-
12, 12, 16, 12, 0, 0, 2, 0,
-
12, 12, 16, 12, 12, 12, 12, 16,
-
0, 0, 0, 30, 30, 0, 31, 0,
-
0, 2, 0, 12, 12, 16, 8, 8,
-
8, 8, 0, 0, 10, 0, 0, 0,
-
0, 0, 8, 8, 8, 9, 8, 0,
-
0, 10, 0, 0, 30, 30, 31, 36,
-
36, 37, 4, 4, 5, 38, 38, 39,
-
0, 0, 11, 11, 13, 40, 40, 41,
-
0, 0, 10, 0, 4, 4, 5, 49,
-
49, 50, 0, 0, 11, 11, 13, 51,
-
51, 52, 12, 12, 53, 53, 16, 53,
-
0, 0, 0, 21, 21, 22, 23, 0,
-
0, 2, 0, 12, 12, 16, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
21, 21, 22, 23, 0, 0, 2, 0,
-
12, 12, 16, 0, 0, 0, 0, 0,
-
0, 0, 43, 43, 43, 43, 48, 43,
-
43, 0, 0, 0, 0, 2, 0, 0,
-
0, 0, 0, 2, 0, 54, 0, 0,
-
12, 12, 16, 12, 55, 0, 0, 2,
-
0, 12, 12, 16, 0, 0, 0, 0,
-
0, 0, 0, 0, 2, 0, 12, 12,
-
16, 0, 12, 12, 12, 12, 16, 12,
-
12, 0, 0, 0, 8, 8, 9, 8,
-
0, 0, 10, 0, 0, 12, 12, 12,
-
16, 0, 0, 0, 0, 0, 0, 21,
-
21, 22, 0, 0, 2, 0, 12, 12,
-
16, 0, 21, 21, 0, 22, 0, 0,
-
2, 0, 12, 12, 16, 8, 8, 8,
-
8, 0, 0, 10, 0, 0, 0, 0,
-
0, 8, 8, 8, 9, 8, 0, 0,
-
10, 0, 0, 21, 21, 22, 37, 0,
-
0, 2, 39, 0, 12, 12, 16, 41,
-
0, 0, 10, 0, 0, 0, 2, 50,
-
0, 12, 12, 16, 52, 12, 12, 53,
-
53, 16, 12, 53, 12, 0, 2, 0,
-
12, 16, 43, 43, 43, 43, 48, 43,
-
43, 0, 0, 0, 0, 2, 0, 0,
-
0, 0, 0, 2, 54, 0, 54, 0,
-
0, 12, 12, 16, 55, 12, 55, 0,
-
0, 2, 0, 0, 12, 12, 16, 12,
-
56, 56, 56, 56, 57, 56, 0, 0,
-
2, 0, 12, 12, 16, 0, 0, 0,
-
2, 0, 12, 12, 16, 0, 0, 0,
-
0, 0, 0, 0, 0, 2, 0, 12,
-
12, 16, 0, 12, 12, 12, 12, 16,
-
12, 12, 0, 0, 0, 12, 12, 16,
-
12, 26, 26, 58, 0, 0, 2, 0,
-
12, 12, 16, 42, 42, 43, 43, 44,
-
43, 43, 0, 0, 0, 0, 2, 0,
-
0, 0, 0, 0, 4, 4, 0, 5,
-
0, 0, 2, 0, 12, 12, 12, 16,
-
8, 8, 8, 8, 0, 0, 10, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
12, 12, 12, 12, 16, 12, 12, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 8, 8, 9, 8,
-
0, 0, 10, 0, 0, 12, 12, 12,
-
12, 16, 8, 8, 8, 8, 0, 0,
-
10, 0, 0, 0, 0, 0, 0, 43,
-
43, 43, 43, 48, 43, 0, 0, 0,
-
2, 0, 0, 0, 0, 0, 0, 2,
-
0, 12, 12, 12, 16, 12, 12, 12,
-
12, 16, 0, 0, 0, 0, 0, 8,
-
8, 8, 8, 0, 0, 10, 0, 0,
-
0, 0, 0, 8, 8, 8, 9, 8,
-
0, 0, 10, 0, 0, 0, 0, 0,
-
0, 10, 0, 0, 0, 12, 12, 53,
-
53, 16, 53, 0, 0, 1, 1, 2,
-
1, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
43, 43, 43, 43, 48, 43, 43, 0,
-
0, 0, 0, 2, 0, 0, 0, 0,
-
0, 2, 0, 54, 0, 0, 12, 12,
-
16, 12, 55, 0, 0, 2, 0, 12,
-
12, 16, 0, 0, 0, 0, 0, 0,
-
0, 0, 2, 0, 12, 12, 16, 0,
-
12, 12, 12, 12, 16, 12, 12, 0,
-
0, 0, 8, 8, 9, 8, 0, 0,
-
10, 0, 0, 12, 12, 12, 16, 0,
-
0, 0, 0, 0, 0, 21, 21, 22,
-
0, 0, 2, 0, 12, 12, 16, 0,
-
21, 21, 0, 22, 0, 0, 2, 0,
-
12, 12, 16, 8, 8, 8, 8, 0,
-
0, 10, 0, 0, 0, 0, 0, 8,
-
8, 8, 9, 8, 0, 0, 10, 0,
-
0, 21, 21, 22, 37, 0, 0, 2,
-
39, 0, 12, 12, 16, 41, 0, 0,
-
10, 0, 0, 0, 2, 50, 0, 12,
-
12, 16, 52, 12, 12, 53, 53, 16,
-
12, 53, 12, 0, 2, 0, 12, 16,
-
43, 43, 43, 43, 48, 43, 43, 0,
-
0, 0, 0, 2, 0, 0, 0, 0,
-
0, 2, 54, 0, 54, 0, 0, 12,
-
12, 16, 55, 12, 55, 0, 0, 2,
-
0, 0, 12, 12, 16, 12, 56, 56,
-
56, 56, 57, 56, 0, 0, 2, 0,
-
12, 12, 16, 0, 0, 0, 2, 0,
-
12, 12, 16, 0, 0, 0, 0, 0,
-
0, 0, 0, 2, 0, 12, 12, 16,
-
0, 12, 12, 12, 12, 16, 12, 12,
-
0, 0, 0, 4, 4, 0, 0, 5,
-
0, 0, 2, 0, 0, 0, 42, 42,
-
44, 0, 0, 2, 0, 0, 12, 12,
-
16, 12, 0, 0, 0, 0, 0, 0,
-
0, 0, 8, 8, 9, 8, 0, 0,
-
10, 0, 0, 12, 12, 12, 12, 16,
-
8, 8, 8, 8, 0, 0, 10, 0,
-
0, 0, 0, 0, 0, 43, 43, 43,
-
43, 48, 43, 0, 0, 0, 2, 0,
-
12, 12, 12, 12, 16, 0, 0, 0,
-
0, 0, 8, 8, 8, 8, 0, 0,
-
10, 0, 0, 0, 0, 0, 8, 8,
-
8, 9, 8, 0, 0, 10, 0, 0,
-
0, 0, 0, 0, 10, 0, 0, 0,
-
12, 12, 53, 53, 16, 53, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 59,
-
59, 60, 61, 59, 0, 0, 2, 62,
-
0, 0, 63, 63, 63, 63, 64, 63,
-
28, 65, 30, 30, 0, 31, 32, 0,
-
23, 4, 4, 5, 33, 0, 24, 34,
-
34, 2, 34, 35, 35, 16, 35, 63,
-
63, 63, 63, 64, 63, 30, 30, 0,
-
31, 0, 23, 4, 4, 5, 0, 24,
-
11, 11, 13, 47, 12, 25, 4, 4,
-
5, 0, 11, 11, 13, 12, 26, 26,
-
27, 0, 2, 12, 12, 16, 12, 0,
-
2, 12, 16, 4, 4, 5, 45, 0,
-
0, 11, 11, 13, 46, 12, 4, 4,
-
5, 11, 11, 13, 45, 45, 2, 46,
-
46, 16, 30, 30, 31, 0, 4, 4,
-
5, 11, 11, 13, 30, 30, 31, 33,
-
33, 2, 24, 47, 47, 16, 25, 45,
-
45, 2, 0, 46, 46, 16, 30, 30,
-
0, 31, 33, 33, 2, 47, 47, 16,
-
30, 30, 31, 36, 37, 4, 4, 5,
-
38, 39, 11, 11, 13, 40, 41, 4,
-
4, 5, 49, 50, 11, 11, 13, 51,
-
52, 32, 32, 22, 23, 33, 33, 2,
-
47, 47, 16, 11, 11, 13, 12, 25,
-
4, 4, 5, 0, 11, 11, 13, 12,
-
0, 30, 30, 31, 0, 4, 4, 5,
-
11, 11, 13, 30, 30, 31, 33, 33,
-
2, 24, 47, 47, 16, 25, 0, 0,
-
30, 30, 0, 31, 33, 33, 2, 47,
-
47, 16, 30, 30, 31, 37, 4, 4,
-
5, 39, 11, 11, 13, 41, 4, 4,
-
5, 50, 11, 11, 13, 52, 32, 32,
-
22, 23, 33, 33, 2, 47, 47, 16
-
]
-
-
1
class << self
-
1
attr_accessor :_address_lists_eof_actions
-
1
private :_address_lists_eof_actions, :_address_lists_eof_actions=
-
end
-
1
self._address_lists_eof_actions = [
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 32,
-
33, 34, 35, 0, 32, 33, 47, 34,
-
34, 35, 0, 0, 12, 0, 12, 45,
-
45, 46, 45, 45, 45, 46, 45, 45,
-
46, 32, 33, 47, 32, 33, 47, 45,
-
45, 46, 45, 32, 33, 47, 32, 36,
-
38, 40, 49, 51, 32, 33, 47, 47,
-
34, 34, 35, 45, 32, 33, 47, 32,
-
33, 47, 45, 32, 33, 47, 32, 36,
-
38, 40, 49, 51, 32, 33, 47, 0
-
]
-
-
1
class << self
-
1
attr_accessor :address_lists_start
-
end
-
1
self.address_lists_start = 1334;
-
1
class << self
-
1
attr_accessor :address_lists_first_final
-
end
-
1
self.address_lists_first_final = 1334;
-
1
class << self
-
1
attr_accessor :address_lists_error
-
end
-
1
self.address_lists_error = 0;
-
-
1
class << self
-
1
attr_accessor :address_lists_en_comment_tail
-
end
-
1
self.address_lists_en_comment_tail = 1329;
-
1
class << self
-
1
attr_accessor :address_lists_en_main
-
end
-
1
self.address_lists_en_main = 1334;
-
-
-
# line 17 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb.rl"
-
-
1
def self.parse(data)
-
p = 0
-
eof = data.length
-
stack = []
-
-
actions = []
-
data_unpacked = data.bytes.to_a
-
-
# line 14059 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb"
-
begin
-
p ||= 0
-
pe ||= data.length
-
cs = address_lists_start
-
top = 0
-
end
-
-
# line 26 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb.rl"
-
-
# line 14069 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb"
-
begin
-
testEof = false
-
_slen, _trans, _keys, _inds, _acts, _nacts = nil
-
_goto_level = 0
-
_resume = 10
-
_eof_trans = 15
-
_again = 20
-
_test_eof = 30
-
_out = 40
-
while true
-
if _goto_level <= 0
-
if p == pe
-
_goto_level = _test_eof
-
next
-
end
-
if cs == 0
-
_goto_level = _out
-
next
-
end
-
end
-
if _goto_level <= _resume
-
_keys = cs << 1
-
_inds = _address_lists_index_offsets[cs]
-
_slen = _address_lists_key_spans[cs]
-
_trans = if ( _slen > 0 &&
-
_address_lists_trans_keys[_keys] <= ( data_unpacked[p]) &&
-
( data_unpacked[p]) <= _address_lists_trans_keys[_keys + 1]
-
) then
-
_address_lists_indicies[ _inds + ( data_unpacked[p]) - _address_lists_trans_keys[_keys] ]
-
else
-
_address_lists_indicies[ _inds + _slen ]
-
end
-
cs = _address_lists_trans_targs[_trans]
-
if _address_lists_trans_actions[_trans] != 0
-
case _address_lists_trans_actions[_trans]
-
when 34 then
-
# line 4 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(1, p) end
-
when 3 then
-
# line 6 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(3, p) end
-
when 12 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
when 59 then
-
# line 8 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(5, p) end
-
when 54 then
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
when 43 then
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 24 then
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
when 21 then
-
# line 22 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(19, p) end
-
when 1 then
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
when 50 then
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
when 20 then
-
# line 34 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(31, p) end
-
when 4 then
-
# line 39 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(36, p) end
-
when 10 then
-
# line 41 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(38, p) end
-
when 8 then
-
# line 42 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(39, p) end
-
when 2 then
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 1329
-
_goto_level = _again
-
next
-
end
-
end
-
when 62 then
-
# line 6 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
top -= 1
-
cs = stack[top]
-
_goto_level = _again
-
next
-
end
-
end
-
when 28 then
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(2, p) end
-
# line 6 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(3, p) end
-
when 29 then
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(2, p) end
-
# line 40 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(37, p) end
-
when 35 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 4 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(1, p) end
-
when 17 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 6 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(3, p) end
-
when 55 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
when 25 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
when 53 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
when 52 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
when 11 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 39 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(36, p) end
-
when 16 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 1329
-
_goto_level = _again
-
next
-
end
-
end
-
when 60 then
-
# line 8 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(5, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 1329
-
_goto_level = _again
-
next
-
end
-
end
-
when 61 then
-
# line 8 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(5, p) end
-
# line 6 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
top -= 1
-
cs = stack[top]
-
_goto_level = _again
-
next
-
end
-
end
-
when 45 then
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 4 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(1, p) end
-
when 48 then
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 1329
-
_goto_level = _again
-
next
-
end
-
end
-
when 33 then
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 4 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(1, p) end
-
when 39 then
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
when 23 then
-
# line 22 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(19, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
when 22 then
-
# line 22 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(19, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 1329
-
_goto_level = _again
-
next
-
end
-
end
-
when 18 then
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 34 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(31, p) end
-
when 49 then
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
# line 4 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(1, p) end
-
when 56 then
-
# line 33 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(30, p) end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
when 7 then
-
# line 39 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(36, p) end
-
# line 6 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(3, p) end
-
when 42 then
-
# line 39 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(36, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 6 then
-
# line 39 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(36, p) end
-
# line 19 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(16, p) end
-
when 30 then
-
# line 39 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(36, p) end
-
# line 22 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(19, p) end
-
when 9 then
-
# line 42 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(39, p) end
-
# line 41 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(38, p) end
-
when 5 then
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 1329
-
_goto_level = _again
-
next
-
end
-
end
-
# line 39 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(36, p) end
-
when 26 then
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(2, p) end
-
# line 40 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(37, p) end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
when 46 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 4 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(1, p) end
-
when 47 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 4 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(1, p) end
-
when 41 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
when 51 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
# line 4 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(1, p) end
-
when 15 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 39 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(36, p) end
-
# line 6 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(3, p) end
-
when 14 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 39 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(36, p) end
-
# line 19 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(16, p) end
-
when 13 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 1329
-
_goto_level = _again
-
next
-
end
-
end
-
# line 39 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(36, p) end
-
when 65 then
-
# line 20 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(17, p) end
-
# line 40 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(37, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(2, p) end
-
when 38 then
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
# line 4 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(1, p) end
-
when 32 then
-
# line 22 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(19, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 4 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(1, p) end
-
when 37 then
-
# line 22 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(19, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
when 19 then
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 1329
-
_goto_level = _again
-
next
-
end
-
end
-
# line 34 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(31, p) end
-
when 57 then
-
# line 33 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(30, p) end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 1329
-
_goto_level = _again
-
next
-
end
-
end
-
when 44 then
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 1329
-
_goto_level = _again
-
next
-
end
-
end
-
# line 39 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(36, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 31 then
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 1329
-
_goto_level = _again
-
next
-
end
-
end
-
# line 39 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(36, p) end
-
# line 22 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(19, p) end
-
when 27 then
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(2, p) end
-
# line 40 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(37, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 1329
-
_goto_level = _again
-
next
-
end
-
end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
when 40 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
# line 4 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(1, p) end
-
when 63 then
-
# line 20 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(17, p) end
-
# line 40 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(37, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(2, p) end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
when 36 then
-
# line 22 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(19, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
# line 4 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(1, p) end
-
when 58 then
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 1329
-
_goto_level = _again
-
next
-
end
-
end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(2, p) end
-
# line 40 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(37, p) end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
when 64 then
-
# line 20 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(17, p) end
-
# line 40 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(37, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 1329
-
_goto_level = _again
-
next
-
end
-
end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(2, p) end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 14709 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb"
-
end
-
end
-
end
-
if _goto_level <= _again
-
if cs == 0
-
_goto_level = _out
-
next
-
end
-
p += 1
-
if p != pe
-
_goto_level = _resume
-
next
-
end
-
end
-
if _goto_level <= _test_eof
-
if p == eof
-
case _address_lists_eof_actions[cs]
-
when 34 then
-
# line 4 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(1, p) end
-
when 12 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
when 35 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 4 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(1, p) end
-
when 45 then
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 4 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(1, p) end
-
when 33 then
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 4 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(1, p) end
-
when 49 then
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
# line 4 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(1, p) end
-
when 46 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 4 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(1, p) end
-
when 47 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 4 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(1, p) end
-
when 51 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
# line 4 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(1, p) end
-
when 38 then
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
# line 4 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(1, p) end
-
when 32 then
-
# line 22 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(19, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 4 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(1, p) end
-
when 40 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
# line 4 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(1, p) end
-
when 36 then
-
# line 22 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(19, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
# line 4 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(1, p) end
-
# line 14839 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb"
-
end
-
end
-
-
end
-
if _goto_level <= _out
-
break
-
end
-
end
-
end
-
-
# line 27 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb.rl"
-
-
if p == eof && cs >=
-
# line 14853 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb"
-
1334
-
# line 28 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb.rl"
-
-
return actions, nil
-
else
-
return [], "Only able to parse up to #{data[0..p]}"
-
end
-
end
-
end
-
end
-
end
-
end
-
-
# line 1 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb.rl"
-
-
# line 10 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb.rl"
-
-
-
1
module Mail
-
1
module Parsers
-
1
module Ragel
-
1
module ContentDispositionMachine
-
-
# line 13 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb"
-
1
class << self
-
1
attr_accessor :_content_disposition_trans_keys
-
1
private :_content_disposition_trans_keys, :_content_disposition_trans_keys=
-
end
-
1
self._content_disposition_trans_keys = [
-
0, 0, 9, 59, 10, 10,
-
9, 32, 9, 59, 9,
-
126, 10, 10, 9, 32,
-
33, 126, 9, 126, 9, 40,
-
10, 10, 9, 32, 1,
-
127, 1, 127, 10, 10,
-
9, 32, 10, 10, 9, 32,
-
0, 127, 9, 40, 10,
-
10, 9, 32, 9, 126,
-
1, 127, 1, 127, 10, 10,
-
9, 32, 0, 127, 9,
-
126, 9, 59, 9, 59,
-
9, 126, 9, 59, 9, 59,
-
9, 126, 0, 0, 0
-
]
-
-
1
class << self
-
1
attr_accessor :_content_disposition_key_spans
-
1
private :_content_disposition_key_spans, :_content_disposition_key_spans=
-
end
-
1
self._content_disposition_key_spans = [
-
0, 51, 1, 24, 51, 118, 1, 24,
-
94, 118, 32, 1, 24, 127, 127, 1,
-
24, 1, 24, 128, 32, 1, 24, 118,
-
127, 127, 1, 24, 128, 118, 51, 51,
-
118, 51, 51, 118, 0
-
]
-
-
1
class << self
-
1
attr_accessor :_content_disposition_index_offsets
-
1
private :_content_disposition_index_offsets, :_content_disposition_index_offsets=
-
end
-
1
self._content_disposition_index_offsets = [
-
0, 0, 52, 54, 79, 131, 250, 252,
-
277, 372, 491, 524, 526, 551, 679, 807,
-
809, 834, 836, 861, 990, 1023, 1025, 1050,
-
1169, 1297, 1425, 1427, 1452, 1581, 1700, 1752,
-
1804, 1923, 1975, 2027, 2146
-
]
-
-
1
class << self
-
1
attr_accessor :_content_disposition_indicies
-
1
private :_content_disposition_indicies, :_content_disposition_indicies=
-
end
-
1
self._content_disposition_indicies = [
-
0, 1, 1, 1, 2, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 0,
-
1, 1, 1, 1, 1, 1, 1, 3,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 4, 1, 5, 1, 0, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 0, 1, 6,
-
1, 1, 1, 7, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 6, 1,
-
1, 1, 1, 1, 1, 1, 8, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 9, 1, 4, 1, 1, 1, 10,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 4, 11, 11, 11, 11, 11,
-
11, 11, 12, 1, 11, 11, 11, 11,
-
11, 1, 11, 11, 11, 11, 11, 11,
-
11, 11, 11, 11, 1, 1, 1, 1,
-
1, 1, 1, 11, 11, 11, 11, 11,
-
11, 11, 11, 11, 11, 11, 11, 11,
-
11, 11, 11, 11, 11, 11, 11, 11,
-
11, 11, 11, 11, 11, 1, 1, 1,
-
11, 11, 11, 11, 11, 11, 11, 11,
-
11, 11, 11, 11, 11, 11, 11, 11,
-
11, 11, 11, 11, 11, 11, 11, 11,
-
11, 11, 11, 11, 11, 11, 11, 11,
-
11, 1, 13, 1, 4, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 4, 1, 14, 14, 14,
-
14, 14, 14, 14, 1, 1, 14, 14,
-
14, 14, 14, 1, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 1, 1,
-
1, 15, 1, 1, 1, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 1,
-
1, 1, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 1, 16, 1, 1, 1,
-
17, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 16, 18, 19, 18, 18,
-
18, 18, 18, 20, 1, 18, 18, 18,
-
18, 18, 1, 18, 18, 18, 18, 18,
-
18, 18, 18, 18, 18, 1, 1, 1,
-
18, 1, 1, 1, 18, 18, 18, 18,
-
18, 18, 18, 18, 18, 18, 18, 18,
-
18, 18, 18, 18, 18, 18, 18, 18,
-
18, 18, 18, 18, 18, 18, 1, 1,
-
1, 18, 18, 18, 18, 18, 18, 18,
-
18, 18, 18, 18, 18, 18, 18, 18,
-
18, 18, 18, 18, 18, 18, 18, 18,
-
18, 18, 18, 18, 18, 18, 18, 18,
-
18, 18, 1, 21, 1, 1, 1, 22,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 21, 1, 23, 1, 1, 1,
-
1, 1, 24, 1, 25, 1, 21, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 21, 1, 26,
-
26, 26, 26, 26, 26, 26, 26, 26,
-
1, 26, 26, 27, 26, 26, 26, 26,
-
26, 26, 26, 26, 26, 26, 26, 26,
-
26, 26, 26, 26, 26, 26, 26, 26,
-
28, 26, 26, 26, 26, 26, 26, 26,
-
26, 26, 26, 26, 26, 26, 26, 26,
-
26, 26, 26, 26, 26, 26, 26, 26,
-
26, 26, 26, 26, 26, 26, 26, 26,
-
26, 26, 26, 26, 26, 26, 26, 26,
-
26, 26, 26, 26, 26, 26, 26, 26,
-
26, 26, 26, 26, 26, 26, 26, 26,
-
26, 26, 29, 26, 26, 26, 26, 26,
-
26, 26, 26, 26, 26, 26, 26, 26,
-
26, 26, 26, 26, 26, 26, 26, 26,
-
26, 26, 26, 26, 26, 26, 26, 26,
-
26, 26, 26, 26, 26, 26, 1, 30,
-
30, 30, 30, 30, 30, 30, 30, 30,
-
1, 30, 30, 31, 30, 30, 30, 30,
-
30, 30, 30, 30, 30, 30, 30, 30,
-
30, 30, 30, 30, 30, 30, 30, 30,
-
32, 30, 30, 30, 30, 30, 30, 30,
-
30, 30, 30, 30, 30, 30, 30, 30,
-
30, 30, 30, 30, 30, 30, 30, 30,
-
30, 30, 30, 30, 30, 30, 30, 30,
-
30, 30, 30, 30, 30, 30, 30, 30,
-
30, 30, 30, 30, 30, 30, 30, 30,
-
30, 30, 30, 30, 30, 30, 30, 30,
-
30, 30, 33, 30, 30, 30, 30, 30,
-
30, 30, 30, 30, 30, 30, 30, 30,
-
30, 30, 30, 30, 30, 30, 30, 30,
-
30, 30, 30, 30, 30, 30, 30, 30,
-
30, 30, 30, 30, 30, 30, 1, 34,
-
1, 30, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
30, 1, 35, 1, 36, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 36, 1, 30, 30, 30,
-
30, 30, 30, 30, 30, 30, 30, 30,
-
30, 30, 30, 30, 30, 30, 30, 30,
-
30, 30, 30, 30, 30, 30, 30, 30,
-
30, 30, 30, 30, 30, 30, 30, 30,
-
30, 30, 30, 30, 30, 30, 30, 30,
-
30, 30, 30, 30, 30, 30, 30, 30,
-
30, 30, 30, 30, 30, 30, 30, 30,
-
30, 30, 30, 30, 30, 30, 30, 30,
-
30, 30, 30, 30, 30, 30, 30, 30,
-
30, 30, 30, 30, 30, 30, 30, 30,
-
30, 30, 30, 30, 30, 30, 30, 30,
-
30, 30, 30, 30, 30, 30, 30, 30,
-
30, 30, 30, 30, 30, 30, 30, 30,
-
30, 30, 30, 30, 30, 30, 30, 30,
-
30, 30, 30, 30, 30, 30, 30, 30,
-
30, 30, 30, 30, 30, 1, 37, 1,
-
1, 1, 38, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 37, 1, 39,
-
1, 1, 1, 1, 1, 40, 1, 41,
-
1, 42, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
42, 1, 9, 1, 1, 1, 43, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 9, 44, 44, 44, 44, 44, 44,
-
44, 45, 1, 44, 44, 44, 44, 44,
-
1, 44, 44, 44, 44, 44, 44, 44,
-
44, 44, 44, 1, 1, 1, 1, 1,
-
1, 1, 44, 44, 44, 44, 44, 44,
-
44, 44, 44, 44, 44, 44, 44, 44,
-
44, 44, 44, 44, 44, 44, 44, 44,
-
44, 44, 44, 44, 1, 1, 1, 44,
-
44, 44, 44, 44, 44, 44, 44, 44,
-
44, 44, 44, 44, 44, 44, 44, 44,
-
44, 44, 44, 44, 44, 44, 44, 44,
-
44, 44, 44, 44, 44, 44, 44, 44,
-
1, 46, 46, 46, 46, 46, 46, 46,
-
46, 46, 1, 46, 46, 47, 46, 46,
-
46, 46, 46, 46, 46, 46, 46, 46,
-
46, 46, 46, 46, 46, 46, 46, 46,
-
46, 46, 46, 46, 46, 46, 46, 46,
-
48, 49, 46, 46, 46, 46, 46, 46,
-
46, 46, 46, 46, 46, 46, 46, 46,
-
46, 46, 46, 46, 46, 46, 46, 46,
-
46, 46, 46, 46, 46, 46, 46, 46,
-
46, 46, 46, 46, 46, 46, 46, 46,
-
46, 46, 46, 46, 46, 46, 46, 46,
-
46, 46, 46, 46, 50, 46, 46, 46,
-
46, 46, 46, 46, 46, 46, 46, 46,
-
46, 46, 46, 46, 46, 46, 46, 46,
-
46, 46, 46, 46, 46, 46, 46, 46,
-
46, 46, 46, 46, 46, 46, 46, 46,
-
1, 51, 51, 51, 51, 51, 51, 51,
-
51, 51, 1, 51, 51, 52, 51, 51,
-
51, 51, 51, 51, 51, 51, 51, 51,
-
51, 51, 51, 51, 51, 51, 51, 51,
-
51, 51, 51, 51, 51, 51, 51, 51,
-
53, 54, 51, 51, 51, 51, 51, 51,
-
51, 51, 51, 51, 51, 51, 51, 51,
-
51, 51, 51, 51, 51, 51, 51, 51,
-
51, 51, 51, 51, 51, 51, 51, 51,
-
51, 51, 51, 51, 51, 51, 51, 51,
-
51, 51, 51, 51, 51, 51, 51, 51,
-
51, 51, 51, 51, 55, 51, 51, 51,
-
51, 51, 51, 51, 51, 51, 51, 51,
-
51, 51, 51, 51, 51, 51, 51, 51,
-
51, 51, 51, 51, 51, 51, 51, 51,
-
51, 51, 51, 51, 51, 51, 51, 51,
-
1, 56, 1, 51, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 51, 1, 51, 51, 51, 51,
-
51, 51, 51, 51, 51, 51, 51, 51,
-
51, 51, 51, 51, 51, 51, 51, 51,
-
51, 51, 51, 51, 51, 51, 51, 51,
-
51, 51, 51, 51, 51, 51, 51, 51,
-
51, 51, 51, 51, 51, 51, 51, 51,
-
51, 51, 51, 51, 51, 51, 51, 51,
-
51, 51, 51, 51, 51, 51, 51, 51,
-
51, 51, 51, 51, 51, 51, 51, 51,
-
51, 51, 51, 51, 51, 51, 51, 51,
-
51, 51, 51, 51, 51, 51, 51, 51,
-
51, 51, 51, 51, 51, 51, 51, 51,
-
51, 51, 51, 51, 51, 51, 51, 51,
-
51, 51, 51, 51, 51, 51, 51, 51,
-
51, 51, 51, 51, 51, 51, 51, 51,
-
51, 51, 51, 51, 51, 51, 51, 51,
-
51, 51, 51, 51, 1, 57, 1, 1,
-
1, 58, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 57, 59, 59, 59,
-
59, 59, 59, 59, 60, 1, 59, 59,
-
59, 59, 59, 1, 59, 59, 59, 59,
-
59, 59, 59, 59, 59, 59, 1, 61,
-
1, 1, 1, 1, 1, 59, 59, 59,
-
59, 59, 59, 59, 59, 59, 59, 59,
-
59, 59, 59, 59, 59, 59, 59, 59,
-
59, 59, 59, 59, 59, 59, 59, 1,
-
1, 1, 59, 59, 59, 59, 59, 59,
-
59, 59, 59, 59, 59, 59, 59, 59,
-
59, 59, 59, 59, 59, 59, 59, 59,
-
59, 59, 59, 59, 59, 59, 59, 59,
-
59, 59, 59, 1, 62, 1, 1, 1,
-
63, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 62, 1, 1, 1, 1,
-
1, 1, 1, 64, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 65, 1,
-
66, 1, 1, 1, 67, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 66,
-
1, 1, 1, 1, 1, 1, 1, 68,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 69, 1, 70, 1, 1, 1,
-
71, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 70, 72, 1, 72, 72,
-
72, 72, 72, 73, 1, 72, 72, 72,
-
72, 72, 1, 72, 72, 72, 72, 72,
-
72, 72, 72, 72, 72, 1, 65, 1,
-
72, 1, 1, 1, 72, 72, 72, 72,
-
72, 72, 72, 72, 72, 72, 72, 72,
-
72, 72, 72, 72, 72, 72, 72, 72,
-
72, 72, 72, 72, 72, 72, 1, 1,
-
1, 72, 72, 72, 72, 72, 72, 72,
-
72, 72, 72, 72, 72, 72, 72, 72,
-
72, 72, 72, 72, 72, 72, 72, 72,
-
72, 72, 72, 72, 72, 72, 72, 72,
-
72, 72, 1, 42, 1, 1, 1, 74,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 42, 1, 1, 1, 1, 1,
-
1, 1, 75, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 4, 1, 76,
-
1, 1, 1, 77, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 76, 1,
-
1, 1, 1, 1, 1, 1, 78, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 9, 1, 79, 1, 1, 1, 80,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 79, 81, 81, 81, 81, 81,
-
81, 81, 82, 1, 81, 81, 81, 81,
-
81, 1, 81, 81, 81, 81, 81, 81,
-
81, 81, 81, 81, 1, 83, 1, 1,
-
1, 1, 1, 81, 81, 81, 81, 81,
-
81, 81, 81, 81, 81, 81, 81, 81,
-
81, 81, 81, 81, 81, 81, 81, 81,
-
81, 81, 81, 81, 81, 1, 1, 1,
-
81, 81, 81, 81, 81, 81, 81, 81,
-
81, 81, 81, 81, 81, 81, 81, 81,
-
81, 81, 81, 81, 81, 81, 81, 81,
-
81, 81, 81, 81, 81, 81, 81, 81,
-
81, 1, 1, 0
-
]
-
-
1
class << self
-
1
attr_accessor :_content_disposition_trans_targs
-
1
private :_content_disposition_trans_targs, :_content_disposition_trans_targs=
-
end
-
1
self._content_disposition_trans_targs = [
-
1, 0, 2, 4, 5, 3, 1, 2,
-
4, 5, 6, 8, 23, 7, 8, 9,
-
10, 11, 32, 13, 20, 10, 11, 13,
-
20, 12, 14, 15, 30, 19, 14, 15,
-
30, 19, 16, 18, 30, 10, 11, 13,
-
20, 22, 33, 6, 8, 23, 25, 26,
-
25, 36, 28, 25, 26, 25, 36, 28,
-
27, 1, 2, 35, 4, 5, 30, 17,
-
31, 5, 30, 17, 31, 5, 33, 21,
-
32, 34, 21, 34, 33, 21, 34, 1,
-
2, 35, 4, 5
-
]
-
-
1
class << self
-
1
attr_accessor :_content_disposition_trans_actions
-
1
private :_content_disposition_trans_actions, :_content_disposition_trans_actions=
-
end
-
1
self._content_disposition_trans_actions = [
-
0, 0, 0, 1, 0, 0, 2, 2,
-
3, 2, 0, 4, 1, 0, 0, 5,
-
6, 6, 6, 6, 7, 0, 0, 0,
-
1, 0, 8, 8, 9, 8, 0, 0,
-
10, 0, 0, 0, 0, 2, 2, 2,
-
3, 0, 0, 2, 11, 3, 12, 12,
-
13, 14, 12, 0, 0, 1, 15, 0,
-
0, 16, 16, 17, 18, 16, 19, 19,
-
20, 19, 21, 21, 22, 21, 19, 19,
-
0, 23, 0, 1, 2, 2, 3, 24,
-
24, 0, 25, 24
-
]
-
-
1
class << self
-
1
attr_accessor :_content_disposition_eof_actions
-
1
private :_content_disposition_eof_actions, :_content_disposition_eof_actions=
-
end
-
1
self._content_disposition_eof_actions = [
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 16, 19, 21,
-
19, 0, 2, 24, 0
-
]
-
-
1
class << self
-
1
attr_accessor :content_disposition_start
-
end
-
1
self.content_disposition_start = 29;
-
1
class << self
-
1
attr_accessor :content_disposition_first_final
-
end
-
1
self.content_disposition_first_final = 29;
-
1
class << self
-
1
attr_accessor :content_disposition_error
-
end
-
1
self.content_disposition_error = 0;
-
-
1
class << self
-
1
attr_accessor :content_disposition_en_comment_tail
-
end
-
1
self.content_disposition_en_comment_tail = 24;
-
1
class << self
-
1
attr_accessor :content_disposition_en_main
-
end
-
1
self.content_disposition_en_main = 29;
-
-
-
# line 17 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb.rl"
-
-
1
def self.parse(data)
-
p = 0
-
eof = data.length
-
stack = []
-
-
actions = []
-
data_unpacked = data.bytes.to_a
-
-
# line 416 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb"
-
begin
-
p ||= 0
-
pe ||= data.length
-
cs = content_disposition_start
-
top = 0
-
end
-
-
# line 26 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb.rl"
-
-
# line 426 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb"
-
begin
-
testEof = false
-
_slen, _trans, _keys, _inds, _acts, _nacts = nil
-
_goto_level = 0
-
_resume = 10
-
_eof_trans = 15
-
_again = 20
-
_test_eof = 30
-
_out = 40
-
while true
-
if _goto_level <= 0
-
if p == pe
-
_goto_level = _test_eof
-
next
-
end
-
if cs == 0
-
_goto_level = _out
-
next
-
end
-
end
-
if _goto_level <= _resume
-
_keys = cs << 1
-
_inds = _content_disposition_index_offsets[cs]
-
_slen = _content_disposition_key_spans[cs]
-
_trans = if ( _slen > 0 &&
-
_content_disposition_trans_keys[_keys] <= ( data_unpacked[p]) &&
-
( data_unpacked[p]) <= _content_disposition_trans_keys[_keys + 1]
-
) then
-
_content_disposition_indicies[ _inds + ( data_unpacked[p]) - _content_disposition_trans_keys[_keys] ]
-
else
-
_content_disposition_indicies[ _inds + _slen ]
-
end
-
cs = _content_disposition_trans_targs[_trans]
-
if _content_disposition_trans_actions[_trans] != 0
-
case _content_disposition_trans_actions[_trans]
-
when 2 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
when 12 then
-
# line 8 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(5, p) end
-
when 24 then
-
# line 13 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(10, p) end
-
when 17 then
-
# line 14 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(11, p) end
-
when 5 then
-
# line 35 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(32, p) end
-
when 4 then
-
# line 36 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(33, p) end
-
when 19 then
-
# line 37 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(34, p) end
-
when 6 then
-
# line 38 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(35, p) end
-
when 10 then
-
# line 41 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(38, p) end
-
when 8 then
-
# line 42 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(39, p) end
-
when 1 then
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 24
-
_goto_level = _again
-
next
-
end
-
end
-
when 15 then
-
# line 6 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
top -= 1
-
cs = stack[top]
-
_goto_level = _again
-
next
-
end
-
end
-
when 11 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 36 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(33, p) end
-
when 21 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 37 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(34, p) end
-
when 3 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 24
-
_goto_level = _again
-
next
-
end
-
end
-
when 13 then
-
# line 8 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(5, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 24
-
_goto_level = _again
-
next
-
end
-
end
-
when 14 then
-
# line 8 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(5, p) end
-
# line 6 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
top -= 1
-
cs = stack[top]
-
_goto_level = _again
-
next
-
end
-
end
-
when 25 then
-
# line 13 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(10, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 24
-
_goto_level = _again
-
next
-
end
-
end
-
when 16 then
-
# line 14 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(11, p) end
-
# line 13 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(10, p) end
-
when 23 then
-
# line 37 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(34, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 24
-
_goto_level = _again
-
next
-
end
-
end
-
when 7 then
-
# line 38 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(35, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 24
-
_goto_level = _again
-
next
-
end
-
end
-
when 9 then
-
# line 42 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(39, p) end
-
# line 41 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(38, p) end
-
when 20 then
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 24
-
_goto_level = _again
-
next
-
end
-
end
-
# line 37 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(34, p) end
-
when 22 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 24
-
_goto_level = _again
-
next
-
end
-
end
-
# line 37 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(34, p) end
-
when 18 then
-
# line 14 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(11, p) end
-
# line 13 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(10, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 24
-
_goto_level = _again
-
next
-
end
-
end
-
# line 682 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb"
-
end
-
end
-
end
-
if _goto_level <= _again
-
if cs == 0
-
_goto_level = _out
-
next
-
end
-
p += 1
-
if p != pe
-
_goto_level = _resume
-
next
-
end
-
end
-
if _goto_level <= _test_eof
-
if p == eof
-
case _content_disposition_eof_actions[cs]
-
when 2 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
when 24 then
-
# line 13 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(10, p) end
-
when 19 then
-
# line 37 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(34, p) end
-
when 21 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 37 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(34, p) end
-
when 16 then
-
# line 14 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(11, p) end
-
# line 13 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(10, p) end
-
# line 726 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb"
-
end
-
end
-
-
end
-
if _goto_level <= _out
-
break
-
end
-
end
-
end
-
-
# line 27 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb.rl"
-
-
if p == eof && cs >=
-
# line 740 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb"
-
29
-
# line 28 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb.rl"
-
-
return actions, nil
-
else
-
return [], "Only able to parse up to #{data[0..p]}"
-
end
-
end
-
end
-
end
-
end
-
end
-
-
# line 1 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb.rl"
-
-
# line 10 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb.rl"
-
-
-
1
module Mail
-
1
module Parsers
-
1
module Ragel
-
1
module ContentLocationMachine
-
-
# line 13 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb"
-
1
class << self
-
1
attr_accessor :_content_location_trans_keys
-
1
private :_content_location_trans_keys, :_content_location_trans_keys=
-
end
-
1
self._content_location_trans_keys = [
-
0, 0, 9, 126, 10, 10,
-
9, 32, 10, 10, 9,
-
32, 1, 127, 10, 10,
-
9, 32, -128, -1, 10, 10,
-
9, 32, 9, 126, 1,
-
127, 1, 127, 10, 10,
-
9, 32, 0, 127, 9, 126,
-
9, 40, 9, 40, 1,
-
127, 1, 127, 1, 127,
-
1, 127, 9, 126, 0, 0,
-
0
-
]
-
-
1
class << self
-
1
attr_accessor :_content_location_key_spans
-
1
private :_content_location_key_spans, :_content_location_key_spans=
-
end
-
1
self._content_location_key_spans = [
-
0, 118, 1, 24, 1, 24, 127, 1,
-
24, 128, 1, 24, 118, 127, 127, 1,
-
24, 128, 118, 32, 32, 127, 127, 127,
-
127, 118, 0
-
]
-
-
1
class << self
-
1
attr_accessor :_content_location_index_offsets
-
1
private :_content_location_index_offsets, :_content_location_index_offsets=
-
end
-
1
self._content_location_index_offsets = [
-
0, 0, 119, 121, 146, 148, 173, 301,
-
303, 328, 457, 459, 484, 603, 731, 859,
-
861, 886, 1015, 1134, 1167, 1200, 1328, 1456,
-
1584, 1712, 1831
-
]
-
-
1
class << self
-
1
attr_accessor :_content_location_indicies
-
1
private :_content_location_indicies, :_content_location_indicies=
-
end
-
1
self._content_location_indicies = [
-
0, 1, 1, 1, 2, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 0,
-
3, 4, 3, 3, 3, 3, 3, 5,
-
1, 3, 3, 3, 3, 3, 1, 3,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 1, 1, 1, 3, 1, 1, 1,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 3, 1, 1, 1, 3, 3, 3,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 3, 3, 3, 3, 3, 1, 6,
-
1, 0, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
0, 1, 7, 1, 8, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 8, 1, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 1, 9,
-
9, 10, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 11, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
12, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 1, 13, 1, 9,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 9, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
9, 14, 1, 15, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 15, 1, 16, 1, 1, 1,
-
17, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 16, 18, 19, 18, 18,
-
18, 18, 18, 20, 1, 18, 18, 18,
-
18, 18, 1, 18, 18, 18, 18, 18,
-
18, 18, 18, 18, 18, 1, 1, 1,
-
18, 1, 1, 1, 18, 18, 18, 18,
-
18, 18, 18, 18, 18, 18, 18, 18,
-
18, 18, 18, 18, 18, 18, 18, 18,
-
18, 18, 18, 18, 18, 18, 1, 1,
-
1, 18, 18, 18, 18, 18, 18, 18,
-
18, 18, 18, 18, 18, 18, 18, 18,
-
18, 18, 18, 18, 18, 18, 18, 18,
-
18, 18, 18, 18, 18, 18, 18, 18,
-
18, 18, 1, 21, 21, 21, 21, 21,
-
21, 21, 21, 21, 1, 21, 21, 22,
-
21, 21, 21, 21, 21, 21, 21, 21,
-
21, 21, 21, 21, 21, 21, 21, 21,
-
21, 21, 21, 21, 21, 21, 21, 21,
-
21, 21, 23, 24, 21, 21, 21, 21,
-
21, 21, 21, 21, 21, 21, 21, 21,
-
21, 21, 21, 21, 21, 21, 21, 21,
-
21, 21, 21, 21, 21, 21, 21, 21,
-
21, 21, 21, 21, 21, 21, 21, 21,
-
21, 21, 21, 21, 21, 21, 21, 21,
-
21, 21, 21, 21, 21, 21, 25, 21,
-
21, 21, 21, 21, 21, 21, 21, 21,
-
21, 21, 21, 21, 21, 21, 21, 21,
-
21, 21, 21, 21, 21, 21, 21, 21,
-
21, 21, 21, 21, 21, 21, 21, 21,
-
21, 21, 1, 26, 26, 26, 26, 26,
-
26, 26, 26, 26, 1, 26, 26, 27,
-
26, 26, 26, 26, 26, 26, 26, 26,
-
26, 26, 26, 26, 26, 26, 26, 26,
-
26, 26, 26, 26, 26, 26, 26, 26,
-
26, 26, 28, 29, 26, 26, 26, 26,
-
26, 26, 26, 26, 26, 26, 26, 26,
-
26, 26, 26, 26, 26, 26, 26, 26,
-
26, 26, 26, 26, 26, 26, 26, 26,
-
26, 26, 26, 26, 26, 26, 26, 26,
-
26, 26, 26, 26, 26, 26, 26, 26,
-
26, 26, 26, 26, 26, 26, 30, 26,
-
26, 26, 26, 26, 26, 26, 26, 26,
-
26, 26, 26, 26, 26, 26, 26, 26,
-
26, 26, 26, 26, 26, 26, 26, 26,
-
26, 26, 26, 26, 26, 26, 26, 26,
-
26, 26, 1, 31, 1, 26, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 26, 1, 26, 26,
-
26, 26, 26, 26, 26, 26, 26, 26,
-
26, 26, 26, 26, 26, 26, 26, 26,
-
26, 26, 26, 26, 26, 26, 26, 26,
-
26, 26, 26, 26, 26, 26, 26, 26,
-
26, 26, 26, 26, 26, 26, 26, 26,
-
26, 26, 26, 26, 26, 26, 26, 26,
-
26, 26, 26, 26, 26, 26, 26, 26,
-
26, 26, 26, 26, 26, 26, 26, 26,
-
26, 26, 26, 26, 26, 26, 26, 26,
-
26, 26, 26, 26, 26, 26, 26, 26,
-
26, 26, 26, 26, 26, 26, 26, 26,
-
26, 26, 26, 26, 26, 26, 26, 26,
-
26, 26, 26, 26, 26, 26, 26, 26,
-
26, 26, 26, 26, 26, 26, 26, 26,
-
26, 26, 26, 26, 26, 26, 26, 26,
-
26, 26, 26, 26, 26, 26, 1, 32,
-
1, 1, 1, 33, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 32, 34,
-
34, 34, 34, 34, 34, 34, 35, 1,
-
34, 34, 34, 34, 34, 1, 34, 34,
-
34, 34, 34, 34, 34, 34, 34, 34,
-
1, 1, 1, 34, 1, 1, 1, 34,
-
34, 34, 34, 34, 34, 34, 34, 34,
-
34, 34, 34, 34, 34, 34, 34, 34,
-
34, 34, 34, 34, 34, 34, 34, 34,
-
34, 1, 1, 1, 34, 34, 34, 34,
-
34, 34, 34, 34, 34, 34, 34, 34,
-
34, 34, 34, 34, 34, 34, 34, 34,
-
34, 34, 34, 34, 34, 34, 34, 34,
-
34, 34, 34, 34, 34, 1, 8, 1,
-
1, 1, 36, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 8, 1, 1,
-
1, 1, 1, 1, 1, 37, 1, 38,
-
1, 1, 1, 39, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 38, 1,
-
1, 1, 1, 1, 1, 1, 40, 1,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
42, 1, 41, 41, 43, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 42,
-
44, 45, 44, 44, 44, 44, 44, 46,
-
41, 44, 44, 44, 44, 44, 41, 44,
-
44, 44, 44, 44, 44, 44, 44, 44,
-
44, 41, 41, 41, 44, 41, 41, 41,
-
44, 44, 44, 44, 44, 44, 44, 44,
-
44, 44, 44, 44, 44, 44, 44, 44,
-
44, 44, 44, 44, 44, 44, 44, 44,
-
44, 44, 41, 47, 41, 44, 44, 44,
-
44, 44, 44, 44, 44, 44, 44, 44,
-
44, 44, 44, 44, 44, 44, 44, 44,
-
44, 44, 44, 44, 44, 44, 44, 44,
-
44, 44, 44, 44, 44, 44, 41, 1,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
15, 1, 9, 9, 48, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 15,
-
9, 11, 9, 9, 9, 9, 9, 49,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 12, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 1,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
50, 1, 9, 9, 51, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 50,
-
9, 11, 9, 9, 9, 9, 9, 52,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 12, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 1,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
53, 1, 9, 9, 54, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 53,
-
55, 56, 55, 55, 55, 55, 55, 57,
-
9, 55, 55, 55, 55, 55, 9, 55,
-
55, 55, 55, 55, 55, 55, 55, 55,
-
55, 9, 9, 9, 55, 9, 9, 9,
-
55, 55, 55, 55, 55, 55, 55, 55,
-
55, 55, 55, 55, 55, 55, 55, 55,
-
55, 55, 55, 55, 55, 55, 55, 55,
-
55, 55, 9, 12, 9, 55, 55, 55,
-
55, 55, 55, 55, 55, 55, 55, 55,
-
55, 55, 55, 55, 55, 55, 55, 55,
-
55, 55, 55, 55, 55, 55, 55, 55,
-
55, 55, 55, 55, 55, 55, 9, 1,
-
32, 1, 1, 1, 33, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 32,
-
34, 34, 34, 34, 34, 34, 34, 58,
-
1, 34, 34, 34, 34, 34, 1, 34,
-
34, 34, 34, 34, 34, 34, 34, 34,
-
34, 1, 1, 1, 34, 1, 1, 1,
-
34, 34, 34, 34, 34, 34, 34, 34,
-
34, 34, 34, 34, 34, 34, 34, 34,
-
34, 34, 34, 34, 34, 34, 34, 34,
-
34, 34, 1, 1, 1, 34, 34, 34,
-
34, 34, 34, 34, 34, 34, 34, 34,
-
34, 34, 34, 34, 34, 34, 34, 34,
-
34, 34, 34, 34, 34, 34, 34, 34,
-
34, 34, 34, 34, 34, 34, 1, 1,
-
0
-
]
-
-
1
class << self
-
1
attr_accessor :_content_location_trans_targs
-
1
private :_content_location_trans_targs, :_content_location_trans_targs=
-
end
-
1
self._content_location_trans_targs = [
-
1, 0, 2, 18, 21, 12, 3, 5,
-
19, 6, 7, 19, 9, 8, 11, 22,
-
1, 2, 18, 21, 12, 14, 15, 14,
-
26, 17, 14, 15, 14, 26, 17, 16,
-
19, 4, 18, 20, 4, 20, 19, 4,
-
20, 6, 22, 10, 24, 25, 23, 9,
-
10, 23, 22, 10, 23, 22, 10, 24,
-
25, 23, 20
-
]
-
-
1
class << self
-
1
attr_accessor :_content_location_trans_actions
-
1
private :_content_location_trans_actions, :_content_location_trans_actions=
-
end
-
1
self._content_location_trans_actions = [
-
0, 0, 0, 1, 1, 2, 0, 0,
-
0, 0, 0, 3, 0, 0, 0, 0,
-
4, 4, 5, 5, 6, 7, 7, 8,
-
9, 7, 0, 0, 2, 10, 0, 0,
-
11, 11, 0, 12, 0, 2, 4, 4,
-
6, 13, 14, 14, 13, 15, 16, 13,
-
0, 2, 4, 4, 6, 11, 11, 0,
-
3, 12, 17
-
]
-
-
1
class << self
-
1
attr_accessor :_content_location_eof_actions
-
1
private :_content_location_eof_actions, :_content_location_eof_actions=
-
end
-
1
self._content_location_eof_actions = [
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 11, 0, 4, 11, 0, 4,
-
11, 11, 0
-
]
-
-
1
class << self
-
1
attr_accessor :content_location_start
-
end
-
1
self.content_location_start = 1;
-
1
class << self
-
1
attr_accessor :content_location_first_final
-
end
-
1
self.content_location_first_final = 18;
-
1
class << self
-
1
attr_accessor :content_location_error
-
end
-
1
self.content_location_error = 0;
-
-
1
class << self
-
1
attr_accessor :content_location_en_comment_tail
-
end
-
1
self.content_location_en_comment_tail = 13;
-
1
class << self
-
1
attr_accessor :content_location_en_main
-
end
-
1
self.content_location_en_main = 1;
-
-
-
# line 17 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb.rl"
-
-
1
def self.parse(data)
-
p = 0
-
eof = data.length
-
stack = []
-
-
actions = []
-
data_unpacked = data.bytes.to_a
-
-
# line 365 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb"
-
begin
-
p ||= 0
-
pe ||= data.length
-
cs = content_location_start
-
top = 0
-
end
-
-
# line 26 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb.rl"
-
-
# line 375 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb"
-
begin
-
testEof = false
-
_slen, _trans, _keys, _inds, _acts, _nacts = nil
-
_goto_level = 0
-
_resume = 10
-
_eof_trans = 15
-
_again = 20
-
_test_eof = 30
-
_out = 40
-
while true
-
if _goto_level <= 0
-
if p == pe
-
_goto_level = _test_eof
-
next
-
end
-
if cs == 0
-
_goto_level = _out
-
next
-
end
-
end
-
if _goto_level <= _resume
-
_keys = cs << 1
-
_inds = _content_location_index_offsets[cs]
-
_slen = _content_location_key_spans[cs]
-
_trans = if ( _slen > 0 &&
-
_content_location_trans_keys[_keys] <= ( data_unpacked[p]) &&
-
( data_unpacked[p]) <= _content_location_trans_keys[_keys + 1]
-
) then
-
_content_location_indicies[ _inds + ( data_unpacked[p]) - _content_location_trans_keys[_keys] ]
-
else
-
_content_location_indicies[ _inds + _slen ]
-
end
-
cs = _content_location_trans_targs[_trans]
-
if _content_location_trans_actions[_trans] != 0
-
case _content_location_trans_actions[_trans]
-
when 4 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
when 7 then
-
# line 8 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(5, p) end
-
when 3 then
-
# line 41 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(38, p) end
-
when 13 then
-
# line 42 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(39, p) end
-
when 11 then
-
# line 49 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(46, p) end
-
when 1 then
-
# line 50 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(47, p) end
-
when 2 then
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 13
-
_goto_level = _again
-
next
-
end
-
end
-
when 10 then
-
# line 6 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
top -= 1
-
cs = stack[top]
-
_goto_level = _again
-
next
-
end
-
end
-
when 5 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 50 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(47, p) end
-
when 6 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 13
-
_goto_level = _again
-
next
-
end
-
end
-
when 8 then
-
# line 8 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(5, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 13
-
_goto_level = _again
-
next
-
end
-
end
-
when 9 then
-
# line 8 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(5, p) end
-
# line 6 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
top -= 1
-
cs = stack[top]
-
_goto_level = _again
-
next
-
end
-
end
-
when 15 then
-
# line 42 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(39, p) end
-
# line 41 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(38, p) end
-
when 14 then
-
# line 42 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(39, p) end
-
# line 49 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(46, p) end
-
when 12 then
-
# line 49 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(46, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 13
-
_goto_level = _again
-
next
-
end
-
end
-
when 17 then
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 13
-
_goto_level = _again
-
next
-
end
-
end
-
# line 49 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(46, p) end
-
when 16 then
-
# line 42 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(39, p) end
-
# line 49 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(46, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 13
-
_goto_level = _again
-
next
-
end
-
end
-
# line 563 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb"
-
end
-
end
-
end
-
if _goto_level <= _again
-
if cs == 0
-
_goto_level = _out
-
next
-
end
-
p += 1
-
if p != pe
-
_goto_level = _resume
-
next
-
end
-
end
-
if _goto_level <= _test_eof
-
if p == eof
-
case _content_location_eof_actions[cs]
-
when 4 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
when 11 then
-
# line 49 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(46, p) end
-
# line 589 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb"
-
end
-
end
-
-
end
-
if _goto_level <= _out
-
break
-
end
-
end
-
end
-
-
# line 27 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb.rl"
-
-
if p == eof && cs >=
-
# line 603 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb"
-
18
-
# line 28 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb.rl"
-
-
return actions, nil
-
else
-
return [], "Only able to parse up to #{data[0..p]}"
-
end
-
end
-
end
-
end
-
end
-
end
-
-
# line 1 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb.rl"
-
-
# line 10 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb.rl"
-
-
-
1
module Mail
-
1
module Parsers
-
1
module Ragel
-
1
module ContentTransferEncodingMachine
-
-
# line 13 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb"
-
1
class << self
-
1
attr_accessor :_content_transfer_encoding_trans_keys
-
1
private :_content_transfer_encoding_trans_keys, :_content_transfer_encoding_trans_keys=
-
end
-
1
self._content_transfer_encoding_trans_keys = [
-
0, 0, 9, 126, 10, 10,
-
9, 32, 10, 10, 9,
-
32, 10, 10, 9, 32,
-
9, 126, 1, 127, 1, 127,
-
10, 10, 9, 32, -128,
-
-1, 9, 126, 9, 59,
-
9, 59, 9, 40, 9, 40,
-
0, 0, 0
-
]
-
-
1
class << self
-
1
attr_accessor :_content_transfer_encoding_key_spans
-
1
private :_content_transfer_encoding_key_spans, :_content_transfer_encoding_key_spans=
-
end
-
1
self._content_transfer_encoding_key_spans = [
-
0, 118, 1, 24, 1, 24, 1, 24,
-
118, 127, 127, 1, 24, 128, 118, 51,
-
51, 32, 32, 0
-
]
-
-
1
class << self
-
1
attr_accessor :_content_transfer_encoding_index_offsets
-
1
private :_content_transfer_encoding_index_offsets, :_content_transfer_encoding_index_offsets=
-
end
-
1
self._content_transfer_encoding_index_offsets = [
-
0, 0, 119, 121, 146, 148, 173, 175,
-
200, 319, 447, 575, 577, 602, 731, 850,
-
902, 954, 987, 1020
-
]
-
-
1
class << self
-
1
attr_accessor :_content_transfer_encoding_indicies
-
1
private :_content_transfer_encoding_indicies, :_content_transfer_encoding_indicies=
-
end
-
1
self._content_transfer_encoding_indicies = [
-
0, 1, 1, 1, 2, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 0,
-
3, 3, 3, 3, 3, 3, 3, 4,
-
1, 3, 3, 3, 3, 3, 1, 3,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 1, 1, 1, 1, 1, 1, 1,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 3, 1, 1, 1, 3, 3, 3,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 3, 3, 3, 3, 3, 1, 5,
-
1, 0, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
0, 1, 6, 1, 7, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 7, 1, 8, 1, 9,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 9, 1,
-
10, 1, 1, 1, 11, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 10,
-
12, 12, 12, 12, 12, 12, 12, 13,
-
1, 12, 12, 12, 12, 12, 1, 12,
-
12, 12, 12, 12, 12, 12, 12, 12,
-
12, 1, 1, 1, 1, 1, 1, 1,
-
12, 12, 12, 12, 12, 12, 12, 12,
-
12, 12, 12, 12, 12, 12, 12, 12,
-
12, 12, 12, 12, 12, 12, 12, 12,
-
12, 12, 1, 1, 1, 12, 12, 12,
-
12, 12, 12, 12, 12, 12, 12, 12,
-
12, 12, 12, 12, 12, 12, 12, 12,
-
12, 12, 12, 12, 12, 12, 12, 12,
-
12, 12, 12, 12, 12, 12, 1, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
1, 14, 14, 15, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 16, 17,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 18, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 1, 19,
-
19, 19, 19, 19, 19, 19, 19, 19,
-
1, 19, 19, 20, 19, 19, 19, 19,
-
19, 19, 19, 19, 19, 19, 19, 19,
-
19, 19, 19, 19, 19, 19, 19, 19,
-
19, 19, 19, 19, 19, 19, 21, 22,
-
19, 19, 19, 19, 19, 19, 19, 19,
-
19, 19, 19, 19, 19, 19, 19, 19,
-
19, 19, 19, 19, 19, 19, 19, 19,
-
19, 19, 19, 19, 19, 19, 19, 19,
-
19, 19, 19, 19, 19, 19, 19, 19,
-
19, 19, 19, 19, 19, 19, 19, 19,
-
19, 19, 23, 19, 19, 19, 19, 19,
-
19, 19, 19, 19, 19, 19, 19, 19,
-
19, 19, 19, 19, 19, 19, 19, 19,
-
19, 19, 19, 19, 19, 19, 19, 19,
-
19, 19, 19, 19, 19, 19, 1, 24,
-
1, 19, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
19, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 19, 25, 1, 1, 1, 26,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 25, 27, 27, 27, 27, 27,
-
27, 27, 28, 1, 27, 27, 27, 27,
-
27, 1, 27, 27, 27, 27, 27, 27,
-
27, 27, 27, 27, 1, 29, 1, 1,
-
1, 1, 1, 27, 27, 27, 27, 27,
-
27, 27, 27, 27, 27, 27, 27, 27,
-
27, 27, 27, 27, 27, 27, 27, 27,
-
27, 27, 27, 27, 27, 1, 1, 1,
-
27, 27, 27, 27, 27, 27, 27, 27,
-
27, 27, 27, 27, 27, 27, 27, 27,
-
27, 27, 27, 27, 27, 27, 27, 27,
-
27, 27, 27, 27, 27, 27, 27, 27,
-
27, 1, 7, 1, 1, 1, 30, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 7, 1, 1, 1, 1, 1, 1,
-
1, 31, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 9, 1, 32, 1,
-
1, 1, 33, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 32, 1, 1,
-
1, 1, 1, 1, 1, 34, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
35, 1, 9, 1, 1, 1, 36, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 9, 1, 1, 1, 1, 1, 1,
-
1, 37, 1, 35, 1, 1, 1, 38,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 35, 1, 1, 1, 1, 1,
-
1, 1, 39, 1, 1, 0
-
]
-
-
1
class << self
-
1
attr_accessor :_content_transfer_encoding_trans_targs
-
1
private :_content_transfer_encoding_trans_targs, :_content_transfer_encoding_trans_targs=
-
end
-
1
self._content_transfer_encoding_trans_targs = [
-
1, 0, 2, 14, 8, 3, 5, 15,
-
7, 17, 1, 2, 14, 8, 10, 11,
-
10, 19, 13, 10, 11, 10, 19, 13,
-
12, 15, 4, 14, 16, 17, 4, 16,
-
15, 4, 16, 17, 6, 18, 6, 18
-
]
-
-
1
class << self
-
1
attr_accessor :_content_transfer_encoding_trans_actions
-
1
private :_content_transfer_encoding_trans_actions, :_content_transfer_encoding_trans_actions=
-
end
-
1
self._content_transfer_encoding_trans_actions = [
-
0, 0, 0, 1, 2, 0, 0, 0,
-
0, 0, 3, 3, 4, 5, 6, 6,
-
7, 8, 6, 0, 0, 2, 9, 0,
-
0, 10, 10, 0, 11, 10, 0, 2,
-
3, 3, 5, 3, 0, 2, 3, 5
-
]
-
-
1
class << self
-
1
attr_accessor :_content_transfer_encoding_eof_actions
-
1
private :_content_transfer_encoding_eof_actions, :_content_transfer_encoding_eof_actions=
-
end
-
1
self._content_transfer_encoding_eof_actions = [
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 10, 0,
-
3, 0, 3, 0
-
]
-
-
1
class << self
-
1
attr_accessor :content_transfer_encoding_start
-
end
-
1
self.content_transfer_encoding_start = 1;
-
1
class << self
-
1
attr_accessor :content_transfer_encoding_first_final
-
end
-
1
self.content_transfer_encoding_first_final = 14;
-
1
class << self
-
1
attr_accessor :content_transfer_encoding_error
-
end
-
1
self.content_transfer_encoding_error = 0;
-
-
1
class << self
-
1
attr_accessor :content_transfer_encoding_en_comment_tail
-
end
-
1
self.content_transfer_encoding_en_comment_tail = 9;
-
1
class << self
-
1
attr_accessor :content_transfer_encoding_en_main
-
end
-
1
self.content_transfer_encoding_en_main = 1;
-
-
-
# line 17 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb.rl"
-
-
1
def self.parse(data)
-
p = 0
-
eof = data.length
-
stack = []
-
-
actions = []
-
data_unpacked = data.bytes.to_a
-
-
# line 251 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb"
-
begin
-
p ||= 0
-
pe ||= data.length
-
cs = content_transfer_encoding_start
-
top = 0
-
end
-
-
# line 26 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb.rl"
-
-
# line 261 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb"
-
begin
-
testEof = false
-
_slen, _trans, _keys, _inds, _acts, _nacts = nil
-
_goto_level = 0
-
_resume = 10
-
_eof_trans = 15
-
_again = 20
-
_test_eof = 30
-
_out = 40
-
while true
-
if _goto_level <= 0
-
if p == pe
-
_goto_level = _test_eof
-
next
-
end
-
if cs == 0
-
_goto_level = _out
-
next
-
end
-
end
-
if _goto_level <= _resume
-
_keys = cs << 1
-
_inds = _content_transfer_encoding_index_offsets[cs]
-
_slen = _content_transfer_encoding_key_spans[cs]
-
_trans = if ( _slen > 0 &&
-
_content_transfer_encoding_trans_keys[_keys] <= ( data_unpacked[p]) &&
-
( data_unpacked[p]) <= _content_transfer_encoding_trans_keys[_keys + 1]
-
) then
-
_content_transfer_encoding_indicies[ _inds + ( data_unpacked[p]) - _content_transfer_encoding_trans_keys[_keys] ]
-
else
-
_content_transfer_encoding_indicies[ _inds + _slen ]
-
end
-
cs = _content_transfer_encoding_trans_targs[_trans]
-
if _content_transfer_encoding_trans_actions[_trans] != 0
-
case _content_transfer_encoding_trans_actions[_trans]
-
when 3 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
when 6 then
-
# line 8 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(5, p) end
-
when 10 then
-
# line 17 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(14, p) end
-
when 1 then
-
# line 18 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(15, p) end
-
when 2 then
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 9
-
_goto_level = _again
-
next
-
end
-
end
-
when 9 then
-
# line 6 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
top -= 1
-
cs = stack[top]
-
_goto_level = _again
-
next
-
end
-
end
-
when 4 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 18 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(15, p) end
-
when 5 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 9
-
_goto_level = _again
-
next
-
end
-
end
-
when 7 then
-
# line 8 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(5, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 9
-
_goto_level = _again
-
next
-
end
-
end
-
when 8 then
-
# line 8 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(5, p) end
-
# line 6 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
top -= 1
-
cs = stack[top]
-
_goto_level = _again
-
next
-
end
-
end
-
when 11 then
-
# line 17 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(14, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 9
-
_goto_level = _again
-
next
-
end
-
end
-
# line 396 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb"
-
end
-
end
-
end
-
if _goto_level <= _again
-
if cs == 0
-
_goto_level = _out
-
next
-
end
-
p += 1
-
if p != pe
-
_goto_level = _resume
-
next
-
end
-
end
-
if _goto_level <= _test_eof
-
if p == eof
-
case _content_transfer_encoding_eof_actions[cs]
-
when 3 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
when 10 then
-
# line 17 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(14, p) end
-
# line 422 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb"
-
end
-
end
-
-
end
-
if _goto_level <= _out
-
break
-
end
-
end
-
end
-
-
# line 27 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb.rl"
-
-
if p == eof && cs >=
-
# line 436 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb"
-
14
-
# line 28 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb.rl"
-
-
return actions, nil
-
else
-
return [], "Only able to parse up to #{data[0..p]}"
-
end
-
end
-
end
-
end
-
end
-
end
-
-
# line 1 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb.rl"
-
-
# line 10 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb.rl"
-
-
-
1
module Mail
-
1
module Parsers
-
1
module Ragel
-
1
module ContentTypeMachine
-
-
# line 13 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb"
-
1
class << self
-
1
attr_accessor :_content_type_trans_keys
-
1
private :_content_type_trans_keys, :_content_type_trans_keys=
-
end
-
1
self._content_type_trans_keys = [
-
0, 0, 33, 126, 33, 126,
-
33, 126, 9, 126, 10,
-
10, 9, 32, 33, 126,
-
9, 126, 9, 40, 10, 10,
-
9, 32, 1, 127, 1,
-
127, 10, 10, 9, 32,
-
10, 10, 9, 32, 9, 126,
-
9, 126, 10, 10, 9,
-
32, 9, 126, 0, 127,
-
9, 40, 10, 10, 9, 32,
-
9, 126, 1, 127, 1,
-
127, 10, 10, 9, 32,
-
0, 127, 9, 126, 9, 59,
-
9, 126, 9, 126, 9,
-
126, 9, 126, 9, 126,
-
0, 0, 0
-
]
-
-
1
class << self
-
1
attr_accessor :_content_type_key_spans
-
1
private :_content_type_key_spans, :_content_type_key_spans=
-
end
-
1
self._content_type_key_spans = [
-
0, 94, 94, 94, 118, 1, 24, 94,
-
118, 32, 1, 24, 127, 127, 1, 24,
-
1, 24, 118, 118, 1, 24, 118, 128,
-
32, 1, 24, 118, 127, 127, 1, 24,
-
128, 118, 51, 118, 118, 118, 118, 118,
-
0
-
]
-
-
1
class << self
-
1
attr_accessor :_content_type_index_offsets
-
1
private :_content_type_index_offsets, :_content_type_index_offsets=
-
end
-
1
self._content_type_index_offsets = [
-
0, 0, 95, 190, 285, 404, 406, 431,
-
526, 645, 678, 680, 705, 833, 961, 963,
-
988, 990, 1015, 1134, 1253, 1255, 1280, 1399,
-
1528, 1561, 1563, 1588, 1707, 1835, 1963, 1965,
-
1990, 2119, 2238, 2290, 2409, 2528, 2647, 2766,
-
2885
-
]
-
-
1
class << self
-
1
attr_accessor :_content_type_indicies
-
1
private :_content_type_indicies, :_content_type_indicies=
-
end
-
1
self._content_type_indicies = [
-
0, 0, 0, 0, 0, 0, 0, 1,
-
1, 0, 0, 0, 0, 0, 1, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 1, 1, 1, 1, 1, 1, 1,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 1, 1, 1, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 1, 2,
-
2, 2, 2, 2, 2, 2, 1, 1,
-
2, 2, 2, 2, 2, 3, 2, 2,
-
2, 2, 2, 2, 2, 2, 2, 2,
-
1, 1, 1, 1, 1, 1, 1, 2,
-
2, 2, 2, 2, 2, 2, 2, 2,
-
2, 2, 2, 2, 2, 2, 2, 2,
-
2, 2, 2, 2, 2, 2, 2, 2,
-
2, 1, 1, 1, 2, 2, 2, 2,
-
2, 2, 2, 2, 2, 2, 2, 2,
-
2, 2, 2, 2, 2, 2, 2, 2,
-
2, 2, 2, 2, 2, 2, 2, 2,
-
2, 2, 2, 2, 2, 1, 4, 4,
-
4, 4, 4, 4, 4, 1, 1, 4,
-
4, 4, 4, 4, 1, 4, 4, 4,
-
4, 4, 4, 4, 4, 4, 4, 1,
-
1, 1, 1, 1, 1, 1, 4, 4,
-
4, 4, 4, 4, 4, 4, 4, 4,
-
4, 4, 4, 4, 4, 4, 4, 4,
-
4, 4, 4, 4, 4, 4, 4, 4,
-
1, 1, 1, 4, 4, 4, 4, 4,
-
4, 4, 4, 4, 4, 4, 4, 4,
-
4, 4, 4, 4, 4, 4, 4, 4,
-
4, 4, 4, 4, 4, 4, 4, 4,
-
4, 4, 4, 4, 1, 5, 1, 1,
-
1, 6, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 5, 7, 7, 7,
-
7, 7, 7, 7, 8, 1, 7, 7,
-
7, 7, 7, 1, 7, 7, 7, 7,
-
7, 7, 7, 7, 7, 7, 1, 9,
-
1, 1, 1, 1, 1, 7, 7, 7,
-
7, 7, 7, 7, 7, 7, 7, 7,
-
7, 7, 7, 7, 7, 7, 7, 7,
-
7, 7, 7, 7, 7, 7, 7, 1,
-
1, 1, 7, 7, 7, 7, 7, 7,
-
7, 7, 7, 7, 7, 7, 7, 7,
-
7, 7, 7, 7, 7, 7, 7, 7,
-
7, 7, 7, 7, 7, 7, 7, 7,
-
7, 7, 7, 1, 10, 1, 5, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 5, 1, 11,
-
11, 11, 11, 11, 11, 11, 1, 1,
-
11, 11, 11, 11, 11, 1, 11, 11,
-
11, 11, 11, 11, 11, 11, 11, 11,
-
1, 1, 1, 12, 1, 1, 1, 11,
-
11, 11, 11, 11, 11, 11, 11, 11,
-
11, 11, 11, 11, 11, 11, 11, 11,
-
11, 11, 11, 11, 11, 11, 11, 11,
-
11, 1, 1, 1, 11, 11, 11, 11,
-
11, 11, 11, 11, 11, 11, 11, 11,
-
11, 11, 11, 11, 11, 11, 11, 11,
-
11, 11, 11, 11, 11, 11, 11, 11,
-
11, 11, 11, 11, 11, 1, 13, 1,
-
1, 1, 14, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 13, 15, 16,
-
15, 15, 15, 15, 15, 17, 1, 15,
-
15, 15, 15, 15, 1, 15, 15, 15,
-
15, 15, 15, 15, 15, 15, 15, 1,
-
1, 1, 15, 1, 1, 1, 15, 15,
-
15, 15, 15, 15, 15, 15, 15, 15,
-
15, 15, 15, 15, 15, 15, 15, 15,
-
15, 15, 15, 15, 15, 15, 15, 15,
-
1, 1, 1, 15, 15, 15, 15, 15,
-
15, 15, 15, 15, 15, 15, 15, 15,
-
15, 15, 15, 15, 15, 15, 15, 15,
-
15, 15, 15, 15, 15, 15, 15, 15,
-
15, 15, 15, 15, 1, 18, 1, 1,
-
1, 19, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 18, 1, 20, 1,
-
1, 1, 1, 1, 21, 1, 22, 1,
-
18, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 18,
-
1, 23, 23, 23, 23, 23, 23, 23,
-
23, 23, 1, 23, 23, 24, 23, 23,
-
23, 23, 23, 23, 23, 23, 23, 23,
-
23, 23, 23, 23, 23, 23, 23, 23,
-
23, 23, 25, 23, 23, 23, 23, 23,
-
23, 23, 23, 23, 23, 23, 23, 23,
-
23, 23, 23, 23, 23, 23, 23, 23,
-
23, 23, 23, 23, 23, 23, 23, 23,
-
23, 23, 23, 23, 23, 23, 23, 23,
-
23, 23, 23, 23, 23, 23, 23, 23,
-
23, 23, 23, 23, 23, 23, 23, 23,
-
23, 23, 23, 23, 26, 23, 23, 23,
-
23, 23, 23, 23, 23, 23, 23, 23,
-
23, 23, 23, 23, 23, 23, 23, 23,
-
23, 23, 23, 23, 23, 23, 23, 23,
-
23, 23, 23, 23, 23, 23, 23, 23,
-
1, 27, 27, 27, 27, 27, 27, 27,
-
27, 27, 1, 27, 27, 28, 27, 27,
-
27, 27, 27, 27, 27, 27, 27, 27,
-
27, 27, 27, 27, 27, 27, 27, 27,
-
27, 27, 29, 27, 27, 27, 27, 27,
-
27, 27, 27, 27, 27, 27, 27, 27,
-
27, 27, 27, 27, 27, 27, 27, 27,
-
27, 27, 27, 27, 27, 27, 27, 27,
-
27, 27, 27, 27, 27, 27, 27, 27,
-
27, 27, 27, 27, 27, 27, 27, 27,
-
27, 27, 27, 27, 27, 27, 27, 27,
-
27, 27, 27, 27, 30, 27, 27, 27,
-
27, 27, 27, 27, 27, 27, 27, 27,
-
27, 27, 27, 27, 27, 27, 27, 27,
-
27, 27, 27, 27, 27, 27, 27, 27,
-
27, 27, 27, 27, 27, 27, 27, 27,
-
1, 31, 1, 27, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 27, 1, 32, 1, 33, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 33, 1, 34,
-
1, 1, 1, 35, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 34, 7,
-
7, 7, 7, 7, 7, 7, 36, 1,
-
7, 7, 7, 7, 7, 1, 7, 7,
-
7, 7, 7, 7, 7, 7, 7, 7,
-
1, 9, 1, 1, 1, 1, 1, 7,
-
7, 7, 7, 7, 7, 7, 7, 7,
-
7, 7, 7, 7, 7, 7, 7, 7,
-
7, 7, 7, 7, 7, 7, 7, 7,
-
7, 1, 1, 1, 7, 7, 7, 7,
-
7, 7, 7, 7, 7, 7, 7, 7,
-
7, 7, 7, 7, 7, 7, 7, 7,
-
7, 7, 7, 7, 7, 7, 7, 7,
-
7, 7, 7, 7, 7, 1, 34, 1,
-
1, 1, 35, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 34, 7, 7,
-
7, 7, 7, 7, 7, 36, 1, 7,
-
7, 7, 7, 7, 1, 7, 7, 7,
-
7, 7, 7, 7, 7, 7, 7, 1,
-
1, 1, 1, 1, 1, 1, 7, 7,
-
7, 7, 7, 7, 7, 7, 7, 7,
-
7, 7, 7, 7, 7, 7, 7, 7,
-
7, 7, 7, 7, 7, 7, 7, 7,
-
1, 1, 1, 7, 7, 7, 7, 7,
-
7, 7, 7, 7, 7, 7, 7, 7,
-
7, 7, 7, 7, 7, 7, 7, 7,
-
7, 7, 7, 7, 7, 7, 7, 7,
-
7, 7, 7, 7, 1, 37, 1, 34,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 34, 1,
-
38, 1, 1, 1, 39, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 38,
-
40, 40, 40, 40, 40, 40, 40, 41,
-
1, 40, 40, 40, 40, 40, 1, 40,
-
40, 40, 40, 40, 40, 40, 40, 40,
-
40, 1, 1, 1, 1, 1, 1, 1,
-
40, 40, 40, 40, 40, 40, 40, 40,
-
40, 40, 40, 40, 40, 40, 40, 40,
-
40, 40, 40, 40, 40, 40, 40, 40,
-
40, 40, 1, 1, 1, 40, 40, 40,
-
40, 40, 40, 40, 40, 40, 40, 40,
-
40, 40, 40, 40, 40, 40, 40, 40,
-
40, 40, 40, 40, 40, 40, 40, 40,
-
40, 40, 40, 40, 40, 40, 1, 27,
-
27, 27, 27, 27, 27, 27, 27, 27,
-
27, 27, 27, 27, 27, 27, 27, 27,
-
27, 27, 27, 27, 27, 27, 27, 27,
-
27, 27, 27, 27, 27, 27, 27, 27,
-
27, 27, 27, 27, 27, 27, 27, 27,
-
27, 27, 27, 27, 27, 27, 27, 27,
-
27, 27, 27, 27, 27, 27, 27, 27,
-
27, 27, 27, 27, 27, 27, 27, 27,
-
27, 27, 27, 27, 27, 27, 27, 27,
-
27, 27, 27, 27, 27, 27, 27, 27,
-
27, 27, 27, 27, 27, 27, 27, 27,
-
27, 27, 27, 27, 27, 27, 27, 27,
-
27, 27, 27, 27, 27, 27, 27, 27,
-
27, 27, 27, 27, 27, 27, 27, 27,
-
27, 27, 27, 27, 27, 27, 27, 27,
-
27, 27, 27, 27, 27, 27, 27, 1,
-
42, 1, 1, 1, 43, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 42,
-
1, 44, 1, 1, 1, 1, 1, 45,
-
1, 46, 1, 47, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 47, 1, 48, 1, 1, 1,
-
49, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 48, 40, 40, 40, 40,
-
40, 40, 40, 50, 1, 40, 40, 40,
-
40, 40, 1, 40, 40, 40, 40, 40,
-
40, 40, 40, 40, 40, 1, 51, 1,
-
1, 1, 1, 1, 40, 40, 40, 40,
-
40, 40, 40, 40, 40, 40, 40, 40,
-
40, 40, 40, 40, 40, 40, 40, 40,
-
40, 40, 40, 40, 40, 40, 1, 1,
-
1, 40, 40, 40, 40, 40, 40, 40,
-
40, 40, 40, 40, 40, 40, 40, 40,
-
40, 40, 40, 40, 40, 40, 40, 40,
-
40, 40, 40, 40, 40, 40, 40, 40,
-
40, 40, 1, 52, 52, 52, 52, 52,
-
52, 52, 52, 52, 1, 52, 52, 53,
-
52, 52, 52, 52, 52, 52, 52, 52,
-
52, 52, 52, 52, 52, 52, 52, 52,
-
52, 52, 52, 52, 52, 52, 52, 52,
-
52, 52, 54, 55, 52, 52, 52, 52,
-
52, 52, 52, 52, 52, 52, 52, 52,
-
52, 52, 52, 52, 52, 52, 52, 52,
-
52, 52, 52, 52, 52, 52, 52, 52,
-
52, 52, 52, 52, 52, 52, 52, 52,
-
52, 52, 52, 52, 52, 52, 52, 52,
-
52, 52, 52, 52, 52, 52, 56, 52,
-
52, 52, 52, 52, 52, 52, 52, 52,
-
52, 52, 52, 52, 52, 52, 52, 52,
-
52, 52, 52, 52, 52, 52, 52, 52,
-
52, 52, 52, 52, 52, 52, 52, 52,
-
52, 52, 1, 57, 57, 57, 57, 57,
-
57, 57, 57, 57, 1, 57, 57, 58,
-
57, 57, 57, 57, 57, 57, 57, 57,
-
57, 57, 57, 57, 57, 57, 57, 57,
-
57, 57, 57, 57, 57, 57, 57, 57,
-
57, 57, 59, 60, 57, 57, 57, 57,
-
57, 57, 57, 57, 57, 57, 57, 57,
-
57, 57, 57, 57, 57, 57, 57, 57,
-
57, 57, 57, 57, 57, 57, 57, 57,
-
57, 57, 57, 57, 57, 57, 57, 57,
-
57, 57, 57, 57, 57, 57, 57, 57,
-
57, 57, 57, 57, 57, 57, 61, 57,
-
57, 57, 57, 57, 57, 57, 57, 57,
-
57, 57, 57, 57, 57, 57, 57, 57,
-
57, 57, 57, 57, 57, 57, 57, 57,
-
57, 57, 57, 57, 57, 57, 57, 57,
-
57, 57, 1, 62, 1, 57, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 57, 1, 57, 57,
-
57, 57, 57, 57, 57, 57, 57, 57,
-
57, 57, 57, 57, 57, 57, 57, 57,
-
57, 57, 57, 57, 57, 57, 57, 57,
-
57, 57, 57, 57, 57, 57, 57, 57,
-
57, 57, 57, 57, 57, 57, 57, 57,
-
57, 57, 57, 57, 57, 57, 57, 57,
-
57, 57, 57, 57, 57, 57, 57, 57,
-
57, 57, 57, 57, 57, 57, 57, 57,
-
57, 57, 57, 57, 57, 57, 57, 57,
-
57, 57, 57, 57, 57, 57, 57, 57,
-
57, 57, 57, 57, 57, 57, 57, 57,
-
57, 57, 57, 57, 57, 57, 57, 57,
-
57, 57, 57, 57, 57, 57, 57, 57,
-
57, 57, 57, 57, 57, 57, 57, 57,
-
57, 57, 57, 57, 57, 57, 57, 57,
-
57, 57, 57, 57, 57, 57, 1, 63,
-
1, 1, 1, 64, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 63, 65,
-
65, 65, 65, 65, 65, 65, 66, 1,
-
65, 65, 65, 65, 65, 1, 65, 65,
-
65, 65, 65, 65, 65, 65, 65, 65,
-
1, 67, 1, 1, 1, 1, 1, 65,
-
65, 65, 65, 65, 65, 65, 65, 65,
-
65, 65, 65, 65, 65, 65, 65, 65,
-
65, 65, 65, 65, 65, 65, 65, 65,
-
65, 1, 1, 1, 65, 65, 65, 65,
-
65, 65, 65, 65, 65, 65, 65, 65,
-
65, 65, 65, 65, 65, 65, 65, 65,
-
65, 65, 65, 65, 65, 65, 65, 65,
-
65, 65, 65, 65, 65, 1, 68, 1,
-
1, 1, 69, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 68, 1, 1,
-
1, 1, 1, 1, 1, 70, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
71, 1, 68, 1, 1, 1, 69, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 68, 7, 7, 7, 7, 7, 7,
-
7, 70, 1, 7, 7, 7, 7, 7,
-
1, 7, 7, 7, 7, 7, 7, 7,
-
7, 7, 7, 1, 71, 1, 1, 1,
-
1, 1, 7, 7, 7, 7, 7, 7,
-
7, 7, 7, 7, 7, 7, 7, 7,
-
7, 7, 7, 7, 7, 7, 7, 7,
-
7, 7, 7, 7, 1, 1, 1, 7,
-
7, 7, 7, 7, 7, 7, 7, 7,
-
7, 7, 7, 7, 7, 7, 7, 7,
-
7, 7, 7, 7, 7, 7, 7, 7,
-
7, 7, 7, 7, 7, 7, 7, 7,
-
1, 72, 1, 1, 1, 73, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
72, 40, 40, 40, 40, 40, 40, 40,
-
74, 1, 40, 40, 40, 40, 40, 1,
-
40, 40, 40, 40, 40, 40, 40, 40,
-
40, 40, 1, 75, 1, 1, 1, 1,
-
1, 40, 40, 40, 40, 40, 40, 40,
-
40, 40, 40, 40, 40, 40, 40, 40,
-
40, 40, 40, 40, 40, 40, 40, 40,
-
40, 40, 40, 1, 1, 1, 40, 40,
-
40, 40, 40, 40, 40, 40, 40, 40,
-
40, 40, 40, 40, 40, 40, 40, 40,
-
40, 40, 40, 40, 40, 40, 40, 40,
-
40, 40, 40, 40, 40, 40, 40, 1,
-
76, 1, 1, 1, 77, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 76,
-
78, 1, 78, 78, 78, 78, 78, 79,
-
1, 78, 78, 78, 78, 78, 1, 78,
-
78, 78, 78, 78, 78, 78, 78, 78,
-
78, 1, 71, 1, 78, 1, 1, 1,
-
78, 78, 78, 78, 78, 78, 78, 78,
-
78, 78, 78, 78, 78, 78, 78, 78,
-
78, 78, 78, 78, 78, 78, 78, 78,
-
78, 78, 1, 1, 1, 78, 78, 78,
-
78, 78, 78, 78, 78, 78, 78, 78,
-
78, 78, 78, 78, 78, 78, 78, 78,
-
78, 78, 78, 78, 78, 78, 78, 78,
-
78, 78, 78, 78, 78, 78, 1, 47,
-
1, 1, 1, 80, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 47, 7,
-
7, 7, 7, 7, 7, 7, 81, 1,
-
7, 7, 7, 7, 7, 1, 7, 7,
-
7, 7, 7, 7, 7, 7, 7, 7,
-
1, 9, 1, 1, 1, 1, 1, 7,
-
7, 7, 7, 7, 7, 7, 7, 7,
-
7, 7, 7, 7, 7, 7, 7, 7,
-
7, 7, 7, 7, 7, 7, 7, 7,
-
7, 1, 1, 1, 7, 7, 7, 7,
-
7, 7, 7, 7, 7, 7, 7, 7,
-
7, 7, 7, 7, 7, 7, 7, 7,
-
7, 7, 7, 7, 7, 7, 7, 7,
-
7, 7, 7, 7, 7, 1, 82, 1,
-
1, 1, 83, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 82, 40, 40,
-
40, 40, 40, 40, 40, 84, 1, 40,
-
40, 40, 40, 40, 1, 40, 40, 40,
-
40, 40, 40, 40, 40, 40, 40, 1,
-
51, 1, 1, 1, 1, 1, 40, 40,
-
40, 40, 40, 40, 40, 40, 40, 40,
-
40, 40, 40, 40, 40, 40, 40, 40,
-
40, 40, 40, 40, 40, 40, 40, 40,
-
1, 1, 1, 40, 40, 40, 40, 40,
-
40, 40, 40, 40, 40, 40, 40, 40,
-
40, 40, 40, 40, 40, 40, 40, 40,
-
40, 40, 40, 40, 40, 40, 40, 40,
-
40, 40, 40, 40, 1, 1, 0
-
]
-
-
1
class << self
-
1
attr_accessor :_content_type_trans_targs
-
1
private :_content_type_trans_targs, :_content_type_trans_targs=
-
end
-
1
self._content_type_trans_targs = [
-
2, 0, 2, 3, 33, 4, 5, 7,
-
27, 18, 6, 7, 8, 9, 10, 37,
-
12, 24, 9, 10, 12, 24, 11, 13,
-
14, 34, 23, 13, 14, 34, 23, 15,
-
17, 35, 19, 20, 22, 21, 19, 20,
-
7, 22, 9, 10, 12, 24, 26, 38,
-
4, 5, 27, 18, 29, 30, 29, 40,
-
32, 29, 30, 29, 40, 32, 31, 4,
-
5, 33, 27, 18, 35, 16, 36, 18,
-
35, 16, 36, 18, 38, 25, 37, 39,
-
25, 39, 38, 25, 39
-
]
-
-
1
class << self
-
1
attr_accessor :_content_type_trans_actions
-
1
private :_content_type_trans_actions, :_content_type_trans_actions=
-
end
-
1
self._content_type_trans_actions = [
-
1, 0, 0, 2, 3, 0, 0, 4,
-
5, 0, 0, 0, 6, 7, 7, 7,
-
7, 8, 0, 0, 0, 5, 0, 9,
-
9, 10, 9, 0, 0, 11, 0, 0,
-
0, 0, 0, 0, 5, 0, 12, 12,
-
13, 14, 12, 12, 12, 14, 0, 0,
-
12, 12, 14, 12, 15, 15, 16, 17,
-
15, 0, 0, 5, 18, 0, 0, 19,
-
19, 0, 20, 19, 21, 21, 22, 21,
-
23, 23, 24, 23, 21, 21, 0, 25,
-
0, 5, 12, 12, 14
-
]
-
-
1
class << self
-
1
attr_accessor :_content_type_eof_actions
-
1
private :_content_type_eof_actions, :_content_type_eof_actions=
-
end
-
1
self._content_type_eof_actions = [
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 19, 21, 21, 23, 21, 0, 12,
-
0
-
]
-
-
1
class << self
-
1
attr_accessor :content_type_start
-
end
-
1
self.content_type_start = 1;
-
1
class << self
-
1
attr_accessor :content_type_first_final
-
end
-
1
self.content_type_first_final = 33;
-
1
class << self
-
1
attr_accessor :content_type_error
-
end
-
1
self.content_type_error = 0;
-
-
1
class << self
-
1
attr_accessor :content_type_en_comment_tail
-
end
-
1
self.content_type_en_comment_tail = 28;
-
1
class << self
-
1
attr_accessor :content_type_en_main
-
end
-
1
self.content_type_en_main = 1;
-
-
-
# line 17 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb.rl"
-
-
1
def self.parse(data)
-
p = 0
-
eof = data.length
-
stack = []
-
-
actions = []
-
data_unpacked = data.bytes.to_a
-
-
# line 513 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb"
-
begin
-
p ||= 0
-
pe ||= data.length
-
cs = content_type_start
-
top = 0
-
end
-
-
# line 26 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb.rl"
-
-
# line 523 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb"
-
begin
-
testEof = false
-
_slen, _trans, _keys, _inds, _acts, _nacts = nil
-
_goto_level = 0
-
_resume = 10
-
_eof_trans = 15
-
_again = 20
-
_test_eof = 30
-
_out = 40
-
while true
-
if _goto_level <= 0
-
if p == pe
-
_goto_level = _test_eof
-
next
-
end
-
if cs == 0
-
_goto_level = _out
-
next
-
end
-
end
-
if _goto_level <= _resume
-
_keys = cs << 1
-
_inds = _content_type_index_offsets[cs]
-
_slen = _content_type_key_spans[cs]
-
_trans = if ( _slen > 0 &&
-
_content_type_trans_keys[_keys] <= ( data_unpacked[p]) &&
-
( data_unpacked[p]) <= _content_type_trans_keys[_keys + 1]
-
) then
-
_content_type_indicies[ _inds + ( data_unpacked[p]) - _content_type_trans_keys[_keys] ]
-
else
-
_content_type_indicies[ _inds + _slen ]
-
end
-
cs = _content_type_trans_targs[_trans]
-
if _content_type_trans_actions[_trans] != 0
-
case _content_type_trans_actions[_trans]
-
when 12 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
when 15 then
-
# line 8 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(5, p) end
-
when 2 then
-
# line 25 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(22, p) end
-
when 1 then
-
# line 26 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(23, p) end
-
when 6 then
-
# line 35 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(32, p) end
-
when 4 then
-
# line 36 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(33, p) end
-
when 21 then
-
# line 37 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(34, p) end
-
when 7 then
-
# line 38 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(35, p) end
-
when 11 then
-
# line 41 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(38, p) end
-
when 9 then
-
# line 42 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(39, p) end
-
when 19 then
-
# line 45 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(42, p) end
-
when 3 then
-
# line 46 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(43, p) end
-
when 5 then
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 28
-
_goto_level = _again
-
next
-
end
-
end
-
when 18 then
-
# line 6 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
top -= 1
-
cs = stack[top]
-
_goto_level = _again
-
next
-
end
-
end
-
when 13 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 36 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(33, p) end
-
when 23 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 37 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(34, p) end
-
when 14 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 28
-
_goto_level = _again
-
next
-
end
-
end
-
when 16 then
-
# line 8 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(5, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 28
-
_goto_level = _again
-
next
-
end
-
end
-
when 17 then
-
# line 8 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(5, p) end
-
# line 6 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
top -= 1
-
cs = stack[top]
-
_goto_level = _again
-
next
-
end
-
end
-
when 25 then
-
# line 37 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(34, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 28
-
_goto_level = _again
-
next
-
end
-
end
-
when 8 then
-
# line 38 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(35, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 28
-
_goto_level = _again
-
next
-
end
-
end
-
when 10 then
-
# line 42 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(39, p) end
-
# line 41 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(38, p) end
-
when 20 then
-
# line 45 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(42, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 28
-
_goto_level = _again
-
next
-
end
-
end
-
when 22 then
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 28
-
_goto_level = _again
-
next
-
end
-
end
-
# line 37 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(34, p) end
-
when 24 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 28
-
_goto_level = _again
-
next
-
end
-
end
-
# line 37 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(34, p) end
-
# line 763 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb"
-
end
-
end
-
end
-
if _goto_level <= _again
-
if cs == 0
-
_goto_level = _out
-
next
-
end
-
p += 1
-
if p != pe
-
_goto_level = _resume
-
next
-
end
-
end
-
if _goto_level <= _test_eof
-
if p == eof
-
case _content_type_eof_actions[cs]
-
when 12 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
when 21 then
-
# line 37 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(34, p) end
-
when 19 then
-
# line 45 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(42, p) end
-
when 23 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 37 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(34, p) end
-
# line 800 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb"
-
end
-
end
-
-
end
-
if _goto_level <= _out
-
break
-
end
-
end
-
end
-
-
# line 27 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb.rl"
-
-
if p == eof && cs >=
-
# line 814 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb"
-
33
-
# line 28 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb.rl"
-
-
return actions, nil
-
else
-
return [], "Only able to parse up to #{data[0..p]}"
-
end
-
end
-
end
-
end
-
end
-
end
-
-
# line 1 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb.rl"
-
-
# line 10 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb.rl"
-
-
-
1
module Mail
-
1
module Parsers
-
1
module Ragel
-
1
module DateTimeMachine
-
-
# line 13 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb"
-
1
class << self
-
1
attr_accessor :_date_time_trans_keys
-
1
private :_date_time_trans_keys, :_date_time_trans_keys=
-
end
-
1
self._date_time_trans_keys = [
-
0, 0, 9, 87, 9, 87,
-
10, 10, 9, 32, 9,
-
87, 9, 83, 9, 83,
-
10, 10, 9, 32, 9, 83,
-
112, 117, 114, 114, 9,
-
57, 10, 10, 9, 32,
-
9, 57, 48, 57, 9, 57,
-
9, 57, 10, 10, 9,
-
32, 9, 57, 48, 57,
-
9, 58, 10, 10, 9, 32,
-
9, 58, 9, 57, 10,
-
10, 9, 32, 9, 57,
-
48, 57, 9, 58, 9, 122,
-
10, 10, 9, 32, 9,
-
58, 9, 57, 10, 10,
-
9, 32, 9, 57, 48, 57,
-
9, 40, 9, 122, 10,
-
10, 9, 32, 9, 40,
-
48, 57, 48, 57, 48, 57,
-
48, 57, 10, 10, 9,
-
32, 84, 84, 103, 103,
-
101, 101, 99, 99, 101, 101,
-
98, 98, 97, 117, 110,
-
110, 108, 110, 97, 97,
-
114, 121, 111, 111, 118, 118,
-
99, 99, 116, 116, 101,
-
101, 112, 112, 114, 114,
-
105, 105, 9, 44, 10, 10,
-
9, 32, 9, 44, 9,
-
57, 9, 57, 10, 10,
-
9, 32, 9, 57, 111, 111,
-
110, 110, 97, 117, 116,
-
116, 104, 117, 117, 117,
-
101, 101, 101, 101, 100, 100,
-
1, 127, 1, 127, 10,
-
10, 9, 32, 0, 127,
-
9, 40, 9, 40, 9, 40,
-
9, 83, 9, 77, 9,
-
84, 0, 0, 0
-
]
-
-
1
class << self
-
1
attr_accessor :_date_time_key_spans
-
1
private :_date_time_key_spans, :_date_time_key_spans=
-
end
-
1
self._date_time_key_spans = [
-
0, 79, 79, 1, 24, 79, 75, 75,
-
1, 24, 75, 6, 1, 49, 1, 24,
-
49, 10, 49, 49, 1, 24, 49, 10,
-
50, 1, 24, 50, 49, 1, 24, 49,
-
10, 50, 114, 1, 24, 50, 49, 1,
-
24, 49, 10, 32, 114, 1, 24, 32,
-
10, 10, 10, 10, 1, 24, 1, 1,
-
1, 1, 1, 1, 21, 1, 3, 1,
-
8, 1, 1, 1, 1, 1, 1, 1,
-
1, 36, 1, 24, 36, 49, 49, 1,
-
24, 49, 1, 1, 21, 1, 14, 1,
-
1, 1, 1, 127, 127, 1, 24, 128,
-
32, 32, 32, 75, 69, 76, 0
-
]
-
-
1
class << self
-
1
attr_accessor :_date_time_index_offsets
-
1
private :_date_time_index_offsets, :_date_time_index_offsets=
-
end
-
1
self._date_time_index_offsets = [
-
0, 0, 80, 160, 162, 187, 267, 343,
-
419, 421, 446, 522, 529, 531, 581, 583,
-
608, 658, 669, 719, 769, 771, 796, 846,
-
857, 908, 910, 935, 986, 1036, 1038, 1063,
-
1113, 1124, 1175, 1290, 1292, 1317, 1368, 1418,
-
1420, 1445, 1495, 1506, 1539, 1654, 1656, 1681,
-
1714, 1725, 1736, 1747, 1758, 1760, 1785, 1787,
-
1789, 1791, 1793, 1795, 1797, 1819, 1821, 1825,
-
1827, 1836, 1838, 1840, 1842, 1844, 1846, 1848,
-
1850, 1852, 1889, 1891, 1916, 1953, 2003, 2053,
-
2055, 2080, 2130, 2132, 2134, 2156, 2158, 2173,
-
2175, 2177, 2179, 2181, 2309, 2437, 2439, 2464,
-
2593, 2626, 2659, 2692, 2768, 2838, 2915
-
]
-
-
1
class << self
-
1
attr_accessor :_date_time_indicies
-
1
private :_date_time_indicies, :_date_time_indicies=
-
end
-
1
self._date_time_indicies = [
-
0, 1, 1, 1, 2, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 0,
-
1, 1, 1, 1, 1, 1, 1, 3,
-
1, 1, 1, 1, 1, 1, 1, 4,
-
4, 4, 4, 4, 4, 4, 4, 4,
-
4, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 5, 1, 1,
-
1, 1, 1, 1, 6, 1, 1, 1,
-
1, 1, 7, 8, 1, 1, 9, 1,
-
10, 1, 1, 1, 11, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 10,
-
1, 1, 1, 1, 1, 1, 1, 12,
-
1, 1, 1, 1, 1, 1, 1, 13,
-
13, 13, 13, 13, 13, 13, 13, 13,
-
13, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 5, 1, 1,
-
1, 1, 1, 1, 6, 1, 1, 1,
-
1, 1, 7, 8, 1, 1, 9, 1,
-
14, 1, 10, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 10, 1, 15, 1, 1, 1, 16,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 15, 1, 1, 1, 1, 1,
-
1, 1, 17, 1, 1, 1, 1, 1,
-
1, 1, 18, 18, 18, 18, 18, 18,
-
18, 18, 18, 18, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
19, 1, 1, 1, 1, 1, 1, 20,
-
1, 1, 1, 1, 1, 21, 22, 1,
-
1, 23, 1, 24, 1, 1, 1, 25,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 24, 1, 1, 1, 1, 1,
-
1, 1, 26, 1, 1, 1, 1, 1,
-
1, 1, 24, 24, 24, 24, 24, 24,
-
24, 24, 24, 24, 1, 1, 1, 1,
-
1, 1, 1, 27, 1, 1, 28, 1,
-
29, 1, 1, 1, 30, 1, 1, 31,
-
32, 33, 1, 1, 1, 34, 1, 24,
-
1, 1, 1, 25, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 24, 1,
-
1, 1, 1, 1, 1, 1, 26, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 27,
-
1, 1, 28, 1, 29, 1, 1, 1,
-
30, 1, 1, 31, 32, 33, 1, 1,
-
1, 34, 1, 35, 1, 24, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 24, 1, 36, 1,
-
1, 1, 37, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 36, 1, 1,
-
1, 1, 1, 1, 1, 38, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 39, 1,
-
1, 40, 1, 41, 1, 1, 1, 42,
-
1, 1, 43, 44, 45, 1, 1, 1,
-
46, 1, 47, 1, 1, 1, 1, 48,
-
1, 49, 1, 49, 1, 1, 1, 50,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 49, 1, 1, 1, 1, 1,
-
1, 1, 51, 1, 1, 1, 1, 1,
-
1, 1, 52, 52, 52, 52, 52, 52,
-
52, 52, 52, 52, 1, 53, 1, 49,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 49, 1,
-
54, 1, 1, 1, 55, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 54,
-
1, 1, 1, 1, 1, 1, 1, 56,
-
1, 1, 1, 1, 1, 1, 1, 57,
-
57, 57, 57, 57, 57, 57, 57, 57,
-
57, 1, 58, 58, 58, 58, 58, 58,
-
58, 58, 58, 58, 1, 59, 1, 1,
-
1, 60, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 59, 1, 1, 1,
-
1, 1, 1, 1, 61, 1, 1, 1,
-
1, 1, 1, 1, 58, 58, 58, 58,
-
58, 58, 58, 58, 58, 58, 1, 59,
-
1, 1, 1, 60, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 59, 1,
-
1, 1, 1, 1, 1, 1, 61, 1,
-
1, 1, 1, 1, 1, 1, 62, 62,
-
62, 62, 62, 62, 62, 62, 62, 62,
-
1, 63, 1, 59, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 59, 1, 64, 1, 1, 1,
-
65, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 64, 1, 1, 1, 1,
-
1, 1, 1, 66, 1, 1, 1, 1,
-
1, 1, 1, 67, 67, 67, 67, 67,
-
67, 67, 67, 67, 67, 1, 68, 68,
-
68, 68, 68, 68, 68, 68, 68, 68,
-
1, 68, 1, 1, 1, 69, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
68, 1, 1, 1, 1, 1, 1, 1,
-
70, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 71, 1, 72, 1, 68, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 68, 1, 73,
-
1, 1, 1, 74, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 73, 1,
-
1, 1, 1, 1, 1, 1, 75, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
76, 1, 71, 1, 1, 1, 77, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 71, 1, 1, 1, 1, 1, 1,
-
1, 78, 1, 1, 1, 1, 1, 1,
-
1, 79, 79, 79, 79, 79, 79, 79,
-
79, 79, 79, 1, 80, 1, 71, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 71, 1, 76,
-
1, 1, 1, 81, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 76, 1,
-
1, 1, 1, 1, 1, 1, 82, 1,
-
1, 1, 1, 1, 1, 1, 83, 83,
-
83, 83, 83, 83, 83, 83, 83, 83,
-
1, 84, 84, 84, 84, 84, 84, 84,
-
84, 84, 84, 1, 85, 1, 1, 1,
-
86, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 85, 1, 1, 1, 1,
-
1, 1, 1, 87, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 88, 1, 85,
-
1, 1, 1, 86, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 85, 1,
-
1, 1, 1, 1, 1, 1, 87, 1,
-
1, 89, 1, 89, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
88, 1, 1, 1, 1, 1, 1, 90,
-
90, 91, 90, 91, 90, 92, 90, 90,
-
1, 90, 90, 91, 90, 90, 91, 90,
-
90, 90, 90, 93, 90, 90, 90, 90,
-
90, 1, 1, 1, 1, 1, 1, 90,
-
90, 90, 90, 90, 90, 90, 90, 90,
-
1, 90, 90, 90, 90, 90, 90, 90,
-
90, 90, 90, 90, 90, 90, 90, 90,
-
90, 1, 94, 1, 85, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 85, 1, 95, 1, 1,
-
1, 96, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 95, 1, 1, 1,
-
1, 1, 1, 1, 97, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 98, 1,
-
88, 1, 1, 1, 99, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 88,
-
1, 1, 1, 1, 1, 1, 1, 100,
-
1, 1, 1, 1, 1, 1, 1, 101,
-
101, 101, 101, 101, 101, 101, 101, 101,
-
101, 1, 102, 1, 88, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 88, 1, 98, 1, 1,
-
1, 103, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 98, 1, 1, 1,
-
1, 1, 1, 1, 104, 1, 1, 1,
-
1, 1, 1, 1, 105, 105, 105, 105,
-
105, 105, 105, 105, 105, 105, 1, 106,
-
106, 106, 106, 106, 106, 106, 106, 106,
-
106, 1, 107, 1, 1, 1, 108, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 107, 1, 1, 1, 1, 1, 1,
-
1, 109, 1, 107, 1, 1, 1, 108,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 107, 1, 1, 1, 1, 1,
-
1, 1, 109, 1, 1, 89, 1, 89,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 90, 90, 91, 90, 91,
-
90, 92, 90, 90, 1, 90, 90, 91,
-
90, 90, 91, 90, 90, 90, 90, 93,
-
90, 90, 90, 90, 90, 1, 1, 1,
-
1, 1, 1, 90, 90, 90, 90, 90,
-
90, 90, 90, 90, 1, 90, 90, 90,
-
90, 90, 90, 90, 90, 90, 90, 90,
-
90, 90, 90, 90, 90, 1, 110, 1,
-
107, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 107,
-
1, 111, 1, 1, 1, 112, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
111, 1, 1, 1, 1, 1, 1, 1,
-
113, 1, 114, 114, 114, 114, 114, 114,
-
114, 114, 114, 114, 1, 115, 115, 115,
-
115, 115, 115, 115, 115, 115, 115, 1,
-
116, 116, 116, 116, 116, 116, 116, 116,
-
116, 116, 1, 90, 90, 90, 90, 90,
-
90, 90, 90, 90, 90, 1, 117, 1,
-
118, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 118,
-
1, 90, 1, 49, 1, 119, 1, 49,
-
1, 120, 1, 49, 1, 121, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 122, 1, 49, 1, 49, 1, 49,
-
1, 123, 1, 49, 1, 1, 1, 1,
-
1, 1, 49, 1, 124, 1, 49, 1,
-
125, 1, 49, 1, 126, 1, 49, 1,
-
127, 1, 128, 1, 128, 1, 1, 1,
-
129, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 128, 1, 1, 1, 1,
-
1, 1, 1, 130, 1, 1, 1, 131,
-
1, 132, 1, 128, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 128, 1, 133, 1, 1, 1,
-
134, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 133, 1, 1, 1, 1,
-
1, 1, 1, 135, 1, 1, 1, 136,
-
1, 137, 1, 1, 1, 138, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
137, 1, 1, 1, 1, 1, 1, 1,
-
139, 1, 1, 1, 1, 1, 1, 1,
-
4, 4, 4, 4, 4, 4, 4, 4,
-
4, 4, 1, 140, 1, 1, 1, 141,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 140, 1, 1, 1, 1, 1,
-
1, 1, 142, 1, 1, 1, 1, 1,
-
1, 1, 13, 13, 13, 13, 13, 13,
-
13, 13, 13, 13, 1, 143, 1, 140,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 140, 1,
-
144, 1, 1, 1, 145, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 144,
-
1, 1, 1, 1, 1, 1, 1, 146,
-
1, 1, 1, 1, 1, 1, 1, 18,
-
18, 18, 18, 18, 18, 18, 18, 18,
-
18, 1, 147, 1, 128, 1, 148, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 147, 1, 128, 1, 149, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 150, 1, 128, 1, 128,
-
1, 151, 1, 128, 1, 152, 152, 152,
-
152, 152, 152, 152, 152, 152, 1, 152,
-
152, 153, 152, 152, 152, 152, 152, 152,
-
152, 152, 152, 152, 152, 152, 152, 152,
-
152, 152, 152, 152, 152, 152, 152, 152,
-
152, 152, 152, 152, 154, 155, 152, 152,
-
152, 152, 152, 152, 152, 152, 152, 152,
-
152, 152, 152, 152, 152, 152, 152, 152,
-
152, 152, 152, 152, 152, 152, 152, 152,
-
152, 152, 152, 152, 152, 152, 152, 152,
-
152, 152, 152, 152, 152, 152, 152, 152,
-
152, 152, 152, 152, 152, 152, 152, 152,
-
156, 152, 152, 152, 152, 152, 152, 152,
-
152, 152, 152, 152, 152, 152, 152, 152,
-
152, 152, 152, 152, 152, 152, 152, 152,
-
152, 152, 152, 152, 152, 152, 152, 152,
-
152, 152, 152, 152, 1, 157, 157, 157,
-
157, 157, 157, 157, 157, 157, 1, 157,
-
157, 158, 157, 157, 157, 157, 157, 157,
-
157, 157, 157, 157, 157, 157, 157, 157,
-
157, 157, 157, 157, 157, 157, 157, 157,
-
157, 157, 157, 157, 159, 160, 157, 157,
-
157, 157, 157, 157, 157, 157, 157, 157,
-
157, 157, 157, 157, 157, 157, 157, 157,
-
157, 157, 157, 157, 157, 157, 157, 157,
-
157, 157, 157, 157, 157, 157, 157, 157,
-
157, 157, 157, 157, 157, 157, 157, 157,
-
157, 157, 157, 157, 157, 157, 157, 157,
-
161, 157, 157, 157, 157, 157, 157, 157,
-
157, 157, 157, 157, 157, 157, 157, 157,
-
157, 157, 157, 157, 157, 157, 157, 157,
-
157, 157, 157, 157, 157, 157, 157, 157,
-
157, 157, 157, 157, 1, 162, 1, 157,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 157, 1,
-
157, 157, 157, 157, 157, 157, 157, 157,
-
157, 157, 157, 157, 157, 157, 157, 157,
-
157, 157, 157, 157, 157, 157, 157, 157,
-
157, 157, 157, 157, 157, 157, 157, 157,
-
157, 157, 157, 157, 157, 157, 157, 157,
-
157, 157, 157, 157, 157, 157, 157, 157,
-
157, 157, 157, 157, 157, 157, 157, 157,
-
157, 157, 157, 157, 157, 157, 157, 157,
-
157, 157, 157, 157, 157, 157, 157, 157,
-
157, 157, 157, 157, 157, 157, 157, 157,
-
157, 157, 157, 157, 157, 157, 157, 157,
-
157, 157, 157, 157, 157, 157, 157, 157,
-
157, 157, 157, 157, 157, 157, 157, 157,
-
157, 157, 157, 157, 157, 157, 157, 157,
-
157, 157, 157, 157, 157, 157, 157, 157,
-
157, 157, 157, 157, 157, 157, 157, 157,
-
1, 163, 1, 1, 1, 164, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
163, 1, 1, 1, 1, 1, 1, 1,
-
165, 1, 118, 1, 1, 1, 166, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 118, 1, 1, 1, 1, 1, 1,
-
1, 167, 1, 168, 1, 1, 1, 169,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 168, 1, 1, 1, 1, 1,
-
1, 1, 170, 1, 163, 1, 1, 1,
-
164, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 163, 1, 1, 1, 1,
-
1, 1, 1, 165, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 171,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 171, 1,
-
163, 1, 1, 1, 164, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 163,
-
1, 1, 1, 1, 1, 1, 1, 165,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 171, 1, 163, 1,
-
1, 1, 164, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 163, 1, 1,
-
1, 1, 1, 1, 1, 165, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 90, 1, 1, 0
-
]
-
-
1
class << self
-
1
attr_accessor :_date_time_trans_targs
-
1
private :_date_time_trans_targs, :_date_time_trans_targs=
-
end
-
1
self._date_time_trans_targs = [
-
2, 0, 3, 5, 6, 71, 82, 84,
-
86, 89, 2, 3, 5, 6, 4, 2,
-
3, 5, 6, 71, 82, 84, 86, 89,
-
7, 8, 10, 11, 56, 58, 60, 63,
-
65, 67, 69, 9, 7, 8, 10, 11,
-
56, 58, 60, 63, 65, 67, 69, 12,
-
55, 13, 14, 16, 17, 15, 13, 14,
-
16, 17, 18, 19, 20, 22, 23, 21,
-
19, 20, 22, 23, 24, 25, 27, 28,
-
26, 24, 25, 27, 28, 29, 31, 32,
-
30, 29, 31, 32, 33, 34, 35, 37,
-
38, 48, 96, 99, 100, 101, 36, 34,
-
35, 37, 38, 39, 41, 42, 40, 39,
-
41, 42, 43, 44, 45, 47, 46, 44,
-
45, 47, 49, 50, 51, 53, 97, 57,
-
59, 61, 62, 64, 66, 68, 70, 72,
-
73, 74, 76, 77, 75, 73, 74, 76,
-
77, 78, 79, 81, 78, 79, 81, 80,
-
78, 79, 81, 83, 85, 87, 88, 90,
-
92, 93, 92, 102, 95, 92, 93, 92,
-
102, 95, 94, 97, 52, 98, 52, 98,
-
97, 52, 98, 54
-
]
-
-
1
class << self
-
1
attr_accessor :_date_time_trans_actions
-
1
private :_date_time_trans_actions, :_date_time_trans_actions=
-
end
-
1
self._date_time_trans_actions = [
-
1, 0, 1, 2, 1, 0, 0, 0,
-
0, 0, 0, 0, 3, 0, 0, 4,
-
4, 5, 4, 4, 4, 4, 4, 4,
-
0, 0, 3, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 4, 4, 5, 4,
-
4, 4, 4, 4, 4, 4, 4, 0,
-
0, 0, 0, 3, 0, 0, 4, 4,
-
5, 4, 0, 0, 0, 3, 6, 0,
-
4, 4, 5, 7, 0, 0, 3, 0,
-
0, 4, 4, 5, 4, 0, 3, 0,
-
0, 4, 5, 4, 0, 0, 0, 3,
-
0, 0, 0, 0, 0, 0, 0, 4,
-
4, 5, 4, 0, 3, 0, 0, 4,
-
5, 4, 0, 0, 0, 3, 0, 4,
-
4, 5, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 3, 0, 0, 4, 4, 5,
-
4, 1, 1, 8, 0, 0, 3, 0,
-
4, 4, 5, 0, 0, 0, 0, 0,
-
9, 9, 10, 11, 9, 0, 0, 3,
-
12, 0, 0, 13, 13, 14, 0, 3,
-
4, 4, 5, 0
-
]
-
-
1
class << self
-
1
attr_accessor :_date_time_eof_actions
-
1
private :_date_time_eof_actions, :_date_time_eof_actions=
-
end
-
1
self._date_time_eof_actions = [
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
13, 0, 4, 13, 13, 13, 0
-
]
-
-
1
class << self
-
1
attr_accessor :date_time_start
-
end
-
1
self.date_time_start = 1;
-
1
class << self
-
1
attr_accessor :date_time_first_final
-
end
-
1
self.date_time_first_final = 96;
-
1
class << self
-
1
attr_accessor :date_time_error
-
end
-
1
self.date_time_error = 0;
-
-
1
class << self
-
1
attr_accessor :date_time_en_comment_tail
-
end
-
1
self.date_time_en_comment_tail = 91;
-
1
class << self
-
1
attr_accessor :date_time_en_main
-
end
-
1
self.date_time_en_main = 1;
-
-
-
# line 17 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb.rl"
-
-
1
def self.parse(data)
-
p = 0
-
eof = data.length
-
stack = []
-
-
actions = []
-
data_unpacked = data.bytes.to_a
-
-
# line 583 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb"
-
begin
-
p ||= 0
-
pe ||= data.length
-
cs = date_time_start
-
top = 0
-
end
-
-
# line 26 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb.rl"
-
-
# line 593 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb"
-
begin
-
testEof = false
-
_slen, _trans, _keys, _inds, _acts, _nacts = nil
-
_goto_level = 0
-
_resume = 10
-
_eof_trans = 15
-
_again = 20
-
_test_eof = 30
-
_out = 40
-
while true
-
if _goto_level <= 0
-
if p == pe
-
_goto_level = _test_eof
-
next
-
end
-
if cs == 0
-
_goto_level = _out
-
next
-
end
-
end
-
if _goto_level <= _resume
-
_keys = cs << 1
-
_inds = _date_time_index_offsets[cs]
-
_slen = _date_time_key_spans[cs]
-
_trans = if ( _slen > 0 &&
-
_date_time_trans_keys[_keys] <= ( data_unpacked[p]) &&
-
( data_unpacked[p]) <= _date_time_trans_keys[_keys + 1]
-
) then
-
_date_time_indicies[ _inds + ( data_unpacked[p]) - _date_time_trans_keys[_keys] ]
-
else
-
_date_time_indicies[ _inds + _slen ]
-
end
-
cs = _date_time_trans_targs[_trans]
-
if _date_time_trans_actions[_trans] != 0
-
case _date_time_trans_actions[_trans]
-
when 4 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
when 9 then
-
# line 8 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(5, p) end
-
when 1 then
-
# line 12 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(9, p) end
-
when 13 then
-
# line 47 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(44, p) end
-
when 3 then
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 91
-
_goto_level = _again
-
next
-
end
-
end
-
when 12 then
-
# line 6 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
top -= 1
-
cs = stack[top]
-
_goto_level = _again
-
next
-
end
-
end
-
when 5 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 91
-
_goto_level = _again
-
next
-
end
-
end
-
when 10 then
-
# line 8 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(5, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 91
-
_goto_level = _again
-
next
-
end
-
end
-
when 11 then
-
# line 8 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(5, p) end
-
# line 6 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
top -= 1
-
cs = stack[top]
-
_goto_level = _again
-
next
-
end
-
end
-
when 6 then
-
# line 11 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(8, p) end
-
# line 48 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(45, p) end
-
when 8 then
-
# line 12 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(9, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 91
-
_goto_level = _again
-
next
-
end
-
end
-
when 14 then
-
# line 47 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(44, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 91
-
_goto_level = _again
-
next
-
end
-
end
-
when 2 then
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 91
-
_goto_level = _again
-
next
-
end
-
end
-
# line 12 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(9, p) end
-
when 7 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 11 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(8, p) end
-
# line 48 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(45, p) end
-
# line 766 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb"
-
end
-
end
-
end
-
if _goto_level <= _again
-
if cs == 0
-
_goto_level = _out
-
next
-
end
-
p += 1
-
if p != pe
-
_goto_level = _resume
-
next
-
end
-
end
-
if _goto_level <= _test_eof
-
if p == eof
-
case _date_time_eof_actions[cs]
-
when 4 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
when 13 then
-
# line 47 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(44, p) end
-
# line 792 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb"
-
end
-
end
-
-
end
-
if _goto_level <= _out
-
break
-
end
-
end
-
end
-
-
# line 27 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb.rl"
-
-
if p == eof && cs >=
-
# line 806 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb"
-
96
-
# line 28 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb.rl"
-
-
return actions, nil
-
else
-
return [], "Only able to parse up to #{data[0..p]}"
-
end
-
end
-
end
-
end
-
end
-
end
-
-
# line 1 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb.rl"
-
-
# line 10 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb.rl"
-
-
-
1
module Mail
-
1
module Parsers
-
1
module Ragel
-
1
module EnvelopeFromMachine
-
-
# line 13 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb"
-
1
class << self
-
1
attr_accessor :_envelope_from_trans_keys
-
1
private :_envelope_from_trans_keys, :_envelope_from_trans_keys=
-
end
-
1
self._envelope_from_trans_keys = [
-
0, 0, 9, 126, 9, 126,
-
10, 10, 9, 32, 9,
-
126, 9, 64, 10, 10,
-
9, 32, 9, 87, 9, 64,
-
9, 126, 10, 10, 9,
-
32, 9, 126, 9, 64,
-
10, 10, 9, 32, 9, 87,
-
9, 64, 9, 126, 9,
-
126, 10, 10, 9, 32,
-
9, 126, 9, 46, 10, 10,
-
9, 32, 9, 87, 9,
-
46, 9, 126, 10, 10,
-
9, 32, 9, 126, 9, 126,
-
114, 114, 105, 105, 32,
-
32, 32, 83, 112, 117,
-
114, 114, 32, 32, 9, 57,
-
10, 10, 9, 32, 9,
-
57, 9, 57, 9, 40,
-
10, 10, 9, 32, 9, 57,
-
10, 10, 9, 32, 9,
-
57, 48, 57, 9, 58,
-
10, 10, 9, 32, 9, 58,
-
9, 57, 10, 10, 9,
-
32, 9, 57, 48, 57,
-
9, 58, 10, 10, 9, 32,
-
9, 58, 10, 10, 9,
-
32, 9, 58, 48, 57,
-
10, 10, 9, 32, 9, 57,
-
10, 10, 9, 32, 9,
-
57, 48, 57, 9, 40,
-
10, 10, 9, 32, 9, 57,
-
10, 10, 9, 32, 9,
-
57, 9, 40, 9, 58,
-
9, 40, 103, 103, 101, 101,
-
99, 99, 101, 101, 98,
-
98, 97, 117, 110, 110,
-
108, 110, 97, 97, 114, 121,
-
111, 111, 118, 118, 99,
-
99, 116, 116, 101, 101,
-
112, 112, 111, 111, 110, 110,
-
97, 117, 116, 116, 104,
-
117, 117, 117, 101, 101,
-
101, 101, 100, 100, 1, 127,
-
1, 127, 10, 10, 9,
-
32, 9, 126, 9, 40,
-
10, 10, 9, 32, 9, 87,
-
9, 40, 33, 126, -128,
-
-1, 10, 10, 9, 32,
-
9, 126, 9, 126, 1, 127,
-
10, 10, 9, 32, 0,
-
127, 1, 127, 1, 127,
-
10, 10, 9, 32, -128, -1,
-
9, 126, 1, 127, 1,
-
127, 10, 10, 9, 32,
-
9, 126, 9, 64, 10, 10,
-
9, 32, 9, 87, 9,
-
64, -128, -1, 10, 10,
-
9, 32, 9, 126, 9, 126,
-
10, 10, 9, 32, 9,
-
126, 9, 126, 9, 126,
-
9, 126, 9, 126, 9, 87,
-
101, 114, 97, 111, 97,
-
117, 9, 126, 9, 126,
-
9, 126, 9, 126, 9, 126,
-
9, 126, 9, 126, 9,
-
126, 9, 126, 1, 127,
-
1, 127, 10, 10, 9, 32,
-
9, 126, -128, -1, 1,
-
127, 10, 10, 9, 32,
-
1, 127, 1, 127, 10, 10,
-
9, 32, 9, 126, 9,
-
64, 10, 10, 9, 32,
-
9, 87, 9, 64, -128, -1,
-
1, 127, 10, 10, 9,
-
32, 9, 64, 10, 10,
-
9, 32, 9, 87, 9, 64,
-
9, 126, 33, 126, 1,
-
127, 1, 127, 10, 10,
-
9, 32, -128, -1, 9, 57,
-
9, 40, 9, 40, 0,
-
0, 0
-
]
-
-
1
class << self
-
1
attr_accessor :_envelope_from_key_spans
-
1
private :_envelope_from_key_spans, :_envelope_from_key_spans=
-
end
-
1
self._envelope_from_key_spans = [
-
0, 118, 118, 1, 24, 118, 56, 1,
-
24, 79, 56, 118, 1, 24, 118, 56,
-
1, 24, 79, 56, 118, 118, 1, 24,
-
118, 38, 1, 24, 79, 38, 118, 1,
-
24, 118, 118, 1, 1, 1, 52, 6,
-
1, 1, 49, 1, 24, 49, 49, 32,
-
1, 24, 49, 1, 24, 49, 10, 50,
-
1, 24, 50, 49, 1, 24, 49, 10,
-
50, 1, 24, 50, 1, 24, 50, 10,
-
1, 24, 49, 1, 24, 49, 10, 32,
-
1, 24, 49, 1, 24, 49, 32, 50,
-
32, 1, 1, 1, 1, 1, 21, 1,
-
3, 1, 8, 1, 1, 1, 1, 1,
-
1, 1, 1, 21, 1, 14, 1, 1,
-
1, 1, 127, 127, 1, 24, 118, 32,
-
1, 24, 79, 32, 94, 128, 1, 24,
-
118, 118, 127, 1, 24, 128, 127, 127,
-
1, 24, 128, 118, 127, 127, 1, 24,
-
118, 56, 1, 24, 79, 56, 128, 1,
-
24, 118, 118, 1, 24, 118, 118, 118,
-
118, 118, 79, 14, 15, 21, 118, 118,
-
118, 118, 118, 118, 118, 118, 118, 127,
-
127, 1, 24, 118, 128, 127, 1, 24,
-
127, 127, 1, 24, 118, 56, 1, 24,
-
79, 56, 128, 127, 1, 24, 56, 1,
-
24, 79, 56, 118, 94, 127, 127, 1,
-
24, 128, 49, 32, 32, 0
-
]
-
-
1
class << self
-
1
attr_accessor :_envelope_from_index_offsets
-
1
private :_envelope_from_index_offsets, :_envelope_from_index_offsets=
-
end
-
1
self._envelope_from_index_offsets = [
-
0, 0, 119, 238, 240, 265, 384, 441,
-
443, 468, 548, 605, 724, 726, 751, 870,
-
927, 929, 954, 1034, 1091, 1210, 1329, 1331,
-
1356, 1475, 1514, 1516, 1541, 1621, 1660, 1779,
-
1781, 1806, 1925, 2044, 2046, 2048, 2050, 2103,
-
2110, 2112, 2114, 2164, 2166, 2191, 2241, 2291,
-
2324, 2326, 2351, 2401, 2403, 2428, 2478, 2489,
-
2540, 2542, 2567, 2618, 2668, 2670, 2695, 2745,
-
2756, 2807, 2809, 2834, 2885, 2887, 2912, 2963,
-
2974, 2976, 3001, 3051, 3053, 3078, 3128, 3139,
-
3172, 3174, 3199, 3249, 3251, 3276, 3326, 3359,
-
3410, 3443, 3445, 3447, 3449, 3451, 3453, 3475,
-
3477, 3481, 3483, 3492, 3494, 3496, 3498, 3500,
-
3502, 3504, 3506, 3508, 3530, 3532, 3547, 3549,
-
3551, 3553, 3555, 3683, 3811, 3813, 3838, 3957,
-
3990, 3992, 4017, 4097, 4130, 4225, 4354, 4356,
-
4381, 4500, 4619, 4747, 4749, 4774, 4903, 5031,
-
5159, 5161, 5186, 5315, 5434, 5562, 5690, 5692,
-
5717, 5836, 5893, 5895, 5920, 6000, 6057, 6186,
-
6188, 6213, 6332, 6451, 6453, 6478, 6597, 6716,
-
6835, 6954, 7073, 7153, 7168, 7184, 7206, 7325,
-
7444, 7563, 7682, 7801, 7920, 8039, 8158, 8277,
-
8405, 8533, 8535, 8560, 8679, 8808, 8936, 8938,
-
8963, 9091, 9219, 9221, 9246, 9365, 9422, 9424,
-
9449, 9529, 9586, 9715, 9843, 9845, 9870, 9927,
-
9929, 9954, 10034, 10091, 10210, 10305, 10433, 10561,
-
10563, 10588, 10717, 10767, 10800, 10833
-
]
-
-
1
class << self
-
1
attr_accessor :_envelope_from_indicies
-
1
private :_envelope_from_indicies, :_envelope_from_indicies=
-
end
-
1
self._envelope_from_indicies = [
-
0, 1, 1, 1, 2, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 0,
-
3, 4, 3, 3, 3, 3, 3, 5,
-
1, 3, 3, 1, 3, 6, 3, 3,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 1, 1, 1, 3, 1, 3, 1,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 3, 1, 1, 1, 3, 3, 3,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 3, 3, 3, 3, 3, 1, 7,
-
1, 1, 1, 8, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 7, 9,
-
10, 9, 9, 9, 9, 9, 11, 1,
-
9, 9, 1, 9, 12, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
1, 1, 1, 9, 1, 9, 1, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 1, 1, 1, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 1, 13, 1,
-
7, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 7,
-
1, 14, 1, 1, 1, 15, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
16, 17, 18, 17, 17, 17, 17, 17,
-
19, 1, 17, 17, 1, 17, 20, 17,
-
17, 17, 17, 17, 17, 17, 17, 17,
-
17, 17, 1, 1, 1, 17, 1, 17,
-
21, 17, 17, 17, 17, 17, 17, 17,
-
17, 17, 17, 17, 17, 17, 17, 17,
-
17, 17, 17, 17, 17, 17, 17, 17,
-
17, 17, 17, 1, 1, 1, 17, 17,
-
17, 17, 17, 17, 17, 17, 17, 17,
-
17, 17, 17, 17, 17, 17, 17, 17,
-
17, 17, 17, 17, 17, 17, 17, 17,
-
17, 17, 17, 17, 17, 17, 17, 1,
-
22, 1, 1, 1, 23, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 24,
-
1, 1, 1, 1, 1, 1, 1, 25,
-
1, 1, 1, 1, 1, 26, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 27,
-
1, 28, 1, 22, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 22, 1, 22, 1, 1, 1,
-
23, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 24, 1, 1, 1, 1,
-
1, 1, 1, 25, 1, 1, 1, 1,
-
1, 26, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 27, 1, 1, 1, 1,
-
1, 29, 1, 1, 1, 1, 1, 1,
-
30, 1, 1, 1, 1, 1, 31, 32,
-
1, 1, 33, 1, 34, 1, 1, 1,
-
35, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 36, 1, 1, 1, 1,
-
1, 1, 1, 37, 1, 1, 1, 1,
-
1, 38, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 39, 1, 26, 1, 1,
-
1, 40, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 26, 41, 42, 41,
-
41, 41, 41, 41, 43, 1, 41, 41,
-
1, 41, 1, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 1, 1,
-
1, 41, 1, 41, 1, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 1,
-
1, 1, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 1, 44, 1, 26, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 26, 1, 45,
-
1, 1, 1, 46, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 47, 41,
-
1, 41, 41, 41, 41, 41, 48, 1,
-
41, 41, 1, 41, 26, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
1, 1, 1, 41, 1, 41, 49, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 1, 1, 1, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 1, 45, 1,
-
1, 1, 46, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 47, 1, 1,
-
1, 1, 1, 1, 1, 48, 1, 1,
-
1, 1, 1, 26, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 49, 1, 50,
-
1, 45, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
45, 1, 45, 1, 1, 1, 46, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 47, 1, 1, 1, 1, 1, 1,
-
1, 48, 1, 1, 1, 1, 1, 26,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 49, 1, 1, 1, 1, 1, 29,
-
1, 1, 1, 1, 1, 1, 30, 1,
-
1, 1, 1, 1, 31, 32, 1, 1,
-
33, 1, 51, 1, 1, 1, 52, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 53, 1, 1, 1, 1, 1, 1,
-
1, 54, 1, 1, 1, 1, 1, 38,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 55, 1, 56, 1, 1, 1, 57,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 56, 58, 59, 58, 58, 58,
-
58, 58, 60, 1, 58, 58, 1, 58,
-
61, 58, 58, 58, 58, 58, 58, 58,
-
58, 58, 58, 58, 1, 1, 1, 58,
-
1, 58, 1, 58, 58, 58, 58, 58,
-
58, 58, 58, 58, 58, 58, 58, 58,
-
58, 58, 58, 58, 58, 58, 58, 58,
-
58, 58, 58, 58, 58, 62, 1, 1,
-
58, 58, 58, 58, 58, 58, 58, 58,
-
58, 58, 58, 58, 58, 58, 58, 58,
-
58, 58, 58, 58, 58, 58, 58, 58,
-
58, 58, 58, 58, 58, 58, 58, 58,
-
58, 1, 63, 1, 1, 1, 64, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 63, 65, 66, 65, 65, 65, 65,
-
65, 67, 1, 65, 65, 1, 65, 68,
-
65, 65, 65, 65, 65, 65, 65, 65,
-
65, 65, 65, 1, 1, 1, 65, 1,
-
65, 1, 65, 65, 65, 65, 65, 65,
-
65, 65, 65, 65, 65, 65, 65, 65,
-
65, 65, 65, 65, 65, 65, 65, 65,
-
65, 65, 65, 65, 69, 1, 1, 65,
-
65, 65, 65, 65, 65, 65, 65, 65,
-
65, 65, 65, 65, 65, 65, 65, 65,
-
65, 65, 65, 65, 65, 65, 65, 65,
-
65, 65, 65, 65, 65, 65, 65, 65,
-
1, 70, 1, 63, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 63, 1, 71, 1, 1, 1,
-
72, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 73, 65, 66, 65, 65,
-
65, 65, 65, 74, 1, 65, 65, 1,
-
65, 75, 65, 65, 65, 65, 65, 65,
-
65, 65, 65, 65, 65, 1, 1, 1,
-
65, 1, 65, 1, 65, 65, 65, 65,
-
65, 65, 65, 65, 65, 65, 65, 65,
-
65, 65, 65, 65, 65, 65, 65, 65,
-
65, 65, 65, 65, 65, 65, 1, 1,
-
1, 65, 65, 65, 65, 65, 65, 65,
-
65, 65, 65, 65, 65, 65, 65, 65,
-
65, 65, 65, 65, 65, 65, 65, 65,
-
65, 65, 65, 65, 65, 65, 65, 65,
-
65, 65, 1, 71, 1, 1, 1, 72,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 73, 1, 1, 1, 1, 1,
-
1, 1, 74, 1, 1, 1, 1, 1,
-
76, 1, 77, 1, 71, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 71, 1, 71, 1, 1,
-
1, 72, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 73, 1, 1, 1,
-
1, 1, 1, 1, 74, 1, 1, 1,
-
1, 1, 76, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 29, 1, 1, 1, 1, 1,
-
1, 30, 1, 1, 1, 1, 1, 31,
-
32, 1, 1, 33, 1, 78, 1, 1,
-
1, 79, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 80, 1, 1, 1,
-
1, 1, 1, 1, 81, 1, 1, 1,
-
1, 1, 82, 1, 76, 1, 1, 1,
-
83, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 76, 84, 1, 84, 84,
-
84, 84, 84, 85, 1, 84, 84, 1,
-
84, 1, 84, 84, 84, 84, 84, 84,
-
84, 84, 84, 84, 84, 1, 1, 1,
-
84, 1, 84, 1, 84, 84, 84, 84,
-
84, 84, 84, 84, 84, 84, 84, 84,
-
84, 84, 84, 84, 84, 84, 84, 84,
-
84, 84, 84, 84, 84, 84, 1, 1,
-
1, 84, 84, 84, 84, 84, 84, 84,
-
84, 84, 84, 84, 84, 84, 84, 84,
-
84, 84, 84, 84, 84, 84, 84, 84,
-
84, 84, 84, 84, 84, 84, 84, 84,
-
84, 84, 1, 86, 1, 76, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 76, 1, 71, 1,
-
1, 1, 72, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 73, 84, 1,
-
84, 84, 84, 84, 84, 74, 1, 84,
-
84, 1, 84, 76, 84, 84, 84, 84,
-
84, 84, 84, 84, 84, 84, 84, 1,
-
1, 1, 84, 1, 84, 1, 84, 84,
-
84, 84, 84, 84, 84, 84, 84, 84,
-
84, 84, 84, 84, 84, 84, 84, 84,
-
84, 84, 84, 84, 84, 84, 84, 84,
-
1, 1, 1, 84, 84, 84, 84, 84,
-
84, 84, 84, 84, 84, 84, 84, 84,
-
84, 84, 84, 84, 84, 84, 84, 84,
-
84, 84, 84, 84, 84, 84, 84, 84,
-
84, 84, 84, 84, 1, 82, 1, 1,
-
1, 87, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 82, 88, 1, 88,
-
88, 88, 88, 88, 89, 1, 88, 88,
-
1, 88, 1, 88, 88, 88, 88, 88,
-
88, 88, 88, 88, 88, 88, 1, 1,
-
1, 88, 1, 88, 1, 88, 88, 88,
-
88, 88, 88, 88, 88, 88, 88, 88,
-
88, 88, 88, 88, 88, 88, 88, 88,
-
88, 88, 88, 88, 88, 88, 88, 1,
-
1, 1, 88, 88, 88, 88, 88, 88,
-
88, 88, 88, 88, 88, 88, 88, 88,
-
88, 88, 88, 88, 88, 88, 88, 88,
-
88, 88, 88, 88, 88, 88, 88, 88,
-
88, 88, 88, 1, 90, 1, 91, 1,
-
92, 1, 92, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 93, 1, 1, 94, 1,
-
95, 1, 1, 1, 96, 1, 1, 97,
-
98, 99, 1, 1, 1, 100, 1, 101,
-
1, 1, 1, 1, 102, 1, 103, 1,
-
104, 1, 104, 1, 1, 1, 105, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 104, 1, 1, 1, 1, 1, 1,
-
1, 106, 1, 1, 1, 1, 1, 1,
-
1, 107, 107, 107, 107, 107, 107, 107,
-
107, 107, 107, 1, 108, 1, 104, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 104, 1, 109,
-
1, 1, 1, 110, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 109, 1,
-
1, 1, 1, 1, 1, 1, 111, 1,
-
1, 1, 1, 1, 1, 1, 112, 112,
-
112, 112, 112, 112, 112, 112, 112, 112,
-
1, 113, 1, 1, 1, 114, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
115, 1, 1, 1, 1, 1, 1, 1,
-
116, 1, 1, 1, 1, 1, 1, 1,
-
113, 113, 113, 113, 113, 113, 113, 113,
-
113, 113, 1, 113, 1, 1, 1, 114,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 115, 1, 1, 1, 1, 1,
-
1, 1, 116, 1, 117, 1, 113, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 113, 1, 115,
-
1, 1, 1, 118, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 115, 1,
-
1, 1, 1, 1, 1, 1, 119, 1,
-
1, 1, 1, 1, 1, 1, 120, 120,
-
120, 120, 120, 120, 120, 120, 120, 120,
-
1, 121, 1, 115, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 115, 1, 122, 1, 1, 1,
-
123, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 122, 1, 1, 1, 1,
-
1, 1, 1, 124, 1, 1, 1, 1,
-
1, 1, 1, 125, 125, 125, 125, 125,
-
125, 125, 125, 125, 125, 1, 126, 126,
-
126, 126, 126, 126, 126, 126, 126, 126,
-
1, 126, 1, 1, 1, 127, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
126, 1, 1, 1, 1, 1, 1, 1,
-
128, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 129, 1, 130, 1, 126, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 126, 1, 131,
-
1, 1, 1, 132, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 131, 1,
-
1, 1, 1, 1, 1, 1, 133, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
134, 1, 129, 1, 1, 1, 135, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 129, 1, 1, 1, 1, 1, 1,
-
1, 136, 1, 1, 1, 1, 1, 1,
-
1, 137, 137, 137, 137, 137, 137, 137,
-
137, 137, 137, 1, 138, 1, 129, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 129, 1, 134,
-
1, 1, 1, 139, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 134, 1,
-
1, 1, 1, 1, 1, 1, 140, 1,
-
1, 1, 1, 1, 1, 1, 141, 141,
-
141, 141, 141, 141, 141, 141, 141, 141,
-
1, 142, 142, 142, 142, 142, 142, 142,
-
142, 142, 142, 1, 142, 1, 1, 1,
-
143, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 144, 1, 1, 1, 1,
-
1, 1, 1, 145, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 146, 1, 147,
-
1, 142, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
142, 1, 144, 1, 1, 1, 148, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 144, 1, 1, 1, 1, 1, 1,
-
1, 149, 1, 1, 1, 1, 1, 1,
-
1, 150, 150, 150, 150, 150, 150, 150,
-
150, 150, 150, 146, 1, 151, 1, 144,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 144, 1,
-
152, 1, 1, 1, 153, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 152,
-
1, 1, 1, 1, 1, 1, 1, 154,
-
1, 1, 1, 1, 1, 1, 1, 155,
-
155, 155, 155, 155, 155, 155, 155, 155,
-
155, 156, 1, 157, 157, 157, 157, 157,
-
157, 157, 157, 157, 157, 1, 158, 1,
-
159, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 159,
-
1, 146, 1, 1, 1, 160, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
146, 1, 1, 1, 1, 1, 1, 1,
-
161, 1, 1, 1, 1, 1, 1, 1,
-
162, 162, 162, 162, 162, 162, 162, 162,
-
162, 162, 1, 163, 1, 146, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 146, 1, 156, 1,
-
1, 1, 164, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 156, 1, 1,
-
1, 1, 1, 1, 1, 165, 1, 1,
-
1, 1, 1, 1, 1, 166, 166, 166,
-
166, 166, 166, 166, 166, 166, 166, 1,
-
167, 167, 167, 167, 167, 167, 167, 167,
-
167, 167, 1, 167, 1, 1, 1, 168,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 169, 1, 1, 1, 1, 1,
-
1, 1, 170, 1, 171, 1, 167, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 167, 1, 169,
-
1, 1, 1, 172, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 169, 1,
-
1, 1, 1, 1, 1, 1, 173, 1,
-
1, 1, 1, 1, 1, 1, 150, 150,
-
150, 150, 150, 150, 150, 150, 150, 150,
-
1, 174, 1, 169, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 169, 1, 175, 1, 1, 1,
-
176, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 175, 1, 1, 1, 1,
-
1, 1, 1, 177, 1, 1, 1, 1,
-
1, 1, 1, 155, 155, 155, 155, 155,
-
155, 155, 155, 155, 155, 1, 178, 1,
-
1, 1, 179, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 175, 1, 1,
-
1, 1, 1, 1, 1, 180, 1, 181,
-
1, 1, 1, 182, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 152, 1,
-
1, 1, 1, 1, 1, 1, 183, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
156, 1, 184, 1, 1, 1, 185, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 122, 1, 1, 1, 1, 1, 1,
-
1, 186, 1, 103, 1, 187, 1, 103,
-
1, 188, 1, 103, 1, 189, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 190, 1, 103, 1, 103, 1, 103,
-
1, 191, 1, 103, 1, 1, 1, 1,
-
1, 1, 103, 1, 192, 1, 103, 1,
-
193, 1, 103, 1, 194, 1, 103, 1,
-
195, 1, 91, 1, 196, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
195, 1, 91, 1, 197, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 198, 1, 91, 1, 91, 1, 199,
-
1, 91, 1, 200, 200, 200, 200, 200,
-
200, 200, 200, 66, 1, 200, 200, 201,
-
200, 200, 200, 200, 200, 200, 200, 200,
-
200, 200, 200, 200, 200, 200, 200, 200,
-
200, 200, 66, 200, 1, 200, 200, 200,
-
200, 200, 200, 200, 200, 200, 200, 200,
-
200, 200, 200, 200, 200, 200, 200, 200,
-
200, 200, 200, 200, 200, 200, 200, 200,
-
200, 200, 200, 200, 200, 200, 200, 200,
-
200, 200, 200, 200, 200, 200, 200, 200,
-
200, 200, 200, 200, 200, 200, 200, 200,
-
200, 200, 200, 200, 200, 200, 202, 200,
-
200, 200, 200, 200, 200, 200, 200, 200,
-
200, 200, 200, 200, 200, 200, 200, 200,
-
200, 200, 200, 200, 200, 200, 200, 200,
-
200, 200, 200, 200, 200, 200, 200, 200,
-
200, 200, 1, 200, 200, 200, 200, 200,
-
200, 200, 200, 200, 1, 200, 200, 203,
-
200, 200, 200, 200, 200, 200, 200, 200,
-
200, 200, 200, 200, 200, 200, 200, 200,
-
200, 200, 200, 200, 204, 200, 200, 200,
-
200, 200, 200, 200, 200, 200, 200, 200,
-
200, 200, 200, 200, 200, 200, 200, 200,
-
200, 200, 200, 200, 200, 200, 200, 200,
-
200, 200, 200, 200, 200, 200, 200, 200,
-
200, 200, 200, 200, 200, 200, 200, 200,
-
200, 200, 200, 200, 200, 200, 200, 200,
-
200, 200, 200, 200, 200, 200, 202, 200,
-
200, 200, 200, 200, 200, 200, 200, 200,
-
200, 200, 200, 200, 200, 200, 200, 200,
-
200, 200, 200, 200, 200, 200, 200, 200,
-
200, 200, 200, 200, 200, 200, 200, 200,
-
200, 200, 1, 205, 1, 200, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 200, 1, 206, 1,
-
1, 1, 207, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 208, 204, 66,
-
204, 204, 204, 204, 204, 209, 1, 204,
-
204, 1, 204, 68, 204, 204, 204, 204,
-
204, 204, 204, 204, 204, 204, 204, 1,
-
1, 1, 204, 1, 204, 1, 204, 204,
-
204, 204, 204, 204, 204, 204, 204, 204,
-
204, 204, 204, 204, 204, 204, 204, 204,
-
204, 204, 204, 204, 204, 204, 204, 204,
-
1, 1, 1, 204, 204, 204, 204, 204,
-
204, 204, 204, 204, 204, 204, 204, 204,
-
204, 204, 204, 204, 204, 204, 204, 204,
-
204, 204, 204, 204, 204, 204, 204, 204,
-
204, 204, 204, 204, 1, 206, 1, 1,
-
1, 207, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 208, 1, 1, 1,
-
1, 1, 1, 1, 209, 1, 210, 1,
-
206, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 206,
-
1, 206, 1, 1, 1, 207, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
208, 1, 1, 1, 1, 1, 1, 1,
-
209, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 29, 1,
-
1, 1, 1, 1, 1, 30, 1, 1,
-
1, 1, 1, 31, 32, 1, 1, 33,
-
1, 211, 1, 1, 1, 212, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
213, 1, 1, 1, 1, 1, 1, 1,
-
214, 1, 204, 66, 204, 204, 204, 204,
-
204, 1, 1, 204, 204, 1, 204, 68,
-
204, 204, 204, 204, 204, 204, 204, 204,
-
204, 204, 204, 1, 1, 1, 204, 1,
-
204, 1, 204, 204, 204, 204, 204, 204,
-
204, 204, 204, 204, 204, 204, 204, 204,
-
204, 204, 204, 204, 204, 204, 204, 204,
-
204, 204, 204, 204, 1, 1, 1, 204,
-
204, 204, 204, 204, 204, 204, 204, 204,
-
204, 204, 204, 204, 204, 204, 204, 204,
-
204, 204, 204, 204, 204, 204, 204, 204,
-
204, 204, 204, 204, 204, 204, 204, 204,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 200, 215, 1, 66, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 66, 1, 76, 1, 1,
-
1, 83, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 76, 65, 66, 65,
-
65, 65, 65, 65, 85, 1, 65, 65,
-
1, 65, 68, 65, 65, 65, 65, 65,
-
65, 65, 65, 65, 65, 65, 1, 1,
-
1, 65, 1, 65, 1, 65, 65, 65,
-
65, 65, 65, 65, 65, 65, 65, 65,
-
65, 65, 65, 65, 65, 65, 65, 65,
-
65, 65, 65, 65, 65, 65, 65, 1,
-
1, 1, 65, 65, 65, 65, 65, 65,
-
65, 65, 65, 65, 65, 65, 65, 65,
-
65, 65, 65, 65, 65, 65, 65, 65,
-
65, 65, 65, 65, 65, 65, 65, 65,
-
65, 65, 65, 1, 216, 1, 1, 1,
-
217, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 216, 218, 219, 218, 218,
-
218, 218, 218, 220, 1, 218, 218, 1,
-
218, 221, 218, 218, 218, 218, 218, 218,
-
218, 218, 218, 218, 218, 1, 1, 1,
-
218, 1, 218, 1, 218, 218, 218, 218,
-
218, 218, 218, 218, 218, 218, 218, 218,
-
218, 218, 218, 218, 218, 218, 218, 218,
-
218, 218, 218, 218, 218, 218, 222, 1,
-
1, 218, 218, 218, 218, 218, 218, 218,
-
218, 218, 218, 218, 218, 218, 218, 218,
-
218, 218, 218, 218, 218, 218, 218, 218,
-
218, 218, 218, 218, 218, 218, 218, 218,
-
218, 218, 1, 69, 69, 69, 69, 69,
-
69, 69, 69, 69, 1, 69, 69, 223,
-
69, 69, 69, 69, 69, 69, 69, 69,
-
69, 69, 69, 69, 69, 69, 69, 69,
-
69, 69, 69, 69, 69, 69, 69, 69,
-
69, 69, 69, 69, 69, 69, 69, 69,
-
69, 69, 69, 69, 69, 69, 69, 69,
-
69, 69, 69, 69, 69, 69, 69, 69,
-
69, 69, 69, 69, 69, 69, 69, 69,
-
69, 69, 69, 69, 69, 69, 69, 69,
-
69, 69, 69, 69, 69, 69, 69, 69,
-
69, 69, 69, 69, 69, 1, 224, 206,
-
69, 69, 69, 69, 69, 69, 69, 69,
-
69, 69, 69, 69, 69, 69, 69, 69,
-
69, 69, 69, 69, 69, 69, 69, 69,
-
69, 69, 69, 69, 69, 69, 69, 69,
-
69, 69, 1, 225, 1, 69, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 69, 1, 69, 69,
-
69, 69, 69, 69, 69, 69, 69, 69,
-
69, 69, 69, 69, 69, 69, 69, 69,
-
69, 69, 69, 69, 69, 69, 69, 69,
-
69, 69, 69, 69, 69, 69, 69, 69,
-
69, 69, 69, 69, 69, 69, 69, 69,
-
69, 69, 69, 69, 69, 69, 69, 69,
-
69, 69, 69, 69, 69, 69, 69, 69,
-
69, 69, 69, 69, 69, 69, 69, 69,
-
69, 69, 69, 69, 69, 69, 69, 69,
-
69, 69, 69, 69, 69, 69, 69, 69,
-
69, 69, 69, 69, 69, 69, 69, 69,
-
69, 69, 69, 69, 69, 69, 69, 69,
-
69, 69, 69, 69, 69, 69, 69, 69,
-
69, 69, 69, 69, 69, 69, 69, 69,
-
69, 69, 69, 69, 69, 69, 69, 69,
-
69, 69, 69, 69, 69, 69, 1, 226,
-
226, 226, 226, 226, 226, 226, 226, 226,
-
1, 226, 226, 227, 226, 226, 226, 226,
-
226, 226, 226, 226, 226, 226, 226, 226,
-
226, 226, 226, 226, 226, 226, 226, 226,
-
228, 226, 226, 226, 226, 226, 226, 226,
-
226, 226, 226, 226, 226, 226, 226, 226,
-
226, 226, 226, 226, 226, 226, 226, 226,
-
226, 226, 226, 226, 226, 226, 226, 226,
-
226, 226, 226, 226, 226, 226, 226, 226,
-
226, 226, 226, 226, 226, 226, 226, 226,
-
226, 226, 226, 226, 226, 226, 226, 226,
-
226, 226, 229, 226, 226, 226, 226, 226,
-
226, 226, 226, 226, 226, 226, 226, 226,
-
226, 226, 226, 226, 226, 226, 226, 226,
-
226, 226, 226, 226, 226, 226, 226, 226,
-
226, 226, 226, 226, 226, 226, 1, 230,
-
230, 230, 230, 230, 230, 230, 230, 230,
-
1, 230, 230, 231, 230, 230, 230, 230,
-
230, 230, 230, 230, 230, 230, 230, 230,
-
230, 230, 230, 230, 230, 230, 230, 230,
-
232, 230, 230, 230, 230, 230, 230, 230,
-
230, 230, 230, 230, 230, 230, 230, 230,
-
230, 230, 230, 230, 230, 230, 230, 230,
-
230, 230, 230, 230, 230, 230, 230, 230,
-
230, 230, 230, 230, 230, 230, 230, 230,
-
230, 230, 230, 230, 230, 230, 230, 230,
-
230, 230, 230, 230, 230, 230, 230, 230,
-
230, 230, 233, 230, 230, 230, 230, 230,
-
230, 230, 230, 230, 230, 230, 230, 230,
-
230, 230, 230, 230, 230, 230, 230, 230,
-
230, 230, 230, 230, 230, 230, 230, 230,
-
230, 230, 230, 230, 230, 230, 1, 234,
-
1, 230, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
230, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 230, 38, 1, 1, 1, 235,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 38, 236, 237, 236, 236, 236,
-
236, 236, 238, 1, 236, 236, 1, 236,
-
1, 236, 236, 236, 236, 236, 236, 236,
-
236, 236, 236, 236, 1, 1, 1, 236,
-
1, 236, 1, 236, 236, 236, 236, 236,
-
236, 236, 236, 236, 236, 236, 236, 236,
-
236, 236, 236, 236, 236, 236, 236, 236,
-
236, 236, 236, 236, 236, 1, 1, 1,
-
236, 236, 236, 236, 236, 236, 236, 236,
-
236, 236, 236, 236, 236, 236, 236, 236,
-
236, 236, 236, 236, 236, 236, 236, 236,
-
236, 236, 236, 236, 236, 236, 236, 236,
-
236, 1, 239, 239, 239, 239, 239, 239,
-
239, 239, 18, 1, 239, 239, 240, 239,
-
239, 239, 239, 239, 239, 239, 239, 239,
-
239, 239, 239, 239, 239, 239, 239, 239,
-
239, 18, 239, 1, 239, 239, 239, 239,
-
239, 239, 239, 239, 239, 239, 239, 239,
-
239, 239, 239, 239, 239, 239, 239, 239,
-
239, 239, 239, 239, 239, 239, 239, 239,
-
239, 239, 239, 239, 239, 239, 239, 239,
-
239, 239, 239, 239, 239, 239, 239, 239,
-
239, 239, 239, 239, 239, 239, 239, 239,
-
239, 239, 239, 239, 239, 241, 239, 239,
-
239, 239, 239, 239, 239, 239, 239, 239,
-
239, 239, 239, 239, 239, 239, 239, 239,
-
239, 239, 239, 239, 239, 239, 239, 239,
-
239, 239, 239, 239, 239, 239, 239, 239,
-
239, 1, 239, 239, 239, 239, 239, 239,
-
239, 239, 239, 1, 239, 239, 242, 239,
-
239, 239, 239, 239, 239, 239, 239, 239,
-
239, 239, 239, 239, 239, 239, 239, 239,
-
239, 239, 239, 243, 239, 239, 239, 239,
-
239, 239, 239, 239, 239, 239, 239, 239,
-
239, 239, 239, 239, 239, 239, 239, 239,
-
239, 239, 239, 239, 239, 239, 239, 239,
-
239, 239, 239, 239, 239, 239, 239, 239,
-
239, 239, 239, 239, 239, 239, 239, 239,
-
239, 239, 239, 239, 239, 239, 239, 239,
-
239, 239, 239, 239, 239, 241, 239, 239,
-
239, 239, 239, 239, 239, 239, 239, 239,
-
239, 239, 239, 239, 239, 239, 239, 239,
-
239, 239, 239, 239, 239, 239, 239, 239,
-
239, 239, 239, 239, 239, 239, 239, 239,
-
239, 1, 244, 1, 239, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 239, 1, 245, 1, 1,
-
1, 246, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 247, 243, 18, 243,
-
243, 243, 243, 243, 248, 1, 243, 243,
-
1, 243, 243, 243, 243, 243, 243, 243,
-
243, 243, 243, 243, 243, 243, 1, 1,
-
1, 243, 1, 243, 21, 243, 243, 243,
-
243, 243, 243, 243, 243, 243, 243, 243,
-
243, 243, 243, 243, 243, 243, 243, 243,
-
243, 243, 243, 243, 243, 243, 243, 1,
-
1, 1, 243, 243, 243, 243, 243, 243,
-
243, 243, 243, 243, 243, 243, 243, 243,
-
243, 243, 243, 243, 243, 243, 243, 243,
-
243, 243, 243, 243, 243, 243, 243, 243,
-
243, 243, 243, 1, 249, 1, 1, 1,
-
250, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 251, 1, 1, 1, 1,
-
1, 1, 1, 252, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 27, 1, 253, 1, 249,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 249, 1,
-
249, 1, 1, 1, 250, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 251,
-
1, 1, 1, 1, 1, 1, 1, 252,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 27,
-
1, 1, 1, 1, 1, 29, 1, 1,
-
1, 1, 1, 1, 30, 1, 1, 1,
-
1, 1, 31, 32, 1, 1, 33, 1,
-
254, 1, 1, 1, 255, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 256,
-
1, 1, 1, 1, 1, 1, 1, 257,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 39,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 239, 258, 1, 18, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 18, 1, 259, 1, 1,
-
1, 260, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 261, 17, 262, 17,
-
17, 17, 17, 17, 263, 1, 17, 17,
-
1, 17, 243, 17, 17, 17, 17, 17,
-
17, 17, 17, 17, 17, 17, 1, 1,
-
1, 17, 1, 17, 21, 17, 17, 17,
-
17, 17, 17, 17, 17, 17, 17, 17,
-
17, 17, 17, 17, 17, 17, 17, 17,
-
17, 17, 17, 17, 17, 17, 17, 1,
-
1, 1, 17, 17, 17, 17, 17, 17,
-
17, 17, 17, 17, 17, 17, 17, 17,
-
17, 17, 17, 17, 17, 17, 17, 17,
-
17, 17, 17, 17, 17, 17, 17, 17,
-
17, 17, 17, 1, 264, 1, 1, 1,
-
265, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 266, 41, 42, 41, 41,
-
41, 41, 41, 267, 1, 41, 41, 1,
-
41, 1, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 1, 1, 1,
-
41, 1, 41, 27, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 1, 1,
-
1, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 1, 268, 1, 264, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 264, 1, 264, 1,
-
1, 1, 265, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 266, 41, 42,
-
41, 41, 41, 41, 41, 267, 1, 41,
-
41, 1, 41, 1, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 1,
-
1, 1, 41, 1, 41, 27, 41, 41,
-
41, 41, 41, 269, 41, 41, 41, 41,
-
41, 41, 270, 41, 41, 41, 41, 41,
-
271, 272, 41, 41, 273, 41, 41, 41,
-
1, 1, 1, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 1, 274, 1, 1,
-
1, 275, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 276, 236, 237, 236,
-
236, 236, 236, 236, 277, 1, 236, 236,
-
1, 236, 1, 236, 236, 236, 236, 236,
-
236, 236, 236, 236, 236, 236, 1, 1,
-
1, 236, 1, 236, 39, 236, 236, 236,
-
236, 236, 236, 236, 236, 236, 236, 236,
-
236, 236, 236, 236, 236, 236, 236, 236,
-
236, 236, 236, 236, 236, 236, 236, 1,
-
1, 1, 236, 236, 236, 236, 236, 236,
-
236, 236, 236, 236, 236, 236, 236, 236,
-
236, 236, 236, 236, 236, 236, 236, 236,
-
236, 236, 236, 236, 236, 236, 236, 236,
-
236, 236, 236, 1, 45, 1, 1, 1,
-
46, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 47, 41, 1, 41, 41,
-
41, 41, 41, 48, 1, 41, 41, 1,
-
41, 26, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 1, 1, 1,
-
41, 1, 41, 49, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 1, 1,
-
1, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 278, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 1, 45, 1, 1, 1, 46,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 47, 41, 1, 41, 41, 41,
-
41, 41, 48, 1, 41, 41, 1, 41,
-
26, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 1, 1, 1, 41,
-
1, 41, 49, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 1, 1, 1,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 279, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 1, 45, 1, 1, 1, 46, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 280, 41, 1, 41, 41, 41, 41,
-
41, 48, 1, 41, 41, 1, 41, 26,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 1, 1, 1, 41, 1,
-
41, 49, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 1, 1, 1, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
1, 45, 1, 1, 1, 46, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
280, 1, 1, 1, 1, 1, 1, 1,
-
48, 1, 1, 1, 1, 1, 26, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
49, 93, 1, 1, 94, 1, 281, 1,
-
1, 1, 96, 1, 1, 282, 98, 99,
-
1, 1, 1, 283, 32, 1, 1, 33,
-
1, 188, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 90, 1,
-
191, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 195, 1,
-
196, 1, 1, 1, 194, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 195, 1, 45, 1,
-
1, 1, 46, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 47, 41, 1,
-
41, 41, 41, 41, 41, 48, 1, 41,
-
41, 1, 41, 26, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 1,
-
1, 1, 41, 1, 41, 49, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
1, 1, 1, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 284, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 1, 45, 1, 1,
-
1, 46, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 47, 41, 1, 41,
-
41, 41, 41, 41, 48, 1, 41, 41,
-
1, 41, 26, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 1, 1,
-
1, 41, 1, 41, 49, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 1,
-
1, 1, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 279, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 1, 45, 1, 1, 1,
-
46, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 47, 41, 1, 41, 41,
-
41, 41, 41, 48, 1, 41, 41, 1,
-
41, 26, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 1, 1, 1,
-
41, 1, 41, 49, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 1, 1,
-
1, 41, 41, 41, 285, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
284, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 1, 45, 1, 1, 1, 46,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 47, 41, 1, 41, 41, 41,
-
41, 41, 48, 1, 41, 41, 1, 41,
-
26, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 1, 1, 1, 41,
-
1, 41, 49, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 1, 1, 1,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 279, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 1, 45, 1, 1, 1, 46, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 47, 41, 1, 41, 41, 41, 41,
-
41, 48, 1, 41, 41, 1, 41, 26,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 1, 1, 1, 41, 1,
-
41, 49, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 1, 1, 1, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 286, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 287, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
1, 45, 1, 1, 1, 46, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
47, 41, 1, 41, 41, 41, 41, 41,
-
48, 1, 41, 41, 1, 41, 26, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 1, 1, 1, 41, 1, 41,
-
49, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 1, 1, 1, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 279, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 1,
-
45, 1, 1, 1, 46, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 47,
-
41, 1, 41, 41, 41, 41, 41, 48,
-
1, 41, 41, 1, 41, 26, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 1, 1, 1, 41, 1, 41, 49,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 1, 1, 1, 41, 41, 41,
-
41, 41, 41, 41, 279, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 1, 45,
-
1, 1, 1, 46, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 47, 41,
-
1, 41, 41, 41, 41, 41, 48, 1,
-
41, 41, 1, 41, 26, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
1, 1, 1, 41, 1, 41, 49, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 1, 1, 1, 41, 41, 41, 41,
-
41, 41, 41, 288, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 1, 45, 1,
-
1, 1, 46, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 47, 41, 1,
-
41, 41, 41, 41, 41, 48, 1, 41,
-
41, 1, 41, 26, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 1,
-
1, 1, 41, 1, 41, 49, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
1, 1, 1, 41, 41, 41, 41, 41,
-
41, 279, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 41, 41, 41, 41,
-
41, 41, 41, 41, 1, 289, 289, 289,
-
289, 289, 289, 289, 289, 290, 1, 289,
-
289, 291, 289, 289, 289, 289, 289, 289,
-
289, 289, 289, 289, 289, 289, 289, 289,
-
289, 289, 289, 289, 290, 289, 228, 289,
-
289, 289, 289, 289, 289, 289, 289, 289,
-
289, 289, 289, 289, 289, 289, 289, 289,
-
289, 289, 289, 289, 289, 289, 289, 289,
-
289, 289, 289, 289, 289, 289, 289, 289,
-
289, 289, 289, 289, 289, 289, 289, 289,
-
289, 289, 289, 289, 289, 289, 289, 289,
-
289, 289, 289, 289, 289, 289, 289, 289,
-
292, 289, 289, 289, 289, 289, 289, 289,
-
289, 289, 289, 289, 289, 289, 289, 289,
-
289, 289, 289, 289, 289, 289, 289, 289,
-
289, 289, 289, 289, 289, 289, 289, 289,
-
289, 289, 289, 289, 1, 293, 293, 293,
-
293, 293, 293, 293, 293, 293, 1, 293,
-
293, 294, 293, 293, 293, 293, 293, 293,
-
293, 293, 293, 293, 293, 293, 293, 293,
-
293, 293, 293, 293, 293, 293, 295, 293,
-
293, 293, 293, 293, 293, 293, 293, 293,
-
293, 293, 293, 293, 293, 293, 293, 293,
-
293, 293, 293, 293, 293, 293, 293, 293,
-
293, 293, 293, 293, 293, 293, 293, 293,
-
293, 293, 293, 293, 293, 293, 293, 293,
-
293, 293, 293, 293, 293, 293, 293, 293,
-
293, 293, 293, 293, 293, 293, 293, 293,
-
296, 293, 293, 293, 293, 293, 293, 293,
-
293, 293, 293, 293, 293, 293, 293, 293,
-
293, 293, 293, 293, 293, 293, 293, 293,
-
293, 293, 293, 293, 293, 293, 293, 293,
-
293, 293, 293, 293, 1, 297, 1, 293,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 293, 1,
-
14, 1, 1, 1, 15, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 16,
-
243, 18, 243, 243, 243, 243, 243, 19,
-
1, 243, 243, 1, 243, 20, 243, 243,
-
243, 243, 243, 243, 243, 243, 243, 243,
-
243, 1, 1, 1, 243, 1, 243, 21,
-
243, 243, 243, 243, 243, 243, 243, 243,
-
243, 243, 243, 243, 243, 243, 243, 243,
-
243, 243, 243, 243, 243, 243, 243, 243,
-
243, 243, 1, 1, 1, 243, 243, 243,
-
243, 243, 243, 243, 243, 243, 243, 243,
-
243, 243, 243, 243, 243, 243, 243, 243,
-
243, 243, 243, 243, 243, 243, 243, 243,
-
243, 243, 243, 243, 243, 243, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 293,
-
293, 293, 293, 293, 293, 293, 293, 293,
-
298, 1, 293, 293, 299, 293, 293, 293,
-
293, 293, 293, 293, 293, 293, 293, 293,
-
293, 293, 293, 293, 293, 293, 293, 298,
-
293, 232, 293, 293, 293, 293, 293, 293,
-
293, 293, 293, 293, 293, 293, 293, 293,
-
293, 293, 293, 293, 293, 293, 293, 293,
-
293, 293, 293, 293, 293, 293, 293, 293,
-
293, 293, 293, 293, 293, 293, 293, 293,
-
293, 293, 293, 293, 293, 293, 293, 293,
-
293, 293, 293, 293, 293, 293, 293, 293,
-
293, 293, 293, 296, 293, 293, 293, 293,
-
293, 293, 293, 293, 293, 293, 293, 293,
-
293, 293, 293, 293, 293, 293, 293, 293,
-
293, 293, 293, 293, 293, 293, 293, 293,
-
293, 293, 293, 293, 293, 293, 293, 1,
-
300, 1, 298, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 298, 1, 301, 301, 301, 301, 301,
-
301, 301, 301, 302, 1, 301, 301, 303,
-
301, 301, 301, 301, 301, 301, 301, 301,
-
301, 301, 301, 301, 301, 301, 301, 301,
-
301, 301, 302, 301, 304, 301, 301, 301,
-
301, 301, 301, 301, 301, 301, 301, 301,
-
301, 301, 301, 301, 301, 301, 301, 301,
-
301, 301, 301, 301, 301, 301, 301, 301,
-
301, 301, 301, 301, 301, 301, 301, 301,
-
301, 301, 301, 301, 301, 301, 301, 301,
-
301, 301, 301, 301, 301, 301, 301, 301,
-
301, 301, 301, 301, 301, 301, 305, 301,
-
301, 301, 301, 301, 301, 301, 301, 301,
-
301, 301, 301, 301, 301, 301, 301, 301,
-
301, 301, 301, 301, 301, 301, 301, 301,
-
301, 301, 301, 301, 301, 301, 301, 301,
-
301, 301, 1, 306, 306, 306, 306, 306,
-
306, 306, 306, 306, 1, 306, 306, 307,
-
306, 306, 306, 306, 306, 306, 306, 306,
-
306, 306, 306, 306, 306, 306, 306, 306,
-
306, 306, 306, 306, 308, 306, 306, 306,
-
306, 306, 306, 306, 306, 306, 306, 306,
-
306, 306, 306, 306, 306, 306, 306, 306,
-
306, 306, 306, 306, 306, 306, 306, 306,
-
306, 306, 306, 306, 306, 306, 306, 306,
-
306, 306, 306, 306, 306, 306, 306, 306,
-
306, 306, 306, 306, 306, 306, 306, 306,
-
306, 306, 306, 306, 306, 306, 309, 306,
-
306, 306, 306, 306, 306, 306, 306, 306,
-
306, 306, 306, 306, 306, 306, 306, 306,
-
306, 306, 306, 306, 306, 306, 306, 306,
-
306, 306, 306, 306, 306, 306, 306, 306,
-
306, 306, 1, 310, 1, 306, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 306, 1, 311, 1,
-
1, 1, 312, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 313, 243, 18,
-
243, 243, 243, 243, 243, 314, 1, 243,
-
243, 1, 243, 20, 243, 243, 243, 243,
-
243, 243, 243, 243, 243, 243, 243, 1,
-
1, 1, 243, 1, 243, 315, 243, 243,
-
243, 243, 243, 243, 243, 243, 243, 243,
-
243, 243, 243, 243, 243, 243, 243, 243,
-
243, 243, 243, 243, 243, 243, 243, 243,
-
1, 1, 1, 243, 243, 243, 243, 243,
-
243, 243, 243, 243, 243, 243, 243, 243,
-
243, 243, 243, 243, 243, 243, 243, 243,
-
243, 243, 243, 243, 243, 243, 243, 243,
-
243, 243, 243, 243, 1, 316, 1, 1,
-
1, 317, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 318, 1, 1, 1,
-
1, 1, 1, 1, 319, 1, 1, 1,
-
1, 1, 26, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 320, 1, 321, 1,
-
316, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 316,
-
1, 316, 1, 1, 1, 317, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
318, 1, 1, 1, 1, 1, 1, 1,
-
319, 1, 1, 1, 1, 1, 26, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
320, 1, 1, 1, 1, 1, 29, 1,
-
1, 1, 1, 1, 1, 30, 1, 1,
-
1, 1, 1, 31, 32, 1, 1, 33,
-
1, 322, 1, 1, 1, 323, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
324, 1, 1, 1, 1, 1, 1, 1,
-
325, 1, 1, 1, 1, 1, 38, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
326, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 306, 306, 306, 306, 306, 306,
-
306, 306, 306, 327, 1, 306, 306, 328,
-
306, 306, 306, 306, 306, 306, 306, 306,
-
306, 306, 306, 306, 306, 306, 306, 306,
-
306, 306, 327, 306, 329, 306, 306, 306,
-
306, 306, 306, 306, 306, 306, 306, 306,
-
306, 306, 306, 306, 306, 306, 306, 306,
-
306, 306, 306, 306, 306, 306, 306, 306,
-
306, 306, 306, 306, 306, 306, 306, 306,
-
306, 306, 306, 306, 306, 306, 306, 306,
-
306, 306, 306, 306, 306, 306, 306, 306,
-
306, 306, 306, 306, 306, 306, 309, 306,
-
306, 306, 306, 306, 306, 306, 306, 306,
-
306, 306, 306, 306, 306, 306, 306, 306,
-
306, 306, 306, 306, 306, 306, 306, 306,
-
306, 306, 306, 306, 306, 306, 306, 306,
-
306, 306, 1, 330, 1, 327, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 327, 1, 331, 1,
-
1, 1, 332, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 333, 1, 1,
-
1, 1, 1, 1, 1, 334, 1, 1,
-
1, 1, 1, 26, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 335, 1, 336,
-
1, 331, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
331, 1, 331, 1, 1, 1, 332, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 333, 1, 1, 1, 1, 1, 1,
-
1, 334, 1, 1, 1, 1, 1, 26,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 335, 1, 1, 1, 1, 1, 29,
-
1, 1, 1, 1, 1, 1, 30, 1,
-
1, 1, 1, 1, 31, 32, 1, 1,
-
33, 1, 337, 1, 1, 1, 338, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 339, 1, 1, 1, 1, 1, 1,
-
1, 340, 1, 1, 1, 1, 1, 38,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 341, 1, 342, 1, 1, 1, 343,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 342, 344, 345, 344, 344, 344,
-
344, 344, 346, 1, 344, 344, 1, 344,
-
347, 344, 344, 344, 344, 344, 344, 344,
-
344, 344, 344, 344, 1, 1, 1, 344,
-
1, 344, 1, 344, 344, 344, 344, 344,
-
344, 344, 344, 344, 344, 344, 344, 344,
-
344, 344, 344, 344, 344, 344, 344, 344,
-
344, 344, 344, 344, 344, 1, 1, 1,
-
344, 344, 344, 344, 344, 344, 344, 344,
-
344, 344, 344, 344, 344, 344, 344, 344,
-
344, 344, 344, 344, 344, 344, 344, 344,
-
344, 344, 344, 344, 344, 344, 344, 344,
-
344, 1, 243, 18, 243, 243, 243, 243,
-
243, 1, 1, 243, 243, 1, 243, 348,
-
243, 243, 243, 243, 243, 243, 243, 243,
-
243, 243, 243, 1, 1, 1, 243, 1,
-
243, 1, 243, 243, 243, 243, 243, 243,
-
243, 243, 243, 243, 243, 243, 243, 243,
-
243, 243, 243, 243, 243, 243, 243, 243,
-
243, 243, 243, 243, 1, 1, 1, 243,
-
243, 243, 243, 243, 243, 243, 243, 243,
-
243, 243, 243, 243, 243, 243, 243, 243,
-
243, 243, 243, 243, 243, 243, 243, 243,
-
243, 243, 243, 243, 243, 243, 243, 243,
-
1, 349, 349, 349, 349, 349, 349, 349,
-
349, 349, 1, 349, 349, 350, 349, 349,
-
349, 349, 349, 349, 349, 349, 349, 349,
-
349, 349, 349, 349, 349, 349, 349, 349,
-
349, 349, 349, 349, 349, 349, 349, 349,
-
351, 352, 349, 349, 349, 349, 349, 349,
-
349, 349, 349, 349, 349, 349, 349, 349,
-
349, 349, 349, 349, 349, 349, 349, 349,
-
349, 349, 349, 349, 349, 349, 349, 349,
-
349, 349, 349, 349, 349, 349, 349, 349,
-
349, 349, 349, 349, 349, 349, 349, 349,
-
349, 349, 349, 349, 353, 349, 349, 349,
-
349, 349, 349, 349, 349, 349, 349, 349,
-
349, 349, 349, 349, 349, 349, 349, 349,
-
349, 349, 349, 349, 349, 349, 349, 349,
-
349, 349, 349, 349, 349, 349, 349, 349,
-
1, 354, 354, 354, 354, 354, 354, 354,
-
354, 354, 1, 354, 354, 355, 354, 354,
-
354, 354, 354, 354, 354, 354, 354, 354,
-
354, 354, 354, 354, 354, 354, 354, 354,
-
354, 354, 354, 354, 354, 354, 354, 354,
-
356, 357, 354, 354, 354, 354, 354, 354,
-
354, 354, 354, 354, 354, 354, 354, 354,
-
354, 354, 354, 354, 354, 354, 354, 354,
-
354, 354, 354, 354, 354, 354, 354, 354,
-
354, 354, 354, 354, 354, 354, 354, 354,
-
354, 354, 354, 354, 354, 354, 354, 354,
-
354, 354, 354, 354, 358, 354, 354, 354,
-
354, 354, 354, 354, 354, 354, 354, 354,
-
354, 354, 354, 354, 354, 354, 354, 354,
-
354, 354, 354, 354, 354, 354, 354, 354,
-
354, 354, 354, 354, 354, 354, 354, 354,
-
1, 359, 1, 354, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 354, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 354, 159, 1, 1,
-
1, 360, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 159, 1, 1, 1,
-
1, 1, 1, 1, 361, 1, 1, 1,
-
1, 1, 1, 1, 157, 157, 157, 157,
-
157, 157, 157, 157, 157, 157, 1, 159,
-
1, 1, 1, 360, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 159, 1,
-
1, 1, 1, 1, 1, 1, 361, 1,
-
362, 1, 1, 1, 363, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 362,
-
1, 1, 1, 1, 1, 1, 1, 364,
-
1, 1, 0
-
]
-
-
1
class << self
-
1
attr_accessor :_envelope_from_trans_targs
-
1
private :_envelope_from_trans_targs, :_envelope_from_trans_targs=
-
end
-
1
self._envelope_from_trans_targs = [
-
2, 0, 3, 5, 184, 203, 204, 2,
-
3, 5, 184, 203, 204, 4, 6, 7,
-
9, 5, 140, 10, 153, 20, 6, 7,
-
9, 10, 11, 20, 8, 35, 105, 107,
-
109, 112, 6, 7, 9, 10, 11, 20,
-
12, 14, 134, 139, 13, 15, 16, 18,
-
19, 20, 17, 15, 16, 18, 19, 20,
-
21, 22, 24, 114, 129, 124, 130, 21,
-
22, 24, 114, 129, 124, 130, 23, 25,
-
26, 28, 29, 128, 30, 27, 25, 26,
-
28, 29, 30, 31, 33, 34, 32, 31,
-
33, 34, 36, 37, 38, 39, 90, 92,
-
94, 97, 99, 101, 103, 40, 89, 41,
-
42, 43, 45, 46, 44, 42, 43, 45,
-
46, 47, 48, 50, 88, 49, 51, 53,
-
54, 52, 50, 51, 53, 54, 55, 56,
-
58, 59, 57, 55, 56, 58, 59, 60,
-
62, 63, 61, 60, 62, 63, 64, 65,
-
67, 87, 74, 66, 68, 70, 71, 69,
-
67, 68, 70, 71, 74, 210, 73, 211,
-
75, 77, 78, 76, 75, 77, 78, 79,
-
80, 82, 86, 81, 83, 85, 84, 82,
-
83, 85, 79, 80, 86, 64, 65, 87,
-
47, 48, 88, 91, 93, 95, 96, 98,
-
100, 102, 104, 106, 108, 110, 111, 113,
-
115, 126, 125, 116, 118, 117, 119, 120,
-
122, 123, 121, 119, 120, 122, 123, 127,
-
21, 22, 24, 114, 129, 124, 130, 131,
-
133, 132, 135, 136, 15, 138, 135, 136,
-
15, 138, 137, 12, 14, 134, 139, 141,
-
151, 150, 142, 144, 143, 145, 146, 148,
-
149, 145, 146, 148, 149, 147, 145, 146,
-
148, 149, 152, 154, 155, 157, 175, 158,
-
154, 155, 157, 158, 156, 159, 166, 168,
-
170, 173, 154, 155, 157, 158, 160, 161,
-
162, 163, 164, 165, 167, 169, 171, 172,
-
174, 176, 181, 182, 180, 176, 177, 179,
-
180, 178, 181, 182, 183, 185, 195, 196,
-
198, 194, 185, 186, 188, 194, 187, 189,
-
190, 192, 193, 20, 189, 190, 192, 193,
-
20, 191, 189, 190, 192, 193, 20, 195,
-
196, 198, 197, 198, 199, 201, 202, 20,
-
200, 198, 199, 201, 202, 20, 2, 3,
-
5, 184, 203, 204, 204, 206, 207, 206,
-
213, 209, 206, 207, 206, 213, 209, 208,
-
72, 212, 211, 72, 212
-
]
-
-
1
class << self
-
1
attr_accessor :_envelope_from_trans_actions
-
1
private :_envelope_from_trans_actions, :_envelope_from_trans_actions=
-
end
-
1
self._envelope_from_trans_actions = [
-
1, 0, 1, 1, 1, 2, 1, 0,
-
0, 3, 3, 4, 3, 0, 5, 5,
-
6, 0, 0, 7, 0, 8, 0, 0,
-
9, 4, 0, 10, 0, 11, 11, 11,
-
11, 11, 12, 12, 13, 14, 12, 15,
-
0, 0, 0, 4, 0, 0, 0, 16,
-
4, 0, 0, 12, 12, 17, 14, 12,
-
18, 18, 18, 18, 19, 18, 18, 0,
-
0, 0, 0, 4, 0, 0, 0, 0,
-
0, 20, 4, 0, 0, 0, 12, 12,
-
21, 14, 12, 0, 0, 4, 0, 12,
-
12, 14, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 4, 0, 0, 12, 12, 14,
-
12, 0, 0, 0, 4, 0, 0, 4,
-
0, 0, 12, 12, 14, 12, 0, 0,
-
4, 0, 0, 12, 12, 14, 12, 0,
-
4, 0, 0, 12, 14, 12, 0, 0,
-
0, 4, 0, 0, 0, 4, 0, 0,
-
12, 12, 14, 12, 12, 0, 0, 0,
-
0, 4, 0, 0, 12, 14, 12, 0,
-
0, 0, 4, 0, 0, 4, 0, 12,
-
12, 14, 12, 12, 14, 12, 12, 14,
-
12, 12, 14, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
20, 4, 0, 12, 12, 21, 14, 0,
-
12, 12, 12, 12, 14, 12, 12, 0,
-
0, 0, 22, 22, 23, 22, 0, 0,
-
24, 0, 0, 12, 12, 12, 14, 0,
-
0, 0, 0, 0, 0, 5, 5, 6,
-
7, 0, 0, 9, 4, 0, 12, 12,
-
13, 14, 0, 5, 5, 6, 0, 7,
-
0, 0, 9, 4, 0, 11, 11, 11,
-
11, 11, 12, 12, 13, 14, 0, 0,
-
16, 11, 11, 11, 0, 0, 0, 0,
-
0, 22, 22, 22, 22, 0, 0, 24,
-
0, 0, 0, 0, 0, 22, 22, 22,
-
23, 22, 0, 0, 24, 0, 0, 5,
-
5, 25, 7, 26, 0, 0, 27, 4,
-
28, 0, 12, 12, 29, 14, 30, 0,
-
0, 24, 0, 0, 0, 31, 4, 32,
-
0, 12, 12, 33, 14, 34, 12, 12,
-
35, 35, 14, 35, 0, 36, 36, 37,
-
38, 36, 0, 0, 4, 39, 0, 0,
-
0, 4, 12, 12, 14
-
]
-
-
1
class << self
-
1
attr_accessor :_envelope_from_eof_actions
-
1
private :_envelope_from_eof_actions, :_envelope_from_eof_actions=
-
end
-
1
self._envelope_from_eof_actions = [
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 40, 40, 41, 0
-
]
-
-
1
class << self
-
1
attr_accessor :envelope_from_start
-
end
-
1
self.envelope_from_start = 1;
-
1
class << self
-
1
attr_accessor :envelope_from_first_final
-
end
-
1
self.envelope_from_first_final = 210;
-
1
class << self
-
1
attr_accessor :envelope_from_error
-
end
-
1
self.envelope_from_error = 0;
-
-
1
class << self
-
1
attr_accessor :envelope_from_en_comment_tail
-
end
-
1
self.envelope_from_en_comment_tail = 205;
-
1
class << self
-
1
attr_accessor :envelope_from_en_main
-
end
-
1
self.envelope_from_en_main = 1;
-
-
-
# line 17 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb.rl"
-
-
1
def self.parse(data)
-
p = 0
-
eof = data.length
-
stack = []
-
-
actions = []
-
data_unpacked = data.bytes.to_a
-
-
# line 1705 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb"
-
begin
-
p ||= 0
-
pe ||= data.length
-
cs = envelope_from_start
-
top = 0
-
end
-
-
# line 26 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb.rl"
-
-
# line 1715 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb"
-
begin
-
testEof = false
-
_slen, _trans, _keys, _inds, _acts, _nacts = nil
-
_goto_level = 0
-
_resume = 10
-
_eof_trans = 15
-
_again = 20
-
_test_eof = 30
-
_out = 40
-
while true
-
if _goto_level <= 0
-
if p == pe
-
_goto_level = _test_eof
-
next
-
end
-
if cs == 0
-
_goto_level = _out
-
next
-
end
-
end
-
if _goto_level <= _resume
-
_keys = cs << 1
-
_inds = _envelope_from_index_offsets[cs]
-
_slen = _envelope_from_key_spans[cs]
-
_trans = if ( _slen > 0 &&
-
_envelope_from_trans_keys[_keys] <= ( data_unpacked[p]) &&
-
( data_unpacked[p]) <= _envelope_from_trans_keys[_keys + 1]
-
) then
-
_envelope_from_indicies[ _inds + ( data_unpacked[p]) - _envelope_from_trans_keys[_keys] ]
-
else
-
_envelope_from_indicies[ _inds + _slen ]
-
end
-
cs = _envelope_from_trans_targs[_trans]
-
if _envelope_from_trans_actions[_trans] != 0
-
case _envelope_from_trans_actions[_trans]
-
when 16 then
-
# line 4 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(1, p) end
-
when 12 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
when 36 then
-
# line 8 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(5, p) end
-
when 11 then
-
# line 10 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(7, p) end
-
when 18 then
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 10 then
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
when 5 then
-
# line 22 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(19, p) end
-
when 3 then
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
when 32 then
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
when 24 then
-
# line 41 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(38, p) end
-
when 22 then
-
# line 42 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(39, p) end
-
when 4 then
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 205
-
_goto_level = _again
-
next
-
end
-
end
-
when 39 then
-
# line 6 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
top -= 1
-
cs = stack[top]
-
_goto_level = _again
-
next
-
end
-
end
-
when 1 then
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(2, p) end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
when 17 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 4 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(1, p) end
-
when 15 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
when 35 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
when 34 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
when 14 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 205
-
_goto_level = _again
-
next
-
end
-
end
-
when 37 then
-
# line 8 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(5, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 205
-
_goto_level = _again
-
next
-
end
-
end
-
when 38 then
-
# line 8 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(5, p) end
-
# line 6 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
top -= 1
-
cs = stack[top]
-
_goto_level = _again
-
next
-
end
-
end
-
when 20 then
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 4 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(1, p) end
-
when 19 then
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 205
-
_goto_level = _again
-
next
-
end
-
end
-
when 9 then
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 4 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(1, p) end
-
when 28 then
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
when 8 then
-
# line 22 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(19, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
when 7 then
-
# line 22 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(19, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 205
-
_goto_level = _again
-
next
-
end
-
end
-
when 31 then
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
# line 4 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(1, p) end
-
when 23 then
-
# line 42 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(39, p) end
-
# line 41 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(38, p) end
-
when 2 then
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(2, p) end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 205
-
_goto_level = _again
-
next
-
end
-
end
-
when 21 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 4 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(1, p) end
-
when 13 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 4 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(1, p) end
-
when 30 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
when 33 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
# line 4 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(1, p) end
-
when 27 then
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
# line 4 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(1, p) end
-
when 6 then
-
# line 22 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(19, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 4 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(1, p) end
-
when 26 then
-
# line 22 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(19, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
when 29 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
# line 4 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(1, p) end
-
when 25 then
-
# line 22 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(19, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
# line 4 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(1, p) end
-
# line 2075 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb"
-
end
-
end
-
end
-
if _goto_level <= _again
-
if cs == 0
-
_goto_level = _out
-
next
-
end
-
p += 1
-
if p != pe
-
_goto_level = _resume
-
next
-
end
-
end
-
if _goto_level <= _test_eof
-
if p == eof
-
case _envelope_from_eof_actions[cs]
-
when 40 then
-
# line 9 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(6, p) end
-
when 41 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 9 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(6, p) end
-
# line 2104 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb"
-
end
-
end
-
-
end
-
if _goto_level <= _out
-
break
-
end
-
end
-
end
-
-
# line 27 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb.rl"
-
-
if p == eof && cs >=
-
# line 2118 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb"
-
210
-
# line 28 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb.rl"
-
-
return actions, nil
-
else
-
return [], "Only able to parse up to #{data[0..p]}"
-
end
-
end
-
end
-
end
-
end
-
end
-
-
# line 1 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb.rl"
-
-
# line 10 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb.rl"
-
-
-
1
module Mail
-
1
module Parsers
-
1
module Ragel
-
1
module MessageIdsMachine
-
-
# line 13 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb"
-
1
class << self
-
1
attr_accessor :_message_ids_trans_keys
-
1
private :_message_ids_trans_keys, :_message_ids_trans_keys=
-
end
-
1
self._message_ids_trans_keys = [
-
0, 0, 9, 60, 10, 10,
-
9, 32, 9, 60, 9,
-
126, 9, 126, 10, 10,
-
9, 32, 9, 126, 9, 64,
-
10, 10, 9, 32, 9,
-
64, 9, 126, 10, 10,
-
9, 32, 9, 126, 9, 64,
-
10, 10, 9, 32, 9,
-
64, 9, 126, 9, 126,
-
10, 10, 9, 32, 9, 126,
-
9, 62, 10, 10, 9,
-
32, 9, 62, 9, 126,
-
10, 10, 9, 32, 9, 126,
-
10, 10, 9, 32, 9,
-
126, 1, 127, 1, 127,
-
10, 10, 9, 32, 9, 126,
-
9, 62, 10, 10, 9,
-
32, 9, 62, 33, 126,
-
-128, -1, 10, 10, 9, 32,
-
9, 126, 9, 126, 1,
-
127, 10, 10, 9, 32,
-
0, 127, 9, 126, 9, 126,
-
33, 126, 33, 126, 1,
-
127, 1, 127, 10, 10,
-
9, 32, -128, -1, 9, 126,
-
1, 127, 1, 127, 10,
-
10, 9, 32, 9, 126,
-
9, 64, 10, 10, 9, 32,
-
9, 64, -128, -1, 10,
-
10, 9, 32, 9, 126,
-
9, 126, 10, 10, 9, 32,
-
9, 126, 1, 127, 1,
-
127, 10, 10, 9, 32,
-
9, 126, -128, -1, 1, 127,
-
10, 10, 9, 32, 1,
-
127, 1, 127, 10, 10,
-
9, 32, 9, 126, 9, 64,
-
10, 10, 9, 32, 9,
-
64, -128, -1, 1, 127,
-
10, 10, 9, 32, 9, 64,
-
10, 10, 9, 32, 9,
-
64, 9, 126, 33, 126,
-
1, 127, 1, 127, 10, 10,
-
9, 32, -128, -1, 9,
-
60, 9, 60, 9, 60,
-
0, 0, 0
-
]
-
-
1
class << self
-
1
attr_accessor :_message_ids_key_spans
-
1
private :_message_ids_key_spans, :_message_ids_key_spans=
-
end
-
1
self._message_ids_key_spans = [
-
0, 52, 1, 24, 52, 118, 118, 1,
-
24, 118, 56, 1, 24, 56, 118, 1,
-
24, 118, 56, 1, 24, 56, 118, 118,
-
1, 24, 118, 54, 1, 24, 54, 118,
-
1, 24, 118, 1, 24, 118, 127, 127,
-
1, 24, 118, 54, 1, 24, 54, 94,
-
128, 1, 24, 118, 118, 127, 1, 24,
-
128, 118, 118, 94, 94, 127, 127, 1,
-
24, 128, 118, 127, 127, 1, 24, 118,
-
56, 1, 24, 56, 128, 1, 24, 118,
-
118, 1, 24, 118, 127, 127, 1, 24,
-
118, 128, 127, 1, 24, 127, 127, 1,
-
24, 118, 56, 1, 24, 56, 128, 127,
-
1, 24, 56, 1, 24, 56, 118, 94,
-
127, 127, 1, 24, 128, 52, 52, 52,
-
0
-
]
-
-
1
class << self
-
1
attr_accessor :_message_ids_index_offsets
-
1
private :_message_ids_index_offsets, :_message_ids_index_offsets=
-
end
-
1
self._message_ids_index_offsets = [
-
0, 0, 53, 55, 80, 133, 252, 371,
-
373, 398, 517, 574, 576, 601, 658, 777,
-
779, 804, 923, 980, 982, 1007, 1064, 1183,
-
1302, 1304, 1329, 1448, 1503, 1505, 1530, 1585,
-
1704, 1706, 1731, 1850, 1852, 1877, 1996, 2124,
-
2252, 2254, 2279, 2398, 2453, 2455, 2480, 2535,
-
2630, 2759, 2761, 2786, 2905, 3024, 3152, 3154,
-
3179, 3308, 3427, 3546, 3641, 3736, 3864, 3992,
-
3994, 4019, 4148, 4267, 4395, 4523, 4525, 4550,
-
4669, 4726, 4728, 4753, 4810, 4939, 4941, 4966,
-
5085, 5204, 5206, 5231, 5350, 5478, 5606, 5608,
-
5633, 5752, 5881, 6009, 6011, 6036, 6164, 6292,
-
6294, 6319, 6438, 6495, 6497, 6522, 6579, 6708,
-
6836, 6838, 6863, 6920, 6922, 6947, 7004, 7123,
-
7218, 7346, 7474, 7476, 7501, 7630, 7683, 7736,
-
7789
-
]
-
-
1
class << self
-
1
attr_accessor :_message_ids_indicies
-
1
private :_message_ids_indicies, :_message_ids_indicies=
-
end
-
1
self._message_ids_indicies = [
-
0, 1, 1, 1, 2, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 0,
-
1, 1, 1, 1, 1, 1, 1, 3,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 4, 1, 5, 1, 0,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 0, 1,
-
6, 1, 1, 1, 7, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 6,
-
1, 1, 1, 1, 1, 1, 1, 8,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 9, 1, 10, 1, 1,
-
1, 11, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 10, 12, 13, 12,
-
12, 12, 12, 12, 14, 1, 12, 12,
-
1, 12, 15, 12, 12, 12, 12, 12,
-
12, 12, 12, 12, 12, 12, 1, 1,
-
1, 12, 1, 12, 1, 12, 12, 12,
-
12, 12, 12, 12, 12, 12, 12, 12,
-
12, 12, 12, 12, 12, 12, 12, 12,
-
12, 12, 12, 12, 12, 12, 12, 1,
-
1, 1, 12, 12, 12, 12, 12, 12,
-
12, 12, 12, 12, 12, 12, 12, 12,
-
12, 12, 12, 12, 12, 12, 12, 12,
-
12, 12, 12, 12, 12, 12, 12, 12,
-
12, 12, 12, 1, 16, 1, 1, 1,
-
17, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 16, 12, 13, 12, 12,
-
12, 12, 12, 18, 1, 12, 12, 1,
-
12, 15, 12, 12, 12, 12, 12, 12,
-
12, 12, 12, 12, 12, 1, 1, 1,
-
12, 1, 12, 1, 12, 12, 12, 12,
-
12, 12, 12, 12, 12, 12, 12, 12,
-
12, 12, 12, 12, 12, 12, 12, 12,
-
12, 12, 12, 12, 12, 12, 1, 1,
-
1, 12, 12, 12, 12, 12, 12, 12,
-
12, 12, 12, 12, 12, 12, 12, 12,
-
12, 12, 12, 12, 12, 12, 12, 12,
-
12, 12, 12, 12, 12, 12, 12, 12,
-
12, 12, 1, 19, 1, 16, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 16, 1, 20, 1,
-
1, 1, 21, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 20, 22, 23,
-
22, 22, 22, 22, 22, 24, 1, 22,
-
22, 1, 22, 25, 22, 22, 22, 22,
-
22, 22, 22, 22, 22, 22, 22, 1,
-
1, 1, 22, 1, 22, 26, 22, 22,
-
22, 22, 22, 22, 22, 22, 22, 22,
-
22, 22, 22, 22, 22, 22, 22, 22,
-
22, 22, 22, 22, 22, 22, 22, 22,
-
1, 1, 1, 22, 22, 22, 22, 22,
-
22, 22, 22, 22, 22, 22, 22, 22,
-
22, 22, 22, 22, 22, 22, 22, 22,
-
22, 22, 22, 22, 22, 22, 22, 22,
-
22, 22, 22, 22, 1, 27, 1, 1,
-
1, 28, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 27, 1, 1, 1,
-
1, 1, 1, 1, 29, 1, 1, 1,
-
1, 1, 30, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 31, 1, 32, 1,
-
27, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 27,
-
1, 33, 1, 1, 1, 34, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
33, 1, 1, 1, 1, 1, 1, 1,
-
35, 1, 1, 1, 1, 1, 36, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
37, 1, 30, 1, 1, 1, 38, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 30, 39, 40, 39, 39, 39, 39,
-
39, 41, 1, 39, 39, 1, 39, 1,
-
39, 39, 39, 39, 39, 39, 39, 39,
-
39, 39, 39, 1, 1, 1, 39, 1,
-
39, 1, 39, 39, 39, 39, 39, 39,
-
39, 39, 39, 39, 39, 39, 39, 39,
-
39, 39, 39, 39, 39, 39, 39, 39,
-
39, 39, 39, 39, 1, 1, 1, 39,
-
39, 39, 39, 39, 39, 39, 39, 39,
-
39, 39, 39, 39, 39, 39, 39, 39,
-
39, 39, 39, 39, 39, 39, 39, 39,
-
39, 39, 39, 39, 39, 39, 39, 39,
-
1, 42, 1, 30, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 30, 1, 43, 1, 1, 1,
-
44, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 43, 39, 1, 39, 39,
-
39, 39, 39, 45, 1, 39, 39, 1,
-
39, 30, 39, 39, 39, 39, 39, 39,
-
39, 39, 39, 39, 39, 1, 1, 1,
-
39, 1, 39, 46, 39, 39, 39, 39,
-
39, 39, 39, 39, 39, 39, 39, 39,
-
39, 39, 39, 39, 39, 39, 39, 39,
-
39, 39, 39, 39, 39, 39, 1, 1,
-
1, 39, 39, 39, 39, 39, 39, 39,
-
39, 39, 39, 39, 39, 39, 39, 39,
-
39, 39, 39, 39, 39, 39, 39, 39,
-
39, 39, 39, 39, 39, 39, 39, 39,
-
39, 39, 1, 43, 1, 1, 1, 44,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 43, 1, 1, 1, 1, 1,
-
1, 1, 45, 1, 1, 1, 1, 1,
-
30, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 46, 1, 47, 1, 43, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 43, 1, 48,
-
1, 1, 1, 49, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 48, 1,
-
1, 1, 1, 1, 1, 1, 50, 1,
-
1, 1, 1, 1, 36, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 51, 1,
-
52, 1, 1, 1, 53, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 52,
-
54, 55, 54, 54, 54, 54, 54, 56,
-
1, 54, 54, 1, 54, 57, 54, 54,
-
54, 54, 54, 54, 54, 54, 54, 54,
-
54, 1, 1, 1, 54, 1, 54, 58,
-
54, 54, 54, 54, 54, 54, 54, 54,
-
54, 54, 54, 54, 54, 54, 54, 54,
-
54, 54, 54, 54, 54, 54, 54, 54,
-
54, 54, 59, 1, 1, 54, 54, 54,
-
54, 54, 54, 54, 54, 54, 54, 54,
-
54, 54, 54, 54, 54, 54, 54, 54,
-
54, 54, 54, 54, 54, 54, 54, 54,
-
54, 54, 54, 54, 54, 54, 1, 60,
-
1, 1, 1, 61, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 60, 62,
-
63, 62, 62, 62, 62, 62, 64, 1,
-
62, 62, 1, 62, 65, 62, 62, 62,
-
62, 62, 62, 62, 62, 62, 62, 62,
-
1, 1, 1, 62, 1, 62, 1, 62,
-
62, 62, 62, 62, 62, 62, 62, 62,
-
62, 62, 62, 62, 62, 62, 62, 62,
-
62, 62, 62, 62, 62, 62, 62, 62,
-
62, 66, 1, 1, 62, 62, 62, 62,
-
62, 62, 62, 62, 62, 62, 62, 62,
-
62, 62, 62, 62, 62, 62, 62, 62,
-
62, 62, 62, 62, 62, 62, 62, 62,
-
62, 62, 62, 62, 62, 1, 67, 1,
-
60, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 60,
-
1, 68, 1, 1, 1, 69, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
68, 62, 63, 62, 62, 62, 62, 62,
-
70, 1, 62, 62, 1, 62, 71, 62,
-
62, 62, 62, 62, 62, 62, 62, 62,
-
62, 62, 1, 1, 1, 62, 72, 62,
-
1, 62, 62, 62, 62, 62, 62, 62,
-
62, 62, 62, 62, 62, 62, 62, 62,
-
62, 62, 62, 62, 62, 62, 62, 62,
-
62, 62, 62, 1, 1, 1, 62, 62,
-
62, 62, 62, 62, 62, 62, 62, 62,
-
62, 62, 62, 62, 62, 62, 62, 62,
-
62, 62, 62, 62, 62, 62, 62, 62,
-
62, 62, 62, 62, 62, 62, 62, 1,
-
68, 1, 1, 1, 69, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 68,
-
1, 1, 1, 1, 1, 1, 1, 70,
-
1, 1, 1, 1, 1, 73, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 72, 1, 74,
-
1, 68, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
68, 1, 75, 1, 1, 1, 76, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 75, 1, 1, 1, 1, 1, 1,
-
1, 77, 1, 1, 1, 1, 1, 78,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 79,
-
1, 73, 1, 1, 1, 80, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
73, 81, 1, 81, 81, 81, 81, 81,
-
82, 1, 81, 81, 1, 81, 1, 81,
-
81, 81, 81, 81, 81, 81, 81, 81,
-
81, 81, 1, 1, 1, 81, 1, 81,
-
1, 81, 81, 81, 81, 81, 81, 81,
-
81, 81, 81, 81, 81, 81, 81, 81,
-
81, 81, 81, 81, 81, 81, 81, 81,
-
81, 81, 81, 1, 1, 1, 81, 81,
-
81, 81, 81, 81, 81, 81, 81, 81,
-
81, 81, 81, 81, 81, 81, 81, 81,
-
81, 81, 81, 81, 81, 81, 81, 81,
-
81, 81, 81, 81, 81, 81, 81, 1,
-
83, 1, 73, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 73, 1, 68, 1, 1, 1, 69,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 68, 81, 1, 81, 81, 81,
-
81, 81, 70, 1, 81, 81, 1, 81,
-
73, 81, 81, 81, 81, 81, 81, 81,
-
81, 81, 81, 81, 1, 1, 1, 81,
-
72, 81, 1, 81, 81, 81, 81, 81,
-
81, 81, 81, 81, 81, 81, 81, 81,
-
81, 81, 81, 81, 81, 81, 81, 81,
-
81, 81, 81, 81, 81, 1, 1, 1,
-
81, 81, 81, 81, 81, 81, 81, 81,
-
81, 81, 81, 81, 81, 81, 81, 81,
-
81, 81, 81, 81, 81, 81, 81, 81,
-
81, 81, 81, 81, 81, 81, 81, 81,
-
81, 1, 84, 1, 85, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 85, 1, 78, 1, 1,
-
1, 86, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 78, 87, 1, 87,
-
87, 87, 87, 87, 88, 1, 87, 87,
-
1, 87, 1, 87, 87, 87, 87, 87,
-
87, 87, 87, 87, 87, 87, 1, 1,
-
1, 87, 1, 87, 1, 87, 87, 87,
-
87, 87, 87, 87, 87, 87, 87, 87,
-
87, 87, 87, 87, 87, 87, 87, 87,
-
87, 87, 87, 87, 87, 87, 87, 1,
-
1, 1, 87, 87, 87, 87, 87, 87,
-
87, 87, 87, 87, 87, 87, 87, 87,
-
87, 87, 87, 87, 87, 87, 87, 87,
-
87, 87, 87, 87, 87, 87, 87, 87,
-
87, 87, 87, 1, 89, 89, 89, 89,
-
89, 89, 89, 89, 63, 1, 89, 89,
-
90, 89, 89, 89, 89, 89, 89, 89,
-
89, 89, 89, 89, 89, 89, 89, 89,
-
89, 89, 89, 63, 89, 1, 89, 89,
-
89, 89, 89, 89, 89, 89, 89, 89,
-
89, 89, 89, 89, 89, 89, 89, 89,
-
89, 89, 89, 89, 89, 89, 89, 89,
-
89, 89, 89, 89, 89, 89, 89, 89,
-
89, 89, 89, 89, 89, 89, 89, 89,
-
89, 89, 89, 89, 89, 89, 89, 89,
-
89, 89, 89, 89, 89, 89, 89, 91,
-
89, 89, 89, 89, 89, 89, 89, 89,
-
89, 89, 89, 89, 89, 89, 89, 89,
-
89, 89, 89, 89, 89, 89, 89, 89,
-
89, 89, 89, 89, 89, 89, 89, 89,
-
89, 89, 89, 1, 89, 89, 89, 89,
-
89, 89, 89, 89, 89, 1, 89, 89,
-
92, 89, 89, 89, 89, 89, 89, 89,
-
89, 89, 89, 89, 89, 89, 89, 89,
-
89, 89, 89, 89, 89, 93, 89, 89,
-
89, 89, 89, 89, 89, 89, 89, 89,
-
89, 89, 89, 89, 89, 89, 89, 89,
-
89, 89, 89, 89, 89, 89, 89, 89,
-
89, 89, 89, 89, 89, 89, 89, 89,
-
89, 89, 89, 89, 89, 89, 89, 89,
-
89, 89, 89, 89, 89, 89, 89, 89,
-
89, 89, 89, 89, 89, 89, 89, 91,
-
89, 89, 89, 89, 89, 89, 89, 89,
-
89, 89, 89, 89, 89, 89, 89, 89,
-
89, 89, 89, 89, 89, 89, 89, 89,
-
89, 89, 89, 89, 89, 89, 89, 89,
-
89, 89, 89, 1, 94, 1, 89, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 89, 1, 95,
-
1, 1, 1, 96, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 95, 93,
-
63, 93, 93, 93, 93, 93, 97, 1,
-
93, 93, 1, 93, 65, 93, 93, 93,
-
93, 93, 93, 93, 93, 93, 93, 93,
-
1, 1, 1, 93, 72, 93, 1, 93,
-
93, 93, 93, 93, 93, 93, 93, 93,
-
93, 93, 93, 93, 93, 93, 93, 93,
-
93, 93, 93, 93, 93, 93, 93, 93,
-
93, 1, 1, 1, 93, 93, 93, 93,
-
93, 93, 93, 93, 93, 93, 93, 93,
-
93, 93, 93, 93, 93, 93, 93, 93,
-
93, 93, 93, 93, 93, 93, 93, 93,
-
93, 93, 93, 93, 93, 1, 95, 1,
-
1, 1, 96, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 95, 1, 1,
-
1, 1, 1, 1, 1, 97, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 72, 1, 98, 1, 95,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 95, 1,
-
99, 1, 1, 1, 100, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 99,
-
1, 1, 1, 1, 1, 1, 1, 101,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 79, 1, 93,
-
63, 93, 93, 93, 93, 93, 1, 1,
-
93, 93, 1, 93, 65, 93, 93, 93,
-
93, 93, 93, 93, 93, 93, 93, 93,
-
1, 1, 1, 93, 1, 93, 1, 93,
-
93, 93, 93, 93, 93, 93, 93, 93,
-
93, 93, 93, 93, 93, 93, 93, 93,
-
93, 93, 93, 93, 93, 93, 93, 93,
-
93, 1, 1, 1, 93, 93, 93, 93,
-
93, 93, 93, 93, 93, 93, 93, 93,
-
93, 93, 93, 93, 93, 93, 93, 93,
-
93, 93, 93, 93, 93, 93, 93, 93,
-
93, 93, 93, 93, 93, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 89, 102,
-
1, 63, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
63, 1, 73, 1, 1, 1, 80, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 73, 62, 63, 62, 62, 62, 62,
-
62, 82, 1, 62, 62, 1, 62, 65,
-
62, 62, 62, 62, 62, 62, 62, 62,
-
62, 62, 62, 1, 1, 1, 62, 1,
-
62, 1, 62, 62, 62, 62, 62, 62,
-
62, 62, 62, 62, 62, 62, 62, 62,
-
62, 62, 62, 62, 62, 62, 62, 62,
-
62, 62, 62, 62, 1, 1, 1, 62,
-
62, 62, 62, 62, 62, 62, 62, 62,
-
62, 62, 62, 62, 62, 62, 62, 62,
-
62, 62, 62, 62, 62, 62, 62, 62,
-
62, 62, 62, 62, 62, 62, 62, 62,
-
1, 103, 1, 1, 1, 104, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
103, 105, 106, 105, 105, 105, 105, 105,
-
107, 1, 105, 105, 1, 105, 108, 105,
-
105, 105, 105, 105, 105, 105, 105, 105,
-
105, 105, 1, 1, 1, 105, 1, 105,
-
1, 105, 105, 105, 105, 105, 105, 105,
-
105, 105, 105, 105, 105, 105, 105, 105,
-
105, 105, 105, 105, 105, 105, 105, 105,
-
105, 105, 105, 109, 1, 1, 105, 105,
-
105, 105, 105, 105, 105, 105, 105, 105,
-
105, 105, 105, 105, 105, 105, 105, 105,
-
105, 105, 105, 105, 105, 105, 105, 105,
-
105, 105, 105, 105, 105, 105, 105, 1,
-
66, 66, 66, 66, 66, 66, 66, 66,
-
66, 1, 66, 66, 110, 66, 66, 66,
-
66, 66, 66, 66, 66, 66, 66, 66,
-
66, 66, 66, 66, 66, 66, 66, 66,
-
66, 66, 66, 66, 66, 66, 66, 66,
-
66, 66, 66, 66, 66, 66, 66, 66,
-
66, 66, 66, 66, 66, 66, 66, 66,
-
66, 66, 66, 66, 66, 66, 66, 66,
-
66, 66, 66, 66, 66, 66, 66, 66,
-
66, 66, 66, 66, 66, 66, 66, 66,
-
66, 66, 66, 66, 66, 66, 66, 66,
-
66, 66, 1, 111, 95, 66, 66, 66,
-
66, 66, 66, 66, 66, 66, 66, 66,
-
66, 66, 66, 66, 66, 66, 66, 66,
-
66, 66, 66, 66, 66, 66, 66, 66,
-
66, 66, 66, 66, 66, 66, 66, 1,
-
112, 1, 66, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 66, 1, 66, 66, 66, 66, 66,
-
66, 66, 66, 66, 66, 66, 66, 66,
-
66, 66, 66, 66, 66, 66, 66, 66,
-
66, 66, 66, 66, 66, 66, 66, 66,
-
66, 66, 66, 66, 66, 66, 66, 66,
-
66, 66, 66, 66, 66, 66, 66, 66,
-
66, 66, 66, 66, 66, 66, 66, 66,
-
66, 66, 66, 66, 66, 66, 66, 66,
-
66, 66, 66, 66, 66, 66, 66, 66,
-
66, 66, 66, 66, 66, 66, 66, 66,
-
66, 66, 66, 66, 66, 66, 66, 66,
-
66, 66, 66, 66, 66, 66, 66, 66,
-
66, 66, 66, 66, 66, 66, 66, 66,
-
66, 66, 66, 66, 66, 66, 66, 66,
-
66, 66, 66, 66, 66, 66, 66, 66,
-
66, 66, 66, 66, 66, 66, 66, 66,
-
66, 66, 66, 1, 68, 1, 1, 1,
-
69, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 68, 113, 63, 113, 113,
-
113, 113, 113, 70, 1, 113, 113, 1,
-
113, 114, 113, 113, 113, 113, 113, 113,
-
113, 113, 113, 113, 113, 1, 1, 1,
-
113, 72, 113, 58, 113, 113, 113, 113,
-
113, 113, 113, 113, 113, 113, 113, 113,
-
113, 113, 113, 113, 113, 113, 113, 113,
-
113, 113, 113, 113, 113, 113, 1, 1,
-
1, 113, 113, 113, 113, 113, 113, 113,
-
113, 113, 113, 113, 113, 113, 113, 113,
-
113, 113, 113, 113, 113, 113, 113, 113,
-
113, 113, 113, 113, 113, 113, 113, 113,
-
113, 113, 1, 73, 1, 1, 1, 80,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 73, 113, 63, 113, 113, 113,
-
113, 113, 82, 1, 113, 113, 1, 113,
-
65, 113, 113, 113, 113, 113, 113, 113,
-
113, 113, 113, 113, 1, 1, 1, 113,
-
115, 113, 58, 113, 113, 113, 113, 113,
-
113, 113, 113, 113, 113, 113, 113, 113,
-
113, 113, 113, 113, 113, 113, 113, 113,
-
113, 113, 113, 113, 113, 1, 1, 1,
-
113, 113, 113, 113, 113, 113, 113, 113,
-
113, 113, 113, 113, 113, 113, 113, 113,
-
113, 113, 113, 113, 113, 113, 113, 113,
-
113, 113, 113, 113, 113, 113, 113, 113,
-
113, 1, 58, 1, 58, 58, 58, 58,
-
58, 1, 1, 58, 58, 1, 58, 116,
-
58, 58, 58, 58, 58, 58, 58, 58,
-
58, 58, 58, 1, 1, 1, 58, 115,
-
58, 58, 58, 58, 58, 58, 58, 58,
-
58, 58, 58, 58, 58, 58, 58, 58,
-
58, 58, 58, 58, 58, 58, 58, 58,
-
58, 58, 58, 58, 1, 1, 1, 58,
-
58, 58, 58, 58, 58, 58, 58, 58,
-
58, 58, 58, 58, 58, 58, 58, 58,
-
58, 58, 58, 58, 58, 58, 58, 58,
-
58, 58, 58, 58, 58, 58, 58, 58,
-
1, 58, 1, 58, 58, 58, 58, 58,
-
1, 1, 58, 58, 1, 58, 1, 58,
-
58, 58, 58, 58, 58, 58, 58, 58,
-
58, 58, 1, 1, 1, 58, 115, 58,
-
58, 58, 58, 58, 58, 58, 58, 58,
-
58, 58, 58, 58, 58, 58, 58, 58,
-
58, 58, 58, 58, 58, 58, 58, 58,
-
58, 58, 58, 1, 1, 1, 58, 58,
-
58, 58, 58, 58, 58, 58, 58, 58,
-
58, 58, 58, 58, 58, 58, 58, 58,
-
58, 58, 58, 58, 58, 58, 58, 58,
-
58, 58, 58, 58, 58, 58, 58, 1,
-
117, 117, 117, 117, 117, 117, 117, 117,
-
117, 1, 117, 117, 118, 117, 117, 117,
-
117, 117, 117, 117, 117, 117, 117, 117,
-
117, 117, 117, 117, 117, 117, 117, 117,
-
117, 119, 117, 117, 117, 117, 117, 117,
-
117, 117, 117, 117, 117, 117, 117, 117,
-
117, 117, 117, 117, 117, 117, 117, 117,
-
117, 117, 117, 117, 117, 117, 117, 117,
-
117, 117, 117, 117, 117, 117, 117, 117,
-
117, 117, 117, 117, 117, 117, 117, 117,
-
117, 117, 117, 117, 117, 117, 117, 117,
-
117, 117, 117, 120, 117, 117, 117, 117,
-
117, 117, 117, 117, 117, 117, 117, 117,
-
117, 117, 117, 117, 117, 117, 117, 117,
-
117, 117, 117, 117, 117, 117, 117, 117,
-
117, 117, 117, 117, 117, 117, 117, 1,
-
121, 121, 121, 121, 121, 121, 121, 121,
-
121, 1, 121, 121, 122, 121, 121, 121,
-
121, 121, 121, 121, 121, 121, 121, 121,
-
121, 121, 121, 121, 121, 121, 121, 121,
-
121, 123, 121, 121, 121, 121, 121, 121,
-
121, 121, 121, 121, 121, 121, 121, 121,
-
121, 121, 121, 121, 121, 121, 121, 121,
-
121, 121, 121, 121, 121, 121, 121, 121,
-
121, 121, 121, 121, 121, 121, 121, 121,
-
121, 121, 121, 121, 121, 121, 121, 121,
-
121, 121, 121, 121, 121, 121, 121, 121,
-
121, 121, 121, 124, 121, 121, 121, 121,
-
121, 121, 121, 121, 121, 121, 121, 121,
-
121, 121, 121, 121, 121, 121, 121, 121,
-
121, 121, 121, 121, 121, 121, 121, 121,
-
121, 121, 121, 121, 121, 121, 121, 1,
-
125, 1, 121, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 121, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 121, 36, 1, 1, 1,
-
126, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 36, 127, 128, 127, 127,
-
127, 127, 127, 129, 1, 127, 127, 1,
-
127, 1, 127, 127, 127, 127, 127, 127,
-
127, 127, 127, 127, 127, 1, 1, 1,
-
127, 1, 127, 1, 127, 127, 127, 127,
-
127, 127, 127, 127, 127, 127, 127, 127,
-
127, 127, 127, 127, 127, 127, 127, 127,
-
127, 127, 127, 127, 127, 127, 1, 1,
-
1, 127, 127, 127, 127, 127, 127, 127,
-
127, 127, 127, 127, 127, 127, 127, 127,
-
127, 127, 127, 127, 127, 127, 127, 127,
-
127, 127, 127, 127, 127, 127, 127, 127,
-
127, 127, 1, 130, 130, 130, 130, 130,
-
130, 130, 130, 23, 1, 130, 130, 131,
-
130, 130, 130, 130, 130, 130, 130, 130,
-
130, 130, 130, 130, 130, 130, 130, 130,
-
130, 130, 23, 130, 1, 130, 130, 130,
-
130, 130, 130, 130, 130, 130, 130, 130,
-
130, 130, 130, 130, 130, 130, 130, 130,
-
130, 130, 130, 130, 130, 130, 130, 130,
-
130, 130, 130, 130, 130, 130, 130, 130,
-
130, 130, 130, 130, 130, 130, 130, 130,
-
130, 130, 130, 130, 130, 130, 130, 130,
-
130, 130, 130, 130, 130, 130, 132, 130,
-
130, 130, 130, 130, 130, 130, 130, 130,
-
130, 130, 130, 130, 130, 130, 130, 130,
-
130, 130, 130, 130, 130, 130, 130, 130,
-
130, 130, 130, 130, 130, 130, 130, 130,
-
130, 130, 1, 130, 130, 130, 130, 130,
-
130, 130, 130, 130, 1, 130, 130, 133,
-
130, 130, 130, 130, 130, 130, 130, 130,
-
130, 130, 130, 130, 130, 130, 130, 130,
-
130, 130, 130, 130, 134, 130, 130, 130,
-
130, 130, 130, 130, 130, 130, 130, 130,
-
130, 130, 130, 130, 130, 130, 130, 130,
-
130, 130, 130, 130, 130, 130, 130, 130,
-
130, 130, 130, 130, 130, 130, 130, 130,
-
130, 130, 130, 130, 130, 130, 130, 130,
-
130, 130, 130, 130, 130, 130, 130, 130,
-
130, 130, 130, 130, 130, 130, 132, 130,
-
130, 130, 130, 130, 130, 130, 130, 130,
-
130, 130, 130, 130, 130, 130, 130, 130,
-
130, 130, 130, 130, 130, 130, 130, 130,
-
130, 130, 130, 130, 130, 130, 130, 130,
-
130, 130, 1, 135, 1, 130, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 130, 1, 136, 1,
-
1, 1, 137, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 136, 134, 23,
-
134, 134, 134, 134, 134, 138, 1, 134,
-
134, 1, 134, 134, 134, 134, 134, 134,
-
134, 134, 134, 134, 134, 134, 134, 1,
-
1, 1, 134, 1, 134, 26, 134, 134,
-
134, 134, 134, 134, 134, 134, 134, 134,
-
134, 134, 134, 134, 134, 134, 134, 134,
-
134, 134, 134, 134, 134, 134, 134, 134,
-
1, 1, 1, 134, 134, 134, 134, 134,
-
134, 134, 134, 134, 134, 134, 134, 134,
-
134, 134, 134, 134, 134, 134, 134, 134,
-
134, 134, 134, 134, 134, 134, 134, 134,
-
134, 134, 134, 134, 1, 139, 1, 1,
-
1, 140, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 139, 1, 1, 1,
-
1, 1, 1, 1, 141, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 31, 1, 142, 1,
-
139, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 139,
-
1, 143, 1, 1, 1, 144, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
143, 1, 1, 1, 1, 1, 1, 1,
-
145, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
37, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 130, 146, 1, 23, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 23, 1, 147, 1,
-
1, 1, 148, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 147, 22, 149,
-
22, 22, 22, 22, 22, 150, 1, 22,
-
22, 1, 22, 134, 22, 22, 22, 22,
-
22, 22, 22, 22, 22, 22, 22, 1,
-
1, 1, 22, 1, 22, 26, 22, 22,
-
22, 22, 22, 22, 22, 22, 22, 22,
-
22, 22, 22, 22, 22, 22, 22, 22,
-
22, 22, 22, 22, 22, 22, 22, 22,
-
1, 1, 1, 22, 22, 22, 22, 22,
-
22, 22, 22, 22, 22, 22, 22, 22,
-
22, 22, 22, 22, 22, 22, 22, 22,
-
22, 22, 22, 22, 22, 22, 22, 22,
-
22, 22, 22, 22, 1, 151, 1, 1,
-
1, 152, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 151, 39, 40, 39,
-
39, 39, 39, 39, 153, 1, 39, 39,
-
1, 39, 1, 39, 39, 39, 39, 39,
-
39, 39, 39, 39, 39, 39, 1, 1,
-
1, 39, 1, 39, 31, 39, 39, 39,
-
39, 39, 39, 39, 39, 39, 39, 39,
-
39, 39, 39, 39, 39, 39, 39, 39,
-
39, 39, 39, 39, 39, 39, 39, 1,
-
1, 1, 39, 39, 39, 39, 39, 39,
-
39, 39, 39, 39, 39, 39, 39, 39,
-
39, 39, 39, 39, 39, 39, 39, 39,
-
39, 39, 39, 39, 39, 39, 39, 39,
-
39, 39, 39, 1, 154, 1, 151, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 151, 1, 155,
-
1, 1, 1, 156, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 155, 127,
-
128, 127, 127, 127, 127, 127, 157, 1,
-
127, 127, 1, 127, 1, 127, 127, 127,
-
127, 127, 127, 127, 127, 127, 127, 127,
-
1, 1, 1, 127, 1, 127, 37, 127,
-
127, 127, 127, 127, 127, 127, 127, 127,
-
127, 127, 127, 127, 127, 127, 127, 127,
-
127, 127, 127, 127, 127, 127, 127, 127,
-
127, 1, 1, 1, 127, 127, 127, 127,
-
127, 127, 127, 127, 127, 127, 127, 127,
-
127, 127, 127, 127, 127, 127, 127, 127,
-
127, 127, 127, 127, 127, 127, 127, 127,
-
127, 127, 127, 127, 127, 1, 158, 158,
-
158, 158, 158, 158, 158, 158, 159, 1,
-
158, 158, 160, 158, 158, 158, 158, 158,
-
158, 158, 158, 158, 158, 158, 158, 158,
-
158, 158, 158, 158, 158, 159, 158, 119,
-
158, 158, 158, 158, 158, 158, 158, 158,
-
158, 158, 158, 158, 158, 158, 158, 158,
-
158, 158, 158, 158, 158, 158, 158, 158,
-
158, 158, 158, 158, 158, 158, 158, 158,
-
158, 158, 158, 158, 158, 158, 158, 158,
-
158, 158, 158, 158, 158, 158, 158, 158,
-
158, 158, 158, 158, 158, 158, 158, 158,
-
158, 161, 158, 158, 158, 158, 158, 158,
-
158, 158, 158, 158, 158, 158, 158, 158,
-
158, 158, 158, 158, 158, 158, 158, 158,
-
158, 158, 158, 158, 158, 158, 158, 158,
-
158, 158, 158, 158, 158, 1, 162, 162,
-
162, 162, 162, 162, 162, 162, 162, 1,
-
162, 162, 163, 162, 162, 162, 162, 162,
-
162, 162, 162, 162, 162, 162, 162, 162,
-
162, 162, 162, 162, 162, 162, 162, 164,
-
162, 162, 162, 162, 162, 162, 162, 162,
-
162, 162, 162, 162, 162, 162, 162, 162,
-
162, 162, 162, 162, 162, 162, 162, 162,
-
162, 162, 162, 162, 162, 162, 162, 162,
-
162, 162, 162, 162, 162, 162, 162, 162,
-
162, 162, 162, 162, 162, 162, 162, 162,
-
162, 162, 162, 162, 162, 162, 162, 162,
-
162, 165, 162, 162, 162, 162, 162, 162,
-
162, 162, 162, 162, 162, 162, 162, 162,
-
162, 162, 162, 162, 162, 162, 162, 162,
-
162, 162, 162, 162, 162, 162, 162, 162,
-
162, 162, 162, 162, 162, 1, 166, 1,
-
162, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 162,
-
1, 20, 1, 1, 1, 21, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
20, 134, 23, 134, 134, 134, 134, 134,
-
24, 1, 134, 134, 1, 134, 25, 134,
-
134, 134, 134, 134, 134, 134, 134, 134,
-
134, 134, 1, 1, 1, 134, 1, 134,
-
26, 134, 134, 134, 134, 134, 134, 134,
-
134, 134, 134, 134, 134, 134, 134, 134,
-
134, 134, 134, 134, 134, 134, 134, 134,
-
134, 134, 134, 1, 1, 1, 134, 134,
-
134, 134, 134, 134, 134, 134, 134, 134,
-
134, 134, 134, 134, 134, 134, 134, 134,
-
134, 134, 134, 134, 134, 134, 134, 134,
-
134, 134, 134, 134, 134, 134, 134, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
162, 162, 162, 162, 162, 162, 162, 162,
-
162, 167, 1, 162, 162, 168, 162, 162,
-
162, 162, 162, 162, 162, 162, 162, 162,
-
162, 162, 162, 162, 162, 162, 162, 162,
-
167, 162, 123, 162, 162, 162, 162, 162,
-
162, 162, 162, 162, 162, 162, 162, 162,
-
162, 162, 162, 162, 162, 162, 162, 162,
-
162, 162, 162, 162, 162, 162, 162, 162,
-
162, 162, 162, 162, 162, 162, 162, 162,
-
162, 162, 162, 162, 162, 162, 162, 162,
-
162, 162, 162, 162, 162, 162, 162, 162,
-
162, 162, 162, 162, 165, 162, 162, 162,
-
162, 162, 162, 162, 162, 162, 162, 162,
-
162, 162, 162, 162, 162, 162, 162, 162,
-
162, 162, 162, 162, 162, 162, 162, 162,
-
162, 162, 162, 162, 162, 162, 162, 162,
-
1, 169, 1, 167, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 167, 1, 170, 170, 170, 170,
-
170, 170, 170, 170, 171, 1, 170, 170,
-
172, 170, 170, 170, 170, 170, 170, 170,
-
170, 170, 170, 170, 170, 170, 170, 170,
-
170, 170, 170, 171, 170, 173, 170, 170,
-
170, 170, 170, 170, 170, 170, 170, 170,
-
170, 170, 170, 170, 170, 170, 170, 170,
-
170, 170, 170, 170, 170, 170, 170, 170,
-
170, 170, 170, 170, 170, 170, 170, 170,
-
170, 170, 170, 170, 170, 170, 170, 170,
-
170, 170, 170, 170, 170, 170, 170, 170,
-
170, 170, 170, 170, 170, 170, 170, 174,
-
170, 170, 170, 170, 170, 170, 170, 170,
-
170, 170, 170, 170, 170, 170, 170, 170,
-
170, 170, 170, 170, 170, 170, 170, 170,
-
170, 170, 170, 170, 170, 170, 170, 170,
-
170, 170, 170, 1, 175, 175, 175, 175,
-
175, 175, 175, 175, 175, 1, 175, 175,
-
176, 175, 175, 175, 175, 175, 175, 175,
-
175, 175, 175, 175, 175, 175, 175, 175,
-
175, 175, 175, 175, 175, 177, 175, 175,
-
175, 175, 175, 175, 175, 175, 175, 175,
-
175, 175, 175, 175, 175, 175, 175, 175,
-
175, 175, 175, 175, 175, 175, 175, 175,
-
175, 175, 175, 175, 175, 175, 175, 175,
-
175, 175, 175, 175, 175, 175, 175, 175,
-
175, 175, 175, 175, 175, 175, 175, 175,
-
175, 175, 175, 175, 175, 175, 175, 178,
-
175, 175, 175, 175, 175, 175, 175, 175,
-
175, 175, 175, 175, 175, 175, 175, 175,
-
175, 175, 175, 175, 175, 175, 175, 175,
-
175, 175, 175, 175, 175, 175, 175, 175,
-
175, 175, 175, 1, 179, 1, 175, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 175, 1, 180,
-
1, 1, 1, 181, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 180, 134,
-
23, 134, 134, 134, 134, 134, 182, 1,
-
134, 134, 1, 134, 25, 134, 134, 134,
-
134, 134, 134, 134, 134, 134, 134, 134,
-
1, 1, 1, 134, 1, 134, 183, 134,
-
134, 134, 134, 134, 134, 134, 134, 134,
-
134, 134, 134, 134, 134, 134, 134, 134,
-
134, 134, 134, 134, 134, 134, 134, 134,
-
134, 1, 1, 1, 134, 134, 134, 134,
-
134, 134, 134, 134, 134, 134, 134, 134,
-
134, 134, 134, 134, 134, 134, 134, 134,
-
134, 134, 134, 134, 134, 134, 134, 134,
-
134, 134, 134, 134, 134, 1, 184, 1,
-
1, 1, 185, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 184, 1, 1,
-
1, 1, 1, 1, 1, 186, 1, 1,
-
1, 1, 1, 30, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 187, 1, 188,
-
1, 184, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
184, 1, 189, 1, 1, 1, 190, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 189, 1, 1, 1, 1, 1, 1,
-
1, 191, 1, 1, 1, 1, 1, 36,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 192, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 175, 175, 175, 175, 175,
-
175, 175, 175, 175, 193, 1, 175, 175,
-
194, 175, 175, 175, 175, 175, 175, 175,
-
175, 175, 175, 175, 175, 175, 175, 175,
-
175, 175, 175, 193, 175, 195, 175, 175,
-
175, 175, 175, 175, 175, 175, 175, 175,
-
175, 175, 175, 175, 175, 175, 175, 175,
-
175, 175, 175, 175, 175, 175, 175, 175,
-
175, 175, 175, 175, 175, 175, 175, 175,
-
175, 175, 175, 175, 175, 175, 175, 175,
-
175, 175, 175, 175, 175, 175, 175, 175,
-
175, 175, 175, 175, 175, 175, 175, 178,
-
175, 175, 175, 175, 175, 175, 175, 175,
-
175, 175, 175, 175, 175, 175, 175, 175,
-
175, 175, 175, 175, 175, 175, 175, 175,
-
175, 175, 175, 175, 175, 175, 175, 175,
-
175, 175, 175, 1, 196, 1, 193, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 193, 1, 197,
-
1, 1, 1, 198, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 197, 1,
-
1, 1, 1, 1, 1, 1, 199, 1,
-
1, 1, 1, 1, 30, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 200, 1,
-
201, 1, 197, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 197, 1, 202, 1, 1, 1, 203,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 202, 1, 1, 1, 1, 1,
-
1, 1, 204, 1, 1, 1, 1, 1,
-
36, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 205, 1, 206, 1, 1, 1,
-
207, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 206, 208, 209, 208, 208,
-
208, 208, 208, 210, 1, 208, 208, 1,
-
208, 211, 208, 208, 208, 208, 208, 208,
-
208, 208, 208, 208, 208, 1, 1, 1,
-
208, 1, 208, 1, 208, 208, 208, 208,
-
208, 208, 208, 208, 208, 208, 208, 208,
-
208, 208, 208, 208, 208, 208, 208, 208,
-
208, 208, 208, 208, 208, 208, 1, 1,
-
1, 208, 208, 208, 208, 208, 208, 208,
-
208, 208, 208, 208, 208, 208, 208, 208,
-
208, 208, 208, 208, 208, 208, 208, 208,
-
208, 208, 208, 208, 208, 208, 208, 208,
-
208, 208, 1, 134, 23, 134, 134, 134,
-
134, 134, 1, 1, 134, 134, 1, 134,
-
212, 134, 134, 134, 134, 134, 134, 134,
-
134, 134, 134, 134, 1, 1, 1, 134,
-
1, 134, 1, 134, 134, 134, 134, 134,
-
134, 134, 134, 134, 134, 134, 134, 134,
-
134, 134, 134, 134, 134, 134, 134, 134,
-
134, 134, 134, 134, 134, 1, 1, 1,
-
134, 134, 134, 134, 134, 134, 134, 134,
-
134, 134, 134, 134, 134, 134, 134, 134,
-
134, 134, 134, 134, 134, 134, 134, 134,
-
134, 134, 134, 134, 134, 134, 134, 134,
-
134, 1, 213, 213, 213, 213, 213, 213,
-
213, 213, 213, 1, 213, 213, 214, 213,
-
213, 213, 213, 213, 213, 213, 213, 213,
-
213, 213, 213, 213, 213, 213, 213, 213,
-
213, 213, 213, 213, 213, 213, 213, 213,
-
213, 215, 216, 213, 213, 213, 213, 213,
-
213, 213, 213, 213, 213, 213, 213, 213,
-
213, 213, 213, 213, 213, 213, 213, 213,
-
213, 213, 213, 213, 213, 213, 213, 213,
-
213, 213, 213, 213, 213, 213, 213, 213,
-
213, 213, 213, 213, 213, 213, 213, 213,
-
213, 213, 213, 213, 213, 217, 213, 213,
-
213, 213, 213, 213, 213, 213, 213, 213,
-
213, 213, 213, 213, 213, 213, 213, 213,
-
213, 213, 213, 213, 213, 213, 213, 213,
-
213, 213, 213, 213, 213, 213, 213, 213,
-
213, 1, 218, 218, 218, 218, 218, 218,
-
218, 218, 218, 1, 218, 218, 219, 218,
-
218, 218, 218, 218, 218, 218, 218, 218,
-
218, 218, 218, 218, 218, 218, 218, 218,
-
218, 218, 218, 218, 218, 218, 218, 218,
-
218, 220, 221, 218, 218, 218, 218, 218,
-
218, 218, 218, 218, 218, 218, 218, 218,
-
218, 218, 218, 218, 218, 218, 218, 218,
-
218, 218, 218, 218, 218, 218, 218, 218,
-
218, 218, 218, 218, 218, 218, 218, 218,
-
218, 218, 218, 218, 218, 218, 218, 218,
-
218, 218, 218, 218, 218, 222, 218, 218,
-
218, 218, 218, 218, 218, 218, 218, 218,
-
218, 218, 218, 218, 218, 218, 218, 218,
-
218, 218, 218, 218, 218, 218, 218, 218,
-
218, 218, 218, 218, 218, 218, 218, 218,
-
218, 1, 223, 1, 218, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 218, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 218, 224, 1,
-
1, 1, 225, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 224, 1, 1,
-
1, 1, 1, 1, 1, 226, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 227, 1, 85, 1, 1, 1, 228,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 85, 1, 1, 1, 1, 1,
-
1, 1, 229, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 4, 1,
-
230, 1, 1, 1, 231, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 230,
-
1, 1, 1, 1, 1, 1, 1, 232,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 9, 1, 1, 0
-
]
-
-
1
class << self
-
1
attr_accessor :_message_ids_trans_targs
-
1
private :_message_ids_trans_targs, :_message_ids_trans_targs=
-
end
-
1
self._message_ids_trans_targs = [
-
1, 0, 2, 4, 5, 3, 1, 2,
-
4, 5, 6, 7, 9, 93, 110, 111,
-
6, 7, 110, 8, 10, 11, 9, 67,
-
13, 79, 22, 10, 11, 13, 14, 22,
-
12, 10, 11, 13, 14, 22, 15, 17,
-
61, 66, 16, 18, 19, 21, 22, 20,
-
18, 19, 21, 22, 23, 24, 57, 38,
-
52, 47, 59, 53, 23, 24, 26, 38,
-
52, 47, 53, 25, 27, 28, 30, 51,
-
117, 31, 29, 27, 28, 30, 31, 117,
-
32, 34, 37, 33, 36, 118, 32, 34,
-
37, 39, 49, 48, 40, 42, 41, 43,
-
44, 46, 45, 43, 44, 46, 50, 23,
-
24, 26, 38, 52, 47, 53, 54, 56,
-
55, 57, 58, 117, 60, 62, 63, 18,
-
65, 62, 63, 18, 65, 64, 15, 17,
-
61, 66, 68, 77, 76, 69, 71, 70,
-
72, 73, 75, 72, 73, 75, 74, 72,
-
73, 75, 78, 80, 81, 84, 83, 80,
-
81, 83, 82, 80, 81, 83, 85, 90,
-
91, 89, 85, 86, 88, 89, 87, 90,
-
91, 92, 94, 103, 104, 106, 102, 94,
-
95, 97, 102, 96, 98, 99, 101, 22,
-
98, 99, 101, 22, 100, 98, 99, 101,
-
22, 103, 104, 106, 105, 106, 107, 109,
-
22, 108, 106, 107, 109, 22, 6, 7,
-
9, 93, 110, 111, 111, 113, 114, 113,
-
120, 116, 113, 114, 113, 120, 116, 115,
-
118, 35, 119, 5, 35, 119, 118, 35,
-
119
-
]
-
-
1
class << self
-
1
attr_accessor :_message_ids_trans_actions
-
1
private :_message_ids_trans_actions, :_message_ids_trans_actions=
-
end
-
1
self._message_ids_trans_actions = [
-
0, 0, 0, 1, 2, 0, 3, 3,
-
4, 5, 6, 6, 6, 6, 7, 6,
-
0, 0, 1, 0, 8, 8, 0, 0,
-
9, 0, 10, 0, 0, 1, 0, 11,
-
0, 3, 3, 4, 3, 12, 0, 0,
-
0, 1, 0, 0, 0, 1, 0, 0,
-
3, 3, 4, 3, 13, 13, 13, 13,
-
14, 13, 0, 13, 0, 0, 0, 0,
-
1, 0, 0, 0, 0, 0, 1, 0,
-
15, 0, 0, 3, 3, 4, 3, 16,
-
0, 0, 1, 0, 0, 0, 3, 3,
-
4, 0, 0, 0, 0, 0, 0, 0,
-
0, 1, 0, 3, 3, 4, 0, 3,
-
3, 3, 3, 4, 3, 3, 0, 0,
-
0, 0, 0, 0, 0, 17, 17, 18,
-
17, 0, 0, 19, 0, 0, 3, 3,
-
3, 4, 0, 0, 0, 0, 0, 0,
-
8, 8, 9, 0, 0, 1, 0, 3,
-
3, 4, 0, 8, 8, 0, 9, 0,
-
0, 1, 0, 3, 3, 4, 17, 17,
-
17, 17, 0, 0, 19, 0, 0, 0,
-
0, 0, 17, 17, 17, 18, 17, 0,
-
0, 19, 0, 0, 8, 8, 9, 20,
-
0, 0, 1, 21, 0, 3, 3, 4,
-
22, 0, 0, 19, 0, 0, 0, 1,
-
23, 0, 3, 3, 4, 24, 3, 3,
-
25, 25, 4, 25, 0, 26, 26, 27,
-
28, 26, 0, 0, 1, 29, 0, 0,
-
30, 30, 31, 32, 0, 1, 3, 3,
-
4
-
]
-
-
1
class << self
-
1
attr_accessor :_message_ids_eof_actions
-
1
private :_message_ids_eof_actions, :_message_ids_eof_actions=
-
end
-
1
self._message_ids_eof_actions = [
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 30, 0, 3,
-
0
-
]
-
-
1
class << self
-
1
attr_accessor :message_ids_start
-
end
-
1
self.message_ids_start = 1;
-
1
class << self
-
1
attr_accessor :message_ids_first_final
-
end
-
1
self.message_ids_first_final = 117;
-
1
class << self
-
1
attr_accessor :message_ids_error
-
end
-
1
self.message_ids_error = 0;
-
-
1
class << self
-
1
attr_accessor :message_ids_en_comment_tail
-
end
-
1
self.message_ids_en_comment_tail = 112;
-
1
class << self
-
1
attr_accessor :message_ids_en_main
-
end
-
1
self.message_ids_en_main = 1;
-
-
-
# line 17 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb.rl"
-
-
1
def self.parse(data)
-
p = 0
-
eof = data.length
-
stack = []
-
-
actions = []
-
data_unpacked = data.bytes.to_a
-
-
# line 1224 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb"
-
begin
-
p ||= 0
-
pe ||= data.length
-
cs = message_ids_start
-
top = 0
-
end
-
-
# line 26 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb.rl"
-
-
# line 1234 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb"
-
begin
-
testEof = false
-
_slen, _trans, _keys, _inds, _acts, _nacts = nil
-
_goto_level = 0
-
_resume = 10
-
_eof_trans = 15
-
_again = 20
-
_test_eof = 30
-
_out = 40
-
while true
-
if _goto_level <= 0
-
if p == pe
-
_goto_level = _test_eof
-
next
-
end
-
if cs == 0
-
_goto_level = _out
-
next
-
end
-
end
-
if _goto_level <= _resume
-
_keys = cs << 1
-
_inds = _message_ids_index_offsets[cs]
-
_slen = _message_ids_key_spans[cs]
-
_trans = if ( _slen > 0 &&
-
_message_ids_trans_keys[_keys] <= ( data_unpacked[p]) &&
-
( data_unpacked[p]) <= _message_ids_trans_keys[_keys + 1]
-
) then
-
_message_ids_indicies[ _inds + ( data_unpacked[p]) - _message_ids_trans_keys[_keys] ]
-
else
-
_message_ids_indicies[ _inds + _slen ]
-
end
-
cs = _message_ids_trans_targs[_trans]
-
if _message_ids_trans_actions[_trans] != 0
-
case _message_ids_trans_actions[_trans]
-
when 3 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
when 26 then
-
# line 8 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(5, p) end
-
when 15 then
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
when 13 then
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 11 then
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
when 8 then
-
# line 22 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(19, p) end
-
when 6 then
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
when 23 then
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
when 30 then
-
# line 31 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(28, p) end
-
when 2 then
-
# line 32 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(29, p) end
-
when 19 then
-
# line 41 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(38, p) end
-
when 17 then
-
# line 42 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(39, p) end
-
when 1 then
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 112
-
_goto_level = _again
-
next
-
end
-
end
-
when 29 then
-
# line 6 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
top -= 1
-
cs = stack[top]
-
_goto_level = _again
-
next
-
end
-
end
-
when 16 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
when 12 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
when 25 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
when 24 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
when 5 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 32 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(29, p) end
-
when 4 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 112
-
_goto_level = _again
-
next
-
end
-
end
-
when 27 then
-
# line 8 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(5, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 112
-
_goto_level = _again
-
next
-
end
-
end
-
when 28 then
-
# line 8 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(5, p) end
-
# line 6 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
top -= 1
-
cs = stack[top]
-
_goto_level = _again
-
next
-
end
-
end
-
when 14 then
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 112
-
_goto_level = _again
-
next
-
end
-
end
-
when 21 then
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
when 10 then
-
# line 22 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(19, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
when 9 then
-
# line 22 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(19, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 112
-
_goto_level = _again
-
next
-
end
-
end
-
when 7 then
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 112
-
_goto_level = _again
-
next
-
end
-
end
-
when 32 then
-
# line 31 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(28, p) end
-
# line 32 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(29, p) end
-
when 31 then
-
# line 31 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(28, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 112
-
_goto_level = _again
-
next
-
end
-
end
-
when 18 then
-
# line 42 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(39, p) end
-
# line 41 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(38, p) end
-
when 22 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
when 20 then
-
# line 22 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(19, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
# line 1519 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb"
-
end
-
end
-
end
-
if _goto_level <= _again
-
if cs == 0
-
_goto_level = _out
-
next
-
end
-
p += 1
-
if p != pe
-
_goto_level = _resume
-
next
-
end
-
end
-
if _goto_level <= _test_eof
-
if p == eof
-
case _message_ids_eof_actions[cs]
-
when 3 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
when 30 then
-
# line 31 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(28, p) end
-
# line 1545 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb"
-
end
-
end
-
-
end
-
if _goto_level <= _out
-
break
-
end
-
end
-
end
-
-
# line 27 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb.rl"
-
-
if p == eof && cs >=
-
# line 1559 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb"
-
117
-
# line 28 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb.rl"
-
-
return actions, nil
-
else
-
return [], "Only able to parse up to #{data[0..p]}"
-
end
-
end
-
end
-
end
-
end
-
end
-
-
# line 1 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb.rl"
-
-
# line 10 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb.rl"
-
-
-
1
module Mail
-
1
module Parsers
-
1
module Ragel
-
1
module MimeVersionMachine
-
-
# line 13 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb"
-
1
class << self
-
1
attr_accessor :_mime_version_trans_keys
-
1
private :_mime_version_trans_keys, :_mime_version_trans_keys=
-
end
-
1
self._mime_version_trans_keys = [
-
0, 0, 9, 57, 10, 10,
-
9, 32, 9, 57, 40,
-
57, 46, 46, 40, 57,
-
48, 57, 10, 10, 9, 32,
-
1, 127, 1, 127, 10,
-
10, 9, 32, 0, 127,
-
9, 57, 9, 40, 9, 40,
-
0, 0, 0
-
]
-
-
1
class << self
-
1
attr_accessor :_mime_version_key_spans
-
1
private :_mime_version_key_spans, :_mime_version_key_spans=
-
end
-
1
self._mime_version_key_spans = [
-
0, 49, 1, 24, 49, 18, 1, 18,
-
10, 1, 24, 127, 127, 1, 24, 128,
-
49, 32, 32, 0
-
]
-
-
1
class << self
-
1
attr_accessor :_mime_version_index_offsets
-
1
private :_mime_version_index_offsets, :_mime_version_index_offsets=
-
end
-
1
self._mime_version_index_offsets = [
-
0, 0, 50, 52, 77, 127, 146, 148,
-
167, 178, 180, 205, 333, 461, 463, 488,
-
617, 667, 700, 733
-
]
-
-
1
class << self
-
1
attr_accessor :_mime_version_indicies
-
1
private :_mime_version_indicies, :_mime_version_indicies=
-
end
-
1
self._mime_version_indicies = [
-
0, 1, 1, 1, 2, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 0,
-
1, 1, 1, 1, 1, 1, 1, 3,
-
1, 1, 1, 1, 1, 1, 1, 4,
-
4, 4, 4, 4, 4, 4, 4, 4,
-
4, 1, 5, 1, 0, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 0, 1, 6, 1, 1,
-
1, 7, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 6, 1, 1, 1,
-
1, 1, 1, 1, 8, 1, 1, 1,
-
1, 1, 1, 1, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 1, 10,
-
1, 1, 1, 1, 1, 11, 1, 12,
-
12, 12, 12, 12, 12, 12, 12, 12,
-
12, 1, 13, 1, 14, 1, 1, 1,
-
1, 1, 1, 1, 15, 15, 15, 15,
-
15, 15, 15, 15, 15, 15, 1, 16,
-
16, 16, 16, 16, 16, 16, 16, 16,
-
16, 1, 17, 1, 18, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 18, 1, 19, 19, 19,
-
19, 19, 19, 19, 19, 19, 1, 19,
-
19, 20, 19, 19, 19, 19, 19, 19,
-
19, 19, 19, 19, 19, 19, 19, 19,
-
19, 19, 19, 19, 19, 19, 19, 19,
-
19, 19, 19, 19, 21, 22, 19, 19,
-
19, 19, 19, 19, 19, 19, 19, 19,
-
19, 19, 19, 19, 19, 19, 19, 19,
-
19, 19, 19, 19, 19, 19, 19, 19,
-
19, 19, 19, 19, 19, 19, 19, 19,
-
19, 19, 19, 19, 19, 19, 19, 19,
-
19, 19, 19, 19, 19, 19, 19, 19,
-
23, 19, 19, 19, 19, 19, 19, 19,
-
19, 19, 19, 19, 19, 19, 19, 19,
-
19, 19, 19, 19, 19, 19, 19, 19,
-
19, 19, 19, 19, 19, 19, 19, 19,
-
19, 19, 19, 19, 1, 24, 24, 24,
-
24, 24, 24, 24, 24, 24, 1, 24,
-
24, 25, 24, 24, 24, 24, 24, 24,
-
24, 24, 24, 24, 24, 24, 24, 24,
-
24, 24, 24, 24, 24, 24, 24, 24,
-
24, 24, 24, 24, 26, 27, 24, 24,
-
24, 24, 24, 24, 24, 24, 24, 24,
-
24, 24, 24, 24, 24, 24, 24, 24,
-
24, 24, 24, 24, 24, 24, 24, 24,
-
24, 24, 24, 24, 24, 24, 24, 24,
-
24, 24, 24, 24, 24, 24, 24, 24,
-
24, 24, 24, 24, 24, 24, 24, 24,
-
28, 24, 24, 24, 24, 24, 24, 24,
-
24, 24, 24, 24, 24, 24, 24, 24,
-
24, 24, 24, 24, 24, 24, 24, 24,
-
24, 24, 24, 24, 24, 24, 24, 24,
-
24, 24, 24, 24, 1, 29, 1, 24,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 24, 1,
-
24, 24, 24, 24, 24, 24, 24, 24,
-
24, 24, 24, 24, 24, 24, 24, 24,
-
24, 24, 24, 24, 24, 24, 24, 24,
-
24, 24, 24, 24, 24, 24, 24, 24,
-
24, 24, 24, 24, 24, 24, 24, 24,
-
24, 24, 24, 24, 24, 24, 24, 24,
-
24, 24, 24, 24, 24, 24, 24, 24,
-
24, 24, 24, 24, 24, 24, 24, 24,
-
24, 24, 24, 24, 24, 24, 24, 24,
-
24, 24, 24, 24, 24, 24, 24, 24,
-
24, 24, 24, 24, 24, 24, 24, 24,
-
24, 24, 24, 24, 24, 24, 24, 24,
-
24, 24, 24, 24, 24, 24, 24, 24,
-
24, 24, 24, 24, 24, 24, 24, 24,
-
24, 24, 24, 24, 24, 24, 24, 24,
-
24, 24, 24, 24, 24, 24, 24, 24,
-
1, 30, 1, 1, 1, 31, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
30, 1, 1, 1, 1, 1, 1, 1,
-
32, 1, 1, 1, 1, 1, 1, 1,
-
33, 33, 33, 33, 33, 33, 33, 33,
-
33, 33, 1, 18, 1, 1, 1, 34,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 18, 1, 1, 1, 1, 1,
-
1, 1, 35, 1, 36, 1, 1, 1,
-
37, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 36, 1, 1, 1, 1,
-
1, 1, 1, 38, 1, 1, 0
-
]
-
-
1
class << self
-
1
attr_accessor :_mime_version_trans_targs
-
1
private :_mime_version_trans_targs, :_mime_version_trans_targs=
-
end
-
1
self._mime_version_trans_targs = [
-
1, 0, 2, 4, 5, 3, 1, 2,
-
4, 5, 6, 7, 5, 7, 8, 16,
-
16, 10, 17, 12, 13, 12, 19, 15,
-
12, 13, 12, 19, 15, 14, 17, 9,
-
18, 16, 9, 18, 17, 9, 18
-
]
-
-
1
class << self
-
1
attr_accessor :_mime_version_trans_actions
-
1
private :_mime_version_trans_actions, :_mime_version_trans_actions=
-
end
-
1
self._mime_version_trans_actions = [
-
0, 0, 0, 1, 2, 0, 3, 3,
-
4, 5, 6, 7, 0, 3, 1, 8,
-
9, 0, 0, 10, 10, 11, 12, 10,
-
0, 0, 1, 13, 0, 0, 14, 14,
-
15, 0, 0, 1, 3, 3, 4
-
]
-
-
1
class << self
-
1
attr_accessor :_mime_version_eof_actions
-
1
private :_mime_version_eof_actions, :_mime_version_eof_actions=
-
end
-
1
self._mime_version_eof_actions = [
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
14, 0, 3, 0
-
]
-
-
1
class << self
-
1
attr_accessor :mime_version_start
-
end
-
1
self.mime_version_start = 1;
-
1
class << self
-
1
attr_accessor :mime_version_first_final
-
end
-
1
self.mime_version_first_final = 16;
-
1
class << self
-
1
attr_accessor :mime_version_error
-
end
-
1
self.mime_version_error = 0;
-
-
1
class << self
-
1
attr_accessor :mime_version_en_comment_tail
-
end
-
1
self.mime_version_en_comment_tail = 11;
-
1
class << self
-
1
attr_accessor :mime_version_en_main
-
end
-
1
self.mime_version_en_main = 1;
-
-
-
# line 17 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb.rl"
-
-
1
def self.parse(data)
-
p = 0
-
eof = data.length
-
stack = []
-
-
actions = []
-
data_unpacked = data.bytes.to_a
-
-
# line 215 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb"
-
begin
-
p ||= 0
-
pe ||= data.length
-
cs = mime_version_start
-
top = 0
-
end
-
-
# line 26 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb.rl"
-
-
# line 225 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb"
-
begin
-
testEof = false
-
_slen, _trans, _keys, _inds, _acts, _nacts = nil
-
_goto_level = 0
-
_resume = 10
-
_eof_trans = 15
-
_again = 20
-
_test_eof = 30
-
_out = 40
-
while true
-
if _goto_level <= 0
-
if p == pe
-
_goto_level = _test_eof
-
next
-
end
-
if cs == 0
-
_goto_level = _out
-
next
-
end
-
end
-
if _goto_level <= _resume
-
_keys = cs << 1
-
_inds = _mime_version_index_offsets[cs]
-
_slen = _mime_version_key_spans[cs]
-
_trans = if ( _slen > 0 &&
-
_mime_version_trans_keys[_keys] <= ( data_unpacked[p]) &&
-
( data_unpacked[p]) <= _mime_version_trans_keys[_keys + 1]
-
) then
-
_mime_version_indicies[ _inds + ( data_unpacked[p]) - _mime_version_trans_keys[_keys] ]
-
else
-
_mime_version_indicies[ _inds + _slen ]
-
end
-
cs = _mime_version_trans_targs[_trans]
-
if _mime_version_trans_actions[_trans] != 0
-
case _mime_version_trans_actions[_trans]
-
when 3 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
when 10 then
-
# line 8 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(5, p) end
-
when 7 then
-
# line 27 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(24, p) end
-
when 2 then
-
# line 28 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(25, p) end
-
when 14 then
-
# line 29 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(26, p) end
-
when 8 then
-
# line 30 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(27, p) end
-
when 1 then
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 11
-
_goto_level = _again
-
next
-
end
-
end
-
when 13 then
-
# line 6 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
top -= 1
-
cs = stack[top]
-
_goto_level = _again
-
next
-
end
-
end
-
when 5 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 28 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(25, p) end
-
when 9 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 30 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(27, p) end
-
when 4 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 11
-
_goto_level = _again
-
next
-
end
-
end
-
when 11 then
-
# line 8 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(5, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 11
-
_goto_level = _again
-
next
-
end
-
end
-
when 12 then
-
# line 8 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(5, p) end
-
# line 6 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
top -= 1
-
cs = stack[top]
-
_goto_level = _again
-
next
-
end
-
end
-
when 6 then
-
# line 27 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(24, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 11
-
_goto_level = _again
-
next
-
end
-
end
-
when 15 then
-
# line 29 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(26, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 11
-
_goto_level = _again
-
next
-
end
-
end
-
# line 389 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb"
-
end
-
end
-
end
-
if _goto_level <= _again
-
if cs == 0
-
_goto_level = _out
-
next
-
end
-
p += 1
-
if p != pe
-
_goto_level = _resume
-
next
-
end
-
end
-
if _goto_level <= _test_eof
-
if p == eof
-
case _mime_version_eof_actions[cs]
-
when 3 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
when 14 then
-
# line 29 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(26, p) end
-
# line 415 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb"
-
end
-
end
-
-
end
-
if _goto_level <= _out
-
break
-
end
-
end
-
end
-
-
# line 27 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb.rl"
-
-
if p == eof && cs >=
-
# line 429 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb"
-
16
-
# line 28 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb.rl"
-
-
return actions, nil
-
else
-
return [], "Only able to parse up to #{data[0..p]}"
-
end
-
end
-
end
-
end
-
end
-
end
-
-
# line 1 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb.rl"
-
-
# line 10 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb.rl"
-
-
-
1
module Mail
-
1
module Parsers
-
1
module Ragel
-
1
module PhraseListsMachine
-
-
# line 13 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb"
-
1
class << self
-
1
attr_accessor :_phrase_lists_trans_keys
-
1
private :_phrase_lists_trans_keys, :_phrase_lists_trans_keys=
-
end
-
1
self._phrase_lists_trans_keys = [
-
0, 0, 9, 126, 9, 126,
-
10, 10, 9, 32, 10,
-
10, 9, 32, 1, 127,
-
1, 127, 10, 10, 9, 32,
-
-128, -1, 9, 126, 10,
-
10, 9, 32, 9, 126,
-
1, 127, 1, 127, 10, 10,
-
9, 32, -128, -1, 9,
-
126, 9, 126, 9, 126,
-
0, 0, 0
-
]
-
-
1
class << self
-
1
attr_accessor :_phrase_lists_key_spans
-
1
private :_phrase_lists_key_spans, :_phrase_lists_key_spans=
-
end
-
1
self._phrase_lists_key_spans = [
-
0, 118, 118, 1, 24, 1, 24, 127,
-
127, 1, 24, 128, 118, 1, 24, 118,
-
127, 127, 1, 24, 128, 118, 118, 118,
-
0
-
]
-
-
1
class << self
-
1
attr_accessor :_phrase_lists_index_offsets
-
1
private :_phrase_lists_index_offsets, :_phrase_lists_index_offsets=
-
end
-
1
self._phrase_lists_index_offsets = [
-
0, 0, 119, 238, 240, 265, 267, 292,
-
420, 548, 550, 575, 704, 823, 825, 850,
-
969, 1097, 1225, 1227, 1252, 1381, 1500, 1619,
-
1738
-
]
-
-
1
class << self
-
1
attr_accessor :_phrase_lists_indicies
-
1
private :_phrase_lists_indicies, :_phrase_lists_indicies=
-
end
-
1
self._phrase_lists_indicies = [
-
0, 1, 1, 1, 2, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 0,
-
3, 4, 3, 3, 3, 3, 3, 5,
-
1, 3, 3, 1, 3, 6, 3, 3,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 1, 1, 1, 3, 1, 3, 6,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 3, 1, 1, 1, 3, 3, 3,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 3, 3, 3, 3, 3, 1, 7,
-
1, 1, 1, 8, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 7, 9,
-
10, 9, 9, 9, 9, 9, 11, 1,
-
9, 9, 1, 9, 1, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
1, 1, 1, 9, 1, 9, 1, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 1, 1, 1, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 1, 12, 1,
-
7, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 7,
-
1, 13, 1, 9, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 9, 1, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 1, 14, 14,
-
15, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 16, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 17,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 14, 14, 14, 14, 14,
-
14, 14, 14, 1, 18, 18, 18, 18,
-
18, 18, 18, 18, 18, 1, 18, 18,
-
19, 18, 18, 18, 18, 18, 18, 18,
-
18, 18, 18, 18, 18, 18, 18, 18,
-
18, 18, 18, 18, 18, 20, 18, 18,
-
18, 18, 18, 18, 18, 18, 18, 18,
-
18, 18, 18, 18, 18, 18, 18, 18,
-
18, 18, 18, 18, 18, 18, 18, 18,
-
18, 18, 18, 18, 18, 18, 18, 18,
-
18, 18, 18, 18, 18, 18, 18, 18,
-
18, 18, 18, 18, 18, 18, 18, 18,
-
18, 18, 18, 18, 18, 18, 18, 21,
-
18, 18, 18, 18, 18, 18, 18, 18,
-
18, 18, 18, 18, 18, 18, 18, 18,
-
18, 18, 18, 18, 18, 18, 18, 18,
-
18, 18, 18, 18, 18, 18, 18, 18,
-
18, 18, 18, 1, 22, 1, 18, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 18, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 18,
-
23, 1, 1, 1, 24, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 23,
-
3, 4, 3, 3, 3, 3, 3, 5,
-
1, 3, 3, 1, 3, 6, 3, 3,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 1, 1, 1, 3, 1, 3, 6,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 3, 1, 1, 1, 3, 3, 3,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 3, 3, 3, 3, 3, 1, 25,
-
1, 26, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
26, 1, 27, 1, 1, 1, 28, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 27, 29, 30, 29, 29, 29, 29,
-
29, 31, 1, 29, 29, 1, 29, 1,
-
29, 29, 29, 29, 29, 29, 29, 29,
-
29, 29, 29, 1, 1, 1, 29, 1,
-
29, 1, 29, 29, 29, 29, 29, 29,
-
29, 29, 29, 29, 29, 29, 29, 29,
-
29, 29, 29, 29, 29, 29, 29, 29,
-
29, 29, 29, 29, 1, 1, 1, 29,
-
29, 29, 29, 29, 29, 29, 29, 29,
-
29, 29, 29, 29, 29, 29, 29, 29,
-
29, 29, 29, 29, 29, 29, 29, 29,
-
29, 29, 29, 29, 29, 29, 29, 29,
-
1, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 1, 32, 32, 33, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
34, 35, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 36, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
32, 32, 32, 32, 32, 32, 32, 32,
-
1, 37, 37, 37, 37, 37, 37, 37,
-
37, 37, 1, 37, 37, 38, 37, 37,
-
37, 37, 37, 37, 37, 37, 37, 37,
-
37, 37, 37, 37, 37, 37, 37, 37,
-
37, 37, 37, 37, 37, 37, 37, 37,
-
39, 40, 37, 37, 37, 37, 37, 37,
-
37, 37, 37, 37, 37, 37, 37, 37,
-
37, 37, 37, 37, 37, 37, 37, 37,
-
37, 37, 37, 37, 37, 37, 37, 37,
-
37, 37, 37, 37, 37, 37, 37, 37,
-
37, 37, 37, 37, 37, 37, 37, 37,
-
37, 37, 37, 37, 41, 37, 37, 37,
-
37, 37, 37, 37, 37, 37, 37, 37,
-
37, 37, 37, 37, 37, 37, 37, 37,
-
37, 37, 37, 37, 37, 37, 37, 37,
-
37, 37, 37, 37, 37, 37, 37, 37,
-
1, 42, 1, 37, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 37, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 37, 9, 1, 1,
-
1, 43, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 9, 9, 10, 9,
-
9, 9, 9, 9, 44, 1, 9, 9,
-
45, 9, 46, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 1, 1,
-
1, 9, 1, 9, 46, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 1,
-
1, 1, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 1, 29, 1, 1, 1,
-
47, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 29, 29, 30, 29, 29,
-
29, 29, 29, 48, 1, 29, 29, 49,
-
29, 50, 29, 29, 29, 29, 29, 29,
-
29, 29, 29, 29, 29, 1, 1, 1,
-
29, 1, 29, 50, 29, 29, 29, 29,
-
29, 29, 29, 29, 29, 29, 29, 29,
-
29, 29, 29, 29, 29, 29, 29, 29,
-
29, 29, 29, 29, 29, 29, 1, 1,
-
1, 29, 29, 29, 29, 29, 29, 29,
-
29, 29, 29, 29, 29, 29, 29, 29,
-
29, 29, 29, 29, 29, 29, 29, 29,
-
29, 29, 29, 29, 29, 29, 29, 29,
-
29, 29, 1, 7, 1, 1, 1, 8,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 7, 9, 10, 9, 9, 9,
-
9, 9, 11, 1, 9, 9, 45, 9,
-
46, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 1, 1, 1, 9,
-
1, 9, 46, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 1, 1, 1,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 9, 9, 9, 9, 9, 9, 9,
-
9, 1, 1, 0
-
]
-
-
1
class << self
-
1
attr_accessor :_phrase_lists_trans_targs
-
1
private :_phrase_lists_trans_targs, :_phrase_lists_trans_targs=
-
end
-
1
self._phrase_lists_trans_targs = [
-
2, 0, 3, 21, 7, 15, 23, 2,
-
3, 21, 7, 15, 4, 6, 8, 9,
-
21, 11, 8, 9, 21, 11, 10, 12,
-
13, 14, 12, 2, 3, 21, 7, 15,
-
17, 18, 17, 24, 20, 17, 18, 17,
-
24, 20, 19, 5, 22, 12, 23, 5,
-
22, 12, 23
-
]
-
-
1
class << self
-
1
attr_accessor :_phrase_lists_trans_actions
-
1
private :_phrase_lists_trans_actions, :_phrase_lists_trans_actions=
-
end
-
1
self._phrase_lists_trans_actions = [
-
1, 0, 1, 1, 1, 2, 1, 0,
-
0, 0, 0, 3, 0, 0, 4, 4,
-
5, 4, 0, 0, 6, 0, 0, 1,
-
1, 0, 0, 7, 7, 7, 7, 8,
-
9, 9, 10, 11, 9, 0, 0, 3,
-
12, 0, 0, 0, 3, 13, 0, 7,
-
8, 14, 7
-
]
-
-
1
class << self
-
1
attr_accessor :_phrase_lists_eof_actions
-
1
private :_phrase_lists_eof_actions, :_phrase_lists_eof_actions=
-
end
-
1
self._phrase_lists_eof_actions = [
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 13, 14, 13,
-
0
-
]
-
-
1
class << self
-
1
attr_accessor :phrase_lists_start
-
end
-
1
self.phrase_lists_start = 1;
-
1
class << self
-
1
attr_accessor :phrase_lists_first_final
-
end
-
1
self.phrase_lists_first_final = 21;
-
1
class << self
-
1
attr_accessor :phrase_lists_error
-
end
-
1
self.phrase_lists_error = 0;
-
-
1
class << self
-
1
attr_accessor :phrase_lists_en_comment_tail
-
end
-
1
self.phrase_lists_en_comment_tail = 16;
-
1
class << self
-
1
attr_accessor :phrase_lists_en_main
-
end
-
1
self.phrase_lists_en_main = 1;
-
-
-
# line 17 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb.rl"
-
-
1
def self.parse(data)
-
p = 0
-
eof = data.length
-
stack = []
-
-
actions = []
-
data_unpacked = data.bytes.to_a
-
-
# line 350 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb"
-
begin
-
p ||= 0
-
pe ||= data.length
-
cs = phrase_lists_start
-
top = 0
-
end
-
-
# line 26 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb.rl"
-
-
# line 360 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb"
-
begin
-
testEof = false
-
_slen, _trans, _keys, _inds, _acts, _nacts = nil
-
_goto_level = 0
-
_resume = 10
-
_eof_trans = 15
-
_again = 20
-
_test_eof = 30
-
_out = 40
-
while true
-
if _goto_level <= 0
-
if p == pe
-
_goto_level = _test_eof
-
next
-
end
-
if cs == 0
-
_goto_level = _out
-
next
-
end
-
end
-
if _goto_level <= _resume
-
_keys = cs << 1
-
_inds = _phrase_lists_index_offsets[cs]
-
_slen = _phrase_lists_key_spans[cs]
-
_trans = if ( _slen > 0 &&
-
_phrase_lists_trans_keys[_keys] <= ( data_unpacked[p]) &&
-
( data_unpacked[p]) <= _phrase_lists_trans_keys[_keys + 1]
-
) then
-
_phrase_lists_indicies[ _inds + ( data_unpacked[p]) - _phrase_lists_trans_keys[_keys] ]
-
else
-
_phrase_lists_indicies[ _inds + _slen ]
-
end
-
cs = _phrase_lists_trans_targs[_trans]
-
if _phrase_lists_trans_actions[_trans] != 0
-
case _phrase_lists_trans_actions[_trans]
-
when 7 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
when 9 then
-
# line 8 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(5, p) end
-
when 13 then
-
# line 39 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(36, p) end
-
when 1 then
-
# line 40 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(37, p) end
-
when 6 then
-
# line 41 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(38, p) end
-
when 4 then
-
# line 42 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(39, p) end
-
when 3 then
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 16
-
_goto_level = _again
-
next
-
end
-
end
-
when 12 then
-
# line 6 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
top -= 1
-
cs = stack[top]
-
_goto_level = _again
-
next
-
end
-
end
-
when 14 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 39 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(36, p) end
-
when 8 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 16
-
_goto_level = _again
-
next
-
end
-
end
-
when 10 then
-
# line 8 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(5, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 16
-
_goto_level = _again
-
next
-
end
-
end
-
when 11 then
-
# line 8 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(5, p) end
-
# line 6 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
top -= 1
-
cs = stack[top]
-
_goto_level = _again
-
next
-
end
-
end
-
when 2 then
-
# line 40 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(37, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 16
-
_goto_level = _again
-
next
-
end
-
end
-
when 5 then
-
# line 42 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(39, p) end
-
# line 41 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(38, p) end
-
# line 510 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb"
-
end
-
end
-
end
-
if _goto_level <= _again
-
if cs == 0
-
_goto_level = _out
-
next
-
end
-
p += 1
-
if p != pe
-
_goto_level = _resume
-
next
-
end
-
end
-
if _goto_level <= _test_eof
-
if p == eof
-
case _phrase_lists_eof_actions[cs]
-
when 13 then
-
# line 39 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(36, p) end
-
when 14 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 39 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(36, p) end
-
# line 539 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb"
-
end
-
end
-
-
end
-
if _goto_level <= _out
-
break
-
end
-
end
-
end
-
-
# line 27 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb.rl"
-
-
if p == eof && cs >=
-
# line 553 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb"
-
21
-
# line 28 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb.rl"
-
-
return actions, nil
-
else
-
return [], "Only able to parse up to #{data[0..p]}"
-
end
-
end
-
end
-
end
-
end
-
end
-
-
# line 1 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/received_machine.rb.rl"
-
-
# line 10 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/received_machine.rb.rl"
-
-
-
1
module Mail
-
1
module Parsers
-
1
module Ragel
-
1
module ReceivedMachine
-
-
# line 13 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/received_machine.rb"
-
1
class << self
-
1
attr_accessor :_received_trans_keys
-
1
private :_received_trans_keys, :_received_trans_keys=
-
end
-
1
self._received_trans_keys = [
-
0, 0, 9, 126, 9, 126,
-
10, 10, 9, 32, 9,
-
126, 9, 126, 10, 10,
-
9, 32, 1, 127, 1, 127,
-
10, 10, 9, 32, 9,
-
126, 9, 126, 10, 10,
-
9, 32, 9, 126, 9, 126,
-
9, 126, 10, 10, 9,
-
32, 9, 126, 10, 10,
-
9, 32, 9, 126, 9, 87,
-
9, 87, 10, 10, 9,
-
32, 9, 87, 9, 83,
-
9, 83, 10, 10, 9, 32,
-
9, 83, 112, 117, 114,
-
114, 9, 57, 10, 10,
-
9, 32, 9, 57, 48, 57,
-
9, 57, 9, 57, 10,
-
10, 9, 32, 9, 57,
-
48, 57, 9, 58, 10, 10,
-
9, 32, 9, 58, 9,
-
57, 10, 10, 9, 32,
-
9, 57, 48, 57, 9, 58,
-
9, 122, 10, 10, 9,
-
32, 9, 58, 9, 57,
-
10, 10, 9, 32, 9, 57,
-
48, 57, 9, 40, 9,
-
122, 10, 10, 9, 32,
-
9, 40, 48, 57, 48, 57,
-
48, 57, 48, 57, 10,
-
10, 9, 32, 84, 84,
-
103, 103, 101, 101, 99, 99,
-
101, 101, 98, 98, 97,
-
117, 110, 110, 108, 110,
-
97, 97, 114, 121, 111, 111,
-
118, 118, 99, 99, 116,
-
116, 101, 101, 112, 112,
-
114, 114, 105, 105, 9, 44,
-
10, 10, 9, 32, 9,
-
44, 9, 57, 9, 57,
-
10, 10, 9, 32, 9, 57,
-
111, 111, 110, 110, 97,
-
117, 116, 116, 104, 117,
-
117, 117, 101, 101, 101, 101,
-
100, 100, 9, 126, 9,
-
126, 10, 10, 9, 32,
-
9, 126, 9, 64, 10, 10,
-
9, 32, 9, 64, 9,
-
126, 10, 10, 9, 32,
-
9, 126, 9, 64, 10, 10,
-
9, 32, 9, 64, 9,
-
126, 9, 126, 10, 10,
-
9, 32, 9, 126, 9, 62,
-
10, 10, 9, 32, 9,
-
62, 9, 126, 10, 10,
-
9, 32, 9, 126, 9, 126,
-
1, 127, 1, 127, 10,
-
10, 9, 32, 9, 126,
-
9, 62, 10, 10, 9, 32,
-
9, 62, 33, 126, -128,
-
-1, 10, 10, 9, 32,
-
9, 126, 9, 126, 1, 127,
-
10, 10, 9, 32, -128,
-
-1, 1, 127, 1, 127,
-
10, 10, 9, 32, 0, 127,
-
9, 126, 1, 127, 1,
-
127, 10, 10, 9, 32,
-
9, 126, 9, 64, 10, 10,
-
9, 32, 9, 64, 0,
-
127, 10, 10, 9, 32,
-
9, 126, 9, 126, 10, 10,
-
9, 32, 9, 126, 1,
-
127, 1, 127, 10, 10,
-
9, 32, 9, 126, 0, 127,
-
1, 127, 10, 10, 9,
-
32, 1, 127, 1, 127,
-
10, 10, 9, 32, 9, 126,
-
9, 64, 10, 10, 9,
-
32, 9, 64, 0, 127,
-
1, 127, 10, 10, 9, 32,
-
9, 64, 10, 10, 9,
-
32, 9, 64, 9, 126,
-
9, 64, 10, 10, 9, 32,
-
9, 64, 9, 126, 9,
-
126, 10, 10, 9, 32,
-
9, 126, 9, 58, 10, 10,
-
9, 32, 9, 58, 9,
-
64, 10, 10, 9, 32,
-
9, 64, 9, 126, 9, 126,
-
10, 10, 9, 32, 9,
-
126, 33, 126, 9, 126,
-
10, 10, 9, 32, 9, 126,
-
9, 126, 1, 127, 1,
-
127, 10, 10, 9, 32,
-
9, 126, 9, 58, 10, 10,
-
9, 32, 9, 58, 33,
-
126, 0, 127, 10, 10,
-
9, 32, 9, 126, 9, 126,
-
1, 127, 10, 10, 9,
-
32, 0, 127, 9, 126,
-
9, 126, 10, 10, 9, 32,
-
9, 126, 9, 126, 10,
-
10, 9, 32, 9, 126,
-
9, 126, 9, 126, 10, 10,
-
9, 32, 9, 126, 9,
-
126, 1, 127, 10, 10,
-
9, 32, 0, 127, 9, 126,
-
9, 126, 10, 10, 9,
-
32, 9, 126, 33, 126,
-
9, 126, 9, 126, 10, 10,
-
9, 32, 9, 126, 9,
-
126, 9, 126, 10, 10,
-
9, 32, 9, 126, 1, 127,
-
1, 127, 10, 10, 9,
-
32, 0, 127, 10, 10,
-
9, 32, 9, 126, 9, 126,
-
1, 127, 1, 127, 10,
-
10, 9, 32, 9, 126,
-
9, 126, 33, 126, 1, 127,
-
1, 127, 10, 10, 9,
-
32, 0, 127, 10, 10,
-
9, 32, -128, -1, 1, 127,
-
10, 10, 9, 32, 9,
-
126, 9, 126, 10, 10,
-
9, 32, 9, 126, 9, 126,
-
1, 127, 1, 127, 10,
-
10, 9, 32, 9, 126,
-
0, 127, 10, 10, 9, 32,
-
9, 126, 33, 126, 1,
-
127, 1, 127, 10, 10,
-
9, 32, 0, 127, 9, 126,
-
9, 126, 9, 126, 10,
-
10, 9, 32, 9, 126,
-
1, 127, 1, 127, 10, 10,
-
9, 32, -128, -1, 1,
-
127, 10, 10, 9, 32,
-
0, 127, 1, 127, 10, 10,
-
9, 32, 9, 126, 9,
-
126, 9, 126, 10, 10,
-
9, 32, 9, 126, 9, 126,
-
10, 10, 9, 32, 9,
-
126, 9, 126, 9, 126,
-
9, 126, 1, 127, 1, 127,
-
10, 10, 9, 32, 0,
-
127, 9, 40, 9, 40,
-
9, 40, 9, 83, 9, 77,
-
9, 84, 0, 0, 0
-
]
-
-
1
class << self
-
1
attr_accessor :_received_key_spans
-
1
private :_received_key_spans, :_received_key_spans=
-
end
-
1
self._received_key_spans = [
-
0, 118, 118, 1, 24, 118, 118, 1,
-
24, 127, 127, 1, 24, 118, 118, 1,
-
24, 118, 118, 118, 1, 24, 118, 1,
-
24, 118, 79, 79, 1, 24, 79, 75,
-
75, 1, 24, 75, 6, 1, 49, 1,
-
24, 49, 10, 49, 49, 1, 24, 49,
-
10, 50, 1, 24, 50, 49, 1, 24,
-
49, 10, 50, 114, 1, 24, 50, 49,
-
1, 24, 49, 10, 32, 114, 1, 24,
-
32, 10, 10, 10, 10, 1, 24, 1,
-
1, 1, 1, 1, 1, 21, 1, 3,
-
1, 8, 1, 1, 1, 1, 1, 1,
-
1, 1, 36, 1, 24, 36, 49, 49,
-
1, 24, 49, 1, 1, 21, 1, 14,
-
1, 1, 1, 1, 118, 118, 1, 24,
-
118, 56, 1, 24, 56, 118, 1, 24,
-
118, 56, 1, 24, 56, 118, 118, 1,
-
24, 118, 54, 1, 24, 54, 118, 1,
-
24, 118, 118, 127, 127, 1, 24, 118,
-
54, 1, 24, 54, 94, 128, 1, 24,
-
118, 118, 127, 1, 24, 128, 127, 127,
-
1, 24, 128, 118, 127, 127, 1, 24,
-
118, 56, 1, 24, 56, 128, 1, 24,
-
118, 118, 1, 24, 118, 127, 127, 1,
-
24, 118, 128, 127, 1, 24, 127, 127,
-
1, 24, 118, 56, 1, 24, 56, 128,
-
127, 1, 24, 56, 1, 24, 56, 118,
-
56, 1, 24, 56, 118, 118, 1, 24,
-
118, 50, 1, 24, 50, 56, 1, 24,
-
56, 118, 118, 1, 24, 118, 94, 118,
-
1, 24, 118, 118, 127, 127, 1, 24,
-
118, 50, 1, 24, 50, 94, 128, 1,
-
24, 118, 118, 127, 1, 24, 128, 118,
-
118, 1, 24, 118, 118, 1, 24, 118,
-
118, 118, 1, 24, 118, 118, 127, 1,
-
24, 128, 118, 118, 1, 24, 118, 94,
-
118, 118, 1, 24, 118, 118, 118, 1,
-
24, 118, 127, 127, 1, 24, 128, 1,
-
24, 118, 118, 127, 127, 1, 24, 118,
-
118, 94, 127, 127, 1, 24, 128, 1,
-
24, 128, 127, 1, 24, 118, 118, 1,
-
24, 118, 118, 127, 127, 1, 24, 118,
-
128, 1, 24, 118, 94, 127, 127, 1,
-
24, 128, 118, 118, 118, 1, 24, 118,
-
127, 127, 1, 24, 128, 127, 1, 24,
-
128, 127, 1, 24, 118, 118, 118, 1,
-
24, 118, 118, 1, 24, 118, 118, 118,
-
118, 127, 127, 1, 24, 128, 32, 32,
-
32, 75, 69, 76, 0
-
]
-
-
1
class << self
-
1
attr_accessor :_received_index_offsets
-
1
private :_received_index_offsets, :_received_index_offsets=
-
end
-
1
self._received_index_offsets = [
-
0, 0, 119, 238, 240, 265, 384, 503,
-
505, 530, 658, 786, 788, 813, 932, 1051,
-
1053, 1078, 1197, 1316, 1435, 1437, 1462, 1581,
-
1583, 1608, 1727, 1807, 1887, 1889, 1914, 1994,
-
2070, 2146, 2148, 2173, 2249, 2256, 2258, 2308,
-
2310, 2335, 2385, 2396, 2446, 2496, 2498, 2523,
-
2573, 2584, 2635, 2637, 2662, 2713, 2763, 2765,
-
2790, 2840, 2851, 2902, 3017, 3019, 3044, 3095,
-
3145, 3147, 3172, 3222, 3233, 3266, 3381, 3383,
-
3408, 3441, 3452, 3463, 3474, 3485, 3487, 3512,
-
3514, 3516, 3518, 3520, 3522, 3524, 3546, 3548,
-
3552, 3554, 3563, 3565, 3567, 3569, 3571, 3573,
-
3575, 3577, 3579, 3616, 3618, 3643, 3680, 3730,
-
3780, 3782, 3807, 3857, 3859, 3861, 3883, 3885,
-
3900, 3902, 3904, 3906, 3908, 4027, 4146, 4148,
-
4173, 4292, 4349, 4351, 4376, 4433, 4552, 4554,
-
4579, 4698, 4755, 4757, 4782, 4839, 4958, 5077,
-
5079, 5104, 5223, 5278, 5280, 5305, 5360, 5479,
-
5481, 5506, 5625, 5744, 5872, 6000, 6002, 6027,
-
6146, 6201, 6203, 6228, 6283, 6378, 6507, 6509,
-
6534, 6653, 6772, 6900, 6902, 6927, 7056, 7184,
-
7312, 7314, 7339, 7468, 7587, 7715, 7843, 7845,
-
7870, 7989, 8046, 8048, 8073, 8130, 8259, 8261,
-
8286, 8405, 8524, 8526, 8551, 8670, 8798, 8926,
-
8928, 8953, 9072, 9201, 9329, 9331, 9356, 9484,
-
9612, 9614, 9639, 9758, 9815, 9817, 9842, 9899,
-
10028, 10156, 10158, 10183, 10240, 10242, 10267, 10324,
-
10443, 10500, 10502, 10527, 10584, 10703, 10822, 10824,
-
10849, 10968, 11019, 11021, 11046, 11097, 11154, 11156,
-
11181, 11238, 11357, 11476, 11478, 11503, 11622, 11717,
-
11836, 11838, 11863, 11982, 12101, 12229, 12357, 12359,
-
12384, 12503, 12554, 12556, 12581, 12632, 12727, 12856,
-
12858, 12883, 13002, 13121, 13249, 13251, 13276, 13405,
-
13524, 13643, 13645, 13670, 13789, 13908, 13910, 13935,
-
14054, 14173, 14292, 14294, 14319, 14438, 14557, 14685,
-
14687, 14712, 14841, 14960, 15079, 15081, 15106, 15225,
-
15320, 15439, 15558, 15560, 15585, 15704, 15823, 15942,
-
15944, 15969, 16088, 16216, 16344, 16346, 16371, 16500,
-
16502, 16527, 16646, 16765, 16893, 17021, 17023, 17048,
-
17167, 17286, 17381, 17509, 17637, 17639, 17664, 17793,
-
17795, 17820, 17949, 18077, 18079, 18104, 18223, 18342,
-
18344, 18369, 18488, 18607, 18735, 18863, 18865, 18890,
-
19009, 19138, 19140, 19165, 19284, 19379, 19507, 19635,
-
19637, 19662, 19791, 19910, 20029, 20148, 20150, 20175,
-
20294, 20422, 20550, 20552, 20577, 20706, 20834, 20836,
-
20861, 20990, 21118, 21120, 21145, 21264, 21383, 21502,
-
21504, 21529, 21648, 21767, 21769, 21794, 21913, 22032,
-
22151, 22270, 22398, 22526, 22528, 22553, 22682, 22715,
-
22748, 22781, 22857, 22927, 23004
-
]
-
-
1
class << self
-
1
attr_accessor :_received_indicies
-
1
private :_received_indicies, :_received_indicies=
-
end
-
1
self._received_indicies = [
-
0, 1, 1, 1, 2, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 0,
-
3, 4, 3, 3, 3, 3, 3, 5,
-
1, 3, 3, 1, 3, 6, 3, 3,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 1, 7, 8, 3, 1, 3, 1,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 3, 9, 1, 1, 3, 3, 3,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 3, 3, 3, 3, 3, 3, 3,
-
3, 3, 3, 3, 3, 3, 1, 10,
-
1, 1, 1, 11, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 10, 12,
-
13, 12, 12, 12, 12, 12, 14, 1,
-
12, 12, 1, 12, 15, 12, 12, 12,
-
12, 12, 12, 12, 12, 12, 12, 12,
-
1, 16, 17, 12, 1, 12, 1, 12,
-
12, 12, 12, 12, 12, 12, 12, 12,
-
12, 12, 12, 12, 12, 12, 12, 12,
-
12, 12, 12, 12, 12, 12, 12, 12,
-
12, 18, 1, 1, 12, 12, 12, 12,
-
12, 12, 12, 12, 12, 12, 12, 12,
-
12, 12, 12, 12, 12, 12, 12, 12,
-
12, 12, 12, 12, 12, 12, 12, 12,
-
12, 12, 12, 12, 12, 1, 19, 1,
-
20, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 20,
-
1, 21, 1, 1, 1, 22, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
21, 23, 24, 23, 23, 23, 23, 23,
-
25, 1, 23, 23, 1, 23, 26, 23,
-
23, 23, 23, 23, 23, 23, 23, 23,
-
23, 23, 1, 27, 28, 23, 1, 23,
-
29, 23, 23, 23, 23, 23, 23, 23,
-
23, 23, 23, 23, 23, 23, 23, 23,
-
23, 23, 23, 23, 23, 23, 23, 23,
-
23, 23, 23, 30, 1, 1, 23, 23,
-
23, 23, 23, 23, 23, 23, 23, 23,
-
23, 23, 23, 23, 23, 23, 23, 23,
-
23, 23, 23, 23, 23, 23, 23, 23,
-
23, 23, 23, 23, 23, 23, 23, 1,
-
31, 1, 1, 1, 32, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 31,
-
33, 34, 33, 33, 33, 33, 33, 35,
-
1, 33, 33, 1, 33, 36, 33, 33,
-
33, 33, 33, 33, 33, 33, 33, 33,
-
33, 1, 37, 38, 33, 1, 33, 39,
-
33, 33, 33, 33, 33, 33, 33, 33,
-
33, 33, 33, 33, 33, 33, 33, 33,
-
33, 33, 33, 33, 33, 33, 33, 33,
-
33, 33, 40, 1, 1, 33, 33, 33,
-
33, 33, 33, 33, 33, 33, 33, 33,
-
33, 33, 33, 33, 33, 33, 33, 33,
-
33, 33, 33, 33, 33, 33, 33, 33,
-
33, 33, 33, 33, 33, 33, 1, 41,
-
1, 42, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
42, 1, 43, 43, 43, 43, 43, 43,
-
43, 43, 44, 1, 43, 43, 45, 43,
-
43, 43, 43, 43, 43, 43, 43, 43,
-
43, 43, 43, 43, 43, 43, 43, 43,
-
43, 44, 43, 46, 43, 43, 43, 43,
-
43, 43, 43, 43, 43, 43, 43, 43,
-
43, 43, 43, 43, 43, 43, 43, 43,
-
43, 43, 43, 43, 43, 43, 43, 43,
-
43, 43, 43, 43, 43, 43, 43, 43,
-
43, 43, 43, 43, 43, 43, 43, 43,
-
43, 43, 43, 43, 43, 43, 43, 43,
-
43, 43, 43, 43, 43, 47, 43, 43,
-
43, 43, 43, 43, 43, 43, 43, 43,
-
43, 43, 43, 43, 43, 43, 43, 43,
-
43, 43, 43, 43, 43, 43, 43, 43,
-
43, 43, 43, 43, 43, 43, 43, 43,
-
43, 1, 48, 48, 48, 48, 48, 48,
-
48, 48, 48, 1, 48, 48, 49, 48,
-
48, 48, 48, 48, 48, 48, 48, 48,
-
48, 48, 48, 48, 48, 48, 48, 48,
-
48, 48, 48, 50, 48, 48, 48, 48,
-
48, 48, 48, 48, 48, 48, 48, 48,
-
48, 48, 48, 48, 48, 48, 48, 48,
-
48, 48, 48, 48, 48, 48, 48, 48,
-
48, 48, 48, 48, 48, 48, 48, 48,
-
48, 48, 48, 48, 48, 48, 48, 48,
-
48, 48, 48, 48, 48, 48, 48, 48,
-
48, 48, 48, 48, 48, 51, 48, 48,
-
48, 48, 48, 48, 48, 48, 48, 48,
-
48, 48, 48, 48, 48, 48, 48, 48,
-
48, 48, 48, 48, 48, 48, 48, 48,
-
48, 48, 48, 48, 48, 48, 48, 48,
-
48, 1, 52, 1, 48, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 48, 1, 53, 1, 1,
-
1, 54, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 53, 55, 56, 55,
-
55, 55, 55, 55, 57, 1, 55, 55,
-
1, 55, 58, 55, 55, 55, 55, 55,
-
55, 55, 55, 55, 55, 55, 1, 59,
-
60, 55, 1, 55, 61, 55, 55, 55,
-
55, 55, 55, 55, 55, 55, 55, 55,
-
55, 55, 55, 55, 55, 55, 55, 55,
-
55, 55, 55, 55, 55, 55, 55, 62,
-
1, 1, 55, 55, 55, 55, 55, 55,
-
55, 55, 55, 55, 55, 55, 55, 55,
-
55, 55, 55, 55, 55, 55, 55, 55,
-
55, 55, 55, 55, 55, 55, 55, 55,
-
55, 55, 55, 1, 63, 1, 1, 1,
-
64, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 63, 65, 66, 65, 65,
-
65, 65, 65, 67, 1, 65, 65, 1,
-
65, 68, 65, 65, 65, 65, 65, 65,
-
65, 65, 65, 65, 65, 1, 69, 70,
-
65, 1, 65, 71, 65, 65, 65, 65,
-
65, 65, 65, 65, 65, 65, 65, 65,
-
65, 65, 65, 65, 65, 65, 65, 65,
-
65, 65, 65, 65, 65, 65, 72, 1,
-
1, 65, 65, 65, 65, 65, 65, 65,
-
65, 65, 65, 65, 65, 65, 65, 65,
-
65, 65, 65, 65, 65, 65, 65, 65,
-
65, 65, 65, 65, 65, 65, 65, 65,
-
65, 65, 1, 73, 1, 74, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 74, 1, 75, 1,
-
1, 1, 76, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 75, 77, 78,
-
77, 77, 77, 77, 77, 79, 1, 77,
-
77, 1, 77, 80, 77, 77, 77, 77,
-
77, 77, 77, 77, 77, 77, 77, 1,
-
81, 82, 77, 1, 77, 83, 77, 77,
-
77, 77, 77, 77, 77, 77, 77, 77,
-
77, 77, 77, 77, 77, 77, 77, 77,
-
77, 77, 77, 77, 77, 77, 77, 77,
-
84, 1, 1, 77, 77, 77, 77, 77,
-
77, 77, 77, 77, 77, 77, 77, 77,
-
77, 77, 77, 77, 77, 77, 77, 77,
-
77, 77, 77, 77, 77, 77, 77, 77,
-
77, 77, 77, 77, 1, 85, 1, 1,
-
1, 86, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 85, 87, 88, 87,
-
87, 87, 87, 87, 89, 1, 87, 87,
-
1, 87, 90, 87, 87, 87, 87, 87,
-
87, 87, 87, 87, 87, 87, 1, 1,
-
1, 87, 1, 87, 1, 87, 87, 87,
-
87, 87, 87, 87, 87, 87, 87, 87,
-
87, 87, 87, 87, 87, 87, 87, 87,
-
87, 87, 87, 87, 87, 87, 87, 1,
-
1, 1, 87, 87, 87, 87, 87, 87,
-
87, 87, 87, 87, 87, 87, 87, 87,
-
87, 87, 87, 87, 87, 87, 87, 87,
-
87, 87, 87, 87, 87, 87, 87, 87,
-
87, 87, 87, 1, 85, 1, 1, 1,
-
86, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 85, 91, 92, 91, 91,
-
91, 91, 91, 89, 1, 91, 91, 1,
-
91, 1, 91, 91, 91, 91, 91, 91,
-
91, 91, 91, 91, 91, 1, 1, 1,
-
91, 1, 91, 1, 91, 91, 91, 91,
-
91, 91, 91, 91, 91, 91, 91, 91,
-
91, 91, 91, 91, 91, 91, 91, 91,
-
91, 91, 91, 91, 91, 91, 1, 1,
-
1, 91, 91, 91, 91, 91, 91, 91,
-
91, 91, 91, 91, 91, 91, 91, 91,
-
91, 91, 91, 91, 91, 91, 91, 91,
-
91, 91, 91, 91, 91, 91, 91, 91,
-
91, 91, 1, 93, 1, 85, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 85, 1, 94, 1,
-
1, 1, 95, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 94, 12, 13,
-
12, 12, 12, 12, 12, 96, 1, 12,
-
12, 1, 12, 97, 12, 12, 12, 12,
-
12, 12, 12, 12, 12, 12, 12, 1,
-
16, 17, 12, 1, 12, 98, 12, 12,
-
12, 12, 12, 12, 12, 12, 12, 12,
-
12, 12, 12, 12, 12, 12, 12, 12,
-
12, 12, 12, 12, 12, 12, 12, 12,
-
18, 1, 1, 12, 12, 12, 12, 12,
-
12, 12, 12, 12, 12, 12, 12, 12,
-
12, 12, 12, 12, 12, 12, 12, 12,
-
12, 12, 12, 12, 12, 12, 12, 12,
-
12, 12, 12, 12, 1, 99, 1, 91,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 91, 1,
-
100, 1, 1, 1, 101, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 100,
-
102, 103, 102, 102, 102, 102, 102, 104,
-
1, 102, 102, 1, 102, 105, 102, 102,
-
102, 102, 102, 102, 102, 102, 102, 102,
-
102, 1, 106, 107, 102, 1, 102, 108,
-
102, 102, 102, 102, 102, 102, 102, 102,
-
102, 102, 102, 102, 102, 102, 102, 102,
-
102, 102, 102, 102, 102, 102, 102, 102,
-
102, 102, 109, 1, 1, 102, 102, 102,
-
102, 102, 102, 102, 102, 102, 102, 102,
-
102, 102, 102, 102, 102, 102, 102, 102,
-
102, 102, 102, 102, 102, 102, 102, 102,
-
102, 102, 102, 102, 102, 102, 1, 110,
-
1, 1, 1, 111, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 110, 1,
-
1, 1, 1, 1, 1, 1, 112, 1,
-
1, 1, 1, 1, 1, 1, 113, 113,
-
113, 113, 113, 113, 113, 113, 113, 113,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 114, 1, 1, 1,
-
1, 1, 1, 115, 1, 1, 1, 1,
-
1, 116, 117, 1, 1, 118, 1, 119,
-
1, 1, 1, 120, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 119, 1,
-
1, 1, 1, 1, 1, 1, 121, 1,
-
1, 1, 1, 1, 1, 1, 122, 122,
-
122, 122, 122, 122, 122, 122, 122, 122,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 114, 1, 1, 1,
-
1, 1, 1, 115, 1, 1, 1, 1,
-
1, 116, 117, 1, 1, 118, 1, 123,
-
1, 119, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
119, 1, 124, 1, 1, 1, 125, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 124, 1, 1, 1, 1, 1, 1,
-
1, 126, 1, 1, 1, 1, 1, 1,
-
1, 127, 127, 127, 127, 127, 127, 127,
-
127, 127, 127, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 128,
-
1, 1, 1, 1, 1, 1, 129, 1,
-
1, 1, 1, 1, 130, 131, 1, 1,
-
132, 1, 133, 1, 1, 1, 134, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 133, 1, 1, 1, 1, 1, 1,
-
1, 135, 1, 1, 1, 1, 1, 1,
-
1, 133, 133, 133, 133, 133, 133, 133,
-
133, 133, 133, 1, 1, 1, 1, 1,
-
1, 1, 136, 1, 1, 137, 1, 138,
-
1, 1, 1, 139, 1, 1, 140, 141,
-
142, 1, 1, 1, 143, 1, 133, 1,
-
1, 1, 134, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 133, 1, 1,
-
1, 1, 1, 1, 1, 135, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 136, 1,
-
1, 137, 1, 138, 1, 1, 1, 139,
-
1, 1, 140, 141, 142, 1, 1, 1,
-
143, 1, 144, 1, 133, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 133, 1, 145, 1, 1,
-
1, 146, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 145, 1, 1, 1,
-
1, 1, 1, 1, 147, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 148, 1, 1,
-
149, 1, 150, 1, 1, 1, 151, 1,
-
1, 152, 153, 154, 1, 1, 1, 155,
-
1, 156, 1, 1, 1, 1, 157, 1,
-
158, 1, 158, 1, 1, 1, 159, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 158, 1, 1, 1, 1, 1, 1,
-
1, 160, 1, 1, 1, 1, 1, 1,
-
1, 161, 161, 161, 161, 161, 161, 161,
-
161, 161, 161, 1, 162, 1, 158, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 158, 1, 163,
-
1, 1, 1, 164, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 163, 1,
-
1, 1, 1, 1, 1, 1, 165, 1,
-
1, 1, 1, 1, 1, 1, 166, 166,
-
166, 166, 166, 166, 166, 166, 166, 166,
-
1, 167, 167, 167, 167, 167, 167, 167,
-
167, 167, 167, 1, 168, 1, 1, 1,
-
169, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 168, 1, 1, 1, 1,
-
1, 1, 1, 170, 1, 1, 1, 1,
-
1, 1, 1, 167, 167, 167, 167, 167,
-
167, 167, 167, 167, 167, 1, 168, 1,
-
1, 1, 169, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 168, 1, 1,
-
1, 1, 1, 1, 1, 170, 1, 1,
-
1, 1, 1, 1, 1, 171, 171, 171,
-
171, 171, 171, 171, 171, 171, 171, 1,
-
172, 1, 168, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 168, 1, 173, 1, 1, 1, 174,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 173, 1, 1, 1, 1, 1,
-
1, 1, 175, 1, 1, 1, 1, 1,
-
1, 1, 176, 176, 176, 176, 176, 176,
-
176, 176, 176, 176, 1, 177, 177, 177,
-
177, 177, 177, 177, 177, 177, 177, 1,
-
177, 1, 1, 1, 178, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 177,
-
1, 1, 1, 1, 1, 1, 1, 179,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 180, 1, 181, 1, 177, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 177, 1, 182, 1,
-
1, 1, 183, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 182, 1, 1,
-
1, 1, 1, 1, 1, 184, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 185,
-
1, 180, 1, 1, 1, 186, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
180, 1, 1, 1, 1, 1, 1, 1,
-
187, 1, 1, 1, 1, 1, 1, 1,
-
188, 188, 188, 188, 188, 188, 188, 188,
-
188, 188, 1, 189, 1, 180, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 180, 1, 185, 1,
-
1, 1, 190, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 185, 1, 1,
-
1, 1, 1, 1, 1, 191, 1, 1,
-
1, 1, 1, 1, 1, 192, 192, 192,
-
192, 192, 192, 192, 192, 192, 192, 1,
-
193, 193, 193, 193, 193, 193, 193, 193,
-
193, 193, 1, 194, 1, 1, 1, 195,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 194, 1, 1, 1, 1, 1,
-
1, 1, 196, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 197, 1, 194, 1,
-
1, 1, 195, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 194, 1, 1,
-
1, 1, 1, 1, 1, 196, 1, 1,
-
198, 1, 198, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 197,
-
1, 1, 1, 1, 1, 1, 199, 199,
-
200, 199, 200, 199, 201, 199, 199, 1,
-
199, 199, 200, 199, 199, 200, 199, 199,
-
199, 199, 202, 199, 199, 199, 199, 199,
-
1, 1, 1, 1, 1, 1, 199, 199,
-
199, 199, 199, 199, 199, 199, 199, 1,
-
199, 199, 199, 199, 199, 199, 199, 199,
-
199, 199, 199, 199, 199, 199, 199, 199,
-
1, 203, 1, 194, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 194, 1, 204, 1, 1, 1,
-
205, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 204, 1, 1, 1, 1,
-
1, 1, 1, 206, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 207, 1, 197,
-
1, 1, 1, 208, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 197, 1,
-
1, 1, 1, 1, 1, 1, 209, 1,
-
1, 1, 1, 1, 1, 1, 210, 210,
-
210, 210, 210, 210, 210, 210, 210, 210,
-
1, 211, 1, 197, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 197, 1, 207, 1, 1, 1,
-
212, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 207, 1, 1, 1, 1,
-
1, 1, 1, 213, 1, 1, 1, 1,
-
1, 1, 1, 214, 214, 214, 214, 214,
-
214, 214, 214, 214, 214, 1, 215, 215,
-
215, 215, 215, 215, 215, 215, 215, 215,
-
1, 216, 1, 1, 1, 217, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
216, 1, 1, 1, 1, 1, 1, 1,
-
218, 1, 216, 1, 1, 1, 217, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 216, 1, 1, 1, 1, 1, 1,
-
1, 218, 1, 1, 198, 1, 198, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 199, 199, 200, 199, 200, 199,
-
201, 199, 199, 1, 199, 199, 200, 199,
-
199, 200, 199, 199, 199, 199, 202, 199,
-
199, 199, 199, 199, 1, 1, 1, 1,
-
1, 1, 199, 199, 199, 199, 199, 199,
-
199, 199, 199, 1, 199, 199, 199, 199,
-
199, 199, 199, 199, 199, 199, 199, 199,
-
199, 199, 199, 199, 1, 219, 1, 216,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 216, 1,
-
220, 1, 1, 1, 221, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 220,
-
1, 1, 1, 1, 1, 1, 1, 222,
-
1, 223, 223, 223, 223, 223, 223, 223,
-
223, 223, 223, 1, 224, 224, 224, 224,
-
224, 224, 224, 224, 224, 224, 1, 225,
-
225, 225, 225, 225, 225, 225, 225, 225,
-
225, 1, 199, 199, 199, 199, 199, 199,
-
199, 199, 199, 199, 1, 226, 1, 227,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 227, 1,
-
199, 1, 158, 1, 228, 1, 158, 1,
-
229, 1, 158, 1, 230, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
231, 1, 158, 1, 158, 1, 158, 1,
-
232, 1, 158, 1, 1, 1, 1, 1,
-
1, 158, 1, 233, 1, 158, 1, 234,
-
1, 158, 1, 235, 1, 158, 1, 236,
-
1, 237, 1, 237, 1, 1, 1, 238,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 237, 1, 1, 1, 1, 1,
-
1, 1, 239, 1, 1, 1, 240, 1,
-
241, 1, 237, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 237, 1, 242, 1, 1, 1, 243,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 242, 1, 1, 1, 1, 1,
-
1, 1, 244, 1, 1, 1, 245, 1,
-
246, 1, 1, 1, 247, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 246,
-
1, 1, 1, 1, 1, 1, 1, 248,
-
1, 1, 1, 1, 1, 1, 1, 113,
-
113, 113, 113, 113, 113, 113, 113, 113,
-
113, 1, 249, 1, 1, 1, 250, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 249, 1, 1, 1, 1, 1, 1,
-
1, 251, 1, 1, 1, 1, 1, 1,
-
1, 122, 122, 122, 122, 122, 122, 122,
-
122, 122, 122, 1, 252, 1, 249, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 249, 1, 253,
-
1, 1, 1, 254, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 253, 1,
-
1, 1, 1, 1, 1, 1, 255, 1,
-
1, 1, 1, 1, 1, 1, 127, 127,
-
127, 127, 127, 127, 127, 127, 127, 127,
-
1, 256, 1, 237, 1, 257, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 256, 1, 237, 1, 258, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 259, 1, 237, 1, 237, 1,
-
260, 1, 237, 1, 261, 1, 1, 1,
-
262, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 261, 263, 264, 263, 263,
-
263, 263, 263, 265, 1, 263, 263, 266,
-
263, 267, 263, 263, 263, 263, 263, 263,
-
263, 263, 263, 263, 263, 1, 1, 1,
-
263, 1, 263, 268, 263, 263, 263, 263,
-
263, 263, 263, 263, 263, 263, 263, 263,
-
263, 263, 263, 263, 263, 263, 263, 263,
-
263, 263, 263, 263, 263, 263, 1, 1,
-
1, 263, 263, 263, 263, 263, 263, 263,
-
263, 263, 263, 263, 263, 263, 263, 263,
-
263, 263, 263, 263, 263, 263, 263, 263,
-
263, 263, 263, 263, 263, 263, 263, 263,
-
263, 263, 1, 269, 1, 1, 1, 270,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 269, 263, 264, 263, 263, 263,
-
263, 263, 271, 1, 263, 263, 272, 263,
-
267, 263, 263, 263, 263, 263, 263, 263,
-
263, 263, 263, 263, 1, 1, 1, 263,
-
1, 263, 273, 263, 263, 263, 263, 263,
-
263, 263, 263, 263, 263, 263, 263, 263,
-
263, 263, 263, 263, 263, 263, 263, 263,
-
263, 263, 263, 263, 263, 1, 1, 1,
-
263, 263, 263, 263, 263, 263, 263, 263,
-
263, 263, 263, 263, 263, 263, 263, 263,
-
263, 263, 263, 263, 263, 263, 263, 263,
-
263, 263, 263, 263, 263, 263, 263, 263,
-
263, 1, 274, 1, 269, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 269, 1, 275, 1, 1,
-
1, 276, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 275, 277, 278, 277,
-
277, 277, 277, 277, 279, 1, 277, 277,
-
1, 277, 280, 277, 277, 277, 277, 277,
-
277, 277, 277, 277, 277, 277, 1, 1,
-
1, 277, 281, 277, 282, 277, 277, 277,
-
277, 277, 277, 277, 277, 277, 277, 277,
-
277, 277, 277, 277, 277, 277, 277, 277,
-
277, 277, 277, 277, 277, 277, 277, 1,
-
1, 1, 277, 277, 277, 277, 277, 277,
-
277, 277, 277, 277, 277, 277, 277, 277,
-
277, 277, 277, 277, 277, 277, 277, 277,
-
277, 277, 277, 277, 277, 277, 277, 277,
-
277, 277, 277, 1, 283, 1, 1, 1,
-
284, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 283, 1, 1, 1, 1,
-
1, 1, 1, 285, 1, 1, 1, 1,
-
1, 286, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 20, 1, 287, 1, 288, 1, 283,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 283, 1,
-
289, 1, 1, 1, 290, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 289,
-
1, 1, 1, 1, 1, 1, 1, 291,
-
1, 1, 1, 1, 1, 292, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 293, 1, 294,
-
1, 286, 1, 1, 1, 295, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
286, 296, 297, 296, 296, 296, 296, 296,
-
298, 1, 296, 296, 1, 296, 1, 296,
-
296, 296, 296, 296, 296, 296, 296, 296,
-
296, 296, 1, 1, 1, 296, 1, 296,
-
1, 296, 296, 296, 296, 296, 296, 296,
-
296, 296, 296, 296, 296, 296, 296, 296,
-
296, 296, 296, 296, 296, 296, 296, 296,
-
296, 296, 296, 1, 1, 1, 296, 296,
-
296, 296, 296, 296, 296, 296, 296, 296,
-
296, 296, 296, 296, 296, 296, 296, 296,
-
296, 296, 296, 296, 296, 296, 296, 296,
-
296, 296, 296, 296, 296, 296, 296, 1,
-
299, 1, 286, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 286, 1, 300, 1, 1, 1, 301,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 300, 296, 1, 296, 296, 296,
-
296, 296, 302, 1, 296, 296, 1, 296,
-
286, 296, 296, 296, 296, 296, 296, 296,
-
296, 296, 296, 296, 1, 1, 1, 296,
-
20, 296, 303, 296, 296, 296, 296, 296,
-
296, 296, 296, 296, 296, 296, 296, 296,
-
296, 296, 296, 296, 296, 296, 296, 296,
-
296, 296, 296, 296, 296, 1, 1, 1,
-
296, 296, 296, 296, 296, 296, 296, 296,
-
296, 296, 296, 296, 296, 296, 296, 296,
-
296, 296, 296, 296, 296, 296, 296, 296,
-
296, 296, 296, 296, 296, 296, 296, 296,
-
296, 1, 300, 1, 1, 1, 301, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 300, 1, 1, 1, 1, 1, 1,
-
1, 302, 1, 1, 1, 1, 1, 286,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 20,
-
1, 303, 1, 304, 1, 300, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 300, 1, 305, 1,
-
1, 1, 306, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 305, 1, 1,
-
1, 1, 1, 1, 1, 307, 1, 1,
-
1, 1, 1, 292, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 293, 1, 308, 1, 309,
-
1, 1, 1, 310, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 309, 311,
-
312, 311, 311, 311, 311, 311, 313, 1,
-
311, 311, 1, 311, 314, 311, 311, 311,
-
311, 311, 311, 311, 311, 311, 311, 311,
-
1, 1, 1, 311, 1, 311, 1, 311,
-
311, 311, 311, 311, 311, 311, 311, 311,
-
311, 311, 311, 311, 311, 311, 311, 311,
-
311, 311, 311, 311, 311, 311, 311, 311,
-
311, 315, 1, 1, 311, 311, 311, 311,
-
311, 311, 311, 311, 311, 311, 311, 311,
-
311, 311, 311, 311, 311, 311, 311, 311,
-
311, 311, 311, 311, 311, 311, 311, 311,
-
311, 311, 311, 311, 311, 1, 316, 1,
-
1, 1, 317, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 316, 318, 319,
-
318, 318, 318, 318, 318, 320, 1, 318,
-
318, 1, 318, 321, 318, 318, 318, 318,
-
318, 318, 318, 318, 318, 318, 318, 1,
-
1, 1, 318, 1, 318, 1, 318, 318,
-
318, 318, 318, 318, 318, 318, 318, 318,
-
318, 318, 318, 318, 318, 318, 318, 318,
-
318, 318, 318, 318, 318, 318, 318, 318,
-
322, 1, 1, 318, 318, 318, 318, 318,
-
318, 318, 318, 318, 318, 318, 318, 318,
-
318, 318, 318, 318, 318, 318, 318, 318,
-
318, 318, 318, 318, 318, 318, 318, 318,
-
318, 318, 318, 318, 1, 323, 1, 316,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 316, 1,
-
324, 1, 1, 1, 325, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 324,
-
318, 319, 318, 318, 318, 318, 318, 326,
-
1, 318, 318, 1, 318, 327, 318, 318,
-
318, 318, 318, 318, 318, 318, 318, 318,
-
318, 1, 1, 1, 318, 328, 318, 1,
-
318, 318, 318, 318, 318, 318, 318, 318,
-
318, 318, 318, 318, 318, 318, 318, 318,
-
318, 318, 318, 318, 318, 318, 318, 318,
-
318, 318, 1, 1, 1, 318, 318, 318,
-
318, 318, 318, 318, 318, 318, 318, 318,
-
318, 318, 318, 318, 318, 318, 318, 318,
-
318, 318, 318, 318, 318, 318, 318, 318,
-
318, 318, 318, 318, 318, 318, 1, 324,
-
1, 1, 1, 325, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 324, 1,
-
1, 1, 1, 1, 1, 1, 326, 1,
-
1, 1, 1, 1, 329, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 328, 1, 330, 1,
-
324, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 324,
-
1, 331, 1, 1, 1, 332, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
331, 1, 1, 1, 1, 1, 1, 1,
-
333, 1, 1, 1, 1, 1, 334, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 335, 1,
-
329, 1, 1, 1, 336, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 329,
-
337, 1, 337, 337, 337, 337, 337, 338,
-
1, 337, 337, 1, 337, 1, 337, 337,
-
337, 337, 337, 337, 337, 337, 337, 337,
-
337, 1, 1, 1, 337, 1, 337, 1,
-
337, 337, 337, 337, 337, 337, 337, 337,
-
337, 337, 337, 337, 337, 337, 337, 337,
-
337, 337, 337, 337, 337, 337, 337, 337,
-
337, 337, 1, 1, 1, 337, 337, 337,
-
337, 337, 337, 337, 337, 337, 337, 337,
-
337, 337, 337, 337, 337, 337, 337, 337,
-
337, 337, 337, 337, 337, 337, 337, 337,
-
337, 337, 337, 337, 337, 337, 1, 339,
-
1, 329, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
329, 1, 324, 1, 1, 1, 325, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 324, 337, 1, 337, 337, 337, 337,
-
337, 326, 1, 337, 337, 1, 337, 329,
-
337, 337, 337, 337, 337, 337, 337, 337,
-
337, 337, 337, 1, 1, 1, 337, 328,
-
337, 1, 337, 337, 337, 337, 337, 337,
-
337, 337, 337, 337, 337, 337, 337, 337,
-
337, 337, 337, 337, 337, 337, 337, 337,
-
337, 337, 337, 337, 1, 1, 1, 337,
-
337, 337, 337, 337, 337, 337, 337, 337,
-
337, 337, 337, 337, 337, 337, 337, 337,
-
337, 337, 337, 337, 337, 337, 337, 337,
-
337, 337, 337, 337, 337, 337, 337, 337,
-
1, 334, 1, 1, 1, 340, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
334, 341, 1, 341, 341, 341, 341, 341,
-
342, 1, 341, 341, 1, 341, 1, 341,
-
341, 341, 341, 341, 341, 341, 341, 341,
-
341, 341, 1, 1, 1, 341, 1, 341,
-
1, 341, 341, 341, 341, 341, 341, 341,
-
341, 341, 341, 341, 341, 341, 341, 341,
-
341, 341, 341, 341, 341, 341, 341, 341,
-
341, 341, 341, 1, 1, 1, 341, 341,
-
341, 341, 341, 341, 341, 341, 341, 341,
-
341, 341, 341, 341, 341, 341, 341, 341,
-
341, 341, 341, 341, 341, 341, 341, 341,
-
341, 341, 341, 341, 341, 341, 341, 1,
-
343, 343, 343, 343, 343, 343, 343, 343,
-
319, 1, 343, 343, 344, 343, 343, 343,
-
343, 343, 343, 343, 343, 343, 343, 343,
-
343, 343, 343, 343, 343, 343, 343, 319,
-
343, 1, 343, 343, 343, 343, 343, 343,
-
343, 343, 343, 343, 343, 343, 343, 343,
-
343, 343, 343, 343, 343, 343, 343, 343,
-
343, 343, 343, 343, 343, 343, 343, 343,
-
343, 343, 343, 343, 343, 343, 343, 343,
-
343, 343, 343, 343, 343, 343, 343, 343,
-
343, 343, 343, 343, 343, 343, 343, 343,
-
343, 343, 343, 345, 343, 343, 343, 343,
-
343, 343, 343, 343, 343, 343, 343, 343,
-
343, 343, 343, 343, 343, 343, 343, 343,
-
343, 343, 343, 343, 343, 343, 343, 343,
-
343, 343, 343, 343, 343, 343, 343, 1,
-
343, 343, 343, 343, 343, 343, 343, 343,
-
343, 1, 343, 343, 346, 343, 343, 343,
-
343, 343, 343, 343, 343, 343, 343, 343,
-
343, 343, 343, 343, 343, 343, 343, 343,
-
343, 347, 343, 343, 343, 343, 343, 343,
-
343, 343, 343, 343, 343, 343, 343, 343,
-
343, 343, 343, 343, 343, 343, 343, 343,
-
343, 343, 343, 343, 343, 343, 343, 343,
-
343, 343, 343, 343, 343, 343, 343, 343,
-
343, 343, 343, 343, 343, 343, 343, 343,
-
343, 343, 343, 343, 343, 343, 343, 343,
-
343, 343, 343, 345, 343, 343, 343, 343,
-
343, 343, 343, 343, 343, 343, 343, 343,
-
343, 343, 343, 343, 343, 343, 343, 343,
-
343, 343, 343, 343, 343, 343, 343, 343,
-
343, 343, 343, 343, 343, 343, 343, 1,
-
348, 1, 343, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 343, 1, 349, 1, 1, 1, 350,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 349, 347, 319, 347, 347, 347,
-
347, 347, 351, 1, 347, 347, 1, 347,
-
321, 347, 347, 347, 347, 347, 347, 347,
-
347, 347, 347, 347, 1, 1, 1, 347,
-
328, 347, 1, 347, 347, 347, 347, 347,
-
347, 347, 347, 347, 347, 347, 347, 347,
-
347, 347, 347, 347, 347, 347, 347, 347,
-
347, 347, 347, 347, 347, 1, 1, 1,
-
347, 347, 347, 347, 347, 347, 347, 347,
-
347, 347, 347, 347, 347, 347, 347, 347,
-
347, 347, 347, 347, 347, 347, 347, 347,
-
347, 347, 347, 347, 347, 347, 347, 347,
-
347, 1, 349, 1, 1, 1, 350, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 349, 1, 1, 1, 1, 1, 1,
-
1, 351, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 328,
-
1, 352, 1, 349, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 349, 1, 353, 1, 1, 1,
-
354, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 353, 1, 1, 1, 1,
-
1, 1, 1, 355, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 335, 1, 347, 319, 347, 347, 347,
-
347, 347, 1, 1, 347, 347, 1, 347,
-
321, 347, 347, 347, 347, 347, 347, 347,
-
347, 347, 347, 347, 1, 1, 1, 347,
-
1, 347, 1, 347, 347, 347, 347, 347,
-
347, 347, 347, 347, 347, 347, 347, 347,
-
347, 347, 347, 347, 347, 347, 347, 347,
-
347, 347, 347, 347, 347, 1, 1, 1,
-
347, 347, 347, 347, 347, 347, 347, 347,
-
347, 347, 347, 347, 347, 347, 347, 347,
-
347, 347, 347, 347, 347, 347, 347, 347,
-
347, 347, 347, 347, 347, 347, 347, 347,
-
347, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 343, 356, 1, 319, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 319, 1, 329, 1,
-
1, 1, 336, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 329, 318, 319,
-
318, 318, 318, 318, 318, 338, 1, 318,
-
318, 1, 318, 321, 318, 318, 318, 318,
-
318, 318, 318, 318, 318, 318, 318, 1,
-
1, 1, 318, 1, 318, 1, 318, 318,
-
318, 318, 318, 318, 318, 318, 318, 318,
-
318, 318, 318, 318, 318, 318, 318, 318,
-
318, 318, 318, 318, 318, 318, 318, 318,
-
1, 1, 1, 318, 318, 318, 318, 318,
-
318, 318, 318, 318, 318, 318, 318, 318,
-
318, 318, 318, 318, 318, 318, 318, 318,
-
318, 318, 318, 318, 318, 318, 318, 318,
-
318, 318, 318, 318, 1, 357, 1, 1,
-
1, 358, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 357, 359, 360, 359,
-
359, 359, 359, 359, 361, 1, 359, 359,
-
1, 359, 362, 359, 359, 359, 359, 359,
-
359, 359, 359, 359, 359, 359, 1, 1,
-
1, 359, 1, 359, 1, 359, 359, 359,
-
359, 359, 359, 359, 359, 359, 359, 359,
-
359, 359, 359, 359, 359, 359, 359, 359,
-
359, 359, 359, 359, 359, 359, 359, 363,
-
1, 1, 359, 359, 359, 359, 359, 359,
-
359, 359, 359, 359, 359, 359, 359, 359,
-
359, 359, 359, 359, 359, 359, 359, 359,
-
359, 359, 359, 359, 359, 359, 359, 359,
-
359, 359, 359, 1, 322, 322, 322, 322,
-
322, 322, 322, 322, 322, 1, 322, 322,
-
364, 322, 322, 322, 322, 322, 322, 322,
-
322, 322, 322, 322, 322, 322, 322, 322,
-
322, 322, 322, 322, 322, 322, 322, 322,
-
322, 322, 322, 322, 322, 322, 322, 322,
-
322, 322, 322, 322, 322, 322, 322, 322,
-
322, 322, 322, 322, 322, 322, 322, 322,
-
322, 322, 322, 322, 322, 322, 322, 322,
-
322, 322, 322, 322, 322, 322, 322, 322,
-
322, 322, 322, 322, 322, 322, 322, 322,
-
322, 322, 322, 322, 322, 322, 1, 365,
-
349, 322, 322, 322, 322, 322, 322, 322,
-
322, 322, 322, 322, 322, 322, 322, 322,
-
322, 322, 322, 322, 322, 322, 322, 322,
-
322, 322, 322, 322, 322, 322, 322, 322,
-
322, 322, 322, 1, 366, 1, 322, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 322, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 322,
-
367, 367, 367, 367, 367, 367, 367, 367,
-
367, 1, 367, 367, 368, 367, 367, 367,
-
367, 367, 367, 367, 367, 367, 367, 367,
-
367, 367, 367, 367, 367, 367, 367, 367,
-
367, 369, 367, 367, 367, 367, 367, 367,
-
367, 367, 367, 367, 367, 367, 367, 367,
-
367, 367, 367, 367, 367, 367, 367, 367,
-
367, 367, 367, 367, 367, 367, 367, 367,
-
367, 367, 367, 367, 367, 367, 367, 367,
-
367, 367, 367, 367, 367, 367, 367, 367,
-
367, 367, 367, 367, 367, 367, 367, 367,
-
367, 367, 367, 370, 367, 367, 367, 367,
-
367, 367, 367, 367, 367, 367, 367, 367,
-
367, 367, 367, 367, 367, 367, 367, 367,
-
367, 367, 367, 367, 367, 367, 367, 367,
-
367, 367, 367, 367, 367, 367, 367, 1,
-
371, 371, 371, 371, 371, 371, 371, 371,
-
371, 1, 371, 371, 372, 371, 371, 371,
-
371, 371, 371, 371, 371, 371, 371, 371,
-
371, 371, 371, 371, 371, 371, 371, 371,
-
371, 373, 371, 371, 371, 371, 371, 371,
-
371, 371, 371, 371, 371, 371, 371, 371,
-
371, 371, 371, 371, 371, 371, 371, 371,
-
371, 371, 371, 371, 371, 371, 371, 371,
-
371, 371, 371, 371, 371, 371, 371, 371,
-
371, 371, 371, 371, 371, 371, 371, 371,
-
371, 371, 371, 371, 371, 371, 371, 371,
-
371, 371, 371, 374, 371, 371, 371, 371,
-
371, 371, 371, 371, 371, 371, 371, 371,
-
371, 371, 371, 371, 371, 371, 371, 371,
-
371, 371, 371, 371, 371, 371, 371, 371,
-
371, 371, 371, 371, 371, 371, 371, 1,
-
375, 1, 371, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 371, 1, 371, 371, 371, 371, 371,
-
371, 371, 371, 371, 371, 371, 371, 371,
-
371, 371, 371, 371, 371, 371, 371, 371,
-
371, 371, 371, 371, 371, 371, 371, 371,
-
371, 371, 371, 371, 371, 371, 371, 371,
-
371, 371, 371, 371, 371, 371, 371, 371,
-
371, 371, 371, 371, 371, 371, 371, 371,
-
371, 371, 371, 371, 371, 371, 371, 371,
-
371, 371, 371, 371, 371, 371, 371, 371,
-
371, 371, 371, 371, 371, 371, 371, 371,
-
371, 371, 371, 371, 371, 371, 371, 371,
-
371, 371, 371, 371, 371, 371, 371, 371,
-
371, 371, 371, 371, 371, 371, 371, 371,
-
371, 371, 371, 371, 371, 371, 371, 371,
-
371, 371, 371, 371, 371, 371, 371, 371,
-
371, 371, 371, 371, 371, 371, 371, 371,
-
371, 371, 371, 1, 292, 1, 1, 1,
-
376, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 292, 377, 378, 377, 377,
-
377, 377, 377, 379, 1, 377, 377, 1,
-
377, 1, 377, 377, 377, 377, 377, 377,
-
377, 377, 377, 377, 377, 1, 1, 1,
-
377, 1, 377, 1, 377, 377, 377, 377,
-
377, 377, 377, 377, 377, 377, 377, 377,
-
377, 377, 377, 377, 377, 377, 377, 377,
-
377, 377, 377, 377, 377, 377, 1, 1,
-
1, 377, 377, 377, 377, 377, 377, 377,
-
377, 377, 377, 377, 377, 377, 377, 377,
-
377, 377, 377, 377, 377, 377, 377, 377,
-
377, 377, 377, 377, 377, 377, 377, 377,
-
377, 377, 1, 380, 380, 380, 380, 380,
-
380, 380, 380, 278, 1, 380, 380, 381,
-
380, 380, 380, 380, 380, 380, 380, 380,
-
380, 380, 380, 380, 380, 380, 380, 380,
-
380, 380, 278, 380, 1, 380, 380, 380,
-
380, 380, 380, 380, 380, 380, 380, 380,
-
380, 380, 380, 380, 380, 380, 380, 380,
-
380, 380, 380, 380, 380, 380, 380, 380,
-
380, 380, 380, 380, 380, 380, 380, 380,
-
380, 380, 380, 380, 380, 380, 380, 380,
-
380, 380, 380, 380, 380, 380, 380, 380,
-
380, 380, 380, 380, 380, 380, 382, 380,
-
380, 380, 380, 380, 380, 380, 380, 380,
-
380, 380, 380, 380, 380, 380, 380, 380,
-
380, 380, 380, 380, 380, 380, 380, 380,
-
380, 380, 380, 380, 380, 380, 380, 380,
-
380, 380, 1, 380, 380, 380, 380, 380,
-
380, 380, 380, 380, 1, 380, 380, 383,
-
380, 380, 380, 380, 380, 380, 380, 380,
-
380, 380, 380, 380, 380, 380, 380, 380,
-
380, 380, 380, 380, 384, 380, 380, 380,
-
380, 380, 380, 380, 380, 380, 380, 380,
-
380, 380, 380, 380, 380, 380, 380, 380,
-
380, 380, 380, 380, 380, 380, 380, 380,
-
380, 380, 380, 380, 380, 380, 380, 380,
-
380, 380, 380, 380, 380, 380, 380, 380,
-
380, 380, 380, 380, 380, 380, 380, 380,
-
380, 380, 380, 380, 380, 380, 382, 380,
-
380, 380, 380, 380, 380, 380, 380, 380,
-
380, 380, 380, 380, 380, 380, 380, 380,
-
380, 380, 380, 380, 380, 380, 380, 380,
-
380, 380, 380, 380, 380, 380, 380, 380,
-
380, 380, 1, 385, 1, 380, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 380, 1, 386, 1,
-
1, 1, 387, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 386, 384, 278,
-
384, 384, 384, 384, 384, 388, 1, 384,
-
384, 1, 384, 384, 384, 384, 384, 384,
-
384, 384, 384, 384, 384, 384, 384, 1,
-
1, 1, 384, 281, 384, 282, 384, 384,
-
384, 384, 384, 384, 384, 384, 384, 384,
-
384, 384, 384, 384, 384, 384, 384, 384,
-
384, 384, 384, 384, 384, 384, 384, 384,
-
1, 1, 1, 384, 384, 384, 384, 384,
-
384, 384, 384, 384, 384, 384, 384, 384,
-
384, 384, 384, 384, 384, 384, 384, 384,
-
384, 384, 384, 384, 384, 384, 384, 384,
-
384, 384, 384, 384, 1, 389, 1, 1,
-
1, 390, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 389, 1, 1, 1,
-
1, 1, 1, 1, 391, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 20, 1, 287, 1, 392, 1,
-
389, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 389,
-
1, 393, 1, 1, 1, 394, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
393, 1, 1, 1, 1, 1, 1, 1,
-
395, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 293, 1,
-
294, 1, 380, 380, 380, 380, 380, 380,
-
380, 380, 380, 380, 380, 380, 380, 380,
-
380, 380, 380, 380, 380, 380, 380, 380,
-
380, 380, 380, 380, 380, 380, 380, 380,
-
380, 380, 380, 380, 380, 380, 380, 380,
-
380, 380, 380, 380, 380, 380, 380, 380,
-
380, 380, 380, 380, 380, 380, 380, 380,
-
380, 380, 380, 380, 380, 380, 380, 380,
-
380, 380, 380, 380, 380, 380, 380, 380,
-
380, 380, 380, 380, 380, 380, 380, 380,
-
380, 380, 380, 380, 380, 380, 380, 380,
-
380, 380, 380, 380, 380, 380, 380, 380,
-
380, 380, 380, 380, 380, 380, 380, 380,
-
380, 380, 380, 380, 380, 380, 380, 380,
-
380, 380, 380, 380, 380, 380, 380, 380,
-
380, 380, 380, 380, 380, 380, 380, 380,
-
380, 380, 1, 396, 1, 278, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 278, 1, 397, 1,
-
1, 1, 398, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 397, 277, 399,
-
277, 277, 277, 277, 277, 400, 1, 277,
-
277, 1, 277, 384, 277, 277, 277, 277,
-
277, 277, 277, 277, 277, 277, 277, 1,
-
1, 1, 277, 281, 277, 282, 277, 277,
-
277, 277, 277, 277, 277, 277, 277, 277,
-
277, 277, 277, 277, 277, 277, 277, 277,
-
277, 277, 277, 277, 277, 277, 277, 277,
-
1, 1, 1, 277, 277, 277, 277, 277,
-
277, 277, 277, 277, 277, 277, 277, 277,
-
277, 277, 277, 277, 277, 277, 277, 277,
-
277, 277, 277, 277, 277, 277, 277, 277,
-
277, 277, 277, 277, 1, 401, 1, 1,
-
1, 402, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 401, 296, 297, 296,
-
296, 296, 296, 296, 403, 1, 296, 296,
-
1, 296, 1, 296, 296, 296, 296, 296,
-
296, 296, 296, 296, 296, 296, 1, 1,
-
1, 296, 20, 296, 287, 296, 296, 296,
-
296, 296, 296, 296, 296, 296, 296, 296,
-
296, 296, 296, 296, 296, 296, 296, 296,
-
296, 296, 296, 296, 296, 296, 296, 1,
-
1, 1, 296, 296, 296, 296, 296, 296,
-
296, 296, 296, 296, 296, 296, 296, 296,
-
296, 296, 296, 296, 296, 296, 296, 296,
-
296, 296, 296, 296, 296, 296, 296, 296,
-
296, 296, 296, 1, 404, 1, 401, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 401, 1, 405,
-
1, 1, 1, 406, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 405, 377,
-
378, 377, 377, 377, 377, 377, 407, 1,
-
377, 377, 1, 377, 1, 377, 377, 377,
-
377, 377, 377, 377, 377, 377, 377, 377,
-
1, 1, 1, 377, 293, 377, 294, 377,
-
377, 377, 377, 377, 377, 377, 377, 377,
-
377, 377, 377, 377, 377, 377, 377, 377,
-
377, 377, 377, 377, 377, 377, 377, 377,
-
377, 1, 1, 1, 377, 377, 377, 377,
-
377, 377, 377, 377, 377, 377, 377, 377,
-
377, 377, 377, 377, 377, 377, 377, 377,
-
377, 377, 377, 377, 377, 377, 377, 377,
-
377, 377, 377, 377, 377, 1, 408, 408,
-
408, 408, 408, 408, 408, 408, 409, 1,
-
408, 408, 410, 408, 408, 408, 408, 408,
-
408, 408, 408, 408, 408, 408, 408, 408,
-
408, 408, 408, 408, 408, 409, 408, 369,
-
408, 408, 408, 408, 408, 408, 408, 408,
-
408, 408, 408, 408, 408, 408, 408, 408,
-
408, 408, 408, 408, 408, 408, 408, 408,
-
408, 408, 408, 408, 408, 408, 408, 408,
-
408, 408, 408, 408, 408, 408, 408, 408,
-
408, 408, 408, 408, 408, 408, 408, 408,
-
408, 408, 408, 408, 408, 408, 408, 408,
-
408, 411, 408, 408, 408, 408, 408, 408,
-
408, 408, 408, 408, 408, 408, 408, 408,
-
408, 408, 408, 408, 408, 408, 408, 408,
-
408, 408, 408, 408, 408, 408, 408, 408,
-
408, 408, 408, 408, 408, 1, 412, 412,
-
412, 412, 412, 412, 412, 412, 412, 1,
-
412, 412, 413, 412, 412, 412, 412, 412,
-
412, 412, 412, 412, 412, 412, 412, 412,
-
412, 412, 412, 412, 412, 412, 412, 414,
-
412, 412, 412, 412, 412, 412, 412, 412,
-
412, 412, 412, 412, 412, 412, 412, 412,
-
412, 412, 412, 412, 412, 412, 412, 412,
-
412, 412, 412, 412, 412, 412, 412, 412,
-
412, 412, 412, 412, 412, 412, 412, 412,
-
412, 412, 412, 412, 412, 412, 412, 412,
-
412, 412, 412, 412, 412, 412, 412, 412,
-
412, 415, 412, 412, 412, 412, 412, 412,
-
412, 412, 412, 412, 412, 412, 412, 412,
-
412, 412, 412, 412, 412, 412, 412, 412,
-
412, 412, 412, 412, 412, 412, 412, 412,
-
412, 412, 412, 412, 412, 1, 416, 1,
-
412, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 412,
-
1, 275, 1, 1, 1, 276, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
275, 384, 278, 384, 384, 384, 384, 384,
-
279, 1, 384, 384, 1, 384, 280, 384,
-
384, 384, 384, 384, 384, 384, 384, 384,
-
384, 384, 1, 1, 1, 384, 281, 384,
-
282, 384, 384, 384, 384, 384, 384, 384,
-
384, 384, 384, 384, 384, 384, 384, 384,
-
384, 384, 384, 384, 384, 384, 384, 384,
-
384, 384, 384, 1, 1, 1, 384, 384,
-
384, 384, 384, 384, 384, 384, 384, 384,
-
384, 384, 384, 384, 384, 384, 384, 384,
-
384, 384, 384, 384, 384, 384, 384, 384,
-
384, 384, 384, 384, 384, 384, 384, 1,
-
412, 412, 412, 412, 412, 412, 412, 412,
-
412, 412, 412, 412, 412, 412, 412, 412,
-
412, 412, 412, 412, 412, 412, 412, 412,
-
412, 412, 412, 412, 412, 412, 412, 412,
-
412, 412, 412, 412, 412, 412, 412, 412,
-
412, 412, 412, 412, 412, 412, 412, 412,
-
412, 412, 412, 412, 412, 412, 412, 412,
-
412, 412, 412, 412, 412, 412, 412, 412,
-
412, 412, 412, 412, 412, 412, 412, 412,
-
412, 412, 412, 412, 412, 412, 412, 412,
-
412, 412, 412, 412, 412, 412, 412, 412,
-
412, 412, 412, 412, 412, 412, 412, 412,
-
412, 412, 412, 412, 412, 412, 412, 412,
-
412, 412, 412, 412, 412, 412, 412, 412,
-
412, 412, 412, 412, 412, 412, 412, 412,
-
412, 412, 412, 412, 412, 412, 412, 412,
-
1, 412, 412, 412, 412, 412, 412, 412,
-
412, 417, 1, 412, 412, 418, 412, 412,
-
412, 412, 412, 412, 412, 412, 412, 412,
-
412, 412, 412, 412, 412, 412, 412, 412,
-
417, 412, 373, 412, 412, 412, 412, 412,
-
412, 412, 412, 412, 412, 412, 412, 412,
-
412, 412, 412, 412, 412, 412, 412, 412,
-
412, 412, 412, 412, 412, 412, 412, 412,
-
412, 412, 412, 412, 412, 412, 412, 412,
-
412, 412, 412, 412, 412, 412, 412, 412,
-
412, 412, 412, 412, 412, 412, 412, 412,
-
412, 412, 412, 412, 415, 412, 412, 412,
-
412, 412, 412, 412, 412, 412, 412, 412,
-
412, 412, 412, 412, 412, 412, 412, 412,
-
412, 412, 412, 412, 412, 412, 412, 412,
-
412, 412, 412, 412, 412, 412, 412, 412,
-
1, 419, 1, 417, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 417, 1, 420, 420, 420, 420,
-
420, 420, 420, 420, 421, 1, 420, 420,
-
422, 420, 420, 420, 420, 420, 420, 420,
-
420, 420, 420, 420, 420, 420, 420, 420,
-
420, 420, 420, 421, 420, 423, 420, 420,
-
420, 420, 420, 420, 420, 420, 420, 420,
-
420, 420, 420, 420, 420, 420, 420, 420,
-
420, 420, 420, 420, 420, 420, 420, 420,
-
420, 420, 420, 420, 420, 420, 420, 420,
-
420, 420, 420, 420, 420, 420, 420, 420,
-
420, 420, 420, 420, 420, 420, 420, 420,
-
420, 420, 420, 420, 420, 420, 420, 424,
-
420, 420, 420, 420, 420, 420, 420, 420,
-
420, 420, 420, 420, 420, 420, 420, 420,
-
420, 420, 420, 420, 420, 420, 420, 420,
-
420, 420, 420, 420, 420, 420, 420, 420,
-
420, 420, 420, 1, 425, 425, 425, 425,
-
425, 425, 425, 425, 425, 1, 425, 425,
-
426, 425, 425, 425, 425, 425, 425, 425,
-
425, 425, 425, 425, 425, 425, 425, 425,
-
425, 425, 425, 425, 425, 427, 425, 425,
-
425, 425, 425, 425, 425, 425, 425, 425,
-
425, 425, 425, 425, 425, 425, 425, 425,
-
425, 425, 425, 425, 425, 425, 425, 425,
-
425, 425, 425, 425, 425, 425, 425, 425,
-
425, 425, 425, 425, 425, 425, 425, 425,
-
425, 425, 425, 425, 425, 425, 425, 425,
-
425, 425, 425, 425, 425, 425, 425, 428,
-
425, 425, 425, 425, 425, 425, 425, 425,
-
425, 425, 425, 425, 425, 425, 425, 425,
-
425, 425, 425, 425, 425, 425, 425, 425,
-
425, 425, 425, 425, 425, 425, 425, 425,
-
425, 425, 425, 1, 429, 1, 425, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 425, 1, 430,
-
1, 1, 1, 431, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 430, 384,
-
278, 384, 384, 384, 384, 384, 432, 1,
-
384, 384, 1, 384, 280, 384, 384, 384,
-
384, 384, 384, 384, 384, 384, 384, 384,
-
1, 1, 1, 384, 281, 384, 433, 384,
-
384, 384, 384, 384, 384, 384, 384, 384,
-
384, 384, 384, 384, 384, 384, 384, 384,
-
384, 384, 384, 384, 384, 384, 384, 384,
-
384, 1, 1, 1, 384, 384, 384, 384,
-
384, 384, 384, 384, 384, 384, 384, 384,
-
384, 384, 384, 384, 384, 384, 384, 384,
-
384, 384, 384, 384, 384, 384, 384, 384,
-
384, 384, 384, 384, 384, 1, 434, 1,
-
1, 1, 435, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 434, 1, 1,
-
1, 1, 1, 1, 1, 436, 1, 1,
-
1, 1, 1, 286, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 20, 1, 437, 1, 438,
-
1, 434, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
434, 1, 439, 1, 1, 1, 440, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 439, 1, 1, 1, 1, 1, 1,
-
1, 441, 1, 1, 1, 1, 1, 292,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 293,
-
1, 442, 1, 425, 425, 425, 425, 425,
-
425, 425, 425, 425, 425, 425, 425, 425,
-
425, 425, 425, 425, 425, 425, 425, 425,
-
425, 425, 425, 425, 425, 425, 425, 425,
-
425, 425, 425, 425, 425, 425, 425, 425,
-
425, 425, 425, 425, 425, 425, 425, 425,
-
425, 425, 425, 425, 425, 425, 425, 425,
-
425, 425, 425, 425, 425, 425, 425, 425,
-
425, 425, 425, 425, 425, 425, 425, 425,
-
425, 425, 425, 425, 425, 425, 425, 425,
-
425, 425, 425, 425, 425, 425, 425, 425,
-
425, 425, 425, 425, 425, 425, 425, 425,
-
425, 425, 425, 425, 425, 425, 425, 425,
-
425, 425, 425, 425, 425, 425, 425, 425,
-
425, 425, 425, 425, 425, 425, 425, 425,
-
425, 425, 425, 425, 425, 425, 425, 425,
-
425, 425, 425, 1, 425, 425, 425, 425,
-
425, 425, 425, 425, 443, 1, 425, 425,
-
444, 425, 425, 425, 425, 425, 425, 425,
-
425, 425, 425, 425, 425, 425, 425, 425,
-
425, 425, 425, 443, 425, 445, 425, 425,
-
425, 425, 425, 425, 425, 425, 425, 425,
-
425, 425, 425, 425, 425, 425, 425, 425,
-
425, 425, 425, 425, 425, 425, 425, 425,
-
425, 425, 425, 425, 425, 425, 425, 425,
-
425, 425, 425, 425, 425, 425, 425, 425,
-
425, 425, 425, 425, 425, 425, 425, 425,
-
425, 425, 425, 425, 425, 425, 425, 428,
-
425, 425, 425, 425, 425, 425, 425, 425,
-
425, 425, 425, 425, 425, 425, 425, 425,
-
425, 425, 425, 425, 425, 425, 425, 425,
-
425, 425, 425, 425, 425, 425, 425, 425,
-
425, 425, 425, 1, 446, 1, 443, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 443, 1, 447,
-
1, 1, 1, 448, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 447, 1,
-
1, 1, 1, 1, 1, 1, 449, 1,
-
1, 1, 1, 1, 286, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 20, 1, 450, 1,
-
451, 1, 447, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 447, 1, 452, 1, 1, 1, 453,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 452, 1, 1, 1, 1, 1,
-
1, 1, 454, 1, 1, 1, 1, 1,
-
292, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
293, 1, 455, 1, 456, 1, 1, 1,
-
457, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 456, 458, 459, 458, 458,
-
458, 458, 458, 460, 1, 458, 458, 461,
-
458, 462, 458, 458, 458, 458, 458, 458,
-
458, 458, 458, 458, 458, 1, 1, 1,
-
458, 1, 458, 463, 458, 458, 458, 458,
-
458, 458, 458, 458, 458, 458, 458, 458,
-
458, 458, 458, 458, 458, 458, 458, 458,
-
458, 458, 458, 458, 458, 458, 1, 1,
-
1, 458, 458, 458, 458, 458, 458, 458,
-
458, 458, 458, 458, 458, 458, 458, 458,
-
458, 458, 458, 458, 458, 458, 458, 458,
-
458, 458, 458, 458, 458, 458, 458, 458,
-
458, 458, 1, 272, 1, 1, 1, 464,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 272, 1, 1, 1, 1, 1,
-
1, 1, 465, 1, 1, 1, 272, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 273, 1, 466, 1, 272, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 272, 1, 461,
-
1, 1, 1, 467, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 461, 1,
-
1, 1, 1, 1, 1, 1, 468, 1,
-
1, 1, 461, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 463, 1,
-
469, 1, 1, 1, 470, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 469,
-
471, 472, 471, 471, 471, 471, 471, 473,
-
1, 471, 471, 1, 471, 474, 471, 471,
-
471, 471, 471, 471, 471, 471, 471, 471,
-
471, 1, 1, 1, 471, 1, 471, 1,
-
471, 471, 471, 471, 471, 471, 471, 471,
-
471, 471, 471, 471, 471, 471, 471, 471,
-
471, 471, 471, 471, 471, 471, 471, 471,
-
471, 471, 475, 1, 1, 471, 471, 471,
-
471, 471, 471, 471, 471, 471, 471, 471,
-
471, 471, 471, 471, 471, 471, 471, 471,
-
471, 471, 471, 471, 471, 471, 471, 471,
-
471, 471, 471, 471, 471, 471, 1, 476,
-
1, 1, 1, 477, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 476, 478,
-
479, 478, 478, 478, 478, 478, 480, 1,
-
478, 478, 1, 478, 481, 478, 478, 478,
-
478, 478, 478, 478, 478, 478, 478, 478,
-
1, 1, 1, 478, 1, 478, 1, 478,
-
478, 478, 478, 478, 478, 478, 478, 478,
-
478, 478, 478, 478, 478, 478, 478, 478,
-
478, 478, 478, 478, 478, 478, 478, 478,
-
478, 482, 1, 1, 478, 478, 478, 478,
-
478, 478, 478, 478, 478, 478, 478, 478,
-
478, 478, 478, 478, 478, 478, 478, 478,
-
478, 478, 478, 478, 478, 478, 478, 478,
-
478, 478, 478, 478, 478, 1, 483, 1,
-
476, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 476,
-
1, 484, 1, 1, 1, 485, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
484, 478, 479, 478, 478, 478, 478, 478,
-
486, 1, 478, 478, 487, 478, 488, 478,
-
478, 478, 478, 478, 478, 478, 478, 478,
-
478, 478, 489, 1, 1, 478, 1, 478,
-
1, 478, 478, 478, 478, 478, 478, 478,
-
478, 478, 478, 478, 478, 478, 478, 478,
-
478, 478, 478, 478, 478, 478, 478, 478,
-
478, 478, 478, 1, 1, 1, 478, 478,
-
478, 478, 478, 478, 478, 478, 478, 478,
-
478, 478, 478, 478, 478, 478, 478, 478,
-
478, 478, 478, 478, 478, 478, 478, 478,
-
478, 478, 478, 478, 478, 478, 478, 1,
-
484, 1, 1, 1, 485, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 484,
-
1, 1, 1, 1, 1, 1, 1, 486,
-
1, 1, 1, 487, 1, 490, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 489, 1, 491, 1, 484, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 484, 1, 492, 1,
-
1, 1, 493, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 492, 1, 1,
-
1, 1, 1, 1, 1, 494, 1, 1,
-
1, 495, 1, 496, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 497,
-
1, 498, 1, 1, 1, 499, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
498, 1, 1, 1, 1, 1, 1, 1,
-
500, 1, 1, 1, 498, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 501, 1, 1, 1, 1, 1,
-
273, 1, 502, 1, 498, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 498, 1, 503, 1, 1,
-
1, 504, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 503, 1, 1, 1,
-
1, 1, 1, 1, 505, 1, 1, 1,
-
503, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 506, 1,
-
1, 1, 1, 1, 463, 1, 507, 1,
-
1, 1, 508, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 507, 509, 510,
-
509, 509, 509, 509, 509, 511, 1, 509,
-
509, 1, 509, 512, 509, 509, 509, 509,
-
509, 509, 509, 509, 509, 509, 509, 1,
-
1, 1, 509, 1, 509, 1, 509, 509,
-
509, 509, 509, 509, 509, 509, 509, 509,
-
509, 509, 509, 509, 509, 509, 509, 509,
-
509, 509, 509, 509, 509, 509, 509, 509,
-
1, 1, 1, 509, 509, 509, 509, 509,
-
509, 509, 509, 509, 509, 509, 509, 509,
-
509, 509, 509, 509, 509, 509, 509, 509,
-
509, 509, 509, 509, 509, 509, 509, 509,
-
509, 509, 509, 509, 1, 513, 1, 1,
-
1, 514, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 513, 263, 264, 263,
-
263, 263, 263, 263, 515, 1, 263, 263,
-
1, 263, 267, 263, 263, 263, 263, 263,
-
263, 263, 263, 263, 263, 263, 1, 1,
-
1, 263, 1, 263, 1, 263, 263, 263,
-
263, 263, 263, 263, 263, 263, 263, 263,
-
263, 263, 263, 263, 263, 263, 263, 263,
-
263, 263, 263, 263, 263, 263, 263, 1,
-
1, 1, 263, 263, 263, 263, 263, 263,
-
263, 263, 263, 263, 263, 263, 263, 263,
-
263, 263, 263, 263, 263, 263, 263, 263,
-
263, 263, 263, 263, 263, 263, 263, 263,
-
263, 263, 263, 1, 516, 1, 513, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 513, 1, 517,
-
1, 1, 1, 518, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 517, 458,
-
459, 458, 458, 458, 458, 458, 519, 1,
-
458, 458, 1, 458, 462, 458, 458, 458,
-
458, 458, 458, 458, 458, 458, 458, 458,
-
1, 1, 1, 458, 1, 458, 1, 458,
-
458, 458, 458, 458, 458, 458, 458, 458,
-
458, 458, 458, 458, 458, 458, 458, 458,
-
458, 458, 458, 458, 458, 458, 458, 458,
-
458, 1, 1, 1, 458, 458, 458, 458,
-
458, 458, 458, 458, 458, 458, 458, 458,
-
458, 458, 458, 458, 458, 458, 458, 458,
-
458, 458, 458, 458, 458, 458, 458, 458,
-
458, 458, 458, 458, 458, 1, 384, 278,
-
384, 384, 384, 384, 384, 1, 1, 384,
-
384, 1, 384, 520, 384, 384, 384, 384,
-
384, 384, 384, 384, 384, 384, 384, 1,
-
1, 1, 384, 1, 384, 1, 384, 384,
-
384, 384, 384, 384, 384, 384, 384, 384,
-
384, 384, 384, 384, 384, 384, 384, 384,
-
384, 384, 384, 384, 384, 384, 384, 384,
-
1, 1, 1, 384, 384, 384, 384, 384,
-
384, 384, 384, 384, 384, 384, 384, 384,
-
384, 384, 384, 384, 384, 384, 384, 384,
-
384, 384, 384, 384, 384, 384, 384, 384,
-
384, 384, 384, 384, 1, 490, 1, 1,
-
1, 521, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 490, 522, 1, 522,
-
522, 522, 522, 522, 523, 1, 522, 522,
-
1, 522, 1, 522, 522, 522, 522, 522,
-
522, 522, 522, 522, 522, 522, 1, 1,
-
1, 522, 1, 522, 1, 522, 522, 522,
-
522, 522, 522, 522, 522, 522, 522, 522,
-
522, 522, 522, 522, 522, 522, 522, 522,
-
522, 522, 522, 522, 522, 522, 522, 1,
-
1, 1, 522, 522, 522, 522, 522, 522,
-
522, 522, 522, 522, 522, 522, 522, 522,
-
522, 522, 522, 522, 522, 522, 522, 522,
-
522, 522, 522, 522, 522, 522, 522, 522,
-
522, 522, 522, 1, 524, 1, 490, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 490, 1, 484,
-
1, 1, 1, 485, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 484, 522,
-
1, 522, 522, 522, 522, 522, 486, 1,
-
522, 522, 487, 522, 490, 522, 522, 522,
-
522, 522, 522, 522, 522, 522, 522, 522,
-
489, 1, 1, 522, 1, 522, 1, 522,
-
522, 522, 522, 522, 522, 522, 522, 522,
-
522, 522, 522, 522, 522, 522, 522, 522,
-
522, 522, 522, 522, 522, 522, 522, 522,
-
522, 1, 1, 1, 522, 522, 522, 522,
-
522, 522, 522, 522, 522, 522, 522, 522,
-
522, 522, 522, 522, 522, 522, 522, 522,
-
522, 522, 522, 522, 522, 522, 522, 522,
-
522, 522, 522, 522, 522, 1, 496, 1,
-
1, 1, 525, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 496, 526, 1,
-
526, 526, 526, 526, 526, 527, 1, 526,
-
526, 1, 526, 1, 526, 526, 526, 526,
-
526, 526, 526, 526, 526, 526, 526, 1,
-
1, 1, 526, 1, 526, 1, 526, 526,
-
526, 526, 526, 526, 526, 526, 526, 526,
-
526, 526, 526, 526, 526, 526, 526, 526,
-
526, 526, 526, 526, 526, 526, 526, 526,
-
1, 1, 1, 526, 526, 526, 526, 526,
-
526, 526, 526, 526, 526, 526, 526, 526,
-
526, 526, 526, 526, 526, 526, 526, 526,
-
526, 526, 526, 526, 526, 526, 526, 526,
-
526, 526, 526, 526, 1, 528, 528, 528,
-
528, 528, 528, 528, 528, 479, 1, 528,
-
528, 529, 528, 528, 528, 528, 528, 528,
-
528, 528, 528, 528, 528, 528, 528, 528,
-
528, 528, 528, 528, 479, 528, 1, 528,
-
528, 528, 528, 528, 528, 528, 528, 528,
-
528, 528, 528, 528, 528, 528, 528, 528,
-
528, 528, 528, 528, 528, 528, 528, 528,
-
528, 528, 528, 528, 528, 528, 528, 528,
-
528, 528, 528, 528, 528, 528, 528, 528,
-
528, 528, 528, 528, 528, 528, 528, 528,
-
528, 528, 528, 528, 528, 528, 528, 528,
-
530, 528, 528, 528, 528, 528, 528, 528,
-
528, 528, 528, 528, 528, 528, 528, 528,
-
528, 528, 528, 528, 528, 528, 528, 528,
-
528, 528, 528, 528, 528, 528, 528, 528,
-
528, 528, 528, 528, 1, 528, 528, 528,
-
528, 528, 528, 528, 528, 528, 1, 528,
-
528, 531, 528, 528, 528, 528, 528, 528,
-
528, 528, 528, 528, 528, 528, 528, 528,
-
528, 528, 528, 528, 528, 528, 532, 528,
-
528, 528, 528, 528, 528, 528, 528, 528,
-
528, 528, 528, 528, 528, 528, 528, 528,
-
528, 528, 528, 528, 528, 528, 528, 528,
-
528, 528, 528, 528, 528, 528, 528, 528,
-
528, 528, 528, 528, 528, 528, 528, 528,
-
528, 528, 528, 528, 528, 528, 528, 528,
-
528, 528, 528, 528, 528, 528, 528, 528,
-
530, 528, 528, 528, 528, 528, 528, 528,
-
528, 528, 528, 528, 528, 528, 528, 528,
-
528, 528, 528, 528, 528, 528, 528, 528,
-
528, 528, 528, 528, 528, 528, 528, 528,
-
528, 528, 528, 528, 1, 533, 1, 528,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 528, 1,
-
534, 1, 1, 1, 535, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 534,
-
532, 479, 532, 532, 532, 532, 532, 536,
-
1, 532, 532, 487, 532, 481, 532, 532,
-
532, 532, 532, 532, 532, 532, 532, 532,
-
532, 489, 1, 1, 532, 1, 532, 1,
-
532, 532, 532, 532, 532, 532, 532, 532,
-
532, 532, 532, 532, 532, 532, 532, 532,
-
532, 532, 532, 532, 532, 532, 532, 532,
-
532, 532, 1, 1, 1, 532, 532, 532,
-
532, 532, 532, 532, 532, 532, 532, 532,
-
532, 532, 532, 532, 532, 532, 532, 532,
-
532, 532, 532, 532, 532, 532, 532, 532,
-
532, 532, 532, 532, 532, 532, 1, 534,
-
1, 1, 1, 535, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 534, 1,
-
1, 1, 1, 1, 1, 1, 536, 1,
-
1, 1, 487, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
489, 1, 537, 1, 534, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 534, 1, 538, 1, 1,
-
1, 539, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 538, 1, 1, 1,
-
1, 1, 1, 1, 540, 1, 1, 1,
-
495, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 497, 1,
-
532, 479, 532, 532, 532, 532, 532, 1,
-
1, 532, 532, 1, 532, 481, 532, 532,
-
532, 532, 532, 532, 532, 532, 532, 532,
-
532, 1, 1, 1, 532, 1, 532, 1,
-
532, 532, 532, 532, 532, 532, 532, 532,
-
532, 532, 532, 532, 532, 532, 532, 532,
-
532, 532, 532, 532, 532, 532, 532, 532,
-
532, 532, 1, 1, 1, 532, 532, 532,
-
532, 532, 532, 532, 532, 532, 532, 532,
-
532, 532, 532, 532, 532, 532, 532, 532,
-
532, 532, 532, 532, 532, 532, 532, 532,
-
532, 532, 532, 532, 532, 532, 1, 528,
-
528, 528, 528, 528, 528, 528, 528, 528,
-
528, 528, 528, 528, 528, 528, 528, 528,
-
528, 528, 528, 528, 528, 528, 528, 528,
-
528, 528, 528, 528, 528, 528, 528, 528,
-
528, 528, 528, 528, 528, 528, 528, 528,
-
528, 528, 528, 528, 528, 528, 528, 528,
-
528, 528, 528, 528, 528, 528, 528, 528,
-
528, 528, 528, 528, 528, 528, 528, 528,
-
528, 528, 528, 528, 528, 528, 528, 528,
-
528, 528, 528, 528, 528, 528, 528, 528,
-
528, 528, 528, 528, 528, 528, 528, 528,
-
528, 528, 528, 528, 528, 528, 528, 528,
-
528, 528, 528, 528, 528, 528, 528, 528,
-
528, 528, 528, 528, 528, 528, 528, 528,
-
528, 528, 528, 528, 528, 528, 528, 528,
-
528, 528, 528, 528, 528, 528, 528, 1,
-
541, 1, 479, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 479, 1, 490, 1, 1, 1, 521,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 490, 478, 479, 478, 478, 478,
-
478, 478, 523, 1, 478, 478, 1, 478,
-
481, 478, 478, 478, 478, 478, 478, 478,
-
478, 478, 478, 478, 1, 1, 1, 478,
-
1, 478, 1, 478, 478, 478, 478, 478,
-
478, 478, 478, 478, 478, 478, 478, 478,
-
478, 478, 478, 478, 478, 478, 478, 478,
-
478, 478, 478, 478, 478, 1, 1, 1,
-
478, 478, 478, 478, 478, 478, 478, 478,
-
478, 478, 478, 478, 478, 478, 478, 478,
-
478, 478, 478, 478, 478, 478, 478, 478,
-
478, 478, 478, 478, 478, 478, 478, 478,
-
478, 1, 542, 1, 1, 1, 543, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 542, 544, 545, 544, 544, 544, 544,
-
544, 546, 1, 544, 544, 1, 544, 547,
-
544, 544, 544, 544, 544, 544, 544, 544,
-
544, 544, 544, 1, 1, 1, 544, 1,
-
544, 1, 544, 544, 544, 544, 544, 544,
-
544, 544, 544, 544, 544, 544, 544, 544,
-
544, 544, 544, 544, 544, 544, 544, 544,
-
544, 544, 544, 544, 548, 1, 1, 544,
-
544, 544, 544, 544, 544, 544, 544, 544,
-
544, 544, 544, 544, 544, 544, 544, 544,
-
544, 544, 544, 544, 544, 544, 544, 544,
-
544, 544, 544, 544, 544, 544, 544, 544,
-
1, 482, 482, 482, 482, 482, 482, 482,
-
482, 482, 1, 482, 482, 549, 482, 482,
-
482, 482, 482, 482, 482, 482, 482, 482,
-
482, 482, 482, 482, 482, 482, 482, 482,
-
482, 482, 482, 482, 482, 482, 482, 482,
-
482, 482, 482, 482, 482, 482, 482, 482,
-
482, 482, 482, 482, 482, 482, 482, 482,
-
482, 482, 482, 482, 482, 482, 482, 482,
-
482, 482, 482, 482, 482, 482, 482, 482,
-
482, 482, 482, 482, 482, 482, 482, 482,
-
482, 482, 482, 482, 482, 482, 482, 482,
-
482, 482, 482, 1, 550, 534, 482, 482,
-
482, 482, 482, 482, 482, 482, 482, 482,
-
482, 482, 482, 482, 482, 482, 482, 482,
-
482, 482, 482, 482, 482, 482, 482, 482,
-
482, 482, 482, 482, 482, 482, 482, 482,
-
1, 551, 1, 482, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 482, 1, 482, 482, 482, 482,
-
482, 482, 482, 482, 482, 482, 482, 482,
-
482, 482, 482, 482, 482, 482, 482, 482,
-
482, 482, 482, 482, 482, 482, 482, 482,
-
482, 482, 482, 482, 482, 482, 482, 482,
-
482, 482, 482, 482, 482, 482, 482, 482,
-
482, 482, 482, 482, 482, 482, 482, 482,
-
482, 482, 482, 482, 482, 482, 482, 482,
-
482, 482, 482, 482, 482, 482, 482, 482,
-
482, 482, 482, 482, 482, 482, 482, 482,
-
482, 482, 482, 482, 482, 482, 482, 482,
-
482, 482, 482, 482, 482, 482, 482, 482,
-
482, 482, 482, 482, 482, 482, 482, 482,
-
482, 482, 482, 482, 482, 482, 482, 482,
-
482, 482, 482, 482, 482, 482, 482, 482,
-
482, 482, 482, 482, 482, 482, 482, 482,
-
482, 482, 482, 482, 1, 552, 1, 1,
-
1, 553, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 552, 554, 555, 554,
-
554, 554, 554, 554, 556, 1, 554, 554,
-
1, 554, 557, 554, 554, 554, 554, 554,
-
554, 554, 554, 554, 554, 554, 1, 1,
-
1, 554, 1, 554, 1, 554, 554, 554,
-
554, 554, 554, 554, 554, 554, 554, 554,
-
554, 554, 554, 554, 554, 554, 554, 554,
-
554, 554, 554, 554, 554, 554, 554, 18,
-
1, 1, 554, 554, 554, 554, 554, 554,
-
554, 554, 554, 554, 554, 554, 554, 554,
-
554, 554, 554, 554, 554, 554, 554, 554,
-
554, 554, 554, 554, 554, 554, 554, 554,
-
554, 554, 554, 1, 558, 1, 1, 1,
-
559, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 558, 560, 561, 560, 560,
-
560, 560, 560, 562, 1, 560, 560, 1,
-
560, 563, 560, 560, 560, 560, 560, 560,
-
560, 560, 560, 560, 560, 1, 1, 1,
-
560, 1, 560, 1, 560, 560, 560, 560,
-
560, 560, 560, 560, 560, 560, 560, 560,
-
560, 560, 560, 560, 560, 560, 560, 560,
-
560, 560, 560, 560, 560, 560, 564, 1,
-
1, 560, 560, 560, 560, 560, 560, 560,
-
560, 560, 560, 560, 560, 560, 560, 560,
-
560, 560, 560, 560, 560, 560, 560, 560,
-
560, 560, 560, 560, 560, 560, 560, 560,
-
560, 560, 1, 565, 1, 558, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 558, 1, 566, 1,
-
1, 1, 567, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 566, 568, 569,
-
568, 568, 568, 568, 568, 570, 1, 568,
-
568, 1, 568, 571, 568, 568, 568, 568,
-
568, 568, 568, 568, 568, 568, 568, 1,
-
572, 573, 568, 1, 568, 1, 568, 568,
-
568, 568, 568, 568, 568, 568, 568, 568,
-
568, 568, 568, 568, 568, 568, 568, 568,
-
568, 568, 568, 568, 568, 568, 568, 568,
-
574, 1, 1, 568, 568, 568, 568, 568,
-
568, 568, 568, 568, 568, 568, 568, 568,
-
568, 568, 568, 568, 568, 568, 568, 568,
-
568, 568, 568, 568, 568, 568, 568, 568,
-
568, 568, 568, 568, 1, 566, 1, 1,
-
1, 567, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 566, 575, 576, 575,
-
575, 575, 575, 575, 570, 1, 575, 575,
-
1, 575, 577, 575, 575, 575, 575, 575,
-
575, 575, 575, 575, 575, 575, 1, 572,
-
578, 575, 1, 575, 1, 575, 575, 575,
-
575, 575, 575, 575, 575, 575, 575, 575,
-
575, 575, 575, 575, 575, 575, 575, 575,
-
575, 575, 575, 575, 575, 575, 575, 574,
-
1, 1, 575, 575, 575, 575, 575, 575,
-
575, 575, 575, 575, 575, 575, 575, 575,
-
575, 575, 575, 575, 575, 575, 575, 575,
-
575, 575, 575, 575, 575, 575, 575, 575,
-
575, 575, 575, 1, 579, 1, 580, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 580, 1, 581,
-
1, 1, 1, 582, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 581, 583,
-
584, 583, 583, 583, 583, 583, 585, 1,
-
583, 583, 1, 583, 586, 583, 583, 583,
-
583, 583, 583, 583, 583, 583, 583, 583,
-
1, 587, 588, 583, 1, 583, 1, 583,
-
583, 583, 583, 583, 583, 583, 583, 583,
-
583, 583, 583, 583, 583, 583, 583, 583,
-
583, 583, 583, 583, 583, 583, 583, 583,
-
583, 589, 1, 1, 583, 583, 583, 583,
-
583, 583, 583, 583, 583, 583, 583, 583,
-
583, 583, 583, 583, 583, 583, 583, 583,
-
583, 583, 583, 583, 583, 583, 583, 583,
-
583, 583, 583, 583, 583, 1, 590, 1,
-
1, 1, 591, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 590, 592, 593,
-
592, 592, 592, 592, 592, 594, 1, 592,
-
592, 1, 592, 90, 592, 592, 592, 592,
-
592, 592, 592, 592, 592, 592, 592, 1,
-
1, 1, 592, 1, 592, 1, 592, 592,
-
592, 592, 592, 592, 592, 592, 592, 592,
-
592, 592, 592, 592, 592, 592, 592, 592,
-
592, 592, 592, 592, 592, 592, 592, 592,
-
1, 1, 1, 592, 592, 592, 592, 592,
-
592, 592, 592, 592, 592, 592, 592, 592,
-
592, 592, 592, 592, 592, 592, 592, 592,
-
592, 592, 592, 592, 592, 592, 592, 592,
-
592, 592, 592, 592, 1, 590, 1, 1,
-
1, 591, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 590, 595, 1, 595,
-
595, 595, 595, 595, 594, 1, 595, 595,
-
1, 595, 1, 595, 595, 595, 595, 595,
-
595, 595, 595, 595, 595, 595, 1, 1,
-
1, 595, 1, 595, 1, 595, 595, 595,
-
595, 595, 595, 595, 595, 595, 595, 595,
-
595, 595, 595, 595, 595, 595, 595, 595,
-
595, 595, 595, 595, 595, 595, 595, 1,
-
1, 1, 595, 595, 595, 595, 595, 595,
-
595, 595, 595, 595, 595, 595, 595, 595,
-
595, 595, 595, 595, 595, 595, 595, 595,
-
595, 595, 595, 595, 595, 595, 595, 595,
-
595, 595, 595, 1, 596, 1, 590, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 590, 1, 566,
-
1, 1, 1, 567, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 566, 597,
-
598, 597, 597, 597, 597, 597, 570, 1,
-
597, 597, 1, 597, 599, 597, 597, 597,
-
597, 597, 597, 597, 597, 597, 597, 597,
-
1, 572, 573, 597, 1, 597, 1, 597,
-
597, 597, 597, 597, 597, 597, 597, 597,
-
597, 597, 597, 597, 597, 597, 597, 597,
-
597, 597, 597, 597, 597, 597, 597, 597,
-
597, 574, 1, 1, 597, 597, 597, 597,
-
597, 597, 597, 597, 597, 597, 597, 597,
-
597, 597, 597, 597, 597, 597, 597, 597,
-
597, 597, 597, 597, 597, 597, 597, 597,
-
597, 597, 597, 597, 597, 1, 566, 1,
-
1, 1, 567, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 566, 597, 598,
-
597, 597, 597, 597, 597, 570, 1, 597,
-
597, 1, 597, 599, 597, 597, 597, 597,
-
597, 597, 597, 597, 597, 597, 597, 1,
-
600, 573, 597, 1, 597, 29, 597, 597,
-
597, 597, 597, 597, 597, 597, 597, 597,
-
597, 597, 597, 597, 597, 597, 597, 597,
-
597, 597, 597, 597, 597, 597, 597, 597,
-
574, 1, 1, 597, 597, 597, 597, 597,
-
597, 597, 597, 597, 597, 597, 597, 597,
-
597, 597, 597, 597, 597, 597, 597, 597,
-
597, 597, 597, 597, 597, 597, 597, 597,
-
597, 597, 597, 597, 1, 564, 564, 564,
-
564, 564, 564, 564, 564, 564, 1, 564,
-
564, 601, 564, 564, 564, 564, 564, 564,
-
564, 564, 564, 564, 564, 564, 564, 564,
-
564, 564, 564, 564, 564, 564, 564, 564,
-
564, 564, 564, 564, 564, 564, 564, 564,
-
564, 564, 564, 564, 564, 564, 564, 564,
-
564, 564, 564, 564, 564, 564, 564, 564,
-
564, 564, 564, 564, 564, 564, 564, 564,
-
564, 564, 564, 564, 564, 564, 564, 564,
-
564, 564, 564, 564, 564, 564, 564, 564,
-
564, 564, 564, 564, 564, 564, 564, 1,
-
602, 603, 564, 564, 564, 564, 564, 564,
-
564, 564, 564, 564, 564, 564, 564, 564,
-
564, 564, 564, 564, 564, 564, 564, 564,
-
564, 564, 564, 564, 564, 564, 564, 564,
-
564, 564, 564, 564, 1, 604, 1, 564,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 564, 1,
-
564, 564, 564, 564, 564, 564, 564, 564,
-
564, 564, 564, 564, 564, 564, 564, 564,
-
564, 564, 564, 564, 564, 564, 564, 564,
-
564, 564, 564, 564, 564, 564, 564, 564,
-
564, 564, 564, 564, 564, 564, 564, 564,
-
564, 564, 564, 564, 564, 564, 564, 564,
-
564, 564, 564, 564, 564, 564, 564, 564,
-
564, 564, 564, 564, 564, 564, 564, 564,
-
564, 564, 564, 564, 564, 564, 564, 564,
-
564, 564, 564, 564, 564, 564, 564, 564,
-
564, 564, 564, 564, 564, 564, 564, 564,
-
564, 564, 564, 564, 564, 564, 564, 564,
-
564, 564, 564, 564, 564, 564, 564, 564,
-
564, 564, 564, 564, 564, 564, 564, 564,
-
564, 564, 564, 564, 564, 564, 564, 564,
-
564, 564, 564, 564, 564, 564, 564, 564,
-
1, 605, 1, 1, 1, 606, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
605, 607, 598, 607, 607, 607, 607, 607,
-
608, 1, 607, 607, 1, 607, 609, 607,
-
607, 607, 607, 607, 607, 607, 607, 607,
-
607, 607, 1, 572, 573, 607, 1, 607,
-
1, 607, 607, 607, 607, 607, 607, 607,
-
607, 607, 607, 607, 607, 607, 607, 607,
-
607, 607, 607, 607, 607, 607, 607, 607,
-
607, 607, 607, 574, 1, 1, 607, 607,
-
607, 607, 607, 607, 607, 607, 607, 607,
-
607, 607, 607, 607, 607, 607, 607, 607,
-
607, 607, 607, 607, 607, 607, 607, 607,
-
607, 607, 607, 607, 607, 607, 607, 1,
-
605, 1, 1, 1, 606, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 605,
-
575, 576, 575, 575, 575, 575, 575, 608,
-
1, 575, 575, 1, 575, 610, 575, 575,
-
575, 575, 575, 575, 575, 575, 575, 575,
-
575, 1, 572, 578, 575, 1, 575, 1,
-
575, 575, 575, 575, 575, 575, 575, 575,
-
575, 575, 575, 575, 575, 575, 575, 575,
-
575, 575, 575, 575, 575, 575, 575, 575,
-
575, 575, 574, 1, 1, 575, 575, 575,
-
575, 575, 575, 575, 575, 575, 575, 575,
-
575, 575, 575, 575, 575, 575, 575, 575,
-
575, 575, 575, 575, 575, 575, 575, 575,
-
575, 575, 575, 575, 575, 575, 1, 611,
-
1, 612, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
612, 1, 613, 1, 1, 1, 614, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 613, 583, 584, 583, 583, 583, 583,
-
583, 615, 1, 583, 583, 1, 583, 616,
-
583, 583, 583, 583, 583, 583, 583, 583,
-
583, 583, 583, 1, 587, 588, 583, 1,
-
583, 1, 583, 583, 583, 583, 583, 583,
-
583, 583, 583, 583, 583, 583, 583, 583,
-
583, 583, 583, 583, 583, 583, 583, 583,
-
583, 583, 583, 583, 589, 1, 1, 583,
-
583, 583, 583, 583, 583, 583, 583, 583,
-
583, 583, 583, 583, 583, 583, 583, 583,
-
583, 583, 583, 583, 583, 583, 583, 583,
-
583, 583, 583, 583, 583, 583, 583, 583,
-
1, 617, 593, 617, 617, 617, 617, 617,
-
1, 1, 617, 617, 1, 617, 90, 617,
-
617, 617, 617, 617, 617, 617, 617, 617,
-
617, 617, 1, 1, 1, 617, 1, 617,
-
1, 617, 617, 617, 617, 617, 617, 617,
-
617, 617, 617, 617, 617, 617, 617, 617,
-
617, 617, 617, 617, 617, 617, 617, 617,
-
617, 617, 617, 1, 1, 1, 617, 617,
-
617, 617, 617, 617, 617, 617, 617, 617,
-
617, 617, 617, 617, 617, 617, 617, 617,
-
617, 617, 617, 617, 617, 617, 617, 617,
-
617, 617, 617, 617, 617, 617, 617, 1,
-
618, 1, 1, 1, 619, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 618,
-
23, 24, 23, 23, 23, 23, 23, 620,
-
1, 23, 23, 1, 23, 26, 23, 23,
-
23, 23, 23, 23, 23, 23, 23, 23,
-
23, 1, 27, 28, 23, 1, 23, 29,
-
23, 23, 23, 23, 23, 23, 23, 23,
-
23, 23, 23, 23, 23, 23, 23, 23,
-
23, 23, 23, 23, 23, 23, 23, 23,
-
23, 23, 30, 1, 1, 23, 23, 23,
-
23, 23, 23, 23, 23, 23, 23, 23,
-
23, 23, 23, 23, 23, 23, 23, 23,
-
23, 23, 23, 23, 23, 23, 23, 23,
-
23, 23, 23, 23, 23, 23, 1, 621,
-
1, 1, 1, 622, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 621, 33,
-
34, 33, 33, 33, 33, 33, 623, 1,
-
33, 33, 1, 33, 624, 33, 33, 33,
-
33, 33, 33, 33, 33, 33, 33, 33,
-
1, 37, 38, 33, 1, 33, 39, 33,
-
33, 33, 33, 33, 33, 33, 33, 33,
-
33, 33, 33, 33, 33, 33, 33, 33,
-
33, 33, 33, 33, 33, 33, 33, 33,
-
33, 40, 1, 1, 33, 33, 33, 33,
-
33, 33, 33, 33, 33, 33, 33, 33,
-
33, 33, 33, 33, 33, 33, 33, 33,
-
33, 33, 33, 33, 33, 33, 33, 33,
-
33, 33, 33, 33, 33, 1, 625, 1,
-
626, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 626,
-
1, 627, 1, 1, 1, 628, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
627, 629, 630, 629, 629, 629, 629, 629,
-
631, 1, 629, 629, 1, 629, 632, 629,
-
629, 629, 629, 629, 629, 629, 629, 629,
-
629, 629, 1, 633, 634, 629, 1, 629,
-
635, 629, 629, 629, 629, 629, 629, 629,
-
629, 629, 629, 629, 629, 629, 629, 629,
-
629, 629, 629, 629, 629, 629, 629, 629,
-
629, 629, 629, 636, 1, 1, 629, 629,
-
629, 629, 629, 629, 629, 629, 629, 629,
-
629, 629, 629, 629, 629, 629, 629, 629,
-
629, 629, 629, 629, 629, 629, 629, 629,
-
629, 629, 629, 629, 629, 629, 629, 1,
-
637, 1, 1, 1, 638, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 637,
-
639, 640, 639, 639, 639, 639, 639, 641,
-
1, 639, 639, 1, 639, 642, 639, 639,
-
639, 639, 639, 639, 639, 639, 639, 639,
-
639, 1, 643, 644, 639, 1, 639, 29,
-
639, 639, 639, 639, 639, 639, 639, 639,
-
639, 639, 639, 639, 639, 639, 639, 639,
-
639, 639, 639, 639, 639, 639, 639, 639,
-
639, 639, 645, 1, 1, 639, 639, 639,
-
639, 639, 639, 639, 639, 639, 639, 639,
-
639, 639, 639, 639, 639, 639, 639, 639,
-
639, 639, 639, 639, 639, 639, 639, 639,
-
639, 639, 639, 639, 639, 639, 1, 646,
-
1, 1, 1, 647, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 646, 648,
-
649, 648, 648, 648, 648, 648, 650, 1,
-
648, 648, 1, 648, 651, 648, 648, 648,
-
648, 648, 648, 648, 648, 648, 648, 648,
-
1, 652, 653, 648, 1, 648, 39, 648,
-
648, 648, 648, 648, 648, 648, 648, 648,
-
648, 648, 648, 648, 648, 648, 648, 648,
-
648, 648, 648, 648, 648, 648, 648, 648,
-
648, 654, 1, 1, 648, 648, 648, 648,
-
648, 648, 648, 648, 648, 648, 648, 648,
-
648, 648, 648, 648, 648, 648, 648, 648,
-
648, 648, 648, 648, 648, 648, 648, 648,
-
648, 648, 648, 648, 648, 1, 655, 1,
-
656, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 656,
-
1, 657, 1, 1, 1, 658, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
657, 659, 660, 659, 659, 659, 659, 659,
-
661, 1, 659, 659, 1, 659, 662, 659,
-
659, 659, 659, 659, 659, 659, 659, 659,
-
659, 659, 1, 663, 664, 659, 1, 659,
-
635, 659, 659, 659, 659, 659, 659, 659,
-
659, 659, 659, 659, 659, 659, 659, 659,
-
659, 659, 659, 659, 659, 659, 659, 659,
-
659, 659, 659, 665, 1, 1, 659, 659,
-
659, 659, 659, 659, 659, 659, 659, 659,
-
659, 659, 659, 659, 659, 659, 659, 659,
-
659, 659, 659, 659, 659, 659, 659, 659,
-
659, 659, 659, 659, 659, 659, 659, 1,
-
666, 666, 666, 666, 666, 666, 666, 666,
-
593, 1, 666, 666, 667, 666, 666, 666,
-
666, 666, 666, 666, 666, 666, 666, 666,
-
666, 666, 666, 666, 666, 666, 666, 593,
-
666, 1, 666, 666, 666, 666, 666, 666,
-
666, 666, 666, 666, 666, 666, 666, 666,
-
666, 666, 666, 666, 666, 666, 666, 666,
-
666, 666, 666, 666, 666, 666, 666, 666,
-
666, 666, 666, 666, 666, 666, 666, 666,
-
666, 666, 666, 666, 666, 666, 666, 666,
-
666, 666, 666, 666, 666, 666, 666, 666,
-
666, 666, 666, 668, 666, 666, 666, 666,
-
666, 666, 666, 666, 666, 666, 666, 666,
-
666, 666, 666, 666, 666, 666, 666, 666,
-
666, 666, 666, 666, 666, 666, 666, 666,
-
666, 666, 666, 666, 666, 666, 666, 1,
-
666, 666, 666, 666, 666, 666, 666, 666,
-
666, 1, 666, 666, 669, 666, 666, 666,
-
666, 666, 666, 666, 666, 666, 666, 666,
-
666, 666, 666, 666, 666, 666, 666, 666,
-
666, 617, 666, 666, 666, 666, 666, 666,
-
666, 666, 666, 666, 666, 666, 666, 666,
-
666, 666, 666, 666, 666, 666, 666, 666,
-
666, 666, 666, 666, 666, 666, 666, 666,
-
666, 666, 666, 666, 666, 666, 666, 666,
-
666, 666, 666, 666, 666, 666, 666, 666,
-
666, 666, 666, 666, 666, 666, 666, 666,
-
666, 666, 666, 668, 666, 666, 666, 666,
-
666, 666, 666, 666, 666, 666, 666, 666,
-
666, 666, 666, 666, 666, 666, 666, 666,
-
666, 666, 666, 666, 666, 666, 666, 666,
-
666, 666, 666, 666, 666, 666, 666, 1,
-
670, 1, 666, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 666, 1, 666, 666, 666, 666, 666,
-
666, 666, 666, 666, 666, 666, 666, 666,
-
666, 666, 666, 666, 666, 666, 666, 666,
-
666, 666, 666, 666, 666, 666, 666, 666,
-
666, 666, 666, 666, 666, 666, 666, 666,
-
666, 666, 666, 666, 666, 666, 666, 666,
-
666, 666, 666, 666, 666, 666, 666, 666,
-
666, 666, 666, 666, 666, 666, 666, 666,
-
666, 666, 666, 666, 666, 666, 666, 666,
-
666, 666, 666, 666, 666, 666, 666, 666,
-
666, 666, 666, 666, 666, 666, 666, 666,
-
666, 666, 666, 666, 666, 666, 666, 666,
-
666, 666, 666, 666, 666, 666, 666, 666,
-
666, 666, 666, 666, 666, 666, 666, 666,
-
666, 666, 666, 666, 666, 666, 666, 666,
-
666, 666, 666, 666, 666, 666, 666, 666,
-
666, 666, 666, 1, 671, 1, 593, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 593, 1, 672,
-
1, 1, 1, 673, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 672, 674,
-
1, 674, 674, 674, 674, 674, 675, 1,
-
674, 674, 1, 674, 1, 674, 674, 674,
-
674, 674, 674, 674, 674, 674, 674, 674,
-
1, 1, 1, 674, 1, 674, 1, 674,
-
674, 674, 674, 674, 674, 674, 674, 674,
-
674, 674, 674, 674, 674, 674, 674, 674,
-
674, 674, 674, 674, 674, 674, 674, 674,
-
674, 1, 1, 1, 674, 674, 674, 674,
-
674, 674, 674, 674, 674, 674, 674, 674,
-
674, 674, 674, 674, 674, 674, 674, 674,
-
674, 674, 674, 674, 674, 674, 674, 674,
-
674, 674, 674, 674, 674, 1, 566, 1,
-
1, 1, 567, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 566, 568, 569,
-
568, 568, 568, 568, 568, 570, 1, 568,
-
568, 1, 568, 571, 568, 568, 568, 568,
-
568, 568, 568, 568, 568, 568, 568, 1,
-
600, 573, 568, 1, 568, 29, 568, 568,
-
568, 568, 568, 568, 568, 568, 568, 568,
-
568, 568, 568, 568, 568, 568, 568, 568,
-
568, 568, 568, 568, 568, 568, 568, 568,
-
574, 1, 1, 568, 568, 568, 568, 568,
-
568, 568, 568, 568, 568, 568, 568, 568,
-
568, 568, 568, 568, 568, 568, 568, 568,
-
568, 568, 568, 568, 568, 568, 568, 568,
-
568, 568, 568, 568, 1, 676, 676, 676,
-
676, 676, 676, 676, 676, 677, 1, 676,
-
676, 678, 676, 676, 676, 676, 676, 676,
-
676, 676, 676, 676, 676, 676, 676, 676,
-
676, 676, 676, 676, 677, 676, 46, 676,
-
676, 676, 676, 676, 676, 676, 676, 676,
-
676, 676, 676, 676, 676, 676, 676, 676,
-
676, 676, 676, 676, 676, 676, 676, 676,
-
676, 676, 676, 676, 676, 676, 676, 676,
-
676, 676, 676, 676, 676, 676, 676, 676,
-
676, 676, 676, 676, 676, 676, 676, 676,
-
676, 676, 676, 676, 676, 676, 676, 676,
-
679, 676, 676, 676, 676, 676, 676, 676,
-
676, 676, 676, 676, 676, 676, 676, 676,
-
676, 676, 676, 676, 676, 676, 676, 676,
-
676, 676, 676, 676, 676, 676, 676, 676,
-
676, 676, 676, 676, 1, 680, 680, 680,
-
680, 680, 680, 680, 680, 680, 1, 680,
-
680, 681, 680, 680, 680, 680, 680, 680,
-
680, 680, 680, 680, 680, 680, 680, 680,
-
680, 680, 680, 680, 680, 680, 682, 680,
-
680, 680, 680, 680, 680, 680, 680, 680,
-
680, 680, 680, 680, 680, 680, 680, 680,
-
680, 680, 680, 680, 680, 680, 680, 680,
-
680, 680, 680, 680, 680, 680, 680, 680,
-
680, 680, 680, 680, 680, 680, 680, 680,
-
680, 680, 680, 680, 680, 680, 680, 680,
-
680, 680, 680, 680, 680, 680, 680, 680,
-
683, 680, 680, 680, 680, 680, 680, 680,
-
680, 680, 680, 680, 680, 680, 680, 680,
-
680, 680, 680, 680, 680, 680, 680, 680,
-
680, 680, 680, 680, 680, 680, 680, 680,
-
680, 680, 680, 680, 1, 684, 1, 680,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 680, 1,
-
605, 1, 1, 1, 606, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 605,
-
685, 569, 685, 685, 685, 685, 685, 608,
-
1, 685, 685, 1, 685, 686, 685, 685,
-
685, 685, 685, 685, 685, 685, 685, 685,
-
685, 1, 687, 573, 685, 1, 685, 61,
-
685, 685, 685, 685, 685, 685, 685, 685,
-
685, 685, 685, 685, 685, 685, 685, 685,
-
685, 685, 685, 685, 685, 685, 685, 685,
-
685, 685, 574, 1, 1, 685, 685, 685,
-
685, 685, 685, 685, 685, 685, 685, 685,
-
685, 685, 685, 685, 685, 685, 685, 685,
-
685, 685, 685, 685, 685, 685, 685, 685,
-
685, 685, 685, 685, 685, 685, 1, 605,
-
1, 1, 1, 606, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 605, 685,
-
569, 685, 685, 685, 685, 685, 608, 1,
-
685, 685, 1, 685, 686, 685, 685, 685,
-
685, 685, 685, 685, 685, 685, 685, 685,
-
1, 600, 573, 685, 1, 685, 29, 685,
-
685, 685, 685, 685, 685, 685, 685, 685,
-
685, 685, 685, 685, 685, 685, 685, 685,
-
685, 685, 685, 685, 685, 685, 685, 685,
-
685, 574, 1, 1, 685, 685, 685, 685,
-
685, 685, 685, 685, 685, 685, 685, 685,
-
685, 685, 685, 685, 685, 685, 685, 685,
-
685, 685, 685, 685, 685, 685, 685, 685,
-
685, 685, 685, 685, 685, 1, 688, 689,
-
688, 688, 688, 688, 688, 1, 1, 688,
-
688, 1, 688, 690, 688, 688, 688, 688,
-
688, 688, 688, 688, 688, 688, 688, 1,
-
1, 1, 688, 1, 688, 1, 688, 688,
-
688, 688, 688, 688, 688, 688, 688, 688,
-
688, 688, 688, 688, 688, 688, 688, 688,
-
688, 688, 688, 688, 688, 688, 688, 688,
-
1, 1, 1, 688, 688, 688, 688, 688,
-
688, 688, 688, 688, 688, 688, 688, 688,
-
688, 688, 688, 688, 688, 688, 688, 688,
-
688, 688, 688, 688, 688, 688, 688, 688,
-
688, 688, 688, 688, 1, 691, 691, 691,
-
691, 691, 691, 691, 691, 689, 1, 691,
-
691, 692, 691, 691, 691, 691, 691, 691,
-
691, 691, 691, 691, 691, 691, 691, 691,
-
691, 691, 691, 691, 689, 691, 1, 691,
-
691, 691, 691, 691, 691, 691, 691, 691,
-
691, 691, 691, 691, 691, 691, 691, 691,
-
691, 691, 691, 691, 691, 691, 691, 691,
-
691, 691, 691, 691, 691, 691, 691, 691,
-
691, 691, 691, 691, 691, 691, 691, 691,
-
691, 691, 691, 691, 691, 691, 691, 691,
-
691, 691, 691, 691, 691, 691, 691, 691,
-
693, 691, 691, 691, 691, 691, 691, 691,
-
691, 691, 691, 691, 691, 691, 691, 691,
-
691, 691, 691, 691, 691, 691, 691, 691,
-
691, 691, 691, 691, 691, 691, 691, 691,
-
691, 691, 691, 691, 1, 691, 691, 691,
-
691, 691, 691, 691, 691, 691, 1, 691,
-
691, 694, 691, 691, 691, 691, 691, 691,
-
691, 691, 691, 691, 691, 691, 691, 691,
-
691, 691, 691, 691, 691, 691, 688, 691,
-
691, 691, 691, 691, 691, 691, 691, 691,
-
691, 691, 691, 691, 691, 691, 691, 691,
-
691, 691, 691, 691, 691, 691, 691, 691,
-
691, 691, 691, 691, 691, 691, 691, 691,
-
691, 691, 691, 691, 691, 691, 691, 691,
-
691, 691, 691, 691, 691, 691, 691, 691,
-
691, 691, 691, 691, 691, 691, 691, 691,
-
693, 691, 691, 691, 691, 691, 691, 691,
-
691, 691, 691, 691, 691, 691, 691, 691,
-
691, 691, 691, 691, 691, 691, 691, 691,
-
691, 691, 691, 691, 691, 691, 691, 691,
-
691, 691, 691, 691, 1, 695, 1, 691,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 691, 1,
-
691, 691, 691, 691, 691, 691, 691, 691,
-
691, 691, 691, 691, 691, 691, 691, 691,
-
691, 691, 691, 691, 691, 691, 691, 691,
-
691, 691, 691, 691, 691, 691, 691, 691,
-
691, 691, 691, 691, 691, 691, 691, 691,
-
691, 691, 691, 691, 691, 691, 691, 691,
-
691, 691, 691, 691, 691, 691, 691, 691,
-
691, 691, 691, 691, 691, 691, 691, 691,
-
691, 691, 691, 691, 691, 691, 691, 691,
-
691, 691, 691, 691, 691, 691, 691, 691,
-
691, 691, 691, 691, 691, 691, 691, 691,
-
691, 691, 691, 691, 691, 691, 691, 691,
-
691, 691, 691, 691, 691, 691, 691, 691,
-
691, 691, 691, 691, 691, 691, 691, 691,
-
691, 691, 691, 691, 691, 691, 691, 691,
-
691, 691, 691, 691, 691, 691, 691, 691,
-
1, 696, 1, 689, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 689, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 680, 680, 680, 680,
-
680, 680, 680, 680, 680, 697, 1, 680,
-
680, 698, 680, 680, 680, 680, 680, 680,
-
680, 680, 680, 680, 680, 680, 680, 680,
-
680, 680, 680, 680, 697, 680, 699, 680,
-
680, 680, 680, 680, 680, 680, 680, 680,
-
680, 680, 680, 680, 680, 680, 680, 680,
-
680, 680, 680, 680, 680, 680, 680, 680,
-
680, 680, 680, 680, 680, 680, 680, 680,
-
680, 680, 680, 680, 680, 680, 680, 680,
-
680, 680, 680, 680, 680, 680, 680, 680,
-
680, 680, 680, 680, 680, 680, 680, 680,
-
683, 680, 680, 680, 680, 680, 680, 680,
-
680, 680, 680, 680, 680, 680, 680, 680,
-
680, 680, 680, 680, 680, 680, 680, 680,
-
680, 680, 680, 680, 680, 680, 680, 680,
-
680, 680, 680, 680, 1, 700, 1, 697,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 697, 1,
-
701, 1, 1, 1, 702, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 701,
-
703, 704, 703, 703, 703, 703, 703, 705,
-
1, 703, 703, 1, 703, 706, 703, 703,
-
703, 703, 703, 703, 703, 703, 703, 703,
-
703, 1, 707, 708, 703, 1, 703, 709,
-
703, 703, 703, 703, 703, 703, 703, 703,
-
703, 703, 703, 703, 703, 703, 703, 703,
-
703, 703, 703, 703, 703, 703, 703, 703,
-
703, 703, 710, 1, 1, 703, 703, 703,
-
703, 703, 703, 703, 703, 703, 703, 703,
-
703, 703, 703, 703, 703, 703, 703, 703,
-
703, 703, 703, 703, 703, 703, 703, 703,
-
703, 703, 703, 703, 703, 703, 1, 701,
-
1, 1, 1, 702, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 701, 711,
-
712, 711, 711, 711, 711, 711, 705, 1,
-
711, 711, 1, 711, 713, 711, 711, 711,
-
711, 711, 711, 711, 711, 711, 711, 711,
-
1, 707, 714, 711, 1, 711, 709, 711,
-
711, 711, 711, 711, 711, 711, 711, 711,
-
711, 711, 711, 711, 711, 711, 711, 711,
-
711, 711, 711, 711, 711, 711, 711, 711,
-
711, 710, 1, 1, 711, 711, 711, 711,
-
711, 711, 711, 711, 711, 711, 711, 711,
-
711, 711, 711, 711, 711, 711, 711, 711,
-
711, 711, 711, 711, 711, 711, 711, 711,
-
711, 711, 711, 711, 711, 1, 715, 1,
-
716, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 716,
-
1, 717, 1, 1, 1, 718, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
717, 719, 720, 719, 719, 719, 719, 719,
-
721, 1, 719, 719, 1, 719, 722, 719,
-
719, 719, 719, 719, 719, 719, 719, 719,
-
719, 719, 1, 723, 724, 719, 1, 719,
-
725, 719, 719, 719, 719, 719, 719, 719,
-
719, 719, 719, 719, 719, 719, 719, 719,
-
719, 719, 719, 719, 719, 719, 719, 719,
-
719, 719, 719, 726, 1, 1, 719, 719,
-
719, 719, 719, 719, 719, 719, 719, 719,
-
719, 719, 719, 719, 719, 719, 719, 719,
-
719, 719, 719, 719, 719, 719, 719, 719,
-
719, 719, 719, 719, 719, 719, 719, 1,
-
590, 1, 1, 1, 591, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 590,
-
727, 689, 727, 727, 727, 727, 727, 594,
-
1, 727, 727, 1, 727, 690, 727, 727,
-
727, 727, 727, 727, 727, 727, 727, 727,
-
727, 1, 1, 1, 727, 1, 727, 1,
-
727, 727, 727, 727, 727, 727, 727, 727,
-
727, 727, 727, 727, 727, 727, 727, 727,
-
727, 727, 727, 727, 727, 727, 727, 727,
-
727, 727, 1, 1, 1, 727, 727, 727,
-
727, 727, 727, 727, 727, 727, 727, 727,
-
727, 727, 727, 727, 727, 727, 727, 727,
-
727, 727, 727, 727, 727, 727, 727, 727,
-
727, 727, 727, 727, 727, 727, 1, 728,
-
728, 728, 728, 728, 728, 728, 728, 561,
-
1, 728, 728, 729, 728, 728, 728, 728,
-
728, 728, 728, 728, 728, 728, 728, 728,
-
728, 728, 728, 728, 728, 728, 561, 728,
-
1, 728, 728, 728, 728, 728, 728, 728,
-
728, 728, 728, 728, 728, 728, 728, 728,
-
728, 728, 728, 728, 728, 728, 728, 728,
-
728, 728, 728, 728, 728, 728, 728, 728,
-
728, 728, 728, 728, 728, 728, 728, 728,
-
728, 728, 728, 728, 728, 728, 728, 728,
-
728, 728, 728, 728, 728, 728, 728, 728,
-
728, 728, 730, 728, 728, 728, 728, 728,
-
728, 728, 728, 728, 728, 728, 728, 728,
-
728, 728, 728, 728, 728, 728, 728, 728,
-
728, 728, 728, 728, 728, 728, 728, 728,
-
728, 728, 728, 728, 728, 728, 1, 728,
-
728, 728, 728, 728, 728, 728, 728, 728,
-
1, 728, 728, 731, 728, 728, 728, 728,
-
728, 728, 728, 728, 728, 728, 728, 728,
-
728, 728, 728, 728, 728, 728, 728, 728,
-
732, 728, 728, 728, 728, 728, 728, 728,
-
728, 728, 728, 728, 728, 728, 728, 728,
-
728, 728, 728, 728, 728, 728, 728, 728,
-
728, 728, 728, 728, 728, 728, 728, 728,
-
728, 728, 728, 728, 728, 728, 728, 728,
-
728, 728, 728, 728, 728, 728, 728, 728,
-
728, 728, 728, 728, 728, 728, 728, 728,
-
728, 728, 730, 728, 728, 728, 728, 728,
-
728, 728, 728, 728, 728, 728, 728, 728,
-
728, 728, 728, 728, 728, 728, 728, 728,
-
728, 728, 728, 728, 728, 728, 728, 728,
-
728, 728, 728, 728, 728, 728, 1, 733,
-
1, 728, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
728, 1, 605, 1, 1, 1, 606, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 605, 685, 569, 685, 685, 685, 685,
-
685, 608, 1, 685, 685, 1, 685, 686,
-
685, 685, 685, 685, 685, 685, 685, 685,
-
685, 685, 685, 1, 572, 573, 685, 1,
-
685, 1, 685, 685, 685, 685, 685, 685,
-
685, 685, 685, 685, 685, 685, 685, 685,
-
685, 685, 685, 685, 685, 685, 685, 685,
-
685, 685, 685, 685, 574, 1, 1, 685,
-
685, 685, 685, 685, 685, 685, 685, 685,
-
685, 685, 685, 685, 685, 685, 685, 685,
-
685, 685, 685, 685, 685, 685, 685, 685,
-
685, 685, 685, 685, 685, 685, 685, 685,
-
1, 728, 728, 728, 728, 728, 728, 728,
-
728, 728, 728, 728, 728, 728, 728, 728,
-
728, 728, 728, 728, 728, 728, 728, 728,
-
728, 728, 728, 728, 728, 728, 728, 728,
-
728, 728, 728, 728, 728, 728, 728, 728,
-
728, 728, 728, 728, 728, 728, 728, 728,
-
728, 728, 728, 728, 728, 728, 728, 728,
-
728, 728, 728, 728, 728, 728, 728, 728,
-
728, 728, 728, 728, 728, 728, 728, 728,
-
728, 728, 728, 728, 728, 728, 728, 728,
-
728, 728, 728, 728, 728, 728, 728, 728,
-
728, 728, 728, 728, 728, 728, 728, 728,
-
728, 728, 728, 728, 728, 728, 728, 728,
-
728, 728, 728, 728, 728, 728, 728, 728,
-
728, 728, 728, 728, 728, 728, 728, 728,
-
728, 728, 728, 728, 728, 728, 728, 728,
-
728, 1, 734, 1, 561, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 561, 1, 735, 1, 1,
-
1, 736, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 735, 737, 738, 737,
-
737, 737, 737, 737, 739, 1, 737, 737,
-
1, 737, 740, 737, 737, 737, 737, 737,
-
737, 737, 737, 737, 737, 737, 1, 1,
-
1, 737, 1, 737, 1, 737, 737, 737,
-
737, 737, 737, 737, 737, 737, 737, 737,
-
737, 737, 737, 737, 737, 737, 737, 737,
-
737, 737, 737, 737, 737, 737, 737, 741,
-
1, 1, 737, 737, 737, 737, 737, 737,
-
737, 737, 737, 737, 737, 737, 737, 737,
-
737, 737, 737, 737, 737, 737, 737, 737,
-
737, 737, 737, 737, 737, 737, 737, 737,
-
737, 737, 737, 1, 732, 561, 732, 732,
-
732, 732, 732, 1, 1, 732, 732, 1,
-
732, 563, 732, 732, 732, 732, 732, 732,
-
732, 732, 732, 732, 732, 1, 1, 1,
-
732, 1, 732, 1, 732, 732, 732, 732,
-
732, 732, 732, 732, 732, 732, 732, 732,
-
732, 732, 732, 732, 732, 732, 732, 732,
-
732, 732, 732, 732, 732, 732, 1, 1,
-
1, 732, 732, 732, 732, 732, 732, 732,
-
732, 732, 732, 732, 732, 732, 732, 732,
-
732, 732, 732, 732, 732, 732, 732, 732,
-
732, 732, 732, 732, 732, 732, 732, 732,
-
732, 732, 1, 742, 742, 742, 742, 742,
-
742, 742, 742, 742, 1, 742, 742, 743,
-
742, 742, 742, 742, 742, 742, 742, 742,
-
742, 742, 742, 742, 742, 742, 742, 742,
-
742, 742, 742, 742, 744, 742, 742, 742,
-
742, 742, 742, 742, 742, 742, 742, 742,
-
742, 742, 742, 742, 742, 742, 742, 742,
-
742, 742, 742, 742, 742, 742, 742, 742,
-
742, 742, 742, 742, 742, 742, 742, 742,
-
742, 742, 742, 742, 742, 742, 742, 742,
-
742, 742, 742, 742, 742, 742, 742, 742,
-
742, 742, 742, 742, 742, 742, 745, 742,
-
742, 742, 742, 742, 742, 742, 742, 742,
-
742, 742, 742, 742, 742, 742, 742, 742,
-
742, 742, 742, 742, 742, 742, 742, 742,
-
742, 742, 742, 742, 742, 742, 742, 742,
-
742, 742, 1, 746, 746, 746, 746, 746,
-
746, 746, 746, 746, 1, 746, 746, 747,
-
746, 746, 746, 746, 746, 746, 746, 746,
-
746, 746, 746, 746, 746, 746, 746, 746,
-
746, 746, 746, 746, 748, 746, 746, 746,
-
746, 746, 746, 746, 746, 746, 746, 746,
-
746, 746, 746, 746, 746, 746, 746, 746,
-
746, 746, 746, 746, 746, 746, 746, 746,
-
746, 746, 746, 746, 746, 746, 746, 746,
-
746, 746, 746, 746, 746, 746, 746, 746,
-
746, 746, 746, 746, 746, 746, 746, 746,
-
746, 746, 746, 746, 746, 746, 749, 746,
-
746, 746, 746, 746, 746, 746, 746, 746,
-
746, 746, 746, 746, 746, 746, 746, 746,
-
746, 746, 746, 746, 746, 746, 746, 746,
-
746, 746, 746, 746, 746, 746, 746, 746,
-
746, 746, 1, 750, 1, 746, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 746, 1, 746, 746,
-
746, 746, 746, 746, 746, 746, 746, 746,
-
746, 746, 746, 746, 746, 746, 746, 746,
-
746, 746, 746, 746, 746, 746, 746, 746,
-
746, 746, 746, 746, 746, 746, 746, 746,
-
746, 746, 746, 746, 746, 746, 746, 746,
-
746, 746, 746, 746, 746, 746, 746, 746,
-
746, 746, 746, 746, 746, 746, 746, 746,
-
746, 746, 746, 746, 746, 746, 746, 746,
-
746, 746, 746, 746, 746, 746, 746, 746,
-
746, 746, 746, 746, 746, 746, 746, 746,
-
746, 746, 746, 746, 746, 746, 746, 746,
-
746, 746, 746, 746, 746, 746, 746, 746,
-
746, 746, 746, 746, 746, 746, 746, 746,
-
746, 746, 746, 746, 746, 746, 746, 746,
-
746, 746, 746, 746, 746, 746, 746, 746,
-
746, 746, 746, 746, 746, 746, 1, 751,
-
1, 1, 1, 752, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 751, 753,
-
754, 753, 753, 753, 753, 753, 755, 1,
-
753, 753, 1, 753, 1, 753, 753, 753,
-
753, 753, 753, 753, 753, 753, 753, 753,
-
1, 1, 1, 753, 1, 753, 1, 753,
-
753, 753, 753, 753, 753, 753, 753, 753,
-
753, 753, 753, 753, 753, 753, 753, 753,
-
753, 753, 753, 753, 753, 753, 753, 753,
-
753, 1, 1, 1, 753, 753, 753, 753,
-
753, 753, 753, 753, 753, 753, 753, 753,
-
753, 753, 753, 753, 753, 753, 753, 753,
-
753, 753, 753, 753, 753, 753, 753, 753,
-
753, 753, 753, 753, 753, 1, 756, 1,
-
1, 1, 757, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 756, 23, 24,
-
23, 23, 23, 23, 23, 758, 1, 23,
-
23, 1, 23, 26, 23, 23, 23, 23,
-
23, 23, 23, 23, 23, 23, 23, 1,
-
27, 28, 23, 1, 23, 29, 23, 23,
-
23, 23, 23, 23, 23, 23, 23, 23,
-
23, 23, 23, 23, 23, 23, 23, 23,
-
23, 23, 23, 23, 23, 23, 23, 23,
-
30, 1, 1, 23, 23, 23, 23, 23,
-
23, 23, 23, 23, 23, 23, 23, 23,
-
23, 23, 23, 23, 23, 23, 23, 23,
-
23, 23, 23, 23, 23, 23, 23, 23,
-
23, 23, 23, 23, 1, 759, 1, 1,
-
1, 760, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 759, 33, 34, 33,
-
33, 33, 33, 33, 761, 1, 33, 33,
-
1, 33, 762, 33, 33, 33, 33, 33,
-
33, 33, 33, 33, 33, 33, 1, 37,
-
38, 33, 1, 33, 39, 33, 33, 33,
-
33, 33, 33, 33, 33, 33, 33, 33,
-
33, 33, 33, 33, 33, 33, 33, 33,
-
33, 33, 33, 33, 33, 33, 33, 40,
-
1, 1, 33, 33, 33, 33, 33, 33,
-
33, 33, 33, 33, 33, 33, 33, 33,
-
33, 33, 33, 33, 33, 33, 33, 33,
-
33, 33, 33, 33, 33, 33, 33, 33,
-
33, 33, 33, 1, 763, 1, 764, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 764, 1, 765,
-
1, 1, 1, 766, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 765, 629,
-
630, 629, 629, 629, 629, 629, 767, 1,
-
629, 629, 1, 629, 768, 629, 629, 629,
-
629, 629, 629, 629, 629, 629, 629, 629,
-
1, 633, 634, 629, 1, 629, 635, 629,
-
629, 629, 629, 629, 629, 629, 629, 629,
-
629, 629, 629, 629, 629, 629, 629, 629,
-
629, 629, 629, 629, 629, 629, 629, 629,
-
629, 636, 1, 1, 629, 629, 629, 629,
-
629, 629, 629, 629, 629, 629, 629, 629,
-
629, 629, 629, 629, 629, 629, 629, 629,
-
629, 629, 629, 629, 629, 629, 629, 629,
-
629, 629, 629, 629, 629, 1, 769, 769,
-
769, 769, 769, 769, 769, 769, 770, 1,
-
769, 769, 771, 769, 769, 769, 769, 769,
-
769, 769, 769, 769, 769, 769, 769, 769,
-
769, 769, 769, 769, 769, 770, 769, 744,
-
769, 769, 769, 769, 769, 769, 769, 769,
-
769, 769, 769, 769, 769, 769, 769, 769,
-
769, 769, 769, 769, 769, 769, 769, 769,
-
769, 769, 769, 769, 769, 769, 769, 769,
-
769, 769, 769, 769, 769, 769, 769, 769,
-
769, 769, 769, 769, 769, 769, 769, 769,
-
769, 769, 769, 769, 769, 769, 769, 769,
-
769, 772, 769, 769, 769, 769, 769, 769,
-
769, 769, 769, 769, 769, 769, 769, 769,
-
769, 769, 769, 769, 769, 769, 769, 769,
-
769, 769, 769, 769, 769, 769, 769, 769,
-
769, 769, 769, 769, 769, 1, 773, 773,
-
773, 773, 773, 773, 773, 773, 773, 1,
-
773, 773, 774, 773, 773, 773, 773, 773,
-
773, 773, 773, 773, 773, 773, 773, 773,
-
773, 773, 773, 773, 773, 773, 773, 775,
-
773, 773, 773, 773, 773, 773, 773, 773,
-
773, 773, 773, 773, 773, 773, 773, 773,
-
773, 773, 773, 773, 773, 773, 773, 773,
-
773, 773, 773, 773, 773, 773, 773, 773,
-
773, 773, 773, 773, 773, 773, 773, 773,
-
773, 773, 773, 773, 773, 773, 773, 773,
-
773, 773, 773, 773, 773, 773, 773, 773,
-
773, 776, 773, 773, 773, 773, 773, 773,
-
773, 773, 773, 773, 773, 773, 773, 773,
-
773, 773, 773, 773, 773, 773, 773, 773,
-
773, 773, 773, 773, 773, 773, 773, 773,
-
773, 773, 773, 773, 773, 1, 777, 1,
-
773, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 773,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 773, 773, 773, 773, 773, 773, 773,
-
773, 773, 778, 1, 773, 773, 779, 773,
-
773, 773, 773, 773, 773, 773, 773, 773,
-
773, 773, 773, 773, 773, 773, 773, 773,
-
773, 778, 773, 748, 773, 773, 773, 773,
-
773, 773, 773, 773, 773, 773, 773, 773,
-
773, 773, 773, 773, 773, 773, 773, 773,
-
773, 773, 773, 773, 773, 773, 773, 773,
-
773, 773, 773, 773, 773, 773, 773, 773,
-
773, 773, 773, 773, 773, 773, 773, 773,
-
773, 773, 773, 773, 773, 773, 773, 773,
-
773, 773, 773, 773, 773, 776, 773, 773,
-
773, 773, 773, 773, 773, 773, 773, 773,
-
773, 773, 773, 773, 773, 773, 773, 773,
-
773, 773, 773, 773, 773, 773, 773, 773,
-
773, 773, 773, 773, 773, 773, 773, 773,
-
773, 1, 780, 1, 778, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 778, 1, 48, 48, 48,
-
48, 48, 48, 48, 48, 48, 48, 48,
-
48, 48, 48, 48, 48, 48, 48, 48,
-
48, 48, 48, 48, 48, 48, 48, 48,
-
48, 48, 48, 48, 48, 48, 48, 48,
-
48, 48, 48, 48, 48, 48, 48, 48,
-
48, 48, 48, 48, 48, 48, 48, 48,
-
48, 48, 48, 48, 48, 48, 48, 48,
-
48, 48, 48, 48, 48, 48, 48, 48,
-
48, 48, 48, 48, 48, 48, 48, 48,
-
48, 48, 48, 48, 48, 48, 48, 48,
-
48, 48, 48, 48, 48, 48, 48, 48,
-
48, 48, 48, 48, 48, 48, 48, 48,
-
48, 48, 48, 48, 48, 48, 48, 48,
-
48, 48, 48, 48, 48, 48, 48, 48,
-
48, 48, 48, 48, 48, 48, 48, 48,
-
48, 48, 48, 48, 48, 1, 48, 48,
-
48, 48, 48, 48, 48, 48, 781, 1,
-
48, 48, 782, 48, 48, 48, 48, 48,
-
48, 48, 48, 48, 48, 48, 48, 48,
-
48, 48, 48, 48, 48, 781, 48, 699,
-
48, 48, 48, 48, 48, 48, 48, 48,
-
48, 48, 48, 48, 48, 48, 48, 48,
-
48, 48, 48, 48, 48, 48, 48, 48,
-
48, 48, 48, 48, 48, 48, 48, 48,
-
48, 48, 48, 48, 48, 48, 48, 48,
-
48, 48, 48, 48, 48, 48, 48, 48,
-
48, 48, 48, 48, 48, 48, 48, 48,
-
48, 51, 48, 48, 48, 48, 48, 48,
-
48, 48, 48, 48, 48, 48, 48, 48,
-
48, 48, 48, 48, 48, 48, 48, 48,
-
48, 48, 48, 48, 48, 48, 48, 48,
-
48, 48, 48, 48, 48, 1, 783, 1,
-
781, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 781,
-
1, 784, 1, 1, 1, 785, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
784, 629, 630, 629, 629, 629, 629, 629,
-
786, 1, 629, 629, 1, 629, 787, 629,
-
629, 629, 629, 629, 629, 629, 629, 629,
-
629, 629, 1, 633, 634, 629, 1, 629,
-
635, 629, 629, 629, 629, 629, 629, 629,
-
629, 629, 629, 629, 629, 629, 629, 629,
-
629, 629, 629, 629, 629, 629, 629, 629,
-
629, 629, 629, 636, 1, 1, 629, 629,
-
629, 629, 629, 629, 629, 629, 629, 629,
-
629, 629, 629, 629, 629, 629, 629, 629,
-
629, 629, 629, 629, 629, 629, 629, 629,
-
629, 629, 629, 629, 629, 629, 629, 1,
-
788, 1, 1, 1, 789, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 788,
-
790, 88, 790, 790, 790, 790, 790, 791,
-
1, 790, 790, 1, 790, 90, 790, 790,
-
790, 790, 790, 790, 790, 790, 790, 790,
-
790, 1, 1, 1, 790, 1, 790, 1,
-
790, 790, 790, 790, 790, 790, 790, 790,
-
790, 790, 790, 790, 790, 790, 790, 790,
-
790, 790, 790, 790, 790, 790, 790, 790,
-
790, 790, 1, 1, 1, 790, 790, 790,
-
790, 790, 790, 790, 790, 790, 790, 790,
-
790, 790, 790, 790, 790, 790, 790, 790,
-
790, 790, 790, 790, 790, 790, 790, 790,
-
790, 790, 790, 790, 790, 790, 1, 788,
-
1, 1, 1, 789, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 788, 792,
-
92, 792, 792, 792, 792, 792, 791, 1,
-
792, 792, 1, 792, 1, 792, 792, 792,
-
792, 792, 792, 792, 792, 792, 792, 792,
-
1, 1, 1, 792, 1, 792, 1, 792,
-
792, 792, 792, 792, 792, 792, 792, 792,
-
792, 792, 792, 792, 792, 792, 792, 792,
-
792, 792, 792, 792, 792, 792, 792, 792,
-
792, 1, 1, 1, 792, 792, 792, 792,
-
792, 792, 792, 792, 792, 792, 792, 792,
-
792, 792, 792, 792, 792, 792, 792, 792,
-
792, 792, 792, 792, 792, 792, 792, 792,
-
792, 792, 792, 792, 792, 1, 793, 1,
-
788, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 788,
-
1, 794, 1, 1, 1, 795, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
794, 607, 598, 607, 607, 607, 607, 607,
-
796, 1, 607, 607, 1, 607, 797, 607,
-
607, 607, 607, 607, 607, 607, 607, 607,
-
607, 607, 1, 572, 573, 607, 1, 607,
-
98, 607, 607, 607, 607, 607, 607, 607,
-
607, 607, 607, 607, 607, 607, 607, 607,
-
607, 607, 607, 607, 607, 607, 607, 607,
-
607, 607, 607, 574, 1, 1, 607, 607,
-
607, 607, 607, 607, 607, 607, 607, 607,
-
607, 607, 607, 607, 607, 607, 607, 607,
-
607, 607, 607, 607, 607, 607, 607, 607,
-
607, 607, 607, 607, 607, 607, 607, 1,
-
794, 1, 1, 1, 795, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 794,
-
575, 576, 575, 575, 575, 575, 575, 796,
-
1, 575, 575, 1, 575, 798, 575, 575,
-
575, 575, 575, 575, 575, 575, 575, 575,
-
575, 1, 572, 578, 575, 1, 575, 98,
-
575, 575, 575, 575, 575, 575, 575, 575,
-
575, 575, 575, 575, 575, 575, 575, 575,
-
575, 575, 575, 575, 575, 575, 575, 575,
-
575, 575, 574, 1, 1, 575, 575, 575,
-
575, 575, 575, 575, 575, 575, 575, 575,
-
575, 575, 575, 575, 575, 575, 575, 575,
-
575, 575, 575, 575, 575, 575, 575, 575,
-
575, 575, 575, 575, 575, 575, 1, 799,
-
1, 800, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
800, 1, 801, 1, 1, 1, 802, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 801, 583, 584, 583, 583, 583, 583,
-
583, 803, 1, 583, 583, 1, 583, 804,
-
583, 583, 583, 583, 583, 583, 583, 583,
-
583, 583, 583, 1, 587, 588, 583, 1,
-
583, 108, 583, 583, 583, 583, 583, 583,
-
583, 583, 583, 583, 583, 583, 583, 583,
-
583, 583, 583, 583, 583, 583, 583, 583,
-
583, 583, 583, 583, 589, 1, 1, 583,
-
583, 583, 583, 583, 583, 583, 583, 583,
-
583, 583, 583, 583, 583, 583, 583, 583,
-
583, 583, 583, 583, 583, 583, 583, 583,
-
583, 583, 583, 583, 583, 583, 583, 583,
-
1, 805, 1, 1, 1, 806, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
805, 807, 754, 807, 807, 807, 807, 807,
-
808, 1, 807, 807, 1, 807, 1, 807,
-
807, 807, 807, 807, 807, 807, 807, 807,
-
807, 807, 1, 1, 1, 807, 1, 807,
-
1, 807, 807, 807, 807, 807, 807, 807,
-
807, 807, 807, 807, 807, 807, 807, 807,
-
807, 807, 807, 807, 807, 807, 807, 807,
-
807, 807, 807, 1, 1, 1, 807, 807,
-
807, 807, 807, 807, 807, 807, 807, 807,
-
807, 807, 807, 807, 807, 807, 807, 807,
-
807, 807, 807, 807, 807, 807, 807, 807,
-
807, 807, 807, 807, 807, 807, 807, 1,
-
21, 1, 1, 1, 22, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 21,
-
23, 24, 23, 23, 23, 23, 23, 809,
-
1, 23, 23, 1, 23, 26, 23, 23,
-
23, 23, 23, 23, 23, 23, 23, 23,
-
23, 1, 27, 28, 23, 1, 23, 29,
-
23, 23, 23, 23, 23, 23, 23, 23,
-
23, 23, 23, 23, 23, 23, 23, 23,
-
23, 23, 23, 23, 23, 23, 23, 23,
-
23, 23, 30, 1, 1, 23, 23, 23,
-
23, 23, 23, 23, 23, 23, 23, 23,
-
23, 23, 23, 23, 23, 23, 23, 23,
-
23, 23, 23, 23, 23, 23, 23, 23,
-
23, 23, 23, 23, 23, 23, 1, 810,
-
1, 1, 1, 811, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 810, 102,
-
103, 102, 102, 102, 102, 102, 812, 1,
-
102, 102, 1, 102, 813, 102, 102, 102,
-
102, 102, 102, 102, 102, 102, 102, 102,
-
1, 106, 107, 102, 1, 102, 1, 102,
-
102, 102, 102, 102, 102, 102, 102, 102,
-
102, 102, 102, 102, 102, 102, 102, 102,
-
102, 102, 102, 102, 102, 102, 102, 102,
-
102, 109, 1, 1, 102, 102, 102, 102,
-
102, 102, 102, 102, 102, 102, 102, 102,
-
102, 102, 102, 102, 102, 102, 102, 102,
-
102, 102, 102, 102, 102, 102, 102, 102,
-
102, 102, 102, 102, 102, 1, 814, 814,
-
814, 814, 814, 814, 814, 814, 814, 1,
-
814, 814, 815, 814, 814, 814, 814, 814,
-
814, 814, 814, 814, 814, 814, 814, 814,
-
814, 814, 814, 814, 814, 814, 814, 814,
-
814, 814, 814, 814, 814, 816, 817, 814,
-
814, 814, 814, 814, 814, 814, 814, 814,
-
814, 814, 814, 814, 814, 814, 814, 814,
-
814, 814, 814, 814, 814, 814, 814, 814,
-
814, 814, 814, 814, 814, 814, 814, 814,
-
814, 814, 814, 814, 814, 814, 814, 814,
-
814, 814, 814, 814, 814, 814, 814, 814,
-
814, 818, 814, 814, 814, 814, 814, 814,
-
814, 814, 814, 814, 814, 814, 814, 814,
-
814, 814, 814, 814, 814, 814, 814, 814,
-
814, 814, 814, 814, 814, 814, 814, 814,
-
814, 814, 814, 814, 814, 1, 819, 819,
-
819, 819, 819, 819, 819, 819, 819, 1,
-
819, 819, 820, 819, 819, 819, 819, 819,
-
819, 819, 819, 819, 819, 819, 819, 819,
-
819, 819, 819, 819, 819, 819, 819, 819,
-
819, 819, 819, 819, 819, 821, 822, 819,
-
819, 819, 819, 819, 819, 819, 819, 819,
-
819, 819, 819, 819, 819, 819, 819, 819,
-
819, 819, 819, 819, 819, 819, 819, 819,
-
819, 819, 819, 819, 819, 819, 819, 819,
-
819, 819, 819, 819, 819, 819, 819, 819,
-
819, 819, 819, 819, 819, 819, 819, 819,
-
819, 823, 819, 819, 819, 819, 819, 819,
-
819, 819, 819, 819, 819, 819, 819, 819,
-
819, 819, 819, 819, 819, 819, 819, 819,
-
819, 819, 819, 819, 819, 819, 819, 819,
-
819, 819, 819, 819, 819, 1, 824, 1,
-
819, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 819,
-
1, 819, 819, 819, 819, 819, 819, 819,
-
819, 819, 819, 819, 819, 819, 819, 819,
-
819, 819, 819, 819, 819, 819, 819, 819,
-
819, 819, 819, 819, 819, 819, 819, 819,
-
819, 819, 819, 819, 819, 819, 819, 819,
-
819, 819, 819, 819, 819, 819, 819, 819,
-
819, 819, 819, 819, 819, 819, 819, 819,
-
819, 819, 819, 819, 819, 819, 819, 819,
-
819, 819, 819, 819, 819, 819, 819, 819,
-
819, 819, 819, 819, 819, 819, 819, 819,
-
819, 819, 819, 819, 819, 819, 819, 819,
-
819, 819, 819, 819, 819, 819, 819, 819,
-
819, 819, 819, 819, 819, 819, 819, 819,
-
819, 819, 819, 819, 819, 819, 819, 819,
-
819, 819, 819, 819, 819, 819, 819, 819,
-
819, 819, 819, 819, 819, 819, 819, 819,
-
819, 1, 825, 1, 1, 1, 826, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 825, 1, 1, 1, 1, 1, 1,
-
1, 827, 1, 227, 1, 1, 1, 828,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 227, 1, 1, 1, 1, 1,
-
1, 1, 829, 1, 830, 1, 1, 1,
-
831, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 830, 1, 1, 1, 1,
-
1, 1, 1, 832, 1, 825, 1, 1,
-
1, 826, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 825, 1, 1, 1,
-
1, 1, 1, 1, 827, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
833, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 833,
-
1, 825, 1, 1, 1, 826, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
825, 1, 1, 1, 1, 1, 1, 1,
-
827, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 833, 1, 825,
-
1, 1, 1, 826, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 825, 1,
-
1, 1, 1, 1, 1, 1, 827, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 1, 1, 1, 1, 1, 1,
-
1, 1, 199, 1, 1, 0
-
]
-
-
1
class << self
-
1
attr_accessor :_received_trans_targs
-
1
private :_received_trans_targs, :_received_trans_targs=
-
end
-
1
self._received_trans_targs = [
-
2, 0, 3, 5, 9, 376, 287, 26,
-
116, 278, 2, 3, 5, 9, 376, 287,
-
26, 116, 278, 4, 2, 6, 7, 5,
-
9, 364, 293, 26, 116, 263, 278, 6,
-
7, 5, 9, 364, 365, 26, 116, 263,
-
278, 8, 6, 10, 361, 362, 325, 360,
-
10, 11, 13, 360, 12, 14, 15, 5,
-
9, 17, 293, 26, 116, 263, 278, 14,
-
15, 5, 9, 17, 18, 26, 116, 263,
-
278, 16, 14, 14, 15, 5, 9, 17,
-
18, 26, 116, 263, 278, 19, 20, 347,
-
352, 346, 287, 22, 341, 21, 22, 23,
-
25, 18, 263, 24, 22, 23, 5, 9,
-
25, 18, 26, 116, 263, 278, 27, 28,
-
30, 31, 96, 107, 109, 111, 114, 27,
-
28, 30, 31, 29, 27, 28, 30, 31,
-
96, 107, 109, 111, 114, 32, 33, 35,
-
36, 81, 83, 85, 88, 90, 92, 94,
-
34, 32, 33, 35, 36, 81, 83, 85,
-
88, 90, 92, 94, 37, 80, 38, 39,
-
41, 42, 40, 38, 39, 41, 42, 43,
-
44, 45, 47, 48, 46, 44, 45, 47,
-
48, 49, 50, 52, 53, 51, 49, 50,
-
52, 53, 54, 56, 57, 55, 54, 56,
-
57, 58, 59, 60, 62, 63, 73, 382,
-
385, 386, 387, 61, 59, 60, 62, 63,
-
64, 66, 67, 65, 64, 66, 67, 68,
-
69, 70, 72, 71, 69, 70, 72, 74,
-
75, 76, 78, 383, 82, 84, 86, 87,
-
89, 91, 93, 95, 97, 98, 99, 101,
-
102, 100, 98, 99, 101, 102, 103, 104,
-
106, 103, 104, 106, 105, 103, 104, 106,
-
108, 110, 112, 113, 115, 117, 118, 120,
-
198, 215, 216, 238, 220, 117, 118, 215,
-
216, 220, 119, 121, 122, 120, 172, 124,
-
184, 2, 133, 121, 122, 124, 125, 133,
-
123, 121, 122, 124, 125, 2, 133, 126,
-
128, 166, 171, 127, 129, 130, 132, 133,
-
131, 129, 130, 132, 133, 134, 135, 137,
-
147, 161, 156, 162, 134, 135, 137, 147,
-
161, 156, 162, 136, 138, 139, 141, 160,
-
2, 142, 140, 138, 139, 141, 142, 2,
-
143, 145, 146, 144, 143, 145, 146, 148,
-
158, 157, 149, 151, 150, 152, 153, 155,
-
154, 152, 153, 155, 159, 134, 135, 137,
-
147, 161, 156, 162, 163, 165, 164, 167,
-
168, 129, 170, 167, 168, 129, 170, 169,
-
126, 128, 166, 171, 173, 182, 181, 174,
-
176, 175, 177, 178, 180, 177, 178, 180,
-
179, 177, 178, 180, 183, 185, 186, 189,
-
188, 185, 186, 188, 187, 185, 186, 188,
-
190, 195, 196, 194, 190, 191, 193, 194,
-
192, 195, 196, 197, 199, 208, 209, 211,
-
207, 199, 200, 202, 207, 201, 203, 204,
-
206, 133, 203, 204, 206, 133, 205, 203,
-
204, 206, 133, 208, 209, 211, 210, 211,
-
212, 214, 133, 213, 211, 212, 214, 133,
-
117, 118, 120, 198, 215, 216, 238, 220,
-
217, 219, 218, 217, 219, 221, 222, 224,
-
244, 258, 253, 259, 221, 222, 224, 244,
-
258, 253, 259, 223, 225, 226, 228, 229,
-
257, 233, 239, 227, 225, 226, 228, 229,
-
239, 233, 229, 230, 232, 233, 231, 229,
-
230, 232, 233, 234, 235, 120, 198, 237,
-
238, 234, 235, 237, 236, 234, 235, 237,
-
238, 240, 242, 243, 241, 240, 242, 243,
-
245, 255, 254, 246, 248, 247, 249, 250,
-
252, 251, 249, 250, 252, 256, 221, 222,
-
224, 244, 258, 253, 259, 260, 262, 261,
-
264, 265, 267, 331, 339, 340, 264, 265,
-
267, 331, 339, 340, 278, 266, 268, 269,
-
306, 307, 271, 330, 26, 116, 278, 5,
-
9, 272, 116, 270, 268, 268, 269, 5,
-
9, 271, 272, 26, 116, 278, 273, 274,
-
277, 298, 305, 276, 275, 277, 9, 272,
-
26, 279, 281, 282, 280, 283, 284, 5,
-
286, 287, 287, 285, 283, 283, 284, 286,
-
287, 288, 289, 290, 292, 289, 290, 292,
-
287, 291, 289, 289, 290, 5, 9, 292,
-
287, 26, 116, 263, 278, 294, 295, 5,
-
9, 297, 293, 26, 116, 278, 294, 295,
-
5, 9, 297, 287, 26, 116, 278, 296,
-
294, 294, 295, 5, 9, 297, 287, 26,
-
116, 278, 299, 303, 302, 300, 301, 304,
-
273, 274, 276, 305, 308, 322, 323, 321,
-
308, 309, 311, 321, 310, 312, 313, 26,
-
312, 314, 313, 315, 319, 318, 316, 317,
-
320, 322, 323, 325, 324, 326, 327, 5,
-
9, 329, 18, 26, 116, 263, 278, 5,
-
9, 18, 116, 328, 326, 326, 327, 5,
-
9, 329, 18, 26, 116, 263, 278, 306,
-
332, 337, 336, 333, 335, 334, 338, 264,
-
265, 267, 331, 339, 340, 278, 342, 343,
-
22, 345, 342, 343, 22, 345, 344, 19,
-
20, 22, 341, 346, 348, 349, 351, 348,
-
349, 351, 18, 350, 348, 348, 349, 351,
-
18, 353, 357, 358, 356, 353, 354, 347,
-
356, 355, 357, 358, 359, 361, 362, 363,
-
6, 7, 364, 365, 366, 367, 375, 374,
-
369, 368, 370, 371, 373, 365, 365, 372,
-
370, 370, 371, 373, 365, 366, 367, 369,
-
374, 364, 2, 3, 376, 287, 378, 379,
-
378, 388, 381, 378, 379, 378, 388, 381,
-
380, 383, 77, 384, 77, 384, 383, 77,
-
384, 79
-
]
-
-
1
class << self
-
1
attr_accessor :_received_trans_actions
-
1
private :_received_trans_actions, :_received_trans_actions=
-
end
-
1
self._received_trans_actions = [
-
1, 0, 1, 1, 1, 2, 1, 3,
-
4, 5, 6, 6, 6, 6, 7, 6,
-
8, 9, 10, 0, 0, 11, 11, 11,
-
11, 12, 11, 13, 14, 15, 16, 17,
-
17, 18, 18, 19, 18, 20, 21, 22,
-
23, 0, 0, 24, 24, 24, 25, 24,
-
0, 0, 26, 0, 0, 27, 27, 27,
-
27, 28, 27, 29, 30, 31, 32, 33,
-
33, 34, 34, 35, 34, 36, 37, 38,
-
39, 0, 0, 40, 40, 41, 41, 42,
-
41, 43, 44, 45, 46, 0, 0, 0,
-
0, 47, 0, 0, 0, 0, 6, 6,
-
7, 6, 0, 0, 48, 48, 48, 48,
-
49, 48, 50, 51, 52, 53, 54, 54,
-
55, 54, 0, 0, 0, 0, 0, 0,
-
0, 47, 0, 0, 52, 52, 56, 52,
-
52, 52, 52, 52, 52, 0, 0, 47,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 52, 52, 56, 52, 52, 52, 52,
-
52, 52, 52, 52, 0, 0, 0, 0,
-
47, 0, 0, 52, 52, 56, 52, 0,
-
0, 0, 47, 57, 0, 52, 52, 56,
-
58, 0, 0, 47, 0, 0, 52, 52,
-
56, 52, 0, 47, 0, 0, 52, 56,
-
52, 0, 0, 0, 47, 0, 0, 0,
-
0, 0, 0, 0, 52, 52, 56, 52,
-
0, 47, 0, 0, 52, 56, 52, 0,
-
0, 0, 47, 0, 52, 52, 56, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 47,
-
0, 0, 52, 52, 56, 52, 54, 54,
-
59, 0, 0, 47, 0, 52, 52, 56,
-
0, 0, 0, 0, 0, 60, 60, 61,
-
61, 62, 63, 61, 63, 0, 0, 47,
-
0, 0, 0, 64, 64, 0, 0, 65,
-
0, 64, 15, 0, 0, 47, 0, 22,
-
0, 52, 52, 56, 52, 52, 66, 0,
-
0, 0, 47, 0, 0, 0, 47, 0,
-
0, 52, 52, 56, 52, 10, 10, 10,
-
10, 67, 10, 10, 0, 0, 0, 0,
-
47, 0, 0, 0, 0, 0, 47, 0,
-
68, 0, 0, 52, 52, 56, 52, 69,
-
0, 0, 47, 0, 52, 52, 56, 0,
-
0, 0, 0, 0, 0, 0, 0, 47,
-
0, 52, 52, 56, 0, 52, 52, 52,
-
52, 56, 52, 52, 0, 0, 0, 24,
-
24, 25, 24, 0, 0, 26, 0, 0,
-
52, 52, 52, 56, 0, 0, 0, 0,
-
0, 0, 64, 64, 65, 0, 0, 47,
-
0, 52, 52, 56, 0, 64, 64, 0,
-
65, 0, 0, 47, 0, 52, 52, 56,
-
24, 24, 24, 24, 0, 0, 26, 0,
-
0, 0, 0, 0, 24, 24, 24, 25,
-
24, 0, 0, 26, 0, 0, 64, 64,
-
65, 31, 0, 0, 47, 38, 0, 52,
-
52, 56, 45, 0, 0, 26, 0, 0,
-
0, 47, 70, 0, 52, 52, 56, 71,
-
52, 52, 72, 72, 56, 52, 72, 52,
-
0, 47, 0, 52, 56, 10, 10, 10,
-
10, 67, 10, 10, 0, 0, 0, 0,
-
47, 0, 0, 0, 0, 0, 47, 68,
-
0, 68, 0, 0, 52, 52, 56, 69,
-
52, 69, 0, 0, 47, 0, 0, 52,
-
52, 56, 52, 73, 73, 73, 73, 74,
-
73, 0, 0, 47, 0, 52, 52, 56,
-
0, 0, 0, 47, 0, 52, 52, 56,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
47, 0, 52, 52, 56, 0, 52, 52,
-
52, 52, 56, 52, 52, 0, 0, 0,
-
10, 10, 10, 10, 67, 10, 0, 0,
-
0, 0, 47, 0, 0, 0, 75, 75,
-
75, 75, 76, 75, 77, 78, 79, 80,
-
80, 80, 81, 0, 0, 82, 82, 83,
-
83, 84, 83, 85, 86, 87, 0, 0,
-
0, 0, 47, 0, 0, 75, 75, 75,
-
88, 0, 0, 0, 0, 75, 75, 75,
-
76, 75, 80, 0, 0, 82, 82, 84,
-
83, 0, 11, 11, 89, 17, 17, 19,
-
18, 0, 0, 90, 90, 91, 91, 92,
-
91, 93, 94, 66, 95, 96, 96, 96,
-
96, 97, 96, 98, 99, 100, 101, 101,
-
102, 102, 103, 102, 104, 105, 106, 0,
-
0, 107, 107, 108, 108, 109, 108, 110,
-
111, 112, 0, 0, 0, 0, 0, 0,
-
52, 52, 52, 56, 24, 24, 24, 24,
-
0, 0, 26, 0, 0, 75, 75, 113,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 26, 0, 114, 114, 114,
-
114, 115, 114, 116, 117, 70, 118, 119,
-
119, 119, 120, 0, 0, 121, 121, 122,
-
122, 123, 122, 124, 125, 71, 126, 0,
-
0, 0, 0, 0, 0, 0, 0, 52,
-
52, 52, 52, 56, 52, 52, 24, 24,
-
25, 24, 0, 0, 26, 0, 0, 52,
-
52, 52, 52, 56, 11, 11, 89, 17,
-
17, 19, 18, 0, 0, 90, 90, 92,
-
91, 24, 24, 24, 24, 0, 0, 26,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
90, 90, 92, 91, 0, 0, 0, 47,
-
0, 0, 75, 75, 76, 75, 80, 0,
-
0, 82, 82, 84, 83, 52, 52, 52,
-
56, 89, 48, 48, 49, 48, 127, 127,
-
128, 129, 127, 0, 0, 47, 130, 0,
-
0, 131, 131, 132, 0, 47, 52, 52,
-
56, 0
-
]
-
-
1
class << self
-
1
attr_accessor :_received_eof_actions
-
1
private :_received_eof_actions, :_received_eof_actions=
-
end
-
1
self._received_eof_actions = [
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 0, 0,
-
0, 0, 0, 0, 0, 0, 131, 0,
-
52, 131, 131, 131, 0
-
]
-
-
1
class << self
-
1
attr_accessor :received_start
-
end
-
1
self.received_start = 1;
-
1
class << self
-
1
attr_accessor :received_first_final
-
end
-
1
self.received_first_final = 382;
-
1
class << self
-
1
attr_accessor :received_error
-
end
-
1
self.received_error = 0;
-
-
1
class << self
-
1
attr_accessor :received_en_comment_tail
-
end
-
1
self.received_en_comment_tail = 377;
-
1
class << self
-
1
attr_accessor :received_en_main
-
end
-
1
self.received_en_main = 1;
-
-
-
# line 17 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/received_machine.rb.rl"
-
-
1
def self.parse(data)
-
p = 0
-
eof = data.length
-
stack = []
-
-
actions = []
-
data_unpacked = data.bytes.to_a
-
-
# line 3475 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/received_machine.rb"
-
begin
-
p ||= 0
-
pe ||= data.length
-
cs = received_start
-
top = 0
-
end
-
-
# line 26 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/received_machine.rb.rl"
-
-
# line 3485 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/received_machine.rb"
-
begin
-
testEof = false
-
_slen, _trans, _keys, _inds, _acts, _nacts = nil
-
_goto_level = 0
-
_resume = 10
-
_eof_trans = 15
-
_again = 20
-
_test_eof = 30
-
_out = 40
-
while true
-
if _goto_level <= 0
-
if p == pe
-
_goto_level = _test_eof
-
next
-
end
-
if cs == 0
-
_goto_level = _out
-
next
-
end
-
end
-
if _goto_level <= _resume
-
_keys = cs << 1
-
_inds = _received_index_offsets[cs]
-
_slen = _received_key_spans[cs]
-
_trans = if ( _slen > 0 &&
-
_received_trans_keys[_keys] <= ( data_unpacked[p]) &&
-
( data_unpacked[p]) <= _received_trans_keys[_keys + 1]
-
) then
-
_received_indicies[ _inds + ( data_unpacked[p]) - _received_trans_keys[_keys] ]
-
else
-
_received_indicies[ _inds + _slen ]
-
end
-
cs = _received_trans_targs[_trans]
-
if _received_trans_actions[_trans] != 0
-
case _received_trans_actions[_trans]
-
when 9 then
-
# line 6 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(3, p) end
-
when 52 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
when 127 then
-
# line 8 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(5, p) end
-
when 54 then
-
# line 12 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(9, p) end
-
when 68 then
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
when 10 then
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 22 then
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
when 64 then
-
# line 22 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(19, p) end
-
when 61 then
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
when 70 then
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
when 63 then
-
# line 34 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(31, p) end
-
when 26 then
-
# line 41 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(38, p) end
-
when 24 then
-
# line 42 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(39, p) end
-
when 8 then
-
# line 43 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(40, p) end
-
when 131 then
-
# line 47 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(44, p) end
-
when 47 then
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 377
-
_goto_level = _again
-
next
-
end
-
end
-
when 130 then
-
# line 6 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
top -= 1
-
cs = stack[top]
-
_goto_level = _again
-
next
-
end
-
end
-
when 81 then
-
# line 6 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(3, p) end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
when 105 then
-
# line 6 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(3, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
when 120 then
-
# line 6 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(3, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
when 51 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 6 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(3, p) end
-
when 69 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
when 53 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 66 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
when 72 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
when 71 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
when 50 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 43 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(40, p) end
-
when 56 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 377
-
_goto_level = _again
-
next
-
end
-
end
-
when 128 then
-
# line 8 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(5, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 377
-
_goto_level = _again
-
next
-
end
-
end
-
when 129 then
-
# line 8 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(5, p) end
-
# line 6 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
top -= 1
-
cs = stack[top]
-
_goto_level = _again
-
next
-
end
-
end
-
when 57 then
-
# line 11 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(8, p) end
-
# line 48 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(45, p) end
-
when 59 then
-
# line 12 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(9, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 377
-
_goto_level = _again
-
next
-
end
-
end
-
when 78 then
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 6 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(3, p) end
-
when 79 then
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 77 then
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 43 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(40, p) end
-
when 67 then
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 377
-
_goto_level = _again
-
next
-
end
-
end
-
when 106 then
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 38 then
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
when 104 then
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 43 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(40, p) end
-
when 15 then
-
# line 22 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(19, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
when 65 then
-
# line 22 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(19, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 377
-
_goto_level = _again
-
next
-
end
-
end
-
when 6 then
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 60 then
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 34 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(31, p) end
-
when 117 then
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
# line 6 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(3, p) end
-
when 118 then
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 116 then
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
# line 43 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(40, p) end
-
when 73 then
-
# line 33 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(30, p) end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
when 25 then
-
# line 42 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(39, p) end
-
# line 41 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(38, p) end
-
when 4 then
-
# line 44 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(41, p) end
-
# line 6 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(3, p) end
-
when 5 then
-
# line 44 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(41, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 3 then
-
# line 44 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(41, p) end
-
# line 43 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(40, p) end
-
when 132 then
-
# line 47 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(44, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 377
-
_goto_level = _again
-
next
-
end
-
end
-
when 55 then
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 377
-
_goto_level = _again
-
next
-
end
-
end
-
# line 12 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(9, p) end
-
when 21 then
-
# line 6 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(3, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
when 86 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 6 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(3, p) end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
when 111 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 6 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(3, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
when 125 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 6 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(3, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
when 58 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 11 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(8, p) end
-
# line 48 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(45, p) end
-
when 87 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 85 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 43 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(40, p) end
-
when 112 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 45 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
when 110 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 43 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(40, p) end
-
when 48 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 126 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 124 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
# line 43 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(40, p) end
-
when 75 then
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 23 then
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 20 then
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 43 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(40, p) end
-
when 101 then
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 99 then
-
# line 22 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(19, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 6 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(3, p) end
-
when 100 then
-
# line 22 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(19, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 31 then
-
# line 22 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(19, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
when 98 then
-
# line 22 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(19, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 43 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(40, p) end
-
when 80 then
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 102 then
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 119 then
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 62 then
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 377
-
_goto_level = _again
-
next
-
end
-
end
-
# line 34 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(31, p) end
-
when 114 then
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 74 then
-
# line 33 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(30, p) end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 377
-
_goto_level = _again
-
next
-
end
-
end
-
when 1 then
-
# line 44 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(41, p) end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 7 then
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 377
-
_goto_level = _again
-
next
-
end
-
end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 37 then
-
# line 6 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(3, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
when 94 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 6 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(3, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
when 82 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 95 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 93 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 43 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(40, p) end
-
when 107 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 83 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 108 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 122 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 121 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 49 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 377
-
_goto_level = _again
-
next
-
end
-
end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 88 then
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 22 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(19, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 43 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(40, p) end
-
when 17 then
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 39 then
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 36 then
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 43 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(40, p) end
-
when 14 then
-
# line 22 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(19, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 6 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(3, p) end
-
when 16 then
-
# line 22 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(19, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 13 then
-
# line 22 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(19, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 43 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(40, p) end
-
when 96 then
-
# line 22 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(19, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 18 then
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 2 then
-
# line 44 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(41, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 377
-
_goto_level = _again
-
next
-
end
-
end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 76 then
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 377
-
_goto_level = _again
-
next
-
end
-
end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 103 then
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 377
-
_goto_level = _again
-
next
-
end
-
end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 115 then
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 377
-
_goto_level = _again
-
next
-
end
-
end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 44 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 6 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(3, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
when 90 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 46 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 43 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 43 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(40, p) end
-
when 91 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 84 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 377
-
_goto_level = _again
-
next
-
end
-
end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 109 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 377
-
_goto_level = _again
-
next
-
end
-
end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 123 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 377
-
_goto_level = _again
-
next
-
end
-
end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 113 then
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 22 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(19, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
# line 43 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(40, p) end
-
when 33 then
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 11 then
-
# line 22 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(19, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 30 then
-
# line 22 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(19, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 6 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(3, p) end
-
when 32 then
-
# line 22 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(19, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 29 then
-
# line 22 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(19, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 43 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(40, p) end
-
when 97 then
-
# line 22 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(19, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 377
-
_goto_level = _again
-
next
-
end
-
end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 34 then
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 19 then
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 377
-
_goto_level = _again
-
next
-
end
-
end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 40 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 41 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 92 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 377
-
_goto_level = _again
-
next
-
end
-
end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 27 then
-
# line 22 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(19, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 89 then
-
# line 22 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(19, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 377
-
_goto_level = _again
-
next
-
end
-
end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 35 then
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 377
-
_goto_level = _again
-
next
-
end
-
end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 12 then
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 377
-
_goto_level = _again
-
next
-
end
-
end
-
# line 22 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(19, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 42 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 377
-
_goto_level = _again
-
next
-
end
-
end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
when 28 then
-
# line 5 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/../../common.rl"
-
begin
-
begin
-
stack[top] = cs
-
top+= 1
-
cs = 377
-
_goto_level = _again
-
next
-
end
-
end
-
# line 22 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(19, p) end
-
# line 21 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(18, p) end
-
# line 24 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(21, p) end
-
# line 15 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(12, p) end
-
# line 23 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(20, p) end
-
# line 16 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(13, p) end
-
# line 5093 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/received_machine.rb"
-
end
-
end
-
end
-
if _goto_level <= _again
-
if cs == 0
-
_goto_level = _out
-
next
-
end
-
p += 1
-
if p != pe
-
_goto_level = _resume
-
next
-
end
-
end
-
if _goto_level <= _test_eof
-
if p == eof
-
case _received_eof_actions[cs]
-
when 52 then
-
# line 7 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(4, p) end
-
when 131 then
-
# line 47 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl"
-
begin
-
actions.push(44, p) end
-
# line 5119 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/received_machine.rb"
-
end
-
end
-
-
end
-
if _goto_level <= _out
-
break
-
end
-
end
-
end
-
-
# line 27 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/received_machine.rb.rl"
-
-
if p == eof && cs >=
-
# line 5133 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/received_machine.rb"
-
382
-
# line 28 "/home/bpot/src/gh/bpot/mail/lib/mail/parsers/ragel/ruby/machines/received_machine.rb.rl"
-
-
return actions, nil
-
else
-
return [], "Only able to parse up to #{data[0..p]}"
-
end
-
end
-
end
-
end
-
end
-
end
-
1
module Mail::Parsers
-
1
class ReceivedParser
-
-
1
def parse(s)
-
actions, error = Ragel.parse(:received, s)
-
if error
-
raise Mail::Field::ParseError.new(Mail::ReceivedElement, s, error)
-
end
-
-
received = ReceivedStruct.new
-
-
received_tokens_s = date_s = time_s = nil
-
actions.each_slice(2) do |action_id, p|
-
action = Mail::Parsers::Ragel::ACTIONS[action_id]
-
case action
-
-
# Received Tokens:
-
when :received_tokens_s then received_tokens_s = p
-
when :received_tokens_e
-
received.info = s[received_tokens_s..(p-1)]
-
-
# Date
-
when :date_s then date_s = p
-
when :date_e
-
received.date = s[date_s..(p-1)].strip
-
-
# Time
-
when :time_s then time_s = p
-
when :time_e
-
received.time = s[time_s..(p-1)]
-
-
when :angle_addr_s, :comment_e, :comment_s,
-
:domain_e, :domain_s, :local_dot_atom_e,
-
:local_dot_atom_pre_comment_e,
-
:local_dot_atom_pre_comment_s,
-
:local_dot_atom_s, :qstr_e, :qstr_s,
-
:local_quoted_string_s, :local_quoted_string_e
-
# ignored actions
-
-
else
-
raise Mail::Field::ParseError.new(Mail::ReceivedElement, s, "Failed to process unknown action: #{action}")
-
end
-
end
-
received
-
end
-
end
-
end
-
# encoding: utf-8
-
1
module Mail
-
1
class Part < Message
-
-
# Creates a new empty Content-ID field and inserts it in the correct order
-
# into the Header. The ContentIdField object will automatically generate
-
# a unique content ID if you try and encode it or output it to_s without
-
# specifying a content id.
-
#
-
# It will preserve the content ID you specify if you do.
-
1
def add_content_id(content_id_val = '')
-
header['content-id'] = content_id_val
-
end
-
-
# Returns true if the part has a content ID field, the field may or may
-
# not have a value, but the field exists or not.
-
1
def has_content_id?
-
header.has_content_id?
-
end
-
-
1
def inline_content_id
-
# TODO: Deprecated in 2.2.2 - Remove in 2.3
-
STDERR.puts("Part#inline_content_id is deprecated, please call Part#cid instead")
-
cid
-
end
-
-
1
def cid
-
add_content_id unless has_content_id?
-
uri_escape(unbracket(content_id))
-
end
-
-
1
def url
-
"cid:#{cid}"
-
end
-
-
1
def inline?
-
header[:content_disposition].disposition_type == 'inline' if header[:content_disposition]
-
end
-
-
1
def add_required_fields
-
super
-
add_content_id if !has_content_id? && inline?
-
end
-
-
1
def add_required_message_fields
-
# Override so we don't add Date, MIME-Version, or Message-ID.
-
end
-
-
1
def delivery_status_report_part?
-
(main_type =~ /message/i && sub_type =~ /delivery-status/i) && body =~ /Status:/
-
end
-
-
1
def delivery_status_data
-
delivery_status_report_part? ? parse_delivery_status_report : {}
-
end
-
-
1
def bounced?
-
if action.is_a?(Array)
-
!!(action.first =~ /failed/i)
-
else
-
!!(action =~ /failed/i)
-
end
-
end
-
-
-
# Either returns the action if the message has just a single report, or an
-
# array of all the actions, one for each report
-
1
def action
-
get_return_values('action')
-
end
-
-
1
def final_recipient
-
get_return_values('final-recipient')
-
end
-
-
1
def error_status
-
get_return_values('status')
-
end
-
-
1
def diagnostic_code
-
get_return_values('diagnostic-code')
-
end
-
-
1
def remote_mta
-
get_return_values('remote-mta')
-
end
-
-
1
def retryable?
-
!(error_status =~ /^5/)
-
end
-
-
1
private
-
-
1
def get_return_values(key)
-
if delivery_status_data[key].is_a?(Array)
-
delivery_status_data[key].map { |a| a.value }
-
else
-
delivery_status_data[key].value
-
end
-
end
-
-
# A part may not have a header.... so, just init a body if no header
-
1
def parse_message
-
header_part, body_part = raw_source.split(/#{CRLF}#{WSP}*#{CRLF}/m, 2)
-
if header_part =~ HEADER_LINE
-
self.header = header_part
-
self.body = body_part
-
else
-
self.header = "Content-Type: text/plain\r\n"
-
self.body = raw_source
-
end
-
end
-
-
1
def parse_delivery_status_report
-
@delivery_status_data ||= Header.new(body.to_s.gsub("\r\n\r\n", "\r\n"))
-
end
-
-
end
-
-
end
-
1
module Mail
-
1
class PartsList < Array
-
-
1
def attachments
-
Mail::AttachmentsList.new(self)
-
end
-
-
1
def collect
-
if block_given?
-
ary = PartsList.new
-
each { |o| ary << yield(o) }
-
ary
-
else
-
to_a
-
end
-
end
-
-
1
undef :map
-
1
alias_method :map, :collect
-
-
1
def map!
-
raise NoMethodError, "#map! is not defined, please call #collect and create a new PartsList"
-
end
-
-
1
def collect!
-
raise NoMethodError, "#collect! is not defined, please call #collect and create a new PartsList"
-
end
-
-
1
def sort
-
self.class.new(super)
-
end
-
-
1
def sort!(order)
-
# stable sort should be used to maintain the relative order as the parts are added
-
i = 0;
-
sorted = self.sort_by do |a|
-
# OK, 10000 is arbitrary... if anyone actually wants to explicitly sort 10000 parts of a
-
# single email message... please show me a use case and I'll put more work into this method,
-
# in the meantime, it works :)
-
[get_order_value(a, order), i += 1]
-
end
-
self.clear
-
sorted.each { |p| self << p }
-
end
-
-
1
private
-
-
1
def get_order_value(part, order)
-
if part.respond_to?(:content_type) && !part[:content_type].nil?
-
order.index(part[:content_type].string.downcase) || 10000
-
else
-
10000
-
end
-
end
-
-
end
-
end
-
# encoding: us-ascii
-
1
module Mail
-
1
module Patterns
-
1
white_space = %Q|\x9\x20|
-
1
text = %Q|\x1-\x8\xB\xC\xE-\x7f|
-
1
field_name = %Q|\x21-\x39\x3b-\x7e|
-
1
qp_safe = %Q|\x20-\x3c\x3e-\x7e|
-
-
1
aspecial = %Q|()<>[]:;@\\,."| # RFC5322
-
1
tspecial = %Q|()<>@,;:\\"/[]?=| # RFC2045
-
1
sp = %Q| |
-
1
control = %Q|\x00-\x1f\x7f-\xff|
-
-
1
if control.respond_to?(:force_encoding)
-
1
control = control.force_encoding(Encoding::BINARY)
-
end
-
-
1
CRLF = /\r\n/
-
1
WSP = /[#{white_space}]/
-
1
FWS = /#{CRLF}#{WSP}*/
-
1
TEXT = /[#{text}]/ # + obs-text
-
1
FIELD_NAME = /[#{field_name}]+/
-
1
FIELD_PREFIX = /\A(#{FIELD_NAME})/
-
1
FIELD_BODY = /.+/m
-
1
FIELD_LINE = /^[#{field_name}]+:\s*.+$/
-
1
FIELD_SPLIT = /^(#{FIELD_NAME})\s*:\s*(#{FIELD_BODY})?$/
-
1
HEADER_LINE = /^([#{field_name}]+:\s*.+)$/
-
1
HEADER_SPLIT = /#{CRLF}(?!#{WSP})/
-
-
1
QP_UNSAFE = /[^#{qp_safe}]/
-
1
QP_SAFE = /[#{qp_safe}]/
-
1
CONTROL_CHAR = /[#{control}]/n
-
1
ATOM_UNSAFE = /[#{Regexp.quote aspecial}#{control}#{sp}]/n
-
1
PHRASE_UNSAFE = /[#{Regexp.quote aspecial}#{control}]/n
-
1
TOKEN_UNSAFE = /[#{Regexp.quote tspecial}#{control}#{sp}]/n
-
end
-
end
-
# encoding: utf-8
-
1
module Mail
-
1
module Utilities
-
1
include Patterns
-
-
# Returns true if the string supplied is free from characters not allowed as an ATOM
-
1
def atom_safe?( str )
-
not ATOM_UNSAFE === str
-
end
-
-
# If the string supplied has ATOM unsafe characters in it, will return the string quoted
-
# in double quotes, otherwise returns the string unmodified
-
1
def quote_atom( str )
-
atom_safe?( str ) ? str : dquote(str)
-
end
-
-
# If the string supplied has PHRASE unsafe characters in it, will return the string quoted
-
# in double quotes, otherwise returns the string unmodified
-
1
def quote_phrase( str )
-
if RUBY_VERSION >= '1.9'
-
original_encoding = str.encoding
-
str.force_encoding('ASCII-8BIT')
-
if (PHRASE_UNSAFE === str)
-
quoted_str = dquote(str).force_encoding(original_encoding)
-
str.force_encoding(original_encoding)
-
quoted_str
-
else
-
str.force_encoding(original_encoding)
-
end
-
else
-
(PHRASE_UNSAFE === str) ? dquote(str) : str
-
end
-
end
-
-
# Returns true if the string supplied is free from characters not allowed as a TOKEN
-
1
def token_safe?( str )
-
not TOKEN_UNSAFE === str
-
end
-
-
# If the string supplied has TOKEN unsafe characters in it, will return the string quoted
-
# in double quotes, otherwise returns the string unmodified
-
1
def quote_token( str )
-
token_safe?( str ) ? str : dquote(str)
-
end
-
-
# Wraps supplied string in double quotes and applies \-escaping as necessary,
-
# unless it is already wrapped.
-
#
-
# Example:
-
#
-
# string = 'This is a string'
-
# dquote(string) #=> '"This is a string"'
-
#
-
# string = 'This is "a string"'
-
# dquote(string #=> '"This is \"a string\"'
-
1
def dquote( str )
-
'"' + unquote(str).gsub(/[\\"]/n) {|s| '\\' + s } + '"'
-
end
-
-
# Unwraps supplied string from inside double quotes and
-
# removes any \-escaping.
-
#
-
# Example:
-
#
-
# string = '"This is a string"'
-
# unquote(string) #=> 'This is a string'
-
#
-
# string = '"This is \"a string\""'
-
# unqoute(string) #=> 'This is "a string"'
-
1
def unquote( str )
-
if str =~ /^"(.*?)"$/
-
$1.gsub(/\\(.)/, '\1')
-
else
-
str
-
end
-
end
-
-
# Wraps a string in parenthesis and escapes any that are in the string itself.
-
#
-
# Example:
-
#
-
# paren( 'This is a string' ) #=> '(This is a string)'
-
1
def paren( str )
-
RubyVer.paren( str )
-
end
-
-
# Unwraps a string from being wrapped in parenthesis
-
#
-
# Example:
-
#
-
# str = '(This is a string)'
-
# unparen( str ) #=> 'This is a string'
-
1
def unparen( str )
-
match = str.match(/^\((.*?)\)$/)
-
match ? match[1] : str
-
end
-
-
# Wraps a string in angle brackets and escapes any that are in the string itself
-
#
-
# Example:
-
#
-
# bracket( 'This is a string' ) #=> '<This is a string>'
-
1
def bracket( str )
-
RubyVer.bracket( str )
-
end
-
-
# Unwraps a string from being wrapped in parenthesis
-
#
-
# Example:
-
#
-
# str = '<This is a string>'
-
# unbracket( str ) #=> 'This is a string'
-
1
def unbracket( str )
-
match = str.match(/^\<(.*?)\>$/)
-
match ? match[1] : str
-
end
-
-
# Escape parenthesies in a string
-
#
-
# Example:
-
#
-
# str = 'This is (a) string'
-
# escape_paren( str ) #=> 'This is \(a\) string'
-
1
def escape_paren( str )
-
RubyVer.escape_paren( str )
-
end
-
-
1
def uri_escape( str )
-
uri_parser.escape(str)
-
end
-
-
1
def uri_unescape( str )
-
uri_parser.unescape(str)
-
end
-
-
1
def uri_parser
-
@uri_parser ||= URI.const_defined?(:Parser) ? URI::Parser.new : URI
-
end
-
-
# Matches two objects with their to_s values case insensitively
-
#
-
# Example:
-
#
-
# obj2 = "This_is_An_object"
-
# obj1 = :this_IS_an_object
-
# match_to_s( obj1, obj2 ) #=> true
-
1
def match_to_s( obj1, obj2 )
-
obj1.to_s.casecmp(obj2.to_s) == 0
-
end
-
-
# Capitalizes a string that is joined by hyphens correctly.
-
#
-
# Example:
-
#
-
# string = 'resent-from-field'
-
# capitalize_field( string ) #=> 'Resent-From-Field'
-
1
def capitalize_field( str )
-
str.to_s.split("-").map { |v| v.capitalize }.join("-")
-
end
-
-
# Takes an underscored word and turns it into a class name
-
#
-
# Example:
-
#
-
# constantize("hello") #=> "Hello"
-
# constantize("hello-there") #=> "HelloThere"
-
# constantize("hello-there-mate") #=> "HelloThereMate"
-
1
def constantize( str )
-
str.to_s.split(/[-_]/).map { |v| v.capitalize }.to_s
-
end
-
-
# Swaps out all underscores (_) for hyphens (-) good for stringing from symbols
-
# a field name.
-
#
-
# Example:
-
#
-
# string = :resent_from_field
-
# dasherize ( string ) #=> 'resent_from_field'
-
1
def dasherize( str )
-
str.to_s.gsub('_', '-')
-
end
-
-
# Swaps out all hyphens (-) for underscores (_) good for stringing to symbols
-
# a field name.
-
#
-
# Example:
-
#
-
# string = :resent_from_field
-
# underscoreize ( string ) #=> 'resent_from_field'
-
1
def underscoreize( str )
-
str.to_s.downcase.gsub('-', '_')
-
end
-
-
1
if RUBY_VERSION <= '1.8.6'
-
-
def map_lines( str, &block )
-
results = []
-
str.each_line do |line|
-
results << yield(line)
-
end
-
results
-
end
-
-
def map_with_index( enum, &block )
-
results = []
-
enum.each_with_index do |token, i|
-
results[i] = yield(token, i)
-
end
-
results
-
end
-
-
else
-
-
1
def map_lines( str, &block )
-
str.each_line.map(&block)
-
end
-
-
1
def map_with_index( enum, &block )
-
enum.each_with_index.map(&block)
-
end
-
-
end
-
-
end
-
end
-
# encoding: utf-8
-
1
module Mail
-
1
module VERSION
-
-
1
version = {}
-
1
File.read(File.join(File.dirname(__FILE__), '../', '../', 'VERSION')).each_line do |line|
-
4
type, value = line.chomp.split(":")
-
4
next if type =~ /^\s+$/ || value =~ /^\s+$/
-
4
version[type] = value
-
end
-
-
1
MAJOR = version['major']
-
1
MINOR = version['minor']
-
1
PATCH = version['patch']
-
1
BUILD = version['build']
-
-
1
STRING = [MAJOR, MINOR, PATCH, BUILD].compact.join('.')
-
-
1
def self.version
-
STRING
-
end
-
-
end
-
end
-
# encoding: utf-8
-
-
1
module Mail
-
1
class Ruby19
-
-
# Escapes any parenthesis in a string that are unescaped this uses
-
# a Ruby 1.9.1 regexp feature of negative look behind
-
1
def Ruby19.escape_paren( str )
-
re = /(?<!\\)([\(\)])/ # Only match unescaped parens
-
str.gsub(re) { |s| '\\' + s }
-
end
-
-
1
def Ruby19.paren( str )
-
str = $1 if str =~ /^\((.*)?\)$/
-
str = escape_paren( str )
-
'(' + str + ')'
-
end
-
-
1
def Ruby19.escape_bracket( str )
-
re = /(?<!\\)([\<\>])/ # Only match unescaped brackets
-
str.gsub(re) { |s| '\\' + s }
-
end
-
-
1
def Ruby19.bracket( str )
-
str = $1 if str =~ /^\<(.*)?\>$/
-
str = escape_bracket( str )
-
'<' + str + '>'
-
end
-
-
1
def Ruby19.decode_base64(str)
-
str.unpack( 'm' ).first
-
end
-
-
1
def Ruby19.encode_base64(str)
-
[str].pack( 'm' )
-
end
-
-
1
def Ruby19.has_constant?(klass, string)
-
klass.const_defined?( string, false )
-
end
-
-
1
def Ruby19.get_constant(klass, string)
-
klass.const_get( string )
-
end
-
-
1
def Ruby19.b_value_encode(str, encoding = nil)
-
encoding = str.encoding.to_s
-
[Ruby19.encode_base64(str), encoding]
-
end
-
-
1
def Ruby19.b_value_decode(str)
-
match = str.match(/\=\?(.+)?\?[Bb]\?(.+)?\?\=/m)
-
if match
-
charset = match[1]
-
str = Ruby19.decode_base64(match[2])
-
str.force_encoding(pick_encoding(charset))
-
end
-
decoded = str.encode("utf-8", :invalid => :replace, :replace => "")
-
decoded.valid_encoding? ? decoded : decoded.encode("utf-16le", :invalid => :replace, :replace => "").encode("utf-8")
-
end
-
-
1
def Ruby19.q_value_encode(str, encoding = nil)
-
encoding = str.encoding.to_s
-
[Encodings::QuotedPrintable.encode(str), encoding]
-
end
-
-
1
def Ruby19.q_value_decode(str)
-
match = str.match(/\=\?(.+)?\?[Qq]\?(.+)?\?\=/m)
-
if match
-
charset = match[1]
-
string = match[2].gsub(/_/, '=20')
-
# Remove trailing = if it exists in a Q encoding
-
string = string.sub(/\=$/, '')
-
str = Encodings::QuotedPrintable.decode(string)
-
str.force_encoding(pick_encoding(charset))
-
# We assume that binary strings hold utf-8 directly to work around
-
# jruby/jruby#829 which subtly changes String#encode semantics.
-
str.force_encoding('utf-8') if str.encoding == Encoding::ASCII_8BIT
-
end
-
decoded = str.encode("utf-8", :invalid => :replace, :replace => "")
-
decoded.valid_encoding? ? decoded : decoded.encode("utf-16le", :invalid => :replace, :replace => "").encode("utf-8")
-
rescue Encoding::UndefinedConversionError
-
str.dup.force_encoding("utf-8")
-
end
-
-
1
def Ruby19.param_decode(str, encoding)
-
string = uri_parser.unescape(str)
-
string.force_encoding(encoding) if encoding
-
string
-
end
-
-
1
def Ruby19.param_encode(str)
-
encoding = str.encoding.to_s.downcase
-
language = Configuration.instance.param_encode_language
-
"#{encoding}'#{language}'#{uri_parser.escape(str)}"
-
end
-
-
1
def Ruby19.uri_parser
-
@uri_parser ||= URI::Parser.new
-
end
-
-
# Pick a Ruby encoding corresponding to the message charset. Most
-
# charsets have a Ruby encoding, but some need manual aliasing here.
-
#
-
# TODO: add this as a test somewhere:
-
# Encoding.list.map { |e| [e.to_s.upcase == pick_encoding(e.to_s.downcase.gsub("-", "")), e.to_s] }.select {|a,b| !b}
-
# Encoding.list.map { |e| [e.to_s == pick_encoding(e.to_s), e.to_s] }.select {|a,b| !b}
-
1
def Ruby19.pick_encoding(charset)
-
case charset
-
-
# ISO-8859-8-I etc. http://en.wikipedia.org/wiki/ISO-8859-8-I
-
when /^iso-?8859-(\d+)(-i)?$/i
-
"ISO-8859-#{$1}"
-
-
# ISO-8859-15, ISO-2022-JP and alike
-
when /iso-?(\d{4})-?(\w{1,2})/i
-
"ISO-#{$1}-#{$2}"
-
-
# "ISO-2022-JP-KDDI" and alike
-
when /iso-?(\d{4})-?(\w{1,2})-?(\w*)/i
-
"ISO-#{$1}-#{$2}-#{$3}"
-
-
# UTF-8, UTF-32BE and alike
-
when /utf[\-_]?(\d{1,2})?(\w{1,2})/i
-
"UTF-#{$1}#{$2}".gsub(/\A(UTF-(?:16|32))\z/, '\\1BE')
-
-
# Windows-1252 and alike
-
when /Windows-?(.*)/i
-
"Windows-#{$1}"
-
-
when /^8bit$/
-
Encoding::ASCII_8BIT
-
-
# alternatives/misspellings of us-ascii seen in the wild
-
when /^iso-?646(-us)?$/i, /us=ascii/i
-
Encoding::ASCII
-
-
# Microsoft-specific alias for MACROMAN
-
when /^macintosh$/i
-
Encoding::MACROMAN
-
-
# Microsoft-specific alias for CP949 (Korean)
-
when 'ks_c_5601-1987'
-
Encoding::CP949
-
-
# Wrongly written Shift_JIS (Japanese)
-
when 'shift-jis'
-
Encoding::Shift_JIS
-
-
# GB2312 (Chinese charset) is a subset of GB18030 (its replacement)
-
when /gb2312/i
-
Encoding::GB18030
-
-
else
-
charset
-
end
-
end
-
end
-
end
-
# (C) John Mair (banisterfiend) 2011
-
# MIT License
-
-
1
direc = File.dirname(__FILE__)
-
-
1
require "#{direc}/method_source/version"
-
1
require "#{direc}/method_source/source_location"
-
1
require "#{direc}/method_source/code_helpers"
-
-
1
module MethodSource
-
1
extend MethodSource::CodeHelpers
-
-
# An Exception to mark errors that were raised trying to find the source from
-
# a given source_location.
-
#
-
1
class SourceNotFoundError < StandardError; end
-
-
# Helper method responsible for extracting method body.
-
# Defined here to avoid polluting `Method` class.
-
# @param [Array] source_location The array returned by Method#source_location
-
# @param [String] method_name
-
# @return [String] The method body
-
1
def self.source_helper(source_location, name=nil)
-
raise SourceNotFoundError, "Could not locate source for #{name}!" unless source_location
-
file, line = *source_location
-
-
expression_at(lines_for(file), line)
-
rescue SyntaxError => e
-
raise SourceNotFoundError, "Could not parse source for #{name}: #{e.message}"
-
end
-
-
# Helper method responsible for opening source file and buffering up
-
# the comments for a specified method. Defined here to avoid polluting
-
# `Method` class.
-
# @param [Array] source_location The array returned by Method#source_location
-
# @param [String] method_name
-
# @return [String] The comments up to the point of the method.
-
1
def self.comment_helper(source_location, name=nil)
-
raise SourceNotFoundError, "Could not locate source for #{name}!" unless source_location
-
file, line = *source_location
-
-
comment_describing(lines_for(file), line)
-
end
-
-
# Load a memoized copy of the lines in a file.
-
#
-
# @param [String] file_name
-
# @param [String] method_name
-
# @return [Array<String>] the contents of the file
-
# @raise [SourceNotFoundError]
-
1
def self.lines_for(file_name, name=nil)
-
@lines_for_file ||= {}
-
@lines_for_file[file_name] ||= File.readlines(file_name)
-
rescue Errno::ENOENT => e
-
raise SourceNotFoundError, "Could not load source for #{name}: #{e.message}"
-
end
-
-
# @deprecated — use MethodSource::CodeHelpers#complete_expression?
-
1
def self.valid_expression?(str)
-
complete_expression?(str)
-
rescue SyntaxError
-
false
-
end
-
-
# @deprecated — use MethodSource::CodeHelpers#expression_at
-
1
def self.extract_code(source_location)
-
source_helper(source_location)
-
end
-
-
# This module is to be included by `Method` and `UnboundMethod` and
-
# provides the `#source` functionality
-
1
module MethodExtensions
-
-
# We use the included hook to patch Method#source on rubinius.
-
# We need to use the included hook as Rubinius defines a `source`
-
# on Method so including a module will have no effect (as it's
-
# higher up the MRO).
-
# @param [Class] klass The class that includes the module.
-
1
def self.included(klass)
-
3
if klass.method_defined?(:source) && Object.const_defined?(:RUBY_ENGINE) &&
-
RUBY_ENGINE =~ /rbx/
-
-
klass.class_eval do
-
orig_source = instance_method(:source)
-
-
define_method(:source) do
-
begin
-
super
-
rescue
-
orig_source.bind(self).call
-
end
-
end
-
-
end
-
end
-
end
-
-
# Return the sourcecode for the method as a string
-
# @return [String] The method sourcecode as a string
-
# @raise SourceNotFoundException
-
#
-
# @example
-
# Set.instance_method(:clear).source.display
-
# =>
-
# def clear
-
# @hash.clear
-
# self
-
# end
-
1
def source
-
MethodSource.source_helper(source_location, defined?(name) ? name : inspect)
-
end
-
-
# Return the comments associated with the method as a string.
-
# @return [String] The method's comments as a string
-
# @raise SourceNotFoundException
-
#
-
# @example
-
# Set.instance_method(:clear).comment.display
-
# =>
-
# # Removes all elements and returns self.
-
1
def comment
-
MethodSource.comment_helper(source_location, defined?(name) ? name : inspect)
-
end
-
end
-
end
-
-
1
class Method
-
1
include MethodSource::SourceLocation::MethodExtensions
-
1
include MethodSource::MethodExtensions
-
end
-
-
1
class UnboundMethod
-
1
include MethodSource::SourceLocation::UnboundMethodExtensions
-
1
include MethodSource::MethodExtensions
-
end
-
-
1
class Proc
-
1
include MethodSource::SourceLocation::ProcExtensions
-
1
include MethodSource::MethodExtensions
-
end
-
-
1
module MethodSource
-
-
1
module CodeHelpers
-
# Retrieve the first expression starting on the given line of the given file.
-
#
-
# This is useful to get module or method source code.
-
#
-
# @param [Array<String>, File, String] file The file to parse, either as a File or as
-
# @param [Fixnum] line_number The line number at which to look.
-
# NOTE: The first line in a file is
-
# line 1!
-
# @param [Hash] options The optional configuration parameters.
-
# @option options [Boolean] :strict If set to true, then only completely
-
# valid expressions are returned. Otherwise heuristics are used to extract
-
# expressions that may have been valid inside an eval.
-
# @option options [Fixnum] :consume A number of lines to automatically
-
# consume (add to the expression buffer) without checking for validity.
-
# @return [String] The first complete expression
-
# @raise [SyntaxError] If the first complete expression can't be identified
-
1
def expression_at(file, line_number, options={})
-
options = {
-
:strict => false,
-
:consume => 0
-
}.merge!(options)
-
-
lines = file.is_a?(Array) ? file : file.each_line.to_a
-
-
relevant_lines = lines[(line_number - 1)..-1] || []
-
-
extract_first_expression(relevant_lines, options[:consume])
-
rescue SyntaxError => e
-
raise if options[:strict]
-
-
begin
-
extract_first_expression(relevant_lines) do |code|
-
code.gsub(/\#\{.*?\}/, "temp")
-
end
-
rescue SyntaxError
-
raise e
-
end
-
end
-
-
# Retrieve the comment describing the expression on the given line of the given file.
-
#
-
# This is useful to get module or method documentation.
-
#
-
# @param [Array<String>, File, String] file The file to parse, either as a File or as
-
# a String or an Array of lines.
-
# @param [Fixnum] line_number The line number at which to look.
-
# NOTE: The first line in a file is line 1!
-
# @return [String] The comment
-
1
def comment_describing(file, line_number)
-
lines = file.is_a?(Array) ? file : file.each_line.to_a
-
-
extract_last_comment(lines[0..(line_number - 2)])
-
end
-
-
# Determine if a string of code is a complete Ruby expression.
-
# @param [String] code The code to validate.
-
# @return [Boolean] Whether or not the code is a complete Ruby expression.
-
# @raise [SyntaxError] Any SyntaxError that does not represent incompleteness.
-
# @example
-
# complete_expression?("class Hello") #=> false
-
# complete_expression?("class Hello; end") #=> true
-
# complete_expression?("class 123") #=> SyntaxError: unexpected tINTEGER
-
1
def complete_expression?(str)
-
old_verbose = $VERBOSE
-
$VERBOSE = nil
-
-
catch(:valid) do
-
eval("BEGIN{throw :valid}\n#{str}")
-
end
-
-
# Assert that a line which ends with a , or \ is incomplete.
-
str !~ /[,\\]\s*\z/
-
rescue IncompleteExpression
-
false
-
ensure
-
$VERBOSE = old_verbose
-
end
-
-
1
private
-
-
# Get the first expression from the input.
-
#
-
# @param [Array<String>] lines
-
# @param [Fixnum] consume A number of lines to automatically
-
# consume (add to the expression buffer) without checking for validity.
-
# @yield a clean-up function to run before checking for complete_expression
-
# @return [String] a valid ruby expression
-
# @raise [SyntaxError]
-
1
def extract_first_expression(lines, consume=0, &block)
-
code = consume.zero? ? "" : lines.slice!(0..(consume - 1)).join
-
-
lines.each do |v|
-
code << v
-
return code if complete_expression?(block ? block.call(code) : code)
-
end
-
raise SyntaxError, "unexpected $end"
-
end
-
-
# Get the last comment from the input.
-
#
-
# @param [Array<String>] lines
-
# @return [String]
-
1
def extract_last_comment(lines)
-
buffer = ""
-
-
lines.each do |line|
-
# Add any line that is a valid ruby comment,
-
# but clear as soon as we hit a non comment line.
-
if (line =~ /^\s*#/) || (line =~ /^\s*$/)
-
buffer << line.lstrip
-
else
-
buffer.replace("")
-
end
-
end
-
-
buffer
-
end
-
-
# An exception matcher that matches only subsets of SyntaxErrors that can be
-
# fixed by adding more input to the buffer.
-
1
module IncompleteExpression
-
1
GENERIC_REGEXPS = [
-
/unexpected (\$end|end-of-file|end-of-input|END_OF_FILE)/, # mri, jruby, ruby-2.0, ironruby
-
/embedded document meets end of file/, # =begin
-
/unterminated (quoted string|string|regexp) meets end of file/, # "quoted string" is ironruby
-
/can't find string ".*" anywhere before EOF/, # rbx and jruby
-
/missing 'end' for/, /expecting kWHEN/ # rbx
-
]
-
-
1
RBX_ONLY_REGEXPS = [
-
/expecting '[})\]]'(?:$|:)/, /expecting keyword_end/
-
]
-
-
1
def self.===(ex)
-
return false unless SyntaxError === ex
-
case ex.message
-
when *GENERIC_REGEXPS
-
true
-
when *RBX_ONLY_REGEXPS
-
rbx?
-
else
-
false
-
end
-
end
-
-
1
def self.rbx?
-
RbConfig::CONFIG['ruby_install_name'] == 'rbx'
-
end
-
end
-
end
-
end
-
1
module MethodSource
-
1
module ReeSourceLocation
-
# Ruby enterprise edition provides all the information that's
-
# needed, in a slightly different way.
-
1
def source_location
-
[__file__, __line__] rescue nil
-
end
-
end
-
-
1
module SourceLocation
-
1
module MethodExtensions
-
1
if Proc.method_defined? :__file__
-
include ReeSourceLocation
-
-
elsif defined?(RUBY_ENGINE) && RUBY_ENGINE =~ /jruby/
-
require 'java'
-
-
# JRuby version source_location hack
-
# @return [Array] A two element array containing the source location of the method
-
def source_location
-
to_java.source_location(Thread.current.to_java.getContext())
-
end
-
else
-
-
-
1
def trace_func(event, file, line, id, binding, classname)
-
return unless event == 'call'
-
set_trace_func nil
-
-
@file, @line = file, line
-
raise :found
-
end
-
-
1
private :trace_func
-
-
# Return the source location of a method for Ruby 1.8.
-
# @return [Array] A two element array. First element is the
-
# file, second element is the line in the file where the
-
# method definition is found.
-
1
def source_location
-
if @file.nil?
-
args =[*(1..(arity<-1 ? -arity-1 : arity ))]
-
-
set_trace_func method(:trace_func).to_proc
-
call(*args) rescue nil
-
set_trace_func nil
-
@file = File.expand_path(@file) if @file && File.exist?(File.expand_path(@file))
-
end
-
[@file, @line] if @file
-
end
-
end
-
end
-
-
1
module ProcExtensions
-
1
if Proc.method_defined? :__file__
-
include ReeSourceLocation
-
-
elsif defined?(RUBY_ENGINE) && RUBY_ENGINE =~ /rbx/
-
-
# Return the source location for a Proc (Rubinius only)
-
# @return [Array] A two element array. First element is the
-
# file, second element is the line in the file where the
-
# proc definition is found.
-
def source_location
-
[block.file.to_s, block.line]
-
end
-
else
-
-
# Return the source location for a Proc (in implementations
-
# without Proc#source_location)
-
# @return [Array] A two element array. First element is the
-
# file, second element is the line in the file where the
-
# proc definition is found.
-
1
def source_location
-
self.to_s =~ /@(.*):(\d+)/
-
[$1, $2.to_i]
-
end
-
end
-
end
-
-
1
module UnboundMethodExtensions
-
1
if Proc.method_defined? :__file__
-
include ReeSourceLocation
-
-
elsif defined?(RUBY_ENGINE) && RUBY_ENGINE =~ /jruby/
-
require 'java'
-
-
# JRuby version source_location hack
-
# @return [Array] A two element array containing the source location of the method
-
def source_location
-
to_java.source_location(Thread.current.to_java.getContext())
-
end
-
-
else
-
-
-
# Return the source location of an instance method for Ruby 1.8.
-
# @return [Array] A two element array. First element is the
-
# file, second element is the line in the file where the
-
# method definition is found.
-
1
def source_location
-
klass = case owner
-
when Class
-
owner
-
when Module
-
method_owner = owner
-
Class.new { include(method_owner) }
-
end
-
-
# deal with immediate values
-
case
-
when klass == Symbol
-
return :a.method(name).source_location
-
when klass == Fixnum
-
return 0.method(name).source_location
-
when klass == TrueClass
-
return true.method(name).source_location
-
when klass == FalseClass
-
return false.method(name).source_location
-
when klass == NilClass
-
return nil.method(name).source_location
-
end
-
-
begin
-
Object.instance_method(:method).bind(klass.allocate).call(name).source_location
-
rescue TypeError
-
-
# Assume we are dealing with a Singleton Class:
-
# 1. Get the instance object
-
# 2. Forward the source_location lookup to the instance
-
instance ||= ObjectSpace.each_object(owner).first
-
Object.instance_method(:method).bind(instance).call(name).source_location
-
end
-
end
-
end
-
end
-
end
-
end
-
1
module MethodSource
-
1
VERSION = "0.8.2"
-
end
-
# -*- ruby encoding: utf-8 -*-
-
-
# The namespace for MIME applications, tools, and libraries.
-
1
module MIME # :nodoc:
-
end
-
-
1
class << MIME
-
# Used to mark a method as deprecated in the mime-types interface.
-
1
def deprecated(klass, sym, message = nil, &block) # :nodoc:
-
level = case klass
-
when Class, Module
-
'.'
-
else
-
klass = klass.class
-
'#'
-
end
-
unless defined?(@__deprecated) and @__deprecated["#{klass}#{level}#{sym}"]
-
message = case message
-
when :private, :protected
-
"and will be #{message}"
-
when nil
-
"and will be removed"
-
else
-
message
-
end
-
warn "#{klass}#{level}#{sym} is deprecated #{message}."
-
(@__deprecated ||= {})["#{klass}#{level}#{sym}"] = true
-
block.call if block
-
end
-
end
-
-
# MIME::InvalidContentType was moved to MIME::Type::InvalidContentType.
-
# Provide a single warning about this fact in the interim.
-
1
def const_missing(name) # :nodoc:
-
case name.to_s
-
when "InvalidContentType"
-
warn_about_moved_constants(name)
-
MIME::Type.const_get(name.to_sym)
-
else
-
super
-
end
-
end
-
-
1
private
-
1
def warn_about_moved_constants(name) # :nodoc:
-
unless defined?(@__warned_constants) and @__warned_constants[name]
-
warn "MIME::#{name} is deprecated. Use MIME::Type::#{name} instead."
-
(@__warned_constants ||= {})[name] = true
-
end
-
end
-
end
-
# -*- ruby encoding: utf-8 -*-
-
-
1
require 'mime'
-
1
require 'json'
-
-
# The definition of one MIME content-type.
-
#
-
# == Usage
-
# require 'mime/types'
-
#
-
# plaintext = MIME::Types['text/plain'].first
-
# # returns [text/plain, text/plain]
-
# text = plaintext.first
-
# print text.media_type # => 'text'
-
# print text.sub_type # => 'plain'
-
#
-
# puts text.extensions.join(" ") # => 'asc txt c cc h hh cpp'
-
#
-
# puts text.encoding # => 8bit
-
# puts text.binary? # => false
-
# puts text.ascii? # => true
-
# puts text == 'text/plain' # => true
-
# puts MIME::Type.simplified('x-appl/x-zip') # => 'appl/zip'
-
#
-
# puts MIME::Types.any? { |type|
-
# type.content_type == 'text/plain'
-
# } # => true
-
# puts MIME::Types.all?(&:registered?)
-
# # => false
-
1
class MIME::Type
-
# Reflects a MIME content-type specification that is not correctly
-
# formatted (it isn't +type+/+subtype+).
-
1
class InvalidContentType < ArgumentError
-
# :stopdoc:
-
1
def initialize(type_string)
-
@type_string = type_string
-
end
-
-
1
def to_s
-
"Invalid Content-Type #{@type_string.inspect}"
-
end
-
# :startdoc:
-
end
-
-
# Reflects an unsupported MIME encoding.
-
1
class InvalidEncoding < ArgumentError
-
# :stopdoc:
-
1
def initialize(encoding)
-
@encoding = encoding
-
end
-
-
1
def to_s
-
"Invalid Encoding #{@encoding.inspect} (valid values: #{VALID_ENCODINGS.inspect})."
-
end
-
# :startdoc:
-
end
-
-
# The released version of the mime-types library.
-
1
VERSION = '2.4.3'
-
-
1
include Comparable
-
-
1
MEDIA_TYPE_RE = %r{([-\w.+]+)/([-\w.+]*)}o
-
1
UNREGISTERED_RE = %r{[Xx]-}o
-
1
I18N_RE = %r{[^[:alnum:]]}o
-
1
PLATFORM_RE = %r{#{RUBY_PLATFORM}}o
-
-
1
DEFAULT_ENCODINGS = [ nil, :default ]
-
1
BINARY_ENCODINGS = %w(base64 8bit)
-
1
TEXT_ENCODINGS = %w(7bit quoted-printable)
-
1
VALID_ENCODINGS = DEFAULT_ENCODINGS + BINARY_ENCODINGS + TEXT_ENCODINGS
-
-
1
IANA_URL = "http://www.iana.org/assignments/media-types/%s/%s"
-
1
RFC_URL = "http://rfc-editor.org/rfc/rfc%s.txt"
-
1
DRAFT_URL = "http://datatracker.ietf.org/public/idindex.cgi?command=id_details&filename=%s"
-
1
CONTACT_URL = "http://www.iana.org/assignments/contact-people.htm#%s"
-
-
1
if respond_to? :private_constant
-
1
private_constant :MEDIA_TYPE_RE, :UNREGISTERED_RE, :I18N_RE, :PLATFORM_RE,
-
:DEFAULT_ENCODINGS, :BINARY_ENCODINGS, :TEXT_ENCODINGS, :VALID_ENCODINGS,
-
:IANA_URL, :RFC_URL, :DRAFT_URL, :CONTACT_URL
-
end
-
-
# Builds a MIME::Type object from the provided MIME Content Type value
-
# (e.g., 'text/plain' or 'applicaton/x-eruby'). The constructed object is
-
# yielded to an optional block for additional configuration, such as
-
# associating extensions and encoding information.
-
#
-
# * When provided a Hash or a MIME::Type, the MIME::Type will be
-
# constructed with #init_with.
-
# * When provided an Array, the MIME::Type will be constructed only using
-
# the first two elements of the array as the content type and
-
# extensions.
-
# * Otherwise, the content_type will be used as a string.
-
1
def initialize(content_type) # :yields self:
-
1869
self.system = nil
-
1869
self.obsolete = false
-
1869
self.registered = nil
-
1869
self.use_instead = nil
-
1869
self.signature = nil
-
-
1869
case content_type
-
when Hash
-
1869
init_with(content_type)
-
when Array
-
self.content_type = content_type[0]
-
self.extensions = content_type[1] || []
-
when MIME::Type
-
init_with(content_type.to_h)
-
else
-
self.content_type = content_type
-
end
-
-
1869
self.extensions ||= []
-
1869
self.docs ||= nil
-
1869
self.encoding ||= :default
-
1869
self.friendly({})
-
# This value will be deprecated in the future, as it will be an
-
# alternative view on #xrefs. Silence an unnecessary warning for now by
-
# assigning directly to the instance variable.
-
1869
@references ||= []
-
1869
self.xrefs ||= {}
-
-
1869
yield self if block_given?
-
end
-
-
# Returns +true+ if the simplified type matches the current
-
1
def like?(other)
-
if other.respond_to?(:simplified)
-
@simplified == other.simplified
-
else
-
@simplified == MIME::Type.simplified(other)
-
end
-
end
-
-
# Compares the MIME::Type against the exact content type or the simplified
-
# type (the simplified type will be used if comparing against something
-
# that can be treated as a String with #to_s). In comparisons, this is
-
# done against the lowercase version of the MIME::Type.
-
1
def <=>(other)
-
if other.respond_to?(:content_type)
-
@content_type.downcase <=> other.content_type.downcase
-
elsif other.respond_to?(:to_s)
-
@simplified <=> MIME::Type.simplified(other.to_s)
-
end
-
end
-
-
# Compares the MIME::Type based on how reliable it is before doing a
-
# normal <=> comparison. Used by MIME::Types#[] to sort types. The
-
# comparisons involved are:
-
#
-
# 1. self.simplified <=> other.simplified (ensures that we
-
# don't try to compare different types)
-
# 2. IANA-registered definitions < other definitions.
-
# 3. Generic definitions < platform definitions.
-
# 3. Complete definitions < incomplete definitions.
-
# 4. Current definitions < obsolete definitions.
-
# 5. Obselete with use-instead references < obsolete without.
-
# 6. Obsolete use-instead definitions are compared.
-
1
def priority_compare(other)
-
pc = simplified <=> other.simplified
-
if pc.zero?
-
pc = if (reg = registered?) != other.registered?
-
reg ? -1 : 1 # registered < unregistered
-
elsif (plat = platform?(true)) != other.platform?(true)
-
plat ? 1 : -1 # generic < platform
-
elsif (comp = complete?) != other.complete?
-
comp ? -1 : 1 # complete < incomplete
-
elsif (obs = obsolete?) != other.obsolete?
-
obs ? 1 : -1 # current < obsolete
-
elsif obs and ((ui = use_instead) != (oui = other.use_instead))
-
if ui.nil?
-
1
-
elsif oui.nil?
-
-1
-
else
-
ui <=> oui
-
end
-
else
-
0
-
end
-
end
-
-
pc
-
end
-
-
# Returns +true+ if the other object is a MIME::Type and the content types
-
# match.
-
1
def eql?(other)
-
other.kind_of?(MIME::Type) and self == other
-
end
-
-
# Returns the whole MIME content-type string.
-
#
-
# text/plain => text/plain
-
# x-chemical/x-pdb => x-chemical/x-pdb
-
1
attr_reader :content_type
-
# Returns the media type of the simplified MIME::Type.
-
#
-
# text/plain => text
-
# x-chemical/x-pdb => chemical
-
1
attr_reader :media_type
-
# Returns the media type of the unmodified MIME::Type.
-
#
-
# text/plain => text
-
# x-chemical/x-pdb => x-chemical
-
1
attr_reader :raw_media_type
-
# Returns the sub-type of the simplified MIME::Type.
-
#
-
# text/plain => plain
-
# x-chemical/x-pdb => pdb
-
1
attr_reader :sub_type
-
# Returns the media type of the unmodified MIME::Type.
-
#
-
# text/plain => plain
-
# x-chemical/x-pdb => x-pdb
-
1
attr_reader :raw_sub_type
-
# The MIME types main- and sub-label can both start with <tt>x-</tt>,
-
# which indicates that it is a non-registered name. Of course, after
-
# registration this flag can disappear, adds to the confusing
-
# proliferation of MIME types. The simplified string has the <tt>x-</tt>
-
# removed and are translated to lowercase.
-
#
-
# text/plain => text/plain
-
# x-chemical/x-pdb => chemical/pdb
-
1
attr_reader :simplified
-
-
# The list of extensions which are known to be used for this MIME::Type.
-
# Non-array values will be coerced into an array with #to_a. Array values
-
# will be flattened, +nil+ values removed, and made unique.
-
1
attr_reader :extensions
-
1
def extensions=(ext) # :nodoc:
-
1869
@extensions = Array(ext).flatten.compact.uniq
-
end
-
-
# Merge the extensions provided into this MIME::Type. The extensions added
-
# will be merged uniquely.
-
1
def add_extensions(*ext)
-
self.extensions = self.extensions + ext
-
end
-
-
##
-
# The preferred extension for this MIME type, if one is set.
-
#
-
# :attr_reader: preferred_extension
-
-
##
-
1
def preferred_extension
-
extensions.first
-
end
-
-
# The encoding (7bit, 8bit, quoted-printable, or base64) required to
-
# transport the data of this content type safely across a network, which
-
# roughly corresponds to Content-Transfer-Encoding. A value of +nil+ or
-
# <tt>:default</tt> will reset the #encoding to the #default_encoding for
-
# the MIME::Type. Raises ArgumentError if the encoding provided is
-
# invalid.
-
#
-
# If the encoding is not provided on construction, this will be either
-
# 'quoted-printable' (for text/* media types) and 'base64' for eveything
-
# else.
-
1
attr_reader :encoding
-
1
def encoding=(enc) # :nodoc:
-
1869
if DEFAULT_ENCODINGS.include?(enc)
-
@encoding = self.default_encoding
-
1869
elsif BINARY_ENCODINGS.include?(enc) or TEXT_ENCODINGS.include?(enc)
-
1869
@encoding = enc
-
else
-
raise InvalidEncoding, enc
-
end
-
end
-
-
# If the MIME::Type is a system-specific MIME::Type, returns the regular
-
# expression for the operating system indicated.
-
#
-
# This information about MIME content types is deprecated.
-
1
def system
-
MIME.deprecated(self, __method__)
-
@system
-
end
-
-
1
def system=(os) # :nodoc:
-
3738
if os.nil? or os.kind_of?(Regexp)
-
3735
@system = os
-
else
-
3
@system = %r|#{os}|
-
end
-
end
-
-
# Returns the default encoding for the MIME::Type based on the media type.
-
1
def default_encoding
-
(@media_type == 'text') ? 'quoted-printable' : 'base64'
-
end
-
-
##
-
# Returns the media type or types that should be used instead of this
-
# media type, if it is obsolete. If there is no replacement media type, or
-
# it is not obsolete, +nil+ will be returned.
-
1
def use_instead
-
return nil unless obsolete?
-
@use_instead
-
end
-
1
attr_writer :use_instead # :nodoc:
-
-
# Returns +true+ if the media type is obsolete.
-
1
def obsolete?
-
!!@obsolete
-
end
-
-
1
def obsolete=(v) # :nodoc:
-
3738
@obsolete = !!v
-
end
-
-
# The documentation for this MIME::Type.
-
1
attr_accessor :docs
-
-
# A friendly short description for this MIME::Type.
-
#
-
# call-seq:
-
# text_plain.friendly # => "Text File"
-
# text_plain.friendly('en') # => "Text File"
-
1
def friendly(lang = 'en')
-
3738
@friendly ||= {}
-
-
3738
case lang
-
when String
-
@friendly[lang]
-
when Array
-
@friendly.merge!(Hash[*lang])
-
when Hash
-
3738
@friendly.merge!(lang)
-
else
-
raise ArgumentError
-
end
-
end
-
-
# A key suitable for use as a lookup key for translations, such as with
-
# the I18n library.
-
#
-
# call-seq:
-
# text_plain.i18n_key # => "text.plain"
-
# 3gpp_xml.i18n_key # => "application.vnd-3gpp-bsf-xml"
-
# # from application/vnd.3gpp.bsf+xml
-
# x_msword.i18n_key # => "application.word"
-
# # from application/x-msword
-
1
attr_reader :i18n_key
-
-
# The encoded references URL list for this MIME::Type. See #urls for more
-
# information.
-
#
-
# This was previously called #url.
-
1
attr_reader :references
-
1
def references=(r) # :nodoc:
-
MIME.deprecated(self, __method__)
-
@references = Array(r).flatten.compact.uniq
-
end
-
-
1
def url # :nodoc:
-
MIME.deprecated(self, __method__, "and has been renamed to #references")
-
references
-
end
-
-
1
def url=(r) # :nodoc:
-
MIME.deprecated(self, __method__)
-
self.references = r
-
end
-
-
# The cross-references list for this MIME::Type.
-
1
attr_reader :xrefs
-
1
def xrefs=(x) # :nodoc:
-
1869
@xrefs = MIME::Types::Container.new.merge(x)
-
1869
@xrefs.each_value(&:sort!)
-
1869
@xrefs.each_value(&:uniq!)
-
end
-
-
# The decoded URL list for this MIME::Type.
-
#
-
# The special URL value IANA will be translated into:
-
# http://www.iana.org/assignments/media-types/<mediatype>/<subtype>
-
#
-
# The special URL value RFC### will be translated into:
-
# http://www.rfc-editor.org/rfc/rfc###.txt
-
#
-
# The special URL value DRAFT:name will be translated into:
-
# https://datatracker.ietf.org/public/idindex.cgi?
-
# command=id_detail&filename=<name>
-
#
-
# The special URL value [token] will be translated into:
-
# http://www.iana.org/assignments/contact-people.htm#<token>
-
#
-
# These values will be accessible through #urls, which always returns an
-
# array.
-
1
def urls
-
references.map do |el|
-
case el
-
when %r{^IANA$}
-
IANA_URL % [ @media_type, @sub_type ]
-
when %r{^RFC(\d+)$}
-
RFC_URL % $1
-
when %r{^DRAFT:(.+)$}
-
DRAFT_URL % $1
-
when %r{^\{([^=]+)=([^\}]+)\}}
-
[$1, $2]
-
when %r{^\[([^=]+)=([^\]]+)\]}
-
[$1, CONTACT_URL % $2]
-
when %r{^\[([^\]]+)\]}
-
CONTACT_URL % $1
-
else
-
el
-
end
-
end
-
end
-
-
# The decoded cross-reference URL list for this MIME::Type.
-
1
def xref_urls
-
xrefs.map { |(type, values)|
-
case type
-
when 'rfc'
-
values.map { |data| "http://www.iana.org/go/#{data}" }
-
when 'draft'
-
values.map { |data|
-
"http://www.iana.org/go/#{data.sub(/\ARFC/, 'draft')}"
-
}
-
when 'rfc-errata'
-
values.map { |data|
-
"http://www.rfc-editor.org/errata_search.php?eid=#{data}"
-
}
-
when 'person'
-
values.map { |data|
-
"http://www.iana.org/assignments/media-types/media-types.xhtml##{data}"
-
}
-
when 'template'
-
values.map { |data|
-
"http://www.iana.org/assignments/media-types/#{data}"
-
}
-
else # 'uri', 'text', etc.
-
values
-
end
-
}.flatten
-
end
-
-
# Prior to BCP 178 (RFC 6648), it could be assumed that MIME content types
-
# that start with <tt>x-</tt> were unregistered MIME. Per this BCP, this
-
# assumption is no longer being made by default in this library.
-
#
-
# There are three possible registration states for a MIME::Type:
-
# - Explicitly registered, like application/x-www-url-encoded.
-
# - Explicitly not registered, like image/webp.
-
# - Unspecified, in which case the media-type and the content-type will be
-
# scanned to see if they start with <tt>x-</tt>, indicating that they
-
# are assumed unregistered.
-
1
def registered?
-
if @registered.nil?
-
(@raw_media_type !~ UNREGISTERED_RE) and
-
(@raw_sub_type !~ UNREGISTERED_RE)
-
else
-
!!@registered
-
end
-
end
-
-
1
def registered=(v) # :nodoc:
-
3738
@registered = v.nil? ? v : !!v
-
end
-
-
# MIME types can be specified to be sent across a network in particular
-
# formats. This method returns +true+ when the MIME::Type encoding is set
-
# to <tt>base64</tt>.
-
1
def binary?
-
BINARY_ENCODINGS.include?(@encoding)
-
end
-
-
# MIME types can be specified to be sent across a network in particular
-
# formats. This method returns +false+ when the MIME::Type encoding is
-
# set to <tt>base64</tt>.
-
1
def ascii?
-
not binary?
-
end
-
-
# Returns +true+ when the simplified MIME::Type is in the list of known
-
# digital signatures.
-
1
def signature?
-
!!@signature
-
end
-
-
1
def signature=(v) # :nodoc:
-
3738
@signature = !!v
-
end
-
-
# Returns +true+ if the MIME::Type is specific to an operating system.
-
#
-
# This method is deprecated.
-
1
def system?(__internal__ = false)
-
MIME.deprecated(self, __method__) unless __internal__
-
not @system.nil?
-
end
-
-
# Returns +true+ if the MIME::Type is specific to the current operating
-
# system as represented by RUBY_PLATFORM.
-
#
-
# This method is deprecated.
-
1
def platform?(__internal__ = false)
-
MIME.deprecated(self, __method__) unless __internal__
-
system?(__internal__) and (RUBY_PLATFORM =~ @system)
-
end
-
-
# Returns +true+ if the MIME::Type specifies an extension list,
-
# indicating that it is a complete MIME::Type.
-
1
def complete?
-
not @extensions.empty?
-
end
-
-
# Returns the MIME::Type as a string.
-
1
def to_s
-
@content_type
-
end
-
-
# Returns the MIME::Type as a string for implicit conversions. This allows
-
# MIME::Type objects to appear on either side of a comparison.
-
#
-
# 'text/plain' == MIME::Type.new('text/plain')
-
1
def to_str
-
@content_type
-
end
-
-
# Returns the MIME::Type as an array suitable for use with
-
# MIME::Type.from_array.
-
#
-
# This method is deprecated.
-
1
def to_a
-
MIME.deprecated(self, __method__)
-
[ @content_type, @extensions, @encoding, @system, obsolete?, @docs,
-
@references, registered? ]
-
end
-
-
# Returns the MIME::Type as an array suitable for use with
-
# MIME::Type.from_hash.
-
#
-
# This method is deprecated.
-
1
def to_hash
-
MIME.deprecated(self, __method__)
-
{ 'Content-Type' => @content_type,
-
'Content-Transfer-Encoding' => @encoding,
-
'Extensions' => @extensions,
-
'System' => @system,
-
'Obsolete' => obsolete?,
-
'Docs' => @docs,
-
'URL' => @references,
-
'Registered' => registered?,
-
}
-
end
-
-
# Converts the MIME::Type to a JSON string.
-
1
def to_json(*args)
-
to_h.to_json(*args)
-
end
-
-
# Converts the MIME::Type to a hash suitable for use in JSON. The output
-
# of this method can also be used to initialize a MIME::Type.
-
1
def to_h
-
encode_with({})
-
end
-
-
# Populates the +coder+ with attributes about this record for
-
# serialization. The structure of +coder+ should match the structure used
-
# with #init_with.
-
1
def encode_with(coder)
-
coder['content-type'] = @content_type
-
coder['docs'] = @docs unless @docs.nil? or @docs.empty?
-
coder['friendly'] = @friendly unless @friendly.empty?
-
coder['encoding'] = @encoding
-
coder['extensions'] = @extensions unless @extensions.empty?
-
if obsolete?
-
coder['obsolete'] = obsolete?
-
coder['use-instead'] = use_instead if use_instead
-
end
-
coder['references'] = references unless references.empty?
-
coder['xrefs'] = xrefs unless xrefs.empty?
-
coder['registered'] = registered?
-
coder['signature'] = signature? if signature?
-
coder['system'] = @system if @system
-
coder
-
end
-
-
# Initialize an empty object from +coder+, which must contain the
-
# attributes necessary for initializing an empty object.
-
1
def init_with(coder)
-
1869
self.content_type = coder['content-type']
-
1869
self.docs = coder['docs'] || []
-
1869
self.friendly(coder['friendly'] || {})
-
1869
self.encoding = coder['encoding']
-
1869
self.extensions = coder['extensions'] || []
-
1869
self.obsolete = coder['obsolete']
-
# This value will be deprecated in the future, as it will be an
-
# alternative view on #xrefs. Silence an unnecessary warning for now by
-
# assigning directly to the instance variable.
-
1869
@references = Array(coder['references']).flatten.compact.uniq
-
1869
self.registered = coder['registered']
-
1869
self.signature = coder['signature']
-
1869
self.system = coder['system']
-
1869
self.xrefs = coder['xrefs'] || {}
-
1869
self.use_instead = coder['use-instead']
-
end
-
# :startdoc:
-
-
1
class << self
-
# The MIME types main- and sub-label can both start with <tt>x-</tt>,
-
# which indicates that it is a non-registered name. Of course, after
-
# registration this flag can disappear, adds to the confusing
-
# proliferation of MIME types. The simplified string has the <tt>x-</tt>
-
# removed and are translated to lowercase.
-
1
def simplified(content_type)
-
1869
matchdata = case content_type
-
when MatchData
-
1869
content_type
-
else
-
MEDIA_TYPE_RE.match(content_type)
-
end
-
-
1869
if matchdata
-
matchdata.captures.map { |e|
-
3738
e.downcase.gsub(UNREGISTERED_RE, '')
-
1869
}.join('/')
-
end
-
end
-
-
# Converts a provided content type into a translation key suitable for
-
# use with the I18n library.
-
1
def i18n_key(content_type)
-
1869
matchdata = case content_type
-
when MatchData
-
1869
content_type
-
else
-
MEDIA_TYPE_RE.match(content_type)
-
end
-
-
1869
if matchdata
-
matchdata.captures.map { |e|
-
3738
e.downcase.gsub(UNREGISTERED_RE, '').gsub(I18N_RE, '-')
-
1869
}.join('.')
-
end
-
end
-
-
# Creates a MIME::Type from an array in the form of:
-
# [type-name, [extensions], encoding, system]
-
#
-
# +extensions+, +encoding+, and +system+ are optional.
-
#
-
# MIME::Type.from_array("application/x-ruby", ['rb'], '8bit')
-
# MIME::Type.from_array(["application/x-ruby", ['rb'], '8bit'])
-
#
-
# These are equivalent to:
-
#
-
# MIME::Type.new('application/x-ruby') do |t|
-
# t.extensions = %w(rb)
-
# t.encoding = '8bit'
-
# end
-
#
-
# This method is deprecated.
-
1
def from_array(*args) # :yields MIME::Type.new:
-
MIME.deprecated(self, __method__)
-
-
# Dereferences the array one level, if necessary.
-
args = args.first if args.first.kind_of? Array
-
-
unless args.size.between?(1, 8)
-
raise ArgumentError, "Array provided must contain between one and eight elements."
-
end
-
-
MIME::Type.new(args.shift) do |t|
-
t.extensions, t.encoding, t.system, t.obsolete, t.docs, t.references,
-
t.registered = *args
-
yield t if block_given?
-
end
-
end
-
-
# Creates a MIME::Type from a hash. Keys are case-insensitive, dashes
-
# may be replaced with underscores, and the internal Symbol of the
-
# lowercase-underscore version can be used as well. That is,
-
# Content-Type can be provided as content-type, Content_Type,
-
# content_type, or :content_type.
-
#
-
# Known keys are <tt>Content-Type</tt>,
-
# <tt>Content-Transfer-Encoding</tt>, <tt>Extensions</tt>, and
-
# <tt>System</tt>.
-
#
-
# MIME::Type.from_hash('Content-Type' => 'text/x-yaml',
-
# 'Content-Transfer-Encoding' => '8bit',
-
# 'System' => 'linux',
-
# 'Extensions' => ['yaml', 'yml'])
-
#
-
# This is equivalent to:
-
#
-
# MIME::Type.new('text/x-yaml') do |t|
-
# t.encoding = '8bit'
-
# t.system = 'linux'
-
# t.extensions = ['yaml', 'yml']
-
# end
-
#
-
# This method has been deprecated.
-
1
def from_hash(hash) # :yields MIME::Type.new:
-
MIME.deprecated(self, __method__)
-
type = {}
-
hash.each_pair do |k, v|
-
type[k.to_s.tr('A-Z', 'a-z').gsub(/-/, '_').to_sym] = v
-
end
-
-
MIME::Type.new(type[:content_type]) do |t|
-
t.extensions = type[:extensions]
-
t.encoding = type[:content_transfer_encoding]
-
t.system = type[:system]
-
t.obsolete = type[:obsolete]
-
t.docs = type[:docs]
-
t.url = type[:url]
-
t.registered = type[:registered]
-
-
yield t if block_given?
-
end
-
end
-
-
# Essentially a copy constructor.
-
#
-
# MIME::Type.from_mime_type(plaintext)
-
#
-
# is equivalent to:
-
#
-
# MIME::Type.new(plaintext.content_type.dup) do |t|
-
# t.extensions = plaintext.extensions.dup
-
# t.system = plaintext.system.dup
-
# t.encoding = plaintext.encoding.dup
-
# end
-
#
-
# This method has been deprecated.
-
1
def from_mime_type(mime_type) # :yields the new MIME::Type:
-
MIME.deprecated(self, __method__)
-
new(mime_type)
-
end
-
end
-
-
1
private
-
1
def content_type=(type_string)
-
1869
match = MEDIA_TYPE_RE.match(type_string)
-
1869
raise InvalidContentType, type_string if match.nil?
-
-
1869
@content_type = type_string
-
1869
@raw_media_type, @raw_sub_type = *match.captures
-
1869
@simplified = MIME::Type.simplified(match)
-
1869
@i18n_key = MIME::Type.i18n_key(match)
-
1869
@media_type, @sub_type =
-
*MEDIA_TYPE_RE.match(@simplified).captures
-
end
-
end
-
# -*- ruby encoding: utf-8 -*-
-
-
1
require 'mime/type'
-
1
require 'mime/types/cache'
-
1
require 'mime/types/loader'
-
-
# MIME::Types is a registry of MIME types. It is both a class (created with
-
# MIME::Types.new) and a default registry (loaded automatically or through
-
# interactions with MIME::Types.[] and MIME::Types.type_for).
-
#
-
# == The Default mime-types Registry
-
#
-
# The default mime-types registry is loaded automatically when the library
-
# is required (<tt>require 'mime/types'</tt>), but it may be lazily loaded
-
# (loaded on first use) with the use of the environment variable
-
# +RUBY_MIME_TYPES_LAZY_LOAD+ having any value other than +false+. The
-
# initial startup is about 14× faster (~10 ms vs ~140 ms), but the
-
# registry will be loaded at some point in the future.
-
#
-
# The default mime-types registry can also be loaded from a Marshal cache
-
# file specific to the version of MIME::Types being loaded. This will be
-
# handled automatically with the use of a file referred to in the
-
# environment variable +RUBY_MIME_TYPES_CACHE+. MIME::Types will attempt to
-
# load the registry from this cache file (MIME::Type::Cache.load); if it
-
# cannot be loaded (because the file does not exist, there is an error, or
-
# the data is for a different version of mime-types), the default registry
-
# will be loaded from the normal JSON version and then the cache file will
-
# be *written* to the location indicated by +RUBY_MIME_TYPES_CACHE+. Cache
-
# file loads just over 4½× faster (~30 ms vs ~140 ms).
-
# loads.
-
#
-
# Notes:
-
# * The loading of the default registry is *not* atomic; when using a
-
# multi-threaded environment, it is recommended that lazy loading is not
-
# used and mime-types is loaded as early as possible.
-
# * Cache files should be specified per application in a multiprocess
-
# environment and should be initialized during deployment or before
-
# forking to minimize the chance that the multiple processes will be
-
# trying to write to the same cache file at the same time, or that two
-
# applications that are on different versions of mime-types would be
-
# thrashing the cache.
-
# * Unless cache files are preinitialized, the application using the
-
# mime-types cache file must have read/write permission to the cache file.
-
#
-
# == Usage
-
# require 'mime/types'
-
#
-
# plaintext = MIME::Types['text/plain']
-
# print plaintext.media_type # => 'text'
-
# print plaintext.sub_type # => 'plain'
-
#
-
# puts plaintext.extensions.join(" ") # => 'asc txt c cc h hh cpp'
-
#
-
# puts plaintext.encoding # => 8bit
-
# puts plaintext.binary? # => false
-
# puts plaintext.ascii? # => true
-
# puts plaintext.obsolete? # => false
-
# puts plaintext.registered? # => true
-
# puts plaintext == 'text/plain' # => true
-
# puts MIME::Type.simplified('x-appl/x-zip') # => 'appl/zip'
-
#
-
1
class MIME::Types
-
# The release version of Ruby MIME::Types
-
1
VERSION = MIME::Type::VERSION
-
-
1
include Enumerable
-
-
# The data version.
-
1
attr_reader :data_version
-
-
# Creates a new MIME::Types registry.
-
1
def initialize
-
1
@type_variants = Container.new
-
1
@extension_index = Container.new
-
1
@data_version = VERSION.dup.freeze
-
end
-
-
1
def add_type_variant(mime_type) # :nodoc:
-
MIME.deprecated(self, __method__, :private)
-
add_type_variant!(mime_type)
-
end
-
-
1
def index_extensions(mime_type) # :nodoc:
-
MIME.deprecated(self, __method__, :private)
-
index_extensions!(mime_type)
-
end
-
-
1
def defined_types # :nodoc:
-
MIME.deprecated(self, __method__)
-
@type_variants.values.flatten
-
end
-
-
# Returns the number of known type variants.
-
1
def count
-
@type_variants.values.reduce(0) { |m, o| m + o.size }
-
end
-
-
# Iterates through the type variants.
-
1
def each
-
if block_given?
-
@type_variants.each_value { |tv| tv.each { |t| yield t } }
-
else
-
enum_for(:each)
-
end
-
end
-
-
1
@__types__ = nil
-
-
# Returns a list of MIME::Type objects, which may be empty. The optional
-
# flag parameters are <tt>:complete</tt> (finds only complete MIME::Type
-
# objects) and <tt>:registered</tt> (finds only MIME::Types that are
-
# registered). It is possible for multiple matches to be returned for
-
# either type (in the example below, 'text/plain' returns two values --
-
# one for the general case, and one for VMS systems).
-
#
-
# puts "\nMIME::Types['text/plain']"
-
# MIME::Types['text/plain'].each { |t| puts t.to_a.join(", ") }
-
#
-
# puts "\nMIME::Types[/^image/, complete: true]"
-
# MIME::Types[/^image/, :complete => true].each do |t|
-
# puts t.to_a.join(", ")
-
# end
-
#
-
# If multiple type definitions are returned, returns them sorted as
-
# follows:
-
# 1. Complete definitions sort before incomplete ones;
-
# 2. IANA-registered definitions sort before LTSW-recorded
-
# definitions.
-
# 3. Generic definitions sort before platform-specific ones;
-
# 4. Current definitions sort before obsolete ones;
-
# 5. Obsolete definitions with use-instead clauses sort before those
-
# without;
-
# 6. Obsolete definitions use-instead clauses are compared.
-
# 7. Sort on name.
-
#
-
# An additional flag of :platform (finds only MIME::Types for the current
-
# platform) is currently supported but deprecated.
-
1
def [](type_id, flags = {})
-
if flags[:platform]
-
MIME.deprecated(self, __method__, "using the :platform flag")
-
end
-
-
matches = case type_id
-
when MIME::Type
-
@type_variants[type_id.simplified]
-
when Regexp
-
match(type_id)
-
else
-
@type_variants[MIME::Type.simplified(type_id)]
-
end
-
-
prune_matches(matches, flags).sort { |a, b| a.priority_compare(b) }
-
end
-
-
# Return the list of MIME::Types which belongs to the file based on its
-
# filename extension. If there is no extension, the filename will be used
-
# as the matching criteria on its own.
-
#
-
# This will always return a merged, flatten, priority sorted, unique array.
-
#
-
# puts MIME::Types.type_for('citydesk.xml')
-
# => [application/xml, text/xml]
-
# puts MIME::Types.type_for('citydesk.gif')
-
# => [image/gif]
-
# puts MIME::Types.type_for(%w(citydesk.xml citydesk.gif))
-
# => [application/xml, image/gif, text/xml]
-
#
-
# If +platform+ is +true+, then only file types that are specific to the
-
# current platform will be returned. This parameter has been deprecated.
-
1
def type_for(filename, platform = false)
-
types = Array(filename).flat_map { |fn|
-
@extension_index[fn.chomp.downcase[/\.?([^.]*?)$/, 1]]
-
}.compact.sort { |a, b| a.priority_compare(b) }.uniq
-
-
if platform
-
MIME.deprecated(self, __method__,
-
"using the platform parameter")
-
types.select(&:platform?)
-
else
-
types
-
end
-
end
-
1
alias_method :of, :type_for
-
-
# Add one or more MIME::Type objects to the set of known types. If the
-
# type is already known, a warning will be displayed.
-
#
-
# The last parameter may be the value <tt>:silent</tt> or +true+ which
-
# will suppress duplicate MIME type warnings.
-
1
def add(*types)
-
1
quiet = ((types.last == :silent) or (types.last == true))
-
-
1
types.each do |mime_type|
-
1870
case mime_type
-
when true, false, nil, Symbol
-
1
nil
-
when MIME::Types
-
variants = mime_type.instance_variable_get(:@type_variants)
-
add(*variants.values.flatten, quiet)
-
when Array
-
add(*mime_type, quiet)
-
else
-
1869
add_type(mime_type, quiet)
-
end
-
end
-
end
-
-
# Add a single MIME::Type object to the set of known types. If the type is
-
# already known, a warning will be displayed. The +quiet+ parameter may be
-
# a truthy value to suppress that warning.
-
1
def add_type(mime_type, quiet = false)
-
1869
if !quiet and @type_variants[mime_type.simplified].include?(mime_type)
-
warn('Type %s is already registered as a variant of %s.' %
-
[ mime_type, mime_type.simplified ])
-
end
-
-
1869
add_type_variant!(mime_type)
-
1869
index_extensions!(mime_type)
-
end
-
-
1
class << self
-
1
include Enumerable
-
-
# Load MIME::Types from a v1 file registry.
-
#
-
# This method has been deprecated.
-
1
def load_from_file(filename)
-
MIME.deprecated(self, __method__)
-
MIME::Types::Loader.load_from_v1(filename)
-
end
-
-
# MIME::Types#[] against the default MIME::Types registry.
-
1
def [](type_id, flags = {})
-
__types__[type_id, flags]
-
end
-
-
# MIME::Types#count against the default MIME::Types registry.
-
1
def count
-
__types__.count
-
end
-
-
# MIME::Types#each against the default MIME::Types registry.
-
1
def each
-
if block_given?
-
__types__.each { |t| yield t }
-
else
-
enum_for(:each)
-
end
-
end
-
-
# MIME::Types#type_for against the default MIME::Types registry.
-
1
def type_for(filename, platform = false)
-
__types__.type_for(filename, platform)
-
end
-
1
alias_method :of, :type_for
-
-
# MIME::Types#add against the default MIME::Types registry.
-
1
def add(*types)
-
__types__.add(*types)
-
end
-
-
# Returns the currently defined cache file, if any.
-
1
def cache_file
-
MIME.deprecated(self, __method__)
-
ENV['RUBY_MIME_TYPES_CACHE']
-
end
-
-
1
def add_type_variant(mime_type) # :nodoc:
-
__types__.add_type_variant(mime_type)
-
end
-
-
1
def index_extensions(mime_type) # :nodoc:
-
__types__.index_extensions(mime_type)
-
end
-
-
1
private
-
1
def lazy_load?
-
1
(lazy = ENV['RUBY_MIME_TYPES_LAZY_LOAD']) && (lazy != 'false')
-
end
-
-
1
def __types__
-
(defined?(@__types__) and @__types__) or load_default_mime_types
-
end
-
-
1
def load_default_mime_types
-
1
@__types__ = MIME::Types::Cache.load
-
1
unless @__types__
-
1
@__types__ = MIME::Types::Loader.load
-
1
MIME::Types::Cache.save(@__types__)
-
end
-
1
@__types__
-
end
-
end
-
-
1
private
-
1
def add_type_variant!(mime_type)
-
1869
@type_variants[mime_type.simplified] << mime_type
-
end
-
-
1
def index_extensions!(mime_type)
-
3179
mime_type.extensions.each { |ext| @extension_index[ext] << mime_type }
-
end
-
-
1
def prune_matches(matches, flags)
-
matches.delete_if { |e| not e.complete? } if flags[:complete]
-
matches.delete_if { |e| not e.platform? } if flags[:platform]
-
matches.delete_if { |e| not e.registered? } if flags[:registered]
-
matches
-
end
-
-
1
def match(pattern)
-
@type_variants.select { |k, v| k =~ pattern }.values.flatten
-
end
-
-
1
load_default_mime_types unless lazy_load?
-
end
-
-
# vim: ft=ruby
-
# -*- ruby encoding: utf-8 -*-
-
-
1
class MIME::Types
-
# Caching of MIME::Types registries is advisable if you will be loading
-
# the default registry relatively frequently. With the class methods on
-
# MIME::Types::Cache, any MIME::Types registry can be marshaled quickly
-
# and easily.
-
#
-
# The cache is invalidated on a per-version basis; a cache file for
-
# version 2.0 will not be reused with version 2.0.1.
-
1
Cache = Struct.new(:version, :data)
-
-
1
class << Cache
-
# Attempts to load the cache from the file provided as a parameter or in
-
# the environment variable +RUBY_MIME_TYPES_CACHE+. Returns +nil+ if the
-
# file does not exist, if the file cannot be loaded, or if the data in
-
# the cache version is different than this version.
-
1
def load(cache_file = nil)
-
1
cache_file = cache_file || ENV['RUBY_MIME_TYPES_CACHE']
-
1
return nil unless cache_file and File.exist?(cache_file)
-
-
cache = Marshal.load(File.binread(cache_file))
-
if cache.version == MIME::Types::VERSION
-
Marshal.load(cache.data)
-
else
-
warn "Could not load MIME::Types cache: invalid version"
-
nil
-
end
-
rescue => e
-
warn "Could not load MIME::Types cache: #{e}"
-
return nil
-
end
-
-
# Attempts to save the types provided to the cache file provided.
-
#
-
# If +types+ is not provided or is +nil+, the cache will contain the
-
# current MIME::Types default registry.
-
#
-
# If +cache_file+ is not provided or is +nil+, the cache will be written
-
# to the file specified in the environment variable
-
# +RUBY_MIME_TYPES_CACHE+. If there is no cache file specified either
-
# directly or through the environment, this method will return +nil+
-
1
def save(types = nil, cache_file = nil)
-
1
cache_file = cache_file || ENV['RUBY_MIME_TYPES_CACHE']
-
1
return nil unless cache_file
-
-
types = types || MIME::Types.send(:__types__)
-
-
File.open(cache_file, 'wb') do |f|
-
f.write(Marshal.dump(new(types.data_version, Marshal.dump(types))))
-
end
-
end
-
end
-
-
# MIME::Types requires a container Hash with a default values for keys
-
# resulting in an empty array (<tt>[]</tt>), but this cannot be dumped
-
# through Marshal because of the presence of that default Proc. This class
-
# exists solely to satisfy that need.
-
1
class Container < Hash # :nodoc:
-
1
def initialize
-
1871
super
-
4843
self.default_proc = ->(h, k) { h[k] = [] }
-
end
-
-
1
def marshal_dump
-
{}.merge(self)
-
end
-
-
1
def marshal_load(hash)
-
self.merge!(hash)
-
end
-
end
-
end
-
# -*- ruby encoding: utf-8 -*-
-
-
1
require 'mime/types/loader_path'
-
-
# This class is responsible for initializing the MIME::Types registry from
-
# the data files supplied with the mime-types library.
-
#
-
# The Loader will use one of the following paths:
-
# 1. The +path+ provided in its constructor argument;
-
# 2. The value of ENV['RUBY_MIME_TYPES_DATA']; or
-
# 3. The value of MIME::Types::Loader::PATH.
-
#
-
# When #load is called, the +path+ will be searched recursively for all YAML
-
# (.yml or .yaml) files. By convention, there is one file for each media
-
# type (application.yml, audio.yml, etc.), but this is not required.
-
1
class MIME::Types::Loader
-
# The path that will be read for the MIME::Types files.
-
1
attr_reader :path
-
# The MIME::Types container instance that will be loaded. If not provided
-
# at initialization, a new MIME::Types instance will be constructed.
-
1
attr_reader :container
-
-
# Creates a Loader object that can be used to load MIME::Types registries
-
# into memory, using YAML, JSON, or v1 registry format loaders.
-
1
def initialize(path = nil, container = nil)
-
1
path = path || ENV['RUBY_MIME_TYPES_DATA'] ||
-
MIME::Types::Loader::PATH
-
1
@path = File.expand_path(File.join(path, '**'))
-
1
@container = container || MIME::Types.new
-
end
-
-
# Loads a MIME::Types registry from YAML files (<tt>*.yml</tt> or
-
# <tt>*.yaml</tt>) recursively found in +path+.
-
#
-
# It is expected that the YAML objects contained within the registry array
-
# will be tagged as <tt>!ruby/object:MIME::Type</tt>.
-
#
-
# Note that the YAML format is about 2½ times *slower* than either the v1
-
# format or the JSON format.
-
#
-
# NOTE: The purpose of this format is purely for maintenance reasons.
-
1
def load_yaml
-
Dir[yaml_path].sort.each do |f|
-
container.add(*self.class.load_from_yaml(f), :silent)
-
end
-
container
-
end
-
-
# Loads a MIME::Types registry from JSON files (<tt>*.json</tt>)
-
# recursively found in +path+.
-
#
-
# It is expected that the JSON objects will be an array of hash objects.
-
# The JSON format is the registry format for the MIME types registry
-
# shipped with the mime-types library.
-
#
-
# This method is aliased to #load.
-
1
def load_json
-
1
Dir[json_path].sort.each do |f|
-
1
types = self.class.load_from_json(f)
-
1
container.add(*types, :silent)
-
end
-
1
container
-
end
-
1
alias_method :load, :load_json
-
-
# Loads a MIME::Types registry from files found in +path+ that are in the
-
# v1 data format. The file search for this will exclude both JSON
-
# (<tt>*.json</tt>) and YAML (<tt>*.yml</tt> or <tt>*.yaml</tt>) files.
-
#
-
# This method has been deprecated.
-
1
def load_v1
-
MIME.deprecated(self.class, __method__)
-
Dir[v1_path].sort.each do |f|
-
next if f =~ /\.ya?ml$|\.json$/
-
container.add(self.class.load_from_v1(f), true)
-
end
-
container
-
end
-
-
# Raised when a V1 format file is discovered.
-
1
BadV1Format = Class.new(Exception)
-
-
1
class << self
-
# Loads the default MIME::Type registry.
-
1
def load
-
1
new.load
-
end
-
-
# Build the type list from a file in the format:
-
#
-
# [*][!][os:]mt/st[<ws>@ext][<ws>:enc][<ws>'url-list][<ws>=docs]
-
#
-
# == *
-
# An unofficial MIME type. This should be used if and only if the MIME type
-
# is not properly specified (that is, not under either x-type or
-
# vnd.name.type).
-
#
-
# == !
-
# An obsolete MIME type. May be used with an unofficial MIME type.
-
#
-
# == os:
-
# Platform-specific MIME type definition.
-
#
-
# == mt
-
# The media type.
-
#
-
# == st
-
# The media subtype.
-
#
-
# == <ws>@ext
-
# The list of comma-separated extensions.
-
#
-
# == <ws>:enc
-
# The encoding.
-
#
-
# == <ws>'url-list
-
# The list of comma-separated URLs.
-
#
-
# == <ws>=docs
-
# The documentation string.
-
#
-
# That is, everything except the media type and the subtype is optional. The
-
# more information that's available, though, the richer the values that can
-
# be provided.
-
1
def load_from_v1(filename)
-
MIME.deprecated(self.class, __method__)
-
data = read_file(filename).split($/)
-
mime = MIME::Types.new
-
data.each_with_index { |line, index|
-
item = line.chomp.strip
-
next if item.empty?
-
-
m = V1_FORMAT.match(item)
-
-
unless m
-
warn <<-EOS
-
#{filename}:#{index + 1}: Parsing error in v1 MIME type definition.
-
=> #{line}
-
EOS
-
raise BadV1Format, line
-
end
-
-
unregistered, obsolete, platform, mediatype, subtype, extensions,
-
encoding, urls, docs, _ = *m.captures
-
-
next if mediatype.nil?
-
-
extensions &&= extensions.split(/,/)
-
urls &&= urls.split(/,/)
-
-
if docs.nil?
-
use_instead = nil
-
else
-
use_instead = docs.scan(%r{use-instead:(\S+)}).flatten
-
docs = docs.gsub(%r{use-instead:\S+}, "").squeeze(" \t")
-
end
-
-
mime_type = MIME::Type.new("#{mediatype}/#{subtype}") do |t|
-
t.extensions = extensions
-
t.encoding = encoding
-
t.system = platform
-
t.obsolete = obsolete
-
t.registered = false if unregistered
-
t.use_instead = use_instead
-
t.docs = docs
-
t.references = urls
-
end
-
-
mime.add_type(mime_type, true)
-
}
-
mime
-
end
-
-
# Loads MIME::Types from a single YAML file.
-
#
-
# It is expected that the YAML objects contained within the registry
-
# array will be tagged as <tt>!ruby/object:MIME::Type</tt>.
-
#
-
# Note that the YAML format is about 2½ times *slower* than either the v1
-
# format or the JSON format.
-
#
-
# NOTE: The purpose of this format is purely for maintenance reasons.
-
1
def load_from_yaml(filename)
-
begin
-
require 'psych'
-
rescue LoadError
-
nil
-
end
-
require 'yaml'
-
YAML.load(read_file(filename))
-
end
-
-
# Loads MIME::Types from a single JSON file.
-
#
-
# It is expected that the JSON objects will be an array of hash objects.
-
# The JSON format is the registry format for the MIME types registry
-
# shipped with the mime-types library.
-
1
def load_from_json(filename)
-
1
require 'json'
-
1870
JSON.parse(read_file(filename)).map { |type| MIME::Type.new(type) }
-
end
-
-
1
private
-
1
def read_file(filename)
-
2
File.open(filename, 'r:UTF-8:-') { |f| f.read }
-
end
-
end
-
-
1
private
-
1
def yaml_path
-
File.join(path, '*.y{,a}ml')
-
end
-
-
1
def json_path
-
1
File.join(path, '*.json')
-
end
-
-
1
def v1_path
-
File.join(path, '*')
-
end
-
-
# The regular expression used to match a v1-format file-based MIME type
-
# definition.
-
1
V1_FORMAT = # :nodoc:
-
%r{\A\s*
-
([*])? # 0: Unregistered?
-
(!)? # 1: Obsolete?
-
(?:(\w+):)? # 2: Platform marker
-
([-\w.+]+)/([-\w.+]*) # 3, 4: Media Type
-
(?:\s+@(\S+))? # 5: Extensions
-
(?:\s+:(base64|7bit|8bit|quoted\-printable))? # 6: Encoding
-
(?:\s+'(\S+))? # 7: URL list
-
(?:\s+=(.+))? # 8: Documentation
-
(?:\s*([#].*)?)? # Comments
-
\s*
-
\z
-
}x
-
-
1
private_constant :V1_FORMAT if respond_to? :private_constant
-
end
-
# -*- ruby encoding: utf-8 -*-
-
-
1
class MIME::Types::Loader
-
# The path that will be used for loading the MIME::Types data. The default
-
# location is __FILE__/../../../../data, which is where the data lives
-
# in the gem installation of the mime-types library.
-
#
-
# The MIME::Types::Loader will load all YAML files contained in this path.
-
# By convention, there is one file for each media type (e.g.,
-
# application.yml, audio.yml, etc.).
-
#
-
# System repackagers note: this is the constant that you would change if
-
# you repackage mime-types for your system. It is recommended that the
-
# path be something like /usr/share/ruby/mime-types/.
-
1
PATH = File.expand_path('../../../../data', __FILE__)
-
end
-
1
require "optparse"
-
1
require "thread"
-
1
require "mutex_m"
-
1
require "minitest/parallel"
-
-
##
-
# :include: README.txt
-
-
1
module Minitest
-
1
VERSION = "5.4.2" # :nodoc:
-
-
1
@@installed_at_exit ||= false
-
1
@@after_run = []
-
1
@extensions = []
-
-
2
mc = (class << self; self; end)
-
-
##
-
# Parallel test executor
-
-
1
mc.send :attr_accessor, :parallel_executor
-
1
self.parallel_executor = Parallel::Executor.new((ENV['N'] || 2).to_i)
-
-
##
-
# Filter object for backtraces.
-
-
1
mc.send :attr_accessor, :backtrace_filter
-
-
##
-
# Reporter object to be used for all runs.
-
#
-
# NOTE: This accessor is only available during setup, not during runs.
-
-
1
mc.send :attr_accessor, :reporter
-
-
##
-
# Names of known extension plugins.
-
-
1
mc.send :attr_accessor, :extensions
-
-
##
-
# Registers Minitest to run at process exit
-
-
1
def self.autorun
-
at_exit {
-
next if $! and not ($!.kind_of? SystemExit and $!.success?)
-
-
exit_code = nil
-
-
at_exit {
-
@@after_run.reverse_each(&:call)
-
exit exit_code || false
-
}
-
-
exit_code = Minitest.run ARGV
-
} unless @@installed_at_exit
-
@@installed_at_exit = true
-
end
-
-
##
-
# A simple hook allowing you to run a block of code after everything
-
# is done running. Eg:
-
#
-
# Minitest.after_run { p $debugging_info }
-
-
1
def self.after_run &block
-
@@after_run << block
-
end
-
-
1
def self.init_plugins options # :nodoc:
-
self.extensions.each do |name|
-
msg = "plugin_#{name}_init"
-
send msg, options if self.respond_to? msg
-
end
-
end
-
-
1
def self.load_plugins # :nodoc:
-
return unless self.extensions.empty?
-
-
seen = {}
-
-
require "rubygems" unless defined? Gem
-
-
Gem.find_files("minitest/*_plugin.rb").each do |plugin_path|
-
name = File.basename plugin_path, "_plugin.rb"
-
-
next if seen[name]
-
seen[name] = true
-
-
require plugin_path
-
self.extensions << name
-
end
-
end
-
-
##
-
# This is the top-level run method. Everything starts from here. It
-
# tells each Runnable sub-class to run, and each of those are
-
# responsible for doing whatever they do.
-
#
-
# The overall structure of a run looks like this:
-
#
-
# Minitest.autorun
-
# Minitest.run(args)
-
# Minitest.__run(reporter, options)
-
# Runnable.runnables.each
-
# runnable.run(reporter, options)
-
# self.runnable_methods.each
-
# self.run_one_method(self, runnable_method, reporter)
-
# Minitest.run_one_method(klass, runnable_method, reporter)
-
# klass.new(runnable_method).run
-
-
1
def self.run args = []
-
self.load_plugins
-
-
options = process_args args
-
-
reporter = CompositeReporter.new
-
reporter << SummaryReporter.new(options[:io], options)
-
reporter << ProgressReporter.new(options[:io], options)
-
-
self.reporter = reporter # this makes it available to plugins
-
self.init_plugins options
-
self.reporter = nil # runnables shouldn't depend on the reporter, ever
-
-
reporter.start
-
__run reporter, options
-
self.parallel_executor.shutdown
-
reporter.report
-
-
reporter.passed?
-
end
-
-
##
-
# Internal run method. Responsible for telling all Runnable
-
# sub-classes to run.
-
#
-
# NOTE: this method is redefined in parallel_each.rb, which is
-
# loaded if a Runnable calls parallelize_me!.
-
-
1
def self.__run reporter, options
-
suites = Runnable.runnables.shuffle
-
parallel, serial = suites.partition { |s| s.test_order == :parallel }
-
-
# If we run the parallel tests before the serial tests, the parallel tests
-
# could run in parallel with the serial tests. This would be bad because
-
# the serial tests won't lock around Reporter#record. Run the serial tests
-
# first, so that after they complete, the parallel tests will lock when
-
# recording results.
-
serial.map { |suite| suite.run reporter, options } +
-
parallel.map { |suite| suite.run reporter, options }
-
end
-
-
1
def self.process_args args = [] # :nodoc:
-
options = {
-
:io => $stdout,
-
}
-
orig_args = args.dup
-
-
OptionParser.new do |opts|
-
opts.banner = "minitest options:"
-
opts.version = Minitest::VERSION
-
-
opts.on "-h", "--help", "Display this help." do
-
puts opts
-
exit
-
end
-
-
opts.on "-s", "--seed SEED", Integer, "Sets random seed" do |m|
-
options[:seed] = m.to_i
-
end
-
-
opts.on "-v", "--verbose", "Verbose. Show progress processing files." do
-
options[:verbose] = true
-
end
-
-
opts.on "-n", "--name PATTERN","Filter run on /pattern/ or string." do |a|
-
options[:filter] = a
-
end
-
-
unless extensions.empty?
-
opts.separator ""
-
opts.separator "Known extensions: #{extensions.join(', ')}"
-
-
extensions.each do |meth|
-
msg = "plugin_#{meth}_options"
-
send msg, opts, options if self.respond_to?(msg)
-
end
-
end
-
-
begin
-
opts.parse! args
-
rescue OptionParser::InvalidOption => e
-
puts
-
puts e
-
puts
-
puts opts
-
exit 1
-
end
-
-
orig_args -= args
-
end
-
-
unless options[:seed] then
-
srand
-
options[:seed] = srand % 0xFFFF
-
orig_args << "--seed" << options[:seed].to_s
-
end
-
-
srand options[:seed]
-
-
options[:args] = orig_args.map { |s|
-
s =~ /[\s|&<>$()]/ ? s.inspect : s
-
}.join " "
-
-
options
-
end
-
-
1
def self.filter_backtrace bt # :nodoc:
-
backtrace_filter.filter bt
-
end
-
-
##
-
# Represents anything "runnable", like Test, Spec, Benchmark, or
-
# whatever you can dream up.
-
#
-
# Subclasses of this are automatically registered and available in
-
# Runnable.runnables.
-
-
1
class Runnable
-
##
-
# Number of assertions executed in this run.
-
-
1
attr_accessor :assertions
-
-
##
-
# An assertion raised during the run, if any.
-
-
1
attr_accessor :failures
-
-
##
-
# Name of the run.
-
-
1
def name
-
@NAME
-
end
-
-
##
-
# Set the name of the run.
-
-
1
def name= o
-
@NAME = o
-
end
-
-
1
def self.inherited klass # :nodoc:
-
7
self.runnables << klass
-
7
super
-
end
-
-
##
-
# Returns all instance methods matching the pattern +re+.
-
-
1
def self.methods_matching re
-
public_instance_methods(true).grep(re).map(&:to_s)
-
end
-
-
1
def self.reset # :nodoc:
-
1
@@runnables = []
-
end
-
-
1
reset
-
-
##
-
# Responsible for running all runnable methods in a given class,
-
# each in its own instance. Each instance is passed to the
-
# reporter to record.
-
-
1
def self.run reporter, options = {}
-
filter = options[:filter] || '/./'
-
filter = Regexp.new $1 if filter =~ /\/(.*)\//
-
-
filtered_methods = self.runnable_methods.find_all { |m|
-
filter === m || filter === "#{self}##{m}"
-
}
-
-
with_info_handler reporter do
-
filtered_methods.each do |method_name|
-
run_one_method self, method_name, reporter
-
end
-
end
-
end
-
-
1
def self.run_one_method klass, method_name, reporter
-
reporter.record Minitest.run_one_method(klass, method_name)
-
end
-
-
1
def self.with_info_handler reporter, &block # :nodoc:
-
handler = lambda do
-
unless reporter.passed? then
-
warn "Current results:"
-
warn ""
-
warn reporter.reporters.first
-
warn ""
-
end
-
end
-
-
on_signal "INFO", handler, &block
-
end
-
-
1
SIGNALS = Signal.list
-
-
1
def self.on_signal name, action # :nodoc:
-
supported = SIGNALS[name]
-
-
old_trap = trap name do
-
old_trap.call if old_trap.respond_to? :call
-
action.call
-
end if supported
-
-
yield
-
ensure
-
trap name, old_trap if supported
-
end
-
-
##
-
# Each subclass of Runnable is responsible for overriding this
-
# method to return all runnable methods. See #methods_matching.
-
-
1
def self.runnable_methods
-
raise NotImplementedError, "subclass responsibility"
-
end
-
-
##
-
# Returns all subclasses of Runnable.
-
-
1
def self.runnables
-
7
@@runnables
-
end
-
-
1
def marshal_dump # :nodoc:
-
[self.name, self.failures, self.assertions]
-
end
-
-
1
def marshal_load ary # :nodoc:
-
self.name, self.failures, self.assertions = ary
-
end
-
-
1
def failure # :nodoc:
-
self.failures.first
-
end
-
-
1
def initialize name # :nodoc:
-
self.name = name
-
self.failures = []
-
self.assertions = 0
-
end
-
-
##
-
# Runs a single method. Needs to return self.
-
-
1
def run
-
raise NotImplementedError, "subclass responsibility"
-
end
-
-
##
-
# Did this run pass?
-
#
-
# Note: skipped runs are not considered passing, but they don't
-
# cause the process to exit non-zero.
-
-
1
def passed?
-
raise NotImplementedError, "subclass responsibility"
-
end
-
-
##
-
# Returns a single character string to print based on the result
-
# of the run. Eg ".", "F", or "E".
-
-
1
def result_code
-
raise NotImplementedError, "subclass responsibility"
-
end
-
-
##
-
# Was this run skipped? See #passed? for more information.
-
-
1
def skipped?
-
raise NotImplementedError, "subclass responsibility"
-
end
-
end
-
-
##
-
# Defines the API for Reporters. Subclass this and override whatever
-
# you want. Go nuts.
-
-
1
class AbstractReporter
-
1
include Mutex_m
-
-
##
-
# Starts reporting on the run.
-
-
1
def start
-
end
-
-
##
-
# Record a result and output the Runnable#result_code. Stores the
-
# result of the run if the run did not pass.
-
-
1
def record result
-
end
-
-
##
-
# Outputs the summary of the run.
-
-
1
def report
-
end
-
-
##
-
# Did this run pass?
-
-
1
def passed?
-
true
-
end
-
end
-
-
1
class Reporter < AbstractReporter # :nodoc:
-
##
-
# The IO used to report.
-
-
1
attr_accessor :io
-
-
##
-
# Command-line options for this run.
-
-
1
attr_accessor :options
-
-
1
def initialize io = $stdout, options = {} # :nodoc:
-
super()
-
self.io = io
-
self.options = options
-
end
-
end
-
-
##
-
# A very simple reporter that prints the "dots" during the run.
-
#
-
# This is added to the top-level CompositeReporter at the start of
-
# the run. If you want to change the output of minitest via a
-
# plugin, pull this out of the composite and replace it with your
-
# own.
-
-
1
class ProgressReporter < Reporter
-
1
def record result # :nodoc:
-
io.print "%s#%s = %.2f s = " % [result.class, result.name, result.time] if
-
options[:verbose]
-
io.print result.result_code
-
io.puts if options[:verbose]
-
end
-
end
-
-
##
-
# A reporter that gathers statistics about a test run. Does not do
-
# any IO because meant to be used as a parent class for a reporter
-
# that does.
-
#
-
# If you want to create an entirely different type of output (eg,
-
# CI, HTML, etc), this is the place to start.
-
-
1
class StatisticsReporter < Reporter
-
# :stopdoc:
-
1
attr_accessor :assertions
-
1
attr_accessor :count
-
1
attr_accessor :results
-
1
attr_accessor :start_time
-
1
attr_accessor :total_time
-
1
attr_accessor :failures
-
1
attr_accessor :errors
-
1
attr_accessor :skips
-
# :startdoc:
-
-
1
def initialize io = $stdout, options = {} # :nodoc:
-
super
-
-
self.assertions = 0
-
self.count = 0
-
self.results = []
-
self.start_time = nil
-
self.total_time = nil
-
self.failures = nil
-
self.errors = nil
-
self.skips = nil
-
end
-
-
1
def passed? # :nodoc:
-
results.all?(&:skipped?)
-
end
-
-
1
def start # :nodoc:
-
self.start_time = Time.now
-
end
-
-
1
def record result # :nodoc:
-
self.count += 1
-
self.assertions += result.assertions
-
-
results << result if not result.passed? or result.skipped?
-
end
-
-
1
def report # :nodoc:
-
aggregate = results.group_by { |r| r.failure.class }
-
aggregate.default = [] # dumb. group_by should provide this
-
-
self.total_time = Time.now - start_time
-
self.failures = aggregate[Assertion].size
-
self.errors = aggregate[UnexpectedError].size
-
self.skips = aggregate[Skip].size
-
end
-
end
-
-
##
-
# A reporter that prints the header, summary, and failure details at
-
# the end of the run.
-
#
-
# This is added to the top-level CompositeReporter at the start of
-
# the run. If you want to change the output of minitest via a
-
# plugin, pull this out of the composite and replace it with your
-
# own.
-
-
1
class SummaryReporter < StatisticsReporter
-
# :stopdoc:
-
1
attr_accessor :sync
-
1
attr_accessor :old_sync
-
# :startdoc:
-
-
1
def start # :nodoc:
-
super
-
-
io.puts "Run options: #{options[:args]}"
-
io.puts
-
io.puts "# Running:"
-
io.puts
-
-
self.sync = io.respond_to? :"sync=" # stupid emacs
-
self.old_sync, io.sync = io.sync, true if self.sync
-
end
-
-
1
def report # :nodoc:
-
super
-
-
io.sync = self.old_sync
-
-
io.puts unless options[:verbose] # finish the dots
-
io.puts
-
io.puts statistics
-
io.puts aggregated_results
-
io.puts summary
-
end
-
-
1
def statistics # :nodoc:
-
"Finished in %.6fs, %.4f runs/s, %.4f assertions/s." %
-
[total_time, count / total_time, assertions / total_time]
-
end
-
-
1
def aggregated_results # :nodoc:
-
filtered_results = results.dup
-
filtered_results.reject!(&:skipped?) unless options[:verbose]
-
-
filtered_results.each_with_index.map do |result, i|
-
"\n%3d) %s" % [i+1, result]
-
end.join("\n") + "\n"
-
end
-
-
1
alias to_s aggregated_results
-
-
1
def summary # :nodoc:
-
extra = ""
-
-
extra = "\n\nYou have skipped tests. Run with --verbose for details." if
-
results.any?(&:skipped?) unless options[:verbose] or ENV["MT_NO_SKIP_MSG"]
-
-
"%d runs, %d assertions, %d failures, %d errors, %d skips%s" %
-
[count, assertions, failures, errors, skips, extra]
-
end
-
end
-
-
##
-
# Dispatch to multiple reporters as one.
-
-
1
class CompositeReporter < AbstractReporter
-
##
-
# The list of reporters to dispatch to.
-
-
1
attr_accessor :reporters
-
-
1
def initialize *reporters # :nodoc:
-
super()
-
self.reporters = reporters
-
end
-
-
##
-
# Add another reporter to the mix.
-
-
1
def << reporter
-
self.reporters << reporter
-
end
-
-
1
def passed? # :nodoc:
-
self.reporters.all?(&:passed?)
-
end
-
-
1
def start # :nodoc:
-
self.reporters.each(&:start)
-
end
-
-
1
def record result # :nodoc:
-
self.reporters.each do |reporter|
-
reporter.record result
-
end
-
end
-
-
1
def report # :nodoc:
-
self.reporters.each(&:report)
-
end
-
end
-
-
##
-
# Represents run failures.
-
-
1
class Assertion < Exception
-
1
def error # :nodoc:
-
self
-
end
-
-
##
-
# Where was this run before an assertion was raised?
-
-
1
def location
-
last_before_assertion = ""
-
self.backtrace.reverse_each do |s|
-
break if s =~ /in .(assert|refute|flunk|pass|fail|raise|must|wont)/
-
last_before_assertion = s
-
end
-
last_before_assertion.sub(/:in .*$/, "")
-
end
-
-
1
def result_code # :nodoc:
-
result_label[0, 1]
-
end
-
-
1
def result_label # :nodoc:
-
"Failure"
-
end
-
end
-
-
##
-
# Assertion raised when skipping a run.
-
-
1
class Skip < Assertion
-
1
def result_label # :nodoc:
-
"Skipped"
-
end
-
end
-
-
##
-
# Assertion wrapping an unexpected error that was raised during a run.
-
-
1
class UnexpectedError < Assertion
-
1
attr_accessor :exception # :nodoc:
-
-
1
def initialize exception # :nodoc:
-
super
-
self.exception = exception
-
end
-
-
1
def backtrace # :nodoc:
-
self.exception.backtrace
-
end
-
-
1
def error # :nodoc:
-
self.exception
-
end
-
-
1
def message # :nodoc:
-
bt = Minitest::filter_backtrace(self.backtrace).join "\n "
-
"#{self.exception.class}: #{self.exception.message}\n #{bt}"
-
end
-
-
1
def result_label # :nodoc:
-
"Error"
-
end
-
end
-
-
##
-
# Provides a simple set of guards that you can use in your tests
-
# to skip execution if it is not applicable. These methods are
-
# mixed into Test as both instance and class methods so you
-
# can use them inside or outside of the test methods.
-
#
-
# def test_something_for_mri
-
# skip "bug 1234" if jruby?
-
# # ...
-
# end
-
#
-
# if windows? then
-
# # ... lots of test methods ...
-
# end
-
-
1
module Guard
-
-
##
-
# Is this running on jruby?
-
-
1
def jruby? platform = RUBY_PLATFORM
-
"java" == platform
-
end
-
-
##
-
# Is this running on maglev?
-
-
1
def maglev? platform = defined?(RUBY_ENGINE) && RUBY_ENGINE
-
"maglev" == platform
-
end
-
-
##
-
# Is this running on mri?
-
-
1
def mri? platform = RUBY_DESCRIPTION
-
/^ruby/ =~ platform
-
end
-
-
##
-
# Is this running on rubinius?
-
-
1
def rubinius? platform = defined?(RUBY_ENGINE) && RUBY_ENGINE
-
"rbx" == platform
-
end
-
-
##
-
# Is this running on windows?
-
-
1
def windows? platform = RUBY_PLATFORM
-
/mswin|mingw/ =~ platform
-
end
-
end
-
-
1
class BacktraceFilter # :nodoc:
-
1
def filter bt
-
return ["No backtrace"] unless bt
-
-
return bt.dup if $DEBUG
-
-
new_bt = bt.take_while { |line| line !~ /lib\/minitest/ }
-
new_bt = bt.select { |line| line !~ /lib\/minitest/ } if new_bt.empty?
-
new_bt = bt.dup if new_bt.empty?
-
-
new_bt
-
end
-
end
-
-
1
self.backtrace_filter = BacktraceFilter.new
-
-
1
def self.run_one_method klass, method_name # :nodoc:
-
result = klass.new(method_name).run
-
raise "#{klass}#run _must_ return self" unless klass === result
-
result
-
end
-
end
-
-
1
require "minitest/test"
-
1
require "rbconfig"
-
1
require "tempfile"
-
1
require 'stringio'
-
-
1
module Minitest
-
##
-
# Minitest Assertions. All assertion methods accept a +msg+ which is
-
# printed if the assertion fails.
-
#
-
# Protocol: Nearly everything here boils up to +assert+, which
-
# expects to be able to increment an instance accessor named
-
# +assertions+. This is not provided by Assertions and must be
-
# provided by the thing including Assertions. See Minitest::Runnable
-
# for an example.
-
-
1
module Assertions
-
1
UNDEFINED = Object.new # :nodoc:
-
-
1
def UNDEFINED.inspect # :nodoc:
-
"UNDEFINED" # again with the rdoc bugs... :(
-
end
-
-
##
-
# Returns the diff command to use in #diff. Tries to intelligently
-
# figure out what diff to use.
-
-
1
def self.diff
-
@diff = if (RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ &&
-
system("diff.exe", __FILE__, __FILE__)) then
-
"diff.exe -u"
-
elsif Minitest::Test.maglev? then
-
"diff -u"
-
elsif system("gdiff", __FILE__, __FILE__)
-
"gdiff -u" # solaris and kin suck
-
elsif system("diff", __FILE__, __FILE__)
-
"diff -u"
-
else
-
nil
-
end unless defined? @diff
-
-
@diff
-
end
-
-
##
-
# Set the diff command to use in #diff.
-
-
1
def self.diff= o
-
@diff = o
-
end
-
-
##
-
# Returns a diff between +exp+ and +act+. If there is no known
-
# diff command or if it doesn't make sense to diff the output
-
# (single line, short output), then it simply returns a basic
-
# comparison between the two.
-
-
1
def diff exp, act
-
expect = mu_pp_for_diff exp
-
butwas = mu_pp_for_diff act
-
result = nil
-
-
need_to_diff =
-
(expect.include?("\n") ||
-
butwas.include?("\n") ||
-
expect.size > 30 ||
-
butwas.size > 30 ||
-
expect == butwas) &&
-
Minitest::Assertions.diff
-
-
-
return "Expected: #{mu_pp exp}\n Actual: #{mu_pp act}" unless
-
need_to_diff
-
-
Tempfile.open("expect") do |a|
-
a.puts expect
-
a.flush
-
-
Tempfile.open("butwas") do |b|
-
b.puts butwas
-
b.flush
-
-
result = `#{Minitest::Assertions.diff} #{a.path} #{b.path}`
-
result.sub!(/^\-\-\- .+/, "--- expected")
-
result.sub!(/^\+\+\+ .+/, "+++ actual")
-
-
if result.empty? then
-
klass = exp.class
-
result = [
-
"No visible difference in the #{klass}#inspect output.\n",
-
"You should look at the implementation of #== on ",
-
"#{klass} or its members.\n",
-
expect,
-
].join
-
end
-
end
-
end
-
-
result
-
end
-
-
##
-
# This returns a human-readable version of +obj+. By default
-
# #inspect is called. You can override this to use #pretty_print
-
# if you want.
-
-
1
def mu_pp obj
-
2
s = obj.inspect
-
2
s = s.encode Encoding.default_external if defined? Encoding
-
2
s
-
end
-
-
##
-
# This returns a diff-able human-readable version of +obj+. This
-
# differs from the regular mu_pp because it expands escaped
-
# newlines and makes hex-values generic (like object_ids). This
-
# uses mu_pp to do the first pass and then cleans it up.
-
-
1
def mu_pp_for_diff obj
-
mu_pp(obj).gsub(/\\n/, "\n").gsub(/:0x[a-fA-F0-9]{4,}/m, ':0xXXXXXX')
-
end
-
-
##
-
# Fails unless +test+ is truthy.
-
-
1
def assert test, msg = nil
-
3
self.assertions += 1
-
3
unless test then
-
1
msg ||= "Failed assertion, no message given."
-
1
msg = msg.call if Proc === msg
-
1
raise Minitest::Assertion, msg
-
end
-
2
true
-
end
-
-
1
def _synchronize # :nodoc:
-
yield
-
end
-
-
##
-
# Fails unless +obj+ is empty.
-
-
1
def assert_empty obj, msg = nil
-
msg = message(msg) { "Expected #{mu_pp(obj)} to be empty" }
-
assert_respond_to obj, :empty?
-
assert obj.empty?, msg
-
end
-
-
1
E = ""
-
-
##
-
# Fails unless <tt>exp == act</tt> printing the difference between
-
# the two, if possible.
-
#
-
# If there is no visible difference but the assertion fails, you
-
# should suspect that your #== is buggy, or your inspect output is
-
# missing crucial details.
-
#
-
# For floats use assert_in_delta.
-
#
-
# See also: Minitest::Assertions.diff
-
-
1
def assert_equal exp, act, msg = nil
-
msg = message(msg, E) { diff exp, act }
-
assert exp == act, msg
-
end
-
-
##
-
# For comparing Floats. Fails unless +exp+ and +act+ are within +delta+
-
# of each other.
-
#
-
# assert_in_delta Math::PI, (22.0 / 7.0), 0.01
-
-
1
def assert_in_delta exp, act, delta = 0.001, msg = nil
-
n = (exp - act).abs
-
msg = message(msg) {
-
"Expected |#{exp} - #{act}| (#{n}) to be <= #{delta}"
-
}
-
assert delta >= n, msg
-
end
-
-
##
-
# For comparing Floats. Fails unless +exp+ and +act+ have a relative
-
# error less than +epsilon+.
-
-
1
def assert_in_epsilon a, b, epsilon = 0.001, msg = nil
-
assert_in_delta a, b, [a.abs, b.abs].min * epsilon, msg
-
end
-
-
##
-
# Fails unless +collection+ includes +obj+.
-
-
1
def assert_includes collection, obj, msg = nil
-
msg = message(msg) {
-
"Expected #{mu_pp(collection)} to include #{mu_pp(obj)}"
-
}
-
assert_respond_to collection, :include?
-
assert collection.include?(obj), msg
-
end
-
-
##
-
# Fails unless +obj+ is an instance of +cls+.
-
-
1
def assert_instance_of cls, obj, msg = nil
-
msg = message(msg) {
-
"Expected #{mu_pp(obj)} to be an instance of #{cls}, not #{obj.class}"
-
}
-
-
assert obj.instance_of?(cls), msg
-
end
-
-
##
-
# Fails unless +obj+ is a kind of +cls+.
-
-
1
def assert_kind_of cls, obj, msg = nil
-
msg = message(msg) {
-
"Expected #{mu_pp(obj)} to be a kind of #{cls}, not #{obj.class}" }
-
-
assert obj.kind_of?(cls), msg
-
end
-
-
##
-
# Fails unless +matcher+ <tt>=~</tt> +obj+.
-
-
1
def assert_match matcher, obj, msg = nil
-
msg = message(msg) { "Expected #{mu_pp matcher} to match #{mu_pp obj}" }
-
assert_respond_to matcher, :"=~"
-
matcher = Regexp.new Regexp.escape matcher if String === matcher
-
assert matcher =~ obj, msg
-
end
-
-
##
-
# Fails unless +obj+ is nil
-
-
1
def assert_nil obj, msg = nil
-
msg = message(msg) { "Expected #{mu_pp(obj)} to be nil" }
-
assert obj.nil?, msg
-
end
-
-
##
-
# For testing with binary operators. Eg:
-
#
-
# assert_operator 5, :<=, 4
-
-
1
def assert_operator o1, op, o2 = UNDEFINED, msg = nil
-
1
return assert_predicate o1, op, msg if UNDEFINED == o2
-
2
msg = message(msg) { "Expected #{mu_pp(o1)} to be #{op} #{mu_pp(o2)}" }
-
1
assert o1.__send__(op, o2), msg
-
end
-
-
##
-
# Fails if stdout or stderr do not output the expected results.
-
# Pass in nil if you don't care about that streams output. Pass in
-
# "" if you require it to be silent. Pass in a regexp if you want
-
# to pattern match.
-
#
-
# NOTE: this uses #capture_io, not #capture_subprocess_io.
-
#
-
# See also: #assert_silent
-
-
1
def assert_output stdout = nil, stderr = nil
-
out, err = capture_io do
-
yield
-
end
-
-
err_msg = Regexp === stderr ? :assert_match : :assert_equal if stderr
-
out_msg = Regexp === stdout ? :assert_match : :assert_equal if stdout
-
-
y = send err_msg, stderr, err, "In stderr" if err_msg
-
x = send out_msg, stdout, out, "In stdout" if out_msg
-
-
(!stdout || x) && (!stderr || y)
-
end
-
-
##
-
# For testing with predicates. Eg:
-
#
-
# assert_predicate str, :empty?
-
#
-
# This is really meant for specs and is front-ended by assert_operator:
-
#
-
# str.must_be :empty?
-
-
1
def assert_predicate o1, op, msg = nil
-
msg = message(msg) { "Expected #{mu_pp(o1)} to be #{op}" }
-
assert o1.__send__(op), msg
-
end
-
-
##
-
# Fails unless the block raises one of +exp+. Returns the
-
# exception matched so you can check the message, attributes, etc.
-
-
1
def assert_raises *exp
-
msg = "#{exp.pop}.\n" if String === exp.last
-
-
begin
-
yield
-
rescue Minitest::Skip => e
-
return e if exp.include? Minitest::Skip
-
raise e
-
rescue Exception => e
-
expected = exp.any? { |ex|
-
if ex.instance_of? Module then
-
e.kind_of? ex
-
else
-
e.instance_of? ex
-
end
-
}
-
-
assert expected, proc {
-
exception_details(e, "#{msg}#{mu_pp(exp)} exception expected, not")
-
}
-
-
return e
-
end
-
-
exp = exp.first if exp.size == 1
-
-
flunk "#{msg}#{mu_pp(exp)} expected but nothing was raised."
-
end
-
-
##
-
# Fails unless +obj+ responds to +meth+.
-
-
1
def assert_respond_to obj, meth, msg = nil
-
msg = message(msg) {
-
"Expected #{mu_pp(obj)} (#{obj.class}) to respond to ##{meth}"
-
}
-
assert obj.respond_to?(meth), msg
-
end
-
-
##
-
# Fails unless +exp+ and +act+ are #equal?
-
-
1
def assert_same exp, act, msg = nil
-
msg = message(msg) {
-
data = [mu_pp(act), act.object_id, mu_pp(exp), exp.object_id]
-
"Expected %s (oid=%d) to be the same as %s (oid=%d)" % data
-
}
-
assert exp.equal?(act), msg
-
end
-
-
##
-
# +send_ary+ is a receiver, message and arguments.
-
#
-
# Fails unless the call returns a true value
-
-
1
def assert_send send_ary, m = nil
-
recv, msg, *args = send_ary
-
m = message(m) {
-
"Expected #{mu_pp(recv)}.#{msg}(*#{mu_pp(args)}) to return true" }
-
assert recv.__send__(msg, *args), m
-
end
-
-
##
-
# Fails if the block outputs anything to stderr or stdout.
-
#
-
# See also: #assert_output
-
-
1
def assert_silent
-
assert_output "", "" do
-
yield
-
end
-
end
-
-
##
-
# Fails unless the block throws +sym+
-
-
1
def assert_throws sym, msg = nil
-
default = "Expected #{mu_pp(sym)} to have been thrown"
-
caught = true
-
catch(sym) do
-
begin
-
yield
-
rescue ThreadError => e # wtf?!? 1.8 + threads == suck
-
default += ", not \:#{e.message[/uncaught throw \`(\w+?)\'/, 1]}"
-
rescue ArgumentError => e # 1.9 exception
-
default += ", not #{e.message.split(/ /).last}"
-
rescue NameError => e # 1.8 exception
-
default += ", not #{e.name.inspect}"
-
end
-
caught = false
-
end
-
-
assert caught, message(msg) { default }
-
end
-
-
##
-
# Captures $stdout and $stderr into strings:
-
#
-
# out, err = capture_io do
-
# puts "Some info"
-
# warn "You did a bad thing"
-
# end
-
#
-
# assert_match %r%info%, out
-
# assert_match %r%bad%, err
-
#
-
# NOTE: For efficiency, this method uses StringIO and does not
-
# capture IO for subprocesses. Use #capture_subprocess_io for
-
# that.
-
-
1
def capture_io
-
_synchronize do
-
begin
-
captured_stdout, captured_stderr = StringIO.new, StringIO.new
-
-
orig_stdout, orig_stderr = $stdout, $stderr
-
$stdout, $stderr = captured_stdout, captured_stderr
-
-
yield
-
-
return captured_stdout.string, captured_stderr.string
-
ensure
-
$stdout = orig_stdout
-
$stderr = orig_stderr
-
end
-
end
-
end
-
-
##
-
# Captures $stdout and $stderr into strings, using Tempfile to
-
# ensure that subprocess IO is captured as well.
-
#
-
# out, err = capture_subprocess_io do
-
# system "echo Some info"
-
# system "echo You did a bad thing 1>&2"
-
# end
-
#
-
# assert_match %r%info%, out
-
# assert_match %r%bad%, err
-
#
-
# NOTE: This method is approximately 10x slower than #capture_io so
-
# only use it when you need to test the output of a subprocess.
-
-
1
def capture_subprocess_io
-
_synchronize do
-
begin
-
require 'tempfile'
-
-
captured_stdout, captured_stderr = Tempfile.new("out"), Tempfile.new("err")
-
-
orig_stdout, orig_stderr = $stdout.dup, $stderr.dup
-
$stdout.reopen captured_stdout
-
$stderr.reopen captured_stderr
-
-
yield
-
-
$stdout.rewind
-
$stderr.rewind
-
-
return captured_stdout.read, captured_stderr.read
-
ensure
-
captured_stdout.unlink
-
captured_stderr.unlink
-
$stdout.reopen orig_stdout
-
$stderr.reopen orig_stderr
-
end
-
end
-
end
-
-
##
-
# Returns details for exception +e+
-
-
1
def exception_details e, msg
-
[
-
"#{msg}",
-
"Class: <#{e.class}>",
-
"Message: <#{e.message.inspect}>",
-
"---Backtrace---",
-
"#{Minitest::filter_backtrace(e.backtrace).join("\n")}",
-
"---------------",
-
].join "\n"
-
end
-
-
##
-
# Fails with +msg+
-
-
1
def flunk msg = nil
-
msg ||= "Epic Fail!"
-
assert false, msg
-
end
-
-
##
-
# Returns a proc that will output +msg+ along with the default message.
-
-
1
def message msg = nil, ending = nil, &default
-
1
proc {
-
1
msg = msg.call.chomp(".") if Proc === msg
-
1
custom_message = "#{msg}.\n" unless msg.nil? or msg.to_s.empty?
-
1
"#{custom_message}#{default.call}#{ending || "."}"
-
}
-
end
-
-
##
-
# used for counting assertions
-
-
1
def pass msg = nil
-
assert true
-
end
-
-
##
-
# Fails if +test+ is truthy.
-
-
1
def refute test, msg = nil
-
msg ||= "Failed refutation, no message given"
-
not assert(! test, msg)
-
end
-
-
##
-
# Fails if +obj+ is empty.
-
-
1
def refute_empty obj, msg = nil
-
msg = message(msg) { "Expected #{mu_pp(obj)} to not be empty" }
-
assert_respond_to obj, :empty?
-
refute obj.empty?, msg
-
end
-
-
##
-
# Fails if <tt>exp == act</tt>.
-
#
-
# For floats use refute_in_delta.
-
-
1
def refute_equal exp, act, msg = nil
-
msg = message(msg) {
-
"Expected #{mu_pp(act)} to not be equal to #{mu_pp(exp)}"
-
}
-
refute exp == act, msg
-
end
-
-
##
-
# For comparing Floats. Fails if +exp+ is within +delta+ of +act+.
-
#
-
# refute_in_delta Math::PI, (22.0 / 7.0)
-
-
1
def refute_in_delta exp, act, delta = 0.001, msg = nil
-
n = (exp - act).abs
-
msg = message(msg) {
-
"Expected |#{exp} - #{act}| (#{n}) to not be <= #{delta}"
-
}
-
refute delta >= n, msg
-
end
-
-
##
-
# For comparing Floats. Fails if +exp+ and +act+ have a relative error
-
# less than +epsilon+.
-
-
1
def refute_in_epsilon a, b, epsilon = 0.001, msg = nil
-
refute_in_delta a, b, a * epsilon, msg
-
end
-
-
##
-
# Fails if +collection+ includes +obj+.
-
-
1
def refute_includes collection, obj, msg = nil
-
msg = message(msg) {
-
"Expected #{mu_pp(collection)} to not include #{mu_pp(obj)}"
-
}
-
assert_respond_to collection, :include?
-
refute collection.include?(obj), msg
-
end
-
-
##
-
# Fails if +obj+ is an instance of +cls+.
-
-
1
def refute_instance_of cls, obj, msg = nil
-
msg = message(msg) {
-
"Expected #{mu_pp(obj)} to not be an instance of #{cls}"
-
}
-
refute obj.instance_of?(cls), msg
-
end
-
-
##
-
# Fails if +obj+ is a kind of +cls+.
-
-
1
def refute_kind_of cls, obj, msg = nil
-
msg = message(msg) { "Expected #{mu_pp(obj)} to not be a kind of #{cls}" }
-
refute obj.kind_of?(cls), msg
-
end
-
-
##
-
# Fails if +matcher+ <tt>=~</tt> +obj+.
-
-
1
def refute_match matcher, obj, msg = nil
-
msg = message(msg) {"Expected #{mu_pp matcher} to not match #{mu_pp obj}"}
-
assert_respond_to matcher, :"=~"
-
matcher = Regexp.new Regexp.escape matcher if String === matcher
-
refute matcher =~ obj, msg
-
end
-
-
##
-
# Fails if +obj+ is nil.
-
-
1
def refute_nil obj, msg = nil
-
msg = message(msg) { "Expected #{mu_pp(obj)} to not be nil" }
-
refute obj.nil?, msg
-
end
-
-
##
-
# Fails if +o1+ is not +op+ +o2+. Eg:
-
#
-
# refute_operator 1, :>, 2 #=> pass
-
# refute_operator 1, :<, 2 #=> fail
-
-
1
def refute_operator o1, op, o2 = UNDEFINED, msg = nil
-
return refute_predicate o1, op, msg if UNDEFINED == o2
-
msg = message(msg) { "Expected #{mu_pp(o1)} to not be #{op} #{mu_pp(o2)}"}
-
refute o1.__send__(op, o2), msg
-
end
-
-
##
-
# For testing with predicates.
-
#
-
# refute_predicate str, :empty?
-
#
-
# This is really meant for specs and is front-ended by refute_operator:
-
#
-
# str.wont_be :empty?
-
-
1
def refute_predicate o1, op, msg = nil
-
msg = message(msg) { "Expected #{mu_pp(o1)} to not be #{op}" }
-
refute o1.__send__(op), msg
-
end
-
-
##
-
# Fails if +obj+ responds to the message +meth+.
-
-
1
def refute_respond_to obj, meth, msg = nil
-
msg = message(msg) { "Expected #{mu_pp(obj)} to not respond to #{meth}" }
-
-
refute obj.respond_to?(meth), msg
-
end
-
-
##
-
# Fails if +exp+ is the same (by object identity) as +act+.
-
-
1
def refute_same exp, act, msg = nil
-
msg = message(msg) {
-
data = [mu_pp(act), act.object_id, mu_pp(exp), exp.object_id]
-
"Expected %s (oid=%d) to not be the same as %s (oid=%d)" % data
-
}
-
refute exp.equal?(act), msg
-
end
-
-
##
-
# Skips the current run. If run in verbose-mode, the skipped run
-
# gets listed at the end of the run but doesn't cause a failure
-
# exit code.
-
-
1
def skip msg = nil, bt = caller
-
msg ||= "Skipped, no message given"
-
@skip = true
-
raise Minitest::Skip, msg, bt
-
end
-
-
##
-
# Was this testcase skipped? Meant for #teardown.
-
-
1
def skipped?
-
defined?(@skip) and @skip
-
end
-
end
-
end
-
1
module Minitest
-
1
module Parallel
-
1
class Executor
-
1
attr_reader :size
-
-
1
def initialize size
-
1
@size = size
-
1
@queue = Queue.new
-
1
@pool = size.times.map {
-
2
Thread.new(@queue) do |queue|
-
2
Thread.current.abort_on_exception = true
-
2
while job = queue.pop
-
klass, method, reporter = job
-
result = Minitest.run_one_method klass, method
-
reporter.synchronize { reporter.record result }
-
end
-
end
-
}
-
end
-
-
1
def << work; @queue << work; end
-
-
1
def shutdown
-
size.times { @queue << nil }
-
@pool.each(&:join)
-
end
-
end
-
-
1
module Test
-
1
def _synchronize; Test.io_lock.synchronize { yield }; end
-
-
1
module ClassMethods
-
1
def run_one_method klass, method_name, reporter
-
Minitest.parallel_executor << [klass, method_name, reporter]
-
end
-
1
def test_order; :parallel; end
-
end
-
end
-
end
-
end
-
1
require "minitest" unless defined? Minitest::Runnable
-
-
1
module Minitest
-
##
-
# Subclass Test to create your own tests. Typically you'll want a
-
# Test subclass per implementation class.
-
#
-
# See Minitest::Assertions
-
-
1
class Test < Runnable
-
1
require "minitest/assertions"
-
1
include Minitest::Assertions
-
-
1
PASSTHROUGH_EXCEPTIONS = [NoMemoryError, SignalException, # :nodoc:
-
Interrupt, SystemExit]
-
-
2
class << self; attr_accessor :io_lock; end
-
1
self.io_lock = Mutex.new
-
-
##
-
# Call this at the top of your tests when you absolutely
-
# positively need to have ordered tests. In doing so, you're
-
# admitting that you suck and your tests are weak.
-
-
1
def self.i_suck_and_my_tests_are_order_dependent!
-
1
class << self
-
1
undef_method :test_order if method_defined? :test_order
-
1
define_method :test_order do :alpha end
-
end
-
end
-
-
##
-
# Make diffs for this Test use #pretty_inspect so that diff
-
# in assert_equal can have more details. NOTE: this is much slower
-
# than the regular inspect but much more usable for complex
-
# objects.
-
-
1
def self.make_my_diffs_pretty!
-
require "pp"
-
-
define_method :mu_pp do |o|
-
o.pretty_inspect
-
end
-
end
-
-
##
-
# Call this at the top of your tests when you want to run your
-
# tests in parallel. In doing so, you're admitting that you rule
-
# and your tests are awesome.
-
-
1
def self.parallelize_me!
-
include Minitest::Parallel::Test
-
extend Minitest::Parallel::Test::ClassMethods
-
end
-
-
##
-
# Returns all instance methods starting with "test_". Based on
-
# #test_order, the methods are either sorted, randomized
-
# (default), or run in parallel.
-
-
1
def self.runnable_methods
-
methods = methods_matching(/^test_/)
-
-
case self.test_order
-
when :random, :parallel then
-
max = methods.size
-
methods.sort.sort_by { rand max }
-
when :alpha, :sorted then
-
methods.sort
-
else
-
raise "Unknown test_order: #{self.test_order.inspect}"
-
end
-
end
-
-
##
-
# Defines the order to run tests (:random by default). Override
-
# this or use a convenience method to change it for your tests.
-
-
1
def self.test_order
-
:random
-
end
-
-
##
-
# The time it took to run this test.
-
-
1
attr_accessor :time
-
-
1
def marshal_dump # :nodoc:
-
super << self.time
-
end
-
-
1
def marshal_load ary # :nodoc:
-
self.time = ary.pop
-
super
-
end
-
-
1
TEARDOWN_METHODS = %w{ before_teardown teardown after_teardown } # :nodoc:
-
-
##
-
# Runs a single test with setup/teardown hooks.
-
-
1
def run
-
with_info_handler do
-
time_it do
-
capture_exceptions do
-
before_setup; setup; after_setup
-
-
self.send self.name
-
end
-
-
TEARDOWN_METHODS.each do |hook|
-
capture_exceptions do
-
self.send hook
-
end
-
end
-
end
-
end
-
-
self # per contract
-
end
-
-
##
-
# Provides before/after hooks for setup and teardown. These are
-
# meant for library writers, NOT for regular test authors. See
-
# #before_setup for an example.
-
-
1
module LifecycleHooks
-
-
##
-
# Runs before every test, before setup. This hook is meant for
-
# libraries to extend minitest. It is not meant to be used by
-
# test developers.
-
#
-
# As a simplistic example:
-
#
-
# module MyMinitestPlugin
-
# def before_setup
-
# super
-
# # ... stuff to do before setup is run
-
# end
-
#
-
# def after_setup
-
# # ... stuff to do after setup is run
-
# super
-
# end
-
#
-
# def before_teardown
-
# super
-
# # ... stuff to do before teardown is run
-
# end
-
#
-
# def after_teardown
-
# # ... stuff to do after teardown is run
-
# super
-
# end
-
# end
-
#
-
# class MiniTest::Test
-
# include MyMinitestPlugin
-
# end
-
-
1
def before_setup; end
-
-
##
-
# Runs before every test. Use this to set up before each test
-
# run.
-
-
1
def setup; end
-
-
##
-
# Runs before every test, after setup. This hook is meant for
-
# libraries to extend minitest. It is not meant to be used by
-
# test developers.
-
#
-
# See #before_setup for an example.
-
-
1
def after_setup; end
-
-
##
-
# Runs after every test, before teardown. This hook is meant for
-
# libraries to extend minitest. It is not meant to be used by
-
# test developers.
-
#
-
# See #before_setup for an example.
-
-
1
def before_teardown; end
-
-
##
-
# Runs after every test. Use this to clean up after each test
-
# run.
-
-
1
def teardown; end
-
-
##
-
# Runs after every test, after teardown. This hook is meant for
-
# libraries to extend minitest. It is not meant to be used by
-
# test developers.
-
#
-
# See #before_setup for an example.
-
-
1
def after_teardown; end
-
end # LifecycleHooks
-
-
1
def capture_exceptions # :nodoc:
-
begin
-
yield
-
rescue *PASSTHROUGH_EXCEPTIONS
-
raise
-
rescue Assertion => e
-
self.failures << e
-
rescue Exception => e
-
self.failures << UnexpectedError.new(e)
-
end
-
end
-
-
##
-
# Did this run error?
-
-
1
def error?
-
self.failures.any? { |f| UnexpectedError === f }
-
end
-
-
##
-
# The location identifier of this test.
-
-
1
def location
-
loc = " [#{self.failure.location}]" unless passed? or error?
-
"#{self.class}##{self.name}#{loc}"
-
end
-
-
##
-
# Did this run pass?
-
#
-
# Note: skipped runs are not considered passing, but they don't
-
# cause the process to exit non-zero.
-
-
1
def passed?
-
not self.failure
-
end
-
-
##
-
# Returns ".", "F", or "E" based on the result of the run.
-
-
1
def result_code
-
self.failure and self.failure.result_code or "."
-
end
-
-
##
-
# Was this run skipped?
-
-
1
def skipped?
-
self.failure and Skip === self.failure
-
end
-
-
1
def time_it # :nodoc:
-
t0 = Time.now
-
-
yield
-
ensure
-
self.time = Time.now - t0
-
end
-
-
1
def to_s # :nodoc:
-
return location if passed? and not skipped?
-
-
failures.map { |failure|
-
"#{failure.result_label}:\n#{self.location}:\n#{failure.message}\n"
-
}.join "\n"
-
end
-
-
1
def with_info_handler &block # :nodoc:
-
t0 = Time.now
-
-
handler = lambda do
-
warn "\nCurrent: %s#%s %.2fs" % [self.class, self.name, Time.now - t0]
-
end
-
-
self.class.on_signal "INFO", handler, &block
-
end
-
-
1
include LifecycleHooks
-
1
include Guard
-
1
extend Guard
-
end # Test
-
end
-
-
1
require "minitest/unit" unless defined?(MiniTest) # compatibility layer only
-
# :stopdoc:
-
-
1
unless defined?(Minitest) then
-
# all of this crap is just to avoid circular requires and is only
-
# needed if a user requires "minitest/unit" directly instead of
-
# "minitest/autorun", so we also warn
-
-
from = caller.reject { |s| s =~ /rubygems/ }.join("\n ")
-
warn "Warning: you should require 'minitest/autorun' instead."
-
warn %(Warning: or add 'gem "minitest"' before 'require "minitest/autorun"')
-
warn "From:\n #{from}"
-
-
module Minitest; end
-
MiniTest = Minitest # prevents minitest.rb from requiring back to us
-
require "minitest"
-
end
-
-
1
MiniTest = Minitest unless defined?(MiniTest)
-
-
1
module Minitest
-
1
class Unit
-
1
VERSION = Minitest::VERSION
-
1
class TestCase < Minitest::Test
-
1
def self.inherited klass # :nodoc:
-
from = caller.first
-
warn "MiniTest::Unit::TestCase is now Minitest::Test. From #{from}"
-
super
-
end
-
end
-
-
1
def self.autorun # :nodoc:
-
from = caller.first
-
warn "MiniTest::Unit.autorun is now Minitest.autorun. From #{from}"
-
Minitest.autorun
-
end
-
-
1
def self.after_tests(&b)
-
from = caller.first
-
warn "MiniTest::Unit.after_tests is now Minitest.after_run. From #{from}"
-
Minitest.after_run(&b)
-
end
-
end
-
end
-
-
# :startdoc:
-
1
require 'singleton'
-
1
require 'multi_json/options'
-
-
1
module MultiJson
-
1
class Adapter
-
1
extend Options
-
1
include Singleton
-
1
class << self
-
-
1
def defaults(action, value)
-
2
metaclass = class << self; self; end
-
-
1
metaclass.instance_eval do
-
2
define_method("default_#{action}_options"){ value }
-
end
-
end
-
-
1
def load(string, options={})
-
1
raise self::ParseError if blank?(string)
-
1
instance.load(string, load_options(options).merge(MultiJson.load_options(options)).merge!(options))
-
end
-
-
1
def dump(object, options={})
-
instance.dump(object, dump_options(options).merge(MultiJson.dump_options(options)).merge!(options))
-
end
-
-
1
private
-
-
1
def blank?(input)
-
1
input.nil? || /\A\s*\z/ === input
-
rescue ArgumentError # invalid byte sequence in UTF-8
-
false
-
end
-
-
end
-
end
-
end
-
1
require 'multi_json/adapter'
-
-
1
module MultiJson
-
1
module Adapters
-
1
class JsonCommon < Adapter
-
1
defaults :load, :create_additions => false, :quirks_mode => true
-
-
1
def load(string, options={})
-
1
string = string.read if string.respond_to?(:read)
-
-
1
if string.respond_to?(:force_encoding)
-
1
string = string.dup.force_encoding(::Encoding::ASCII_8BIT)
-
end
-
-
1
options[:symbolize_names] = true if options.delete(:symbolize_keys)
-
1
::JSON.parse(string, options)
-
end
-
-
1
def dump(object, options={})
-
options.merge!(::JSON::PRETTY_STATE_PROTOTYPE.to_h) if options.delete(:pretty)
-
object.to_json(options)
-
end
-
end
-
end
-
end
-
1
require 'json/ext'
-
1
require 'multi_json/adapters/json_common'
-
-
1
module MultiJson
-
1
module Adapters
-
# Use the JSON gem to dump/load.
-
1
class JsonGem < JsonCommon
-
1
ParseError = ::JSON::ParserError
-
end
-
end
-
end
-
1
require 'base64'
-
1
require 'bigdecimal'
-
1
require 'date'
-
1
require 'stringio'
-
1
require 'time'
-
1
require 'yaml'
-
-
1
module MultiXml
-
1
class ParseError < StandardError; end
-
1
class DisallowedTypeError < StandardError
-
1
def initialize(type)
-
super "Disallowed type attribute: #{type.inspect}"
-
end
-
end
-
-
REQUIREMENT_MAP = [
-
['ox', :ox],
-
['libxml', :libxml],
-
['nokogiri', :nokogiri],
-
['rexml/document', :rexml]
-
1
] unless defined?(REQUIREMENT_MAP)
-
-
1
CONTENT_ROOT = '__content__'.freeze unless defined?(CONTENT_ROOT)
-
-
1
unless defined?(PARSING)
-
1
PARSING = {
-
'symbol' => Proc.new{|symbol| symbol.to_sym},
-
'date' => Proc.new{|date| Date.parse(date)},
-
'datetime' => Proc.new{|time| Time.parse(time).utc rescue DateTime.parse(time).utc},
-
'integer' => Proc.new{|integer| integer.to_i},
-
'float' => Proc.new{|float| float.to_f},
-
'decimal' => Proc.new{|number| BigDecimal(number)},
-
'boolean' => Proc.new{|boolean| !%w(0 false).include?(boolean.strip)},
-
'string' => Proc.new{|string| string.to_s},
-
'yaml' => Proc.new{|yaml| YAML::load(yaml) rescue yaml},
-
'base64Binary' => Proc.new{|binary| ::Base64.decode64(binary)},
-
'binary' => Proc.new{|binary, entity| parse_binary(binary, entity)},
-
'file' => Proc.new{|file, entity| parse_file(file, entity)},
-
}
-
-
1
PARSING.update(
-
'double' => PARSING['float'],
-
'dateTime' => PARSING['datetime']
-
)
-
end
-
-
TYPE_NAMES = {
-
'Symbol' => 'symbol',
-
'Fixnum' => 'integer',
-
'Bignum' => 'integer',
-
'BigDecimal' => 'decimal',
-
'Float' => 'float',
-
'TrueClass' => 'boolean',
-
'FalseClass' => 'boolean',
-
'Date' => 'date',
-
'DateTime' => 'datetime',
-
'Time' => 'datetime',
-
'Array' => 'array',
-
'Hash' => 'hash'
-
1
} unless defined?(TYPE_NAMES)
-
-
1
DISALLOWED_XML_TYPES = %w(symbol yaml)
-
-
1
DEFAULT_OPTIONS = {
-
:typecast_xml_value => true,
-
:disallowed_types => DISALLOWED_XML_TYPES,
-
:symbolize_keys => false
-
}
-
-
1
class << self
-
# Get the current parser class.
-
1
def parser
-
return @parser if defined?(@parser)
-
self.parser = self.default_parser
-
@parser
-
end
-
-
# The default parser based on what you currently
-
# have loaded and installed. First checks to see
-
# if any parsers are already loaded, then checks
-
# to see which are installed if none are loaded.
-
1
def default_parser
-
return :ox if defined?(::Ox)
-
return :libxml if defined?(::LibXML)
-
return :nokogiri if defined?(::Nokogiri)
-
-
REQUIREMENT_MAP.each do |library, parser|
-
begin
-
require library
-
return parser
-
rescue LoadError
-
next
-
end
-
end
-
end
-
-
# Set the XML parser utilizing a symbol, string, or class.
-
# Supported by default are:
-
#
-
# * <tt>:libxml</tt>
-
# * <tt>:nokogiri</tt>
-
# * <tt>:ox</tt>
-
# * <tt>:rexml</tt>
-
1
def parser=(new_parser)
-
case new_parser
-
when String, Symbol
-
require "multi_xml/parsers/#{new_parser.to_s.downcase}"
-
@parser = MultiXml::Parsers.const_get("#{new_parser.to_s.split('_').map{|s| s.capitalize}.join('')}")
-
when Class, Module
-
@parser = new_parser
-
else
-
raise "Did not recognize your parser specification. Please specify either a symbol or a class."
-
end
-
end
-
-
# Parse an XML string or IO into Ruby.
-
#
-
# <b>Options</b>
-
#
-
# <tt>:symbolize_keys</tt> :: If true, will use symbols instead of strings for the keys.
-
#
-
# <tt>:disallowed_types</tt> :: Types to disallow from being typecasted. Defaults to `['yaml', 'symbol']`. Use `[]` to allow all types.
-
#
-
# <tt>:typecast_xml_value</tt> :: If true, won't typecast values for parsed document
-
1
def parse(xml, options={})
-
xml ||= ''
-
-
options = DEFAULT_OPTIONS.merge(options)
-
-
xml.strip! if xml.respond_to?(:strip!)
-
begin
-
xml = StringIO.new(xml) unless xml.respond_to?(:read)
-
-
char = xml.getc
-
return {} if char.nil?
-
xml.ungetc(char)
-
-
hash = undasherize_keys(parser.parse(xml) || {})
-
hash = options[:typecast_xml_value] ? typecast_xml_value(hash, options[:disallowed_types]) : hash
-
rescue DisallowedTypeError
-
raise
-
rescue parser.parse_error => error
-
raise ParseError, error.message, error.backtrace
-
end
-
hash = symbolize_keys(hash) if options[:symbolize_keys]
-
hash
-
end
-
-
# This module decorates files with the <tt>original_filename</tt>
-
# and <tt>content_type</tt> methods.
-
1
module FileLike #:nodoc:
-
1
attr_writer :original_filename, :content_type
-
-
1
def original_filename
-
@original_filename || 'untitled'
-
end
-
-
1
def content_type
-
@content_type || 'application/octet-stream'
-
end
-
end
-
-
1
private
-
-
# TODO: Add support for other encodings
-
1
def parse_binary(binary, entity) #:nodoc:
-
case entity['encoding']
-
when 'base64'
-
Base64.decode64(binary)
-
else
-
binary
-
end
-
end
-
-
1
def parse_file(file, entity)
-
f = StringIO.new(Base64.decode64(file))
-
f.extend(FileLike)
-
f.original_filename = entity['name']
-
f.content_type = entity['content_type']
-
f
-
end
-
-
1
def symbolize_keys(params)
-
case params
-
when Hash
-
params.inject({}) do |result, (key, value)|
-
result.merge(key.to_sym => symbolize_keys(value))
-
end
-
when Array
-
params.map{|value| symbolize_keys(value)}
-
else
-
params
-
end
-
end
-
-
1
def undasherize_keys(params)
-
case params
-
when Hash
-
params.inject({}) do |hash, (key, value)|
-
hash[key.to_s.tr('-', '_')] = undasherize_keys(value)
-
hash
-
end
-
when Array
-
params.map{|value| undasherize_keys(value)}
-
else
-
params
-
end
-
end
-
-
1
def typecast_xml_value(value, disallowed_types=nil)
-
disallowed_types ||= DISALLOWED_XML_TYPES
-
-
case value
-
when Hash
-
if value.include?('type') && !value['type'].is_a?(Hash) && disallowed_types.include?(value['type'])
-
raise DisallowedTypeError, value['type']
-
end
-
-
if value['type'] == 'array'
-
-
# this commented-out suggestion helps to avoid the multiple attribute
-
# problem, but it breaks when there is only one item in the array.
-
#
-
# from: https://github.com/jnunemaker/httparty/issues/102
-
#
-
# _, entries = value.detect { |k, v| k != 'type' && v.is_a?(Array) }
-
-
# This attempt fails to consider the order that the detect method
-
# retrieves the entries.
-
#_, entries = value.detect {|key, _| key != 'type'}
-
-
# This approach ignores attribute entries that are not convertable
-
# to an Array which allows attributes to be ignored.
-
_, entries = value.detect {|k, v| k != 'type' && (v.is_a?(Array) || v.is_a?(Hash)) }
-
-
if entries.nil? || (entries.is_a?(String) && entries.strip.empty?)
-
[]
-
else
-
case entries
-
when Array
-
entries.map {|entry| typecast_xml_value(entry, disallowed_types)}
-
when Hash
-
[typecast_xml_value(entries, disallowed_types)]
-
else
-
raise "can't typecast #{entries.class.name}: #{entries.inspect}"
-
end
-
end
-
elsif value.has_key?(CONTENT_ROOT)
-
content = value[CONTENT_ROOT]
-
if block = PARSING[value['type']]
-
if block.arity == 1
-
value.delete('type') if PARSING[value['type']]
-
if value.keys.size > 1
-
value[CONTENT_ROOT] = block.call(content)
-
value
-
else
-
block.call(content)
-
end
-
else
-
block.call(content, value)
-
end
-
else
-
value.keys.size > 1 ? value : content
-
end
-
elsif value['type'] == 'string' && value['nil'] != 'true'
-
''
-
# blank or nil parsed values are represented by nil
-
elsif value.empty? || value['nil'] == 'true'
-
nil
-
# If the type is the only element which makes it then
-
# this still makes the value nil, except if type is
-
# a XML node(where type['value'] is a Hash)
-
elsif value['type'] && value.size == 1 && !value['type'].is_a?(Hash)
-
nil
-
else
-
xml_value = value.inject({}) do |hash, (k, v)|
-
hash[k] = typecast_xml_value(v, disallowed_types)
-
hash
-
end
-
-
# Turn {:files => {:file => #<StringIO>} into {:files => #<StringIO>} so it is compatible with
-
# how multipart uploaded files from HTML appear
-
xml_value['file'].is_a?(StringIO) ? xml_value['file'] : xml_value
-
end
-
when Array
-
value.map!{|i| typecast_xml_value(i, disallowed_types)}
-
value.length > 1 ? value : value.first
-
when String
-
value
-
else
-
raise "can't typecast #{value.class.name}: #{value.inspect}"
-
end
-
end
-
end
-
-
end
-
#--
-
# Copyright (c) 2007-2012 Nick Sieger.
-
# See the file README.txt included with the distribution for
-
# software license details.
-
#++
-
-
# Concatenate together multiple IO objects into a single, composite IO object
-
# for purposes of reading as a single stream.
-
#
-
# Usage:
-
#
-
# crio = CompositeReadIO.new(StringIO.new('one'), StringIO.new('two'), StringIO.new('three'))
-
# puts crio.read # => "onetwothree"
-
#
-
1
class CompositeReadIO
-
# Create a new composite-read IO from the arguments, all of which should
-
# respond to #read in a manner consistent with IO.
-
1
def initialize(*ios)
-
@ios = ios.flatten
-
@index = 0
-
end
-
-
# Read from IOs in order until `length` bytes have been received.
-
1
def read(length = nil, outbuf = nil)
-
got_result = false
-
outbuf = outbuf ? outbuf.replace("") : ""
-
-
while io = current_io
-
if result = io.read(length)
-
got_result ||= !result.nil?
-
result.force_encoding("BINARY") if result.respond_to?(:force_encoding)
-
outbuf << result
-
length -= result.length if length
-
break if length == 0
-
end
-
advance_io
-
end
-
(!got_result && length) ? nil : outbuf
-
end
-
-
1
def rewind
-
@ios.each { |io| io.rewind }
-
@index = 0
-
end
-
-
1
private
-
-
1
def current_io
-
@ios[@index]
-
end
-
-
1
def advance_io
-
@index += 1
-
end
-
end
-
-
# Convenience methods for dealing with files and IO that are to be uploaded.
-
1
class UploadIO
-
# Create an upload IO suitable for including in the params hash of a
-
# Net::HTTP::Post::Multipart.
-
#
-
# Can take two forms. The first accepts a filename and content type, and
-
# opens the file for reading (to be closed by finalizer).
-
#
-
# The second accepts an already-open IO, but also requires a third argument,
-
# the filename from which it was opened (particularly useful/recommended if
-
# uploading directly from a form in a framework, which often save the file to
-
# an arbitrarily named RackMultipart file in /tmp).
-
#
-
# Usage:
-
#
-
# UploadIO.new("file.txt", "text/plain")
-
# UploadIO.new(file_io, "text/plain", "file.txt")
-
#
-
1
attr_reader :content_type, :original_filename, :local_path, :io, :opts
-
-
1
def initialize(filename_or_io, content_type, filename = nil, opts = {})
-
io = filename_or_io
-
local_path = ""
-
if io.respond_to? :read
-
# in Ruby 1.9.2, StringIOs no longer respond to path
-
# (since they respond to :length, so we don't need their local path, see parts.rb:41)
-
local_path = filename_or_io.respond_to?(:path) ? filename_or_io.path : "local.path"
-
else
-
io = File.open(filename_or_io)
-
local_path = filename_or_io
-
end
-
filename ||= local_path
-
-
@content_type = content_type
-
@original_filename = File.basename(filename)
-
@local_path = local_path
-
@io = io
-
@opts = opts
-
end
-
-
1
def self.convert!(io, content_type, original_filename, local_path)
-
raise ArgumentError, "convert! has been removed. You must now wrap IOs using:\nUploadIO.new(filename_or_io, content_type, filename=nil)\nPlease update your code."
-
end
-
-
1
def method_missing(*args)
-
@io.send(*args)
-
end
-
-
1
def respond_to?(meth, include_all = false)
-
@io.respond_to?(meth, include_all) || super(meth, include_all)
-
end
-
end
-
#--
-
# Copyright (c) 2007-2013 Nick Sieger.
-
# See the file README.txt included with the distribution for
-
# software license details.
-
#++
-
-
1
module Parts
-
1
module Part #:nodoc:
-
1
def self.new(boundary, name, value, headers = {})
-
headers ||= {} # avoid nil values
-
if file?(value)
-
FilePart.new(boundary, name, value, headers)
-
else
-
ParamPart.new(boundary, name, value, headers)
-
end
-
end
-
-
1
def self.file?(value)
-
value.respond_to?(:content_type) && value.respond_to?(:original_filename)
-
end
-
-
1
def length
-
@part.length
-
end
-
-
1
def to_io
-
@io
-
end
-
end
-
-
1
class ParamPart
-
1
include Part
-
1
def initialize(boundary, name, value, headers = {})
-
@part = build_part(boundary, name, value, headers)
-
@io = StringIO.new(@part)
-
end
-
-
1
def length
-
@part.bytesize
-
end
-
-
1
def build_part(boundary, name, value, headers = {})
-
part = ''
-
part << "--#{boundary}\r\n"
-
part << "Content-Disposition: form-data; name=\"#{name.to_s}\"\r\n"
-
part << "Content-Type: #{headers["Content-Type"]}\r\n" if headers["Content-Type"]
-
part << "\r\n"
-
part << "#{value}\r\n"
-
end
-
end
-
-
# Represents a part to be filled from file IO.
-
1
class FilePart
-
1
include Part
-
1
attr_reader :length
-
1
def initialize(boundary, name, io, headers = {})
-
file_length = io.respond_to?(:length) ? io.length : File.size(io.local_path)
-
@head = build_head(boundary, name, io.original_filename, io.content_type, file_length,
-
io.respond_to?(:opts) ? io.opts.merge(headers) : headers)
-
@foot = "\r\n"
-
@length = @head.bytesize + file_length + @foot.length
-
@io = CompositeReadIO.new(StringIO.new(@head), io, StringIO.new(@foot))
-
end
-
-
1
def build_head(boundary, name, filename, type, content_len, opts = {}, headers = {})
-
trans_encoding = opts["Content-Transfer-Encoding"] || "binary"
-
content_disposition = opts["Content-Disposition"] || "form-data"
-
-
part = ''
-
part << "--#{boundary}\r\n"
-
part << "Content-Disposition: #{content_disposition}; name=\"#{name.to_s}\"; filename=\"#{filename}\"\r\n"
-
part << "Content-Length: #{content_len}\r\n"
-
if content_id = opts["Content-ID"]
-
part << "Content-ID: #{content_id}\r\n"
-
end
-
-
if headers["Content-Type"] != nil
-
part << "Content-Type: " + headers["Content-Type"] + "\r\n"
-
else
-
part << "Content-Type: #{type}\r\n"
-
end
-
-
part << "Content-Transfer-Encoding: #{trans_encoding}\r\n"
-
part << "\r\n"
-
end
-
end
-
-
# Represents the epilogue or closing boundary.
-
1
class EpiloguePart
-
1
include Part
-
1
def initialize(boundary)
-
@part = "--#{boundary}--\r\n\r\n"
-
@io = StringIO.new(@part)
-
end
-
end
-
end
-
# -*- coding: utf-8 -*-
-
# Modify the PATH on windows so that the external DLLs will get loaded.
-
-
1
require 'rbconfig'
-
-
1
if defined?(RUBY_ENGINE) && RUBY_ENGINE == "jruby"
-
# The line below caused a problem on non-GAE rack environment.
-
# unless defined?(JRuby::Rack::VERSION) || defined?(AppEngine::ApiProxy)
-
#
-
# However, simply cutting defined?(JRuby::Rack::VERSION) off resulted in
-
# an unable-to-load-nokogiri problem. Thus, now, Nokogiri checks the presense
-
# of appengine-rack.jar in $LOAD_PATH. If Nokogiri is on GAE, Nokogiri
-
# should skip loading xml jars. This is because those are in WEB-INF/lib and
-
# already set in the classpath.
-
unless $LOAD_PATH.to_s.include?("appengine-rack")
-
require 'stringio'
-
require 'isorelax.jar'
-
require 'jing.jar'
-
require 'nekohtml.jar'
-
require 'nekodtd.jar'
-
require 'xercesImpl.jar'
-
end
-
end
-
-
1
begin
-
1
RUBY_VERSION =~ /(\d+.\d+)/
-
1
require "nokogiri/#{$1}/nokogiri"
-
rescue LoadError
-
1
require 'nokogiri/nokogiri'
-
end
-
1
require 'nokogiri/version'
-
1
require 'nokogiri/syntax_error'
-
1
require 'nokogiri/xml'
-
1
require 'nokogiri/xslt'
-
1
require 'nokogiri/html'
-
1
require 'nokogiri/decorators/slop'
-
1
require 'nokogiri/css'
-
1
require 'nokogiri/html/builder'
-
-
# Nokogiri parses and searches XML/HTML very quickly, and also has
-
# correctly implemented CSS3 selector support as well as XPath 1.0
-
# support.
-
#
-
# Parsing a document returns either a Nokogiri::XML::Document, or a
-
# Nokogiri::HTML::Document depending on the kind of document you parse.
-
#
-
# Here is an example:
-
#
-
# require 'nokogiri'
-
# require 'open-uri'
-
#
-
# # Get a Nokogiri::HTML:Document for the page we’re interested in...
-
#
-
# doc = Nokogiri::HTML(open('http://www.google.com/search?q=tenderlove'))
-
#
-
# # Do funky things with it using Nokogiri::XML::Node methods...
-
#
-
# ####
-
# # Search for nodes by css
-
# doc.css('h3.r a.l').each do |link|
-
# puts link.content
-
# end
-
#
-
# See Nokogiri::XML::Node#css for more information about CSS searching.
-
# See Nokogiri::XML::Node#xpath for more information about XPath searching.
-
1
module Nokogiri
-
1
class << self
-
###
-
# Parse an HTML or XML document. +string+ contains the document.
-
1
def parse string, url = nil, encoding = nil, options = nil
-
if string.respond_to?(:read) ||
-
/^\s*<(?:!DOCTYPE\s+)?html[\s>]/i === string[0, 512]
-
# Expect an HTML indicator to appear within the first 512
-
# characters of a document. (<?xml ?> + <?xml-stylesheet ?>
-
# shouldn't be that long)
-
Nokogiri.HTML(string, url, encoding,
-
options || XML::ParseOptions::DEFAULT_HTML)
-
else
-
Nokogiri.XML(string, url, encoding,
-
options || XML::ParseOptions::DEFAULT_XML)
-
end.tap { |doc|
-
yield doc if block_given?
-
}
-
end
-
-
###
-
# Create a new Nokogiri::XML::DocumentFragment
-
1
def make input = nil, opts = {}, &blk
-
if input
-
Nokogiri::HTML.fragment(input).children.first
-
else
-
Nokogiri(&blk)
-
end
-
end
-
-
###
-
# Parse a document and add the Slop decorator. The Slop decorator
-
# implements method_missing such that methods may be used instead of CSS
-
# or XPath. For example:
-
#
-
# doc = Nokogiri::Slop(<<-eohtml)
-
# <html>
-
# <body>
-
# <p>first</p>
-
# <p>second</p>
-
# </body>
-
# </html>
-
# eohtml
-
# assert_equal('second', doc.html.body.p[1].text)
-
#
-
1
def Slop(*args, &block)
-
Nokogiri(*args, &block).slop!
-
end
-
end
-
-
# Make sure to support some popular encoding aliases not known by
-
# all iconv implementations.
-
{
-
'Windows-31J' => 'CP932', # Windows-31J is the IANA registered name of CP932.
-
1
}.each { |alias_name, name|
-
1
EncodingHandler.alias(name, alias_name) if EncodingHandler[alias_name].nil?
-
}
-
end
-
-
###
-
# Parser a document contained in +args+. Nokogiri will try to guess what
-
# type of document you are attempting to parse. For more information, see
-
# Nokogiri.parse
-
#
-
# To specify the type of document, use Nokogiri.XML or Nokogiri.HTML.
-
1
def Nokogiri(*args, &block)
-
if block_given?
-
Nokogiri::HTML::Builder.new(&block).doc.root
-
else
-
Nokogiri.parse(*args)
-
end
-
end
-
1
require 'nokogiri/css/node'
-
1
require 'nokogiri/css/xpath_visitor'
-
1
x = $-w
-
1
$-w = false
-
1
require 'nokogiri/css/parser'
-
1
$-w = x
-
-
1
require 'nokogiri/css/tokenizer'
-
1
require 'nokogiri/css/syntax_error'
-
-
1
module Nokogiri
-
1
module CSS
-
1
class << self
-
###
-
# Parse this CSS selector in +selector+. Returns an AST.
-
1
def parse selector
-
Parser.new.parse selector
-
end
-
-
###
-
# Get the XPath for +selector+.
-
1
def xpath_for selector, options={}
-
Parser.new(options[:ns] || {}).xpath_for selector, options
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module CSS
-
1
class Node
-
1
ALLOW_COMBINATOR_ON_SELF = [:DIRECT_ADJACENT_SELECTOR, :FOLLOWING_SELECTOR, :CHILD_SELECTOR]
-
-
# Get the type of this node
-
1
attr_accessor :type
-
# Get the value of this node
-
1
attr_accessor :value
-
-
# Create a new Node with +type+ and +value+
-
1
def initialize type, value
-
@type = type
-
@value = value
-
end
-
-
# Accept +visitor+
-
1
def accept visitor
-
visitor.send(:"visit_#{type.to_s.downcase}", self)
-
end
-
-
###
-
# Convert this CSS node to xpath with +prefix+ using +visitor+
-
1
def to_xpath prefix = '//', visitor = XPathVisitor.new
-
prefix = '.' if ALLOW_COMBINATOR_ON_SELF.include?(type) && value.first.nil?
-
prefix + visitor.accept(self)
-
end
-
-
# Find a node by type using +types+
-
1
def find_by_type types
-
matches = []
-
matches << self if to_type == types
-
@value.each do |v|
-
matches += v.find_by_type(types) if v.respond_to?(:find_by_type)
-
end
-
matches
-
end
-
-
# Convert to_type
-
1
def to_type
-
[@type] + @value.map { |n|
-
n.to_type if n.respond_to?(:to_type)
-
}.compact
-
end
-
-
# Convert to array
-
1
def to_a
-
[@type] + @value.map { |n| n.respond_to?(:to_a) ? n.to_a : [n] }
-
end
-
end
-
end
-
end
-
#
-
# DO NOT MODIFY!!!!
-
# This file is automatically generated by Racc 1.4.9
-
# from Racc grammer file "".
-
#
-
-
1
require 'racc/parser.rb'
-
-
-
1
require 'nokogiri/css/parser_extras'
-
1
module Nokogiri
-
1
module CSS
-
1
class Parser < Racc::Parser
-
##### State transition tables begin ###
-
-
1
racc_action_table = [
-
21, 31, 20, 32, 30, 4, 5, 7, 24, 19,
-
92, 21, 90, 32, 95, 6, 91, 9, 8, 22,
-
62, 90, 20, 85, 23, 15, 17, 21, 24, -23,
-
47, 30, 4, 5, 7, 23, 19, 52, 84, 21,
-
56, 89, 6, 30, 9, 8, 22, 83, 19, 20,
-
21, 23, 15, 17, 30, 24, 98, 97, 22, 19,
-
68, 20, 21, 23, 15, 17, 30, 24, 86, 22,
-
87, 19, 20, 93, 23, 15, 17, 30, 24, 30,
-
82, 22, 52, 84, 20, 56, 23, 15, 17, -23,
-
24, 30, 35, 30, 35, 20, 19, 20, 15, 96,
-
15, 24, -23, 24, 30, 99, 35, 21, 35, 20,
-
100, 20, 15, 17, 15, 24, 42, 24, 45, 35,
-
30, 30, 20, 88, 87, 15, 47, 90, 24, 71,
-
102, 23, 30, 40, 41, 35, 35, 105, 20, 20,
-
106, 15, 15, nil, 24, 24, nil, 35, nil, nil,
-
20, 79, 80, 15, 30, nil, 24, nil, 52, 54,
-
nil, 56, 75, 76, 77, nil, 78, nil, nil, 35,
-
74, nil, 20, 79, 80, 15, 17, nil, 24, nil,
-
52, 53, nil, 51, 75, 76, 77, nil, 78, 4,
-
5, 7, 74, 48, 52, 84, nil, 56, nil, 6,
-
nil, 9, 8, 52, 84, nil, 56 ]
-
-
1
racc_action_check = [
-
0, 1, 11, 60, 0, 0, 0, 0, 11, 0,
-
55, 24, 54, 1, 60, 0, 53, 0, 0, 0,
-
24, 53, 0, 49, 0, 0, 0, 23, 0, 54,
-
24, 23, 23, 23, 23, 24, 23, 89, 89, 13,
-
89, 52, 23, 13, 23, 23, 23, 46, 13, 23,
-
39, 23, 23, 23, 39, 23, 73, 73, 13, 39,
-
31, 13, 32, 13, 13, 13, 32, 13, 50, 39,
-
56, 32, 39, 57, 39, 39, 39, 27, 39, 28,
-
45, 32, 47, 47, 32, 47, 32, 32, 32, 19,
-
32, 35, 27, 10, 28, 27, 35, 28, 27, 72,
-
28, 27, 42, 28, 29, 81, 35, 20, 10, 35,
-
83, 10, 35, 35, 10, 35, 20, 10, 20, 29,
-
26, 58, 29, 51, 51, 29, 20, 84, 29, 41,
-
88, 20, 25, 15, 18, 26, 58, 91, 26, 58,
-
102, 26, 58, nil, 26, 58, nil, 25, nil, nil,
-
25, 43, 43, 25, 22, nil, 25, nil, 22, 22,
-
nil, 22, 43, 43, 43, nil, 43, nil, nil, 22,
-
43, nil, 22, 44, 44, 22, 22, nil, 22, nil,
-
21, 21, nil, 21, 44, 44, 44, nil, 44, 14,
-
14, 14, 44, 21, 87, 87, nil, 87, nil, 14,
-
nil, 14, 14, 90, 90, nil, 90 ]
-
-
1
racc_action_pointer = [
-
-2, 1, nil, nil, nil, nil, nil, nil, nil, nil,
-
87, -22, nil, 37, 182, 122, nil, nil, 105, 60,
-
105, 170, 148, 25, 9, 126, 114, 71, 73, 98,
-
nil, 60, 60, nil, nil, 85, nil, nil, nil, 48,
-
nil, 118, 73, 148, 170, 55, 18, 72, nil, 0,
-
45, 112, 29, 9, 0, -13, 58, 50, 115, nil,
-
-9, nil, nil, nil, nil, nil, nil, nil, nil, nil,
-
nil, nil, 74, 46, nil, nil, nil, nil, nil, nil,
-
nil, 80, nil, 99, 115, nil, nil, 184, 123, 27,
-
193, 124, nil, nil, nil, nil, nil, nil, nil, nil,
-
nil, nil, 127, nil, nil, nil, nil ]
-
-
1
racc_action_default = [
-
-24, -73, -2, -3, -4, -5, -6, -7, -8, -9,
-
-47, -11, -14, -24, -17, -73, -19, -20, -73, -22,
-
-24, -73, -24, -24, -73, -53, -54, -55, -56, -57,
-
-58, -73, -24, -10, -46, -24, -12, -13, -15, -24,
-
-18, -73, -29, -61, -61, -73, -73, -73, -30, -73,
-
-73, -38, -39, -40, -22, -73, -38, -73, -70, -72,
-
-73, -44, -45, -48, -49, -50, -51, -52, 107, -1,
-
-16, -21, -73, -73, -62, -63, -64, -65, -66, -67,
-
-68, -73, -27, -73, -40, -31, -32, -73, -43, -73,
-
-73, -73, -33, -69, -71, -34, -25, -59, -60, -26,
-
-28, -35, -73, -36, -37, -42, -41 ]
-
-
1
racc_goto_table = [
-
49, 34, 38, 44, 1, 72, 81, 61, 37, 58,
-
36, 46, 43, 59, 33, 39, 63, 64, 65, 66,
-
67, 69, 58, 50, nil, nil, 59, 60, 70, nil,
-
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
-
nil, nil, nil, nil, nil, nil, nil, nil, nil, 94,
-
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
-
nil, nil, nil, nil, nil, nil, 101, nil, 103, 104 ]
-
-
1
racc_goto_check = [
-
17, 11, 2, 8, 1, 16, 16, 8, 10, 6,
-
9, 14, 15, 11, 7, 4, 11, 11, 11, 11,
-
11, 2, 6, 18, nil, nil, 11, 1, 2, nil,
-
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
-
nil, nil, nil, nil, nil, nil, nil, nil, nil, 11,
-
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
-
nil, nil, nil, nil, nil, nil, 17, nil, 17, 17 ]
-
-
1
racc_goto_pointer = [
-
nil, 4, -11, nil, 1, nil, -13, 4, -17, -1,
-
-3, -9, nil, nil, -9, -8, -38, -21, 2, nil,
-
nil, nil, nil ]
-
-
1
racc_goto_default = [
-
nil, nil, 3, 2, 13, 14, 10, nil, 11, 28,
-
27, 12, 26, 16, 18, nil, nil, 55, nil, 25,
-
29, 73, 57 ]
-
-
1
racc_reduce_table = [
-
0, 0, :racc_error,
-
3, 32, :_reduce_1,
-
1, 32, :_reduce_2,
-
1, 32, :_reduce_3,
-
1, 35, :_reduce_4,
-
1, 35, :_reduce_5,
-
1, 35, :_reduce_6,
-
1, 35, :_reduce_7,
-
1, 35, :_reduce_8,
-
1, 35, :_reduce_9,
-
2, 36, :_reduce_10,
-
1, 36, :_reduce_none,
-
2, 36, :_reduce_12,
-
2, 36, :_reduce_13,
-
1, 36, :_reduce_14,
-
2, 34, :_reduce_15,
-
3, 33, :_reduce_16,
-
1, 33, :_reduce_none,
-
2, 43, :_reduce_18,
-
1, 37, :_reduce_none,
-
1, 37, :_reduce_20,
-
3, 44, :_reduce_21,
-
1, 44, :_reduce_22,
-
1, 45, :_reduce_23,
-
0, 45, :_reduce_none,
-
4, 41, :_reduce_25,
-
4, 41, :_reduce_26,
-
3, 41, :_reduce_27,
-
3, 46, :_reduce_28,
-
1, 46, :_reduce_29,
-
2, 39, :_reduce_30,
-
3, 39, :_reduce_31,
-
3, 39, :_reduce_32,
-
3, 39, :_reduce_33,
-
3, 39, :_reduce_34,
-
3, 48, :_reduce_35,
-
3, 48, :_reduce_36,
-
3, 48, :_reduce_37,
-
1, 48, :_reduce_none,
-
1, 48, :_reduce_none,
-
1, 48, :_reduce_40,
-
4, 49, :_reduce_41,
-
3, 49, :_reduce_42,
-
2, 49, :_reduce_43,
-
2, 40, :_reduce_44,
-
2, 40, :_reduce_45,
-
1, 38, :_reduce_none,
-
0, 38, :_reduce_none,
-
2, 42, :_reduce_48,
-
2, 42, :_reduce_49,
-
2, 42, :_reduce_50,
-
2, 42, :_reduce_51,
-
2, 42, :_reduce_52,
-
1, 42, :_reduce_none,
-
1, 42, :_reduce_none,
-
1, 42, :_reduce_none,
-
1, 42, :_reduce_none,
-
1, 42, :_reduce_none,
-
1, 50, :_reduce_58,
-
2, 47, :_reduce_59,
-
2, 47, :_reduce_60,
-
0, 47, :_reduce_none,
-
1, 52, :_reduce_62,
-
1, 52, :_reduce_63,
-
1, 52, :_reduce_64,
-
1, 52, :_reduce_65,
-
1, 52, :_reduce_66,
-
1, 52, :_reduce_67,
-
1, 52, :_reduce_68,
-
3, 51, :_reduce_69,
-
1, 53, :_reduce_none,
-
2, 53, :_reduce_none,
-
1, 53, :_reduce_none ]
-
-
1
racc_reduce_n = 73
-
-
1
racc_shift_n = 107
-
-
1
racc_token_table = {
-
false => 0,
-
:error => 1,
-
:FUNCTION => 2,
-
:INCLUDES => 3,
-
:DASHMATCH => 4,
-
:LBRACE => 5,
-
:HASH => 6,
-
:PLUS => 7,
-
:GREATER => 8,
-
:S => 9,
-
:STRING => 10,
-
:IDENT => 11,
-
:COMMA => 12,
-
:NUMBER => 13,
-
:PREFIXMATCH => 14,
-
:SUFFIXMATCH => 15,
-
:SUBSTRINGMATCH => 16,
-
:TILDE => 17,
-
:NOT_EQUAL => 18,
-
:SLASH => 19,
-
:DOUBLESLASH => 20,
-
:NOT => 21,
-
:EQUAL => 22,
-
:RPAREN => 23,
-
:LSQUARE => 24,
-
:RSQUARE => 25,
-
:HAS => 26,
-
"." => 27,
-
"*" => 28,
-
"|" => 29,
-
":" => 30 }
-
-
1
racc_nt_base = 31
-
-
1
racc_use_result_var = true
-
-
1
Racc_arg = [
-
racc_action_table,
-
racc_action_check,
-
racc_action_default,
-
racc_action_pointer,
-
racc_goto_table,
-
racc_goto_check,
-
racc_goto_default,
-
racc_goto_pointer,
-
racc_nt_base,
-
racc_reduce_table,
-
racc_token_table,
-
racc_shift_n,
-
racc_reduce_n,
-
racc_use_result_var ]
-
-
1
Racc_token_to_s_table = [
-
"$end",
-
"error",
-
"FUNCTION",
-
"INCLUDES",
-
"DASHMATCH",
-
"LBRACE",
-
"HASH",
-
"PLUS",
-
"GREATER",
-
"S",
-
"STRING",
-
"IDENT",
-
"COMMA",
-
"NUMBER",
-
"PREFIXMATCH",
-
"SUFFIXMATCH",
-
"SUBSTRINGMATCH",
-
"TILDE",
-
"NOT_EQUAL",
-
"SLASH",
-
"DOUBLESLASH",
-
"NOT",
-
"EQUAL",
-
"RPAREN",
-
"LSQUARE",
-
"RSQUARE",
-
"HAS",
-
"\".\"",
-
"\"*\"",
-
"\"|\"",
-
"\":\"",
-
"$start",
-
"selector",
-
"simple_selector_1toN",
-
"prefixless_combinator_selector",
-
"combinator",
-
"simple_selector",
-
"element_name",
-
"hcap_0toN",
-
"function",
-
"pseudo",
-
"attrib",
-
"hcap_1toN",
-
"class",
-
"namespaced_ident",
-
"namespace",
-
"attrib_name",
-
"attrib_val_0or1",
-
"expr",
-
"nth",
-
"attribute_id",
-
"negation",
-
"eql_incl_dash",
-
"negation_arg" ]
-
-
1
Racc_debug_parser = false
-
-
##### State transition tables end #####
-
-
# reduce 0 omitted
-
-
1
def _reduce_1(val, _values, result)
-
result = [val.first, val.last].flatten
-
-
result
-
end
-
-
1
def _reduce_2(val, _values, result)
-
result = val.flatten
-
result
-
end
-
-
1
def _reduce_3(val, _values, result)
-
result = val.flatten
-
result
-
end
-
-
1
def _reduce_4(val, _values, result)
-
result = :DIRECT_ADJACENT_SELECTOR
-
result
-
end
-
-
1
def _reduce_5(val, _values, result)
-
result = :CHILD_SELECTOR
-
result
-
end
-
-
1
def _reduce_6(val, _values, result)
-
result = :FOLLOWING_SELECTOR
-
result
-
end
-
-
1
def _reduce_7(val, _values, result)
-
result = :DESCENDANT_SELECTOR
-
result
-
end
-
-
1
def _reduce_8(val, _values, result)
-
result = :DESCENDANT_SELECTOR
-
result
-
end
-
-
1
def _reduce_9(val, _values, result)
-
result = :CHILD_SELECTOR
-
result
-
end
-
-
1
def _reduce_10(val, _values, result)
-
result = if val[1].nil?
-
val.first
-
else
-
Node.new(:CONDITIONAL_SELECTOR, [val.first, val[1]])
-
end
-
-
result
-
end
-
-
# reduce 11 omitted
-
-
1
def _reduce_12(val, _values, result)
-
result = Node.new(:CONDITIONAL_SELECTOR, val)
-
-
result
-
end
-
-
1
def _reduce_13(val, _values, result)
-
result = Node.new(:CONDITIONAL_SELECTOR, val)
-
-
result
-
end
-
-
1
def _reduce_14(val, _values, result)
-
result = Node.new(:CONDITIONAL_SELECTOR,
-
[Node.new(:ELEMENT_NAME, ['*']), val.first]
-
)
-
-
result
-
end
-
-
1
def _reduce_15(val, _values, result)
-
result = Node.new(val.first, [nil, val.last])
-
-
result
-
end
-
-
1
def _reduce_16(val, _values, result)
-
result = Node.new(val[1], [val.first, val.last])
-
-
result
-
end
-
-
# reduce 17 omitted
-
-
1
def _reduce_18(val, _values, result)
-
result = Node.new(:CLASS_CONDITION, [val[1]])
-
result
-
end
-
-
# reduce 19 omitted
-
-
1
def _reduce_20(val, _values, result)
-
result = Node.new(:ELEMENT_NAME, val)
-
result
-
end
-
-
1
def _reduce_21(val, _values, result)
-
result = Node.new(:ELEMENT_NAME,
-
[[val.first, val.last].compact.join(':')]
-
)
-
-
result
-
end
-
-
1
def _reduce_22(val, _values, result)
-
name = @namespaces.key?('xmlns') ? "xmlns:#{val.first}" : val.first
-
result = Node.new(:ELEMENT_NAME, [name])
-
-
result
-
end
-
-
1
def _reduce_23(val, _values, result)
-
result = val[0]
-
result
-
end
-
-
# reduce 24 omitted
-
-
1
def _reduce_25(val, _values, result)
-
result = Node.new(:ATTRIBUTE_CONDITION,
-
[val[1]] + (val[2] || [])
-
)
-
-
result
-
end
-
-
1
def _reduce_26(val, _values, result)
-
result = Node.new(:ATTRIBUTE_CONDITION,
-
[val[1]] + (val[2] || [])
-
)
-
-
result
-
end
-
-
1
def _reduce_27(val, _values, result)
-
# Non standard, but hpricot supports it.
-
result = Node.new(:PSEUDO_CLASS,
-
[Node.new(:FUNCTION, ['nth-child(', val[1]])]
-
)
-
-
result
-
end
-
-
1
def _reduce_28(val, _values, result)
-
result = Node.new(:ELEMENT_NAME,
-
[[val.first, val.last].compact.join(':')]
-
)
-
-
result
-
end
-
-
1
def _reduce_29(val, _values, result)
-
# Default namespace is not applied to attributes.
-
# So we don't add prefix "xmlns:" as in namespaced_ident.
-
result = Node.new(:ELEMENT_NAME, [val.first])
-
-
result
-
end
-
-
1
def _reduce_30(val, _values, result)
-
result = Node.new(:FUNCTION, [val.first.strip])
-
-
result
-
end
-
-
1
def _reduce_31(val, _values, result)
-
result = Node.new(:FUNCTION, [val.first.strip, val[1]].flatten)
-
-
result
-
end
-
-
1
def _reduce_32(val, _values, result)
-
result = Node.new(:FUNCTION, [val.first.strip, val[1]].flatten)
-
-
result
-
end
-
-
1
def _reduce_33(val, _values, result)
-
result = Node.new(:FUNCTION, [val.first.strip, val[1]].flatten)
-
-
result
-
end
-
-
1
def _reduce_34(val, _values, result)
-
result = Node.new(:FUNCTION, [val.first.strip, val[1]].flatten)
-
-
result
-
end
-
-
1
def _reduce_35(val, _values, result)
-
result = [val.first, val.last]
-
result
-
end
-
-
1
def _reduce_36(val, _values, result)
-
result = [val.first, val.last]
-
result
-
end
-
-
1
def _reduce_37(val, _values, result)
-
result = [val.first, val.last]
-
result
-
end
-
-
# reduce 38 omitted
-
-
# reduce 39 omitted
-
-
1
def _reduce_40(val, _values, result)
-
if val[0] == 'even'
-
val = ["2","n","+","0"]
-
result = Node.new(:NTH, val)
-
elsif val[0] == 'odd'
-
val = ["2","n","+","1"]
-
result = Node.new(:NTH, val)
-
else
-
# This is not CSS standard. It allows us to support this:
-
# assert_xpath("//a[foo(., @href)]", @parser.parse('a:foo(@href)'))
-
# assert_xpath("//a[foo(., @a, b)]", @parser.parse('a:foo(@a, b)'))
-
# assert_xpath("//a[foo(., a, 10)]", @parser.parse('a:foo(a, 10)'))
-
result = val
-
end
-
-
result
-
end
-
-
1
def _reduce_41(val, _values, result)
-
if val[1] == 'n'
-
result = Node.new(:NTH, val)
-
else
-
raise Racc::ParseError, "parse error on IDENT '#{val[1]}'"
-
end
-
-
result
-
end
-
-
1
def _reduce_42(val, _values, result)
-
# n+3, -n+3
-
if val[0] == 'n'
-
val.unshift("1")
-
result = Node.new(:NTH, val)
-
elsif val[0] == '-n'
-
val[0] = 'n'
-
val.unshift("-1")
-
result = Node.new(:NTH, val)
-
else
-
raise Racc::ParseError, "parse error on IDENT '#{val[1]}'"
-
end
-
-
result
-
end
-
-
1
def _reduce_43(val, _values, result)
-
# 5n, -5n, 10n-1
-
n = val[1]
-
if n[0, 2] == 'n-'
-
val[1] = 'n'
-
val << "-"
-
# b is contained in n as n is the string "n-b"
-
val << n[2, n.size]
-
result = Node.new(:NTH, val)
-
elsif n == 'n'
-
val << "+"
-
val << "0"
-
result = Node.new(:NTH, val)
-
else
-
raise Racc::ParseError, "parse error on IDENT '#{val[1]}'"
-
end
-
-
result
-
end
-
-
1
def _reduce_44(val, _values, result)
-
result = Node.new(:PSEUDO_CLASS, [val[1]])
-
-
result
-
end
-
-
1
def _reduce_45(val, _values, result)
-
result = Node.new(:PSEUDO_CLASS, [val[1]])
-
result
-
end
-
-
# reduce 46 omitted
-
-
# reduce 47 omitted
-
-
1
def _reduce_48(val, _values, result)
-
result = Node.new(:COMBINATOR, val)
-
-
result
-
end
-
-
1
def _reduce_49(val, _values, result)
-
result = Node.new(:COMBINATOR, val)
-
-
result
-
end
-
-
1
def _reduce_50(val, _values, result)
-
result = Node.new(:COMBINATOR, val)
-
-
result
-
end
-
-
1
def _reduce_51(val, _values, result)
-
result = Node.new(:COMBINATOR, val)
-
-
result
-
end
-
-
1
def _reduce_52(val, _values, result)
-
result = Node.new(:COMBINATOR, val)
-
-
result
-
end
-
-
# reduce 53 omitted
-
-
# reduce 54 omitted
-
-
# reduce 55 omitted
-
-
# reduce 56 omitted
-
-
# reduce 57 omitted
-
-
1
def _reduce_58(val, _values, result)
-
result = Node.new(:ID, val)
-
result
-
end
-
-
1
def _reduce_59(val, _values, result)
-
result = [val.first, val[1]]
-
result
-
end
-
-
1
def _reduce_60(val, _values, result)
-
result = [val.first, val[1]]
-
result
-
end
-
-
# reduce 61 omitted
-
-
1
def _reduce_62(val, _values, result)
-
result = :equal
-
result
-
end
-
-
1
def _reduce_63(val, _values, result)
-
result = :prefix_match
-
result
-
end
-
-
1
def _reduce_64(val, _values, result)
-
result = :suffix_match
-
result
-
end
-
-
1
def _reduce_65(val, _values, result)
-
result = :substring_match
-
result
-
end
-
-
1
def _reduce_66(val, _values, result)
-
result = :not_equal
-
result
-
end
-
-
1
def _reduce_67(val, _values, result)
-
result = :includes
-
result
-
end
-
-
1
def _reduce_68(val, _values, result)
-
result = :dash_match
-
result
-
end
-
-
1
def _reduce_69(val, _values, result)
-
result = Node.new(:NOT, [val[1]])
-
-
result
-
end
-
-
# reduce 70 omitted
-
-
# reduce 71 omitted
-
-
# reduce 72 omitted
-
-
1
def _reduce_none(val, _values, result)
-
val[0]
-
end
-
-
end # class Parser
-
end # module CSS
-
end # module Nokogiri
-
1
require 'thread'
-
-
1
module Nokogiri
-
1
module CSS
-
1
class Parser < Racc::Parser
-
1
@cache_on = true
-
1
@cache = {}
-
1
@mutex = Mutex.new
-
-
1
class << self
-
# Turn on CSS parse caching
-
1
attr_accessor :cache_on
-
1
alias :cache_on? :cache_on
-
1
alias :set_cache :cache_on=
-
-
# Get the css selector in +string+ from the cache
-
1
def [] string
-
return unless @cache_on
-
@mutex.synchronize { @cache[string] }
-
end
-
-
# Set the css selector in +string+ in the cache to +value+
-
1
def []= string, value
-
return value unless @cache_on
-
@mutex.synchronize { @cache[string] = value }
-
end
-
-
# Clear the cache
-
1
def clear_cache
-
@mutex.synchronize { @cache = {} }
-
end
-
-
# Execute +block+ without cache
-
1
def without_cache &block
-
tmp = @cache_on
-
@cache_on = false
-
block.call
-
@cache_on = tmp
-
end
-
-
###
-
# Parse this CSS selector in +selector+. Returns an AST.
-
1
def parse selector
-
@warned ||= false
-
unless @warned
-
$stderr.puts('Nokogiri::CSS::Parser.parse is deprecated, call Nokogiri::CSS.parse(), this will be removed August 1st or version 1.4.0 (whichever is first)')
-
@warned = true
-
end
-
new.parse selector
-
end
-
end
-
-
# Create a new CSS parser with respect to +namespaces+
-
1
def initialize namespaces = {}
-
@tokenizer = Tokenizer.new
-
@namespaces = namespaces
-
super()
-
end
-
-
1
def parse string
-
@tokenizer.scan_setup string
-
do_parse
-
end
-
-
1
def next_token
-
@tokenizer.next_token
-
end
-
-
# Get the xpath for +string+ using +options+
-
1
def xpath_for string, options={}
-
key = "#{string}#{options[:ns]}#{options[:prefix]}"
-
v = self.class[key]
-
return v if v
-
-
args = [
-
options[:prefix] || '//',
-
options[:visitor] || XPathVisitor.new
-
]
-
self.class[key] = parse(string).map { |ast|
-
ast.to_xpath(*args)
-
}
-
end
-
-
# On CSS parser error, raise an exception
-
1
def on_error error_token_id, error_value, value_stack
-
after = value_stack.compact.last
-
raise SyntaxError.new("unexpected '#{error_value}' after '#{after}'")
-
end
-
end
-
end
-
end
-
1
require 'nokogiri/syntax_error'
-
1
module Nokogiri
-
1
module CSS
-
1
class SyntaxError < ::Nokogiri::SyntaxError
-
end
-
end
-
end
-
#--
-
# DO NOT MODIFY!!!!
-
# This file is automatically generated by rex 1.0.5
-
# from lexical definition file "lib/nokogiri/css/tokenizer.rex".
-
#++
-
-
1
module Nokogiri
-
1
module CSS
-
1
class Tokenizer # :nodoc:
-
1
require 'strscan'
-
-
1
class ScanError < StandardError ; end
-
-
1
attr_reader :lineno
-
1
attr_reader :filename
-
1
attr_accessor :state
-
-
1
def scan_setup(str)
-
@ss = StringScanner.new(str)
-
@lineno = 1
-
@state = nil
-
end
-
-
1
def action
-
yield
-
end
-
-
1
def scan_str(str)
-
scan_setup(str)
-
do_parse
-
end
-
1
alias :scan :scan_str
-
-
1
def load_file( filename )
-
@filename = filename
-
open(filename, "r") do |f|
-
scan_setup(f.read)
-
end
-
end
-
-
1
def scan_file( filename )
-
load_file(filename)
-
do_parse
-
end
-
-
-
1
def next_token
-
return if @ss.eos?
-
-
# skips empty actions
-
until token = _next_token or @ss.eos?; end
-
token
-
end
-
-
1
def _next_token
-
text = @ss.peek(1)
-
@lineno += 1 if text == "\n"
-
token = case @state
-
when nil
-
case
-
when (text = @ss.scan(/has\([\s]*/))
-
action { [:HAS, text] }
-
-
when (text = @ss.scan(/[-@]?([_A-Za-z]|[^\0-\177]|\\[0-9A-Fa-f]{1,6}(\r\n|[\s])?|\\[^\n\r\f0-9A-Fa-f])([_A-Za-z0-9-]|[^\0-\177]|\\[0-9A-Fa-f]{1,6}(\r\n|[\s])?|\\[^\n\r\f0-9A-Fa-f])*\([\s]*/))
-
action { [:FUNCTION, text] }
-
-
when (text = @ss.scan(/[-@]?([_A-Za-z]|[^\0-\177]|\\[0-9A-Fa-f]{1,6}(\r\n|[\s])?|\\[^\n\r\f0-9A-Fa-f])([_A-Za-z0-9-]|[^\0-\177]|\\[0-9A-Fa-f]{1,6}(\r\n|[\s])?|\\[^\n\r\f0-9A-Fa-f])*/))
-
action { [:IDENT, text] }
-
-
when (text = @ss.scan(/\#([_A-Za-z0-9-]|[^\0-\177]|\\[0-9A-Fa-f]{1,6}(\r\n|[\s])?|\\[^\n\r\f0-9A-Fa-f])+/))
-
action { [:HASH, text] }
-
-
when (text = @ss.scan(/[\s]*~=[\s]*/))
-
action { [:INCLUDES, text] }
-
-
when (text = @ss.scan(/[\s]*\|=[\s]*/))
-
action { [:DASHMATCH, text] }
-
-
when (text = @ss.scan(/[\s]*\^=[\s]*/))
-
action { [:PREFIXMATCH, text] }
-
-
when (text = @ss.scan(/[\s]*\$=[\s]*/))
-
action { [:SUFFIXMATCH, text] }
-
-
when (text = @ss.scan(/[\s]*\*=[\s]*/))
-
action { [:SUBSTRINGMATCH, text] }
-
-
when (text = @ss.scan(/[\s]*!=[\s]*/))
-
action { [:NOT_EQUAL, text] }
-
-
when (text = @ss.scan(/[\s]*=[\s]*/))
-
action { [:EQUAL, text] }
-
-
when (text = @ss.scan(/[\s]*\)/))
-
action { [:RPAREN, text] }
-
-
when (text = @ss.scan(/[\s]*\[[\s]*/))
-
action { [:LSQUARE, text] }
-
-
when (text = @ss.scan(/[\s]*\]/))
-
action { [:RSQUARE, text] }
-
-
when (text = @ss.scan(/[\s]*\+[\s]*/))
-
action { [:PLUS, text] }
-
-
when (text = @ss.scan(/[\s]*>[\s]*/))
-
action { [:GREATER, text] }
-
-
when (text = @ss.scan(/[\s]*,[\s]*/))
-
action { [:COMMA, text] }
-
-
when (text = @ss.scan(/[\s]*~[\s]*/))
-
action { [:TILDE, text] }
-
-
when (text = @ss.scan(/\:not\([\s]*/))
-
action { [:NOT, text] }
-
-
when (text = @ss.scan(/-?([0-9]+|[0-9]*\.[0-9]+)/))
-
action { [:NUMBER, text] }
-
-
when (text = @ss.scan(/[\s]*\/\/[\s]*/))
-
action { [:DOUBLESLASH, text] }
-
-
when (text = @ss.scan(/[\s]*\/[\s]*/))
-
action { [:SLASH, text] }
-
-
when (text = @ss.scan(/U\+[0-9a-f?]{1,6}(-[0-9a-f]{1,6})?/))
-
action {[:UNICODE_RANGE, text] }
-
-
when (text = @ss.scan(/[\s]+/))
-
action { [:S, text] }
-
-
when (text = @ss.scan(/"([^\n\r\f"]|\n|\r\n|\r|\f|[^\0-\177]|\\[0-9A-Fa-f]{1,6}(\r\n|[\s])?|\\[^\n\r\f0-9A-Fa-f])*"|'([^\n\r\f']|\n|\r\n|\r|\f|[^\0-\177]|\\[0-9A-Fa-f]{1,6}(\r\n|[\s])?|\\[^\n\r\f0-9A-Fa-f])*'/))
-
action { [:STRING, text] }
-
-
when (text = @ss.scan(/./))
-
action { [text, text] }
-
-
else
-
text = @ss.string[@ss.pos .. -1]
-
raise ScanError, "can not match: '" + text + "'"
-
end # if
-
-
else
-
raise ScanError, "undefined state: '" + state.to_s + "'"
-
end # case state
-
token
-
end # def _next_token
-
-
end # class
-
end
-
end
-
1
module Nokogiri
-
1
module CSS
-
1
class XPathVisitor # :nodoc:
-
1
def visit_function node
-
-
msg = :"visit_function_#{node.value.first.gsub(/[(]/, '')}"
-
return self.send(msg, node) if self.respond_to?(msg)
-
-
case node.value.first
-
when /^text\(/
-
'child::text()'
-
when /^self\(/
-
"self::#{node.value[1]}"
-
when /^eq\(/
-
"position() = #{node.value[1]}"
-
when /^(nth|nth-of-type)\(/
-
if node.value[1].is_a?(Nokogiri::CSS::Node) and node.value[1].type == :NTH
-
nth(node.value[1])
-
else
-
"position() = #{node.value[1]}"
-
end
-
when /^nth-child\(/
-
if node.value[1].is_a?(Nokogiri::CSS::Node) and node.value[1].type == :NTH
-
nth(node.value[1], :child => true)
-
else
-
"count(preceding-sibling::*) = #{node.value[1].to_i-1}"
-
end
-
when /^nth-last-of-type\(/
-
if node.value[1].is_a?(Nokogiri::CSS::Node) and node.value[1].type == :NTH
-
nth(node.value[1], :last => true)
-
else
-
index = node.value[1].to_i - 1
-
index == 0 ? "position() = last()" : "position() = last() - #{index}"
-
end
-
when /^nth-last-child\(/
-
if node.value[1].is_a?(Nokogiri::CSS::Node) and node.value[1].type == :NTH
-
nth(node.value[1], :last => true, :child => true)
-
else
-
"count(following-sibling::*) = #{node.value[1].to_i-1}"
-
end
-
when /^(first|first-of-type)\(/
-
"position() = 1"
-
when /^(last|last-of-type)\(/
-
"position() = last()"
-
when /^contains\(/
-
"contains(., #{node.value[1]})"
-
when /^gt\(/
-
"position() > #{node.value[1]}"
-
when /^only-child\(/
-
"last() = 1"
-
when /^comment\(/
-
"comment()"
-
when /^has\(/
-
node.value[1].accept(self)
-
else
-
args = ['.'] + node.value[1..-1]
-
"#{node.value.first}#{args.join(', ')})"
-
end
-
end
-
-
1
def visit_not node
-
child = node.value.first
-
if :ELEMENT_NAME == child.type
-
"not(self::#{child.accept(self)})"
-
else
-
"not(#{child.accept(self)})"
-
end
-
end
-
-
1
def visit_id node
-
node.value.first =~ /^#(.*)$/
-
"@id = '#{$1}'"
-
end
-
-
1
def visit_attribute_condition node
-
attribute = if (node.value.first.type == :FUNCTION) or (node.value.first.value.first =~ /::/)
-
''
-
else
-
'@'
-
end
-
attribute += node.value.first.accept(self)
-
-
# Support non-standard css
-
attribute.gsub!(/^@@/, '@')
-
-
return attribute unless node.value.length == 3
-
-
value = node.value.last
-
value = "'#{value}'" if value !~ /^['"]/
-
-
case node.value[1]
-
when :equal
-
attribute + " = " + "#{value}"
-
when :not_equal
-
attribute + " != " + "#{value}"
-
when :substring_match
-
"contains(#{attribute}, #{value})"
-
when :prefix_match
-
"starts-with(#{attribute}, #{value})"
-
when :dash_match
-
"#{attribute} = #{value} or starts-with(#{attribute}, concat(#{value}, '-'))"
-
when :includes
-
"contains(concat(\" \", #{attribute}, \" \"),concat(\" \", #{value}, \" \"))"
-
when :suffix_match
-
"substring(#{attribute}, string-length(#{attribute}) - " +
-
"string-length(#{value}) + 1, string-length(#{value})) = #{value}"
-
else
-
attribute + " #{node.value[1]} " + "#{value}"
-
end
-
end
-
-
1
def visit_pseudo_class node
-
if node.value.first.is_a?(Nokogiri::CSS::Node) and node.value.first.type == :FUNCTION
-
node.value.first.accept(self)
-
else
-
msg = :"visit_pseudo_class_#{node.value.first.gsub(/[(]/, '')}"
-
return self.send(msg, node) if self.respond_to?(msg)
-
-
case node.value.first
-
when "first" then "position() = 1"
-
when "first-child" then "count(preceding-sibling::*) = 0"
-
when "last" then "position() = last()"
-
when "last-child" then "count(following-sibling::*) = 0"
-
when "first-of-type" then "position() = 1"
-
when "last-of-type" then "position() = last()"
-
when "only-child" then "count(preceding-sibling::*) = 0 and count(following-sibling::*) = 0"
-
when "only-of-type" then "last() = 1"
-
when "empty" then "not(node())"
-
when "parent" then "node()"
-
when "root" then "not(parent::*)"
-
else
-
node.value.first + "(.)"
-
end
-
end
-
end
-
-
1
def visit_class_condition node
-
"contains(concat(' ', normalize-space(@class), ' '), ' #{node.value.first} ')"
-
end
-
-
1
def visit_combinator node
-
if is_of_type_pseudo_class?(node.value.last)
-
"#{node.value.first.accept(self) if node.value.first}][#{node.value.last.accept(self)}"
-
else
-
"#{node.value.first.accept(self) if node.value.first} and #{node.value.last.accept(self)}"
-
end
-
end
-
-
{
-
'direct_adjacent_selector' => "/following-sibling::*[1]/self::",
-
'following_selector' => "/following-sibling::",
-
'descendant_selector' => '//',
-
'child_selector' => '/',
-
1
}.each do |k,v|
-
4
class_eval %{
-
def visit_#{k} node
-
"\#{node.value.first.accept(self) if node.value.first}#{v}\#{node.value.last.accept(self)}"
-
end
-
}
-
end
-
-
1
def visit_conditional_selector node
-
node.value.first.accept(self) + '[' +
-
node.value.last.accept(self) + ']'
-
end
-
-
1
def visit_element_name node
-
node.value.first
-
end
-
-
1
def accept node
-
node.accept(self)
-
end
-
-
1
private
-
1
def nth node, options={}
-
raise ArgumentError, "expected an+b node to contain 4 tokens, but is #{node.value.inspect}" unless node.value.size == 4
-
-
a, b = read_a_and_positive_b node.value
-
position = if options[:child]
-
options[:last] ? "(count(following-sibling::*) + 1)" : "(count(preceding-sibling::*) + 1)"
-
else
-
options[:last] ? "(last()-position()+1)" : "position()"
-
end
-
-
if (b == 0)
-
return "(#{position} mod #{a}) = 0"
-
else
-
compare = (a < 0) ? "<=" : ">="
-
return "(#{position} #{compare} #{b}) and (((#{position}-#{b}) mod #{a.abs}) = 0)"
-
end
-
end
-
-
1
def read_a_and_positive_b values
-
op = values[2]
-
if op == "+"
-
a = values[0].to_i
-
b = values[3].to_i
-
elsif op == "-"
-
a = values[0].to_i
-
b = a - (values[3].to_i % a)
-
else
-
raise ArgumentError, "expected an+b node to have either + or - as the operator, but is #{op.inspect}"
-
end
-
[a, b]
-
end
-
-
1
def is_of_type_pseudo_class? node
-
if node.type==:PSEUDO_CLASS
-
if node.value[0].is_a?(Nokogiri::CSS::Node) and node.value[0].type == :FUNCTION
-
node.value[0].value[0]
-
else
-
node.value[0]
-
end =~ /(nth|first|last|only)-of-type(\()?/
-
end
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module Decorators
-
###
-
# The Slop decorator implements method missing such that a methods may be
-
# used instead of XPath or CSS. See Nokogiri.Slop
-
1
module Slop
-
###
-
# look for node with +name+. See Nokogiri.Slop
-
1
def method_missing name, *args, &block
-
prefix = implied_xpath_context
-
-
if args.empty?
-
list = xpath("#{prefix}#{name.to_s.sub(/^_/, '')}")
-
elsif args.first.is_a? Hash
-
hash = args.first
-
if hash[:css]
-
list = css("#{name}#{hash[:css]}")
-
elsif hash[:xpath]
-
conds = Array(hash[:xpath]).join(' and ')
-
list = xpath("#{prefix}#{name}[#{conds}]")
-
end
-
else
-
CSS::Parser.without_cache do
-
list = xpath(
-
*CSS.xpath_for("#{name}#{args.first}", :prefix => prefix)
-
)
-
end
-
end
-
-
super if list.empty?
-
list.length == 1 ? list.first : list
-
end
-
end
-
end
-
end
-
1
require 'nokogiri/html/entity_lookup'
-
1
require 'nokogiri/html/document'
-
1
require 'nokogiri/html/document_fragment'
-
1
require 'nokogiri/html/sax/parser_context'
-
1
require 'nokogiri/html/sax/parser'
-
1
require 'nokogiri/html/sax/push_parser'
-
1
require 'nokogiri/html/element_description'
-
1
require 'nokogiri/html/element_description_defaults'
-
-
1
module Nokogiri
-
1
class << self
-
###
-
# Parse HTML. Convenience method for Nokogiri::HTML::Document.parse
-
1
def HTML thing, url = nil, encoding = nil, options = XML::ParseOptions::DEFAULT_HTML, &block
-
Nokogiri::HTML::Document.parse(thing, url, encoding, options, &block)
-
end
-
end
-
-
1
module HTML
-
1
class << self
-
###
-
# Parse HTML. Convenience method for Nokogiri::HTML::Document.parse
-
1
def parse thing, url = nil, encoding = nil, options = XML::ParseOptions::DEFAULT_HTML, &block
-
Document.parse(thing, url, encoding, options, &block)
-
end
-
-
####
-
# Parse a fragment from +string+ in to a NodeSet.
-
1
def fragment string, encoding = nil
-
HTML::DocumentFragment.parse string, encoding
-
end
-
end
-
-
# Instance of Nokogiri::HTML::EntityLookup
-
1
NamedCharacters = EntityLookup.new
-
end
-
end
-
1
module Nokogiri
-
1
module HTML
-
###
-
# Nokogiri HTML builder is used for building HTML documents. It is very
-
# similar to the Nokogiri::XML::Builder. In fact, you should go read the
-
# documentation for Nokogiri::XML::Builder before reading this
-
# documentation.
-
#
-
# == Synopsis:
-
#
-
# Create an HTML document with a body that has an onload attribute, and a
-
# span tag with a class of "bold" that has content of "Hello world".
-
#
-
# builder = Nokogiri::HTML::Builder.new do |doc|
-
# doc.html {
-
# doc.body(:onload => 'some_func();') {
-
# doc.span.bold {
-
# doc.text "Hello world"
-
# }
-
# }
-
# }
-
# end
-
# puts builder.to_html
-
#
-
# The HTML builder inherits from the XML builder, so make sure to read the
-
# Nokogiri::XML::Builder documentation.
-
1
class Builder < Nokogiri::XML::Builder
-
###
-
# Convert the builder to HTML
-
1
def to_html
-
@doc.to_html
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module HTML
-
1
class Document < Nokogiri::XML::Document
-
###
-
# Get the meta tag encoding for this document. If there is no meta tag,
-
# then nil is returned.
-
1
def meta_encoding
-
case
-
when meta = at('//meta[@charset]')
-
meta[:charset]
-
when meta = meta_content_type
-
meta['content'][/charset\s*=\s*([\w-]+)/i, 1]
-
end
-
end
-
-
###
-
# Set the meta tag encoding for this document.
-
#
-
# If an meta encoding tag is already present, its content is
-
# replaced with the given text.
-
#
-
# Otherwise, this method tries to create one at an appropriate
-
# place supplying head and/or html elements as necessary, which
-
# is inside a head element if any, and before any text node or
-
# content element (typically <body>) if any.
-
#
-
# The result when trying to set an encoding that is different
-
# from the document encoding is undefined.
-
#
-
# Beware in CRuby, that libxml2 automatically inserts a meta tag
-
# into a head element.
-
1
def meta_encoding= encoding
-
case
-
when meta = meta_content_type
-
meta['content'] = 'text/html; charset=%s' % encoding
-
encoding
-
when meta = at('//meta[@charset]')
-
meta['charset'] = encoding
-
else
-
meta = XML::Node.new('meta', self)
-
if dtd = internal_subset and dtd.html5_dtd?
-
meta['charset'] = encoding
-
else
-
meta['http-equiv'] = 'Content-Type'
-
meta['content'] = 'text/html; charset=%s' % encoding
-
end
-
-
case
-
when head = at('//head')
-
head.prepend_child(meta)
-
else
-
set_metadata_element(meta)
-
end
-
encoding
-
end
-
end
-
-
1
def meta_content_type
-
xpath('//meta[@http-equiv and boolean(@content)]').find { |node|
-
node['http-equiv'] =~ /\AContent-Type\z/i
-
}
-
end
-
1
private :meta_content_type
-
-
###
-
# Get the title string of this document. Return nil if there is
-
# no title tag.
-
1
def title
-
title = at('//title') and title.inner_text
-
end
-
-
###
-
# Set the title string of this document.
-
#
-
# If a title element is already present, its content is replaced
-
# with the given text.
-
#
-
# Otherwise, this method tries to create one at an appropriate
-
# place supplying head and/or html elements as necessary, which
-
# is inside a head element if any, right after a meta
-
# encoding/charset tag if any, and before any text node or
-
# content element (typically <body>) if any.
-
1
def title=(text)
-
tnode = XML::Text.new(text, self)
-
if title = at('//title')
-
title.children = tnode
-
return text
-
end
-
-
title = XML::Node.new('title', self) << tnode
-
case
-
when head = at('//head')
-
head << title
-
when meta = at('//meta[@charset]') || meta_content_type
-
# better put after charset declaration
-
meta.add_next_sibling(title)
-
else
-
set_metadata_element(title)
-
end
-
text
-
end
-
-
1
def set_metadata_element(element)
-
case
-
when head = at('//head')
-
head << element
-
when html = at('//html')
-
head = html.prepend_child(XML::Node.new('head', self))
-
head.prepend_child(element)
-
when first = children.find { |node|
-
case node
-
when XML::Element, XML::Text
-
true
-
end
-
}
-
# We reach here only if the underlying document model
-
# allows <html>/<head> elements to be omitted and does not
-
# automatically supply them.
-
first.add_previous_sibling(element)
-
else
-
html = add_child(XML::Node.new('html', self))
-
head = html.add_child(XML::Node.new('head', self))
-
head.prepend_child(element)
-
end
-
end
-
1
private :set_metadata_element
-
-
####
-
# Serialize Node using +options+. Save options can also be set using a
-
# block. See SaveOptions.
-
#
-
# These two statements are equivalent:
-
#
-
# node.serialize(:encoding => 'UTF-8', :save_with => FORMAT | AS_XML)
-
#
-
# or
-
#
-
# node.serialize(:encoding => 'UTF-8') do |config|
-
# config.format.as_xml
-
# end
-
#
-
1
def serialize options = {}
-
options[:save_with] ||= XML::Node::SaveOptions::DEFAULT_HTML
-
super
-
end
-
-
####
-
# Create a Nokogiri::XML::DocumentFragment from +tags+
-
1
def fragment tags = nil
-
DocumentFragment.new(self, tags, self.root)
-
end
-
-
1
class << self
-
###
-
# Parse HTML. +string_or_io+ may be a String, or any object that
-
# responds to _read_ and _close_ such as an IO, or StringIO.
-
# +url+ is resource where this document is located. +encoding+ is the
-
# encoding that should be used when processing the document. +options+
-
# is a number that sets options in the parser, such as
-
# Nokogiri::XML::ParseOptions::RECOVER. See the constants in
-
# Nokogiri::XML::ParseOptions.
-
1
def parse string_or_io, url = nil, encoding = nil, options = XML::ParseOptions::DEFAULT_HTML
-
-
options = Nokogiri::XML::ParseOptions.new(options) if Fixnum === options
-
# Give the options to the user
-
yield options if block_given?
-
-
if string_or_io.respond_to?(:encoding)
-
unless string_or_io.encoding.name == "ASCII-8BIT"
-
encoding ||= string_or_io.encoding.name
-
end
-
end
-
-
if string_or_io.respond_to?(:read)
-
url ||= string_or_io.respond_to?(:path) ? string_or_io.path : nil
-
if !encoding
-
# Libxml2's parser has poor support for encoding
-
# detection. First, it does not recognize the HTML5
-
# style meta charset declaration. Secondly, even if it
-
# successfully detects an encoding hint, it does not
-
# re-decode or re-parse the preceding part which may be
-
# garbled.
-
#
-
# EncodingReader aims to perform advanced encoding
-
# detection beyond what Libxml2 does, and to emulate
-
# rewinding of a stream and make Libxml2 redo parsing
-
# from the start when an encoding hint is found.
-
string_or_io = EncodingReader.new(string_or_io)
-
begin
-
return read_io(string_or_io, url, encoding, options.to_i)
-
rescue EncodingFound => e
-
encoding = e.found_encoding
-
end
-
end
-
return read_io(string_or_io, url, encoding, options.to_i)
-
end
-
-
# read_memory pukes on empty docs
-
return new if string_or_io.nil? or string_or_io.empty?
-
-
encoding ||= EncodingReader.detect_encoding(string_or_io)
-
-
read_memory(string_or_io, url, encoding, options.to_i)
-
end
-
end
-
-
1
class EncodingFound < StandardError # :nodoc:
-
1
attr_reader :found_encoding
-
-
1
def initialize(encoding)
-
@found_encoding = encoding
-
super("encoding found: %s" % encoding)
-
end
-
end
-
-
1
class EncodingReader # :nodoc:
-
1
class SAXHandler < Nokogiri::XML::SAX::Document # :nodoc:
-
1
attr_reader :encoding
-
-
1
def initialize
-
@encoding = nil
-
super()
-
end
-
-
1
def start_element(name, attrs = [])
-
return unless name == 'meta'
-
attr = Hash[attrs]
-
charset = attr['charset'] and
-
@encoding = charset
-
http_equiv = attr['http-equiv'] and
-
http_equiv.match(/\AContent-Type\z/i) and
-
content = attr['content'] and
-
m = content.match(/;\s*charset\s*=\s*([\w-]+)/) and
-
@encoding = m[1]
-
end
-
end
-
-
1
class JumpSAXHandler < SAXHandler
-
1
def initialize(jumptag)
-
@jumptag = jumptag
-
super()
-
end
-
-
1
def start_element(name, attrs = [])
-
super
-
throw @jumptag, @encoding if @encoding
-
throw @jumptag, nil if name =~ /\A(?:div|h1|img|p|br)\z/
-
end
-
end
-
-
1
def self.detect_encoding(chunk)
-
if Nokogiri.jruby? && EncodingReader.is_jruby_without_fix?
-
return EncodingReader.detect_encoding_for_jruby_without_fix(chunk)
-
end
-
m = chunk.match(/\A(<\?xml[ \t\r\n]+[^>]*>)/) and
-
return Nokogiri.XML(m[1]).encoding
-
-
if Nokogiri.jruby?
-
m = chunk.match(/(<meta\s)(.*)(charset\s*=\s*([\w-]+))(.*)/i) and
-
return m[4]
-
catch(:encoding_found) {
-
Nokogiri::HTML::SAX::Parser.new(JumpSAXHandler.new(:encoding_found)).parse(chunk)
-
nil
-
}
-
else
-
handler = SAXHandler.new
-
parser = Nokogiri::HTML::SAX::PushParser.new(handler)
-
parser << chunk rescue Nokogiri::SyntaxError
-
handler.encoding
-
end
-
end
-
-
1
def self.is_jruby_without_fix?
-
JRUBY_VERSION.split('.').join.to_i < 165
-
end
-
-
1
def self.detect_encoding_for_jruby_without_fix(chunk)
-
m = chunk.match(/\A(<\?xml[ \t\r\n]+[^>]*>)/) and
-
return Nokogiri.XML(m[1]).encoding
-
-
m = chunk.match(/(<meta\s)(.*)(charset\s*=\s*([\w-]+))(.*)/i) and
-
return m[4]
-
-
catch(:encoding_found) {
-
Nokogiri::HTML::SAX::Parser.new(JumpSAXHandler.new(:encoding_found.to_s)).parse(chunk)
-
nil
-
}
-
rescue Nokogiri::SyntaxError, RuntimeError
-
# Ignore parser errors that nokogiri may raise
-
nil
-
end
-
-
1
def initialize(io)
-
@io = io
-
@firstchunk = nil
-
@encoding_found = nil
-
end
-
-
# This method is used by the C extension so that
-
# Nokogiri::HTML::Document#read_io() does not leak memory when
-
# EncodingFound is raised.
-
1
attr_reader :encoding_found
-
-
1
def read(len)
-
# no support for a call without len
-
-
if !@firstchunk
-
@firstchunk = @io.read(len) or return nil
-
-
# This implementation expects that the first call from
-
# htmlReadIO() is made with a length long enough (~1KB) to
-
# achieve advanced encoding detection.
-
if encoding = EncodingReader.detect_encoding(@firstchunk)
-
# The first chunk is stored for the next read in retry.
-
raise @encoding_found = EncodingFound.new(encoding)
-
end
-
end
-
@encoding_found = nil
-
-
ret = @firstchunk.slice!(0, len)
-
if (len -= ret.length) > 0
-
rest = @io.read(len) and ret << rest
-
end
-
if ret.empty?
-
nil
-
else
-
ret
-
end
-
end
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module HTML
-
1
class DocumentFragment < Nokogiri::XML::DocumentFragment
-
1
attr_accessor :errors
-
-
####
-
# Create a Nokogiri::XML::DocumentFragment from +tags+, using +encoding+
-
1
def self.parse tags, encoding = nil
-
doc = HTML::Document.new
-
-
encoding ||= tags.respond_to?(:encoding) ? tags.encoding.name : 'UTF-8'
-
doc.encoding = encoding
-
-
new(doc, tags)
-
end
-
-
1
def initialize document, tags = nil, ctx = nil
-
return self unless tags
-
-
if ctx
-
preexisting_errors = document.errors.dup
-
node_set = ctx.parse("<div>#{tags}</div>")
-
node_set.first.children.each { |child| child.parent = self } unless node_set.empty?
-
self.errors = document.errors - preexisting_errors
-
else
-
# This is a horrible hack, but I don't care
-
if tags.strip =~ /^<body/i
-
path = "/html/body"
-
else
-
path = "/html/body/node()"
-
end
-
-
temp_doc = HTML::Document.parse "<html><body>#{tags}", nil, document.encoding
-
temp_doc.xpath(path).each { |child| child.parent = self }
-
self.errors = temp_doc.errors
-
end
-
children
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module HTML
-
1
class ElementDescription
-
###
-
# Is this element a block element?
-
1
def block?
-
!inline?
-
end
-
-
###
-
# Convert this description to a string
-
1
def to_s
-
"#{name}: #{description}"
-
end
-
-
###
-
# Inspection information
-
1
def inspect
-
"#<#{self.class.name}: #{name} #{description}>"
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module HTML
-
1
class ElementDescription
-
-
# Methods are defined protected by method_defined? because at
-
# this point the C-library or Java library is already loaded,
-
# and we don't want to clobber any methods that have been
-
# defined there.
-
-
1
Desc = Struct.new("HTMLElementDescription", :name,
-
:startTag, :endTag, :saveEndTag,
-
:empty, :depr, :dtd, :isinline,
-
:desc,
-
:subelts, :defaultsubelt,
-
:attrs_opt, :attrs_depr, :attrs_req)
-
-
# This is filled in down below.
-
1
DefaultDescriptions = Hash.new()
-
-
1
def default_desc
-
DefaultDescriptions[name.downcase]
-
end
-
1
private :default_desc
-
-
1
unless method_defined? :implied_start_tag?
-
def implied_start_tag?
-
d = default_desc
-
d ? d.startTag : nil
-
end
-
end
-
-
1
unless method_defined? :implied_end_tag?
-
def implied_end_tag?
-
d = default_desc
-
d ? d.endTag : nil
-
end
-
end
-
-
1
unless method_defined? :save_end_tag?
-
def save_end_tag?
-
d = default_desc
-
d ? d.saveEndTag : nil
-
end
-
end
-
-
1
unless method_defined? :deprecated?
-
def deprecated?
-
d = default_desc
-
d ? d.depr : nil
-
end
-
end
-
-
1
unless method_defined? :description
-
def description
-
d = default_desc
-
d ? d.desc : nil
-
end
-
end
-
-
1
unless method_defined? :default_sub_element
-
def default_sub_element
-
d = default_desc
-
d ? d.defaultsubelt : nil
-
end
-
end
-
-
1
unless method_defined? :optional_attributes
-
def optional_attributes
-
d = default_desc
-
d ? d.attrs_opt : []
-
end
-
end
-
-
1
unless method_defined? :deprecated_attributes
-
def deprecated_attributes
-
d = default_desc
-
d ? d.attrs_depr : []
-
end
-
end
-
-
1
unless method_defined? :required_attributes
-
def required_attributes
-
d = default_desc
-
d ? d.attrs_req : []
-
end
-
end
-
-
###
-
# Default Element Descriptions (HTML 4.0) copied from
-
# libxml2/HTMLparser.c and libxml2/include/libxml/HTMLparser.h
-
#
-
# The copyright notice for those files and the following list of
-
# element and attribute descriptions is reproduced here:
-
#
-
# Except where otherwise noted in the source code (e.g. the
-
# files hash.c, list.c and the trio files, which are covered by
-
# a similar licence but with different Copyright notices) all
-
# the files are:
-
#
-
# Copyright (C) 1998-2003 Daniel Veillard. All Rights Reserved.
-
#
-
# Permission is hereby granted, free of charge, to any person
-
# obtaining a copy of this software and associated documentation
-
# files (the "Software"), to deal in the Software without
-
# restriction, including without limitation the rights to use,
-
# copy, modify, merge, publish, distribute, sublicense, and/or
-
# sell copies of the Software, and to permit persons to whom the
-
# Software is fur- nished to do so, subject to the following
-
# conditions:
-
-
# The above copyright notice and this permission notice shall be
-
# included in all copies or substantial portions of the
-
# Software.
-
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
-
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
-
# WARRANTIES OF MERCHANTABILITY, FIT- NESS FOR A PARTICULAR
-
# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE DANIEL
-
# VEILLARD BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-
# FROM, OUT OF OR IN CON- NECTION WITH THE SOFTWARE OR THE USE
-
# OR OTHER DEALINGS IN THE SOFTWARE.
-
-
# Except as contained in this notice, the name of Daniel
-
# Veillard shall not be used in advertising or otherwise to
-
# promote the sale, use or other deal- ings in this Software
-
# without prior written authorization from him.
-
-
# Attributes defined and categorized
-
1
FONTSTYLE = ["tt", "i", "b", "u", "s", "strike", "big", "small"]
-
1
PHRASE = ['em', 'strong', 'dfn', 'code', 'samp',
-
'kbd', 'var', 'cite', 'abbr', 'acronym']
-
1
SPECIAL = ['a', 'img', 'applet', 'embed', 'object', 'font','basefont',
-
'br', 'script', 'map', 'q', 'sub', 'sup', 'span', 'bdo',
-
'iframe']
-
1
PCDATA = []
-
1
HEADING = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']
-
1
LIST = ['ul', 'ol', 'dir', 'menu']
-
1
FORMCTRL = ['input', 'select', 'textarea', 'label', 'button']
-
1
BLOCK = [HEADING, LIST, 'pre', 'p', 'dl', 'div', 'center', 'noscript',
-
'noframes', 'blockquote', 'form', 'isindex', 'hr', 'table',
-
'fieldset', 'address']
-
1
INLINE = [PCDATA, FONTSTYLE, PHRASE, SPECIAL, FORMCTRL]
-
1
FLOW = [BLOCK, INLINE]
-
1
MODIFIER = []
-
1
EMPTY = []
-
-
1
HTML_FLOW = FLOW
-
1
HTML_INLINE = INLINE
-
1
HTML_PCDATA = PCDATA
-
1
HTML_CDATA = HTML_PCDATA
-
-
1
COREATTRS = ['id', 'class', 'style', 'title']
-
1
I18N = ['lang', 'dir']
-
1
EVENTS = ['onclick', 'ondblclick', 'onmousedown', 'onmouseup',
-
'onmouseover', 'onmouseout', 'onkeypress', 'onkeydown',
-
'onkeyup']
-
1
ATTRS = [COREATTRS, I18N,EVENTS]
-
1
CELLHALIGN = ['align', 'char', 'charoff']
-
1
CELLVALIGN = ['valign']
-
-
1
HTML_ATTRS = ATTRS
-
1
CORE_I18N_ATTRS = [COREATTRS, I18N]
-
1
CORE_ATTRS = COREATTRS
-
1
I18N_ATTRS = I18N
-
-
-
1
A_ATTRS = [ATTRS, 'charset', 'type', 'name',
-
'href', 'hreflang', 'rel', 'rev', 'accesskey', 'shape',
-
'coords', 'tabindex', 'onfocus', 'onblur']
-
1
TARGET_ATTR = ['target']
-
1
ROWS_COLS_ATTR = ['rows', 'cols']
-
1
ALT_ATTR = ['alt']
-
1
SRC_ALT_ATTRS = ['src', 'alt']
-
1
HREF_ATTRS = ['href']
-
1
CLEAR_ATTRS = ['clear']
-
1
INLINE_P = [INLINE, 'p']
-
-
1
FLOW_PARAM = [FLOW, 'param']
-
1
APPLET_ATTRS = [COREATTRS , 'codebase',
-
'archive', 'alt', 'name', 'height', 'width', 'align',
-
'hspace', 'vspace']
-
1
AREA_ATTRS = ['shape', 'coords', 'href', 'nohref',
-
'tabindex', 'accesskey', 'onfocus', 'onblur']
-
1
BASEFONT_ATTRS = ['id', 'size', 'color', 'face']
-
1
QUOTE_ATTRS = [ATTRS, 'cite']
-
1
BODY_CONTENTS = [FLOW, 'ins', 'del']
-
1
BODY_ATTRS = [ATTRS, 'onload', 'onunload']
-
1
BODY_DEPR = ['background', 'bgcolor', 'text',
-
'link', 'vlink', 'alink']
-
1
BUTTON_ATTRS = [ATTRS, 'name', 'value', 'type',
-
'disabled', 'tabindex', 'accesskey', 'onfocus', 'onblur']
-
-
-
1
COL_ATTRS = [ATTRS, 'span', 'width', CELLHALIGN, CELLVALIGN]
-
1
COL_ELT = ['col']
-
1
EDIT_ATTRS = [ATTRS, 'datetime', 'cite']
-
1
COMPACT_ATTRS = [ATTRS, 'compact']
-
1
DL_CONTENTS = ['dt', 'dd']
-
1
COMPACT_ATTR = ['compact']
-
1
LABEL_ATTR = ['label']
-
1
FIELDSET_CONTENTS = [FLOW, 'legend' ]
-
1
FONT_ATTRS = [COREATTRS, I18N, 'size', 'color', 'face' ]
-
1
FORM_CONTENTS = [HEADING, LIST, INLINE, 'pre', 'p', 'div', 'center',
-
'noscript', 'noframes', 'blockquote', 'isindex', 'hr',
-
'table', 'fieldset', 'address']
-
1
FORM_ATTRS = [ATTRS, 'method', 'enctype', 'accept', 'name', 'onsubmit',
-
'onreset', 'accept-charset']
-
1
FRAME_ATTRS = [COREATTRS, 'longdesc', 'name', 'src', 'frameborder',
-
'marginwidth', 'marginheight', 'noresize', 'scrolling' ]
-
1
FRAMESET_ATTRS = [COREATTRS, 'rows', 'cols', 'onload', 'onunload']
-
1
FRAMESET_CONTENTS = ['frameset', 'frame', 'noframes']
-
1
HEAD_ATTRS = [I18N, 'profile']
-
1
HEAD_CONTENTS = ['title', 'isindex', 'base', 'script', 'style', 'meta',
-
'link', 'object']
-
1
HR_DEPR = ['align', 'noshade', 'size', 'width']
-
1
VERSION_ATTR = ['version']
-
1
HTML_CONTENT = ['head', 'body', 'frameset']
-
1
IFRAME_ATTRS = [COREATTRS, 'longdesc', 'name', 'src', 'frameborder',
-
'marginwidth', 'marginheight', 'scrolling', 'align',
-
'height', 'width']
-
1
IMG_ATTRS = [ATTRS, 'longdesc', 'name', 'height', 'width', 'usemap',
-
'ismap']
-
1
EMBED_ATTRS = [COREATTRS, 'align', 'alt', 'border', 'code', 'codebase',
-
'frameborder', 'height', 'hidden', 'hspace', 'name',
-
'palette', 'pluginspace', 'pluginurl', 'src', 'type',
-
'units', 'vspace', 'width']
-
1
INPUT_ATTRS = [ATTRS, 'type', 'name', 'value', 'checked', 'disabled',
-
'readonly', 'size', 'maxlength', 'src', 'alt', 'usemap',
-
'ismap', 'tabindex', 'accesskey', 'onfocus', 'onblur',
-
'onselect', 'onchange', 'accept']
-
1
PROMPT_ATTRS = [COREATTRS, I18N, 'prompt']
-
1
LABEL_ATTRS = [ATTRS, 'for', 'accesskey', 'onfocus', 'onblur']
-
1
LEGEND_ATTRS = [ATTRS, 'accesskey']
-
1
ALIGN_ATTR = ['align']
-
1
LINK_ATTRS = [ATTRS, 'charset', 'href', 'hreflang', 'type', 'rel', 'rev',
-
'media']
-
1
MAP_CONTENTS = [BLOCK, 'area']
-
1
NAME_ATTR = ['name']
-
1
ACTION_ATTR = ['action']
-
1
BLOCKLI_ELT = [BLOCK, 'li']
-
1
META_ATTRS = [I18N, 'http-equiv', 'name', 'scheme']
-
1
CONTENT_ATTR = ['content']
-
1
TYPE_ATTR = ['type']
-
1
NOFRAMES_CONTENT = ['body', FLOW, MODIFIER]
-
1
OBJECT_CONTENTS = [FLOW, 'param']
-
1
OBJECT_ATTRS = [ATTRS, 'declare', 'classid', 'codebase', 'data', 'type',
-
'codetype', 'archive', 'standby', 'height', 'width',
-
'usemap', 'name', 'tabindex']
-
1
OBJECT_DEPR = ['align', 'border', 'hspace', 'vspace']
-
1
OL_ATTRS = ['type', 'compact', 'start']
-
1
OPTION_ELT = ['option']
-
1
OPTGROUP_ATTRS = [ATTRS, 'disabled']
-
1
OPTION_ATTRS = [ATTRS, 'disabled', 'label', 'selected', 'value']
-
1
PARAM_ATTRS = ['id', 'value', 'valuetype', 'type']
-
1
WIDTH_ATTR = ['width']
-
1
PRE_CONTENT = [PHRASE, 'tt', 'i', 'b', 'u', 's', 'strike', 'a', 'br',
-
'script', 'map', 'q', 'span', 'bdo', 'iframe']
-
1
SCRIPT_ATTRS = ['charset', 'src', 'defer', 'event', 'for']
-
1
LANGUAGE_ATTR = ['language']
-
1
SELECT_CONTENT = ['optgroup', 'option']
-
1
SELECT_ATTRS = [ATTRS, 'name', 'size', 'multiple', 'disabled', 'tabindex',
-
'onfocus', 'onblur', 'onchange']
-
1
STYLE_ATTRS = [I18N, 'media', 'title']
-
1
TABLE_ATTRS = [ATTRS, 'summary', 'width', 'border', 'frame', 'rules',
-
'cellspacing', 'cellpadding', 'datapagesize']
-
1
TABLE_DEPR = ['align', 'bgcolor']
-
1
TABLE_CONTENTS = ['caption', 'col', 'colgroup', 'thead', 'tfoot', 'tbody',
-
'tr']
-
1
TR_ELT = ['tr']
-
1
TALIGN_ATTRS = [ATTRS, CELLHALIGN, CELLVALIGN]
-
1
TH_TD_DEPR = ['nowrap', 'bgcolor', 'width', 'height']
-
1
TH_TD_ATTR = [ATTRS, 'abbr', 'axis', 'headers', 'scope', 'rowspan',
-
'colspan', CELLHALIGN, CELLVALIGN]
-
1
TEXTAREA_ATTRS = [ATTRS, 'name', 'disabled', 'readonly', 'tabindex',
-
'accesskey', 'onfocus', 'onblur', 'onselect',
-
'onchange']
-
1
TR_CONTENTS = ['th', 'td']
-
1
BGCOLOR_ATTR = ['bgcolor']
-
1
LI_ELT = ['li']
-
1
UL_DEPR = ['type', 'compact']
-
1
DIR_ATTR = ['dir']
-
-
[
-
['a', false, false, false, false, false, :any, true,
-
'anchor ',
-
HTML_INLINE, nil, A_ATTRS, TARGET_ATTR, []
-
],
-
['abbr', false, false, false, false, false, :any, true,
-
'abbreviated form',
-
HTML_INLINE, nil, HTML_ATTRS, [], []
-
],
-
['acronym', false, false, false, false, false, :any, true, '',
-
HTML_INLINE, nil, HTML_ATTRS, [], []
-
],
-
['address', false, false, false, false, false, :any, false,
-
'information on author',
-
INLINE_P , nil, HTML_ATTRS, [], []
-
],
-
['applet', false, false, false, false, true, :loose, true,
-
'java applet ',
-
FLOW_PARAM, nil, [], APPLET_ATTRS, []
-
],
-
['area', false, true, true, true, false, :any, false,
-
'client-side image map area ',
-
EMPTY, nil, AREA_ATTRS, TARGET_ATTR, ALT_ATTR
-
],
-
['b', false, true, false, false, false, :any, true,
-
'bold text style',
-
HTML_INLINE, nil, HTML_ATTRS, [], []
-
],
-
['base', false, true, true, true, false, :any, false,
-
'document base uri ',
-
EMPTY, nil, [], TARGET_ATTR, HREF_ATTRS
-
],
-
['basefont', false, true, true, true, true, :loose, true,
-
'base font size ',
-
EMPTY, nil, [], BASEFONT_ATTRS, []
-
],
-
['bdo', false, false, false, false, false, :any, true,
-
'i18n bidi over-ride ',
-
HTML_INLINE, nil, CORE_I18N_ATTRS, [], DIR_ATTR
-
],
-
['big', false, true, false, false, false, :any, true,
-
'large text style',
-
HTML_INLINE, nil, HTML_ATTRS, [], []
-
],
-
['blockquote', false, false, false, false, false, :any, false,
-
'long quotation ',
-
HTML_FLOW, nil, QUOTE_ATTRS, [], []
-
],
-
['body', true, true, false, false, false, :any, false,
-
'document body ',
-
BODY_CONTENTS, 'div', BODY_ATTRS, BODY_DEPR, []
-
],
-
['br', false, true, true, true, false, :any, true,
-
'forced line break ',
-
EMPTY, nil, CORE_ATTRS, CLEAR_ATTRS, []
-
],
-
['button', false, false, false, false, false, :any, true,
-
'push button ',
-
[HTML_FLOW, MODIFIER], nil, BUTTON_ATTRS, [], []
-
],
-
['caption', false, false, false, false, false, :any, false,
-
'table caption ',
-
HTML_INLINE, nil, HTML_ATTRS, [], []
-
],
-
['center', false, true, false, false, true, :loose, false,
-
'shorthand for div align=center ',
-
HTML_FLOW, nil, [], HTML_ATTRS, []
-
],
-
['cite', false, false, false, false, false, :any, true, 'citation',
-
HTML_INLINE, nil, HTML_ATTRS, [], []
-
],
-
['code', false, false, false, false, false, :any, true,
-
'computer code fragment',
-
HTML_INLINE, nil, HTML_ATTRS, [], []
-
],
-
['col', false, true, true, true, false, :any, false, 'table column ',
-
EMPTY, nil, COL_ATTRS, [], []
-
],
-
['colgroup', false, true, false, false, false, :any, false,
-
'table column group ',
-
COL_ELT, 'col', COL_ATTRS, [], []
-
],
-
['dd', false, true, false, false, false, :any, false,
-
'definition description ',
-
HTML_FLOW, nil, HTML_ATTRS, [], []
-
],
-
['del', false, false, false, false, false, :any, true,
-
'deleted text ',
-
HTML_FLOW, nil, EDIT_ATTRS, [], []
-
],
-
['dfn', false, false, false, false, false, :any, true,
-
'instance definition',
-
HTML_INLINE, nil, HTML_ATTRS, [], []
-
],
-
['dir', false, false, false, false, true, :loose, false,
-
'directory list',
-
BLOCKLI_ELT, 'li', [], COMPACT_ATTRS, []
-
],
-
['div', false, false, false, false, false, :any, false,
-
'generic language/style container',
-
HTML_FLOW, nil, HTML_ATTRS, ALIGN_ATTR, []
-
],
-
['dl', false, false, false, false, false, :any, false,
-
'definition list ',
-
DL_CONTENTS, 'dd', HTML_ATTRS, COMPACT_ATTR, []
-
],
-
['dt', false, true, false, false, false, :any, false,
-
'definition term ',
-
HTML_INLINE, nil, HTML_ATTRS, [], []
-
],
-
['em', false, true, false, false, false, :any, true,
-
'emphasis',
-
HTML_INLINE, nil, HTML_ATTRS, [], []
-
],
-
['embed', false, true, false, false, true, :loose, true,
-
'generic embedded object ',
-
EMPTY, nil, EMBED_ATTRS, [], []
-
],
-
['fieldset', false, false, false, false, false, :any, false,
-
'form control group ',
-
FIELDSET_CONTENTS, nil, HTML_ATTRS, [], []
-
],
-
['font', false, true, false, false, true, :loose, true,
-
'local change to font ',
-
HTML_INLINE, nil, [], FONT_ATTRS, []
-
],
-
['form', false, false, false, false, false, :any, false,
-
'interactive form ',
-
FORM_CONTENTS, 'fieldset', FORM_ATTRS, TARGET_ATTR, ACTION_ATTR
-
],
-
['frame', false, true, true, true, false, :frameset, false,
-
'subwindow ',
-
EMPTY, nil, [], FRAME_ATTRS, []
-
],
-
['frameset', false, false, false, false, false, :frameset, false,
-
'window subdivision',
-
FRAMESET_CONTENTS, 'noframes', [], FRAMESET_ATTRS, []
-
],
-
['htrue', false, false, false, false, false, :any, false,
-
'heading ',
-
HTML_INLINE, nil, HTML_ATTRS, ALIGN_ATTR, []
-
],
-
['htrue', false, false, false, false, false, :any, false,
-
'heading ',
-
HTML_INLINE, nil, HTML_ATTRS, ALIGN_ATTR, []
-
],
-
['htrue', false, false, false, false, false, :any, false,
-
'heading ',
-
HTML_INLINE, nil, HTML_ATTRS, ALIGN_ATTR, []
-
],
-
['h4', false, false, false, false, false, :any, false,
-
'heading ',
-
HTML_INLINE, nil, HTML_ATTRS, ALIGN_ATTR, []
-
],
-
['h5', false, false, false, false, false, :any, false,
-
'heading ',
-
HTML_INLINE, nil, HTML_ATTRS, ALIGN_ATTR, []
-
],
-
['h6', false, false, false, false, false, :any, false,
-
'heading ',
-
HTML_INLINE, nil, HTML_ATTRS, ALIGN_ATTR, []
-
],
-
['head', true, true, false, false, false, :any, false,
-
'document head ',
-
HEAD_CONTENTS, nil, HEAD_ATTRS, [], []
-
],
-
['hr', false, true, true, true, false, :any, false,
-
'horizontal rule ',
-
EMPTY, nil, HTML_ATTRS, HR_DEPR, []
-
],
-
['html', true, true, false, false, false, :any, false,
-
'document root element ',
-
HTML_CONTENT, nil, I18N_ATTRS, VERSION_ATTR, []
-
],
-
['i', false, true, false, false, false, :any, true,
-
'italic text style',
-
HTML_INLINE, nil, HTML_ATTRS, [], []
-
],
-
['iframe', false, false, false, false, false, :any, true,
-
'inline subwindow ',
-
HTML_FLOW, nil, [], IFRAME_ATTRS, []
-
],
-
['img', false, true, true, true, false, :any, true,
-
'embedded image ',
-
EMPTY, nil, IMG_ATTRS, ALIGN_ATTR, SRC_ALT_ATTRS
-
],
-
['input', false, true, true, true, false, :any, true,
-
'form control ',
-
EMPTY, nil, INPUT_ATTRS, ALIGN_ATTR, []
-
],
-
['ins', false, false, false, false, false, :any, true,
-
'inserted text',
-
HTML_FLOW, nil, EDIT_ATTRS, [], []
-
],
-
['isindex', false, true, true, true, true, :loose, false,
-
'single line prompt ',
-
EMPTY, nil, [], PROMPT_ATTRS, []
-
],
-
['kbd', false, false, false, false, false, :any, true,
-
'text to be entered by the user',
-
HTML_INLINE, nil, HTML_ATTRS, [], []
-
],
-
['label', false, false, false, false, false, :any, true,
-
'form field label text ',
-
[HTML_INLINE, MODIFIER], nil, LABEL_ATTRS, [], []
-
],
-
['legend', false, false, false, false, false, :any, false,
-
'fieldset legend ',
-
HTML_INLINE, nil, LEGEND_ATTRS, ALIGN_ATTR, []
-
],
-
['li', false, true, true, false, false, :any, false,
-
'list item ',
-
HTML_FLOW, nil, HTML_ATTRS, [], []
-
],
-
['link', false, true, true, true, false, :any, false,
-
'a media-independent link ',
-
EMPTY, nil, LINK_ATTRS, TARGET_ATTR, []
-
],
-
['map', false, false, false, false, false, :any, true,
-
'client-side image map ',
-
MAP_CONTENTS, nil, HTML_ATTRS, [], NAME_ATTR
-
],
-
['menu', false, false, false, false, true, :loose, false,
-
'menu list ',
-
BLOCKLI_ELT, nil, [], COMPACT_ATTRS, []
-
],
-
['meta', false, true, true, true, false, :any, false,
-
'generic metainformation ',
-
EMPTY, nil, META_ATTRS, [], CONTENT_ATTR
-
],
-
['noframes', false, false, false, false, false, :frameset, false,
-
'alternate content container for non frame-based rendering ',
-
NOFRAMES_CONTENT, 'body', HTML_ATTRS, [], []
-
],
-
['noscript', false, false, false, false, false, :any, false,
-
'alternate content container for non script-based rendering ',
-
HTML_FLOW, 'div', HTML_ATTRS, [], []
-
],
-
['object', false, false, false, false, false, :any, true,
-
'generic embedded object ',
-
OBJECT_CONTENTS, 'div', OBJECT_ATTRS, OBJECT_DEPR, []
-
],
-
['ol', false, false, false, false, false, :any, false,
-
'ordered list ',
-
LI_ELT, 'li', HTML_ATTRS, OL_ATTRS, []
-
],
-
['optgroup', false, false, false, false, false, :any, false,
-
'option group ',
-
OPTION_ELT, 'option', OPTGROUP_ATTRS, [], LABEL_ATTR
-
],
-
['option', false, true, false, false, false, :any, false,
-
'selectable choice ',
-
HTML_PCDATA, nil, OPTION_ATTRS, [], []
-
],
-
['p', false, true, false, false, false, :any, false,
-
'paragraph ',
-
HTML_INLINE, nil, HTML_ATTRS, ALIGN_ATTR, []
-
],
-
['param', false, true, true, true, false, :any, false,
-
'named property value ',
-
EMPTY, nil, PARAM_ATTRS, [], NAME_ATTR
-
],
-
['pre', false, false, false, false, false, :any, false,
-
'preformatted text ',
-
PRE_CONTENT, nil, HTML_ATTRS, WIDTH_ATTR, []
-
],
-
['q', false, false, false, false, false, :any, true,
-
'short inline quotation ',
-
HTML_INLINE, nil, QUOTE_ATTRS, [], []
-
],
-
['s', false, true, false, false, true, :loose, true,
-
'strike-through text style',
-
HTML_INLINE, nil, [], HTML_ATTRS, []
-
],
-
['samp', false, false, false, false, false, :any, true,
-
'sample program output, scripts, etc.',
-
HTML_INLINE, nil, HTML_ATTRS, [], []
-
],
-
['script', false, false, false, false, false, :any, true,
-
'script statements ',
-
HTML_CDATA, nil, SCRIPT_ATTRS, LANGUAGE_ATTR, TYPE_ATTR
-
],
-
['select', false, false, false, false, false, :any, true,
-
'option selector ',
-
SELECT_CONTENT, nil, SELECT_ATTRS, [], []
-
],
-
['small', false, true, false, false, false, :any, true,
-
'small text style',
-
HTML_INLINE, nil, HTML_ATTRS, [], []
-
],
-
['span', false, false, false, false, false, :any, true,
-
'generic language/style container ',
-
HTML_INLINE, nil, HTML_ATTRS, [], []
-
],
-
['strike', false, true, false, false, true, :loose, true,
-
'strike-through text',
-
HTML_INLINE, nil, [], HTML_ATTRS, []
-
],
-
['strong', false, true, false, false, false, :any, true,
-
'strong emphasis',
-
HTML_INLINE, nil, HTML_ATTRS, [], []
-
],
-
['style', false, false, false, false, false, :any, false,
-
'style info ',
-
HTML_CDATA, nil, STYLE_ATTRS, [], TYPE_ATTR
-
],
-
['sub', false, true, false, false, false, :any, true,
-
'subscript',
-
HTML_INLINE, nil, HTML_ATTRS, [], []
-
],
-
['sup', false, true, false, false, false, :any, true,
-
'superscript ',
-
HTML_INLINE, nil, HTML_ATTRS, [], []
-
],
-
['table', false, false, false, false, false, :any, false,
-
'',
-
TABLE_CONTENTS, 'tr', TABLE_ATTRS, TABLE_DEPR, []
-
],
-
['tbody', true, false, false, false, false, :any, false,
-
'table body ',
-
TR_ELT, 'tr', TALIGN_ATTRS, [], []
-
],
-
['td', false, false, false, false, false, :any, false,
-
'table data cell',
-
HTML_FLOW, nil, TH_TD_ATTR, TH_TD_DEPR, []
-
],
-
['textarea', false, false, false, false, false, :any, true,
-
'multi-line text field ',
-
HTML_PCDATA, nil, TEXTAREA_ATTRS, [], ROWS_COLS_ATTR
-
],
-
['tfoot', false, true, false, false, false, :any, false,
-
'table footer ',
-
TR_ELT, 'tr', TALIGN_ATTRS, [], []
-
],
-
['th', false, true, false, false, false, :any, false,
-
'table header cell',
-
HTML_FLOW, nil, TH_TD_ATTR, TH_TD_DEPR, []
-
],
-
['thead', false, true, false, false, false, :any, false,
-
'table header ',
-
TR_ELT, 'tr', TALIGN_ATTRS, [], []
-
],
-
['title', false, false, false, false, false, :any, false,
-
'document title ',
-
HTML_PCDATA, nil, I18N_ATTRS, [], []
-
],
-
['tr', false, false, false, false, false, :any, false,
-
'table row ',
-
TR_CONTENTS, 'td', TALIGN_ATTRS, BGCOLOR_ATTR, []
-
],
-
['tt', false, true, false, false, false, :any, true,
-
'teletype or monospaced text style',
-
HTML_INLINE, nil, HTML_ATTRS, [], []
-
],
-
['u', false, true, false, false, true, :loose, true,
-
'underlined text style',
-
HTML_INLINE, nil, [], HTML_ATTRS, []
-
],
-
['ul', false, false, false, false, false, :any, false,
-
'unordered list ',
-
LI_ELT, 'li', HTML_ATTRS, UL_DEPR, []
-
],
-
['var', false, false, false, false, false, :any, true,
-
'instance of a variable or program argument',
-
HTML_INLINE, nil, HTML_ATTRS, [], []
-
]
-
1
].each do |descriptor|
-
92
name = descriptor[0]
-
-
92
begin
-
92
d = Desc.new(*descriptor)
-
-
# flatten all the attribute lists (Ruby1.9, *[a,b,c] can be
-
# used to flatten a literal list, but not in Ruby1.8).
-
92
d[:subelts] = d[:subelts].flatten
-
92
d[:attrs_opt] = d[:attrs_opt].flatten
-
92
d[:attrs_depr] = d[:attrs_depr].flatten
-
92
d[:attrs_req] = d[:attrs_req].flatten
-
rescue => e
-
p name
-
raise e
-
end
-
-
92
DefaultDescriptions[name] = d
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module HTML
-
1
class EntityDescription < Struct.new(:value, :name, :description); end
-
-
1
class EntityLookup
-
###
-
# Look up entity with +name+
-
1
def [] name
-
(val = get(name)) && val.value
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module HTML
-
###
-
# Nokogiri lets you write a SAX parser to process HTML but get HTML
-
# correction features.
-
#
-
# See Nokogiri::HTML::SAX::Parser for a basic example of using a
-
# SAX parser with HTML.
-
#
-
# For more information on SAX parsers, see Nokogiri::XML::SAX
-
1
module SAX
-
###
-
# This class lets you perform SAX style parsing on HTML with HTML
-
# error correction.
-
#
-
# Here is a basic usage example:
-
#
-
# class MyDoc < Nokogiri::XML::SAX::Document
-
# def start_element name, attributes = []
-
# puts "found a #{name}"
-
# end
-
# end
-
#
-
# parser = Nokogiri::HTML::SAX::Parser.new(MyDoc.new)
-
# parser.parse(File.read(ARGV[0], mode: 'rb'))
-
#
-
# For more information on SAX parsers, see Nokogiri::XML::SAX
-
1
class Parser < Nokogiri::XML::SAX::Parser
-
###
-
# Parse html stored in +data+ using +encoding+
-
1
def parse_memory data, encoding = 'UTF-8'
-
raise ArgumentError unless data
-
return unless data.length > 0
-
ctx = ParserContext.memory(data, encoding)
-
yield ctx if block_given?
-
ctx.parse_with self
-
end
-
-
###
-
# Parse a file with +filename+
-
1
def parse_file filename, encoding = 'UTF-8'
-
raise ArgumentError unless filename
-
raise Errno::ENOENT unless File.exist?(filename)
-
raise Errno::EISDIR if File.directory?(filename)
-
ctx = ParserContext.file(filename, encoding)
-
yield ctx if block_given?
-
ctx.parse_with self
-
end
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module HTML
-
1
module SAX
-
###
-
# Context for HTML SAX parsers. This class is usually not instantiated
-
# by the user. Instead, you should be looking at
-
# Nokogiri::HTML::SAX::Parser
-
1
class ParserContext < Nokogiri::XML::SAX::ParserContext
-
1
def self.new thing, encoding = 'UTF-8'
-
[:read, :close].all? { |x| thing.respond_to?(x) } ? super :
-
memory(thing, encoding)
-
end
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module HTML
-
1
module SAX
-
1
class PushParser
-
1
def initialize(doc = XML::SAX::Document.new, file_name = nil, encoding = 'UTF-8')
-
@document = doc
-
@encoding = encoding
-
@sax_parser = HTML::SAX::Parser.new(doc, @encoding)
-
-
## Create our push parser context
-
initialize_native(@sax_parser, file_name, @encoding)
-
end
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
class SyntaxError < ::StandardError
-
end
-
end
-
1
require 'nokogiri/xml/pp'
-
1
require 'nokogiri/xml/parse_options'
-
1
require 'nokogiri/xml/sax'
-
1
require 'nokogiri/xml/node'
-
1
require 'nokogiri/xml/attribute_decl'
-
1
require 'nokogiri/xml/element_decl'
-
1
require 'nokogiri/xml/element_content'
-
1
require 'nokogiri/xml/character_data'
-
1
require 'nokogiri/xml/namespace'
-
1
require 'nokogiri/xml/attr'
-
1
require 'nokogiri/xml/dtd'
-
1
require 'nokogiri/xml/cdata'
-
1
require 'nokogiri/xml/text'
-
1
require 'nokogiri/xml/document'
-
1
require 'nokogiri/xml/document_fragment'
-
1
require 'nokogiri/xml/processing_instruction'
-
1
require 'nokogiri/xml/node_set'
-
1
require 'nokogiri/xml/syntax_error'
-
1
require 'nokogiri/xml/xpath'
-
1
require 'nokogiri/xml/xpath_context'
-
1
require 'nokogiri/xml/builder'
-
1
require 'nokogiri/xml/reader'
-
1
require 'nokogiri/xml/notation'
-
1
require 'nokogiri/xml/entity_decl'
-
1
require 'nokogiri/xml/schema'
-
1
require 'nokogiri/xml/relax_ng'
-
-
1
module Nokogiri
-
1
class << self
-
###
-
# Parse XML. Convenience method for Nokogiri::XML::Document.parse
-
1
def XML thing, url = nil, encoding = nil, options = XML::ParseOptions::DEFAULT_XML, &block
-
Nokogiri::XML::Document.parse(thing, url, encoding, options, &block)
-
end
-
end
-
-
1
module XML
-
# Original C14N 1.0 spec canonicalization
-
1
XML_C14N_1_0 = 0
-
# Exclusive C14N 1.0 spec canonicalization
-
1
XML_C14N_EXCLUSIVE_1_0 = 1
-
# C14N 1.1 spec canonicalization
-
1
XML_C14N_1_1 = 2
-
1
class << self
-
###
-
# Parse an XML document using the Nokogiri::XML::Reader API. See
-
# Nokogiri::XML::Reader for mor information
-
1
def Reader string_or_io, url = nil, encoding = nil, options = ParseOptions::STRICT
-
-
options = Nokogiri::XML::ParseOptions.new(options) if Fixnum === options
-
# Give the options to the user
-
yield options if block_given?
-
-
if string_or_io.respond_to? :read
-
return Reader.from_io(string_or_io, url, encoding, options.to_i)
-
end
-
Reader.from_memory(string_or_io, url, encoding, options.to_i)
-
end
-
-
###
-
# Parse XML. Convenience method for Nokogiri::XML::Document.parse
-
1
def parse thing, url = nil, encoding = nil, options = ParseOptions::DEFAULT_XML, &block
-
Document.parse(thing, url, encoding, options, &block)
-
end
-
-
####
-
# Parse a fragment from +string+ in to a NodeSet.
-
1
def fragment string
-
XML::DocumentFragment.parse(string)
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
1
class Attr < Node
-
1
alias :value :content
-
1
alias :to_s :content
-
1
alias :content= :value=
-
-
1
private
-
1
def inspect_attributes
-
[:name, :namespace, :value]
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
###
-
# Represents an attribute declaration in a DTD
-
1
class AttributeDecl < Nokogiri::XML::Node
-
1
undef_method :attribute_nodes
-
1
undef_method :attributes
-
1
undef_method :content
-
1
undef_method :namespace
-
1
undef_method :namespace_definitions
-
1
undef_method :line if method_defined?(:line)
-
-
1
def inspect
-
"#<#{self.class.name}:#{sprintf("0x%x", object_id)} #{to_s.inspect}>"
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
###
-
# Nokogiri builder can be used for building XML and HTML documents.
-
#
-
# == Synopsis:
-
#
-
# builder = Nokogiri::XML::Builder.new do |xml|
-
# xml.root {
-
# xml.products {
-
# xml.widget {
-
# xml.id_ "10"
-
# xml.name "Awesome widget"
-
# }
-
# }
-
# }
-
# end
-
# puts builder.to_xml
-
#
-
# Will output:
-
#
-
# <?xml version="1.0"?>
-
# <root>
-
# <products>
-
# <widget>
-
# <id>10</id>
-
# <name>Awesome widget</name>
-
# </widget>
-
# </products>
-
# </root>
-
#
-
#
-
# === Builder scope
-
#
-
# The builder allows two forms. When the builder is supplied with a block
-
# that has a parameter, the outside scope is maintained. This means you
-
# can access variables that are outside your builder. If you don't need
-
# outside scope, you can use the builder without the "xml" prefix like
-
# this:
-
#
-
# builder = Nokogiri::XML::Builder.new do
-
# root {
-
# products {
-
# widget {
-
# id_ "10"
-
# name "Awesome widget"
-
# }
-
# }
-
# }
-
# end
-
#
-
# == Special Tags
-
#
-
# The builder works by taking advantage of method_missing. Unfortunately
-
# some methods are defined in ruby that are difficult or dangerous to
-
# remove. You may want to create tags with the name "type", "class", and
-
# "id" for example. In that case, you can use an underscore to
-
# disambiguate your tag name from the method call.
-
#
-
# Here is an example of using the underscore to disambiguate tag names from
-
# ruby methods:
-
#
-
# @objects = [Object.new, Object.new, Object.new]
-
#
-
# builder = Nokogiri::XML::Builder.new do |xml|
-
# xml.root {
-
# xml.objects {
-
# @objects.each do |o|
-
# xml.object {
-
# xml.type_ o.type
-
# xml.class_ o.class.name
-
# xml.id_ o.id
-
# }
-
# end
-
# }
-
# }
-
# end
-
# puts builder.to_xml
-
#
-
# The underscore may be used with any tag name, and the last underscore
-
# will just be removed. This code will output the following XML:
-
#
-
# <?xml version="1.0"?>
-
# <root>
-
# <objects>
-
# <object>
-
# <type>Object</type>
-
# <class>Object</class>
-
# <id>48390</id>
-
# </object>
-
# <object>
-
# <type>Object</type>
-
# <class>Object</class>
-
# <id>48380</id>
-
# </object>
-
# <object>
-
# <type>Object</type>
-
# <class>Object</class>
-
# <id>48370</id>
-
# </object>
-
# </objects>
-
# </root>
-
#
-
# == Tag Attributes
-
#
-
# Tag attributes may be supplied as method arguments. Here is our
-
# previous example, but using attributes rather than tags:
-
#
-
# @objects = [Object.new, Object.new, Object.new]
-
#
-
# builder = Nokogiri::XML::Builder.new do |xml|
-
# xml.root {
-
# xml.objects {
-
# @objects.each do |o|
-
# xml.object(:type => o.type, :class => o.class, :id => o.id)
-
# end
-
# }
-
# }
-
# end
-
# puts builder.to_xml
-
#
-
# === Tag Attribute Short Cuts
-
#
-
# A couple attribute short cuts are available when building tags. The
-
# short cuts are available by special method calls when building a tag.
-
#
-
# This example builds an "object" tag with the class attribute "classy"
-
# and the id of "thing":
-
#
-
# builder = Nokogiri::XML::Builder.new do |xml|
-
# xml.root {
-
# xml.objects {
-
# xml.object.classy.thing!
-
# }
-
# }
-
# end
-
# puts builder.to_xml
-
#
-
# Which will output:
-
#
-
# <?xml version="1.0"?>
-
# <root>
-
# <objects>
-
# <object class="classy" id="thing"/>
-
# </objects>
-
# </root>
-
#
-
# All other options are still supported with this syntax, including
-
# blocks and extra tag attributes.
-
#
-
# == Namespaces
-
#
-
# Namespaces are added similarly to attributes. Nokogiri::XML::Builder
-
# assumes that when an attribute starts with "xmlns", it is meant to be
-
# a namespace:
-
#
-
# builder = Nokogiri::XML::Builder.new { |xml|
-
# xml.root('xmlns' => 'default', 'xmlns:foo' => 'bar') do
-
# xml.tenderlove
-
# end
-
# }
-
# puts builder.to_xml
-
#
-
# Will output XML like this:
-
#
-
# <?xml version="1.0"?>
-
# <root xmlns:foo="bar" xmlns="default">
-
# <tenderlove/>
-
# </root>
-
#
-
# === Referencing declared namespaces
-
#
-
# Tags that reference non-default namespaces (i.e. a tag "foo:bar") can be
-
# built by using the Nokogiri::XML::Builder#[] method.
-
#
-
# For example:
-
#
-
# builder = Nokogiri::XML::Builder.new do |xml|
-
# xml.root('xmlns:foo' => 'bar') {
-
# xml.objects {
-
# xml['foo'].object.classy.thing!
-
# }
-
# }
-
# end
-
# puts builder.to_xml
-
#
-
# Will output this XML:
-
#
-
# <?xml version="1.0"?>
-
# <root xmlns:foo="bar">
-
# <objects>
-
# <foo:object class="classy" id="thing"/>
-
# </objects>
-
# </root>
-
#
-
# Note the "foo:object" tag.
-
#
-
# == Document Types
-
#
-
# To create a document type (DTD), access use the Builder#doc method to get
-
# the current context document. Then call Node#create_internal_subset to
-
# create the DTD node.
-
#
-
# For example, this Ruby:
-
#
-
# builder = Nokogiri::XML::Builder.new do |xml|
-
# xml.doc.create_internal_subset(
-
# 'html',
-
# "-//W3C//DTD HTML 4.01 Transitional//EN",
-
# "http://www.w3.org/TR/html4/loose.dtd"
-
# )
-
# xml.root do
-
# xml.foo
-
# end
-
# end
-
#
-
# puts builder.to_xml
-
#
-
# Will output this xml:
-
#
-
# <?xml version="1.0"?>
-
# <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-
# <root>
-
# <foo/>
-
# </root>
-
#
-
1
class Builder
-
# The current Document object being built
-
1
attr_accessor :doc
-
-
# The parent of the current node being built
-
1
attr_accessor :parent
-
-
# A context object for use when the block has no arguments
-
1
attr_accessor :context
-
-
1
attr_accessor :arity # :nodoc:
-
-
###
-
# Create a builder with an existing root object. This is for use when
-
# you have an existing document that you would like to augment with
-
# builder methods. The builder context created will start with the
-
# given +root+ node.
-
#
-
# For example:
-
#
-
# doc = Nokogiri::XML(open('somedoc.xml'))
-
# Nokogiri::XML::Builder.with(doc.at('some_tag')) do |xml|
-
# # ... Use normal builder methods here ...
-
# xml.awesome # add the "awesome" tag below "some_tag"
-
# end
-
#
-
1
def self.with root, &block
-
new({}, root, &block)
-
end
-
-
###
-
# Create a new Builder object. +options+ are sent to the top level
-
# Document that is being built.
-
#
-
# Building a document with a particular encoding for example:
-
#
-
# Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
-
# ...
-
# end
-
1
def initialize options = {}, root = nil, &block
-
-
if root
-
@doc = root.document
-
@parent = root
-
else
-
namespace = self.class.name.split('::')
-
namespace[-1] = 'Document'
-
@doc = eval(namespace.join('::')).new
-
@parent = @doc
-
end
-
-
@context = nil
-
@arity = nil
-
@ns = nil
-
-
options.each do |k,v|
-
@doc.send(:"#{k}=", v)
-
end
-
-
return unless block_given?
-
-
@arity = block.arity
-
if @arity <= 0
-
@context = eval('self', block.binding)
-
instance_eval(&block)
-
else
-
yield self
-
end
-
-
@parent = @doc
-
end
-
-
###
-
# Create a Text Node with content of +string+
-
1
def text string
-
insert @doc.create_text_node(string)
-
end
-
-
###
-
# Create a CDATA Node with content of +string+
-
1
def cdata string
-
insert doc.create_cdata(string)
-
end
-
-
###
-
# Create a Comment Node with content of +string+
-
1
def comment string
-
insert doc.create_comment(string)
-
end
-
-
###
-
# Build a tag that is associated with namespace +ns+. Raises an
-
# ArgumentError if +ns+ has not been defined higher in the tree.
-
1
def [] ns
-
if @parent != @doc
-
@ns = @parent.namespace_definitions.find { |x| x.prefix == ns.to_s }
-
end
-
return self if @ns
-
-
@parent.ancestors.each do |a|
-
next if a == doc
-
@ns = a.namespace_definitions.find { |x| x.prefix == ns.to_s }
-
return self if @ns
-
end
-
-
@ns = { :pending => ns.to_s }
-
return self
-
end
-
-
###
-
# Convert this Builder object to XML
-
1
def to_xml(*args)
-
if Nokogiri.jruby?
-
options = args.first.is_a?(Hash) ? args.shift : {}
-
if !options[:save_with]
-
options[:save_with] = Node::SaveOptions::AS_BUILDER
-
end
-
args.insert(0, options)
-
end
-
@doc.to_xml(*args)
-
end
-
-
###
-
# Append the given raw XML +string+ to the document
-
1
def << string
-
@doc.fragment(string).children.each { |x| insert(x) }
-
end
-
-
1
def method_missing method, *args, &block # :nodoc:
-
if @context && @context.respond_to?(method)
-
@context.send(method, *args, &block)
-
else
-
node = @doc.create_element(method.to_s.sub(/[_!]$/, ''),*args) { |n|
-
# Set up the namespace
-
if @ns.is_a? Nokogiri::XML::Namespace
-
n.namespace = @ns
-
@ns = nil
-
end
-
}
-
-
if @ns.is_a? Hash
-
node.namespace = node.namespace_definitions.find { |x| x.prefix == @ns[:pending] }
-
if node.namespace.nil?
-
raise ArgumentError, "Namespace #{@ns[:pending]} has not been defined"
-
end
-
@ns = nil
-
end
-
-
insert(node, &block)
-
end
-
end
-
-
1
private
-
###
-
# Insert +node+ as a child of the current Node
-
1
def insert(node, &block)
-
node = @parent.add_child(node)
-
if block_given?
-
old_parent = @parent
-
@parent = node
-
@arity ||= block.arity
-
if @arity <= 0
-
instance_eval(&block)
-
else
-
block.call(self)
-
end
-
@parent = old_parent
-
end
-
NodeBuilder.new(node, self)
-
end
-
-
1
class NodeBuilder # :nodoc:
-
1
def initialize node, doc_builder
-
@node = node
-
@doc_builder = doc_builder
-
end
-
-
1
def []= k, v
-
@node[k] = v
-
end
-
-
1
def [] k
-
@node[k]
-
end
-
-
1
def method_missing(method, *args, &block)
-
opts = args.last.is_a?(Hash) ? args.pop : {}
-
case method.to_s
-
when /^(.*)!$/
-
@node['id'] = $1
-
@node.content = args.first if args.first
-
when /^(.*)=/
-
@node[$1] = args.first
-
else
-
@node['class'] =
-
((@node['class'] || '').split(/\s/) + [method.to_s]).join(' ')
-
@node.content = args.first if args.first
-
end
-
-
# Assign any extra options
-
opts.each do |k,v|
-
@node[k.to_s] = ((@node[k.to_s] || '').split(/\s/) + [v]).join(' ')
-
end
-
-
if block_given?
-
old_parent = @doc_builder.parent
-
@doc_builder.parent = @node
-
value = @doc_builder.instance_eval(&block)
-
@doc_builder.parent = old_parent
-
return value
-
end
-
self
-
end
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
1
class CDATA < Nokogiri::XML::Text
-
###
-
# Get the name of this CDATA node
-
1
def name
-
'#cdata-section'
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
1
class CharacterData < Nokogiri::XML::Node
-
1
include Nokogiri::XML::PP::CharacterData
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
##
-
# Nokogiri::XML::Document is the main entry point for dealing with
-
# XML documents. The Document is created by parsing an XML document.
-
# See Nokogiri::XML::Document.parse() for more information on parsing.
-
#
-
# For searching a Document, see Nokogiri::XML::Node#css and
-
# Nokogiri::XML::Node#xpath
-
#
-
1
class Document < Nokogiri::XML::Node
-
# I'm ignoring unicode characters here.
-
# See http://www.w3.org/TR/REC-xml-names/#ns-decl for more details.
-
1
NCNAME_START_CHAR = "A-Za-z_"
-
1
NCNAME_CHAR = NCNAME_START_CHAR + "\\-.0-9"
-
1
NCNAME_RE = /^xmlns(:[#{NCNAME_START_CHAR}][#{NCNAME_CHAR}]*)?$/
-
-
##
-
# Parse an XML file.
-
#
-
# +string_or_io+ may be a String, or any object that responds to
-
# _read_ and _close_ such as an IO, or StringIO.
-
#
-
# +url+ (optional) is the URI where this document is located.
-
#
-
# +encoding+ (optional) is the encoding that should be used when processing
-
# the document.
-
#
-
# +options+ (optional) is a configuration object that sets options during
-
# parsing, such as Nokogiri::XML::ParseOptions::RECOVER. See the
-
# Nokogiri::XML::ParseOptions for more information.
-
#
-
# +block+ (optional) is passed a configuration object on which
-
# parse options may be set.
-
#
-
# When parsing untrusted documents, it's recommended that the
-
# +nonet+ option be used, as shown in this example code:
-
#
-
# Nokogiri::XML::Document.parse(xml_string) { |config| config.nonet }
-
#
-
# Nokogiri.XML() is a convenience method which will call this method.
-
#
-
1
def self.parse string_or_io, url = nil, encoding = nil, options = ParseOptions::DEFAULT_XML, &block
-
options = Nokogiri::XML::ParseOptions.new(options) if Fixnum === options
-
# Give the options to the user
-
yield options if block_given?
-
-
return new if !options.strict? && empty_doc?(string_or_io)
-
-
doc = if string_or_io.respond_to?(:read)
-
url ||= string_or_io.respond_to?(:path) ? string_or_io.path : nil
-
read_io(string_or_io, url, encoding, options.to_i)
-
else
-
# read_memory pukes on empty docs
-
read_memory(string_or_io, url, encoding, options.to_i)
-
end
-
-
# do xinclude processing
-
doc.do_xinclude(options) if options.xinclude?
-
-
return doc
-
end
-
-
# A list of Nokogiri::XML::SyntaxError found when parsing a document
-
1
attr_accessor :errors
-
-
1
def initialize *args # :nodoc:
-
@errors = []
-
@decorators = nil
-
end
-
-
##
-
# Create an element with +name+, and optionally setting the content and attributes.
-
#
-
# doc.create_element "div" # <div></div>
-
# doc.create_element "div", :class => "container" # <div class='container'></div>
-
# doc.create_element "div", "contents" # <div>contents</div>
-
# doc.create_element "div", "contents", :class => "container" # <div class='container'>contents</div>
-
# doc.create_element "div" { |node| node['class'] = "container" } # <div class='container'></div>
-
#
-
1
def create_element name, *args, &block
-
elm = Nokogiri::XML::Element.new(name, self, &block)
-
args.each do |arg|
-
case arg
-
when Hash
-
arg.each { |k,v|
-
key = k.to_s
-
if key =~ NCNAME_RE
-
ns_name = key.split(":", 2)[1]
-
elm.add_namespace_definition ns_name, v
-
else
-
elm[k.to_s] = v.to_s
-
end
-
}
-
else
-
elm.content = arg
-
end
-
end
-
if ns = elm.namespace_definitions.find { |n| n.prefix.nil? or n.prefix == '' }
-
elm.namespace = ns
-
end
-
elm
-
end
-
-
# Create a Text Node with +string+
-
1
def create_text_node string, &block
-
Nokogiri::XML::Text.new string.to_s, self, &block
-
end
-
-
# Create a CDATA Node containing +string+
-
1
def create_cdata string, &block
-
Nokogiri::XML::CDATA.new self, string.to_s, &block
-
end
-
-
# Create a Comment Node containing +string+
-
1
def create_comment string, &block
-
Nokogiri::XML::Comment.new self, string.to_s, &block
-
end
-
-
# The name of this document. Always returns "document"
-
1
def name
-
'document'
-
end
-
-
# A reference to +self+
-
1
def document
-
self
-
end
-
-
##
-
# Recursively get all namespaces from this node and its subtree and
-
# return them as a hash.
-
#
-
# For example, given this document:
-
#
-
# <root xmlns:foo="bar">
-
# <bar xmlns:hello="world" />
-
# </root>
-
#
-
# This method will return:
-
#
-
# { 'xmlns:foo' => 'bar', 'xmlns:hello' => 'world' }
-
#
-
# WARNING: this method will clobber duplicate names in the keys.
-
# For example, given this document:
-
#
-
# <root xmlns:foo="bar">
-
# <bar xmlns:foo="baz" />
-
# </root>
-
#
-
# The hash returned will look like this: { 'xmlns:foo' => 'bar' }
-
#
-
# Non-prefixed default namespaces (as in "xmlns=") are not included
-
# in the hash.
-
#
-
# Note that this method does an xpath lookup for nodes with
-
# namespaces, and as a result the order may be dependent on the
-
# implementation of the underlying XML library.
-
#
-
1
def collect_namespaces
-
xpath("//namespace::*").inject({}) do |hash, ns|
-
hash[["xmlns",ns.prefix].compact.join(":")] = ns.href if ns.prefix != "xml"
-
hash
-
end
-
end
-
-
# Get the list of decorators given +key+
-
1
def decorators key
-
@decorators ||= Hash.new
-
@decorators[key] ||= []
-
end
-
-
##
-
# Validate this Document against it's DTD. Returns a list of errors on
-
# the document or +nil+ when there is no DTD.
-
1
def validate
-
return nil unless internal_subset
-
internal_subset.validate self
-
end
-
-
##
-
# Explore a document with shortcut methods. See Nokogiri::Slop for details.
-
#
-
# Note that any nodes that have been instantiated before #slop!
-
# is called will not be decorated with sloppy behavior. So, if you're in
-
# irb, the preferred idiom is:
-
#
-
# irb> doc = Nokogiri::Slop my_markup
-
#
-
# and not
-
#
-
# irb> doc = Nokogiri::HTML my_markup
-
# ... followed by irb's implicit inspect (and therefore instantiation of every node) ...
-
# irb> doc.slop!
-
# ... which does absolutely nothing.
-
#
-
1
def slop!
-
unless decorators(XML::Node).include? Nokogiri::Decorators::Slop
-
decorators(XML::Node) << Nokogiri::Decorators::Slop
-
decorate!
-
end
-
-
self
-
end
-
-
##
-
# Apply any decorators to +node+
-
1
def decorate node
-
return unless @decorators
-
@decorators.each { |klass,list|
-
next unless node.is_a?(klass)
-
list.each { |moodule| node.extend(moodule) }
-
}
-
end
-
-
1
alias :to_xml :serialize
-
1
alias :clone :dup
-
-
# Get the hash of namespaces on the root Nokogiri::XML::Node
-
1
def namespaces
-
root ? root.namespaces : {}
-
end
-
-
##
-
# Create a Nokogiri::XML::DocumentFragment from +tags+
-
# Returns an empty fragment if +tags+ is nil.
-
1
def fragment tags = nil
-
DocumentFragment.new(self, tags, self.root)
-
end
-
-
1
undef_method :swap, :parent, :namespace, :default_namespace=
-
1
undef_method :add_namespace_definition, :attributes
-
1
undef_method :namespace_definitions, :line, :add_namespace
-
-
1
def add_child node_or_tags
-
raise "Document already has a root node" if root && root.name != 'nokogiri_text_wrapper'
-
node_or_tags = coerce(node_or_tags)
-
if node_or_tags.is_a?(XML::NodeSet)
-
raise "Document cannot have multiple root nodes" if node_or_tags.size > 1
-
super(node_or_tags.first)
-
else
-
super
-
end
-
end
-
1
alias :<< :add_child
-
-
##
-
# +JRuby+
-
# Wraps Java's org.w3c.dom.document and returns Nokogiri::XML::Document
-
1
def self.wrap document
-
raise "JRuby only method" unless Nokogiri.jruby?
-
return wrapJavaDocument(document)
-
end
-
-
##
-
# +JRuby+
-
# Returns Java's org.w3c.dom.document of this Document.
-
1
def to_java
-
raise "JRuby only method" unless Nokogiri.jruby?
-
return toJavaDocument()
-
end
-
-
1
private
-
1
def self.empty_doc? string_or_io
-
string_or_io.nil? ||
-
(string_or_io.respond_to?(:empty?) && string_or_io.empty?) ||
-
(string_or_io.respond_to?(:eof?) && string_or_io.eof?)
-
end
-
-
1
def implied_xpath_context
-
"/"
-
end
-
-
1
def inspect_attributes
-
[:name, :children]
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
1
class DTD < Nokogiri::XML::Node
-
1
undef_method :attribute_nodes
-
1
undef_method :values
-
1
undef_method :content
-
1
undef_method :namespace
-
1
undef_method :namespace_definitions
-
1
undef_method :line if method_defined?(:line)
-
-
1
def keys
-
attributes.keys
-
end
-
-
1
def each &block
-
attributes.each { |key, value|
-
block.call([key, value])
-
}
-
end
-
-
1
def html_dtd?
-
name.casecmp('html').zero?
-
end
-
-
1
def html5_dtd?
-
html_dtd? &&
-
external_id.nil? &&
-
(system_id.nil? || system_id == 'about:legacy-compat')
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
###
-
# Represents the allowed content in an Element Declaration inside a DTD:
-
#
-
# <?xml version="1.0"?><?TEST-STYLE PIDATA?>
-
# <!DOCTYPE staff SYSTEM "staff.dtd" [
-
# <!ELEMENT div1 (head, (p | list | note)*, div2*)>
-
# ]>
-
# </root>
-
#
-
# ElementContent represents the tree inside the <!ELEMENT> tag shown above
-
# that lists the possible content for the div1 tag.
-
1
class ElementContent
-
# Possible definitions of type
-
1
PCDATA = 1
-
1
ELEMENT = 2
-
1
SEQ = 3
-
1
OR = 4
-
-
# Possible content occurrences
-
1
ONCE = 1
-
1
OPT = 2
-
1
MULT = 3
-
1
PLUS = 4
-
-
1
attr_reader :document
-
-
###
-
# Get the children of this ElementContent node
-
1
def children
-
[c1, c2].compact
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
1
class ElementDecl < Nokogiri::XML::Node
-
1
undef_method :namespace
-
1
undef_method :namespace_definitions
-
1
undef_method :line if method_defined?(:line)
-
-
1
def inspect
-
"#<#{self.class.name}:#{sprintf("0x%x", object_id)} #{to_s.inspect}>"
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
1
class EntityDecl < Nokogiri::XML::Node
-
1
undef_method :attribute_nodes
-
1
undef_method :attributes
-
1
undef_method :namespace
-
1
undef_method :namespace_definitions
-
1
undef_method :line if method_defined?(:line)
-
-
1
def self.new name, doc, *args
-
doc.create_entity(name, *args)
-
end
-
-
1
def inspect
-
"#<#{self.class.name}:#{sprintf("0x%x", object_id)} #{to_s.inspect}>"
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
1
class Namespace
-
1
include Nokogiri::XML::PP::Node
-
1
attr_reader :document
-
-
1
private
-
1
def inspect_attributes
-
[:prefix, :href]
-
end
-
end
-
end
-
end
-
1
require 'stringio'
-
1
require 'nokogiri/xml/node/save_options'
-
-
1
module Nokogiri
-
1
module XML
-
####
-
# Nokogiri::XML::Node is your window to the fun filled world of dealing
-
# with XML and HTML tags. A Nokogiri::XML::Node may be treated similarly
-
# to a hash with regard to attributes. For example (from irb):
-
#
-
# irb(main):004:0> node
-
# => <a href="#foo" id="link">link</a>
-
# irb(main):005:0> node['href']
-
# => "#foo"
-
# irb(main):006:0> node.keys
-
# => ["href", "id"]
-
# irb(main):007:0> node.values
-
# => ["#foo", "link"]
-
# irb(main):008:0> node['class'] = 'green'
-
# => "green"
-
# irb(main):009:0> node
-
# => <a href="#foo" id="link" class="green">link</a>
-
# irb(main):010:0>
-
#
-
# See Nokogiri::XML::Node#[] and Nokogiri::XML#[]= for more information.
-
#
-
# Nokogiri::XML::Node also has methods that let you move around your
-
# tree. For navigating your tree, see:
-
#
-
# * Nokogiri::XML::Node#parent
-
# * Nokogiri::XML::Node#children
-
# * Nokogiri::XML::Node#next
-
# * Nokogiri::XML::Node#previous
-
#
-
# You may search this node's subtree using Node#xpath and Node#css
-
1
class Node
-
1
include Nokogiri::XML::PP::Node
-
1
include Enumerable
-
-
# Element node type, see Nokogiri::XML::Node#element?
-
1
ELEMENT_NODE = 1
-
# Attribute node type
-
1
ATTRIBUTE_NODE = 2
-
# Text node type, see Nokogiri::XML::Node#text?
-
1
TEXT_NODE = 3
-
# CDATA node type, see Nokogiri::XML::Node#cdata?
-
1
CDATA_SECTION_NODE = 4
-
# Entity reference node type
-
1
ENTITY_REF_NODE = 5
-
# Entity node type
-
1
ENTITY_NODE = 6
-
# PI node type
-
1
PI_NODE = 7
-
# Comment node type, see Nokogiri::XML::Node#comment?
-
1
COMMENT_NODE = 8
-
# Document node type, see Nokogiri::XML::Node#xml?
-
1
DOCUMENT_NODE = 9
-
# Document type node type
-
1
DOCUMENT_TYPE_NODE = 10
-
# Document fragment node type
-
1
DOCUMENT_FRAG_NODE = 11
-
# Notation node type
-
1
NOTATION_NODE = 12
-
# HTML document node type, see Nokogiri::XML::Node#html?
-
1
HTML_DOCUMENT_NODE = 13
-
# DTD node type
-
1
DTD_NODE = 14
-
# Element declaration type
-
1
ELEMENT_DECL = 15
-
# Attribute declaration type
-
1
ATTRIBUTE_DECL = 16
-
# Entity declaration type
-
1
ENTITY_DECL = 17
-
# Namespace declaration type
-
1
NAMESPACE_DECL = 18
-
# XInclude start type
-
1
XINCLUDE_START = 19
-
# XInclude end type
-
1
XINCLUDE_END = 20
-
# DOCB document node type
-
1
DOCB_DOCUMENT_NODE = 21
-
-
1
def initialize name, document # :nodoc:
-
# ... Ya. This is empty on purpose.
-
end
-
-
###
-
# Decorate this node with the decorators set up in this node's Document
-
1
def decorate!
-
document.decorate(self)
-
end
-
-
###
-
# Search this node for +paths+. +paths+ can be XPath or CSS, and an
-
# optional hash of namespaces may be appended.
-
# See Node#xpath and Node#css.
-
1
def search *paths
-
# TODO use paths, handler, ns, binds = extract_params(paths)
-
ns = paths.last.is_a?(Hash) ? paths.pop :
-
(document.root ? document.root.namespaces : {})
-
-
prefix = "#{implied_xpath_context}/"
-
-
xpath(*(paths.map { |path|
-
path = path.to_s
-
path =~ /^(\.\/|\/|\.\.|\.$)/ ? path : CSS.xpath_for(
-
path,
-
:prefix => prefix,
-
:ns => ns
-
)
-
}.flatten.uniq) + [ns])
-
end
-
1
alias :/ :search
-
-
###
-
# call-seq: xpath *paths, [namespace-bindings, variable-bindings, custom-handler-class]
-
#
-
# Search this node for XPath +paths+. +paths+ must be one or more XPath
-
# queries.
-
#
-
# node.xpath('.//title')
-
#
-
# A hash of namespace bindings may be appended. For example:
-
#
-
# node.xpath('.//foo:name', {'foo' => 'http://example.org/'})
-
# node.xpath('.//xmlns:name', node.root.namespaces)
-
#
-
# A hash of variable bindings may also be appended to the namespace bindings. For example:
-
#
-
# node.xpath('.//address[@domestic=$value]', nil, {:value => 'Yes'})
-
#
-
# Custom XPath functions may also be defined. To define custom
-
# functions create a class and implement the function you want
-
# to define. The first argument to the method will be the
-
# current matching NodeSet. Any other arguments are ones that
-
# you pass in. Note that this class may appear anywhere in the
-
# argument list. For example:
-
#
-
# node.xpath('.//title[regex(., "\w+")]', Class.new {
-
# def regex node_set, regex
-
# node_set.find_all { |node| node['some_attribute'] =~ /#{regex}/ }
-
# end
-
# }.new)
-
#
-
1
def xpath *paths
-
return NodeSet.new(document) unless document
-
-
paths, handler, ns, binds = extract_params(paths)
-
-
sets = paths.map { |path|
-
ctx = XPathContext.new(self)
-
ctx.register_namespaces(ns)
-
path = path.gsub(/xmlns:/, ' :') unless Nokogiri.uses_libxml?
-
-
binds.each do |key,value|
-
ctx.register_variable key.to_s, value
-
end if binds
-
-
ctx.evaluate(path, handler)
-
}
-
return sets.first if sets.length == 1
-
-
NodeSet.new(document) do |combined|
-
sets.each do |set|
-
set.each do |node|
-
combined << node
-
end
-
end
-
end
-
end
-
-
###
-
# call-seq: css *rules, [namespace-bindings, custom-pseudo-class]
-
#
-
# Search this node for CSS +rules+. +rules+ must be one or more CSS
-
# selectors. For example:
-
#
-
# node.css('title')
-
# node.css('body h1.bold')
-
# node.css('div + p.green', 'div#one')
-
#
-
# A hash of namespace bindings may be appended. For example:
-
#
-
# node.css('bike|tire', {'bike' => 'http://schwinn.com/'})
-
#
-
# Custom CSS pseudo classes may also be defined. To define
-
# custom pseudo classes, create a class and implement the custom
-
# pseudo class you want defined. The first argument to the
-
# method will be the current matching NodeSet. Any other
-
# arguments are ones that you pass in. For example:
-
#
-
# node.css('title:regex("\w+")', Class.new {
-
# def regex node_set, regex
-
# node_set.find_all { |node| node['some_attribute'] =~ /#{regex}/ }
-
# end
-
# }.new)
-
#
-
# Note that the CSS query string is case-sensitive with regards
-
# to your document type. That is, if you're looking for "H1" in
-
# an HTML document, you'll never find anything, since HTML tags
-
# will match only lowercase CSS queries. However, "H1" might be
-
# found in an XML document, where tags names are case-sensitive
-
# (e.g., "H1" is distinct from "h1").
-
#
-
1
def css *rules
-
rules, handler, ns, binds = extract_params(rules)
-
-
prefix = "#{implied_xpath_context}/"
-
-
rules = rules.map { |rule|
-
CSS.xpath_for(rule, :prefix => prefix, :ns => ns)
-
}.flatten.uniq + [ns, handler, binds].compact
-
-
xpath(*rules)
-
end
-
-
###
-
# Search this node's immediate children using CSS selector +selector+
-
1
def > selector
-
ns = document.root.namespaces
-
xpath CSS.xpath_for(selector, :prefix => "./", :ns => ns).first
-
end
-
-
###
-
# Search for the first occurrence of +path+.
-
#
-
# Returns nil if nothing is found, otherwise a Node.
-
1
def at path, ns = document.root ? document.root.namespaces : {}
-
search(path, ns).first
-
end
-
1
alias :% :at
-
-
##
-
# Search this node for the first occurrence of XPath +paths+.
-
# Equivalent to <tt>xpath(paths).first</tt>
-
# See Node#xpath for more information.
-
#
-
1
def at_xpath *paths
-
xpath(*paths).first
-
end
-
-
##
-
# Search this node for the first occurrence of CSS +rules+.
-
# Equivalent to <tt>css(rules).first</tt>
-
# See Node#css for more information.
-
#
-
1
def at_css *rules
-
css(*rules).first
-
end
-
-
###
-
# Get the attribute value for the attribute +name+
-
1
def [] name
-
get(name.to_s)
-
end
-
-
###
-
# Set the attribute value for the attribute +name+ to +value+
-
1
def []= name, value
-
set name.to_s, value.to_s
-
end
-
-
###
-
# Add +node_or_tags+ as a child of this Node.
-
# +node_or_tags+ can be a Nokogiri::XML::Node, a ::DocumentFragment, a ::NodeSet, or a string containing markup.
-
#
-
# Returns the reparented node (if +node_or_tags+ is a Node), or NodeSet (if +node_or_tags+ is a DocumentFragment, NodeSet, or string).
-
#
-
# Also see related method +<<+.
-
1
def add_child node_or_tags
-
node_or_tags = coerce(node_or_tags)
-
if node_or_tags.is_a?(XML::NodeSet)
-
node_or_tags.each { |n| add_child_node_and_reparent_attrs n }
-
else
-
add_child_node_and_reparent_attrs node_or_tags
-
end
-
node_or_tags
-
end
-
-
###
-
# Add +node_or_tags+ as the first child of this Node.
-
# +node_or_tags+ can be a Nokogiri::XML::Node, a ::DocumentFragment, a ::NodeSet, or a string containing markup.
-
#
-
# Returns the reparented node (if +node_or_tags+ is a Node), or NodeSet (if +node_or_tags+ is a DocumentFragment, NodeSet, or string).
-
#
-
# Also see related method +add_child+.
-
1
def prepend_child node_or_tags
-
if first = children.first
-
# Mimic the error add_child would raise.
-
raise RuntimeError, "Document already has a root node" if document? && !node_or_tags.processing_instruction?
-
first.__send__(:add_sibling, :previous, node_or_tags)
-
else
-
add_child(node_or_tags)
-
end
-
end
-
-
###
-
# Add +node_or_tags+ as a child of this Node.
-
# +node_or_tags+ can be a Nokogiri::XML::Node, a ::DocumentFragment, a ::NodeSet, or a string containing markup.
-
#
-
# Returns self, to support chaining of calls (e.g., root << child1 << child2)
-
#
-
# Also see related method +add_child+.
-
1
def << node_or_tags
-
add_child node_or_tags
-
self
-
end
-
###
-
# Insert +node_or_tags+ before this Node (as a sibling).
-
# +node_or_tags+ can be a Nokogiri::XML::Node, a ::DocumentFragment, a ::NodeSet, or a string containing markup.
-
#
-
# Returns the reparented node (if +node_or_tags+ is a Node), or NodeSet (if +node_or_tags+ is a DocumentFragment, NodeSet, or string).
-
#
-
# Also see related method +before+.
-
1
def add_previous_sibling node_or_tags
-
raise ArgumentError.new("A document may not have multiple root nodes.") if (parent && parent.document?) && !node_or_tags.processing_instruction?
-
-
add_sibling :previous, node_or_tags
-
end
-
-
###
-
# Insert +node_or_tags+ after this Node (as a sibling).
-
# +node_or_tags+ can be a Nokogiri::XML::Node, a ::DocumentFragment, a ::NodeSet, or a string containing markup.
-
#
-
# Returns the reparented node (if +node_or_tags+ is a Node), or NodeSet (if +node_or_tags+ is a DocumentFragment, NodeSet, or string).
-
#
-
# Also see related method +after+.
-
1
def add_next_sibling node_or_tags
-
raise ArgumentError.new("A document may not have multiple root nodes.") if (parent && parent.document?) && !node_or_tags.processing_instruction?
-
-
add_sibling :next, node_or_tags
-
end
-
-
####
-
# Insert +node_or_tags+ before this node (as a sibling).
-
# +node_or_tags+ can be a Nokogiri::XML::Node, a ::DocumentFragment, a ::NodeSet, or a string containing markup.
-
#
-
# Returns self, to support chaining of calls.
-
#
-
# Also see related method +add_previous_sibling+.
-
1
def before node_or_tags
-
add_previous_sibling node_or_tags
-
self
-
end
-
-
####
-
# Insert +node_or_tags+ after this node (as a sibling).
-
# +node_or_tags+ can be a Nokogiri::XML::Node, a Nokogiri::XML::DocumentFragment, or a string containing markup.
-
#
-
# Returns self, to support chaining of calls.
-
#
-
# Also see related method +add_next_sibling+.
-
1
def after node_or_tags
-
add_next_sibling node_or_tags
-
self
-
end
-
-
####
-
# Set the inner html for this Node to +node_or_tags+
-
# +node_or_tags+ can be a Nokogiri::XML::Node, a Nokogiri::XML::DocumentFragment, or a string containing markup.
-
#
-
# Returns self.
-
#
-
# Also see related method +children=+
-
1
def inner_html= node_or_tags
-
self.children = node_or_tags
-
self
-
end
-
-
####
-
# Set the inner html for this Node +node_or_tags+
-
# +node_or_tags+ can be a Nokogiri::XML::Node, a Nokogiri::XML::DocumentFragment, or a string containing markup.
-
#
-
# Returns the reparented node (if +node_or_tags+ is a Node), or NodeSet (if +node_or_tags+ is a DocumentFragment, NodeSet, or string).
-
#
-
# Also see related method +inner_html=+
-
1
def children= node_or_tags
-
node_or_tags = coerce(node_or_tags)
-
children.unlink
-
if node_or_tags.is_a?(XML::NodeSet)
-
node_or_tags.each { |n| add_child_node_and_reparent_attrs n }
-
else
-
add_child_node_and_reparent_attrs node_or_tags
-
end
-
node_or_tags
-
end
-
-
####
-
# Replace this Node with +node_or_tags+.
-
# +node_or_tags+ can be a Nokogiri::XML::Node, a ::DocumentFragment, a ::NodeSet, or a string containing markup.
-
#
-
# Returns the reparented node (if +node_or_tags+ is a Node), or NodeSet (if +node_or_tags+ is a DocumentFragment, NodeSet, or string).
-
#
-
# Also see related method +swap+.
-
1
def replace node_or_tags
-
# We cannot replace a text node directly, otherwise libxml will return
-
# an internal error at parser.c:13031, I don't know exactly why
-
# libxml is trying to find a parent node that is an element or document
-
# so I can't tell if this is bug in libxml or not. issue #775.
-
if text?
-
replacee = Nokogiri::XML::Node.new 'dummy', document
-
add_previous_sibling_node replacee
-
unlink
-
return replacee.replace node_or_tags
-
end
-
-
node_or_tags = coerce(node_or_tags)
-
-
if node_or_tags.is_a?(XML::NodeSet)
-
node_or_tags.each { |n| add_previous_sibling n }
-
unlink
-
else
-
replace_node node_or_tags
-
end
-
node_or_tags
-
end
-
-
####
-
# Swap this Node for +node_or_tags+
-
# +node_or_tags+ can be a Nokogiri::XML::Node, a ::DocumentFragment, a ::NodeSet, or a string containing markup.
-
#
-
# Returns self, to support chaining of calls.
-
#
-
# Also see related method +replace+.
-
1
def swap node_or_tags
-
replace node_or_tags
-
self
-
end
-
-
1
alias :next :next_sibling
-
1
alias :previous :previous_sibling
-
-
# :stopdoc:
-
# HACK: This is to work around an RDoc bug
-
1
alias :next= :add_next_sibling
-
# :startdoc:
-
-
1
alias :previous= :add_previous_sibling
-
1
alias :remove :unlink
-
1
alias :get_attribute :[]
-
1
alias :attr :[]
-
1
alias :set_attribute :[]=
-
1
alias :text :content
-
1
alias :inner_text :content
-
1
alias :has_attribute? :key?
-
1
alias :name :node_name
-
1
alias :name= :node_name=
-
1
alias :type :node_type
-
1
alias :to_str :text
-
1
alias :clone :dup
-
1
alias :elements :element_children
-
-
####
-
# Returns a hash containing the node's attributes. The key is
-
# the attribute name without any namespace, the value is a Nokogiri::XML::Attr
-
# representing the attribute.
-
# If you need to distinguish attributes with the same name, with different namespaces
-
# use #attribute_nodes instead.
-
1
def attributes
-
Hash[attribute_nodes.map { |node|
-
[node.node_name, node]
-
}]
-
end
-
-
###
-
# Get the attribute values for this Node.
-
1
def values
-
attribute_nodes.map { |node| node.value }
-
end
-
-
###
-
# Get the attribute names for this Node.
-
1
def keys
-
attribute_nodes.map { |node| node.node_name }
-
end
-
-
###
-
# Iterate over each attribute name and value pair for this Node.
-
1
def each
-
attribute_nodes.each { |node|
-
yield [node.node_name, node.value]
-
}
-
end
-
-
###
-
# Remove the attribute named +name+
-
1
def remove_attribute name
-
attributes[name].remove if key? name
-
end
-
1
alias :delete :remove_attribute
-
-
###
-
# Returns true if this Node matches +selector+
-
1
def matches? selector
-
ancestors.last.search(selector).include?(self)
-
end
-
-
###
-
# Create a DocumentFragment containing +tags+ that is relative to _this_
-
# context node.
-
1
def fragment tags
-
type = document.html? ? Nokogiri::HTML : Nokogiri::XML
-
type::DocumentFragment.new(document, tags, self)
-
end
-
-
###
-
# Parse +string_or_io+ as a document fragment within the context of
-
# *this* node. Returns a XML::NodeSet containing the nodes parsed from
-
# +string_or_io+.
-
1
def parse string_or_io, options = nil
-
##
-
# When the current node is unparented and not an element node, use the
-
# document as the parsing context instead. Otherwise, the in-context
-
# parser cannot find an element or a document node.
-
# Document Fragments are also not usable by the in-context parser.
-
if !element? && !document? && (!parent || parent.fragment?)
-
return document.parse(string_or_io, options)
-
end
-
-
options ||= (document.html? ? ParseOptions::DEFAULT_HTML : ParseOptions::DEFAULT_XML)
-
if Fixnum === options
-
options = Nokogiri::XML::ParseOptions.new(options)
-
end
-
# Give the options to the user
-
yield options if block_given?
-
-
contents = string_or_io.respond_to?(:read) ?
-
string_or_io.read :
-
string_or_io
-
-
return Nokogiri::XML::NodeSet.new(document) if contents.empty?
-
-
##
-
# This is a horrible hack, but I don't care. See #313 for background.
-
error_count = document.errors.length
-
node_set = in_context(contents, options.to_i)
-
if node_set.empty? and document.errors.length > error_count and options.recover?
-
fragment = Nokogiri::HTML::DocumentFragment.parse contents
-
node_set = fragment.children
-
end
-
node_set
-
end
-
-
####
-
# Set the Node's content to a Text node containing +string+. The string gets XML escaped, not interpreted as markup.
-
1
def content= string
-
self.native_content = encode_special_chars(string.to_s)
-
end
-
-
###
-
# Set the parent Node for this Node
-
1
def parent= parent_node
-
parent_node.add_child(self)
-
parent_node
-
end
-
-
###
-
# Returns a Hash of {prefix => value} for all namespaces on this
-
# node and its ancestors.
-
#
-
# This method returns the same namespaces as #namespace_scopes.
-
#
-
# Returns namespaces in scope for self -- those defined on self
-
# element directly or any ancestor node -- as a Hash of
-
# attribute-name/value pairs. Note that the keys in this hash
-
# XML attributes that would be used to define this namespace,
-
# such as "xmlns:prefix", not just the prefix. Default namespace
-
# set on self will be included with key "xmlns". However,
-
# default namespaces set on ancestor will NOT be, even if self
-
# has no explicit default namespace.
-
1
def namespaces
-
Hash[namespace_scopes.map { |nd|
-
key = ['xmlns', nd.prefix].compact.join(':')
-
if RUBY_VERSION >= '1.9' && document.encoding
-
begin
-
key.force_encoding document.encoding
-
rescue ArgumentError
-
end
-
end
-
[key, nd.href]
-
}]
-
end
-
-
# Returns true if this is a Comment
-
1
def comment?
-
type == COMMENT_NODE
-
end
-
-
# Returns true if this is a CDATA
-
1
def cdata?
-
type == CDATA_SECTION_NODE
-
end
-
-
# Returns true if this is an XML::Document node
-
1
def xml?
-
type == DOCUMENT_NODE
-
end
-
-
# Returns true if this is an HTML::Document node
-
1
def html?
-
type == HTML_DOCUMENT_NODE
-
end
-
-
# Returns true if this is a Document
-
1
def document?
-
is_a? XML::Document
-
end
-
-
# Returns true if this is a ProcessingInstruction node
-
1
def processing_instruction?
-
type == PI_NODE
-
end
-
-
# Returns true if this is a Text node
-
1
def text?
-
type == TEXT_NODE
-
end
-
-
# Returns true if this is a DocumentFragment
-
1
def fragment?
-
type == DOCUMENT_FRAG_NODE
-
end
-
-
###
-
# Fetch the Nokogiri::HTML::ElementDescription for this node. Returns
-
# nil on XML documents and on unknown tags.
-
1
def description
-
return nil if document.xml?
-
Nokogiri::HTML::ElementDescription[name]
-
end
-
-
###
-
# Is this a read only node?
-
1
def read_only?
-
# According to gdome2, these are read-only node types
-
[NOTATION_NODE, ENTITY_NODE, ENTITY_DECL].include?(type)
-
end
-
-
# Returns true if this is an Element node
-
1
def element?
-
type == ELEMENT_NODE
-
end
-
1
alias :elem? :element?
-
-
###
-
# Turn this node in to a string. If the document is HTML, this method
-
# returns html. If the document is XML, this method returns XML.
-
1
def to_s
-
document.xml? ? to_xml : to_html
-
end
-
-
# Get the inner_html for this node's Node#children
-
1
def inner_html *args
-
children.map { |x| x.to_html(*args) }.join
-
end
-
-
# Get the path to this node as a CSS expression
-
1
def css_path
-
path.split(/\//).map { |part|
-
part.length == 0 ? nil : part.gsub(/\[(\d+)\]/, ':nth-of-type(\1)')
-
}.compact.join(' > ')
-
end
-
-
###
-
# Get a list of ancestor Node for this Node. If +selector+ is given,
-
# the ancestors must match +selector+
-
1
def ancestors selector = nil
-
return NodeSet.new(document) unless respond_to?(:parent)
-
return NodeSet.new(document) unless parent
-
-
parents = [parent]
-
-
while parents.last.respond_to?(:parent)
-
break unless ctx_parent = parents.last.parent
-
parents << ctx_parent
-
end
-
-
return NodeSet.new(document, parents) unless selector
-
-
root = parents.last
-
-
NodeSet.new(document, parents.find_all { |parent|
-
root.search(selector).include?(parent)
-
})
-
end
-
-
###
-
# Adds a default namespace supplied as a string +url+ href, to self.
-
# The consequence is as an xmlns attribute with supplied argument were
-
# present in parsed XML. A default namespace set with this method will
-
# now show up in #attributes, but when this node is serialized to XML an
-
# "xmlns" attribute will appear. See also #namespace and #namespace=
-
1
def default_namespace= url
-
add_namespace_definition(nil, url)
-
end
-
1
alias :add_namespace :add_namespace_definition
-
-
###
-
# Set the default namespace on this node (as would be defined with an
-
# "xmlns=" attribute in XML source), as a Namespace object +ns+. Note that
-
# a Namespace added this way will NOT be serialized as an xmlns attribute
-
# for this node. You probably want #default_namespace= instead, or perhaps
-
# #add_namespace_definition with a nil prefix argument.
-
1
def namespace= ns
-
return set_namespace(ns) unless ns
-
-
unless Nokogiri::XML::Namespace === ns
-
raise TypeError, "#{ns.class} can't be coerced into Nokogiri::XML::Namespace"
-
end
-
if ns.document != document
-
raise ArgumentError, 'namespace must be declared on the same document'
-
end
-
-
set_namespace ns
-
end
-
-
####
-
# Yields self and all children to +block+ recursively.
-
1
def traverse &block
-
children.each{|j| j.traverse(&block) }
-
block.call(self)
-
end
-
-
###
-
# Accept a visitor. This method calls "visit" on +visitor+ with self.
-
1
def accept visitor
-
visitor.visit(self)
-
end
-
-
###
-
# Test to see if this Node is equal to +other+
-
1
def == other
-
return false unless other
-
return false unless other.respond_to?(:pointer_id)
-
pointer_id == other.pointer_id
-
end
-
-
###
-
# Serialize Node using +options+. Save options can also be set using a
-
# block. See SaveOptions.
-
#
-
# These two statements are equivalent:
-
#
-
# node.serialize(:encoding => 'UTF-8', :save_with => FORMAT | AS_XML)
-
#
-
# or
-
#
-
# node.serialize(:encoding => 'UTF-8') do |config|
-
# config.format.as_xml
-
# end
-
#
-
1
def serialize *args, &block
-
options = args.first.is_a?(Hash) ? args.shift : {
-
:encoding => args[0],
-
:save_with => args[1]
-
}
-
-
encoding = options[:encoding] || document.encoding
-
options[:encoding] = encoding
-
-
outstring = ""
-
if encoding && outstring.respond_to?(:force_encoding)
-
outstring.force_encoding(Encoding.find(encoding))
-
end
-
io = StringIO.new(outstring)
-
write_to io, options, &block
-
io.string
-
end
-
-
###
-
# Serialize this Node to HTML
-
#
-
# doc.to_html
-
#
-
# See Node#write_to for a list of +options+. For formatted output,
-
# use Node#to_xhtml instead.
-
1
def to_html options = {}
-
to_format SaveOptions::DEFAULT_HTML, options
-
end
-
-
###
-
# Serialize this Node to XML using +options+
-
#
-
# doc.to_xml(:indent => 5, :encoding => 'UTF-8')
-
#
-
# See Node#write_to for a list of +options+
-
1
def to_xml options = {}
-
options[:save_with] ||= SaveOptions::DEFAULT_XML
-
serialize(options)
-
end
-
-
###
-
# Serialize this Node to XHTML using +options+
-
#
-
# doc.to_xhtml(:indent => 5, :encoding => 'UTF-8')
-
#
-
# See Node#write_to for a list of +options+
-
1
def to_xhtml options = {}
-
to_format SaveOptions::DEFAULT_XHTML, options
-
end
-
-
###
-
# Write Node to +io+ with +options+. +options+ modify the output of
-
# this method. Valid options are:
-
#
-
# * +:encoding+ for changing the encoding
-
# * +:indent_text+ the indentation text, defaults to one space
-
# * +:indent+ the number of +:indent_text+ to use, defaults to 2
-
# * +:save_with+ a combination of SaveOptions constants.
-
#
-
# To save with UTF-8 indented twice:
-
#
-
# node.write_to(io, :encoding => 'UTF-8', :indent => 2)
-
#
-
# To save indented with two dashes:
-
#
-
# node.write_to(io, :indent_text => '-', :indent => 2
-
#
-
1
def write_to io, *options
-
options = options.first.is_a?(Hash) ? options.shift : {}
-
encoding = options[:encoding] || options[0]
-
if Nokogiri.jruby?
-
save_options = options[:save_with] || options[1]
-
indent_times = options[:indent] || 0
-
else
-
save_options = options[:save_with] || options[1] || SaveOptions::FORMAT
-
indent_times = options[:indent] || 2
-
end
-
indent_text = options[:indent_text] || ' '
-
-
config = SaveOptions.new(save_options.to_i)
-
yield config if block_given?
-
-
native_write_to(io, encoding, indent_text * indent_times, config.options)
-
end
-
-
###
-
# Write Node as HTML to +io+ with +options+
-
#
-
# See Node#write_to for a list of +options+
-
1
def write_html_to io, options = {}
-
write_format_to SaveOptions::DEFAULT_HTML, io, options
-
end
-
-
###
-
# Write Node as XHTML to +io+ with +options+
-
#
-
# See Node#write_to for a list of +options+
-
1
def write_xhtml_to io, options = {}
-
write_format_to SaveOptions::DEFAULT_XHTML, io, options
-
end
-
-
###
-
# Write Node as XML to +io+ with +options+
-
#
-
# doc.write_xml_to io, :encoding => 'UTF-8'
-
#
-
# See Node#write_to for a list of options
-
1
def write_xml_to io, options = {}
-
options[:save_with] ||= SaveOptions::DEFAULT_XML
-
write_to io, options
-
end
-
-
###
-
# Compare two Node objects with respect to their Document. Nodes from
-
# different documents cannot be compared.
-
1
def <=> other
-
return nil unless other.is_a?(Nokogiri::XML::Node)
-
return nil unless document == other.document
-
compare other
-
end
-
-
###
-
# Do xinclude substitution on the subtree below node. If given a block, a
-
# Nokogiri::XML::ParseOptions object initialized from +options+, will be
-
# passed to it, allowing more convenient modification of the parser options.
-
1
def do_xinclude options = XML::ParseOptions::DEFAULT_XML, &block
-
options = Nokogiri::XML::ParseOptions.new(options) if Fixnum === options
-
-
# give options to user
-
yield options if block_given?
-
-
# call c extension
-
process_xincludes(options.to_i)
-
end
-
-
1
def canonicalize(mode=XML::XML_C14N_1_0,inclusive_namespaces=nil,with_comments=false)
-
c14n_root = self
-
document.canonicalize(mode, inclusive_namespaces, with_comments) do |node, parent|
-
tn = node.is_a?(XML::Node) ? node : parent
-
tn == c14n_root || tn.ancestors.include?(c14n_root)
-
end
-
end
-
-
1
private
-
-
1
def add_sibling next_or_previous, node_or_tags
-
impl = (next_or_previous == :next) ? :add_next_sibling_node : :add_previous_sibling_node
-
iter = (next_or_previous == :next) ? :reverse_each : :each
-
-
node_or_tags = coerce node_or_tags
-
if node_or_tags.is_a?(XML::NodeSet)
-
if text?
-
pivot = Nokogiri::XML::Node.new 'dummy', document
-
send impl, pivot
-
else
-
pivot = self
-
end
-
node_or_tags.send(iter) { |n| pivot.send impl, n }
-
pivot.unlink if text?
-
else
-
send impl, node_or_tags
-
end
-
node_or_tags
-
end
-
-
1
def to_format save_option, options
-
# FIXME: this is a hack around broken libxml versions
-
return dump_html if Nokogiri.uses_libxml? && %w[2 6] === LIBXML_VERSION.split('.')[0..1]
-
-
options[:save_with] = save_option unless options[:save_with]
-
serialize(options)
-
end
-
-
1
def write_format_to save_option, io, options
-
# FIXME: this is a hack around broken libxml versions
-
return (io << dump_html) if Nokogiri.uses_libxml? && %w[2 6] === LIBXML_VERSION.split('.')[0..1]
-
-
options[:save_with] ||= save_option
-
write_to io, options
-
end
-
-
1
def extract_params params # :nodoc:
-
# Pop off our custom function handler if it exists
-
handler = params.find { |param|
-
![Hash, String, Symbol].include?(param.class)
-
}
-
-
params -= [handler] if handler
-
-
hashes = []
-
while Hash === params.last || params.last.nil?
-
hashes << params.pop
-
break if params.empty?
-
end
-
-
ns, binds = hashes.reverse
-
-
ns ||= document.root ? document.root.namespaces : {}
-
-
[params, handler, ns, binds]
-
end
-
-
1
def coerce data # :nodoc:
-
case data
-
when XML::NodeSet
-
return data
-
when XML::DocumentFragment
-
return data.children
-
when String
-
return fragment(data).children
-
when Document, XML::Attr
-
# unacceptable
-
when XML::Node
-
return data
-
end
-
-
raise ArgumentError, <<-EOERR
-
Requires a Node, NodeSet or String argument, and cannot accept a #{data.class}.
-
(You probably want to select a node from the Document with at() or search(), or create a new Node via Node.new().)
-
EOERR
-
end
-
-
1
def implied_xpath_context
-
"./"
-
end
-
-
1
def inspect_attributes
-
[:name, :namespace, :attribute_nodes, :children]
-
end
-
-
1
def add_child_node_and_reparent_attrs node
-
add_child_node node
-
node.attribute_nodes.find_all { |a| a.name =~ /:/ }.each do |attr_node|
-
attr_node.remove
-
node[attr_node.name] = attr_node.value
-
end
-
end
-
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
1
class Node
-
###
-
# Save options for serializing nodes
-
1
class SaveOptions
-
# Format serialized xml
-
1
FORMAT = 1
-
# Do not include declarations
-
1
NO_DECLARATION = 2
-
# Do not include empty tags
-
1
NO_EMPTY_TAGS = 4
-
# Do not save XHTML
-
1
NO_XHTML = 8
-
# Save as XHTML
-
1
AS_XHTML = 16
-
# Save as XML
-
1
AS_XML = 32
-
# Save as HTML
-
1
AS_HTML = 64
-
-
1
if Nokogiri.jruby?
-
# Save builder created document
-
AS_BUILDER = 128
-
# the default for XML documents
-
DEFAULT_XML = AS_XML # https://github.com/sparklemotion/nokogiri/issues/#issue/415
-
# the default for HTML document
-
DEFAULT_HTML = NO_DECLARATION | NO_EMPTY_TAGS | AS_HTML
-
else
-
# the default for XML documents
-
1
DEFAULT_XML = FORMAT | AS_XML
-
# the default for HTML document
-
1
DEFAULT_HTML = FORMAT | NO_DECLARATION | NO_EMPTY_TAGS | AS_HTML
-
end
-
# the default for XHTML document
-
1
DEFAULT_XHTML = FORMAT | NO_DECLARATION | NO_EMPTY_TAGS | AS_XHTML
-
-
# Integer representation of the SaveOptions
-
1
attr_reader :options
-
-
# Create a new SaveOptions object with +options+
-
1
def initialize options = 0; @options = options; end
-
-
1
constants.each do |constant|
-
10
class_eval %{
-
def #{constant.downcase}
-
@options |= #{constant}
-
self
-
end
-
-
def #{constant.downcase}?
-
#{constant} & @options == #{constant}
-
end
-
}
-
end
-
-
1
alias :to_i :options
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
####
-
# A NodeSet contains a list of Nokogiri::XML::Node objects. Typically
-
# a NodeSet is return as a result of searching a Document via
-
# Nokogiri::XML::Node#css or Nokogiri::XML::Node#xpath
-
1
class NodeSet
-
1
include Enumerable
-
-
# The Document this NodeSet is associated with
-
1
attr_accessor :document
-
-
# Create a NodeSet with +document+ defaulting to +list+
-
1
def initialize document, list = []
-
@document = document
-
document.decorate(self)
-
list.each { |x| self << x }
-
yield self if block_given?
-
end
-
-
###
-
# Get the first element of the NodeSet.
-
1
def first n = nil
-
return self[0] unless n
-
list = []
-
n.times { |i| list << self[i] }
-
list
-
end
-
-
###
-
# Get the last element of the NodeSet.
-
1
def last
-
self[-1]
-
end
-
-
###
-
# Is this NodeSet empty?
-
1
def empty?
-
length == 0
-
end
-
-
###
-
# Returns the index of the first node in self that is == to +node+. Returns nil if no match is found.
-
1
def index(node)
-
each_with_index { |member, j| return j if member == node }
-
nil
-
end
-
-
###
-
# Insert +datum+ before the first Node in this NodeSet
-
1
def before datum
-
first.before datum
-
end
-
-
###
-
# Insert +datum+ after the last Node in this NodeSet
-
1
def after datum
-
last.after datum
-
end
-
-
1
alias :<< :push
-
1
alias :remove :unlink
-
-
###
-
# Search this document for +paths+
-
#
-
# For more information see Nokogiri::XML::Node#css and
-
# Nokogiri::XML::Node#xpath
-
1
def search *paths
-
handler = ![
-
Hash, String, Symbol
-
].include?(paths.last.class) ? paths.pop : nil
-
-
ns = paths.last.is_a?(Hash) ? paths.pop : nil
-
-
sub_set = NodeSet.new(document)
-
-
paths.each do |path|
-
sub_set += send(
-
path =~ /^(\.\/|\/|\.\.|\.$)/ ? :xpath : :css,
-
*(paths + [ns, handler]).compact
-
)
-
end
-
-
document.decorate(sub_set)
-
sub_set
-
end
-
1
alias :/ :search
-
-
###
-
# Search this NodeSet for css +paths+
-
#
-
# For more information see Nokogiri::XML::Node#css
-
1
def css *paths
-
handler = ![
-
Hash, String, Symbol
-
].include?(paths.last.class) ? paths.pop : nil
-
-
ns = paths.last.is_a?(Hash) ? paths.pop : nil
-
-
sub_set = NodeSet.new(document)
-
-
each do |node|
-
doc = node.document
-
search_ns = ns || (doc.root ? doc.root.namespaces : {})
-
-
xpaths = paths.map { |rule|
-
[
-
CSS.xpath_for(rule.to_s, :prefix => ".//", :ns => search_ns),
-
CSS.xpath_for(rule.to_s, :prefix => "self::", :ns => search_ns)
-
].join(' | ')
-
}
-
-
sub_set += node.xpath(*(xpaths + [search_ns, handler].compact))
-
end
-
document.decorate(sub_set)
-
sub_set
-
end
-
-
###
-
# Search this NodeSet for XPath +paths+
-
#
-
# For more information see Nokogiri::XML::Node#xpath
-
1
def xpath *paths
-
handler = ![
-
Hash, String, Symbol
-
].include?(paths.last.class) ? paths.pop : nil
-
-
ns = paths.last.is_a?(Hash) ? paths.pop : nil
-
-
sub_set = NodeSet.new(document)
-
each do |node|
-
sub_set += node.xpath(*(paths + [ns, handler].compact))
-
end
-
document.decorate(sub_set)
-
sub_set
-
end
-
-
###
-
# Search this NodeSet's nodes' immediate children using CSS selector +selector+
-
1
def > selector
-
ns = document.root.namespaces
-
xpath CSS.xpath_for(selector, :prefix => "./", :ns => ns).first
-
end
-
-
###
-
# If path is a string, search this document for +path+ returning the
-
# first Node. Otherwise, index in to the array with +path+.
-
1
def at path, ns = document.root ? document.root.namespaces : {}
-
return self[path] if path.is_a?(Numeric)
-
search(path, ns).first
-
end
-
1
alias :% :at
-
-
##
-
# Search this NodeSet for the first occurrence of XPath +paths+.
-
# Equivalent to <tt>xpath(paths).first</tt>
-
# See NodeSet#xpath for more information.
-
#
-
1
def at_xpath *paths
-
xpath(*paths).first
-
end
-
-
##
-
# Search this NodeSet for the first occurrence of CSS +rules+.
-
# Equivalent to <tt>css(rules).first</tt>
-
# See NodeSet#css for more information.
-
#
-
1
def at_css *rules
-
css(*rules).first
-
end
-
-
###
-
# Filter this list for nodes that match +expr+
-
1
def filter expr
-
find_all { |node| node.matches?(expr) }
-
end
-
-
###
-
# Append the class attribute +name+ to all Node objects in the NodeSet.
-
1
def add_class name
-
each do |el|
-
classes = el['class'].to_s.split(/\s+/)
-
el['class'] = classes.push(name).uniq.join " "
-
end
-
self
-
end
-
-
###
-
# Remove the class attribute +name+ from all Node objects in the NodeSet.
-
# If +name+ is nil, remove the class attribute from all Nodes in the
-
# NodeSet.
-
1
def remove_class name = nil
-
each do |el|
-
if name
-
classes = el['class'].to_s.split(/\s+/)
-
if classes.empty?
-
el.delete 'class'
-
else
-
el['class'] = (classes - [name]).uniq.join " "
-
end
-
else
-
el.delete "class"
-
end
-
end
-
self
-
end
-
-
###
-
# Set the attribute +key+ to +value+ or the return value of +blk+
-
# on all Node objects in the NodeSet.
-
1
def attr key, value = nil, &blk
-
unless Hash === key || key && (value || blk)
-
return first.attribute(key)
-
end
-
-
hash = key.is_a?(Hash) ? key : { key => value }
-
-
hash.each { |k,v| each { |el| el[k] = v || blk[el] } }
-
-
self
-
end
-
1
alias :set :attr
-
1
alias :attribute :attr
-
-
###
-
# Remove the attributed named +name+ from all Node objects in the NodeSet
-
1
def remove_attr name
-
each { |el| el.delete name }
-
self
-
end
-
-
###
-
# Iterate over each node, yielding to +block+
-
1
def each(&block)
-
0.upto(length - 1) do |x|
-
yield self[x]
-
end
-
end
-
-
###
-
# Get the inner text of all contained Node objects
-
1
def inner_text
-
collect{|j| j.inner_text}.join('')
-
end
-
1
alias :text :inner_text
-
-
###
-
# Get the inner html of all contained Node objects
-
1
def inner_html *args
-
collect{|j| j.inner_html(*args) }.join('')
-
end
-
-
###
-
# Wrap this NodeSet with +html+ or the results of the builder in +blk+
-
1
def wrap(html, &blk)
-
each do |j|
-
new_parent = document.parse(html).first
-
j.add_next_sibling(new_parent)
-
new_parent.add_child(j)
-
end
-
self
-
end
-
-
###
-
# Convert this NodeSet to a string.
-
1
def to_s
-
map { |x| x.to_s }.join
-
end
-
-
###
-
# Convert this NodeSet to HTML
-
1
def to_html *args
-
if Nokogiri.jruby?
-
options = args.first.is_a?(Hash) ? args.shift : {}
-
if !options[:save_with]
-
options[:save_with] = Node::SaveOptions::NO_DECLARATION | Node::SaveOptions::NO_EMPTY_TAGS | Node::SaveOptions::AS_HTML
-
end
-
args.insert(0, options)
-
end
-
map { |x| x.to_html(*args) }.join
-
end
-
-
###
-
# Convert this NodeSet to XHTML
-
1
def to_xhtml *args
-
map { |x| x.to_xhtml(*args) }.join
-
end
-
-
###
-
# Convert this NodeSet to XML
-
1
def to_xml *args
-
map { |x| x.to_xml(*args) }.join
-
end
-
-
1
alias :size :length
-
1
alias :to_ary :to_a
-
-
###
-
# Removes the last element from set and returns it, or +nil+ if
-
# the set is empty
-
1
def pop
-
return nil if length == 0
-
delete last
-
end
-
-
###
-
# Returns the first element of the NodeSet and removes it. Returns
-
# +nil+ if the set is empty.
-
1
def shift
-
return nil if length == 0
-
delete first
-
end
-
-
###
-
# Equality -- Two NodeSets are equal if the contain the same number
-
# of elements and if each element is equal to the corresponding
-
# element in the other NodeSet
-
1
def == other
-
return false unless other.is_a?(Nokogiri::XML::NodeSet)
-
return false unless length == other.length
-
each_with_index do |node, i|
-
return false unless node == other[i]
-
end
-
true
-
end
-
-
###
-
# Returns a new NodeSet containing all the children of all the nodes in
-
# the NodeSet
-
1
def children
-
inject(NodeSet.new(document)) { |set, node| set += node.children }
-
end
-
-
###
-
# Returns a new NodeSet containing all the nodes in the NodeSet
-
# in reverse order
-
1
def reverse
-
node_set = NodeSet.new(document)
-
(length - 1).downto(0) do |x|
-
node_set.push self[x]
-
end
-
node_set
-
end
-
-
###
-
# Return a nicely formated string representation
-
1
def inspect
-
"[#{map { |c| c.inspect }.join ', '}]"
-
end
-
-
1
alias :+ :|
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
1
class Notation < Struct.new(:name, :public_id, :system_id)
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
###
-
# Parse options for passing to Nokogiri.XML or Nokogiri.HTML
-
1
class ParseOptions
-
# Strict parsing
-
1
STRICT = 0
-
# Recover from errors
-
1
RECOVER = 1 << 0
-
# Substitute entities
-
1
NOENT = 1 << 1
-
# Load external subsets
-
1
DTDLOAD = 1 << 2
-
# Default DTD attributes
-
1
DTDATTR = 1 << 3
-
# validate with the DTD
-
1
DTDVALID = 1 << 4
-
# suppress error reports
-
1
NOERROR = 1 << 5
-
# suppress warning reports
-
1
NOWARNING = 1 << 6
-
# pedantic error reporting
-
1
PEDANTIC = 1 << 7
-
# remove blank nodes
-
1
NOBLANKS = 1 << 8
-
# use the SAX1 interface internally
-
1
SAX1 = 1 << 9
-
# Implement XInclude substitution
-
1
XINCLUDE = 1 << 10
-
# Forbid network access. Recommended for dealing with untrusted documents.
-
1
NONET = 1 << 11
-
# Do not reuse the context dictionary
-
1
NODICT = 1 << 12
-
# remove redundant namespaces declarations
-
1
NSCLEAN = 1 << 13
-
# merge CDATA as text nodes
-
1
NOCDATA = 1 << 14
-
# do not generate XINCLUDE START/END nodes
-
1
NOXINCNODE = 1 << 15
-
# compact small text nodes; no modification of the tree allowed afterwards (will possibly crash if you try to modify the tree)
-
1
COMPACT = 1 << 16
-
# parse using XML-1.0 before update 5
-
1
OLD10 = 1 << 17
-
# do not fixup XINCLUDE xml:base uris
-
1
NOBASEFIX = 1 << 18
-
# relax any hardcoded limit from the parser
-
1
HUGE = 1 << 19
-
-
# the default options used for parsing XML documents
-
1
DEFAULT_XML = RECOVER | NONET
-
# the default options used for parsing HTML documents
-
1
DEFAULT_HTML = RECOVER | NOERROR | NOWARNING | NONET
-
-
1
attr_accessor :options
-
1
def initialize options = STRICT
-
@options = options
-
end
-
-
1
constants.each do |constant|
-
23
next if constant.to_sym == :STRICT
-
22
class_eval %{
-
def #{constant.downcase}
-
@options |= #{constant}
-
self
-
end
-
-
def no#{constant.downcase}
-
@options &= ~#{constant}
-
self
-
end
-
-
def #{constant.downcase}?
-
#{constant} & @options == #{constant}
-
end
-
}
-
end
-
-
1
def strict
-
@options &= ~RECOVER
-
self
-
end
-
-
1
def strict?
-
@options & RECOVER == STRICT
-
end
-
-
1
alias :to_i :options
-
-
1
def inspect
-
options = []
-
self.class.constants.each do |k|
-
options << k.downcase if send(:"#{k.downcase}?")
-
end
-
super.sub(/>$/, " " + options.join(', ') + ">")
-
end
-
end
-
end
-
end
-
1
require 'nokogiri/xml/pp/node'
-
1
require 'nokogiri/xml/pp/character_data'
-
1
module Nokogiri
-
1
module XML
-
1
module PP
-
1
module CharacterData
-
1
def pretty_print pp # :nodoc:
-
nice_name = self.class.name.split('::').last
-
pp.group(2, "#(#{nice_name} ", ')') do
-
pp.pp text
-
end
-
end
-
-
1
def inspect # :nodoc:
-
"#<#{self.class.name}:#{sprintf("0x%x",object_id)} #{text.inspect}>"
-
end
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
1
module PP
-
1
module Node
-
1
def inspect # :nodoc:
-
attributes = inspect_attributes.reject { |x|
-
begin
-
attribute = send x
-
!attribute || (attribute.respond_to?(:empty?) && attribute.empty?)
-
rescue NoMethodError
-
true
-
end
-
}.map { |attribute|
-
"#{attribute.to_s.sub(/_\w+/, 's')}=#{send(attribute).inspect}"
-
}.join ' '
-
"#<#{self.class.name}:#{sprintf("0x%x", object_id)} #{attributes}>"
-
end
-
-
1
def pretty_print pp # :nodoc:
-
nice_name = self.class.name.split('::').last
-
pp.group(2, "#(#{nice_name}:#{sprintf("0x%x", object_id)} {", '})') do
-
-
pp.breakable
-
attrs = inspect_attributes.map { |t|
-
[t, send(t)] if respond_to?(t)
-
}.compact.find_all { |x|
-
if x.last
-
if [:attribute_nodes, :children].include? x.first
-
!x.last.empty?
-
else
-
true
-
end
-
end
-
}
-
-
pp.seplist(attrs) do |v|
-
if [:attribute_nodes, :children].include? v.first
-
pp.group(2, "#{v.first.to_s.sub(/_\w+$/, 's')} = [", "]") do
-
pp.breakable
-
pp.seplist(v.last) do |item|
-
pp.pp item
-
end
-
end
-
else
-
pp.text "#{v.first} = "
-
pp.pp v.last
-
end
-
end
-
pp.breakable
-
-
end
-
end
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
1
class ProcessingInstruction < Node
-
1
def initialize document, name, content
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
1
class << self
-
###
-
# Create a new Nokogiri::XML::RelaxNG document from +string_or_io+.
-
# See Nokogiri::XML::RelaxNG for an example.
-
1
def RelaxNG string_or_io
-
RelaxNG.new(string_or_io)
-
end
-
end
-
-
###
-
# Nokogiri::XML::RelaxNG is used for validating XML against a
-
# RelaxNG schema.
-
#
-
# == Synopsis
-
#
-
# Validate an XML document against a RelaxNG schema. Loop over the errors
-
# that are returned and print them out:
-
#
-
# schema = Nokogiri::XML::RelaxNG(File.open(ADDRESS_SCHEMA_FILE))
-
# doc = Nokogiri::XML(File.open(ADDRESS_XML_FILE))
-
#
-
# schema.validate(doc).each do |error|
-
# puts error.message
-
# end
-
#
-
# The list of errors are Nokogiri::XML::SyntaxError objects.
-
1
class RelaxNG < Nokogiri::XML::Schema
-
end
-
end
-
end
-
1
require 'nokogiri/xml/sax/document'
-
1
require 'nokogiri/xml/sax/parser_context'
-
1
require 'nokogiri/xml/sax/parser'
-
1
require 'nokogiri/xml/sax/push_parser'
-
1
module Nokogiri
-
1
module XML
-
###
-
# SAX Parsers are event driven parsers. Nokogiri provides two different
-
# event based parsers when dealing with XML. If you want to do SAX style
-
# parsing using HTML, check out Nokogiri::HTML::SAX.
-
#
-
# The basic way a SAX style parser works is by creating a parser,
-
# telling the parser about the events we're interested in, then giving
-
# the parser some XML to process. The parser will notify you when
-
# it encounters events you said you would like to know about.
-
#
-
# To register for events, you simply subclass Nokogiri::XML::SAX::Document,
-
# and implement the methods for which you would like notification.
-
#
-
# For example, if I want to be notified when a document ends, and when an
-
# element starts, I would write a class like this:
-
#
-
# class MyDocument < Nokogiri::XML::SAX::Document
-
# def end_document
-
# puts "the document has ended"
-
# end
-
#
-
# def start_element name, attributes = []
-
# puts "#{name} started"
-
# end
-
# end
-
#
-
# Then I would instantiate a SAX parser with this document, and feed the
-
# parser some XML
-
#
-
# # Create a new parser
-
# parser = Nokogiri::XML::SAX::Parser.new(MyDocument.new)
-
#
-
# # Feed the parser some XML
-
# parser.parse(File.open(ARGV[0]))
-
#
-
# Now my document handler will be called when each node starts, and when
-
# then document ends. To see what kinds of events are available, take
-
# a look at Nokogiri::XML::SAX::Document.
-
#
-
# Two SAX parsers for XML are available, a parser that reads from a string
-
# or IO object as it feels necessary, and a parser that lets you spoon
-
# feed it XML. If you want to let Nokogiri deal with reading your XML,
-
# use the Nokogiri::XML::SAX::Parser. If you want to have fine grain
-
# control over the XML input, use the Nokogiri::XML::SAX::PushParser.
-
1
module SAX
-
###
-
# This class is used for registering types of events you are interested
-
# in handling. All of the methods on this class are available as
-
# possible events while parsing an XML document. To register for any
-
# particular event, just subclass this class and implement the methods
-
# you are interested in knowing about.
-
#
-
# To only be notified about start and end element events, write a class
-
# like this:
-
#
-
# class MyDocument < Nokogiri::XML::SAX::Document
-
# def start_element name, attrs = []
-
# puts "#{name} started!"
-
# end
-
#
-
# def end_element name
-
# puts "#{name} ended"
-
# end
-
# end
-
#
-
# You can use this event handler for any SAX style parser included with
-
# Nokogiri. See Nokogiri::XML::SAX, and Nokogiri::HTML::SAX.
-
1
class Document
-
###
-
# Called when an XML declaration is parsed
-
1
def xmldecl version, encoding, standalone
-
end
-
-
###
-
# Called when document starts parsing
-
1
def start_document
-
end
-
-
###
-
# Called when document ends parsing
-
1
def end_document
-
end
-
-
###
-
# Called at the beginning of an element
-
# * +name+ is the name of the tag
-
# * +attrs+ are an assoc list of namespaces and attributes, e.g.:
-
# [ ["xmlns:foo", "http://sample.net"], ["size", "large"] ]
-
1
def start_element name, attrs = []
-
end
-
-
###
-
# Called at the end of an element
-
# +name+ is the tag name
-
1
def end_element name
-
end
-
-
###
-
# Called at the beginning of an element
-
# +name+ is the element name
-
# +attrs+ is a list of attributes
-
# +prefix+ is the namespace prefix for the element
-
# +uri+ is the associated namespace URI
-
# +ns+ is a hash of namespace prefix:urls associated with the element
-
1
def start_element_namespace name, attrs = [], prefix = nil, uri = nil, ns = []
-
###
-
# Deal with SAX v1 interface
-
name = [prefix, name].compact.join(':')
-
attributes = ns.map { |ns_prefix,ns_uri|
-
[['xmlns', ns_prefix].compact.join(':'), ns_uri]
-
} + attrs.map { |attr|
-
[[attr.prefix, attr.localname].compact.join(':'), attr.value]
-
}
-
start_element name, attributes
-
end
-
-
###
-
# Called at the end of an element
-
# +name+ is the element's name
-
# +prefix+ is the namespace prefix associated with the element
-
# +uri+ is the associated namespace URI
-
1
def end_element_namespace name, prefix = nil, uri = nil
-
###
-
# Deal with SAX v1 interface
-
end_element [prefix, name].compact.join(':')
-
end
-
-
###
-
# Characters read between a tag. This method might be called multiple
-
# times given one contiguous string of characters.
-
#
-
# +string+ contains the character data
-
1
def characters string
-
end
-
-
###
-
# Called when comments are encountered
-
# +string+ contains the comment data
-
1
def comment string
-
end
-
-
###
-
# Called on document warnings
-
# +string+ contains the warning
-
1
def warning string
-
end
-
-
###
-
# Called on document errors
-
# +string+ contains the error
-
1
def error string
-
end
-
-
###
-
# Called when cdata blocks are found
-
# +string+ contains the cdata content
-
1
def cdata_block string
-
end
-
-
###
-
# Called when processing instructions are found
-
# +name+ is the target of the instruction
-
# +content+ is the value of the instruction
-
1
def processing_instruction name, content
-
end
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
1
module SAX
-
###
-
# This parser is a SAX style parser that reads it's input as it
-
# deems necessary. The parser takes a Nokogiri::XML::SAX::Document,
-
# an optional encoding, then given an XML input, sends messages to
-
# the Nokogiri::XML::SAX::Document.
-
#
-
# Here is an example of using this parser:
-
#
-
# # Create a subclass of Nokogiri::XML::SAX::Document and implement
-
# # the events we care about:
-
# class MyDoc < Nokogiri::XML::SAX::Document
-
# def start_element name, attrs = []
-
# puts "starting: #{name}"
-
# end
-
#
-
# def end_element name
-
# puts "ending: #{name}"
-
# end
-
# end
-
#
-
# # Create our parser
-
# parser = Nokogiri::XML::SAX::Parser.new(MyDoc.new)
-
#
-
# # Send some XML to the parser
-
# parser.parse(File.open(ARGV[0]))
-
#
-
# For more information about SAX parsers, see Nokogiri::XML::SAX. Also
-
# see Nokogiri::XML::SAX::Document for the available events.
-
1
class Parser
-
1
class Attribute < Struct.new(:localname, :prefix, :uri, :value)
-
end
-
-
# Encodinds this parser supports
-
1
ENCODINGS = {
-
'NONE' => 0, # No char encoding detected
-
'UTF-8' => 1, # UTF-8
-
'UTF16LE' => 2, # UTF-16 little endian
-
'UTF16BE' => 3, # UTF-16 big endian
-
'UCS4LE' => 4, # UCS-4 little endian
-
'UCS4BE' => 5, # UCS-4 big endian
-
'EBCDIC' => 6, # EBCDIC uh!
-
'UCS4-2143' => 7, # UCS-4 unusual ordering
-
'UCS4-3412' => 8, # UCS-4 unusual ordering
-
'UCS2' => 9, # UCS-2
-
'ISO-8859-1' => 10, # ISO-8859-1 ISO Latin 1
-
'ISO-8859-2' => 11, # ISO-8859-2 ISO Latin 2
-
'ISO-8859-3' => 12, # ISO-8859-3
-
'ISO-8859-4' => 13, # ISO-8859-4
-
'ISO-8859-5' => 14, # ISO-8859-5
-
'ISO-8859-6' => 15, # ISO-8859-6
-
'ISO-8859-7' => 16, # ISO-8859-7
-
'ISO-8859-8' => 17, # ISO-8859-8
-
'ISO-8859-9' => 18, # ISO-8859-9
-
'ISO-2022-JP' => 19, # ISO-2022-JP
-
'SHIFT-JIS' => 20, # Shift_JIS
-
'EUC-JP' => 21, # EUC-JP
-
'ASCII' => 22, # pure ASCII
-
}
-
-
# The Nokogiri::XML::SAX::Document where events will be sent.
-
1
attr_accessor :document
-
-
# The encoding beings used for this document.
-
1
attr_accessor :encoding
-
-
# Create a new Parser with +doc+ and +encoding+
-
1
def initialize doc = Nokogiri::XML::SAX::Document.new, encoding = 'UTF-8'
-
check_encoding(encoding)
-
@encoding = encoding
-
@document = doc
-
@warned = false
-
end
-
-
###
-
# Parse given +thing+ which may be a string containing xml, or an
-
# IO object.
-
1
def parse thing, &block
-
if thing.respond_to?(:read) && thing.respond_to?(:close)
-
parse_io(thing, &block)
-
else
-
parse_memory(thing, &block)
-
end
-
end
-
-
###
-
# Parse given +io+
-
1
def parse_io io, encoding = 'ASCII'
-
check_encoding(encoding)
-
@encoding = encoding
-
ctx = ParserContext.io(io, ENCODINGS[encoding])
-
yield ctx if block_given?
-
ctx.parse_with self
-
end
-
-
###
-
# Parse a file with +filename+
-
1
def parse_file filename
-
raise ArgumentError unless filename
-
raise Errno::ENOENT unless File.exist?(filename)
-
raise Errno::EISDIR if File.directory?(filename)
-
ctx = ParserContext.file filename
-
yield ctx if block_given?
-
ctx.parse_with self
-
end
-
-
1
def parse_memory data
-
ctx = ParserContext.memory data
-
yield ctx if block_given?
-
ctx.parse_with self
-
end
-
-
1
private
-
1
def check_encoding(encoding)
-
encoding.upcase!
-
raise ArgumentError.new("'#{encoding}' is not a valid encoding") unless ENCODINGS[encoding]
-
end
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
1
module SAX
-
###
-
# Context for XML SAX parsers. This class is usually not instantiated
-
# by the user. Instead, you should be looking at
-
# Nokogiri::XML::SAX::Parser
-
1
class ParserContext
-
1
def self.new thing, encoding = 'UTF-8'
-
[:read, :close].all? { |x| thing.respond_to?(x) } ?
-
io(thing, Parser::ENCODINGS[encoding]) : memory(thing)
-
end
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
1
module SAX
-
###
-
# PushParser can parse a document that is fed to it manually. It
-
# must be given a SAX::Document object which will be called with
-
# SAX events as the document is being parsed.
-
#
-
# Calling PushParser#<< writes XML to the parser, calling any SAX
-
# callbacks it can.
-
#
-
# PushParser#finish tells the parser that the document is finished
-
# and calls the end_document SAX method.
-
#
-
# Example:
-
#
-
# parser = PushParser.new(Class.new(XML::SAX::Document) {
-
# def start_document
-
# puts "start document called"
-
# end
-
# }.new)
-
# parser << "<div>hello<"
-
# parser << "/div>"
-
# parser.finish
-
1
class PushParser
-
-
# The Nokogiri::XML::SAX::Document on which the PushParser will be
-
# operating
-
1
attr_accessor :document
-
-
###
-
# Create a new PushParser with +doc+ as the SAX Document, providing
-
# an optional +file_name+ and +encoding+
-
1
def initialize(doc = XML::SAX::Document.new, file_name = nil, encoding = 'UTF-8')
-
@document = doc
-
@encoding = encoding
-
@sax_parser = XML::SAX::Parser.new(doc)
-
-
## Create our push parser context
-
initialize_native(@sax_parser, file_name)
-
end
-
-
###
-
# Write a +chunk+ of XML to the PushParser. Any callback methods
-
# that can be called will be called immediately.
-
1
def write chunk, last_chunk = false
-
native_write(chunk, last_chunk)
-
end
-
1
alias :<< :write
-
-
###
-
# Finish the parsing. This method is only necessary for
-
# Nokogiri::XML::SAX::Document#end_document to be called.
-
1
def finish
-
write '', true
-
end
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
1
class << self
-
###
-
# Create a new Nokogiri::XML::Schema object using a +string_or_io+
-
# object.
-
1
def Schema string_or_io
-
Schema.new(string_or_io)
-
end
-
end
-
-
###
-
# Nokogiri::XML::Schema is used for validating XML against a schema
-
# (usually from an xsd file).
-
#
-
# == Synopsis
-
#
-
# Validate an XML document against a Schema. Loop over the errors that
-
# are returned and print them out:
-
#
-
# xsd = Nokogiri::XML::Schema(File.read(PO_SCHEMA_FILE))
-
# doc = Nokogiri::XML(File.read(PO_XML_FILE))
-
#
-
# xsd.validate(doc).each do |error|
-
# puts error.message
-
# end
-
#
-
# The list of errors are Nokogiri::XML::SyntaxError objects.
-
1
class Schema
-
# Errors while parsing the schema file
-
1
attr_accessor :errors
-
-
###
-
# Create a new Nokogiri::XML::Schema object using a +string_or_io+
-
# object.
-
1
def self.new string_or_io
-
from_document Nokogiri::XML(string_or_io)
-
end
-
-
###
-
# Validate +thing+ against this schema. +thing+ can be a
-
# Nokogiri::XML::Document object, or a filename. An Array of
-
# Nokogiri::XML::SyntaxError objects found while validating the
-
# +thing+ is returned.
-
1
def validate thing
-
if thing.is_a?(Nokogiri::XML::Document)
-
validate_document(thing)
-
elsif File.file?(thing)
-
validate_file(thing)
-
else
-
raise ArgumentError, "Must provide Nokogiri::Xml::Document or the name of an existing file"
-
end
-
end
-
-
###
-
# Returns true if +thing+ is a valid Nokogiri::XML::Document or
-
# file.
-
1
def valid? thing
-
validate(thing).length == 0
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
###
-
# This class provides information about XML SyntaxErrors. These
-
# exceptions are typically stored on Nokogiri::XML::Document#errors.
-
1
class SyntaxError < ::Nokogiri::SyntaxError
-
1
attr_reader :domain
-
1
attr_reader :code
-
1
attr_reader :level
-
1
attr_reader :file
-
1
attr_reader :line
-
1
attr_reader :str1
-
1
attr_reader :str2
-
1
attr_reader :str3
-
1
attr_reader :int1
-
1
attr_reader :column
-
-
###
-
# return true if this is a non error
-
1
def none?
-
level == 0
-
end
-
-
###
-
# return true if this is a warning
-
1
def warning?
-
level == 1
-
end
-
-
###
-
# return true if this is an error
-
1
def error?
-
level == 2
-
end
-
-
###
-
# return true if this error is fatal
-
1
def fatal?
-
level == 3
-
end
-
-
1
def to_s
-
super.chomp
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
1
class Text < Nokogiri::XML::CharacterData
-
1
def content=(string)
-
self.native_content = string.to_s
-
end
-
end
-
end
-
end
-
1
require 'nokogiri/xml/xpath/syntax_error'
-
-
1
module Nokogiri
-
1
module XML
-
1
class XPath
-
# The Nokogiri::XML::Document tied to this XPath instance
-
1
attr_accessor :document
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
1
class XPath
-
1
class SyntaxError < XML::SyntaxError
-
1
def to_s
-
[super.chomp, str1].compact.join(': ')
-
end
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XML
-
1
class XPathContext
-
-
###
-
# Register namespaces in +namespaces+
-
1
def register_namespaces(namespaces)
-
namespaces.each do |k, v|
-
k = k.to_s.gsub(/.*:/,'') # strip off 'xmlns:' or 'xml:'
-
register_ns(k, v)
-
end
-
end
-
-
end
-
end
-
end
-
1
require 'nokogiri/xslt/stylesheet'
-
-
1
module Nokogiri
-
1
class << self
-
###
-
# Create a Nokogiri::XSLT::Stylesheet with +stylesheet+.
-
#
-
# Example:
-
#
-
# xslt = Nokogiri::XSLT(File.read(ARGV[0]))
-
#
-
1
def XSLT stylesheet, modules = {}
-
XSLT.parse(stylesheet, modules)
-
end
-
end
-
-
###
-
# See Nokogiri::XSLT::Stylesheet for creating and manipulating
-
# Stylesheet object.
-
1
module XSLT
-
1
class << self
-
###
-
# Parse the stylesheet in +string+, register any +modules+
-
1
def parse string, modules = {}
-
modules.each do |url, klass|
-
XSLT.register url, klass
-
end
-
-
if Nokogiri.jruby?
-
Stylesheet.parse_stylesheet_doc(XML.parse(string), string)
-
else
-
Stylesheet.parse_stylesheet_doc(XML.parse(string))
-
end
-
end
-
-
###
-
# Quote parameters in +params+ for stylesheet safety
-
1
def quote_params params
-
parray = (params.instance_of?(Hash) ? params.to_a.flatten : params).dup
-
parray.each_with_index do |v,i|
-
if i % 2 > 0
-
parray[i]=
-
if v =~ /'/
-
"concat('#{ v.gsub(/'/, %q{', "'", '}) }')"
-
else
-
"'#{v}'";
-
end
-
else
-
parray[i] = v.to_s
-
end
-
end
-
parray.flatten
-
end
-
end
-
end
-
end
-
1
module Nokogiri
-
1
module XSLT
-
###
-
# A Stylesheet represents an XSLT Stylesheet object. Stylesheet creation
-
# is done through Nokogiri.XSLT. Here is an example of transforming
-
# an XML::Document with a Stylesheet:
-
#
-
# doc = Nokogiri::XML(File.read('some_file.xml'))
-
# xslt = Nokogiri::XSLT(File.read('some_transformer.xslt'))
-
#
-
# puts xslt.transform(doc)
-
#
-
# See Nokogiri::XSLT::Stylesheet#transform for more transformation
-
# information.
-
1
class Stylesheet
-
###
-
# Apply an XSLT stylesheet to an XML::Document.
-
# +params+ is an array of strings used as XSLT parameters.
-
# returns serialized document
-
1
def apply_to document, params = []
-
serialize(transform(document, params))
-
end
-
end
-
end
-
end
-
# = digest/hmac.rb
-
#
-
# An implementation of HMAC keyed-hashing algorithm
-
#
-
# == Overview
-
#
-
# This library adds a method named hmac() to Digest classes, which
-
# creates a Digest class for calculating HMAC digests.
-
#
-
# == Examples
-
#
-
# require 'digest/hmac'
-
#
-
# # one-liner example
-
# puts Digest::HMAC.hexdigest("data", "hash key", Digest::SHA1)
-
#
-
# # rather longer one
-
# hmac = Digest::HMAC.new("foo", Digest::RMD160)
-
#
-
# buf = ""
-
# while stream.read(16384, buf)
-
# hmac.update(buf)
-
# end
-
#
-
# puts hmac.bubblebabble
-
#
-
# == License
-
#
-
# Copyright (c) 2006 Akinori MUSHA <knu@iDaemons.org>
-
#
-
# Documentation by Akinori MUSHA
-
#
-
# All rights reserved. You can redistribute and/or modify it under
-
# the same terms as Ruby.
-
#
-
# $Id: hmac.rb 14881 2008-01-04 07:26:14Z akr $
-
#
-
-
1
require 'digest'
-
-
1
unless defined?(Digest::HMAC)
-
1
module Digest
-
1
class HMAC < Digest::Class
-
1
def initialize(key, digester)
-
@md = digester.new
-
-
block_len = @md.block_length
-
-
if key.bytesize > block_len
-
key = @md.digest(key)
-
end
-
-
ipad = Array.new(block_len).fill(0x36)
-
opad = Array.new(block_len).fill(0x5c)
-
-
key.bytes.each_with_index { |c, i|
-
ipad[i] ^= c
-
opad[i] ^= c
-
}
-
-
@key = key.freeze
-
@ipad = ipad.inject('') { |s, c| s << c.chr }.freeze
-
@opad = opad.inject('') { |s, c| s << c.chr }.freeze
-
@md.update(@ipad)
-
end
-
-
1
def initialize_copy(other)
-
@md = other.instance_eval { @md.clone }
-
end
-
-
1
def update(text)
-
@md.update(text)
-
self
-
end
-
1
alias << update
-
-
1
def reset
-
@md.reset
-
@md.update(@ipad)
-
self
-
end
-
-
1
def finish
-
d = @md.digest!
-
@md.update(@opad)
-
@md.update(d)
-
@md.digest!
-
end
-
1
private :finish
-
-
1
def digest_length
-
@md.digest_length
-
end
-
-
1
def block_length
-
@md.block_length
-
end
-
-
1
def inspect
-
sprintf('#<%s: key=%s, digest=%s>', self.class.name, @key.inspect, @md.inspect.sub(/^\#<(.*)>$/) { $1 });
-
end
-
end
-
end
-
end
-
1
$LOAD_PATH << File.dirname(__FILE__) unless $LOAD_PATH.include?(File.dirname(__FILE__))
-
-
1
module OAuth
-
1
VERSION = "0.4.7"
-
end
-
-
1
require 'oauth/oauth'
-
1
require 'oauth/core_ext'
-
-
1
require 'oauth/client/helper'
-
1
require 'oauth/signature/hmac/sha1'
-
1
require 'oauth/signature/rsa/sha1'
-
1
require 'oauth/request_proxy/mock_request'
-
1
module OAuth
-
1
module Client
-
end
-
end
-
1
require 'oauth/client'
-
1
require 'oauth/consumer'
-
1
require 'oauth/helper'
-
1
require 'oauth/token'
-
1
require 'oauth/signature/hmac/sha1'
-
-
1
module OAuth::Client
-
1
class Helper
-
1
include OAuth::Helper
-
-
1
def initialize(request, options = {})
-
@request = request
-
@options = options
-
@options[:signature_method] ||= 'HMAC-SHA1'
-
end
-
-
1
def options
-
@options
-
end
-
-
1
def nonce
-
options[:nonce] ||= generate_key
-
end
-
-
1
def timestamp
-
options[:timestamp] ||= generate_timestamp
-
end
-
-
1
def oauth_parameters
-
{
-
'oauth_body_hash' => options[:body_hash],
-
'oauth_callback' => options[:oauth_callback],
-
'oauth_consumer_key' => options[:consumer].key,
-
'oauth_token' => options[:token] ? options[:token].token : '',
-
'oauth_signature_method' => options[:signature_method],
-
'oauth_timestamp' => timestamp,
-
'oauth_nonce' => nonce,
-
'oauth_verifier' => options[:oauth_verifier],
-
'oauth_version' => (options[:oauth_version] || '1.0'),
-
'oauth_session_handle' => options[:oauth_session_handle]
-
}.reject { |k,v| v.to_s == "" }
-
end
-
-
1
def signature(extra_options = {})
-
OAuth::Signature.sign(@request, { :uri => options[:request_uri],
-
:consumer => options[:consumer],
-
:token => options[:token],
-
:unsigned_parameters => options[:unsigned_parameters]
-
}.merge(extra_options) )
-
end
-
-
1
def signature_base_string(extra_options = {})
-
OAuth::Signature.signature_base_string(@request, { :uri => options[:request_uri],
-
:consumer => options[:consumer],
-
:token => options[:token],
-
:parameters => oauth_parameters}.merge(extra_options) )
-
end
-
-
1
def hash_body
-
@options[:body_hash] = OAuth::Signature.body_hash(@request, :parameters => oauth_parameters)
-
end
-
-
1
def amend_user_agent_header(headers)
-
@oauth_ua_string ||= "OAuth gem v#{OAuth::VERSION}"
-
# Net::HTTP in 1.9 appends Ruby
-
if headers['User-Agent'] && headers['User-Agent'] != 'Ruby'
-
headers['User-Agent'] += " (#{@oauth_ua_string})"
-
else
-
headers['User-Agent'] = @oauth_ua_string
-
end
-
end
-
-
1
def header
-
parameters = oauth_parameters
-
parameters.merge!('oauth_signature' => signature(options.merge(:parameters => parameters)))
-
-
header_params_str = parameters.sort.map { |k,v| "#{k}=\"#{escape(v)}\"" }.join(', ')
-
-
realm = "realm=\"#{options[:realm]}\", " if options[:realm]
-
"OAuth #{realm}#{header_params_str}"
-
end
-
-
1
def parameters
-
OAuth::RequestProxy.proxy(@request).parameters
-
end
-
-
1
def parameters_with_oauth
-
oauth_parameters.merge(parameters)
-
end
-
end
-
end
-
1
require 'oauth/helper'
-
1
require 'oauth/client/helper'
-
1
require 'oauth/request_proxy/net_http'
-
-
1
class Net::HTTPGenericRequest
-
1
include OAuth::Helper
-
-
1
attr_reader :oauth_helper
-
-
# Add the OAuth information to an HTTP request. Depending on the <tt>options[:scheme]</tt> setting
-
# this may add a header, additional query string parameters, or additional POST body parameters.
-
# The default scheme is +header+, in which the OAuth parameters as put into the +Authorization+
-
# header.
-
#
-
# * http - Configured Net::HTTP instance
-
# * consumer - OAuth::Consumer instance
-
# * token - OAuth::Token instance
-
# * options - Request-specific options (e.g. +request_uri+, +consumer+, +token+, +scheme+,
-
# +signature_method+, +nonce+, +timestamp+)
-
#
-
# This method also modifies the <tt>User-Agent</tt> header to add the OAuth gem version.
-
#
-
# See Also: {OAuth core spec version 1.0, section 5.4.1}[http://oauth.net/core/1.0#rfc.section.5.4.1],
-
# {OAuth Request Body Hash 1.0 Draft 4}[http://oauth.googlecode.com/svn/spec/ext/body_hash/1.0/drafts/4/spec.html]
-
1
def oauth!(http, consumer = nil, token = nil, options = {})
-
helper_options = oauth_helper_options(http, consumer, token, options)
-
@oauth_helper = OAuth::Client::Helper.new(self, helper_options)
-
@oauth_helper.amend_user_agent_header(self)
-
@oauth_helper.hash_body if oauth_body_hash_required?
-
self.send("set_oauth_#{helper_options[:scheme]}")
-
end
-
-
# Create a string suitable for signing for an HTTP request. This process involves parameter
-
# normalization as specified in the OAuth specification. The exact normalization also depends
-
# on the <tt>options[:scheme]</tt> being used so this must match what will be used for the request
-
# itself. The default scheme is +header+, in which the OAuth parameters as put into the +Authorization+
-
# header.
-
#
-
# * http - Configured Net::HTTP instance
-
# * consumer - OAuth::Consumer instance
-
# * token - OAuth::Token instance
-
# * options - Request-specific options (e.g. +request_uri+, +consumer+, +token+, +scheme+,
-
# +signature_method+, +nonce+, +timestamp+)
-
#
-
# See Also: {OAuth core spec version 1.0, section 9.1.1}[http://oauth.net/core/1.0#rfc.section.9.1.1],
-
# {OAuth Request Body Hash 1.0 Draft 4}[http://oauth.googlecode.com/svn/spec/ext/body_hash/1.0/drafts/4/spec.html]
-
1
def signature_base_string(http, consumer = nil, token = nil, options = {})
-
helper_options = oauth_helper_options(http, consumer, token, options)
-
oauth_helper = OAuth::Client::Helper.new(self, helper_options)
-
oauth_helper.hash_body if oauth_body_hash_required?
-
oauth_helper.signature_base_string
-
end
-
-
1
private
-
-
1
def oauth_helper_options(http, consumer, token, options)
-
{ :request_uri => oauth_full_request_uri(http,options),
-
:consumer => consumer,
-
:token => token,
-
:scheme => 'header',
-
:signature_method => nil,
-
:nonce => nil,
-
:timestamp => nil }.merge(options)
-
end
-
-
1
def oauth_full_request_uri(http,options)
-
uri = URI.parse(self.path)
-
uri.host = http.address
-
uri.port = http.port
-
-
if options[:request_endpoint] && options[:site]
-
is_https = options[:site].match(%r(^https://))
-
uri.host = options[:site].gsub(%r(^https?://), '')
-
uri.port ||= is_https ? 443 : 80
-
end
-
-
if http.respond_to?(:use_ssl?) && http.use_ssl?
-
uri.scheme = "https"
-
else
-
uri.scheme = "http"
-
end
-
-
uri.to_s
-
end
-
-
1
def oauth_body_hash_required?
-
request_body_permitted? && !content_type.to_s.downcase.start_with?("application/x-www-form-urlencoded")
-
end
-
-
1
def set_oauth_header
-
self['Authorization'] = @oauth_helper.header
-
end
-
-
# FIXME: if you're using a POST body and query string parameters, this method
-
# will move query string parameters into the body unexpectedly. This may
-
# cause problems with non-x-www-form-urlencoded bodies submitted to URLs
-
# containing query string params. If duplicate parameters are present in both
-
# places, all instances should be included when calculating the signature
-
# base string.
-
-
1
def set_oauth_body
-
self.set_form_data(@oauth_helper.stringify_keys(@oauth_helper.parameters_with_oauth))
-
params_with_sig = @oauth_helper.parameters.merge(:oauth_signature => @oauth_helper.signature)
-
self.set_form_data(@oauth_helper.stringify_keys(params_with_sig))
-
end
-
-
1
def set_oauth_query_string
-
oauth_params_str = @oauth_helper.oauth_parameters.map { |k,v| [escape(k), escape(v)] * "=" }.join("&")
-
uri = URI.parse(path)
-
if uri.query.to_s == ""
-
uri.query = oauth_params_str
-
else
-
uri.query = uri.query + "&" + oauth_params_str
-
end
-
-
@path = uri.to_s
-
-
@path << "&oauth_signature=#{escape(oauth_helper.signature)}"
-
end
-
end
-
1
require 'net/http'
-
1
require 'net/https'
-
1
require 'oauth/oauth'
-
1
require 'oauth/client/net_http'
-
1
require 'oauth/errors'
-
1
require 'cgi'
-
-
1
module OAuth
-
1
class Consumer
-
# determine the certificate authority path to verify SSL certs
-
1
CA_FILES = %w(/etc/ssl/certs/ca-certificates.crt /usr/share/curl/curl-ca-bundle.crt)
-
1
CA_FILES.each do |ca_file|
-
2
if File.exists?(ca_file)
-
CA_FILE = ca_file
-
break
-
end
-
end
-
1
CA_FILE = nil unless defined?(CA_FILE)
-
-
1
@@default_options = {
-
# Signature method used by server. Defaults to HMAC-SHA1
-
:signature_method => 'HMAC-SHA1',
-
-
# default paths on site. These are the same as the defaults set up by the generators
-
:request_token_path => '/oauth/request_token',
-
:authorize_path => '/oauth/authorize',
-
:access_token_path => '/oauth/access_token',
-
-
:proxy => nil,
-
# How do we send the oauth values to the server see
-
# http://oauth.net/core/1.0/#consumer_req_param for more info
-
#
-
# Possible values:
-
#
-
# :header - via the Authorize header (Default) ( option 1. in spec)
-
# :body - url form encoded in body of POST request ( option 2. in spec)
-
# :query_string - via the query part of the url ( option 3. in spec)
-
:scheme => :header,
-
-
# Default http method used for OAuth Token Requests (defaults to :post)
-
:http_method => :post,
-
-
# Add a custom ca_file for consumer
-
# :ca_file => '/etc/certs.pem'
-
-
:oauth_version => "1.0"
-
}
-
-
1
attr_accessor :options, :key, :secret
-
1
attr_writer :site, :http
-
-
# Create a new consumer instance by passing it a configuration hash:
-
#
-
# @consumer = OAuth::Consumer.new(key, secret, {
-
# :site => "http://term.ie",
-
# :scheme => :header,
-
# :http_method => :post,
-
# :request_token_path => "/oauth/example/request_token.php",
-
# :access_token_path => "/oauth/example/access_token.php",
-
# :authorize_path => "/oauth/example/authorize.php"
-
# })
-
#
-
# Start the process by requesting a token
-
#
-
# @request_token = @consumer.get_request_token
-
# session[:request_token] = @request_token
-
# redirect_to @request_token.authorize_url
-
#
-
# When user returns create an access_token
-
#
-
# @access_token = @request_token.get_access_token
-
# @photos=@access_token.get('/photos.xml')
-
#
-
1
def initialize(consumer_key, consumer_secret, options = {})
-
@key = consumer_key
-
@secret = consumer_secret
-
-
# ensure that keys are symbols
-
@options = @@default_options.merge(options.inject({}) do |opts, (key, value)|
-
opts[key.to_sym] = value
-
opts
-
end)
-
end
-
-
# The default http method
-
1
def http_method
-
@http_method ||= @options[:http_method] || :post
-
end
-
-
# The HTTP object for the site. The HTTP Object is what you get when you do Net::HTTP.new
-
1
def http
-
@http ||= create_http
-
end
-
-
# Contains the root URI for this site
-
1
def uri(custom_uri = nil)
-
if custom_uri
-
@uri = custom_uri
-
@http = create_http # yike, oh well. less intrusive this way
-
else # if no custom passed, we use existing, which, if unset, is set to site uri
-
@uri ||= URI.parse(site)
-
end
-
end
-
-
1
def get_access_token(request_token, request_options = {}, *arguments, &block)
-
response = token_request(http_method, (access_token_url? ? access_token_url : access_token_path), request_token, request_options, *arguments, &block)
-
OAuth::AccessToken.from_hash(self, response)
-
end
-
-
# Makes a request to the service for a new OAuth::RequestToken
-
#
-
# @request_token = @consumer.get_request_token
-
#
-
# To include OAuth parameters:
-
#
-
# @request_token = @consumer.get_request_token \
-
# :oauth_callback => "http://example.com/cb"
-
#
-
# To include application-specific parameters:
-
#
-
# @request_token = @consumer.get_request_token({}, :foo => "bar")
-
#
-
# TODO oauth_callback should be a mandatory parameter
-
1
def get_request_token(request_options = {}, *arguments, &block)
-
# if oauth_callback wasn't provided, it is assumed that oauth_verifiers
-
# will be exchanged out of band
-
request_options[:oauth_callback] ||= OAuth::OUT_OF_BAND unless request_options[:exclude_callback]
-
-
if block_given?
-
response = token_request(http_method,
-
(request_token_url? ? request_token_url : request_token_path),
-
nil,
-
request_options,
-
*arguments, &block)
-
else
-
response = token_request(http_method, (request_token_url? ? request_token_url : request_token_path), nil, request_options, *arguments)
-
end
-
OAuth::RequestToken.from_hash(self, response)
-
end
-
-
# Creates, signs and performs an http request.
-
# It's recommended to use the OAuth::Token classes to set this up correctly.
-
# request_options take precedence over consumer-wide options when signing
-
# a request.
-
# arguments are POST and PUT bodies (a Hash, string-encoded parameters, or
-
# absent), followed by additional HTTP headers.
-
#
-
# @consumer.request(:get, '/people', @token, { :scheme => :query_string })
-
# @consumer.request(:post, '/people', @token, {}, @person.to_xml, { 'Content-Type' => 'application/xml' })
-
#
-
1
def request(http_method, path, token = nil, request_options = {}, *arguments)
-
if path !~ /^\//
-
@http = create_http(path)
-
_uri = URI.parse(path)
-
path = "#{_uri.path}#{_uri.query ? "?#{_uri.query}" : ""}"
-
end
-
-
# override the request with your own, this is useful for file uploads which Net::HTTP does not do
-
req = create_signed_request(http_method, path, token, request_options, *arguments)
-
return nil if block_given? and yield(req) == :done
-
rsp = http.request(req)
-
# check for an error reported by the Problem Reporting extension
-
# (http://wiki.oauth.net/ProblemReporting)
-
# note: a 200 may actually be an error; check for an oauth_problem key to be sure
-
if !(headers = rsp.to_hash["www-authenticate"]).nil? &&
-
(h = headers.select { |hdr| hdr =~ /^OAuth / }).any? &&
-
h.first =~ /oauth_problem/
-
-
# puts "Header: #{h.first}"
-
-
# TODO doesn't handle broken responses from api.login.yahoo.com
-
# remove debug code when done
-
params = OAuth::Helper.parse_header(h.first)
-
-
# puts "Params: #{params.inspect}"
-
# puts "Body: #{rsp.body}"
-
-
raise OAuth::Problem.new(params.delete("oauth_problem"), rsp, params)
-
end
-
-
rsp
-
end
-
-
# Creates and signs an http request.
-
# It's recommended to use the Token classes to set this up correctly
-
1
def create_signed_request(http_method, path, token = nil, request_options = {}, *arguments)
-
request = create_http_request(http_method, path, *arguments)
-
sign!(request, token, request_options)
-
request
-
end
-
-
# Creates a request and parses the result as url_encoded. This is used internally for the RequestToken and AccessToken requests.
-
1
def token_request(http_method, path, token = nil, request_options = {}, *arguments)
-
response = request(http_method, path, token, request_options, *arguments)
-
case response.code.to_i
-
-
when (200..299)
-
if block_given?
-
yield response.body
-
else
-
# symbolize keys
-
# TODO this could be considered unexpected behavior; symbols or not?
-
# TODO this also drops subsequent values from multi-valued keys
-
CGI.parse(response.body).inject({}) do |h,(k,v)|
-
h[k.strip.to_sym] = v.first
-
h[k.strip] = v.first
-
h
-
end
-
end
-
when (300..399)
-
# this is a redirect
-
uri = URI.parse(response.header['location'])
-
response.error! if uri.path == path # careful of those infinite redirects
-
self.token_request(http_method, uri.path, token, request_options, arguments)
-
when (400..499)
-
raise OAuth::Unauthorized, response
-
else
-
response.error!
-
end
-
end
-
-
# Sign the Request object. Use this if you have an externally generated http request object you want to sign.
-
1
def sign!(request, token = nil, request_options = {})
-
request.oauth!(http, self, token, options.merge(request_options))
-
end
-
-
# Return the signature_base_string
-
1
def signature_base_string(request, token = nil, request_options = {})
-
request.signature_base_string(http, self, token, options.merge(request_options))
-
end
-
-
1
def site
-
@options[:site].to_s
-
end
-
-
1
def request_endpoint
-
return nil if @options[:request_endpoint].nil?
-
@options[:request_endpoint].to_s
-
end
-
-
1
def scheme
-
@options[:scheme]
-
end
-
-
1
def request_token_path
-
@options[:request_token_path]
-
end
-
-
1
def authorize_path
-
@options[:authorize_path]
-
end
-
-
1
def access_token_path
-
@options[:access_token_path]
-
end
-
-
# TODO this is ugly, rewrite
-
1
def request_token_url
-
@options[:request_token_url] || site + request_token_path
-
end
-
-
1
def request_token_url?
-
@options.has_key?(:request_token_url)
-
end
-
-
1
def authorize_url
-
@options[:authorize_url] || site + authorize_path
-
end
-
-
1
def authorize_url?
-
@options.has_key?(:authorize_url)
-
end
-
-
1
def access_token_url
-
@options[:access_token_url] || site + access_token_path
-
end
-
-
1
def access_token_url?
-
@options.has_key?(:access_token_url)
-
end
-
-
1
def proxy
-
@options[:proxy]
-
end
-
-
1
protected
-
-
# Instantiates the http object
-
1
def create_http(_url = nil)
-
-
-
if !request_endpoint.nil?
-
_url = request_endpoint
-
end
-
-
-
if _url.nil? || _url[0] =~ /^\//
-
our_uri = URI.parse(site)
-
else
-
our_uri = URI.parse(_url)
-
end
-
-
-
if proxy.nil?
-
http_object = Net::HTTP.new(our_uri.host, our_uri.port)
-
else
-
proxy_uri = proxy.is_a?(URI) ? proxy : URI.parse(proxy)
-
http_object = Net::HTTP.new(our_uri.host, our_uri.port, proxy_uri.host, proxy_uri.port, proxy_uri.user, proxy_uri.password)
-
end
-
-
http_object.use_ssl = (our_uri.scheme == 'https')
-
-
if @options[:ca_file] || CA_FILE
-
http_object.ca_file = @options[:ca_file] || CA_FILE
-
http_object.verify_mode = OpenSSL::SSL::VERIFY_PEER
-
http_object.verify_depth = 5
-
else
-
http_object.verify_mode = OpenSSL::SSL::VERIFY_NONE
-
end
-
-
http_object.read_timeout = http_object.open_timeout = @options[:timeout] || 30
-
http_object.open_timeout = @options[:open_timeout] if @options[:open_timeout]
-
-
http_object
-
end
-
-
# create the http request object for a given http_method and path
-
1
def create_http_request(http_method, path, *arguments)
-
http_method = http_method.to_sym
-
-
if [:post, :put].include?(http_method)
-
data = arguments.shift
-
end
-
-
# if the base site contains a path, add it now
-
uri = URI.parse(site)
-
path = uri.path + path if uri.path && uri.path != '/'
-
-
headers = arguments.first.is_a?(Hash) ? arguments.shift : {}
-
-
case http_method
-
when :post
-
request = Net::HTTP::Post.new(path,headers)
-
request["Content-Length"] = '0' # Default to 0
-
when :put
-
request = Net::HTTP::Put.new(path,headers)
-
request["Content-Length"] = '0' # Default to 0
-
when :get
-
request = Net::HTTP::Get.new(path,headers)
-
when :delete
-
request = Net::HTTP::Delete.new(path,headers)
-
when :head
-
request = Net::HTTP::Head.new(path,headers)
-
else
-
raise ArgumentError, "Don't know how to handle http_method: :#{http_method.to_s}"
-
end
-
-
if data.is_a?(Hash)
-
request.body = OAuth::Helper.normalize(data)
-
request.content_type = 'application/x-www-form-urlencoded'
-
elsif data
-
if data.respond_to?(:read)
-
request.body_stream = data
-
if data.respond_to?(:length)
-
request["Content-Length"] = data.length.to_s
-
elsif data.respond_to?(:stat) && data.stat.respond_to?(:size)
-
request["Content-Length"] = data.stat.size.to_s
-
else
-
raise ArgumentError, "Don't know how to send a body_stream that doesn't respond to .length or .stat.size"
-
end
-
else
-
request.body = data.to_s
-
request["Content-Length"] = request.body.length.to_s
-
end
-
end
-
-
request
-
end
-
-
1
def marshal_dump(*args)
-
{:key => @key, :secret => @secret, :options => @options}
-
end
-
-
1
def marshal_load(data)
-
initialize(data[:key], data[:secret], data[:options])
-
end
-
-
end
-
end
-
# these are to backport methods from 1.8.7/1.9.1 to 1.8.6
-
-
1
class Object
-
-
1
unless method_defined?(:tap)
-
def tap
-
yield self
-
self
-
end
-
end
-
-
end
-
-
1
class String
-
-
-
-
1
unless method_defined?(:bytesize)
-
def bytesize
-
self.size
-
end
-
end
-
-
1
unless method_defined?(:bytes)
-
def bytes
-
require 'enumerator'
-
Enumerable::Enumerator.new(self, :each_byte)
-
end
-
end
-
-
end
-
1
require 'oauth/errors/error'
-
1
require 'oauth/errors/unauthorized'
-
1
require 'oauth/errors/problem'
-
1
module OAuth
-
1
class Error < StandardError
-
end
-
end
-
1
module OAuth
-
1
class Problem < OAuth::Unauthorized
-
1
attr_reader :problem, :params
-
1
def initialize(problem, request = nil, params = {})
-
super(request)
-
@problem = problem
-
@params = params
-
end
-
-
1
def to_s
-
problem
-
end
-
end
-
end
-
1
module OAuth
-
1
class Unauthorized < OAuth::Error
-
1
attr_reader :request
-
1
def initialize(request = nil)
-
@request = request
-
end
-
-
1
def to_s
-
[request.code, request.message] * " "
-
end
-
end
-
end
-
1
require 'openssl'
-
1
require 'base64'
-
-
1
module OAuth
-
1
module Helper
-
1
extend self
-
-
# Escape +value+ by URL encoding all non-reserved character.
-
#
-
# See Also: {OAuth core spec version 1.0, section 5.1}[http://oauth.net/core/1.0#rfc.section.5.1]
-
1
def escape(value)
-
URI::escape(value.to_s, OAuth::RESERVED_CHARACTERS)
-
rescue ArgumentError
-
URI::escape(value.to_s.force_encoding(Encoding::UTF_8), OAuth::RESERVED_CHARACTERS)
-
end
-
-
# Generate a random key of up to +size+ bytes. The value returned is Base64 encoded with non-word
-
# characters removed.
-
1
def generate_key(size=32)
-
Base64.encode64(OpenSSL::Random.random_bytes(size)).gsub(/\W/, '')
-
end
-
-
1
alias_method :generate_nonce, :generate_key
-
-
1
def generate_timestamp #:nodoc:
-
Time.now.to_i.to_s
-
end
-
-
# Normalize a +Hash+ of parameter values. Parameters are sorted by name, using lexicographical
-
# byte value ordering. If two or more parameters share the same name, they are sorted by their value.
-
# Parameters are concatenated in their sorted order into a single string. For each parameter, the name
-
# is separated from the corresponding value by an "=" character, even if the value is empty. Each
-
# name-value pair is separated by an "&" character.
-
#
-
# See Also: {OAuth core spec version 1.0, section 9.1.1}[http://oauth.net/core/1.0#rfc.section.9.1.1]
-
1
def normalize(params)
-
params.sort.map do |k, values|
-
if values.is_a?(Array)
-
# make sure the array has an element so we don't lose the key
-
values << nil if values.empty?
-
# multiple values were provided for a single key
-
values.sort.collect do |v|
-
[escape(k),escape(v)] * "="
-
end
-
elsif values.is_a?(Hash)
-
normalize_nested_query(values, k)
-
else
-
[escape(k),escape(values)] * "="
-
end
-
end * "&"
-
end
-
-
#Returns a string representation of the Hash like in URL query string
-
# build_nested_query({:level_1 => {:level_2 => ['value_1','value_2']}}, 'prefix'))
-
# #=> ["prefix%5Blevel_1%5D%5Blevel_2%5D%5B%5D=value_1", "prefix%5Blevel_1%5D%5Blevel_2%5D%5B%5D=value_2"]
-
1
def normalize_nested_query(value, prefix = nil)
-
case value
-
when Array
-
value.map do |v|
-
normalize_nested_query(v, "#{prefix}[]")
-
end.flatten.sort
-
when Hash
-
value.map do |k, v|
-
normalize_nested_query(v, prefix ? "#{prefix}[#{k}]" : k)
-
end.flatten.sort
-
else
-
[escape(prefix), escape(value)] * "="
-
end
-
end
-
-
# Parse an Authorization / WWW-Authenticate header into a hash. Takes care of unescaping and
-
# removing surrounding quotes. Raises a OAuth::Problem if the header is not parsable into a
-
# valid hash. Does not validate the keys or values.
-
#
-
# hash = parse_header(headers['Authorization'] || headers['WWW-Authenticate'])
-
# hash['oauth_timestamp']
-
# #=>"1234567890"
-
#
-
1
def parse_header(header)
-
# decompose
-
params = header[6,header.length].split(/[,=&]/)
-
-
# odd number of arguments - must be a malformed header.
-
raise OAuth::Problem.new("Invalid authorization header") if params.size % 2 != 0
-
-
params.map! do |v|
-
# strip and unescape
-
val = unescape(v.strip)
-
# strip quotes
-
val.sub(/^\"(.*)\"$/, '\1')
-
end
-
-
# convert into a Hash
-
Hash[*params.flatten]
-
end
-
-
1
def unescape(value)
-
URI.unescape(value.gsub('+', '%2B'))
-
end
-
-
1
def stringify_keys(hash)
-
new_h = {}
-
hash.each do |k, v|
-
new_h[k.to_s] = v.is_a?(Hash) ? stringify_keys(v) : v
-
end
-
new_h
-
end
-
end
-
end
-
1
module OAuth
-
# request tokens are passed between the consumer and the provider out of
-
# band (i.e. callbacks cannot be used), per section 6.1.1
-
1
OUT_OF_BAND = "oob"
-
-
# required parameters, per sections 6.1.1, 6.3.1, and 7
-
1
PARAMETERS = %w(oauth_callback oauth_consumer_key oauth_token
-
oauth_signature_method oauth_timestamp oauth_nonce oauth_verifier
-
oauth_version oauth_signature oauth_body_hash)
-
-
# reserved character regexp, per section 5.1
-
1
RESERVED_CHARACTERS = /[^a-zA-Z0-9\-\.\_\~]/
-
end
-
1
module OAuth
-
1
module RequestProxy
-
1
def self.available_proxies #:nodoc:
-
2
@available_proxies ||= {}
-
end
-
-
1
def self.proxy(request, options = {})
-
return request if request.kind_of?(OAuth::RequestProxy::Base)
-
-
klass = available_proxies[request.class]
-
-
# Search for possible superclass matches.
-
if klass.nil?
-
request_parent = available_proxies.keys.find { |rc| request.kind_of?(rc) }
-
klass = available_proxies[request_parent]
-
end
-
-
raise UnknownRequestType, request.class.to_s unless klass
-
klass.new(request, options)
-
end
-
-
1
class UnknownRequestType < Exception; end
-
end
-
end
-
1
require 'oauth/request_proxy'
-
1
require 'oauth/helper'
-
-
1
module OAuth::RequestProxy
-
1
class Base
-
1
include OAuth::Helper
-
-
1
def self.proxies(klass)
-
2
OAuth::RequestProxy.available_proxies[klass] = self
-
end
-
-
1
attr_accessor :request, :options, :unsigned_parameters
-
-
1
def initialize(request, options = {})
-
@request = request
-
@unsigned_parameters = (options[:unsigned_parameters] || []).map {|param| param.to_s}
-
@options = options
-
end
-
-
## OAuth parameters
-
-
1
def oauth_callback
-
parameters['oauth_callback']
-
end
-
-
1
def oauth_consumer_key
-
parameters['oauth_consumer_key']
-
end
-
-
1
def oauth_nonce
-
parameters['oauth_nonce']
-
end
-
-
1
def oauth_signature
-
# TODO can this be nil?
-
[parameters['oauth_signature']].flatten.first || ""
-
end
-
-
1
def oauth_signature_method
-
case parameters['oauth_signature_method']
-
when Array
-
parameters['oauth_signature_method'].first
-
else
-
parameters['oauth_signature_method']
-
end
-
end
-
-
1
def oauth_timestamp
-
parameters['oauth_timestamp']
-
end
-
-
1
def oauth_token
-
parameters['oauth_token']
-
end
-
-
1
def oauth_verifier
-
parameters['oauth_verifier']
-
end
-
-
1
def oauth_version
-
parameters["oauth_version"]
-
end
-
-
# TODO deprecate these
-
1
alias_method :consumer_key, :oauth_consumer_key
-
1
alias_method :token, :oauth_token
-
1
alias_method :nonce, :oauth_nonce
-
1
alias_method :timestamp, :oauth_timestamp
-
1
alias_method :signature, :oauth_signature
-
1
alias_method :signature_method, :oauth_signature_method
-
-
## Parameter accessors
-
-
1
def parameters
-
raise NotImplementedError, "Must be implemented by subclasses"
-
end
-
-
1
def parameters_for_signature
-
parameters.reject { |k,v| k == "oauth_signature" || unsigned_parameters.include?(k)}
-
end
-
-
1
def oauth_parameters
-
parameters.select { |k,v| OAuth::PARAMETERS.include?(k) }.reject { |k,v| v == "" }
-
end
-
-
1
def non_oauth_parameters
-
parameters.reject { |k,v| OAuth::PARAMETERS.include?(k) }
-
end
-
-
# See 9.1.2 in specs
-
1
def normalized_uri
-
u = URI.parse(uri)
-
"#{u.scheme.downcase}://#{u.host.downcase}#{(u.scheme.downcase == 'http' && u.port != 80) || (u.scheme.downcase == 'https' && u.port != 443) ? ":#{u.port}" : ""}#{(u.path && u.path != '') ? u.path : '/'}"
-
end
-
-
# See 9.1.1. in specs Normalize Request Parameters
-
1
def normalized_parameters
-
normalize(parameters_for_signature)
-
end
-
-
1
def sign(options = {})
-
OAuth::Signature.sign(self, options)
-
end
-
-
1
def sign!(options = {})
-
parameters["oauth_signature"] = sign(options)
-
@signed = true
-
signature
-
end
-
-
# See 9.1 in specs
-
1
def signature_base_string
-
base = [method, normalized_uri, normalized_parameters]
-
base.map { |v| escape(v) }.join("&")
-
end
-
-
# Has this request been signed yet?
-
1
def signed?
-
@signed
-
end
-
-
# URI, including OAuth parameters
-
1
def signed_uri(with_oauth = true)
-
if signed?
-
if with_oauth
-
params = parameters
-
else
-
params = non_oauth_parameters
-
end
-
-
[uri, normalize(params)] * "?"
-
else
-
STDERR.puts "This request has not yet been signed!"
-
end
-
end
-
-
# Authorization header for OAuth
-
1
def oauth_header(options = {})
-
header_params_str = oauth_parameters.map { |k,v| "#{k}=\"#{escape(v)}\"" }.join(', ')
-
-
realm = "realm=\"#{options[:realm]}\", " if options[:realm]
-
"OAuth #{realm}#{header_params_str}"
-
end
-
-
1
def query_string_blank?
-
if uri = request.request_uri
-
uri.split('?', 2)[1].nil?
-
else
-
request.query_string.blank?
-
end
-
end
-
-
1
protected
-
-
1
def header_params
-
%w( X-HTTP_AUTHORIZATION Authorization HTTP_AUTHORIZATION ).each do |header|
-
next unless request.env.include?(header)
-
-
header = request.env[header]
-
next unless header[0,6] == 'OAuth '
-
-
# parse the header into a Hash
-
oauth_params = OAuth::Helper.parse_header(header)
-
-
# remove non-OAuth parameters
-
oauth_params.reject! { |k,v| k !~ /^oauth_/ }
-
-
return oauth_params
-
end
-
-
return {}
-
end
-
end
-
end
-
1
require 'oauth/request_proxy/base'
-
-
1
module OAuth
-
1
module RequestProxy
-
# RequestProxy for Hashes to facilitate simpler signature creation.
-
# Usage:
-
# request = OAuth::RequestProxy.proxy \
-
# "method" => "iq",
-
# "uri" => [from, to] * "&",
-
# "parameters" => {
-
# "oauth_consumer_key" => oauth_consumer_key,
-
# "oauth_token" => oauth_token,
-
# "oauth_signature_method" => "HMAC-SHA1"
-
# }
-
#
-
# signature = OAuth::Signature.sign \
-
# request,
-
# :consumer_secret => oauth_consumer_secret,
-
# :token_secret => oauth_token_secret,
-
1
class MockRequest < OAuth::RequestProxy::Base
-
1
proxies Hash
-
-
1
def parameters
-
@request["parameters"]
-
end
-
-
1
def method
-
@request["method"]
-
end
-
-
1
def normalized_uri
-
super
-
rescue
-
# if this is a non-standard URI, it may not parse properly
-
# in that case, assume that it's already been normalized
-
uri
-
end
-
-
1
def uri
-
@request["uri"]
-
end
-
end
-
end
-
end
-
1
require 'oauth/request_proxy/base'
-
1
require 'net/http'
-
1
require 'uri'
-
1
require 'cgi'
-
-
1
module OAuth::RequestProxy::Net
-
1
module HTTP
-
1
class HTTPRequest < OAuth::RequestProxy::Base
-
1
proxies ::Net::HTTPGenericRequest
-
-
1
def method
-
request.method
-
end
-
-
1
def uri
-
options[:uri].to_s
-
end
-
-
1
def parameters
-
if options[:clobber_request]
-
options[:parameters]
-
else
-
all_parameters
-
end
-
end
-
-
1
def body
-
request.body
-
end
-
-
1
private
-
-
1
def all_parameters
-
request_params = CGI.parse(query_string)
-
# request_params.each{|k,v| request_params[k] = [nil] if v == []}
-
-
if options[:parameters]
-
options[:parameters].each do |k,v|
-
if request_params.has_key?(k) && v
-
request_params[k] << v
-
else
-
request_params[k] = [v]
-
end
-
end
-
end
-
request_params
-
end
-
-
1
def query_string
-
params = [ query_params, auth_header_params ]
-
params << post_params if (method.to_s.upcase == 'POST' || method.to_s.upcase == 'PUT') && form_url_encoded?
-
params.compact.join('&')
-
end
-
-
1
def form_url_encoded?
-
request['Content-Type'] != nil && request['Content-Type'].to_s.downcase.start_with?('application/x-www-form-urlencoded')
-
end
-
-
1
def query_params
-
URI.parse(request.path).query
-
end
-
-
1
def post_params
-
request.body
-
end
-
-
1
def auth_header_params
-
return nil unless request['Authorization'] && request['Authorization'][0,5] == 'OAuth'
-
auth_params = request['Authorization']
-
end
-
end
-
end
-
end
-
1
module OAuth
-
1
module Signature
-
# Returns a list of available signature methods
-
1
def self.available_methods
-
2
@available_methods ||= {}
-
end
-
-
# Build a signature from a +request+.
-
#
-
# Raises UnknownSignatureMethod exception if the signature method is unknown.
-
1
def self.build(request, options = {}, &block)
-
request = OAuth::RequestProxy.proxy(request, options)
-
klass = available_methods[
-
(request.signature_method ||
-
((c = request.options[:consumer]) && c.options[:signature_method]) ||
-
"").downcase]
-
raise UnknownSignatureMethod, request.signature_method unless klass
-
klass.new(request, options, &block)
-
end
-
-
# Sign a +request+
-
1
def self.sign(request, options = {}, &block)
-
self.build(request, options, &block).signature
-
end
-
-
# Verify the signature of +request+
-
1
def self.verify(request, options = {}, &block)
-
self.build(request, options, &block).verify
-
end
-
-
# Create the signature base string for +request+. This string is the normalized parameter information.
-
#
-
# See Also: {OAuth core spec version 1.0, section 9.1.1}[http://oauth.net/core/1.0#rfc.section.9.1.1]
-
1
def self.signature_base_string(request, options = {}, &block)
-
self.build(request, options, &block).signature_base_string
-
end
-
-
# Create the body hash for a request
-
1
def self.body_hash(request, options = {}, &block)
-
self.build(request, options, &block).body_hash
-
end
-
-
1
class UnknownSignatureMethod < Exception; end
-
end
-
end
-
1
require 'oauth/signature'
-
1
require 'oauth/helper'
-
1
require 'oauth/request_proxy/base'
-
1
require 'base64'
-
-
1
module OAuth::Signature
-
1
class Base
-
1
include OAuth::Helper
-
-
1
attr_accessor :options
-
1
attr_reader :token_secret, :consumer_secret, :request
-
-
1
def self.implements(signature_method = nil)
-
2
return @implements if signature_method.nil?
-
2
@implements = signature_method
-
2
OAuth::Signature.available_methods[@implements] = self
-
end
-
-
1
def self.digest_class(digest_class = nil)
-
return @digest_class if digest_class.nil?
-
@digest_class = digest_class
-
end
-
-
1
def self.digest_klass(digest_klass = nil)
-
1
return @digest_klass if digest_klass.nil?
-
1
@digest_klass = digest_klass
-
end
-
-
1
def self.hash_class(hash_class = nil)
-
2
return @hash_class if hash_class.nil?
-
2
@hash_class = hash_class
-
end
-
-
1
def initialize(request, options = {}, &block)
-
raise TypeError unless request.kind_of?(OAuth::RequestProxy::Base)
-
@request = request
-
@options = options
-
-
## consumer secret was determined beforehand
-
-
@consumer_secret = options[:consumer].secret if options[:consumer]
-
-
# presence of :consumer_secret option will override any Consumer that's provided
-
@consumer_secret = options[:consumer_secret] if options[:consumer_secret]
-
-
## token secret was determined beforehand
-
-
@token_secret = options[:token].secret if options[:token]
-
-
# presence of :token_secret option will override any Token that's provided
-
@token_secret = options[:token_secret] if options[:token_secret]
-
-
# override secrets based on the values returned from the block (if any)
-
if block_given?
-
# consumer secret and token secret need to be looked up based on pieces of the request
-
secrets = yield block.arity == 1 ? request : [token, consumer_key, nonce, request.timestamp]
-
if secrets.is_a?(Array) && secrets.size == 2
-
@token_secret = secrets[0]
-
@consumer_secret = secrets[1]
-
end
-
end
-
end
-
-
1
def signature
-
Base64.encode64(digest).chomp.gsub(/\n/,'')
-
end
-
-
1
def ==(cmp_signature)
-
Base64.decode64(signature) == Base64.decode64(cmp_signature)
-
end
-
-
1
def verify
-
self == self.request.signature
-
end
-
-
1
def signature_base_string
-
request.signature_base_string
-
end
-
-
1
def body_hash
-
if self.class.hash_class
-
Base64.encode64(self.class.hash_class.digest(request.body || '')).chomp.gsub(/\n/,'')
-
else
-
nil # no body hash algorithm defined, so don't generate one
-
end
-
end
-
-
1
private
-
-
1
def token
-
request.token
-
end
-
-
1
def consumer_key
-
request.consumer_key
-
end
-
-
1
def nonce
-
request.nonce
-
end
-
-
1
def secret
-
"#{escape(consumer_secret)}&#{escape(token_secret)}"
-
end
-
-
1
def digest
-
self.class.digest_class.digest(signature_base_string)
-
end
-
end
-
end
-
# -*- encoding: utf-8 -*-
-
-
1
require 'oauth/signature/base'
-
1
require 'digest/hmac'
-
-
1
module OAuth::Signature::HMAC
-
1
class Base < OAuth::Signature::Base
-
-
1
private
-
1
def digest
-
self.class.digest_class Object.module_eval("::Digest::#{self.class.digest_klass}")
-
Digest::HMAC.digest(signature_base_string, secret, self.class.digest_class)
-
end
-
end
-
end
-
1
require 'oauth/signature/hmac/base'
-
-
1
module OAuth::Signature::HMAC
-
1
class SHA1 < Base
-
1
implements 'hmac-sha1'
-
1
digest_klass 'SHA1'
-
1
hash_class ::Digest::SHA1
-
end
-
end
-
1
require 'oauth/signature/base'
-
1
require 'openssl'
-
-
1
module OAuth::Signature::RSA
-
1
class SHA1 < OAuth::Signature::Base
-
1
implements 'rsa-sha1'
-
1
hash_class ::Digest::SHA1
-
-
1
def ==(cmp_signature)
-
public_key.verify(OpenSSL::Digest::SHA1.new, Base64.decode64(cmp_signature.is_a?(Array) ? cmp_signature.first : cmp_signature), signature_base_string)
-
end
-
-
1
def public_key
-
if consumer_secret.is_a?(String)
-
decode_public_key
-
elsif consumer_secret.is_a?(OpenSSL::X509::Certificate)
-
consumer_secret.public_key
-
else
-
consumer_secret
-
end
-
end
-
-
1
private
-
-
1
def decode_public_key
-
case consumer_secret
-
when /-----BEGIN CERTIFICATE-----/
-
OpenSSL::X509::Certificate.new( consumer_secret).public_key
-
else
-
OpenSSL::PKey::RSA.new( consumer_secret)
-
end
-
end
-
-
1
def digest
-
private_key = OpenSSL::PKey::RSA.new(
-
if options[:private_key_file]
-
IO.read(options[:private_key_file])
-
else
-
consumer_secret
-
end
-
)
-
-
private_key.sign(OpenSSL::Digest::SHA1.new, signature_base_string)
-
end
-
end
-
end
-
# this exists for backwards-compatibility
-
-
1
require 'oauth/tokens/token'
-
1
require 'oauth/tokens/server_token'
-
1
require 'oauth/tokens/consumer_token'
-
1
require 'oauth/tokens/request_token'
-
1
require 'oauth/tokens/access_token'
-
1
module OAuth
-
# The Access Token is used for the actual "real" web service calls that you perform against the server
-
1
class AccessToken < ConsumerToken
-
# The less intrusive way. Otherwise, if we are to do it correctly inside consumer,
-
# we need to restructure and touch more methods: request(), sign!(), etc.
-
1
def request(http_method, path, *arguments)
-
request_uri = URI.parse(path)
-
site_uri = consumer.uri
-
is_service_uri_different = (request_uri.absolute? && request_uri != site_uri)
-
begin
-
consumer.uri(request_uri) if is_service_uri_different
-
@response = super(http_method, path, *arguments)
-
ensure
-
# NOTE: reset for wholesomeness? meaning that we admit only AccessToken service calls may use different URIs?
-
# so reset in case consumer is still used for other token-management tasks subsequently?
-
consumer.uri(site_uri) if is_service_uri_different
-
end
-
@response
-
end
-
-
# Make a regular GET request using AccessToken
-
#
-
# @response = @token.get('/people')
-
# @response = @token.get('/people', { 'Accept'=>'application/xml' })
-
#
-
1
def get(path, headers = {})
-
request(:get, path, headers)
-
end
-
-
# Make a regular HEAD request using AccessToken
-
#
-
# @response = @token.head('/people')
-
#
-
1
def head(path, headers = {})
-
request(:head, path, headers)
-
end
-
-
# Make a regular POST request using AccessToken
-
#
-
# @response = @token.post('/people')
-
# @response = @token.post('/people', { :name => 'Bob', :email => 'bob@mailinator.com' })
-
# @response = @token.post('/people', { :name => 'Bob', :email => 'bob@mailinator.com' }, { 'Accept' => 'application/xml' })
-
# @response = @token.post('/people', nil, {'Accept' => 'application/xml' })
-
# @response = @token.post('/people', @person.to_xml, { 'Accept'=>'application/xml', 'Content-Type' => 'application/xml' })
-
#
-
1
def post(path, body = '', headers = {})
-
request(:post, path, body, headers)
-
end
-
-
# Make a regular PUT request using AccessToken
-
#
-
# @response = @token.put('/people/123')
-
# @response = @token.put('/people/123', { :name => 'Bob', :email => 'bob@mailinator.com' })
-
# @response = @token.put('/people/123', { :name => 'Bob', :email => 'bob@mailinator.com' }, { 'Accept' => 'application/xml' })
-
# @response = @token.put('/people/123', nil, { 'Accept' => 'application/xml' })
-
# @response = @token.put('/people/123', @person.to_xml, { 'Accept' => 'application/xml', 'Content-Type' => 'application/xml' })
-
#
-
1
def put(path, body = '', headers = {})
-
request(:put, path, body, headers)
-
end
-
-
# Make a regular DELETE request using AccessToken
-
#
-
# @response = @token.delete('/people/123')
-
# @response = @token.delete('/people/123', { 'Accept' => 'application/xml' })
-
#
-
1
def delete(path, headers = {})
-
request(:delete, path, headers)
-
end
-
end
-
end
-
1
module OAuth
-
# Superclass for tokens used by OAuth Clients
-
1
class ConsumerToken < Token
-
1
attr_accessor :consumer, :params
-
1
attr_reader :response
-
-
1
def self.from_hash(consumer, hash)
-
token = self.new(consumer, hash[:oauth_token], hash[:oauth_token_secret])
-
token.params = hash
-
token
-
end
-
-
1
def initialize(consumer, token="", secret="")
-
super(token, secret)
-
@consumer = consumer
-
@params = {}
-
end
-
-
# Make a signed request using given http_method to the path
-
#
-
# @token.request(:get, '/people')
-
# @token.request(:post, '/people', @person.to_xml, { 'Content-Type' => 'application/xml' })
-
#
-
1
def request(http_method, path, *arguments)
-
@response = consumer.request(http_method, path, self, {}, *arguments)
-
end
-
-
# Sign a request generated elsewhere using Net:HTTP::Post.new or friends
-
1
def sign!(request, options = {})
-
consumer.sign!(request, self, options)
-
end
-
end
-
end
-
1
module OAuth
-
# The RequestToken is used for the initial Request.
-
# This is normally created by the Consumer object.
-
1
class RequestToken < ConsumerToken
-
-
# Generate an authorization URL for user authorization
-
1
def authorize_url(params = nil)
-
params = (params || {}).merge(:oauth_token => self.token)
-
build_authorize_url(consumer.authorize_url, params)
-
end
-
-
1
def callback_confirmed?
-
params[:oauth_callback_confirmed] == "true"
-
end
-
-
# exchange for AccessToken on server
-
1
def get_access_token(options = {}, *arguments)
-
response = consumer.token_request(consumer.http_method, (consumer.access_token_url? ? consumer.access_token_url : consumer.access_token_path), self, options, *arguments)
-
OAuth::AccessToken.from_hash(consumer, response)
-
end
-
-
1
protected
-
-
# construct an authorization url
-
1
def build_authorize_url(base_url, params)
-
uri = URI.parse(base_url.to_s)
-
# TODO doesn't handle array values correctly
-
uri.query = params.map { |k,v| [k, CGI.escape(v)] * "=" } * "&"
-
uri.to_s
-
end
-
end
-
end
-
1
module OAuth
-
# Used on the server for generating tokens
-
1
class ServerToken < Token
-
-
1
def initialize
-
super(generate_key(16), generate_key)
-
end
-
end
-
end
-
1
module OAuth
-
# Superclass for the various tokens used by OAuth
-
1
class Token
-
1
include OAuth::Helper
-
-
1
attr_accessor :token, :secret
-
-
1
def initialize(token, secret)
-
@token = token
-
@secret = secret
-
end
-
-
1
def to_query
-
"oauth_token=#{escape(token)}&oauth_secret=#{escape(secret)}"
-
end
-
end
-
end
-
1
require 'oauth2/error'
-
1
require 'oauth2/client'
-
1
require 'oauth2/strategy/base'
-
1
require 'oauth2/strategy/auth_code'
-
1
require 'oauth2/strategy/implicit'
-
1
require 'oauth2/strategy/password'
-
1
require 'oauth2/strategy/client_credentials'
-
1
require 'oauth2/strategy/assertion'
-
1
require 'oauth2/access_token'
-
1
require 'oauth2/mac_token'
-
1
require 'oauth2/response'
-
1
module OAuth2
-
1
class AccessToken
-
1
attr_reader :client, :token, :expires_in, :expires_at, :params
-
1
attr_accessor :options, :refresh_token
-
-
1
class << self
-
# Initializes an AccessToken from a Hash
-
#
-
# @param [Client] the OAuth2::Client instance
-
# @param [Hash] a hash of AccessToken property values
-
# @return [AccessToken] the initalized AccessToken
-
1
def from_hash(client, hash)
-
new(client, hash.delete('access_token') || hash.delete(:access_token), hash)
-
end
-
-
# Initializes an AccessToken from a key/value application/x-www-form-urlencoded string
-
#
-
# @param [Client] client the OAuth2::Client instance
-
# @param [String] kvform the application/x-www-form-urlencoded string
-
# @return [AccessToken] the initalized AccessToken
-
1
def from_kvform(client, kvform)
-
from_hash(client, Rack::Utils.parse_query(kvform))
-
end
-
end
-
-
# Initalize an AccessToken
-
#
-
# @param [Client] client the OAuth2::Client instance
-
# @param [String] token the Access Token value
-
# @param [Hash] opts the options to create the Access Token with
-
# @option opts [String] :refresh_token (nil) the refresh_token value
-
# @option opts [FixNum, String] :expires_in (nil) the number of seconds in which the AccessToken will expire
-
# @option opts [FixNum, String] :expires_at (nil) the epoch time in seconds in which AccessToken will expire
-
# @option opts [Symbol] :mode (:header) the transmission mode of the Access Token parameter value
-
# one of :header, :body or :query
-
# @option opts [String] :header_format ('Bearer %s') the string format to use for the Authorization header
-
# @option opts [String] :param_name ('access_token') the parameter name to use for transmission of the
-
# Access Token value in :body or :query transmission mode
-
1
def initialize(client, token, opts = {})
-
@client = client
-
@token = token.to_s
-
[:refresh_token, :expires_in, :expires_at].each do |arg|
-
instance_variable_set("@#{arg}", opts.delete(arg) || opts.delete(arg.to_s))
-
end
-
@expires_in ||= opts.delete('expires')
-
@expires_in &&= @expires_in.to_i
-
@expires_at &&= @expires_at.to_i
-
@expires_at ||= Time.now.to_i + @expires_in if @expires_in
-
@options = {:mode => opts.delete(:mode) || :header,
-
:header_format => opts.delete(:header_format) || 'Bearer %s',
-
:param_name => opts.delete(:param_name) || 'access_token'}
-
@params = opts
-
end
-
-
# Indexer to additional params present in token response
-
#
-
# @param [String] key entry key to Hash
-
1
def [](key)
-
@params[key]
-
end
-
-
# Whether or not the token expires
-
#
-
# @return [Boolean]
-
1
def expires?
-
!!@expires_at # rubocop:disable DoubleNegation
-
end
-
-
# Whether or not the token is expired
-
#
-
# @return [Boolean]
-
1
def expired?
-
expires? && (expires_at < Time.now.to_i)
-
end
-
-
# Refreshes the current Access Token
-
#
-
# @return [AccessToken] a new AccessToken
-
# @note options should be carried over to the new AccessToken
-
1
def refresh!(params = {})
-
fail('A refresh_token is not available') unless refresh_token
-
params.merge!(:client_id => @client.id,
-
:client_secret => @client.secret,
-
:grant_type => 'refresh_token',
-
:refresh_token => refresh_token)
-
new_token = @client.get_token(params)
-
new_token.options = options
-
new_token.refresh_token = refresh_token unless new_token.refresh_token
-
new_token
-
end
-
-
# Convert AccessToken to a hash which can be used to rebuild itself with AccessToken.from_hash
-
#
-
# @return [Hash] a hash of AccessToken property values
-
1
def to_hash
-
params.merge(:access_token => token, :refresh_token => refresh_token, :expires_at => expires_at)
-
end
-
-
# Make a request with the Access Token
-
#
-
# @param [Symbol] verb the HTTP request method
-
# @param [String] path the HTTP URL path of the request
-
# @param [Hash] opts the options to make the request with
-
# @see Client#request
-
1
def request(verb, path, opts = {}, &block)
-
self.token = opts
-
@client.request(verb, path, opts, &block)
-
end
-
-
# Make a GET request with the Access Token
-
#
-
# @see AccessToken#request
-
1
def get(path, opts = {}, &block)
-
request(:get, path, opts, &block)
-
end
-
-
# Make a POST request with the Access Token
-
#
-
# @see AccessToken#request
-
1
def post(path, opts = {}, &block)
-
request(:post, path, opts, &block)
-
end
-
-
# Make a PUT request with the Access Token
-
#
-
# @see AccessToken#request
-
1
def put(path, opts = {}, &block)
-
request(:put, path, opts, &block)
-
end
-
-
# Make a PATCH request with the Access Token
-
#
-
# @see AccessToken#request
-
1
def patch(path, opts = {}, &block)
-
request(:patch, path, opts, &block)
-
end
-
-
# Make a DELETE request with the Access Token
-
#
-
# @see AccessToken#request
-
1
def delete(path, opts = {}, &block)
-
request(:delete, path, opts, &block)
-
end
-
-
# Get the headers hash (includes Authorization token)
-
1
def headers
-
{'Authorization' => options[:header_format] % token}
-
end
-
-
1
private
-
-
1
def token=(opts) # rubocop:disable MethodLength
-
case options[:mode]
-
when :header
-
opts[:headers] ||= {}
-
opts[:headers].merge!(headers)
-
when :query
-
opts[:params] ||= {}
-
opts[:params][options[:param_name]] = token
-
when :body
-
opts[:body] ||= {}
-
if opts[:body].is_a?(Hash)
-
opts[:body][options[:param_name]] = token
-
else
-
opts[:body] << "&#{options[:param_name]}=#{token}"
-
end
-
# @todo support for multi-part (file uploads)
-
else
-
fail("invalid :mode option of #{options[:mode]}")
-
end
-
end
-
end
-
end
-
1
require 'faraday'
-
1
require 'logger'
-
-
1
module OAuth2
-
# The OAuth2::Client class
-
1
class Client
-
1
attr_reader :id, :secret, :site
-
1
attr_accessor :options
-
1
attr_writer :connection
-
-
# Instantiate a new OAuth 2.0 client using the
-
# Client ID and Client Secret registered to your
-
# application.
-
#
-
# @param [String] client_id the client_id value
-
# @param [String] client_secret the client_secret value
-
# @param [Hash] opts the options to create the client with
-
# @option opts [String] :site the OAuth2 provider site host
-
# @option opts [String] :authorize_url ('/oauth/authorize') absolute or relative URL path to the Authorization endpoint
-
# @option opts [String] :token_url ('/oauth/token') absolute or relative URL path to the Token endpoint
-
# @option opts [Symbol] :token_method (:post) HTTP method to use to request token (:get or :post)
-
# @option opts [Hash] :connection_opts ({}) Hash of connection options to pass to initialize Faraday with
-
# @option opts [FixNum] :max_redirects (5) maximum number of redirects to follow
-
# @option opts [Boolean] :raise_errors (true) whether or not to raise an OAuth2::Error
-
# on responses with 400+ status codes
-
# @yield [builder] The Faraday connection builder
-
1
def initialize(client_id, client_secret, options = {}, &block)
-
opts = options.dup
-
@id = client_id
-
@secret = client_secret
-
@site = opts.delete(:site)
-
ssl = opts.delete(:ssl)
-
@options = {:authorize_url => '/oauth/authorize',
-
:token_url => '/oauth/token',
-
:token_method => :post,
-
:connection_opts => {},
-
:connection_build => block,
-
:max_redirects => 5,
-
:raise_errors => true}.merge(opts)
-
@options[:connection_opts][:ssl] = ssl if ssl
-
end
-
-
# Set the site host
-
#
-
# @param [String] the OAuth2 provider site host
-
1
def site=(value)
-
@connection = nil
-
@site = value
-
end
-
-
# The Faraday connection object
-
1
def connection
-
@connection ||= begin
-
conn = Faraday.new(site, options[:connection_opts])
-
conn.build do |b|
-
options[:connection_build].call(b)
-
end if options[:connection_build]
-
conn
-
end
-
end
-
-
# The authorize endpoint URL of the OAuth2 provider
-
#
-
# @param [Hash] params additional query parameters
-
1
def authorize_url(params = nil)
-
connection.build_url(options[:authorize_url], params).to_s
-
end
-
-
# The token endpoint URL of the OAuth2 provider
-
#
-
# @param [Hash] params additional query parameters
-
1
def token_url(params = nil)
-
connection.build_url(options[:token_url], params).to_s
-
end
-
-
# Makes a request relative to the specified site root.
-
#
-
# @param [Symbol] verb one of :get, :post, :put, :delete
-
# @param [String] url URL path of request
-
# @param [Hash] opts the options to make the request with
-
# @option opts [Hash] :params additional query parameters for the URL of the request
-
# @option opts [Hash, String] :body the body of the request
-
# @option opts [Hash] :headers http request headers
-
# @option opts [Boolean] :raise_errors whether or not to raise an OAuth2::Error on 400+ status
-
# code response for this request. Will default to client option
-
# @option opts [Symbol] :parse @see Response::initialize
-
# @yield [req] The Faraday request
-
1
def request(verb, url, opts = {}) # rubocop:disable CyclomaticComplexity, MethodLength
-
connection.response :logger, ::Logger.new($stdout) if ENV['OAUTH_DEBUG'] == 'true'
-
-
url = connection.build_url(url, opts[:params]).to_s
-
-
response = connection.run_request(verb, url, opts[:body], opts[:headers]) do |req|
-
yield(req) if block_given?
-
end
-
response = Response.new(response, :parse => opts[:parse])
-
-
case response.status
-
when 301, 302, 303, 307
-
opts[:redirect_count] ||= 0
-
opts[:redirect_count] += 1
-
return response if opts[:redirect_count] > options[:max_redirects]
-
if response.status == 303
-
verb = :get
-
opts.delete(:body)
-
end
-
request(verb, response.headers['location'], opts)
-
when 200..299, 300..399
-
# on non-redirecting 3xx statuses, just return the response
-
response
-
when 400..599
-
error = Error.new(response)
-
fail(error) if opts.fetch(:raise_errors, options[:raise_errors])
-
response.error = error
-
response
-
else
-
error = Error.new(response)
-
fail(error, "Unhandled status code value of #{response.status}")
-
end
-
end
-
-
# Initializes an AccessToken by making a request to the token endpoint
-
#
-
# @param [Hash] params a Hash of params for the token endpoint
-
# @param [Hash] access token options, to pass to the AccessToken object
-
# @param [Class] class of access token for easier subclassing OAuth2::AccessToken
-
# @return [AccessToken] the initalized AccessToken
-
1
def get_token(params, access_token_opts = {}, access_token_class = AccessToken)
-
opts = {:raise_errors => options[:raise_errors], :parse => params.delete(:parse)}
-
if options[:token_method] == :post
-
headers = params.delete(:headers)
-
opts[:body] = params
-
opts[:headers] = {'Content-Type' => 'application/x-www-form-urlencoded'}
-
opts[:headers].merge!(headers) if headers
-
else
-
opts[:params] = params
-
end
-
response = request(options[:token_method], token_url, opts)
-
error = Error.new(response)
-
fail(error) if options[:raise_errors] && !(response.parsed.is_a?(Hash) && response.parsed['access_token'])
-
access_token_class.from_hash(self, response.parsed.merge(access_token_opts))
-
end
-
-
# The Authorization Code strategy
-
#
-
# @see http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-4.1
-
1
def auth_code
-
@auth_code ||= OAuth2::Strategy::AuthCode.new(self)
-
end
-
-
# The Implicit strategy
-
#
-
# @see http://tools.ietf.org/html/draft-ietf-oauth-v2-26#section-4.2
-
1
def implicit
-
@implicit ||= OAuth2::Strategy::Implicit.new(self)
-
end
-
-
# The Resource Owner Password Credentials strategy
-
#
-
# @see http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-4.3
-
1
def password
-
@password ||= OAuth2::Strategy::Password.new(self)
-
end
-
-
# The Client Credentials strategy
-
#
-
# @see http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-4.4
-
1
def client_credentials
-
@client_credentials ||= OAuth2::Strategy::ClientCredentials.new(self)
-
end
-
-
1
def assertion
-
@assertion ||= OAuth2::Strategy::Assertion.new(self)
-
end
-
end
-
end
-
1
module OAuth2
-
1
class Error < StandardError
-
1
attr_reader :response, :code, :description
-
-
# standard error values include:
-
# :invalid_request, :invalid_client, :invalid_token, :invalid_grant, :unsupported_grant_type, :invalid_scope
-
1
def initialize(response)
-
response.error = self
-
@response = response
-
-
message = []
-
-
if response.parsed.is_a?(Hash)
-
@code = response.parsed['error']
-
@description = response.parsed['error_description']
-
message << "#{@code}: #{@description}"
-
end
-
-
message << response.body
-
-
super(message.join("\n"))
-
end
-
end
-
end
-
1
require 'base64'
-
1
require 'digest'
-
1
require 'openssl'
-
1
require 'securerandom'
-
-
1
module OAuth2
-
1
class MACToken < AccessToken
-
# Generates a MACToken from an AccessToken and secret
-
#
-
# @param [AccessToken] token the OAuth2::Token instance
-
# @option [String] secret the secret key value
-
# @param [Hash] opts the options to create the Access Token with
-
# @see MACToken#initialize
-
1
def self.from_access_token(token, secret, options = {})
-
new(token.client, token.token, secret, token.params.merge(
-
:refresh_token => token.refresh_token,
-
:expires_in => token.expires_in,
-
:expires_at => token.expires_at
-
).merge(options))
-
end
-
-
1
attr_reader :secret, :algorithm
-
-
# Initalize a MACToken
-
#
-
# @param [Client] client the OAuth2::Client instance
-
# @param [String] token the Access Token value
-
# @option [String] secret the secret key value
-
# @param [Hash] opts the options to create the Access Token with
-
# @option opts [String] :refresh_token (nil) the refresh_token value
-
# @option opts [FixNum, String] :expires_in (nil) the number of seconds in which the AccessToken will expire
-
# @option opts [FixNum, String] :expires_at (nil) the epoch time in seconds in which AccessToken will expire
-
# @option opts [FixNum, String] :algorithm (hmac-sha-256) the algorithm to use for the HMAC digest (one of 'hmac-sha-256', 'hmac-sha-1')
-
1
def initialize(client, token, secret, opts = {})
-
@secret = secret
-
self.algorithm = opts.delete(:algorithm) || 'hmac-sha-256'
-
-
super(client, token, opts)
-
end
-
-
# Make a request with the MAC Token
-
#
-
# @param [Symbol] verb the HTTP request method
-
# @param [String] path the HTTP URL path of the request
-
# @param [Hash] opts the options to make the request with
-
# @see Client#request
-
1
def request(verb, path, opts = {}, &block)
-
url = client.connection.build_url(path, opts[:params]).to_s
-
-
opts[:headers] ||= {}
-
opts[:headers].merge!('Authorization' => header(verb, url))
-
-
@client.request(verb, path, opts, &block)
-
end
-
-
# Get the headers hash (always an empty hash)
-
1
def headers
-
{}
-
end
-
-
# Generate the MAC header
-
#
-
# @param [Symbol] verb the HTTP request method
-
# @param [String] url the HTTP URL path of the request
-
1
def header(verb, url)
-
timestamp = Time.now.utc.to_i
-
nonce = Digest::MD5.hexdigest([timestamp, SecureRandom.hex].join(':'))
-
-
uri = URI.parse(url)
-
-
fail(ArgumentError, "could not parse \"#{url}\" into URI") unless uri.is_a?(URI::HTTP)
-
-
mac = signature(timestamp, nonce, verb, uri)
-
-
"MAC id=\"#{token}\", ts=\"#{timestamp}\", nonce=\"#{nonce}\", mac=\"#{mac}\""
-
end
-
-
# Generate the Base64-encoded HMAC digest signature
-
#
-
# @param [Fixnum] timestamp the timestamp of the request in seconds since epoch
-
# @param [String] nonce the MAC header nonce
-
# @param [Symbol] verb the HTTP request method
-
# @param [String] url the HTTP URL path of the request
-
1
def signature(timestamp, nonce, verb, uri)
-
signature = [
-
timestamp,
-
nonce,
-
verb.to_s.upcase,
-
uri.request_uri,
-
uri.host,
-
uri.port,
-
'', nil
-
].join("\n")
-
-
strict_encode64(OpenSSL::HMAC.digest(@algorithm, secret, signature))
-
end
-
-
# Set the HMAC algorithm
-
#
-
# @param [String] alg the algorithm to use (one of 'hmac-sha-1', 'hmac-sha-256')
-
1
def algorithm=(alg)
-
@algorithm = case alg.to_s
-
when 'hmac-sha-1'
-
OpenSSL::Digest::SHA1.new
-
when 'hmac-sha-256'
-
OpenSSL::Digest::SHA256.new
-
else
-
fail(ArgumentError, 'Unsupported algorithm')
-
end
-
end
-
-
1
private
-
-
# No-op since we need the verb and path
-
# and the MAC always goes in a header
-
1
def token=(_)
-
end
-
-
# Base64.strict_encode64 is not available on Ruby 1.8.7
-
1
def strict_encode64(str)
-
Base64.encode64(str).gsub("\n", '')
-
end
-
end
-
end
-
1
require 'multi_json'
-
1
require 'multi_xml'
-
1
require 'rack'
-
-
1
module OAuth2
-
# OAuth2::Response class
-
1
class Response
-
1
attr_reader :response
-
1
attr_accessor :error, :options
-
-
# Adds a new content type parser.
-
#
-
# @param [Symbol] key A descriptive symbol key such as :json or :query.
-
# @param [Array] One or more mime types to which this parser applies.
-
# @yield [String] A block returning parsed content.
-
1
def self.register_parser(key, mime_types, &block)
-
1
key = key.to_sym
-
1
PARSERS[key] = block
-
1
Array(mime_types).each do |mime_type|
-
4
CONTENT_TYPES[mime_type] = key
-
end
-
end
-
-
# Initializes a Response instance
-
#
-
# @param [Faraday::Response] response The Faraday response instance
-
# @param [Hash] opts options in which to initialize the instance
-
# @option opts [Symbol] :parse (:automatic) how to parse the response body. one of :query (for x-www-form-urlencoded),
-
# :json, or :automatic (determined by Content-Type response header)
-
1
def initialize(response, opts = {})
-
@response = response
-
@options = {:parse => :automatic}.merge(opts)
-
end
-
-
# The HTTP response headers
-
1
def headers
-
response.headers
-
end
-
-
# The HTTP response status code
-
1
def status
-
response.status
-
end
-
-
# The HTTP response body
-
1
def body
-
response.body || ''
-
end
-
-
# Procs that, when called, will parse a response body according
-
# to the specified format.
-
1
PARSERS = {
-
:json => lambda { |body| MultiJson.load(body) rescue body }, # rubocop:disable RescueModifier
-
:query => lambda { |body| Rack::Utils.parse_query(body) },
-
:text => lambda { |body| body }
-
}
-
-
# Content type assignments for various potential HTTP content types.
-
1
CONTENT_TYPES = {
-
'application/json' => :json,
-
'text/javascript' => :json,
-
'application/x-www-form-urlencoded' => :query,
-
'text/plain' => :text
-
}
-
-
# The parsed response body.
-
# Will attempt to parse application/x-www-form-urlencoded and
-
# application/json Content-Type response bodies
-
1
def parsed
-
return nil unless PARSERS.key?(parser)
-
@parsed ||= PARSERS[parser].call(body)
-
end
-
-
# Attempts to determine the content type of the response.
-
1
def content_type
-
((response.headers.values_at('content-type', 'Content-Type').compact.first || '').split(';').first || '').strip
-
end
-
-
# Determines the parser that will be used to supply the content of #parsed
-
1
def parser
-
return options[:parse].to_sym if PARSERS.key?(options[:parse])
-
CONTENT_TYPES[content_type]
-
end
-
end
-
end
-
-
1
OAuth2::Response.register_parser(:xml, ['text/xml', 'application/rss+xml', 'application/rdf+xml', 'application/atom+xml']) do |body|
-
MultiXml.parse(body) rescue body # rubocop:disable RescueModifier
-
end
-
1
require 'jwt'
-
-
1
module OAuth2
-
1
module Strategy
-
# The Client Assertion Strategy
-
#
-
# @see http://tools.ietf.org/html/draft-ietf-oauth-v2-10#section-4.1.3
-
#
-
# Sample usage:
-
# client = OAuth2::Client.new(client_id, client_secret,
-
# :site => 'http://localhost:8080')
-
#
-
# params = {:hmac_secret => "some secret",
-
# # or :private_key => "private key string",
-
# :iss => "http://localhost:3001",
-
# :prn => "me@here.com",
-
# :exp => Time.now.utc.to_i + 3600}
-
#
-
# access = client.assertion.get_token(params)
-
# access.token # actual access_token string
-
# access.get("/api/stuff") # making api calls with access token in header
-
#
-
1
class Assertion < Base
-
# Not used for this strategy
-
#
-
# @raise [NotImplementedError]
-
1
def authorize_url
-
fail(NotImplementedError, 'The authorization endpoint is not used in this strategy')
-
end
-
-
# Retrieve an access token given the specified client.
-
#
-
# @param [Hash] params assertion params
-
# pass either :hmac_secret or :private_key, but not both.
-
#
-
# params :hmac_secret, secret string.
-
# params :private_key, private key string.
-
#
-
# params :iss, issuer
-
# params :aud, audience, optional
-
# params :prn, principal, current user
-
# params :exp, expired at, in seconds, like Time.now.utc.to_i + 3600
-
#
-
# @param [Hash] opts options
-
1
def get_token(params = {}, opts = {})
-
hash = build_request(params)
-
@client.get_token(hash, opts.merge('refresh_token' => nil))
-
end
-
-
1
def build_request(params)
-
assertion = build_assertion(params)
-
{:grant_type => 'assertion',
-
:assertion_type => 'urn:ietf:params:oauth:grant-type:jwt-bearer',
-
:assertion => assertion,
-
:scope => params[:scope]
-
}.merge(client_params)
-
end
-
-
1
def build_assertion(params)
-
claims = {:iss => params[:iss],
-
:aud => params[:aud],
-
:prn => params[:prn],
-
:exp => params[:exp]
-
}
-
if params[:hmac_secret]
-
JWT.encode(claims, params[:hmac_secret], 'HS256')
-
elsif params[:private_key]
-
JWT.encode(claims, params[:private_key], 'RS256')
-
end
-
end
-
end
-
end
-
end
-
1
module OAuth2
-
1
module Strategy
-
# The Authorization Code Strategy
-
#
-
# @see http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-4.1
-
1
class AuthCode < Base
-
# The required query parameters for the authorize URL
-
#
-
# @param [Hash] params additional query parameters
-
1
def authorize_params(params = {})
-
params.merge('response_type' => 'code', 'client_id' => @client.id)
-
end
-
-
# The authorization URL endpoint of the provider
-
#
-
# @param [Hash] params additional query parameters for the URL
-
1
def authorize_url(params = {})
-
@client.authorize_url(authorize_params.merge(params))
-
end
-
-
# Retrieve an access token given the specified validation code.
-
#
-
# @param [String] code The Authorization Code value
-
# @param [Hash] params additional params
-
# @param [Hash] opts options
-
# @note that you must also provide a :redirect_uri with most OAuth 2.0 providers
-
1
def get_token(code, params = {}, opts = {})
-
params = {'grant_type' => 'authorization_code', 'code' => code}.merge(client_params).merge(params)
-
@client.get_token(params, opts)
-
end
-
end
-
end
-
end
-
1
module OAuth2
-
1
module Strategy
-
1
class Base
-
1
def initialize(client)
-
@client = client
-
end
-
-
# The OAuth client_id and client_secret
-
#
-
# @return [Hash]
-
1
def client_params
-
{'client_id' => @client.id, 'client_secret' => @client.secret}
-
end
-
end
-
end
-
end
-
1
require 'base64'
-
-
1
module OAuth2
-
1
module Strategy
-
# The Client Credentials Strategy
-
#
-
# @see http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-4.4
-
1
class ClientCredentials < Base
-
# Not used for this strategy
-
#
-
# @raise [NotImplementedError]
-
1
def authorize_url
-
fail(NotImplementedError, 'The authorization endpoint is not used in this strategy')
-
end
-
-
# Retrieve an access token given the specified client.
-
#
-
# @param [Hash] params additional params
-
# @param [Hash] opts options
-
1
def get_token(params = {}, opts = {})
-
request_body = opts.delete('auth_scheme') == 'request_body'
-
params.merge!('grant_type' => 'client_credentials')
-
params.merge!(request_body ? client_params : {:headers => {'Authorization' => authorization(client_params['client_id'], client_params['client_secret'])}})
-
@client.get_token(params, opts.merge('refresh_token' => nil))
-
end
-
-
# Returns the Authorization header value for Basic Authentication
-
#
-
# @param [String] The client ID
-
# @param [String] the client secret
-
1
def authorization(client_id, client_secret)
-
'Basic ' + Base64.encode64(client_id + ':' + client_secret).gsub("\n", '')
-
end
-
end
-
end
-
end
-
1
module OAuth2
-
1
module Strategy
-
# The Implicit Strategy
-
#
-
# @see http://tools.ietf.org/html/draft-ietf-oauth-v2-26#section-4.2
-
1
class Implicit < Base
-
# The required query parameters for the authorize URL
-
#
-
# @param [Hash] params additional query parameters
-
1
def authorize_params(params = {})
-
params.merge('response_type' => 'token', 'client_id' => @client.id)
-
end
-
-
# The authorization URL endpoint of the provider
-
#
-
# @param [Hash] params additional query parameters for the URL
-
1
def authorize_url(params = {})
-
@client.authorize_url(authorize_params.merge(params))
-
end
-
-
# Not used for this strategy
-
#
-
# @raise [NotImplementedError]
-
1
def get_token(*)
-
fail(NotImplementedError, 'The token is accessed differently in this strategy')
-
end
-
end
-
end
-
end
-
1
module OAuth2
-
1
module Strategy
-
# The Resource Owner Password Credentials Authorization Strategy
-
#
-
# @see http://tools.ietf.org/html/draft-ietf-oauth-v2-15#section-4.3
-
1
class Password < Base
-
# Not used for this strategy
-
#
-
# @raise [NotImplementedError]
-
1
def authorize_url
-
fail(NotImplementedError, 'The authorization endpoint is not used in this strategy')
-
end
-
-
# Retrieve an access token given the specified End User username and password.
-
#
-
# @param [String] username the End User username
-
# @param [String] password the End User password
-
# @param [Hash] params additional params
-
1
def get_token(username, password, params = {}, opts = {})
-
params = {'grant_type' => 'password',
-
'username' => username,
-
'password' => password}.merge(client_params).merge(params)
-
@client.get_token(params, opts)
-
end
-
end
-
end
-
end
-
1
require 'rack'
-
1
require 'singleton'
-
1
require 'logger'
-
-
1
module OmniAuth
-
1
class Error < StandardError; end
-
-
1
module Strategies
-
1
autoload :Developer, 'omniauth/strategies/developer'
-
end
-
-
1
autoload :Builder, 'omniauth/builder'
-
1
autoload :Strategy, 'omniauth/strategy'
-
1
autoload :Test, 'omniauth/test'
-
1
autoload :Form, 'omniauth/form'
-
1
autoload :AuthHash, 'omniauth/auth_hash'
-
1
autoload :FailureEndpoint, 'omniauth/failure_endpoint'
-
-
1
def self.strategies
-
2
@strategies ||= []
-
end
-
-
1
class Configuration
-
1
include Singleton
-
-
1
def self.default_logger
-
1
logger = Logger.new(STDOUT)
-
1
logger.progname = 'omniauth'
-
1
logger
-
end
-
-
1
def self.defaults
-
@defaults ||= {
-
:camelizations => {},
-
:path_prefix => '/auth',
-
:on_failure => OmniAuth::FailureEndpoint,
-
:failure_raise_out_environments => ['development'],
-
:before_request_phase => nil,
-
:before_callback_phase => nil,
-
:before_options_phase => nil,
-
:form_css => Form::DEFAULT_CSS,
-
:test_mode => false,
-
:logger => default_logger,
-
:allowed_request_methods => [:get, :post],
-
:mock_auth => {:default => AuthHash.new('provider' => 'default', 'uid' => '1234', 'info' => {'name' => 'Example User'})}
-
1
}
-
end
-
-
1
def initialize
-
13
self.class.defaults.each_pair { |k, v| send("#{k}=", v) }
-
end
-
-
1
def on_failure(&block)
-
if block_given?
-
@on_failure = block
-
else
-
@on_failure
-
end
-
end
-
-
1
def before_callback_phase(&block)
-
if block_given?
-
@before_callback_phase = block
-
else
-
@before_callback_phase
-
end
-
end
-
-
1
def before_options_phase(&block)
-
if block_given?
-
@before_options_phase = block
-
else
-
@before_options_phase
-
end
-
end
-
-
1
def before_request_phase(&block)
-
if block_given?
-
@before_request_phase = block
-
else
-
@before_request_phase
-
end
-
end
-
-
1
def add_mock(provider, mock = {})
-
# Stringify keys recursively one level.
-
mock.keys.each do |key|
-
mock[key.to_s] = mock.delete(key)
-
end
-
mock.each_pair do |_key, val|
-
if val.is_a? Hash
-
val.keys.each do |subkey|
-
val[subkey.to_s] = val.delete(subkey)
-
end
-
else
-
next
-
end
-
end
-
-
# Merge with the default mock and ensure provider is correct.
-
mock = mock_auth[:default].dup.merge(mock)
-
mock['provider'] = provider.to_s
-
-
# Add it to the mocks.
-
mock_auth[provider.to_sym] = mock
-
end
-
-
# This is a convenience method to be used by strategy authors
-
# so that they can add special cases to the camelization utility
-
# method that allows OmniAuth::Builder to work.
-
#
-
# @param name [String] The underscored name, e.g. `oauth`
-
# @param camelized [String] The properly camelized name, e.g. 'OAuth'
-
1
def add_camelization(name, camelized)
-
2
camelizations[name.to_s] = camelized.to_s
-
end
-
-
1
attr_writer :on_failure, :before_callback_phase, :before_options_phase, :before_request_phase
-
1
attr_accessor :failure_raise_out_environments, :path_prefix, :allowed_request_methods, :form_css, :test_mode, :mock_auth, :full_host, :camelizations, :logger
-
end
-
-
1
def self.config
-
5
Configuration.instance
-
end
-
-
1
def self.configure
-
yield config
-
end
-
-
1
def self.logger
-
config.logger
-
end
-
-
1
def self.mock_auth_for(provider)
-
config.mock_auth[provider.to_sym] || config.mock_auth[:default]
-
end
-
-
1
module Utils
-
1
module_function
-
-
1
def form_css
-
"<style type='text/css'>#{OmniAuth.config.form_css}</style>"
-
end
-
-
1
def deep_merge(hash, other_hash)
-
target = hash.dup
-
-
other_hash.keys.each do |key|
-
if other_hash[key].is_a?(::Hash) && hash[key].is_a?(::Hash)
-
target[key] = deep_merge(target[key], other_hash[key])
-
next
-
end
-
-
target[key] = other_hash[key]
-
end
-
-
target
-
end
-
-
1
def camelize(word, first_letter_in_uppercase = true)
-
2
return OmniAuth.config.camelizations[word.to_s] if OmniAuth.config.camelizations[word.to_s]
-
-
2
if first_letter_in_uppercase
-
4
word.to_s.gsub(/\/(.?)/) { '::' + Regexp.last_match[1].upcase }.gsub(/(^|_)(.)/) { Regexp.last_match[2].upcase }
-
else
-
word.first + camelize(word)[1..-1]
-
end
-
end
-
end
-
end
-
1
require 'hashie/mash'
-
-
1
module OmniAuth
-
# The AuthHash is a normalized schema returned by all OmniAuth
-
# strategies. It maps as much user information as the provider
-
# is able to provide into the InfoHash (stored as the `'info'`
-
# key).
-
1
class AuthHash < Hashie::Mash
-
1
def self.subkey_class
-
Hashie::Mash
-
end
-
-
# Tells you if this is considered to be a valid
-
# OmniAuth AuthHash. The requirements for that
-
# are that it has a provider name, a uid, and a
-
# valid info hash. See InfoHash#valid? for
-
# more details there.
-
1
def valid?
-
uid? && provider? && info? && info.valid?
-
end
-
-
1
def regular_writer(key, value)
-
4
if key.to_s == 'info' && !value.is_a?(InfoHash)
-
1
value = InfoHash.new(value)
-
end
-
4
super
-
end
-
-
1
class InfoHash < Hashie::Mash
-
1
def self.subkey_class
-
Hashie::Mash
-
end
-
-
1
def name
-
return self[:name] if self[:name]
-
return "#{first_name} #{last_name}".strip if first_name? || last_name?
-
return nickname if nickname?
-
return email if email?
-
nil
-
end
-
-
1
def name?
-
!!name # rubocop:disable DoubleNegation
-
end
-
1
alias_method :valid?, :name?
-
-
1
def to_hash
-
hash = super
-
hash['name'] ||= name
-
hash
-
end
-
end
-
end
-
end
-
1
module OmniAuth
-
1
class Builder < ::Rack::Builder
-
1
def initialize(app, &block)
-
1
@options = nil
-
1
if rack14?
-
1
super
-
else
-
@app = app
-
super(&block)
-
@ins << @app
-
end
-
end
-
-
1
def rack14?
-
1
Rack.release.split('.')[1].to_i >= 4
-
end
-
-
1
def on_failure(&block)
-
OmniAuth.config.on_failure = block
-
end
-
-
1
def before_options_phase(&block)
-
OmniAuth.config.before_options_phase = block
-
end
-
-
1
def before_request_phase(&block)
-
OmniAuth.config.before_request_phase = block
-
end
-
-
1
def before_callback_phase(&block)
-
OmniAuth.config.before_callback_phase = block
-
end
-
-
1
def configure(&block)
-
OmniAuth.configure(&block)
-
end
-
-
1
def options(options = false)
-
2
return @options || {} if options == false
-
@options = options
-
end
-
-
1
def provider(klass, *args, &block)
-
2
if klass.is_a?(Class)
-
middleware = klass
-
else
-
2
begin
-
2
middleware = OmniAuth::Strategies.const_get("#{OmniAuth::Utils.camelize(klass.to_s)}")
-
rescue NameError
-
raise(LoadError.new("Could not find matching strategy for #{klass.inspect}. You may need to install an additional gem (such as omniauth-#{klass})."))
-
end
-
end
-
-
2
args.last.is_a?(Hash) ? args.push(options.merge(args.pop)) : args.push(options)
-
2
use middleware, *args, &block
-
end
-
-
1
def call(env)
-
to_app.call(env)
-
end
-
end
-
end
-
1
module OmniAuth
-
# This simple Rack endpoint that serves as the default
-
# 'failure' mechanism for OmniAuth. If a strategy fails for
-
# any reason this endpoint will be invoked. The default behavior
-
# is to redirect to `/auth/failure` except in the case of
-
# a development `RACK_ENV`, in which case an exception will
-
# be raised.
-
1
class FailureEndpoint
-
1
attr_reader :env
-
-
1
def self.call(env)
-
new(env).call
-
end
-
-
1
def initialize(env)
-
@env = env
-
end
-
-
1
def call
-
raise_out! if OmniAuth.config.failure_raise_out_environments.include?(ENV['RACK_ENV'].to_s)
-
redirect_to_failure
-
end
-
-
1
def raise_out!
-
fail(env['omniauth.error'] || OmniAuth::Error.new(env['omniauth.error.type']))
-
end
-
-
1
def redirect_to_failure
-
message_key = env['omniauth.error.type']
-
new_path = "#{env['SCRIPT_NAME']}#{OmniAuth.config.path_prefix}/failure?message=#{message_key}#{origin_query_param}#{strategy_name_query_param}"
-
Rack::Response.new(['302 Moved'], 302, 'Location' => new_path).finish
-
end
-
-
1
def strategy_name_query_param
-
return '' unless env['omniauth.error.strategy']
-
"&strategy=#{env['omniauth.error.strategy'].name}"
-
end
-
-
1
def origin_query_param
-
return '' unless env['omniauth.origin']
-
"&origin=#{Rack::Utils.escape(env['omniauth.origin'])}"
-
end
-
end
-
end
-
1
module OmniAuth
-
1
class Form # rubocop:disable ClassLength
-
1
DEFAULT_CSS = File.read(File.expand_path('../form.css', __FILE__))
-
-
1
attr_accessor :options
-
-
1
def initialize(options = {})
-
options[:title] ||= 'Authentication Info Required'
-
options[:header_info] ||= ''
-
self.options = options
-
-
@html = ''
-
@with_custom_button = false
-
@footer = nil
-
header(options[:title], options[:header_info])
-
end
-
-
1
def self.build(options = {}, &block)
-
form = OmniAuth::Form.new(options)
-
if block.arity > 0
-
yield form
-
else
-
form.instance_eval(&block)
-
end
-
form
-
end
-
-
1
def label_field(text, target)
-
@html << "\n<label for='#{target}'>#{text}:</label>"
-
self
-
end
-
-
1
def input_field(type, name)
-
@html << "\n<input type='#{type}' id='#{name}' name='#{name}'/>"
-
self
-
end
-
-
1
def text_field(label, name)
-
label_field(label, name)
-
input_field('text', name)
-
self
-
end
-
-
1
def password_field(label, name)
-
label_field(label, name)
-
input_field('password', name)
-
self
-
end
-
-
1
def button(text)
-
@with_custom_button = true
-
@html << "\n<button type='submit'>#{text}</button>"
-
end
-
-
1
def html(html)
-
@html << html
-
end
-
-
1
def fieldset(legend, options = {}, &block)
-
@html << "\n<fieldset#{" style='#{options[:style]}'" if options[:style]}#{" id='#{options[:id]}'" if options[:id]}>\n <legend>#{legend}</legend>\n"
-
instance_eval(&block)
-
@html << "\n</fieldset>"
-
self
-
end
-
-
1
def header(title, header_info)
-
@html << <<-HTML
-
<!DOCTYPE html>
-
<html>
-
<head>
-
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
-
<title>#{title}</title>
-
#{css}
-
#{header_info}
-
</head>
-
<body>
-
<h1>#{title}</h1>
-
<form method='post' #{"action='#{options[:url]}' " if options[:url]}noValidate='noValidate'>
-
HTML
-
self
-
end
-
-
1
def footer
-
return self if @footer
-
@html << "\n<button type='submit'>Connect</button>" unless @with_custom_button
-
@html << <<-HTML
-
</form>
-
</body>
-
</html>
-
HTML
-
@footer = true
-
self
-
end
-
-
1
def to_html
-
footer
-
@html
-
end
-
-
1
def to_response
-
footer
-
Rack::Response.new(@html, 200, 'content-type' => 'text/html').finish
-
end
-
-
1
protected
-
-
1
def css
-
"\n<style type='text/css'>#{OmniAuth.config.form_css}</style>"
-
end
-
end
-
end
-
1
require 'hashie/mash'
-
-
1
module OmniAuth
-
1
class NoSessionError < StandardError; end
-
# The Strategy is the base unit of OmniAuth's ability to
-
# wrangle multiple providers. Each strategy provided by
-
# OmniAuth includes this mixin to gain the default functionality
-
# necessary to be compatible with the OmniAuth library.
-
1
module Strategy
-
1
def self.included(base)
-
2
OmniAuth.strategies << base
-
-
2
base.extend ClassMethods
-
2
base.class_eval do
-
2
option :setup, false
-
2
option :skip_info, false
-
end
-
end
-
-
1
module ClassMethods
-
# Returns an inherited set of default options set at the class-level
-
# for each strategy.
-
1
def default_options
-
25
return @default_options if instance_variable_defined?(:@default_options) && @default_options
-
4
existing = superclass.respond_to?(:default_options) ? superclass.default_options : {}
-
4
@default_options = OmniAuth::Strategy::Options.new(existing)
-
end
-
-
# This allows for more declarative subclassing of strategies by allowing
-
# default options to be set using a simple configure call.
-
#
-
# @param options [Hash] If supplied, these will be the default options (deep-merged into the superclass's default options).
-
# @yield [Options] The options Mash that allows you to set your defaults as you'd like.
-
#
-
# @example Using a yield to configure the default options.
-
#
-
# class MyStrategy
-
# include OmniAuth::Strategy
-
#
-
# configure do |c|
-
# c.foo = 'bar'
-
# end
-
# end
-
#
-
# @example Using a hash to configure the default options.
-
#
-
# class MyStrategy
-
# include OmniAuth::Strategy
-
# configure foo: 'bar'
-
# end
-
1
def configure(options = nil)
-
if block_given?
-
yield default_options
-
else
-
default_options.deep_merge!(options)
-
end
-
end
-
-
# Directly declare a default option for your class. This is a useful from
-
# a documentation perspective as it provides a simple line-by-line analysis
-
# of the kinds of options your strategy provides by default.
-
#
-
# @param name [Symbol] The key of the default option in your configuration hash.
-
# @param value [Object] The value your object defaults to. Nil if not provided.
-
#
-
# @example
-
#
-
# class MyStrategy
-
# include OmniAuth::Strategy
-
#
-
# option :foo, 'bar'
-
# option
-
# end
-
1
def option(name, value = nil)
-
23
default_options[name] = value
-
end
-
-
# Sets (and retrieves) option key names for initializer arguments to be
-
# recorded as. This takes care of 90% of the use cases for overriding
-
# the initializer in OmniAuth Strategies.
-
1
def args(args = nil)
-
2
if args
-
2
@args = Array(args)
-
2
return
-
end
-
existing = superclass.respond_to?(:args) ? superclass.args : []
-
(instance_variable_defined?(:@args) && @args) || existing
-
end
-
-
1
%w(uid info extra credentials).each do |fetcher|
-
4
class_eval <<-RUBY
-
def #{fetcher}(&block)
-
return @#{fetcher}_proc unless block_given?
-
@#{fetcher}_proc = block
-
end
-
-
def #{fetcher}_stack(context)
-
compile_stack(self.ancestors, :#{fetcher}, context)
-
end
-
RUBY
-
end
-
-
1
def compile_stack(ancestors, method, context)
-
stack = ancestors.inject([]) do |a, ancestor|
-
a << context.instance_eval(&ancestor.send(method)) if ancestor.respond_to?(method) && ancestor.send(method)
-
a
-
end
-
stack.reverse!
-
end
-
end
-
-
1
attr_reader :app, :env, :options, :response
-
-
# Initializes the strategy by passing in the Rack endpoint,
-
# the unique URL segment name for this strategy, and any
-
# additional arguments. An `options` hash is automatically
-
# created from the last argument if it is a hash.
-
#
-
# @param app [Rack application] The application on which this middleware is applied.
-
#
-
# @overload new(app, options = {})
-
# If nothing but a hash is supplied, initialized with the supplied options
-
# overriding the strategy's default options via a deep merge.
-
# @overload new(app, *args, options = {})
-
# If the strategy has supplied custom arguments that it accepts, they may
-
# will be passed through and set to the appropriate values.
-
#
-
# @yield [Options] Yields options to block for further configuration.
-
1
def initialize(app, *args, &block) # rubocop:disable UnusedMethodArgument
-
@app = app
-
@env = nil
-
@options = self.class.default_options.dup
-
-
options.deep_merge!(args.pop) if args.last.is_a?(Hash)
-
options.name ||= self.class.to_s.split('::').last.downcase
-
-
self.class.args.each do |arg|
-
break if args.empty?
-
options[arg] = args.shift
-
end
-
-
# Make sure that all of the args have been dealt with, otherwise error out.
-
fail(ArgumentError.new("Received wrong number of arguments. #{args.inspect}")) unless args.empty?
-
-
yield options if block_given?
-
end
-
-
1
def inspect
-
"#<#{self.class}>"
-
end
-
-
# Direct access to the OmniAuth logger, automatically prefixed
-
# with this strategy's name.
-
#
-
# @example
-
# log :warn, "This is a warning."
-
1
def log(level, message)
-
OmniAuth.logger.send(level, "(#{name}) #{message}")
-
end
-
-
# Duplicates this instance and runs #call! on it.
-
# @param [Hash] The Rack environment.
-
1
def call(env)
-
dup.call!(env)
-
end
-
-
# The logic for dispatching any additional actions that need
-
# to be taken. For instance, calling the request phase if
-
# the request path is recognized.
-
#
-
# @param env [Hash] The Rack environment.
-
1
def call!(env) # rubocop:disable CyclomaticComplexity
-
unless env['rack.session']
-
error = OmniAuth::NoSessionError.new('You must provide a session to use OmniAuth.')
-
fail(error)
-
end
-
-
@env = env
-
@env['omniauth.strategy'] = self if on_auth_path?
-
-
return mock_call!(env) if OmniAuth.config.test_mode
-
return options_call if on_auth_path? && options_request?
-
return request_call if on_request_path? && OmniAuth.config.allowed_request_methods.include?(request.request_method.downcase.to_sym)
-
return callback_call if on_callback_path?
-
return other_phase if respond_to?(:other_phase)
-
@app.call(env)
-
end
-
-
# Responds to an OPTIONS request.
-
1
def options_call
-
OmniAuth.config.before_options_phase.call(env) if OmniAuth.config.before_options_phase
-
verbs = OmniAuth.config.allowed_request_methods.collect(&:to_s).collect(&:upcase).join(', ')
-
[200, {'Allow' => verbs}, []]
-
end
-
-
# Performs the steps necessary to run the request phase of a strategy.
-
1
def request_call # rubocop:disable CyclomaticComplexity, MethodLength
-
setup_phase
-
log :info, 'Request phase initiated.'
-
# store query params from the request url, extracted in the callback_phase
-
session['omniauth.params'] = request.params
-
OmniAuth.config.before_request_phase.call(env) if OmniAuth.config.before_request_phase
-
if options.form.respond_to?(:call)
-
log :info, 'Rendering form from supplied Rack endpoint.'
-
options.form.call(env)
-
elsif options.form
-
log :info, 'Rendering form from underlying application.'
-
call_app!
-
else
-
if request.params['origin']
-
env['rack.session']['omniauth.origin'] = request.params['origin']
-
elsif env['HTTP_REFERER'] && !env['HTTP_REFERER'].match(/#{request_path}$/)
-
env['rack.session']['omniauth.origin'] = env['HTTP_REFERER']
-
end
-
request_phase
-
end
-
end
-
-
# Performs the steps necessary to run the callback phase of a strategy.
-
1
def callback_call
-
setup_phase
-
log :info, 'Callback phase initiated.'
-
@env['omniauth.origin'] = session.delete('omniauth.origin')
-
@env['omniauth.origin'] = nil if env['omniauth.origin'] == ''
-
@env['omniauth.params'] = session.delete('omniauth.params') || {}
-
OmniAuth.config.before_callback_phase.call(@env) if OmniAuth.config.before_callback_phase
-
callback_phase
-
end
-
-
# Returns true if the environment recognizes either the
-
# request or callback path.
-
1
def on_auth_path?
-
on_request_path? || on_callback_path?
-
end
-
-
1
def on_request_path?
-
if options.request_path.respond_to?(:call)
-
options.request_path.call(env)
-
else
-
on_path?(request_path)
-
end
-
end
-
-
1
def on_callback_path?
-
on_path?(callback_path)
-
end
-
-
1
def on_path?(path)
-
current_path.casecmp(path) == 0
-
end
-
-
1
def options_request?
-
request.request_method == 'OPTIONS'
-
end
-
-
# This is called in lieu of the normal request process
-
# in the event that OmniAuth has been configured to be
-
# in test mode.
-
1
def mock_call!(*)
-
return mock_request_call if on_request_path?
-
return mock_callback_call if on_callback_path?
-
call_app!
-
end
-
-
1
def mock_request_call
-
setup_phase
-
-
session['omniauth.params'] = request.params
-
OmniAuth.config.before_request_phase.call(env) if OmniAuth.config.before_request_phase
-
if request.params['origin']
-
@env['rack.session']['omniauth.origin'] = request.params['origin']
-
elsif env['HTTP_REFERER'] && !env['HTTP_REFERER'].match(/#{request_path}$/)
-
@env['rack.session']['omniauth.origin'] = env['HTTP_REFERER']
-
end
-
-
redirect(callback_url)
-
end
-
-
1
def mock_callback_call
-
setup_phase
-
mocked_auth = OmniAuth.mock_auth_for(name.to_s)
-
if mocked_auth.is_a?(Symbol)
-
fail!(mocked_auth)
-
else
-
@env['omniauth.auth'] = mocked_auth
-
@env['omniauth.params'] = session.delete('omniauth.params') || {}
-
@env['omniauth.origin'] = session.delete('omniauth.origin')
-
@env['omniauth.origin'] = nil if env['omniauth.origin'] == ''
-
OmniAuth.config.before_callback_phase.call(@env) if OmniAuth.config.before_callback_phase
-
call_app!
-
end
-
end
-
-
# The setup phase looks for the `:setup` option to exist and,
-
# if it is, will call either the Rack endpoint supplied to the
-
# `:setup` option or it will call out to the setup path of the
-
# underlying application. This will default to `/auth/:provider/setup`.
-
1
def setup_phase
-
if options[:setup].respond_to?(:call)
-
log :info, 'Setup endpoint detected, running now.'
-
options[:setup].call(env)
-
elsif options.setup?
-
log :info, 'Calling through to underlying application for setup.'
-
setup_env = env.merge('PATH_INFO' => setup_path, 'REQUEST_METHOD' => 'GET')
-
call_app!(setup_env)
-
end
-
end
-
-
# @abstract This method is called when the user is on the request path. You should
-
# perform any information gathering you need to be able to authenticate
-
# the user in this phase.
-
1
def request_phase
-
fail(NotImplementedError)
-
end
-
-
1
def uid
-
self.class.uid_stack(self).last
-
end
-
-
1
def info
-
merge_stack(self.class.info_stack(self))
-
end
-
-
1
def credentials
-
merge_stack(self.class.credentials_stack(self))
-
end
-
-
1
def extra
-
merge_stack(self.class.extra_stack(self))
-
end
-
-
1
def auth_hash
-
hash = AuthHash.new(:provider => name, :uid => uid)
-
hash.info = info unless skip_info?
-
hash.credentials = credentials if credentials
-
hash.extra = extra if extra
-
hash
-
end
-
-
# Determines whether or not user info should be retrieved. This
-
# allows some strategies to save a call to an external API service
-
# for existing users. You can use it either by setting the `:skip_info`
-
# to true or by setting `:skip_info` to a Proc that takes a uid and
-
# evaluates to true when you would like to skip info.
-
#
-
# @example
-
#
-
# use MyStrategy, :skip_info => lambda{|uid| User.find_by_uid(uid)}
-
1
def skip_info?
-
if options.skip_info?
-
if options.skip_info.respond_to?(:call)
-
return options.skip_info.call(uid)
-
else
-
return true
-
end
-
end
-
false
-
end
-
-
1
def callback_phase
-
env['omniauth.auth'] = auth_hash
-
call_app!
-
end
-
-
1
def path_prefix
-
options[:path_prefix] || OmniAuth.config.path_prefix
-
end
-
-
1
def custom_path(kind)
-
if options[kind].respond_to?(:call)
-
result = options[kind].call(env)
-
return nil unless result.is_a?(String)
-
result
-
else
-
options[kind]
-
end
-
end
-
-
1
def request_path
-
options[:request_path].is_a?(String) ? options[:request_path] : "#{path_prefix}/#{name}"
-
end
-
-
1
def callback_path
-
path = options[:callback_path] if options[:callback_path].is_a?(String)
-
path ||= current_path if options[:callback_path].respond_to?(:call) && options[:callback_path].call(env)
-
path ||= custom_path(:request_path)
-
path ||= "#{path_prefix}/#{name}/callback"
-
path
-
end
-
-
1
def setup_path
-
options[:setup_path] || "#{path_prefix}/#{name}/setup"
-
end
-
-
1
def current_path
-
request.path_info.downcase.sub(/\/$/, '')
-
end
-
-
1
def query_string
-
request.query_string.empty? ? '' : "?#{request.query_string}"
-
end
-
-
1
def call_app!(env = @env)
-
@app.call(env)
-
end
-
-
1
def full_host
-
case OmniAuth.config.full_host
-
when String
-
OmniAuth.config.full_host
-
when Proc
-
OmniAuth.config.full_host.call(env)
-
else
-
# in Rack 1.3.x, request.url explodes if scheme is nil
-
if request.scheme && request.url.match(URI::ABS_URI)
-
uri = URI.parse(request.url.gsub(/\?.*$/, ''))
-
uri.path = ''
-
# sometimes the url is actually showing http inside rails because the
-
# other layers (like nginx) have handled the ssl termination.
-
uri.scheme = 'https' if ssl? # rubocop:disable BlockNesting
-
uri.to_s
-
else ''
-
end
-
end
-
end
-
-
1
def callback_url
-
full_host + script_name + callback_path + query_string
-
end
-
-
1
def script_name
-
@env['SCRIPT_NAME'] || ''
-
end
-
-
1
def session
-
@env['rack.session']
-
end
-
-
1
def request
-
@request ||= Rack::Request.new(@env)
-
end
-
-
1
def name
-
options.name
-
end
-
-
1
def redirect(uri)
-
r = Rack::Response.new
-
-
if options[:iframe]
-
r.write("<script type='text/javascript' charset='utf-8'>top.location.href = '#{uri}';</script>")
-
else
-
r.write("Redirecting to #{uri}...")
-
r.redirect(uri)
-
end
-
-
r.finish
-
end
-
-
1
def user_info
-
{}
-
end
-
-
1
def fail!(message_key, exception = nil)
-
env['omniauth.error'] = exception
-
env['omniauth.error.type'] = message_key.to_sym
-
env['omniauth.error.strategy'] = self
-
-
if exception
-
log :error, "Authentication failure! #{message_key}: #{exception.class}, #{exception.message}"
-
else
-
log :error, "Authentication failure! #{message_key} encountered."
-
end
-
-
OmniAuth.config.on_failure.call(env)
-
end
-
-
1
class Options < Hashie::Mash; end
-
-
1
protected
-
-
1
def merge_stack(stack)
-
stack.inject({}) do |a, e|
-
a.merge!(e)
-
a
-
end
-
end
-
-
1
def ssl?
-
request.env['HTTPS'] == 'on' ||
-
request.env['HTTP_X_FORWARDED_SSL'] == 'on' ||
-
request.env['HTTP_X_FORWARDED_SCHEME'] == 'https' ||
-
(request.env['HTTP_X_FORWARDED_PROTO'] && request.env['HTTP_X_FORWARDED_PROTO'].split(',')[0] == 'https') ||
-
request.env['rack.url_scheme'] == 'https'
-
end
-
end
-
end
-
1
require "omniauth-instagram/version"
-
1
require 'omniauth/strategies/instagram'
-
1
module OmniAuth
-
1
module Instagram
-
1
VERSION = "1.0.1"
-
end
-
end
-
1
require 'omniauth-oauth2'
-
-
1
module OmniAuth
-
1
module Strategies
-
1
class Instagram < OmniAuth::Strategies::OAuth2
-
1
option :client_options, {
-
:site => 'https://api.instagram.com',
-
:authorize_url => 'https://api.instagram.com/oauth/authorize',
-
:token_url => 'https://api.instagram.com/oauth/access_token'
-
}
-
-
1
def request_phase
-
options[:scope] ||= 'basic'
-
options[:response_type] ||= 'code'
-
super
-
end
-
-
1
uid { raw_info['id'] }
-
-
1
info do
-
{
-
'nickname' => raw_info['username'],
-
'name' => raw_info['full_name'],
-
'image' => raw_info['profile_picture'],
-
'bio' => raw_info['bio'],
-
'website' => raw_info['website'],
-
}
-
end
-
-
1
def raw_info
-
@data ||= access_token.params["user"]
-
unless @data
-
access_token.options[:mode] = :query
-
access_token.options[:param_name] = "access_token"
-
@data ||= access_token.get('/v1/users/self').parsed['data'] || {}
-
end
-
@data
-
end
-
end
-
end
-
end
-
1
require "omniauth-oauth/version"
-
1
require 'omniauth/strategies/oauth'
-
-
1
module OmniAuth
-
1
module OAuth
-
1
VERSION = "1.0.1"
-
end
-
end
-
1
require 'multi_json'
-
1
require 'oauth'
-
1
require 'omniauth'
-
-
1
module OmniAuth
-
1
module Strategies
-
1
class OAuth
-
1
include OmniAuth::Strategy
-
-
1
args [:consumer_key, :consumer_secret]
-
1
option :consumer_key, nil
-
1
option :consumer_secret, nil
-
1
option :client_options, {}
-
1
option :open_timeout, 30
-
1
option :read_timeout, 30
-
1
option :authorize_params, {}
-
1
option :request_params, {}
-
-
1
attr_reader :access_token
-
-
1
def consumer
-
consumer = ::OAuth::Consumer.new(options.consumer_key, options.consumer_secret, options.client_options)
-
consumer.http.open_timeout = options.open_timeout if options.open_timeout
-
consumer.http.read_timeout = options.read_timeout if options.read_timeout
-
consumer
-
end
-
-
1
def request_phase
-
request_token = consumer.get_request_token({:oauth_callback => callback_url}, options.request_params)
-
session['oauth'] ||= {}
-
session['oauth'][name.to_s] = {'callback_confirmed' => request_token.callback_confirmed?, 'request_token' => request_token.token, 'request_secret' => request_token.secret}
-
-
if request_token.callback_confirmed?
-
redirect request_token.authorize_url(options[:authorize_params])
-
else
-
redirect request_token.authorize_url(options[:authorize_params].merge(:oauth_callback => callback_url))
-
end
-
-
rescue ::Timeout::Error => e
-
fail!(:timeout, e)
-
rescue ::Net::HTTPFatalError, ::OpenSSL::SSL::SSLError => e
-
fail!(:service_unavailable, e)
-
end
-
-
1
def callback_phase
-
raise OmniAuth::NoSessionError.new("Session Expired") if session['oauth'].nil?
-
-
request_token = ::OAuth::RequestToken.new(consumer, session['oauth'][name.to_s].delete('request_token'), session['oauth'][name.to_s].delete('request_secret'))
-
-
opts = {}
-
if session['oauth'][name.to_s]['callback_confirmed']
-
opts[:oauth_verifier] = request['oauth_verifier']
-
else
-
opts[:oauth_callback] = callback_url
-
end
-
-
@access_token = request_token.get_access_token(opts)
-
super
-
rescue ::Timeout::Error => e
-
fail!(:timeout, e)
-
rescue ::Net::HTTPFatalError, ::OpenSSL::SSL::SSLError => e
-
fail!(:service_unavailable, e)
-
rescue ::OAuth::Unauthorized => e
-
fail!(:invalid_credentials, e)
-
rescue ::MultiJson::DecodeError => e
-
fail!(:invalid_response, e)
-
rescue ::OmniAuth::NoSessionError => e
-
fail!(:session_expired, e)
-
end
-
-
1
credentials do
-
{'token' => access_token.token, 'secret' => access_token.secret}
-
end
-
-
1
extra do
-
{'access_token' => access_token}
-
end
-
end
-
end
-
end
-
-
1
OmniAuth.config.add_camelization 'oauth', 'OAuth'
-
1
require 'omniauth-oauth2/version'
-
1
require 'omniauth/strategies/oauth2'
-
1
module OmniAuth
-
1
module OAuth2
-
1
VERSION = '1.2.0'
-
end
-
end
-
1
require 'oauth2'
-
1
require 'omniauth'
-
1
require 'securerandom'
-
1
require 'socket' # for SocketError
-
1
require 'timeout' # for Timeout::Error
-
1
require 'faraday' # for Faraday::Error::TimeoutError and Faraday::Error::ConnectionFailed
-
1
require 'multi_json' # for MultiJson::DecodeError
-
-
1
module OmniAuth
-
1
module Strategies
-
# Authentication strategy for connecting with APIs constructed using
-
# the [OAuth 2.0 Specification](http://tools.ietf.org/html/draft-ietf-oauth-v2-10).
-
# You must generally register your application with the provider and
-
# utilize an application id and secret in order to authenticate using
-
# OAuth 2.0.
-
1
class OAuth2
-
1
include OmniAuth::Strategy
-
-
1
args [:client_id, :client_secret]
-
-
1
option :client_id, nil
-
1
option :client_secret, nil
-
1
option :client_options, {}
-
1
option :authorize_params, {}
-
1
option :authorize_options, [:scope]
-
1
option :token_params, {}
-
1
option :token_options, []
-
1
option :auth_token_params, {}
-
1
option :provider_ignores_state, false
-
-
1
attr_accessor :access_token
-
-
1
def client
-
::OAuth2::Client.new(options.client_id, options.client_secret, deep_symbolize(options.client_options))
-
end
-
-
1
def callback_url
-
full_host + script_name + callback_path
-
end
-
-
1
credentials do
-
hash = {'token' => access_token.token}
-
hash.merge!('refresh_token' => access_token.refresh_token) if access_token.expires? && access_token.refresh_token
-
hash.merge!('expires_at' => access_token.expires_at) if access_token.expires?
-
hash.merge!('expires' => access_token.expires?)
-
hash
-
end
-
-
1
def request_phase
-
redirect client.auth_code.authorize_url({:redirect_uri => callback_url}.merge(authorize_params))
-
end
-
-
1
def authorize_params
-
options.authorize_params[:state] = SecureRandom.hex(24)
-
params = options.authorize_params.merge(options_for('authorize'))
-
if OmniAuth.config.test_mode
-
@env ||= {}
-
@env['rack.session'] ||= {}
-
end
-
session['omniauth.state'] = params[:state]
-
params
-
end
-
-
1
def token_params
-
options.token_params.merge(options_for('token'))
-
end
-
-
1
def callback_phase # rubocop:disable CyclomaticComplexity
-
error = request.params['error_reason'] || request.params['error']
-
if error
-
fail!(error, CallbackError.new(request.params['error'], request.params['error_description'] || request.params['error_reason'], request.params['error_uri']))
-
elsif !options.provider_ignores_state && (request.params['state'].to_s.empty? || request.params['state'] != session.delete('omniauth.state'))
-
fail!(:csrf_detected, CallbackError.new(:csrf_detected, 'CSRF detected'))
-
else
-
self.access_token = build_access_token
-
self.access_token = access_token.refresh! if access_token.expired?
-
super
-
end
-
rescue ::OAuth2::Error, CallbackError => e
-
fail!(:invalid_credentials, e)
-
rescue ::MultiJson::DecodeError => e
-
fail!(:invalid_response, e)
-
rescue ::Timeout::Error, ::Errno::ETIMEDOUT, Faraday::Error::TimeoutError => e
-
fail!(:timeout, e)
-
rescue ::SocketError, Faraday::Error::ConnectionFailed => e
-
fail!(:failed_to_connect, e)
-
end
-
-
1
protected
-
-
1
def build_access_token
-
verifier = request.params['code']
-
client.auth_code.get_token(verifier, {:redirect_uri => callback_url}.merge(token_params.to_hash(:symbolize_keys => true)), deep_symbolize(options.auth_token_params))
-
end
-
-
1
def deep_symbolize(options)
-
hash = {}
-
options.each do |key, value|
-
hash[key.to_sym] = value.is_a?(Hash) ? deep_symbolize(value) : value
-
end
-
hash
-
end
-
-
1
def options_for(option)
-
hash = {}
-
options.send(:"#{option}_options").select { |key| options[key] }.each do |key|
-
hash[key.to_sym] = options[key]
-
end
-
hash
-
end
-
-
# An error that is indicated in the OAuth 2.0 callback.
-
# This could be a `redirect_uri_mismatch` or other
-
1
class CallbackError < StandardError
-
1
attr_accessor :error, :error_reason, :error_uri
-
-
1
def initialize(error, error_reason = nil, error_uri = nil)
-
self.error = error
-
self.error_reason = error_reason
-
self.error_uri = error_uri
-
end
-
-
1
def message
-
[error, error_reason, error_uri].compact.join(' | ')
-
end
-
end
-
end
-
end
-
end
-
-
1
OmniAuth.config.add_camelization 'oauth2', 'OAuth2'
-
1
require "omniauth-twitter/version"
-
1
require 'omniauth/strategies/twitter'
-
1
module OmniAuth
-
1
module Twitter
-
1
VERSION = "1.1.0"
-
end
-
end
-
1
require 'omniauth-oauth'
-
1
require 'multi_json'
-
-
1
module OmniAuth
-
1
module Strategies
-
1
class Twitter < OmniAuth::Strategies::OAuth
-
1
option :name, 'twitter'
-
-
1
option :client_options, {:authorize_path => '/oauth/authenticate',
-
:site => 'https://api.twitter.com',
-
:proxy => ENV['http_proxy'] ? URI(ENV['http_proxy']) : nil}
-
-
1
uid { access_token.params[:user_id] }
-
-
1
info do
-
{
-
:nickname => raw_info['screen_name'],
-
:name => raw_info['name'],
-
:location => raw_info['location'],
-
:image => image_url,
-
:description => raw_info['description'],
-
:urls => {
-
'Website' => raw_info['url'],
-
'Twitter' => "https://twitter.com/#{raw_info['screen_name']}",
-
}
-
}
-
end
-
-
1
extra do
-
{ :raw_info => raw_info }
-
end
-
-
1
def raw_info
-
@raw_info ||= MultiJson.load(access_token.get('/1.1/account/verify_credentials.json?include_entities=false&skip_status=true').body)
-
rescue ::Errno::ETIMEDOUT
-
raise ::Timeout::Error
-
end
-
-
1
alias :old_request_phase :request_phase
-
-
1
def request_phase
-
%w[force_login lang screen_name].each do |v|
-
if request.params[v]
-
options[:authorize_params][v.to_sym] = request.params[v]
-
end
-
end
-
-
%w[x_auth_access_type].each do |v|
-
if request.params[v]
-
options[:request_params][v.to_sym] = request.params[v]
-
end
-
end
-
-
if options[:use_authorize] || request.params['use_authorize'] == 'true'
-
options[:client_options][:authorize_path] = '/oauth/authorize'
-
else
-
options[:client_options][:authorize_path] = '/oauth/authenticate'
-
end
-
-
old_request_phase
-
end
-
-
1
private
-
-
1
def image_url
-
original_url = options[:secure_image_url] ? raw_info['profile_image_url_https'] : raw_info['profile_image_url']
-
case options[:image_size]
-
when 'mini'
-
original_url.sub('normal', 'mini')
-
when 'bigger'
-
original_url.sub('normal', 'bigger')
-
when 'original'
-
original_url.sub('_normal', '')
-
else
-
original_url
-
end
-
end
-
-
end
-
end
-
end
-
#!/usr/bin/env ruby
-
-
1
begin
-
1
require 'pg_ext'
-
rescue LoadError
-
# If it's a Windows binary gem, try the <major>.<minor> subdirectory
-
if RUBY_PLATFORM =~/(mswin|mingw)/i
-
major_minor = RUBY_VERSION[ /^(\d+\.\d+)/ ] or
-
raise "Oops, can't extract the major/minor version from #{RUBY_VERSION.dump}"
-
require "#{major_minor}/pg_ext"
-
else
-
raise
-
end
-
-
end
-
-
-
# The top-level PG namespace.
-
1
module PG
-
-
# Library version
-
1
VERSION = '0.17.1'
-
-
# VCS revision
-
1
REVISION = %q$Revision: 22d57e3a2b37 $
-
-
1
class NotAllCopyDataRetrieved < PG::Error
-
end
-
-
### Get the PG library version. If +include_buildnum+ is +true+, include the build ID.
-
1
def self::version_string( include_buildnum=false )
-
vstring = "%s %s" % [ self.name, VERSION ]
-
vstring << " (build %s)" % [ REVISION[/: ([[:xdigit:]]+)/, 1] || '0' ] if include_buildnum
-
return vstring
-
end
-
-
-
### Convenience alias for PG::Connection.new.
-
1
def self::connect( *args )
-
return PG::Connection.new( *args )
-
end
-
-
-
1
require 'pg/exceptions'
-
1
require 'pg/constants'
-
1
require 'pg/connection'
-
1
require 'pg/result'
-
-
end # module PG
-
-
-
# Backward-compatible aliase
-
1
PGError = PG::Error
-
-
#!/usr/bin/env ruby
-
-
1
require 'pg' unless defined?( PG )
-
-
# The PostgreSQL connection class. The interface for this class is based on
-
# {libpq}[http://www.postgresql.org/docs/9.2/interactive/libpq.html], the C
-
# application programmer's interface to PostgreSQL. Some familiarity with libpq
-
# is recommended, but not necessary.
-
#
-
# For example, to send query to the database on the localhost:
-
#
-
# require 'pg'
-
# conn = PG::Connection.open(:dbname => 'test')
-
# res = conn.exec_params('SELECT $1 AS a, $2 AS b, $3 AS c', [1, 2, nil])
-
# # Equivalent to:
-
# # res = conn.exec('SELECT 1 AS a, 2 AS b, NULL AS c')
-
#
-
# See the PG::Result class for information on working with the results of a query.
-
#
-
1
class PG::Connection
-
-
# The order the options are passed to the ::connect method.
-
1
CONNECT_ARGUMENT_ORDER = %w[host port options tty dbname user password]
-
-
-
### Quote the given +value+ for use in a connection-parameter string.
-
1
def self::quote_connstr( value )
-
2
return "'" + value.to_s.gsub( /[\\']/ ) {|m| '\\' + m } + "'"
-
end
-
-
-
### Parse the connection +args+ into a connection-parameter string. See PG::Connection.new
-
### for valid arguments.
-
1
def self::parse_connect_args( *args )
-
1
return '' if args.empty?
-
-
# This will be swapped soon for code that makes options like those required for
-
# PQconnectdbParams()/PQconnectStartParams(). For now, stick to an options string for
-
# PQconnectdb()/PQconnectStart().
-
-
# Parameter 'fallback_application_name' was introduced in PostgreSQL 9.0
-
# together with PQescapeLiteral().
-
37
if PG::Connection.instance_methods.find{|m| m.to_sym == :escape_literal }
-
1
appname = $0.sub(/^(.{30}).{4,}(.{30})$/){ $1+"..."+$2 }
-
1
appname = PG::Connection.quote_connstr( appname )
-
1
connopts = ["fallback_application_name=#{appname}"]
-
else
-
connopts = []
-
end
-
-
# Handle an options hash first
-
1
if args.last.is_a?( Hash )
-
1
opthash = args.pop
-
1
opthash.each do |key, val|
-
1
connopts.push( "%s=%s" % [key, PG::Connection.quote_connstr(val)] )
-
end
-
end
-
-
# Option string style
-
1
if args.length == 1 && args.first.to_s.index( '=' )
-
connopts.unshift( args.first )
-
-
# Append positional parameters
-
else
-
1
args.each_with_index do |val, i|
-
next unless val # Skip nil placeholders
-
-
key = CONNECT_ARGUMENT_ORDER[ i ] or
-
raise ArgumentError, "Extra positional parameter %d: %p" % [ i+1, val ]
-
connopts.push( "%s=%s" % [key, PG::Connection.quote_connstr(val.to_s)] )
-
end
-
end
-
-
1
return connopts.join(' ')
-
end
-
-
# call-seq:
-
# conn.copy_data( sql ) {|sql_result| ... } -> PG::Result
-
#
-
# Execute a copy process for transfering data to or from the server.
-
#
-
# This issues the SQL COPY command via #exec. The response to this
-
# (if there is no error in the command) is a PG::Result object that
-
# is passed to the block, bearing a status code of PGRES_COPY_OUT or
-
# PGRES_COPY_IN (depending on the specified copy direction).
-
# The application should then use #put_copy_data or #get_copy_data
-
# to receive or transmit data rows and should return from the block
-
# when finished.
-
#
-
# #copy_data returns another PG::Result object when the data transfer
-
# is complete. An exception is raised if some problem was encountered,
-
# so it isn't required to make use of any of them.
-
# At this point further SQL commands can be issued via #exec.
-
# (It is not possible to execute other SQL commands using the same
-
# connection while the COPY operation is in progress.)
-
#
-
# This method ensures, that the copy process is properly terminated
-
# in case of client side or server side failures. Therefore, in case
-
# of blocking mode of operation, #copy_data is preferred to raw calls
-
# of #put_copy_data, #get_copy_data and #put_copy_end.
-
#
-
# Example with CSV input format:
-
# conn.exec "create table my_table (a text,b text,c text,d text,e text)"
-
# conn.copy_data "COPY my_table FROM STDOUT CSV" do
-
# conn.put_copy_data "some,csv,data,to,copy\n"
-
# conn.put_copy_data "more,csv,data,to,copy\n"
-
# end
-
# This creates +my_table+ and inserts two rows.
-
#
-
# Example with CSV output format:
-
# conn.copy_data "COPY my_table TO STDOUT CSV" do
-
# while row=conn.get_copy_data
-
# p row
-
# end
-
# end
-
# This prints all rows of +my_table+ to stdout:
-
# "some,csv,data,to,copy\n"
-
# "more,csv,data,to,copy\n"
-
1
def copy_data( sql )
-
res = exec( sql )
-
-
case res.result_status
-
when PGRES_COPY_IN
-
begin
-
yield res
-
rescue Exception => err
-
errmsg = "%s while copy data: %s" % [ err.class.name, err.message ]
-
put_copy_end( errmsg )
-
get_result
-
raise
-
else
-
put_copy_end
-
get_last_result
-
end
-
-
when PGRES_COPY_OUT
-
begin
-
yield res
-
rescue Exception => err
-
cancel
-
while get_copy_data
-
end
-
while get_result
-
end
-
raise
-
else
-
res = get_last_result
-
if res.result_status != PGRES_COMMAND_OK
-
while get_copy_data
-
end
-
while get_result
-
end
-
raise PG::NotAllCopyDataRetrieved, "Not all COPY data retrieved"
-
end
-
res
-
end
-
-
else
-
raise ArgumentError, "SQL command is no COPY statement: #{sql}"
-
end
-
end
-
-
# Backward-compatibility aliases for stuff that's moved into PG.
-
1
class << self
-
1
define_method( :isthreadsafe, &PG.method(:isthreadsafe) )
-
end
-
-
-
### Returns an array of Hashes with connection defaults. See ::conndefaults
-
### for details.
-
1
def conndefaults
-
return self.class.conndefaults
-
end
-
-
end # class PG::Connection
-
-
# Backward-compatible alias
-
1
PGconn = PG::Connection
-
-
#!/usr/bin/env ruby
-
-
1
require 'pg' unless defined?( PG )
-
-
-
1
module PG::Constants
-
-
# Most of these are defined in the extension.
-
-
end # module PG::Constants
-
-
#!/usr/bin/env ruby
-
-
1
require 'pg' unless defined?( PG )
-
-
-
1
module PG
-
-
1
class Error < StandardError; end
-
-
end # module PG
-
-
#!/usr/bin/env ruby
-
-
1
require 'pg' unless defined?( PG )
-
-
-
1
class PG::Result
-
-
### Returns all tuples as an array of arrays
-
1
def values
-
27
return enum_for(:each_row).to_a
-
end
-
-
end # class PG::Result
-
-
# Backward-compatible alias
-
1
PGresult = PG::Result
-
# (C) John Mair (banisterfiend) 2013
-
# MIT License
-
#
-
1
require 'pp'
-
-
1
require 'pry/input_lock'
-
1
require 'pry/exceptions'
-
1
require 'pry/helpers/base_helpers'
-
1
require 'pry/hooks'
-
1
require 'forwardable'
-
-
1
class Pry
-
# The default hooks - display messages when beginning and ending Pry sessions.
-
1
DEFAULT_HOOKS = Pry::Hooks.new.add_hook(:before_session, :default) do |out, target, _pry_|
-
next if _pry_.quiet?
-
_pry_.run_command("whereami --quiet")
-
end
-
-
# The default print
-
1
DEFAULT_PRINT = proc do |output, value, _pry_|
-
_pry_.pager.open do |pager|
-
pager.print _pry_.config.output_prefix
-
Pry::ColorPrinter.pp(value, pager, Pry::Terminal.width! - 1)
-
end
-
end
-
-
# may be convenient when working with enormous objects and
-
# pretty_print is too slow
-
1
SIMPLE_PRINT = proc do |output, value|
-
begin
-
output.puts value.inspect
-
rescue RescuableException
-
output.puts "unknown"
-
end
-
end
-
-
# useful when playing with truly enormous objects
-
1
CLIPPED_PRINT = proc do |output, value|
-
output.puts Pry.view_clip(value, id: true)
-
end
-
-
# Will only show the first line of the backtrace
-
1
DEFAULT_EXCEPTION_HANDLER = proc do |output, exception, _|
-
if UserError === exception && SyntaxError === exception
-
output.puts "SyntaxError: #{exception.message.sub(/.*syntax error, */m, '')}"
-
else
-
output.puts "#{exception.class}: #{exception.message}"
-
output.puts "from #{exception.backtrace.first}"
-
end
-
end
-
-
1
DEFAULT_PROMPT_NAME = 'pry'
-
-
# The default prompt; includes the target and nesting level
-
1
DEFAULT_PROMPT = [
-
proc { |target_self, nest_level, pry|
-
"[#{pry.input_array.size}] #{pry.config.prompt_name}(#{Pry.view_clip(target_self)})#{":#{nest_level}" unless nest_level.zero?}> "
-
},
-
-
proc { |target_self, nest_level, pry|
-
"[#{pry.input_array.size}] #{pry.config.prompt_name}(#{Pry.view_clip(target_self)})#{":#{nest_level}" unless nest_level.zero?}* "
-
}
-
]
-
-
1
DEFAULT_PROMPT_SAFE_OBJECTS = [String, Numeric, Symbol, nil, true, false]
-
-
# A simple prompt - doesn't display target or nesting level
-
1
SIMPLE_PROMPT = [proc { ">> " }, proc { " | " }]
-
-
1
NO_PROMPT = [proc { '' }, proc { '' }]
-
-
1
SHELL_PROMPT = [
-
proc { |target_self, _, _pry_| "#{_pry_.config.prompt_name} #{Pry.view_clip(target_self)}:#{Dir.pwd} $ " },
-
proc { |target_self, _, _pry_| "#{_pry_.config.prompt_name} #{Pry.view_clip(target_self)}:#{Dir.pwd} * " }
-
]
-
-
# A prompt that includes the full object path as well as
-
# input/output (_in_ and _out_) information. Good for navigation.
-
1
NAV_PROMPT = [
-
proc do |_, _, _pry_|
-
tree = _pry_.binding_stack.map { |b| Pry.view_clip(b.eval("self")) }.join " / "
-
"[#{_pry_.input_array.count}] (#{_pry_.config.prompt_name}) #{tree}: #{_pry_.binding_stack.size - 1}> "
-
end,
-
proc do |_, _, _pry_|
-
tree = _pry_.binding_stack.map { |b| Pry.view_clip(b.eval("self")) }.join " / "
-
"[#{_pry_.input_array.count}] (#{ _pry_.config.prompt_name}) #{tree}: #{_pry_.binding_stack.size - 1}* "
-
end,
-
]
-
-
# Deal with the ^D key being pressed. Different behaviour in different cases:
-
# 1. In an expression behave like `!` command.
-
# 2. At top-level session behave like `exit` command.
-
# 3. In a nested session behave like `cd ..`.
-
1
DEFAULT_CONTROL_D_HANDLER = proc do |eval_string, _pry_|
-
if !eval_string.empty?
-
eval_string.replace('') # Clear input buffer.
-
elsif _pry_.binding_stack.one?
-
_pry_.binding_stack.clear
-
throw(:breakout)
-
else
-
# Otherwise, saves current binding stack as old stack and pops last
-
# binding out of binding stack (the old stack still has that binding).
-
_pry_.command_state["cd"] ||= Pry::Config.from_hash({}) # FIXME
-
_pry_.command_state['cd'].old_stack = _pry_.binding_stack.dup
-
_pry_.binding_stack.pop
-
end
-
end
-
-
1
DEFAULT_SYSTEM = proc do |output, cmd, _|
-
if !system(cmd)
-
output.puts "Error: there was a problem executing system command: #{cmd}"
-
end
-
end
-
-
# Store the current working directory. This allows show-source etc. to work if
-
# your process has changed directory since boot. [Issue #675]
-
1
INITIAL_PWD = Dir.pwd
-
-
# This is to keep from breaking under Rails 3.2 for people who are doing that
-
# IRB = Pry thing.
-
1
module ExtendCommandBundle; end
-
end
-
-
1
require 'method_source'
-
1
require 'shellwords'
-
1
require 'stringio'
-
1
require 'coderay'
-
1
require 'slop'
-
1
require 'rbconfig'
-
1
require 'tempfile'
-
1
require 'pathname'
-
-
1
require 'pry/version'
-
1
require 'pry/repl'
-
1
require 'pry/rbx_path'
-
1
require 'pry/code'
-
1
require 'pry/history_array'
-
1
require 'pry/helpers'
-
1
require 'pry/code_object'
-
1
require 'pry/method'
-
1
require 'pry/wrapped_module'
-
1
require 'pry/history'
-
1
require 'pry/command'
-
1
require 'pry/command_set'
-
1
require 'pry/commands'
-
1
require 'pry/plugins'
-
1
require 'pry/core_extensions'
-
1
require 'pry/pry_class'
-
1
require 'pry/pry_instance'
-
1
require 'pry/cli'
-
1
require 'pry/color_printer'
-
1
require 'pry/pager'
-
1
require 'pry/terminal'
-
1
require 'pry/editor'
-
1
require 'pry/rubygem'
-
1
require "pry/indent"
-
1
require "pry/last_exception"
-
1
require "pry/prompt"
-
1
require "pry/inspector"
-
1
require 'pry/object_path'
-
1
require 'pry/output'
-
1
class Pry
-
-
# Manage the processing of command line options
-
1
class CLI
-
-
1
NoOptionsError = Class.new(StandardError)
-
-
1
class << self
-
-
# @return [Proc] The Proc defining the valid command line options.
-
1
attr_accessor :options
-
-
# @return [Array] The Procs that process the parsed options. Plugins can
-
# utilize this facility in order to add and process their own Pry
-
# options.
-
1
attr_accessor :option_processors
-
-
# @return [Array<String>] The input array of strings to process
-
# as CLI options.
-
1
attr_accessor :input_args
-
-
# Add another set of CLI options (a Slop block)
-
1
def add_options(&block)
-
1
if options
-
old_options = options
-
self.options = proc do
-
instance_exec(&old_options)
-
instance_exec(&block)
-
end
-
else
-
1
self.options = block
-
end
-
-
1
self
-
end
-
-
# Bring in options defined in plugins
-
1
def add_plugin_options
-
1
Pry.plugins.values.each do |plugin|
-
plugin.load_cli_options
-
end
-
-
1
self
-
end
-
-
# Add a block responsible for processing parsed options.
-
1
def add_option_processor(&block)
-
1
self.option_processors ||= []
-
1
option_processors << block
-
-
1
self
-
end
-
-
# Clear `options` and `option_processors`
-
1
def reset
-
1
self.options = nil
-
1
self.option_processors = nil
-
end
-
-
1
def parse_options(args=ARGV)
-
unless options
-
raise NoOptionsError, "No command line options defined! Use Pry::CLI.add_options to add command line options."
-
end
-
-
self.input_args = args
-
-
begin
-
opts = Slop.parse!(
-
args,
-
:help => true,
-
:multiple_switches => false,
-
:strict => true,
-
&options
-
)
-
rescue Slop::InvalidOptionError
-
# Display help message on unknown switches and exit.
-
puts Slop.new(&options)
-
exit
-
end
-
-
# Option processors are optional.
-
if option_processors
-
option_processors.each { |processor| processor.call(opts) }
-
end
-
-
self
-
end
-
-
end
-
-
1
reset
-
end
-
end
-
-
-
# String that is built to be executed on start (created by -e and -exec switches)
-
1
exec_string = ""
-
-
# Bring in options defined by plugins
-
Slop.new do
-
1
on "no-plugins" do
-
Pry.config.should_load_plugins = false
-
end
-
1
end.parse(ARGV.dup)
-
-
1
if Pry.config.should_load_plugins
-
1
Pry::CLI.add_plugin_options
-
end
-
-
# The default Pry command line options (before plugin options are included)
-
Pry::CLI.add_options do
-
banner %{Usage: pry [OPTIONS]
-
Start a Pry session.
-
See http://pryrepl.org/ for more information.
-
Copyright (c) 2013 John Mair (banisterfiend)
-
--
-
}
-
on :e, :exec=, "A line of code to execute in context before the session starts" do |input|
-
exec_string << input << "\n"
-
end
-
-
on "no-pager", "Disable pager for long output" do
-
Pry.config.pager = false
-
end
-
-
on "no-history", "Disable history loading" do
-
Pry.config.history.should_load = false
-
end
-
-
on "no-color", "Disable syntax highlighting for session" do
-
Pry.config.color = false
-
end
-
-
on :f, "Suppress loading of ~/.pryrc and ./.pryrc" do
-
Pry.config.should_load_rc = false
-
Pry.config.should_load_local_rc = false
-
end
-
-
on :s, "select-plugin=", "Only load specified plugin (and no others)." do |plugin_name|
-
Pry.config.should_load_plugins = false
-
Pry.plugins[plugin_name].activate!
-
end
-
-
on :d, "disable-plugin=", "Disable a specific plugin." do |plugin_name|
-
Pry.plugins[plugin_name].disable!
-
end
-
-
on "no-plugins", "Suppress loading of plugins." do
-
Pry.config.should_load_plugins = false
-
end
-
-
on "plugins", "List installed plugins." do
-
puts "Installed Plugins:"
-
puts "--"
-
Pry.locate_plugins.each do |plugin|
-
puts "#{plugin.name}".ljust(18) << plugin.spec.summary
-
end
-
exit
-
end
-
-
on "simple-prompt", "Enable simple prompt mode" do
-
Pry.config.prompt = Pry::SIMPLE_PROMPT
-
end
-
-
on "noprompt", "No prompt mode" do
-
Pry.config.prompt = Pry::NO_PROMPT
-
end
-
-
on :r, :require=, "`require` a Ruby script at startup" do |file|
-
Pry.config.requires << file
-
end
-
-
on :I=, "Add a path to the $LOAD_PATH", :as => Array, :delimiter => ":" do |load_path|
-
load_path.map! do |path|
-
/\A\.\// =~ path ? path : File.expand_path(path)
-
end
-
-
$LOAD_PATH.unshift(*load_path)
-
end
-
-
on "gem", "Shorthand for -I./lib -rgemname" do |load_path|
-
$LOAD_PATH.unshift("./lib")
-
Dir["./lib/*.rb"].each do |file|
-
Pry.config.requires << file
-
end
-
end
-
-
on :v, :version, "Display the Pry version" do
-
puts "Pry version #{Pry::VERSION} on Ruby #{RUBY_VERSION}"
-
exit
-
end
-
-
on(:c, :context=,
-
"Start the session in the specified context. Equivalent to `context.pry` in a session.",
-
:default => "Pry.toplevel_binding"
-
)
-
1
end.add_option_processor do |opts|
-
-
exit if opts.help?
-
-
# invoked via cli
-
Pry.cli = true
-
-
# create the actual context
-
if opts[:context]
-
Pry.initial_session_setup
-
context = Pry.binding_for(eval(opts[:context]))
-
else
-
context = Pry.toplevel_binding
-
end
-
-
if Pry::CLI.input_args.any? && Pry::CLI.input_args != ["pry"]
-
full_name = File.expand_path(Pry::CLI.input_args.first)
-
Pry.load_file_through_repl(full_name)
-
exit
-
end
-
-
# Start the session (running any code passed with -e, if there is any)
-
Pry.start(context, :input => StringIO.new(exec_string))
-
end
-
1
require 'pry/code/loc'
-
1
require 'pry/code/code_range'
-
1
require 'pry/code/code_file'
-
-
1
class Pry
-
1
class << self
-
# Convert the given object into an instance of `Pry::Code`, if it isn't
-
# already one.
-
#
-
# @param [Code, Method, UnboundMethod, Proc, Pry::Method, String, Array,
-
# IO] obj
-
1
def Code(obj)
-
case obj
-
when Code
-
obj
-
when ::Method, UnboundMethod, Proc, Pry::Method
-
Code.from_method(obj)
-
else
-
Code.new(obj)
-
end
-
end
-
end
-
-
# `Pry::Code` is a class that encapsulates lines of source code and their
-
# line numbers and formats them for terminal output. It can read from a file
-
# or method definition or be instantiated with a `String` or an `Array`.
-
#
-
# In general, the formatting methods in `Code` return a new `Code` object
-
# which will format the text as specified when `#to_s` is called. This allows
-
# arbitrary chaining of formatting methods without mutating the original
-
# object.
-
1
class Code
-
1
class << self
-
1
include MethodSource::CodeHelpers
-
-
# Instantiate a `Code` object containing code loaded from a file or
-
# Pry's line buffer.
-
#
-
# @param [String] filename The name of a file, or "(pry)".
-
# @param [Symbol] code_type The type of code the file contains.
-
# @return [Code]
-
1
def from_file(filename, code_type = nil)
-
code_file = CodeFile.new(filename, code_type)
-
new(code_file.code, 1, code_file.code_type)
-
end
-
-
# Instantiate a `Code` object containing code extracted from a
-
# `::Method`, `UnboundMethod`, `Proc`, or `Pry::Method` object.
-
#
-
# @param [::Method, UnboundMethod, Proc, Pry::Method] meth The method
-
# object.
-
# @param [Integer, nil] start_line The line number to start on, or nil to
-
# use the method's original line numbers.
-
# @return [Code]
-
1
def from_method(meth, start_line = nil)
-
meth = Pry::Method(meth)
-
start_line ||= meth.source_line || 1
-
new(meth.source, start_line, meth.source_type)
-
end
-
-
# Attempt to extract the source code for module (or class) `mod`.
-
#
-
# @param [Module, Class] mod The module (or class) of interest.
-
# @param [Integer] candidate_rank The module candidate (by rank)
-
# to use (see `Pry::WrappedModule::Candidate` for more information).
-
# @param [Integer, nil] start_line The line number to start on, or nil to
-
# use the method's original line numbers.
-
# @return [Code]
-
1
def from_module(mod, candidate_rank = 0, start_line=nil)
-
candidate = Pry::WrappedModule(mod).candidate(candidate_rank)
-
start_line ||= candidate.line
-
new(candidate.source, start_line, :ruby)
-
end
-
end
-
-
# @return [Symbol] The type of code stored in this wrapper.
-
1
attr_accessor :code_type
-
-
# Instantiate a `Code` object containing code from the given `Array`,
-
# `String`, or `IO`. The first line will be line 1 unless specified
-
# otherwise. If you need non-contiguous line numbers, you can create an
-
# empty `Code` object and then use `#push` to insert the lines.
-
#
-
# @param [Array<String>, String, IO] lines
-
# @param [Integer?] start_line
-
# @param [Symbol?] code_type
-
1
def initialize(lines = [], start_line = 1, code_type = :ruby)
-
if lines.is_a? String
-
lines = lines.lines
-
end
-
@lines = lines.each_with_index.map { |line, lineno|
-
LOC.new(line, lineno + start_line.to_i) }
-
@code_type = code_type
-
end
-
-
# Append the given line. +lineno+ is one more than the last existing
-
# line, unless specified otherwise.
-
#
-
# @param [String] line
-
# @param [Integer?] lineno
-
# @return [String] The inserted line.
-
1
def push(line, lineno = nil)
-
if lineno.nil?
-
lineno = @lines.last.lineno + 1
-
end
-
@lines.push(LOC.new(line, lineno))
-
line
-
end
-
1
alias << push
-
-
# Filter the lines using the given block.
-
#
-
# @yield [LOC]
-
# @return [Code]
-
1
def select(&block)
-
alter do
-
@lines = @lines.select(&block)
-
end
-
end
-
-
# Remove all lines that aren't in the given range, expressed either as a
-
# `Range` object or a first and last line number (inclusive). Negative
-
# indices count from the end of the array of lines.
-
#
-
# @param [Range, Integer] start_line
-
# @param [Integer?] end_line
-
# @return [Code]
-
1
def between(start_line, end_line = nil)
-
return self unless start_line
-
-
code_range = CodeRange.new(start_line, end_line)
-
-
alter do
-
@lines = @lines[code_range.indices_range(@lines)] || []
-
end
-
end
-
-
# Take `num_lines` from `start_line`, forward or backwards.
-
#
-
# @param [Integer] start_line
-
# @param [Integer] num_lines
-
# @return [Code]
-
1
def take_lines(start_line, num_lines)
-
start_idx =
-
if start_line >= 0
-
@lines.index { |loc| loc.lineno >= start_line } || @lines.length
-
else
-
[@lines.length + start_line, 0].max
-
end
-
-
alter do
-
@lines = @lines.slice(start_idx, num_lines)
-
end
-
end
-
-
# Remove all lines except for the +lines+ up to and excluding +lineno+.
-
#
-
# @param [Integer] lineno
-
# @param [Integer] lines
-
# @return [Code]
-
1
def before(lineno, lines = 1)
-
return self unless lineno
-
-
select do |loc|
-
loc.lineno >= lineno - lines && loc.lineno < lineno
-
end
-
end
-
-
# Remove all lines except for the +lines+ on either side of and including
-
# +lineno+.
-
#
-
# @param [Integer] lineno
-
# @param [Integer] lines
-
# @return [Code]
-
1
def around(lineno, lines = 1)
-
return self unless lineno
-
-
select do |loc|
-
loc.lineno >= lineno - lines && loc.lineno <= lineno + lines
-
end
-
end
-
-
# Remove all lines except for the +lines+ after and excluding +lineno+.
-
#
-
# @param [Integer] lineno
-
# @param [Integer] lines
-
# @return [Code]
-
1
def after(lineno, lines = 1)
-
return self unless lineno
-
-
select do |loc|
-
loc.lineno > lineno && loc.lineno <= lineno + lines
-
end
-
end
-
-
# Remove all lines that don't match the given `pattern`.
-
#
-
# @param [Regexp] pattern
-
# @return [Code]
-
1
def grep(pattern)
-
return self unless pattern
-
pattern = Regexp.new(pattern)
-
-
select do |loc|
-
loc.line =~ pattern
-
end
-
end
-
-
# Format output with line numbers next to it, unless `y_n` is falsy.
-
#
-
# @param [Boolean?] y_n
-
# @return [Code]
-
1
def with_line_numbers(y_n = true)
-
alter do
-
@with_line_numbers = y_n
-
end
-
end
-
-
# Format output with a marker next to the given +lineno+, unless +lineno+ is
-
# falsy.
-
#
-
# @param [Integer?] lineno
-
# @return [Code]
-
1
def with_marker(lineno = 1)
-
alter do
-
@with_marker = !!lineno
-
@marker_lineno = lineno
-
end
-
end
-
-
# Format output with the specified number of spaces in front of every line,
-
# unless `spaces` is falsy.
-
#
-
# @param [Integer?] spaces
-
# @return [Code]
-
1
def with_indentation(spaces = 0)
-
alter do
-
@with_indentation = !!spaces
-
@indentation_num = spaces
-
end
-
end
-
-
# @return [String]
-
1
def inspect
-
Object.instance_method(:to_s).bind(self).call
-
end
-
-
# @return [Integer] the number of digits in the last line.
-
1
def max_lineno_width
-
@lines.length > 0 ? @lines.last.lineno.to_s.length : 0
-
end
-
-
# @return [String] a formatted representation (based on the configuration of
-
# the object).
-
1
def to_s
-
print_to_output("", false)
-
end
-
-
# @return [String] a (possibly highlighted) copy of the source code.
-
1
def highlighted
-
print_to_output("", true)
-
end
-
-
# Writes a formatted representation (based on the configuration of the
-
# object) to the given output, which must respond to `#<<`.
-
1
def print_to_output(output, color=false)
-
@lines.each do |loc|
-
loc = loc.dup
-
loc.colorize(@code_type) if color
-
loc.add_line_number(max_lineno_width, color) if @with_line_numbers
-
loc.add_marker(@marker_lineno) if @with_marker
-
loc.indent(@indentation_num) if @with_indentation
-
output << loc.line
-
output << "\n"
-
end
-
output
-
end
-
-
# Get the comment that describes the expression on the given line number.
-
#
-
# @param [Integer] line_number (1-based)
-
# @return [String] the code.
-
1
def comment_describing(line_number)
-
self.class.comment_describing(raw, line_number)
-
end
-
-
# Get the multiline expression that starts on the given line number.
-
#
-
# @param [Integer] line_number (1-based)
-
# @return [String] the code.
-
1
def expression_at(line_number, consume = 0)
-
self.class.expression_at(raw, line_number, :consume => consume)
-
end
-
-
# Get the (approximate) Module.nesting at the give line number.
-
#
-
# @param [Integer] line_number line number starting from 1
-
# @param [Module] top_module the module in which this code exists
-
# @return [Array<Module>] a list of open modules.
-
1
def nesting_at(line_number, top_module = Object)
-
Pry::Indent.nesting_at(raw, line_number)
-
end
-
-
# Return an unformatted String of the code.
-
#
-
# @return [String]
-
1
def raw
-
@lines.map(&:line).join("\n") << "\n"
-
end
-
-
# Return the number of lines stored.
-
#
-
# @return [Integer]
-
1
def length
-
@lines ? @lines.length : 0
-
end
-
-
# Two `Code` objects are equal if they contain the same lines with the same
-
# numbers. Otherwise, call `to_s` and `chomp` and compare as Strings.
-
#
-
# @param [Code, Object] other
-
# @return [Boolean]
-
1
def ==(other)
-
if other.is_a?(Code)
-
other_lines = other.instance_variable_get(:@lines)
-
@lines.each_with_index.all? { |loc, i| loc == other_lines[i] }
-
else
-
to_s.chomp == other.to_s.chomp
-
end
-
end
-
-
# Forward any missing methods to the output of `#to_s`.
-
1
def method_missing(name, *args, &block)
-
to_s.send(name, *args, &block)
-
end
-
1
undef =~
-
-
1
protected
-
-
# An abstraction of the `dup.instance_eval` pattern used throughout this
-
# class.
-
1
def alter(&block)
-
dup.tap { |o| o.instance_eval(&block) }
-
end
-
end
-
end
-
1
class Pry
-
1
class CodeFile
-
1
DEFAULT_EXT = '.rb'
-
-
# List of all supported languages.
-
# @return [Hash]
-
1
EXTENSIONS = {
-
%w(.py) => :python,
-
%w(.js) => :javascript,
-
%w(.css) => :css,
-
%w(.xml) => :xml,
-
%w(.php) => :php,
-
%w(.html) => :html,
-
%w(.diff) => :diff,
-
%w(.java) => :java,
-
%w(.json) => :json,
-
%w(.c .h) => :c,
-
%w(.rhtml) => :rhtml,
-
%w(.yaml .yml) => :yaml,
-
%w(.cpp .hpp .cc .h cxx) => :cpp,
-
%w(.rb .ru .irbrc .gemspec .pryrc) => :ruby,
-
}
-
-
# @return [Symbol] The type of code stored in this wrapper.
-
1
attr_reader :code_type
-
-
# @param [String] filename The name of a file with code to be detected
-
# @param [Symbol] code_type The type of code the `filename` contains
-
1
def initialize(filename, code_type = type_from_filename(filename))
-
@filename = filename
-
@code_type = code_type
-
end
-
-
# @return [String] The code contained in the current `@filename`.
-
1
def code
-
if @filename == Pry.eval_path
-
Pry.line_buffer.drop(1)
-
elsif Pry::Method::Patcher.code_for(@filename)
-
Pry::Method::Patcher.code_for(@filename)
-
elsif RbxPath.is_core_path?(@filename)
-
File.read(RbxPath.convert_path_to_full(@filename))
-
else
-
path = abs_path
-
@code_type = type_from_filename(path)
-
File.read(path)
-
end
-
end
-
-
1
private
-
-
# @raise [MethodSource::SourceNotFoundError] if the `filename` is not
-
# readable for some reason.
-
# @return [String] absolute path for the given `filename`.
-
1
def abs_path
-
code_path.detect { |path| readable?(path) } or
-
raise MethodSource::SourceNotFoundError,
-
"Cannot open #{ @filename.inspect } for reading."
-
end
-
-
# @param [String] path
-
# @return [Boolean] if the path, with or without the default ext,
-
# is a readable file then `true`, otherwise `false`.
-
1
def readable?(path)
-
File.readable?(path) && !File.directory?(path) or
-
File.readable?(path << DEFAULT_EXT)
-
end
-
-
# @return [Array] All the paths that contain code that Pry can use for its
-
# API's. Skips directories.
-
1
def code_path
-
[from_pwd, from_pry_init_pwd, *from_load_path]
-
end
-
-
# @param [String] filename
-
# @param [Symbol] default (:unknown) the file type to assume if none could be
-
# detected.
-
# @return [Symbol, nil] The CodeRay type of a file from its extension, or
-
# `nil` if `:unknown`.
-
1
def type_from_filename(filename, default = :unknown)
-
_, @code_type = EXTENSIONS.find do |k, _|
-
k.any? { |ext| ext == File.extname(filename) }
-
end
-
-
code_type || default
-
end
-
-
# @return [String]
-
1
def from_pwd
-
File.expand_path(@filename, Dir.pwd)
-
end
-
-
# @return [String]
-
1
def from_pry_init_pwd
-
File.expand_path(@filename, Pry::INITIAL_PWD)
-
end
-
-
# @return [String]
-
1
def from_load_path
-
$LOAD_PATH.map { |path| File.expand_path(@filename, path) }
-
end
-
-
end
-
end
-
1
class Pry
-
1
class Code
-
-
# Represents a range of lines in a code listing.
-
#
-
# @api private
-
1
class CodeRange
-
-
# @param [Integer] start_line
-
# @param [Integer?] end_line
-
1
def initialize(start_line, end_line = nil)
-
@start_line = start_line
-
@end_line = end_line
-
force_set_end_line
-
end
-
-
# @param [Array<LOC>] lines
-
# @return [Range]
-
1
def indices_range(lines)
-
Range.new(*indices(lines))
-
end
-
-
1
private
-
-
1
def start_line; @start_line; end
-
1
def end_line; @end_line; end
-
-
# If `end_line` is equal to `nil`, then calculate it from the first
-
# parameter, `start_line`. Otherwise, leave it as it is.
-
# @return [void]
-
1
def force_set_end_line
-
if start_line.is_a?(Range)
-
set_end_line_from_range
-
else
-
@end_line ||= start_line
-
end
-
end
-
-
# Finds indices of `start_line` and `end_line` in the given Array of
-
# +lines+.
-
#
-
# @param [Array<LOC>] lines
-
# @return [Array<Integer>]
-
1
def indices(lines)
-
[find_start_index(lines), find_end_index(lines)]
-
end
-
-
# @return [Integer]
-
1
def find_start_index(lines)
-
return start_line if start_line < 0
-
lines.index { |loc| loc.lineno >= start_line } || lines.length
-
end
-
-
# @return [Integer]
-
1
def find_end_index(lines)
-
return end_line if end_line < 0
-
(lines.index { |loc| loc.lineno > end_line } || 0) - 1
-
end
-
-
# For example, if the range is 4..10, then `start_line` would be equal to
-
# 4 and `end_line` to 10.
-
# @return [void]
-
1
def set_end_line_from_range
-
@end_line = start_line.last
-
@end_line -= 1 if start_line.exclude_end?
-
@start_line = start_line.first
-
end
-
end
-
-
end
-
end
-
1
class Pry
-
1
class Code
-
-
# Represents a line of code. A line of code is a tuple, which consists of a
-
# line and a line number. A `LOC` object's state (namely, the line
-
# parameter) can be changed via instance methods. `Pry::Code` heavily uses
-
# this class.
-
#
-
# @api private
-
# @example
-
# loc = LOC.new("def example\n :example\nend", 1)
-
# puts loc.line
-
# def example
-
# :example
-
# end
-
# #=> nil
-
#
-
# loc.indent(3)
-
# loc.line #=> " def example\n :example\nend"
-
1
class LOC
-
-
# @return [Array<String, Integer>]
-
1
attr_reader :tuple
-
-
# @param [String] line The line of code.
-
# @param [Integer] lineno The position of the +line+.
-
1
def initialize(line, lineno)
-
@tuple = [line.chomp, lineno.to_i]
-
end
-
-
# @return [Boolean]
-
1
def ==(other)
-
other.tuple == tuple
-
end
-
-
1
def dup
-
self.class.new(line, lineno)
-
end
-
-
# @return [String]
-
1
def line
-
tuple.first
-
end
-
-
# @return [Integer]
-
1
def lineno
-
tuple.last
-
end
-
-
# Paints the `line` of code.
-
#
-
# @param [Symbol] code_type
-
# @return [void]
-
1
def colorize(code_type)
-
tuple[0] = CodeRay.scan(line, code_type).term
-
end
-
-
# Prepends the line number `lineno` to the `line`.
-
#
-
# @param [Integer] max_width
-
# @return [void]
-
1
def add_line_number(max_width = 0, color = false)
-
padded = lineno.to_s.rjust(max_width)
-
colorized_lineno = color ? Pry::Helpers::BaseHelpers.colorize_code(padded) : padded
-
tuple[0] = "#{ colorized_lineno }: #{ line }"
-
end
-
-
# Prepends a marker "=>" or an empty marker to the +line+.
-
#
-
# @param [Integer] marker_lineno If it is equal to the `lineno`, then
-
# prepend a hashrocket. Otherwise, an empty marker.
-
# @return [void]
-
1
def add_marker(marker_lineno)
-
tuple[0] =
-
if lineno == marker_lineno
-
" => #{ line }"
-
else
-
" #{ line }"
-
end
-
end
-
-
# Indents the `line` with +distance+ spaces.
-
#
-
# @param [Integer] distance
-
# @return [void]
-
1
def indent(distance)
-
tuple[0] = "#{ ' ' * distance }#{ line }"
-
end
-
end
-
-
end
-
end
-
1
class Pry
-
-
# This class is responsible for taking a string (identifying a
-
# command/class/method/etc) and returning the relevant type of object.
-
# For example, if the user looks up "show-source" then a
-
# `Pry::Command` will be returned. Alternatively, if the user passes in "Pry#repl" then
-
# a `Pry::Method` object will be returned.
-
#
-
# The `CodeObject.lookup` method is responsible for 1. figuring out what kind of
-
# object the user wants (applying precedence rules in doing so -- i.e methods
-
# get precedence over commands with the same name) and 2. Returning
-
# the appropriate object. If the user fails to provide a string
-
# identifer for the object (i.e they pass in `nil` or "") then the
-
# object looked up will be the 'current method' or 'current class'
-
# associated with the Binding.
-
#
-
# TODO: This class is a clusterfuck. We need a much more robust
-
# concept of what a "Code Object" really is. Currently
-
# commands/classes/candidates/methods and so on just share a very
-
# ill-defined interface.
-
1
class CodeObject
-
1
module Helpers
-
# we need this helper as some Pry::Method objects can wrap Procs
-
# @return [Boolean]
-
1
def real_method_object?
-
is_a?(::Method) || is_a?(::UnboundMethod)
-
end
-
-
1
def c_method?
-
real_method_object? && source_type == :c
-
end
-
-
1
def module_with_yard_docs?
-
is_a?(WrappedModule) && yard_docs?
-
end
-
-
1
def command?
-
is_a?(Module) && self <= Pry::Command
-
end
-
end
-
-
1
include Pry::Helpers::CommandHelpers
-
-
1
class << self
-
1
def lookup(str, _pry_, options={})
-
co = new(str, _pry_, options)
-
-
co.default_lookup || co.method_or_class_lookup ||
-
co.command_lookup || co.empty_lookup
-
end
-
end
-
-
1
attr_accessor :str
-
1
attr_accessor :target
-
1
attr_accessor :_pry_
-
1
attr_accessor :super_level
-
-
1
def initialize(str, _pry_, options={})
-
options = {
-
:super => 0,
-
}.merge!(options)
-
-
@str = str
-
@_pry_ = _pry_
-
@target = _pry_.current_context
-
@super_level = options[:super]
-
end
-
-
1
def command_lookup
-
# TODO: just make it so find_command_by_match_or_listing doesn't
-
# raise?
-
_pry_.commands.find_command_by_match_or_listing(str) rescue nil
-
end
-
-
# when no paramter is given (i.e CodeObject.lookup(nil)), then we
-
# lookup the 'current object' from the binding.
-
1
def empty_lookup
-
return nil if str && !str.empty?
-
-
obj = if internal_binding?(target)
-
mod = target_self.is_a?(Module) ? target_self : target_self.class
-
Pry::WrappedModule(mod)
-
else
-
Pry::Method.from_binding(target)
-
end
-
-
# respect the super level (i.e user might have specified a
-
# --super flag to show-source)
-
lookup_super(obj, super_level)
-
end
-
-
# lookup variables and constants and `self` that are not modules
-
1
def default_lookup
-
-
# we skip instance methods as we want those to fall through to method_or_class_lookup()
-
if safe_to_evaluate?(str) && !looks_like_an_instance_method?(str)
-
obj = target.eval(str)
-
-
# restrict to only objects we KNOW for sure support the full API
-
# Do NOT support just any object that responds to source_location
-
if sourcable_object?(obj)
-
Pry::Method(obj)
-
elsif !obj.is_a?(Module)
-
Pry::WrappedModule(obj.class)
-
else
-
nil
-
end
-
end
-
-
rescue Pry::RescuableException
-
nil
-
end
-
-
1
def method_or_class_lookup
-
obj = case str
-
when /\S+\(\)\z/
-
Pry::Method.from_str(str.sub(/\(\)\z/, ''),target) || Pry::WrappedModule.from_str(str, target)
-
else
-
Pry::WrappedModule.from_str(str,target) || Pry::Method.from_str(str, target)
-
end
-
-
lookup_super(obj, super_level)
-
end
-
-
1
private
-
-
1
def sourcable_object?(obj)
-
[::Proc, ::Method, ::UnboundMethod].any? { |o| obj.is_a?(o) }
-
end
-
-
# Returns true if `str` looks like a method, i.e Klass#method
-
# We need to consider this case because method lookups should fall
-
# through to the `method_or_class_lookup()` method but a
-
# defined?() on a "Klass#method` string will see the `#` as a
-
# comment and only evaluate the `Klass` part.
-
# @param [String] str
-
# @return [Boolean] Whether the string looks like an instance method.
-
1
def looks_like_an_instance_method?(str)
-
str =~ /\S#\S/
-
end
-
-
# We use this method to decide whether code is safe to eval. Method's are
-
# generally not, but everything else is.
-
# TODO: is just checking != "method" enough??
-
# TODO: see duplication of this method in Pry::WrappedModule
-
# @param [String] str The string to lookup
-
# @return [Boolean]
-
1
def safe_to_evaluate?(str)
-
return true if str.strip == "self"
-
kind = target.eval("defined?(#{str})")
-
kind =~ /variable|constant/
-
end
-
-
1
def target_self
-
target.eval('self')
-
end
-
-
# grab the nth (`super_level`) super of `obj
-
# @param [Object] obj
-
# @param [Fixnum] super_level How far up the super chain to ascend.
-
1
def lookup_super(obj, super_level)
-
return nil if !obj
-
-
sup = obj.super(super_level)
-
if !sup
-
raise Pry::CommandError, "No superclass found for #{obj.wrapped}"
-
else
-
sup
-
end
-
end
-
end
-
end
-
# PP subclass for streaming inspect output in color.
-
1
class Pry
-
1
class ColorPrinter < ::PP
-
1
OBJ_COLOR = begin
-
1
code = CodeRay::Encoders::Terminal::TOKEN_COLORS[:keyword]
-
1
if code.start_with? "\e"
-
1
code
-
else
-
"\e[0m\e[0;#{code}m"
-
end
-
end
-
-
1
CodeRay::Encoders::Terminal::TOKEN_COLORS[:comment][:self] = "\e[1;34m"
-
-
1
def self.pp(obj, out = $>, width = 79)
-
q = ColorPrinter.new(out, width)
-
q.guard_inspect_key { q.pp obj }
-
q.flush
-
out << "\n"
-
end
-
-
1
def text(str, width = str.length)
-
# Don't recolorize output with color [Issue #751]
-
if str.include?("\e[")
-
super "#{str}\e[0m", width
-
elsif str.start_with?('#<') || str == '=' || str == '>'
-
super highlight_object_literal(str), width
-
else
-
super CodeRay.scan(str, :ruby).term, width
-
end
-
end
-
-
1
def pp(obj)
-
super
-
rescue => e
-
raise if e.is_a? Pry::Pager::StopPaging
-
-
# Read the class name off of the singleton class to provide a default
-
# inspect.
-
singleton = class << obj; self; end
-
ancestors = Pry::Method.safe_send(singleton, :ancestors)
-
klass = ancestors.reject { |k| k == singleton }.first
-
obj_id = obj.__id__.to_s(16) rescue 0
-
str = "#<#{klass}:0x#{obj_id}>"
-
-
text highlight_object_literal(str)
-
end
-
-
1
private
-
-
1
def highlight_object_literal(object_literal)
-
"#{OBJ_COLOR}#{object_literal}\e[0m"
-
end
-
end
-
end
-
1
require 'delegate'
-
1
require 'pry/helpers/documentation_helpers'
-
-
1
class Pry
-
-
# The super-class of all commands, new commands should be created by calling
-
# {Pry::CommandSet#command} which creates a BlockCommand or {Pry::CommandSet#create_command}
-
# which creates a ClassCommand. Please don't use this class directly.
-
1
class Command
-
1
extend Helpers::DocumentationHelpers
-
1
extend CodeObject::Helpers
-
-
# represents a void return value for a command
-
1
VOID_VALUE = Object.new
-
-
# give it a nice inspect
-
1
def VOID_VALUE.inspect() "void" end
-
-
# Properties of the command itself (as passed as arguments to
-
# {CommandSet#command} or {CommandSet#create_command}).
-
1
class << self
-
1
attr_writer :block
-
1
attr_writer :description
-
1
attr_writer :command_options
-
1
attr_writer :match
-
-
1
def match(arg=nil)
-
810
if arg
-
50
@command_options ||= default_options(arg)
-
50
@command_options[:listing] = arg.is_a?(String) ? arg : arg.inspect
-
50
@match = arg
-
end
-
810
@match ||= nil
-
end
-
-
# Define or get the command's description
-
1
def description(arg=nil)
-
152
@description = arg if arg
-
152
@description ||= nil
-
end
-
-
# Define or get the command's options
-
1
def command_options(arg=nil)
-
641
@command_options ||= default_options(match)
-
641
@command_options.merge!(arg) if arg
-
641
@command_options
-
end
-
# backward compatibility
-
1
alias_method :options, :command_options
-
1
alias_method :options=, :command_options=
-
-
# Define or get the command's banner
-
1
def banner(arg=nil)
-
48
@banner = arg if arg
-
48
@banner || description
-
end
-
-
1
def block
-
@block || instance_method(:process)
-
end
-
-
1
def source
-
file, line = block.source_location
-
strip_leading_whitespace(Pry::Code.from_file(file).expression_at(line))
-
end
-
-
1
def doc
-
new.help
-
end
-
-
1
def source_location
-
block.source_location
-
end
-
-
1
def source_file
-
Array(block.source_location).first
-
end
-
1
alias_method :file, :source_file
-
-
1
def source_line
-
Array(block.source_location).last
-
end
-
1
alias_method :line, :source_line
-
-
1
def default_options(match)
-
{
-
:requires_gem => [],
-
:keep_retval => false,
-
:argument_required => false,
-
:interpolate => true,
-
:shellwords => true,
-
73
:listing => (String === match ? match : match.inspect),
-
:use_prefix => true,
-
:takes_block => false
-
73
}
-
end
-
end
-
-
# Make those properties accessible to instances
-
1
def name; self.class.name; end
-
1
def match; self.class.match; end
-
1
def description; self.class.description; end
-
1
def block; self.class.block; end
-
1
def command_options; self.class.options; end
-
1
def command_name; self.class.command_name; end
-
1
def source; self.class.source; end
-
1
def source_location; self.class.source_location; end
-
-
1
class << self
-
1
def name
-
super.to_s == "" ? "#<class(Pry::Command #{match.inspect})>" : super
-
end
-
-
1
def inspect
-
name
-
end
-
-
1
def command_name
-
self.options[:listing]
-
end
-
-
# Create a new command with the given properties.
-
# @param [String, Regex] match The thing that triggers this command
-
# @param [String] description The description to appear in `help`
-
# @param [Hash] options Behavioral options (see {Pry::CommandSet#command})
-
# @param [Module] helpers A module of helper functions to be included.
-
# @yield optional, used for BlockCommands
-
# @return [Class] (a subclass of {Pry::Command})
-
1
def subclass(match, description, options, helpers, &block)
-
21
klass = Class.new(self)
-
21
klass.send(:include, helpers)
-
21
klass.match = match
-
21
klass.description = description
-
21
klass.command_options = options
-
21
klass.block = block
-
21
klass
-
end
-
-
# Should this command be called for the given line?
-
# @param [String] val A line input at the REPL
-
# @return [Boolean]
-
1
def matches?(val)
-
498
command_regex =~ val
-
end
-
-
# How well does this command match the given line?
-
#
-
# Higher scores are better because they imply that this command matches
-
# the line more closely.
-
#
-
# The score is calculated by taking the number of characters at the start
-
# of the string that are used only to identify the command, not as part of
-
# the arguments.
-
#
-
# @example
-
# /\.(.*)/.match_score(".foo") #=> 1
-
# /\.*(.*)/.match_score("...foo") #=> 3
-
# 'hi'.match_score("hi there") #=> 2
-
#
-
# @param [String] val A line input at the REPL
-
# @return [Fixnum]
-
1
def match_score(val)
-
12
if command_regex =~ val
-
12
Regexp.last_match.size > 1 ? Regexp.last_match.begin(1) : Regexp.last_match.end(0)
-
else
-
-1
-
end
-
end
-
-
# Store hooks to be run before or after the command body.
-
# @see {Pry::CommandSet#before_command}
-
# @see {Pry::CommandSet#after_command}
-
1
def hooks
-
@hooks ||= {:before => [], :after => []}
-
end
-
-
1
def command_regex
-
510
pr = Pry.respond_to?(:config) ? Pry.config.command_prefix : ""
-
510
prefix = convert_to_regex(pr)
-
510
prefix = "(?:#{prefix})?" unless options[:use_prefix]
-
-
510
/^#{prefix}#{convert_to_regex(match)}(?!\S)/
-
end
-
-
1
def convert_to_regex(obj)
-
1020
case obj
-
when String
-
973
Regexp.escape(obj)
-
else
-
47
obj
-
end
-
end
-
-
# The group in which the command should be displayed in "help" output.
-
# This is usually auto-generated from directory naming, but it can be
-
# manually overridden if necessary.
-
# Group should not be changed once it is initialized.
-
1
def group(name=nil)
-
@group ||= if name
-
60
name
-
else
-
case Pry::Method(block).source_file
-
when %r{/pry/.*_commands/(.*).rb}
-
$1.capitalize.gsub(/_/, " ")
-
when %r{(pry-\w+)-([\d\.]+([\w\.]+)?)}
-
name, version = $1, $2
-
"#{name.to_s} (v#{version.to_s})"
-
when /pryrc/
-
"~/.pryrc"
-
else
-
"(other)"
-
end
-
60
end
-
end
-
end
-
-
# Properties of one execution of a command (passed by {Pry#run_command} as a hash of
-
# context and expanded in `#initialize`
-
1
attr_accessor :output
-
1
attr_accessor :target
-
1
attr_accessor :captures
-
1
attr_accessor :eval_string
-
1
attr_accessor :arg_string
-
1
attr_accessor :context
-
1
attr_accessor :command_set
-
1
attr_accessor :_pry_
-
-
# The block we pass *into* a command so long as `:takes_block` is
-
# not equal to `false`
-
# @example
-
# my-command | do
-
# puts "block content"
-
# end
-
1
attr_accessor :command_block
-
-
# Run a command from another command.
-
# @param [String] command_string The string that invokes the command
-
# @param [Array] args Further arguments to pass to the command
-
# @example
-
# run "show-input"
-
# @example
-
# run ".ls"
-
# @example
-
# run "amend-line", "5", 'puts "hello world"'
-
1
def run(command_string, *args)
-
command_string = _pry_.config.command_prefix.to_s + command_string
-
complete_string = "#{command_string} #{args.join(" ")}".rstrip
-
command_set.process_line(complete_string, context)
-
end
-
-
1
def commands
-
command_set.to_hash
-
end
-
-
1
def text
-
Pry::Helpers::Text
-
end
-
-
1
def void
-
VOID_VALUE
-
end
-
-
1
include Pry::Helpers::BaseHelpers
-
1
include Pry::Helpers::CommandHelpers
-
-
# Instantiate a command, in preparation for calling it.
-
# @param [Hash] context The runtime context to use with this command.
-
1
def initialize(context={})
-
self.context = context
-
self.target = context[:target]
-
self.output = context[:output]
-
self.eval_string = context[:eval_string]
-
self.command_set = context[:command_set]
-
self._pry_ = context[:pry_instance]
-
end
-
-
# @return [Object] The value of `self` inside the `target` binding.
-
1
def target_self; target.eval('self'); end
-
-
# @return [Hash] Pry commands can store arbitrary state
-
# here. This state persists between subsequent command invocations.
-
# All state saved here is unique to the command, it does not
-
# need to be namespaced.
-
# @example
-
# state.my_state = "my state" # this will not conflict with any
-
# # `state.my_state` used in another command.
-
1
def state
-
_pry_.command_state[match] ||= Pry::Config.from_hash({})
-
end
-
-
# Revaluate the string (str) and perform interpolation.
-
# @param [String] str The string to reevaluate with interpolation.
-
#
-
# @return [String] The reevaluated string with interpolations
-
# applied (if any).
-
1
def interpolate_string(str)
-
dumped_str = str.dump
-
if dumped_str.gsub!(/\\\#\{/, '#{')
-
target.eval(dumped_str)
-
else
-
str
-
end
-
end
-
-
# Display a warning if a command collides with a local/method in
-
# the current scope.
-
1
def check_for_command_collision(command_match, arg_string)
-
collision_type = target.eval("defined?(#{command_match})")
-
collision_type ||= 'local-variable' if arg_string.match(%r{\A\s*[-+*/%&|^]*=})
-
-
if collision_type
-
output.puts "#{text.bold('WARNING:')} Calling Pry command '#{command_match}', which conflicts with a #{collision_type}.\n\n"
-
end
-
rescue Pry::RescuableException
-
end
-
-
# Extract necessary information from a line that Command.matches? this
-
# command.
-
#
-
# Returns an array of four elements:
-
#
-
# ```
-
# [String] the portion of the line that matched with the Command match
-
# [String] a string of all the arguments (i.e. everything but the match)
-
# [Array] the captures caught by the command_regex
-
# [Array] the arguments obtained by splitting the arg_string
-
# ```
-
#
-
# @param [String] val The line of input
-
# @return [Array]
-
1
def tokenize(val)
-
val.replace(interpolate_string(val)) if command_options[:interpolate]
-
-
self.class.command_regex =~ val
-
-
# please call Command.matches? before Command#call_safely
-
raise CommandError, "fatal: called a command which didn't match?!" unless Regexp.last_match
-
captures = Regexp.last_match.captures
-
pos = Regexp.last_match.end(0)
-
-
arg_string = val[pos..-1]
-
-
# remove the one leading space if it exists
-
arg_string.slice!(0) if arg_string.start_with?(" ")
-
-
# process and pass a block if one is found
-
pass_block(arg_string) if command_options[:takes_block]
-
-
if arg_string
-
args = command_options[:shellwords] ? Shellwords.shellwords(arg_string) : arg_string.split(" ")
-
else
-
args = []
-
end
-
-
[val[0..pos].rstrip, arg_string, captures, args]
-
end
-
-
# Process a line that Command.matches? this command.
-
# @param [String] line The line to process
-
# @return [Object, Command::VOID_VALUE]
-
1
def process_line(line)
-
command_match, arg_string, captures, args = tokenize(line)
-
-
check_for_command_collision(command_match, arg_string) if Pry.config.collision_warning
-
-
self.arg_string = arg_string
-
self.captures = captures
-
-
call_safely(*(captures + args))
-
end
-
-
# Pass a block argument to a command.
-
# @param [String] arg_string The arguments (as a string) passed to the command.
-
# We inspect these for a '| do' or a '| {' and if we find it we use it
-
# to start a block input sequence. Once we have a complete
-
# block, we save it to an accessor that can be retrieved from the command context.
-
# Note that if we find the '| do' or '| {' we delete this and the
-
# elements following it from `arg_string`.
-
1
def pass_block(arg_string)
-
# Workaround for weird JRuby bug where rindex in this case can return nil
-
# even when there's a match.
-
arg_string.scan(/\| *(?:do|\{)/)
-
block_index = $~ && $~.offset(0)[0]
-
-
return if !block_index
-
-
block_init_string = arg_string.slice!(block_index..-1)[1..-1]
-
prime_string = "proc #{block_init_string}\n"
-
-
if !Pry::Code.complete_expression?(prime_string)
-
block_string = _pry_.r(target, prime_string)
-
else
-
block_string = prime_string
-
end
-
-
begin
-
self.command_block = target.eval(block_string)
-
rescue Pry::RescuableException
-
raise CommandError, "Incomplete block definition."
-
end
-
end
-
-
1
private :pass_block
-
-
# Run the command with the given `args`.
-
#
-
# This is a public wrapper around `#call` which ensures all preconditions
-
# are met.
-
#
-
# @param [Array<String>] args The arguments to pass to this command.
-
# @return [Object] The return value of the `#call` method, or
-
# {Command::VOID_VALUE}.
-
1
def call_safely(*args)
-
unless dependencies_met?
-
gems_needed = Array(command_options[:requires_gem])
-
gems_not_installed = gems_needed.select { |g| !Rubygem.installed?(g) }
-
output.puts "\nThe command '#{command_name}' is #{text.bold("unavailable")} because it requires the following gems to be installed: #{(gems_not_installed.join(", "))}"
-
output.puts "-"
-
output.puts "Type `install-command #{command_name}` to install the required gems and activate this command."
-
return void
-
end
-
-
if command_options[:argument_required] && args.empty?
-
raise CommandError, "The command '#{command_name}' requires an argument."
-
end
-
-
ret = call_with_hooks(*args)
-
command_options[:keep_retval] ? ret : void
-
end
-
-
# Are all the gems required to use this command installed?
-
#
-
# @return Boolean
-
1
def dependencies_met?
-
@dependencies_met ||= command_dependencies_met?(command_options)
-
end
-
-
# Generate completions for this command
-
#
-
# @param [String] search The line typed so far
-
# @return [Array<String>] Completion words
-
1
def complete(search)
-
[]
-
end
-
-
1
private
-
-
# Run the `#call` method and all the registered hooks.
-
# @param [Array<String>] args The arguments to `#call`
-
# @return [Object] The return value from `#call`
-
1
def call_with_hooks(*args)
-
self.class.hooks[:before].each do |block|
-
instance_exec(*args, &block)
-
end
-
-
ret = call(*args)
-
-
self.class.hooks[:after].each do |block|
-
ret = instance_exec(*args, &block)
-
end
-
-
ret
-
end
-
-
# Fix the number of arguments we pass to a block to avoid arity warnings.
-
# @param [Fixnum] arity The arity of the block
-
# @param [Array] args The arguments to pass
-
# @return [Array] A (possibly shorter) array of the arguments to pass
-
1
def correct_arg_arity(arity, args)
-
case
-
when arity < 0
-
args
-
when arity == 0
-
[]
-
when arity > 0
-
args.values_at(*(0..(arity - 1)).to_a)
-
end
-
end
-
end
-
-
# A super-class for Commands that are created with a single block.
-
#
-
# This class ensures that the block is called with the correct number of arguments
-
# and the right context.
-
#
-
# Create subclasses using {Pry::CommandSet#command}.
-
1
class BlockCommand < Command
-
# backwards compatibility
-
1
alias_method :opts, :context
-
-
# Call the block that was registered with this command.
-
# @param [Array<String>] args The arguments passed
-
# @return [Object] The return value of the block
-
1
def call(*args)
-
instance_exec(*correct_arg_arity(block.arity, args), &block)
-
end
-
-
1
def help
-
"#{command_options[:listing].to_s.ljust(18)} #{description}"
-
end
-
end
-
-
# A super-class of Commands with structure.
-
#
-
# This class implements the bare-minimum functionality that a command should
-
# have, namely a --help switch, and then delegates actual processing to its
-
# subclasses.
-
#
-
# Create subclasses using {Pry::CommandSet#create_command}, and override the
-
# `options(opt)` method to set up an instance of Slop, and the `process`
-
# method to actually run the command. If necessary, you can also override
-
# `setup` which will be called before `options`, for example to require any
-
# gems your command needs to run, or to set up state.
-
1
class ClassCommand < Command
-
1
class << self
-
-
# Ensure that subclasses inherit the options, description and
-
# match from a ClassCommand super class.
-
1
def inherited(klass)
-
51
klass.match match
-
51
klass.description description
-
51
klass.command_options options
-
end
-
-
1
def source
-
source_object.source
-
end
-
-
1
def doc
-
new.help
-
end
-
-
1
def source_location
-
source_object.source_location
-
end
-
-
1
def source_file
-
source_object.source_file
-
end
-
1
alias_method :file, :source_file
-
-
1
def source_line
-
source_object.source_line
-
end
-
1
alias_method :line, :source_line
-
-
1
private
-
-
# The object used to extract the source for the command.
-
#
-
# This should be a `Pry::Method(block)` for a command made with `create_command`
-
# and a `Pry::WrappedModule(self)` for a command that's a standard class.
-
# @return [Pry::WrappedModule, Pry::Method]
-
1
def source_object
-
@source_object ||= if name =~ /^[A-Z]/
-
Pry::WrappedModule(self)
-
else
-
Pry::Method(block)
-
end
-
end
-
end
-
-
1
attr_accessor :opts
-
1
attr_accessor :args
-
-
# Set up `opts` and `args`, and then call `process`.
-
#
-
# This method will display help if necessary.
-
#
-
# @param [Array<String>] args The arguments passed
-
# @return [Object] The return value of `process` or VOID_VALUE
-
1
def call(*args)
-
setup
-
-
self.opts = slop
-
self.args = self.opts.parse!(args)
-
-
if opts.present?(:help)
-
output.puts slop.help
-
void
-
else
-
process(*correct_arg_arity(method(:process).arity, args))
-
end
-
end
-
-
# Return the help generated by Slop for this command.
-
1
def help
-
slop.help
-
end
-
-
# Return an instance of Slop that can parse either subcommands or the
-
# options that this command accepts.
-
1
def slop
-
Slop.new do |opt|
-
opt.banner(unindent(self.class.banner))
-
subcommands(opt)
-
options(opt)
-
opt.on :h, :help, 'Show this message.'
-
end
-
end
-
-
# Generate shell completions
-
# @param [String] search The line typed so far
-
# @return [Array<String>] the words to complete
-
1
def complete(search)
-
slop.map do |opt|
-
[opt.long && "--#{opt.long} " || opt.short && "-#{opt.short}"]
-
end.flatten(1).compact + super
-
end
-
-
# A method called just before `options(opt)` as part of `call`.
-
#
-
# This method can be used to set up any context your command needs to run,
-
# for example requiring gems, or setting default values for options.
-
#
-
# @example
-
# def setup
-
# require 'gist'
-
# @action = :method
-
# end
-
1
def setup; end
-
-
# A method to setup Slop commands so it can parse the subcommands your
-
# command expects. If you need to set up default values, use `setup`
-
# instead.
-
#
-
# @example A minimal example
-
# def subcommands(cmd)
-
# cmd.command :download do |opt|
-
# description 'Downloads a content from a server'
-
#
-
# opt.on :verbose, 'Use verbose output'
-
#
-
# run do |options, arguments|
-
# ContentDownloader.download(options, arguments)
-
# end
-
# end
-
# end
-
#
-
# @example Define the invokation block anywhere you want
-
# def subcommands(cmd)
-
# cmd.command :download do |opt|
-
# description 'Downloads a content from a server'
-
#
-
# opt.on :verbose, 'Use verbose output'
-
# end
-
# end
-
#
-
# def process
-
# # Perform calculations...
-
# opts.fetch_command(:download).run do |options, arguments|
-
# ContentDownloader.download(options, arguments)
-
# end
-
# # More calculations...
-
# end
-
1
def subcommands(cmd); end
-
-
# A method to setup Slop so it can parse the options your command expects.
-
#
-
# @note Please don't do anything side-effecty in the main part of this
-
# method, as it may be called by Pry at any time for introspection reasons.
-
# If you need to set up default values, use `setup` instead.
-
#
-
# @example
-
# def options(opt)
-
# opt.banner "Gists methods or classes"
-
# opt.on(:c, :class, "gist a class") do
-
# @action = :class
-
# end
-
# end
-
1
def options(opt); end
-
-
# The actual body of your command should go here.
-
#
-
# The `opts` mehod can be called to get the options that Slop has passed,
-
# and `args` gives the remaining, unparsed arguments.
-
#
-
# The return value of this method is discarded unless the command was
-
# created with `:keep_retval => true`, in which case it is returned to the
-
# repl.
-
#
-
# @example
-
# def process
-
# if opts.present?(:class)
-
# gist_class
-
# else
-
# gist_method
-
# end
-
# end
-
1
def process; raise CommandError, "command '#{command_name}' not implemented" end
-
end
-
end
-
1
class Pry
-
1
class NoCommandError < StandardError
-
1
def initialize(match, owner)
-
super "Command '#{match}' not found in command set #{owner}"
-
end
-
end
-
-
# This class is used to create sets of commands. Commands can be imported from
-
# different sets, aliased, removed, etc.
-
1
class CommandSet
-
1
include Enumerable
-
1
include Pry::Helpers::BaseHelpers
-
1
attr_reader :helper_module
-
-
# @param [Array<Commandset>] imported_sets
-
# Sets which will be imported automatically
-
# @yield Optional block run to define commands
-
1
def initialize(*imported_sets, &block)
-
1
@commands = {}
-
1
@helper_module = Module.new
-
1
import(*imported_sets)
-
1
instance_eval(&block) if block
-
end
-
-
# Defines a new Pry command.
-
# @param [String, Regexp] match The start of invocations of this command.
-
# @param [String] description A description of the command.
-
# @param [Hash] options The optional configuration parameters.
-
# @option options [Boolean] :keep_retval Whether or not to use return value
-
# of the block for return of `command` or just to return `nil`
-
# (the default).
-
# @option options [Array<String>] :requires_gem Whether the command has
-
# any gem dependencies, if it does and dependencies not met then
-
# command is disabled and a stub proc giving instructions to
-
# install command is provided.
-
# @option options [Boolean] :interpolate Whether string #{} based
-
# interpolation is applied to the command arguments before
-
# executing the command. Defaults to true.
-
# @option options [String] :listing The listing name of the
-
# command. That is the name by which the command is looked up by
-
# help and by show-command. Necessary for commands with regex matches.
-
# @option options [Boolean] :use_prefix Whether the command uses
-
# `Pry.config.command_prefix` prefix (if one is defined). Defaults
-
# to true.
-
# @option options [Boolean] :shellwords Whether the command's arguments
-
# should be split using Shellwords instead of just split on spaces.
-
# Defaults to true.
-
# @yield The action to perform. The parameters in the block
-
# determines the parameters the command will receive. All
-
# parameters passed into the block will be strings. Successive
-
# command parameters are separated by whitespace at the Pry prompt.
-
# @example
-
# MyCommands = Pry::CommandSet.new do
-
# command "greet", "Greet somebody" do |name|
-
# puts "Good afternoon #{name.capitalize}!"
-
# end
-
# end
-
#
-
# # From pry:
-
# # pry(main)> _pry_.commands = MyCommands
-
# # pry(main)> greet john
-
# # Good afternoon John!
-
# # pry(main)> help greet
-
# # Greet somebody
-
# @example Regexp command
-
# MyCommands = Pry::CommandSet.new do
-
# command /number-(\d+)/, "number-N regex command", :listing => "number" do |num, name|
-
# puts "hello #{name}, nice number: #{num}"
-
# end
-
# end
-
#
-
# # From pry:
-
# # pry(main)> _pry_.commands = MyCommands
-
# # pry(main)> number-10 john
-
# # hello john, nice number: 10
-
# # pry(main)> help number
-
# # number-N regex command
-
1
def block_command(match, description="No description.", options={}, &block)
-
19
description, options = ["No description.", description] if description.is_a?(Hash)
-
19
options = Pry::Command.default_options(match).merge!(options)
-
-
19
@commands[match] = Pry::BlockCommand.subclass(match, description, options, helper_module, &block)
-
end
-
1
alias_method :command, :block_command
-
-
# Defines a new Pry command class.
-
#
-
# @param [String, Regexp] match The start of invocations of this command.
-
# @param [String] description A description of the command.
-
# @param [Hash] options The optional configuration parameters, see {#command}
-
# @yield The class body's definition.
-
#
-
# @example
-
# Pry::Commands.create_command "echo", "echo's the input", :shellwords => false do
-
# def options(opt)
-
# opt.banner "Usage: echo [-u | -d] <string to echo>"
-
# opt.on :u, :upcase, "ensure the output is all upper-case"
-
# opt.on :d, :downcase, "ensure the output is all lower-case"
-
# end
-
#
-
# def process
-
# raise Pry::CommandError, "-u and -d makes no sense" if opts.present?(:u) && opts.present?(:d)
-
# result = args.join(" ")
-
# result.downcase! if opts.present?(:downcase)
-
# result.upcase! if opts.present?(:upcase)
-
# output.puts result
-
# end
-
# end
-
#
-
1
def create_command(match, description="No description.", options={}, &block)
-
2
description, options = ["No description.", description] if description.is_a?(Hash)
-
2
options = Pry::Command.default_options(match).merge!(options)
-
-
2
@commands[match] = Pry::ClassCommand.subclass(match, description, options, helper_module, &block)
-
2
@commands[match].class_eval(&block)
-
2
@commands[match]
-
end
-
-
# Execute a block of code before a command is invoked. The block also
-
# gets access to parameters that will be passed to the command and
-
# is evaluated in the same context.
-
# @param [String, Regexp] search The match or listing of the command.
-
# @yield The block to be run before the command.
-
# @example Display parameter before invoking command
-
# Pry.config.commands.before_command("whereami") do |n|
-
# output.puts "parameter passed was #{n}"
-
# end
-
1
def before_command(search, &block)
-
cmd = find_command_by_match_or_listing(search)
-
cmd.hooks[:before].unshift block
-
end
-
-
# Execute a block of code after a command is invoked. The block also
-
# gets access to parameters that will be passed to the command and
-
# is evaluated in the same context.
-
# @param [String, Regexp] search The match or listing of the command.
-
# @yield The block to be run after the command.
-
# @example Display text 'command complete' after invoking command
-
# Pry.config.commands.after_command("whereami") do |n|
-
# output.puts "command complete!"
-
# end
-
1
def after_command(search, &block)
-
cmd = find_command_by_match_or_listing(search)
-
cmd.hooks[:after] << block
-
end
-
-
1
def each(&block)
-
@commands.each(&block)
-
end
-
-
# Removes some commands from the set
-
# @param [Array<String>] searches the matches or listings of the commands to remove
-
1
def delete(*searches)
-
searches.each do |search|
-
cmd = find_command_by_match_or_listing(search)
-
@commands.delete cmd.match
-
end
-
end
-
-
# Imports all the commands from one or more sets.
-
# @param [Array<CommandSet>] sets Command sets, all of the commands of which
-
# will be imported.
-
# @return [Pry::CommandSet] Returns the reciever (a command set).
-
1
def import(*sets)
-
1
sets.each do |set|
-
@commands.merge! set.to_hash
-
helper_module.send :include, set.helper_module
-
end
-
1
self
-
end
-
-
# Imports some commands from a set
-
# @param [CommandSet] set Set to import commands from
-
# @param [Array<String>] matches Commands to import
-
# @return [Pry::CommandSet] Returns the reciever (a command set).
-
1
def import_from(set, *matches)
-
helper_module.send :include, set.helper_module
-
matches.each do |match|
-
cmd = set.find_command_by_match_or_listing(match)
-
@commands[cmd.match] = cmd
-
end
-
self
-
end
-
-
# @param [String, Regexp] match_or_listing The match or listing of a command.
-
# of the command to retrieve.
-
# @return [Command] The command object matched.
-
1
def find_command_by_match_or_listing(match_or_listing)
-
cmd = (@commands[match_or_listing] ||
-
Pry::Helpers::BaseHelpers.find_command(match_or_listing, @commands))
-
cmd or raise ArgumentError, "Cannot find a command: '#{match_or_listing}'!"
-
end
-
-
# Aliases a command
-
# @param [String, Regex] match The match of the alias (can be a regex).
-
# @param [String] action The action to be performed (typically
-
# another command).
-
# @param [Hash] options The optional configuration parameters,
-
# accepts the same as the `command` method, but also allows the
-
# command description to be passed this way too as `:desc`
-
# @example Creating an alias for `ls -M`
-
# Pry.config.commands.alias_command "lM", "ls -M"
-
# @example Pass explicit description (overriding default).
-
# Pry.config.commands.alias_command "lM", "ls -M", :desc => "cutiepie"
-
1
def alias_command(match, action, options={})
-
12
cmd = find_command(action) or fail "Command: `#{action}` not found"
-
12
original_options = cmd.options.dup
-
-
12
options = original_options.merge!({
-
:desc => "Alias for `#{action}`",
-
:listing => match
-
}).merge!(options)
-
-
# ensure default description is used if desc is nil
-
12
desc = options.delete(:desc).to_s
-
-
12
c = block_command match, desc, options do |*args|
-
run action, *args
-
end
-
-
12
c.class_eval do
-
12
define_method(:complete) do |input|
-
cmd.new(context).complete(input)
-
end
-
end
-
-
12
c.group "Aliases"
-
-
12
c
-
end
-
-
# Rename a command. Accepts either match or listing for the search.
-
#
-
# @param [String, Regexp] new_match The new match for the command.
-
# @param [String, Regexp] search The command's current match or listing.
-
# @param [Hash] options The optional configuration parameters,
-
# accepts the same as the `command` method, but also allows the
-
# command description to be passed this way too.
-
# @example Renaming the `ls` command and changing its description.
-
# Pry.config.commands.rename "dir", "ls", :description => "DOS friendly ls"
-
1
def rename_command(new_match, search, options={})
-
cmd = find_command_by_match_or_listing(search)
-
-
options = {
-
:listing => new_match,
-
:description => cmd.description
-
}.merge!(options)
-
-
@commands[new_match] = cmd.dup
-
@commands[new_match].match = new_match
-
@commands[new_match].description = options.delete(:description)
-
@commands[new_match].options.merge!(options)
-
@commands.delete(cmd.match)
-
end
-
-
1
def disabled_command(name_of_disabled_command, message, matcher=name_of_disabled_command)
-
2
create_command name_of_disabled_command do
-
2
match matcher
-
2
description ""
-
-
2
define_method(:process) do
-
output.puts "DISABLED: #{message}"
-
end
-
end
-
end
-
-
# Sets or gets the description for a command (replacing the old
-
# description). Returns current description if no description
-
# parameter provided.
-
# @param [String, Regexp] search The command match.
-
# @param [String?] description (nil) The command description.
-
# @example Setting
-
# MyCommands = Pry::CommandSet.new do
-
# desc "help", "help description"
-
# end
-
# @example Getting
-
# Pry.config.commands.desc "amend-line"
-
1
def desc(search, description=nil)
-
cmd = find_command_by_match_or_listing(search)
-
return cmd.description if !description
-
-
cmd.description = description
-
end
-
-
# Defines helpers methods for this command sets.
-
# Those helpers are only defined in this command set.
-
#
-
# @yield A block defining helper methods
-
# @example
-
# helpers do
-
# def hello
-
# puts "Hello!"
-
# end
-
#
-
# include OtherModule
-
# end
-
1
def helpers(&block)
-
helper_module.class_eval(&block)
-
end
-
-
-
# @return [Array]
-
# The list of commands provided by the command set.
-
1
def list_commands
-
@commands.keys
-
end
-
1
alias_method :keys, :list_commands
-
-
1
def to_hash
-
@commands.dup
-
end
-
1
alias_method :to_h, :to_hash
-
-
# Find a command that matches the given line
-
# @param [String] pattern The line that might be a command invocation
-
# @return [Pry::Command, nil]
-
1
def [](pattern)
-
@commands.values.select do |command|
-
498
command.matches?(pattern)
-
end.sort_by do |command|
-
12
command.match_score(pattern)
-
12
end.last
-
end
-
1
alias_method :find_command, :[]
-
-
#
-
# Re-assign the command found at _pattern_ with _command_.
-
#
-
# @param [Regexp, String] pattern
-
# The command to add or replace(found at _pattern_).
-
#
-
# @param [Pry::Command] command
-
# The command to add.
-
#
-
# @return [Pry::Command]
-
# Returns the new command (matched with "pattern".)
-
#
-
# @example
-
# Pry.config.commands["help"] = MyHelpCommand
-
#
-
1
def []=(pattern, command)
-
48
if command.equal?(nil)
-
return @commands.delete(pattern)
-
end
-
48
unless Class === command && command < Pry::Command
-
raise TypeError, "command is not a subclass of Pry::Command"
-
end
-
48
bind_command_to_pattern = pattern != command.match
-
48
if bind_command_to_pattern
-
command_copy = command.dup
-
command_copy.match = pattern
-
@commands[pattern] = command_copy
-
else
-
48
@commands[pattern] = command
-
end
-
end
-
-
#
-
# Add a command to set.
-
#
-
# @param [Command] command
-
# a subclass of Pry::Command.
-
#
-
1
def add_command(command)
-
48
self[command.match] = command
-
end
-
-
# Find the command that the user might be trying to refer to.
-
# @param [String] search The user's search.
-
# @return [Pry::Command?]
-
1
def find_command_for_help(search)
-
find_command(search) || (begin
-
find_command_by_match_or_listing(search)
-
rescue ArgumentError
-
nil
-
end)
-
end
-
-
# Is the given line a command invocation?
-
# @param [String] val
-
# @return [Boolean]
-
1
def valid_command?(val)
-
!!find_command(val)
-
end
-
-
# Process the given line to see whether it needs executing as a command.
-
# @param [String] val The line to execute
-
# @param [Hash] context The context to execute the commands with
-
# @return [CommandSet::Result]
-
1
def process_line(val, context={})
-
if command = find_command(val)
-
context = context.merge(:command_set => self)
-
retval = command.new(context).process_line(val)
-
Result.new(true, retval)
-
else
-
Result.new(false)
-
end
-
end
-
-
# @private (used for testing)
-
1
def run_command(context, match, *args)
-
command = @commands[match] or raise NoCommandError.new(match, self)
-
command.new(context).call_safely(*args)
-
end
-
-
# Generate completions for the user's search.
-
# @param [String] search The line to search for
-
# @param [Hash] context The context to create the command with
-
# @return [Array<String>]
-
1
def complete(search, context={})
-
if command = find_command(search)
-
command.new(context).complete(search)
-
else
-
@commands.keys.select do |key|
-
String === key && key.start_with?(search)
-
end.map{ |key| key + " " }
-
end
-
end
-
end
-
-
# Wraps the return result of process_commands, indicates if the
-
# result IS a command and what kind of command (e.g void)
-
1
class Result
-
1
attr_reader :retval
-
-
1
def initialize(is_command, retval = nil)
-
@is_command, @retval = is_command, retval
-
end
-
-
# Is the result a command?
-
# @return [Boolean]
-
1
def command?
-
@is_command
-
end
-
-
# Is the result a command and if it is, is it a void command?
-
# (one that does not return a value)
-
# @return [Boolean]
-
1
def void_command?
-
retval == Command::VOID_VALUE
-
end
-
end
-
end
-
# Default commands used by Pry.
-
1
Pry::Commands = Pry::CommandSet.new
-
-
1
Dir[File.expand_path('../commands', __FILE__) << '/*.rb'].each do |file|
-
52
require file
-
end
-
1
class Pry
-
1
class Command::AmendLine < Pry::ClassCommand
-
1
match(/amend-line(?: (-?\d+)(?:\.\.(-?\d+))?)?/)
-
1
group 'Editing'
-
1
description 'Amend a line of input in multi-line mode.'
-
1
command_options :interpolate => false, :listing => 'amend-line'
-
-
1
banner <<-'BANNER'
-
Amend a line of input in multi-line mode. `amend-line N`, where the N represents
-
line to replace. Can also specify a range of lines using `amend-line N..M`
-
syntax. Passing "!" as replacement content deletes the line(s) instead.
-
-
amend-line 1 puts 'new' # replace line 1
-
amend-line 1..4 ! # delete lines 1..4
-
amend-line 3 >puts 'bye' # insert before line 3
-
amend-line puts 'appended' # no line number modifies immediately preceding line
-
BANNER
-
-
1
def process
-
raise CommandError, "No input to amend." if eval_string.empty?
-
-
eval_string.replace amended_input(eval_string)
-
run "fix-indent"
-
run "show-input"
-
end
-
-
1
private
-
-
# @param [String] string The string to amend.
-
# @return [String] A new string with the amendments applied to it.
-
1
def amended_input(string)
-
input_array = eval_string.each_line.to_a
-
-
if arg_string == "!"
-
delete_from_array(input_array, line_range)
-
elsif arg_string.start_with?(">")
-
insert_into_array(input_array, line_range)
-
else
-
replace_in_array(input_array, line_range)
-
end
-
-
input_array.join
-
end
-
-
1
def delete_from_array(array, range)
-
array.slice!(range)
-
end
-
-
1
def insert_into_array(array, range)
-
insert_slot = Array(range).first
-
array.insert(insert_slot, arg_string[1..-1] << "\n")
-
end
-
-
1
def replace_in_array(array, range)
-
array[range] = arg_string + "\n"
-
end
-
-
# @return [Fixnum] The number of lines currently in `eval_string` (the input buffer).
-
1
def line_count
-
eval_string.lines.count
-
end
-
-
# Returns the (one-indexed) start and end lines given by the user.
-
# The lines in this range will be affected by the `amend-line`.
-
# Returns `nil` if no lines were specified by the user.
-
# @return [Array<Fixnum>, nil]
-
1
def start_and_end_line_number
-
start_line_number, end_line_number = args
-
end_line_number ||= start_line_number.to_i
-
-
[start_line_number.to_i, end_line_number.to_i] if start_line_number
-
end
-
-
# Takes two numbers that are 1-indexed, and returns a range (or
-
# number) that is 0-indexed. 1-indexed means the first element is
-
# indentified by 1 rather than by 0 (as is the case for Ruby arrays).
-
# @param [Fixnum] start_line_number One-indexed number.
-
# @param [Fixnum] end_line_number One-indexed number.
-
# @return [Range] The zero-indexed range.
-
1
def zero_indexed_range_from_one_indexed_numbers(start_line_number, end_line_number)
-
# FIXME: one_index_number is a horrible name for this method
-
one_index_number(start_line_number)..one_index_number(end_line_number)
-
end
-
-
# The lines (or line) that will be modified by the `amend-line`.
-
# @return [Range, Fixnum] The lines or line.
-
1
def line_range
-
start_line_number, end_line_number = start_and_end_line_number
-
if start_line_number
-
zero_indexed_range_from_one_indexed_numbers(start_line_number,
-
end_line_number)
-
else
-
line_count - 1
-
end
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::AmendLine)
-
end
-
1
class Pry
-
1
class Command::Bang < Pry::ClassCommand
-
1
match(/^\s*!\s*$/)
-
1
group 'Editing'
-
1
description 'Clear the input buffer.'
-
1
command_options :use_prefix => false
-
-
1
banner <<-'BANNER'
-
Clear the input buffer. Useful if the parsing process goes wrong and you get
-
stuck in the read loop.
-
BANNER
-
-
1
def process
-
output.puts 'Input buffer cleared!'
-
eval_string.replace('')
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::Bang)
-
end
-
1
class Pry
-
1
class Command::BangPry < Pry::ClassCommand
-
1
match '!pry'
-
1
group 'Navigating Pry'
-
1
description 'Start a Pry session on current self.'
-
-
1
banner <<-'BANNER'
-
Start a Pry session on current self. Also works mid multi-line expression.
-
BANNER
-
-
1
def process
-
target.pry
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::BangPry)
-
end
-
1
class Pry
-
1
class Command::Cat < Pry::ClassCommand
-
1
require 'pry/commands/cat/abstract_formatter.rb'
-
1
require 'pry/commands/cat/input_expression_formatter.rb'
-
1
require 'pry/commands/cat/exception_formatter.rb'
-
1
require 'pry/commands/cat/file_formatter.rb'
-
-
1
match 'cat'
-
1
group 'Input and Output'
-
1
description "Show code from a file, Pry's input buffer, or the last exception."
-
-
1
banner <<-'BANNER'
-
Usage: cat FILE
-
cat --ex [STACK_INDEX]
-
cat --in [INPUT_INDEX_OR_RANGE]
-
-
`cat` is capable of showing part or all of a source file, the context of the
-
last exception, or an expression from Pry's input history.
-
-
`cat --ex` defaults to showing the lines surrounding the location of the last
-
exception. Invoking it more than once travels up the exception's backtrace, and
-
providing a number shows the context of the given index of the backtrace.
-
BANNER
-
-
1
def options(opt)
-
opt.on :ex, "Show the context of the last exception", :optional_argument => true, :as => Integer
-
opt.on :i, :in, "Show one or more entries from Pry's expression history", :optional_argument => true, :as => Range, :default => -5..-1
-
opt.on :s, :start, "Starting line (defaults to the first line)", :optional_argument => true, :as => Integer
-
opt.on :e, :end, "Ending line (defaults to the last line)", :optional_argument => true, :as => Integer
-
opt.on :l, :'line-numbers', "Show line numbers"
-
opt.on :t, :type, "The file type for syntax highlighting (e.g., 'ruby' or 'python')", :argument => true, :as => Symbol
-
end
-
-
1
def process
-
output = case
-
when opts.present?(:ex)
-
ExceptionFormatter.new(_pry_.last_exception, _pry_, opts).format
-
when opts.present?(:in)
-
InputExpressionFormatter.new(_pry_.input_array, opts).format
-
else
-
FileFormatter.new(args.first, _pry_, opts).format
-
end
-
-
_pry_.pager.page output
-
end
-
-
1
def complete(search)
-
super | load_path_completions
-
end
-
-
1
def load_path_completions
-
$LOAD_PATH.flat_map do |path|
-
Dir[path + '/**/*'].map { |f|
-
next if File.directory?(f)
-
f.sub!(path + '/', '')
-
}
-
end
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::Cat)
-
end
-
1
class Pry
-
1
class Command::Cat
-
1
class AbstractFormatter
-
1
include Pry::Helpers::CommandHelpers
-
1
include Pry::Helpers::BaseHelpers
-
-
1
private
-
1
def decorate(content)
-
content.code_type = code_type
-
content.between(*between_lines).
-
with_line_numbers(use_line_numbers?).highlighted
-
end
-
-
1
def code_type
-
opts[:type] || :ruby
-
end
-
-
1
def use_line_numbers?
-
opts.present?(:'line-numbers') || opts.present?(:ex)
-
end
-
-
1
def between_lines
-
[opts[:start] || 1, opts[:end] || -1]
-
end
-
end
-
end
-
end
-
1
class Pry
-
1
class Command::Cat
-
1
class ExceptionFormatter < AbstractFormatter
-
1
attr_reader :ex
-
1
attr_reader :opts
-
1
attr_reader :_pry_
-
-
1
def initialize(exception, _pry_, opts)
-
@ex = exception
-
@opts = opts
-
@_pry_ = _pry_
-
end
-
-
1
def format
-
check_for_errors
-
set_file_and_dir_locals(backtrace_file, _pry_, _pry_.current_context)
-
code = decorate(Pry::Code.from_file(backtrace_file).
-
between(*start_and_end_line_for_code_window).
-
with_marker(backtrace_line))
-
"#{header}#{code}"
-
end
-
-
1
private
-
-
1
def code_window_size
-
_pry_.config.default_window_size || 5
-
end
-
-
1
def backtrace_level
-
return @backtrace_level if @backtrace_level
-
-
bl = if opts[:ex].nil?
-
ex.bt_index
-
else
-
ex.bt_index = absolute_index_number(opts[:ex], ex.backtrace.size)
-
end
-
-
increment_backtrace_level
-
@backtrace_level = bl
-
end
-
-
1
def increment_backtrace_level
-
ex.inc_bt_index
-
end
-
-
1
def backtrace_file
-
Array(ex.bt_source_location_for(backtrace_level)).first
-
end
-
-
1
def backtrace_line
-
Array(ex.bt_source_location_for(backtrace_level)).last
-
end
-
-
1
def check_for_errors
-
raise CommandError, "No exception found." unless ex
-
raise CommandError, "The given backtrace level is out of bounds." unless backtrace_file
-
end
-
-
1
def start_and_end_line_for_code_window
-
start_line = backtrace_line - code_window_size
-
start_line = 1 if start_line < 1
-
-
[start_line, backtrace_line + code_window_size]
-
end
-
-
1
def header
-
unindent %{
-
#{Helpers::Text.bold 'Exception:'} #{ex.class}: #{ex.message}
-
--
-
#{Helpers::Text.bold('From:')} #{backtrace_file} @ line #{backtrace_line} @ #{Helpers::Text.bold("level: #{backtrace_level}")} of backtrace (of #{ex.backtrace.size - 1}).
-
-
}
-
end
-
-
end
-
end
-
end
-
1
class Pry
-
1
class Command::Cat
-
1
class FileFormatter < AbstractFormatter
-
1
attr_reader :file_with_embedded_line
-
1
attr_reader :opts
-
1
attr_reader :_pry_
-
-
1
def initialize(file_with_embedded_line, _pry_, opts)
-
@file_with_embedded_line = file_with_embedded_line
-
@opts = opts
-
@_pry_ = _pry_
-
@code_from_file = Pry::Code.from_file(file_name)
-
end
-
-
1
def format
-
raise CommandError, "Must provide a filename, --in, or --ex." if !file_with_embedded_line
-
-
set_file_and_dir_locals(file_name, _pry_, _pry_.current_context)
-
decorate(@code_from_file)
-
end
-
-
1
def file_and_line
-
file_name, line_num = file_with_embedded_line.split(/:(?!\/|\\)/)
-
-
[file_name, line_num ? line_num.to_i : nil]
-
end
-
-
1
private
-
-
1
def file_name
-
file_and_line.first
-
end
-
-
1
def line_number
-
file_and_line.last
-
end
-
-
1
def code_window_size
-
_pry_.config.default_window_size || 7
-
end
-
-
1
def decorate(content)
-
line_number ? super.around(line_number, code_window_size) : super
-
end
-
-
1
def code_type
-
opts[:type] || detect_code_type_from_file(file_name)
-
end
-
-
1
def detect_code_type_from_file(file_name)
-
code_type = @code_from_file.code_type
-
-
if code_type == :unknown
-
name = File.basename(file_name).split('.', 2).first
-
case name
-
when "Rakefile", "Gemfile"
-
:ruby
-
else
-
:text
-
end
-
else
-
code_type
-
end
-
end
-
end
-
end
-
end
-
1
class Pry
-
1
class Command::Cat
-
1
class InputExpressionFormatter < AbstractFormatter
-
1
attr_accessor :input_expressions
-
1
attr_accessor :opts
-
-
1
def initialize(input_expressions, opts)
-
@input_expressions = input_expressions
-
@opts = opts
-
end
-
-
1
def format
-
raise CommandError, "No input expressions!" if numbered_input_items.length < 1
-
-
if numbered_input_items.length > 1
-
content = ""
-
numbered_input_items.each do |i, s|
-
content << "#{Helpers::Text.bold(i.to_s)}:\n" << decorate(Pry::Code(s).with_indentation(2)).to_s
-
end
-
-
content
-
else
-
decorate(Pry::Code(selected_input_items.first))
-
end
-
end
-
-
1
private
-
-
1
def selected_input_items
-
input_expressions[normalized_expression_range] || []
-
end
-
-
1
def numbered_input_items
-
@numbered_input_items ||= normalized_expression_range.zip(selected_input_items).
-
reject { |_, s| s.nil? || s == "" }
-
end
-
-
1
def normalized_expression_range
-
absolute_index_range(opts[:i], input_expressions.length)
-
end
-
end
-
end
-
end
-
1
class Pry
-
1
class Command::Cd < Pry::ClassCommand
-
1
match 'cd'
-
1
group 'Context'
-
1
description 'Move into a new context (object or scope).'
-
-
1
banner <<-'BANNER'
-
Usage: cd [OPTIONS] [--help]
-
-
Move into new context (object or scope). As in UNIX shells use `cd ..` to go
-
back, `cd /` to return to Pry top-level and `cd -` to toggle between last two
-
scopes. Complex syntax (e.g `cd ../@x/@y`) also supported.
-
-
cd @x
-
cd ..
-
cd /
-
cd -
-
-
https://github.com/pry/pry/wiki/State-navigation#wiki-Changing_scope
-
BANNER
-
-
1
def process
-
state.old_stack ||= []
-
-
if arg_string.strip == "-"
-
unless state.old_stack.empty?
-
_pry_.binding_stack, state.old_stack = state.old_stack, _pry_.binding_stack
-
end
-
else
-
stack = ObjectPath.new(arg_string, _pry_.binding_stack).resolve
-
-
if stack && stack != _pry_.binding_stack
-
state.old_stack = _pry_.binding_stack
-
_pry_.binding_stack = stack
-
end
-
end
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::Cd)
-
end
-
1
class Pry::Command::ChangeInspector < Pry::ClassCommand
-
1
match 'change-inspector'
-
1
group 'Input and Output'
-
1
description 'Change the current inspector proc.'
-
1
command_options argument_required: true
-
1
banner <<-BANNER
-
Usage: change-inspector NAME
-
-
Change the proc used to print return values. See list-inspectors for a list
-
of available procs and a short description of what each one does.
-
BANNER
-
-
1
def process(inspector)
-
if inspector_map.key?(inspector)
-
_pry_.print = inspector_map[inspector][:value]
-
output.puts "Switched to the '#{inspector}' inspector!"
-
else
-
raise Pry::CommandError, "'#{inspector}' isn't a known inspector!"
-
end
-
end
-
-
1
private
-
1
def inspector_map
-
Pry::Inspector::MAP
-
end
-
1
Pry::Commands.add_command(self)
-
end
-
1
class Pry::Command::ChangePrompt < Pry::ClassCommand
-
1
match 'change-prompt'
-
1
group 'Input and Output'
-
1
description 'Change the current prompt.'
-
1
command_options argument_required: true
-
1
banner <<-BANNER
-
Usage: change-prompt NAME
-
-
Change the current prompt. See list-prompts for a list of available
-
prompts.
-
BANNER
-
-
1
def process(prompt)
-
if prompt_map.key?(prompt)
-
_pry_.prompt = prompt_map[prompt][:value]
-
else
-
raise Pry::CommandError, "'#{prompt}' isn't a known prompt!"
-
end
-
end
-
-
1
private
-
1
def prompt_map
-
Pry::Prompt::MAP
-
end
-
1
Pry::Commands.add_command(self)
-
end
-
1
class Pry
-
1
class Command::CodeCollector
-
1
include Helpers::CommandHelpers
-
-
1
attr_reader :args
-
1
attr_reader :opts
-
1
attr_reader :_pry_
-
-
# The name of the explicitly given file (if any).
-
1
attr_accessor :file
-
-
1
class << self
-
1
attr_accessor :input_expression_ranges
-
1
attr_accessor :output_result_ranges
-
end
-
-
1
@input_expression_ranges = []
-
1
@output_result_ranges = []
-
-
1
def initialize(args, opts, _pry_)
-
@args = args
-
@opts = opts
-
@_pry_ = _pry_
-
end
-
-
# Add the `--lines`, `-o`, `-i`, `-s`, `-d` options.
-
1
def self.inject_options(opt)
-
@input_expression_ranges = []
-
@output_result_ranges = []
-
-
opt.on :l, :lines, "Restrict to a subset of lines. Takes a line number or range",
-
:optional_argument => true, :as => Range, :default => 1..-1
-
opt.on :o, :out, "Select lines from Pry's output result history. Takes an index or range",
-
:optional_argument => true, :as => Range, :default => -5..-1 do |r|
-
output_result_ranges << (r || (-5..-1))
-
end
-
opt.on :i, :in, "Select lines from Pry's input expression history. Takes an index or range",
-
:optional_argument => true, :as => Range, :default => -5..-1 do |r|
-
input_expression_ranges << (r || (-5..-1))
-
end
-
opt.on :s, :super, "Select the 'super' method. Can be repeated to traverse the ancestors",
-
:as => :count
-
opt.on :d, :doc, "Select lines from the code object's documentation"
-
end
-
-
# The content (i.e code/docs) for the selected object.
-
# If the user provided a bare code object, it returns the source.
-
# If the user provided the `-i` or `-o` switches, it returns the
-
# selected input/output lines joined as a string. If the user used
-
# `-d CODE_OBJECT` it returns the docs for that code object.
-
#
-
# @return [String]
-
1
def content
-
return @content if @content
-
raise CommandError, "Only one of --out, --in, --doc and CODE_OBJECT may be specified." if bad_option_combination?
-
-
content = case
-
when opts.present?(:o)
-
pry_output_content
-
when opts.present?(:i)
-
pry_input_content
-
when opts.present?(:d)
-
code_object_doc
-
else
-
code_object_source_or_file
-
end
-
-
@content ||= restrict_to_lines(content, line_range)
-
end
-
-
# The code object
-
#
-
# @return [Pry::WrappedModule, Pry::Method, Pry::Command]
-
1
def code_object
-
Pry::CodeObject.lookup(obj_name, _pry_, :super => opts[:super])
-
end
-
-
# Given a string and a range, return the `range` lines of that
-
# string.
-
#
-
# @param [String] content
-
# @param [Range, Fixnum] range
-
# @return [String] The string restricted to the given range
-
1
def restrict_to_lines(content, range)
-
Array(content.lines.to_a[range]).join
-
end
-
-
# The selected `_pry_.output_array` as a string, as specified by
-
# the `-o` switch.
-
#
-
# @return [String]
-
1
def pry_output_content
-
pry_array_content_as_string(_pry_.output_array, self.class.output_result_ranges) do |v|
-
_pry_.config.gist.inspecter.call(v)
-
end
-
end
-
-
# The selected `_pry_.input_array` as a string, as specified by
-
# the `-i` switch.
-
#
-
# @return [String]
-
1
def pry_input_content
-
pry_array_content_as_string(_pry_.input_array, self.class.input_expression_ranges) { |v| v }
-
end
-
-
# The line range passed to `--lines`, converted to a 0-indexed range.
-
1
def line_range
-
opts.present?(:lines) ? one_index_range_or_number(opts[:lines]) : 0..-1
-
end
-
-
# Name of the object argument
-
1
def obj_name
-
@obj_name ||= args.empty? ? "" : args.join(" ")
-
end
-
-
1
private
-
-
1
def bad_option_combination?
-
[opts.present?(:in), opts.present?(:out),
-
!args.empty?].count(true) > 1
-
end
-
-
1
def pry_array_content_as_string(array, ranges, &block)
-
all = ''
-
ranges.each do |range|
-
raise CommandError, "Minimum value for range is 1, not 0." if convert_to_range(range).first == 0
-
-
ranged_array = Array(array[range]) || []
-
ranged_array.compact.each { |v| all << block.call(v) }
-
end
-
-
all
-
end
-
-
1
def code_object_doc
-
(code_object && code_object.doc) or could_not_locate(obj_name)
-
end
-
-
1
def code_object_source_or_file
-
(code_object && code_object.source) || file_content
-
end
-
-
1
def file_content
-
if File.exists?(obj_name)
-
# Set the file accessor.
-
self.file = obj_name
-
File.read(obj_name)
-
else
-
could_not_locate(obj_name)
-
end
-
end
-
-
1
def could_not_locate(name)
-
raise CommandError, "Cannot locate: #{name}!"
-
end
-
-
1
def convert_to_range(n)
-
if !n.is_a?(Range)
-
(n..n)
-
else
-
n
-
end
-
end
-
end
-
end
-
1
class Pry
-
1
class Command::DisablePry < Pry::ClassCommand
-
1
match 'disable-pry'
-
1
group 'Navigating Pry'
-
1
description 'Stops all future calls to pry and exits the current session.'
-
-
1
banner <<-'BANNER'
-
Usage: disable-pry
-
-
After this command is run any further calls to pry will immediately return `nil`
-
without interrupting the flow of your program. This is particularly useful when
-
you've debugged the problem you were having, and now wish the program to run to
-
the end.
-
-
As alternatives, consider using `exit!` to force the current Ruby process
-
to quit immediately; or using `edit-method -p` to remove the `binding.pry`
-
from the code.
-
BANNER
-
-
1
def process
-
ENV['DISABLE_PRY'] = 'true'
-
_pry_.run_command "exit"
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::DisablePry)
-
end
-
1
Pry::Commands.disabled_command("edit-method", "Use `edit` instead.")
-
1
Pry::Commands.disabled_command("show-command", "Use show-source [command_name] instead.")
-
1
class Pry
-
1
Pry::Commands.instance_eval do
-
1
command "nyan-cat", "", :requires_gem => ["nyancat"] do
-
run ".nyancat"
-
end
-
-
1
command(/!s\/(.*?)\/(.*?)/, "") do |source, dest|
-
eval_string.gsub!(/#{source}/) { dest }
-
run "show-input"
-
end
-
-
1
command "get-naked", "" do
-
text = %{
-
--
-
We dont have to take our clothes off to have a good time.
-
We could dance & party all night And drink some cherry wine.
-
-- Jermaine Stewart }
-
output.puts text
-
text
-
end
-
-
1
command "east-coker", "" do
-
text = %{
-
--
-
Now the light falls
-
Across the open field, leaving the deep lane
-
Shuttered with branches, dark in the afternoon,
-
Where you lean against a bank while a van passes,
-
And the deep lane insists on the direction
-
Into the village, in the electric heat
-
Hypnotised. In a warm haze the sultry light
-
Is absorbed, not refracted, by grey stone.
-
The dahlias sleep in the empty silence.
-
Wait for the early owl.
-
-- T.S Eliot
-
}
-
output.puts text
-
text
-
end
-
-
1
command "cohen-poem", "" do
-
text = %{
-
--
-
When this American woman,
-
whose thighs are bound in casual red cloth,
-
comes thundering past my sitting place
-
like a forest-burning Mongol tribe,
-
the city is ravished
-
and brittle buildings of a hundred years
-
splash into the street;
-
and my eyes are burnt
-
for the embroidered Chinese girls,
-
already old,
-
and so small between the thin pines
-
on these enormous landscapes,
-
that if you turn your head
-
they are lost for hours.
-
-- Leonard Cohen
-
}
-
output.puts text
-
text
-
end
-
-
1
command "pessoa-poem", "" do
-
output.puts <<-TEXT
-
--
-
I've gone to bed with every feeling,
-
I've been the pimp of every emotion,
-
All felt sensations have bought me drinks,
-
I've traded glances with every motive for every act,
-
I've held hands with every urge to depart,
-
..
-
Rage, foam, the vastness that doesn't fit in my handkerchief,
-
The dog in heat howling in the night,
-
The pond from the farm going in circles around my insomnia,
-
The woods as they were, on our late-afternoon walks, the rose,
-
The indifferent tuft of hair, the moss, the pines,
-
The rage of not containing all this, not retaining all this,
-
O abstract hunger for things, impotent libido for moments,
-
Intellectual orgy of feeling life!
-
-- Fernando Pessoa
-
TEXT
-
end
-
-
1
command "test-ansi", "" do
-
prev_color = _pry_.config.color
-
_pry_.config.color = true
-
-
picture = unindent <<-'EOS'.gsub(/[[:alpha:]!]/) { |s| text.red(s) }
-
____ _______________________
-
/ \ | A W G |
-
/ O O \ | N I O N ! |
-
| | | S S R I ! |
-
\ \__/ / __| I K ! |
-
\____/ \________________________|
-
EOS
-
-
if windows_ansi?
-
move_up = proc { |n| "\e[#{n}F" }
-
else
-
move_up = proc { |n| "\e[#{n}A\e[0G" }
-
end
-
-
output.puts "\n" * 6
-
output.puts picture.lines.map(&:chomp).reverse.join(move_up[1])
-
output.puts "\n" * 6
-
output.puts "** ENV['TERM'] is #{ENV['TERM']} **\n\n"
-
-
_pry_.config.color = prev_color
-
end
-
end
-
end
-
1
class Pry
-
1
class Command::Edit < Pry::ClassCommand
-
1
require 'pry/commands/edit/exception_patcher'
-
1
require 'pry/commands/edit/file_and_line_locator'
-
-
1
match 'edit'
-
1
group 'Editing'
-
1
description 'Invoke the default editor on a file.'
-
-
1
banner <<-'BANNER'
-
Usage: edit [--no-reload|--reload|--patch] [--line LINE] [--temp|--ex|FILE[:LINE]|OBJECT|--in N]
-
-
Open a text editor. When no FILE is given, edits the pry input buffer.
-
When a method/module/command is given, the code is opened in an editor.
-
Ensure `Pry.config.editor` or `_pry_.config.editor` is set to your editor of choice.
-
-
edit sample.rb edit -p MyClass#my_method
-
edit sample.rb --line 105 edit MyClass
-
edit MyClass#my_method edit --ex
-
edit --method edit --ex -p
-
-
https://github.com/pry/pry/wiki/Editor-integration#wiki-Edit_command
-
BANNER
-
-
1
def options(opt)
-
opt.on :e, :ex, "Open the file that raised the most recent exception (_ex_.file)",
-
:optional_argument => true, :as => Integer
-
opt.on :i, :in, "Open a temporary file containing the Nth input expression. N may be a range",
-
:optional_argument => true, :as => Range, :default => -1..-1
-
opt.on :t, :temp, "Open an empty temporary file"
-
opt.on :l, :line, "Jump to this line in the opened file",
-
:argument => true, :as => Integer
-
opt.on :n, :"no-reload", "Don't automatically reload the edited file"
-
opt.on :c, :current, "Open the current __FILE__ and at __LINE__ (as returned by `whereami`)"
-
opt.on :r, :reload, "Reload the edited code immediately (default for ruby files)"
-
opt.on :p, :patch, "Instead of editing the object's file, try to edit in a tempfile and apply as a monkey patch"
-
opt.on :m, :method, "Explicitly edit the _current_ method (when inside a method context)."
-
end
-
-
1
def process
-
if bad_option_combination?
-
raise CommandError, "Only one of --ex, --temp, --in, --method and FILE may be specified."
-
end
-
-
if repl_edit?
-
# code defined in pry, eval'd within pry.
-
repl_edit
-
elsif runtime_patch?
-
# patch code without persisting changes
-
apply_runtime_patch
-
else
-
# code stored in actual files, eval'd at top-level
-
file_edit
-
end
-
end
-
-
1
def repl_edit?
-
!opts.present?(:ex) && !opts.present?(:current) && !opts.present?(:method) &&
-
filename_argument.empty?
-
end
-
-
1
def repl_edit
-
content = Pry::Editor.new(_pry_).edit_tempfile_with_content(initial_temp_file_content,
-
initial_temp_file_content.lines.count)
-
silence_warnings do
-
eval_string.replace content
-
end
-
end
-
-
1
def file_based_exception?
-
opts.present?(:ex) && !opts.present?(:patch)
-
end
-
-
1
def runtime_patch?
-
!file_based_exception? && (opts.present?(:patch) || pry_method?(code_object))
-
end
-
-
1
def apply_runtime_patch
-
if patch_exception?
-
ExceptionPatcher.new(_pry_, state, file_and_line_for_current_exception).perform_patch
-
else
-
if code_object.is_a?(Pry::Method)
-
code_object.redefine Pry::Editor.new(_pry_).edit_tempfile_with_content(code_object.source)
-
else
-
raise NotImplementedError, "Cannot yet patch #{code_object} objects!"
-
end
-
end
-
end
-
-
1
def ensure_file_name_is_valid(file_name)
-
raise CommandError, "Cannot find a valid file for #{filename_argument}" if !file_name
-
raise CommandError, "#{file_name} is not a valid file name, cannot edit!" if not_a_real_file?(file_name)
-
end
-
-
1
def file_and_line_for_current_exception
-
FileAndLineLocator.from_exception(_pry_.last_exception, opts[:ex].to_i)
-
end
-
-
1
def file_and_line
-
file_name, line = if opts.present?(:current)
-
FileAndLineLocator.from_binding(target)
-
elsif opts.present?(:ex)
-
file_and_line_for_current_exception
-
elsif code_object
-
FileAndLineLocator.from_code_object(code_object, filename_argument)
-
else
-
# when file and line are passed as a single arg, e.g my_file.rb:30
-
FileAndLineLocator.from_filename_argument(filename_argument)
-
end
-
-
[file_name, opts.present?(:line) ? opts[:l].to_i : line]
-
end
-
-
1
def file_edit
-
file_name, line = file_and_line
-
-
ensure_file_name_is_valid(file_name)
-
-
Pry::Editor.new(_pry_).invoke_editor(file_name, line, reload?(file_name))
-
set_file_and_dir_locals(file_name)
-
-
if reload?(file_name)
-
silence_warnings do
-
load file_name
-
end
-
end
-
end
-
-
1
def filename_argument
-
args.join(' ')
-
end
-
-
1
def code_object
-
@code_object ||= !probably_a_file?(filename_argument) &&
-
Pry::CodeObject.lookup(filename_argument, _pry_)
-
end
-
-
1
def pry_method?(code_object)
-
code_object.is_a?(Pry::Method) &&
-
code_object.pry_method?
-
end
-
-
1
def patch_exception?
-
opts.present?(:ex) && opts.present?(:patch)
-
end
-
-
1
def bad_option_combination?
-
[opts.present?(:ex), opts.present?(:temp),
-
opts.present?(:in), opts.present?(:method), !filename_argument.empty?].count(true) > 1
-
end
-
-
1
def input_expression
-
case opts[:i]
-
when Range
-
(_pry_.input_array[opts[:i]] || []).join
-
when Fixnum
-
_pry_.input_array[opts[:i]] || ""
-
else
-
raise Pry::CommandError, "Not a valid range: #{opts[:i]}"
-
end
-
end
-
-
1
def reloadable?
-
opts.present?(:reload) || opts.present?(:ex)
-
end
-
-
1
def never_reload?
-
opts.present?(:'no-reload') || _pry_.config.disable_auto_reload
-
end
-
-
1
def reload?(file_name="")
-
(reloadable? || file_name.end_with?(".rb")) && !never_reload?
-
end
-
-
1
def initial_temp_file_content
-
case
-
when opts.present?(:temp)
-
""
-
when opts.present?(:in)
-
input_expression
-
when eval_string.strip != ""
-
eval_string
-
else
-
_pry_.input_array.reverse_each.find { |x| x && x.strip != "" } || ""
-
end
-
end
-
-
1
def probably_a_file?(str)
-
[".rb", ".c", ".py", ".yml", ".gemspec"].include?(File.extname(str)) ||
-
str =~ /\/|\\/
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::Edit)
-
end
-
1
class Pry
-
1
class Command::Edit
-
1
class ExceptionPatcher
-
1
attr_accessor :_pry_
-
1
attr_accessor :state
-
1
attr_accessor :file_and_line
-
-
1
def initialize(_pry_, state, exception_file_and_line)
-
@_pry_ = _pry_
-
@state = state
-
@file_and_line = exception_file_and_line
-
end
-
-
# perform the patch
-
1
def perform_patch
-
file_name, _ = file_and_line
-
lines = state.dynamical_ex_file || File.read(file_name)
-
-
source = Pry::Editor.new(_pry_).edit_tempfile_with_content(lines)
-
_pry_.evaluate_ruby source
-
state.dynamical_ex_file = source.split("\n")
-
end
-
end
-
end
-
end
-
1
class Pry
-
1
class Command::Edit
-
1
module FileAndLineLocator
-
1
class << self
-
1
def from_binding(target)
-
[target.eval("__FILE__"), target.eval("__LINE__")]
-
end
-
-
1
def from_code_object(code_object, filename_argument)
-
if File.exists?(code_object.source_file.to_s)
-
[code_object.source_file, code_object.source_line]
-
else
-
raise CommandError, "Cannot find a file for #{filename_argument}!"
-
end
-
end
-
-
1
def from_exception(exception, backtrace_level)
-
raise CommandError, "No exception found." if exception.nil?
-
-
file_name, line = exception.bt_source_location_for(backtrace_level)
-
raise CommandError, "Exception has no associated file." if file_name.nil?
-
raise CommandError, "Cannot edit exceptions raised in REPL." if Pry.eval_path == file_name
-
-
[file_name, line]
-
end
-
-
# when file and line are passed as a single arg, e.g my_file.rb:30
-
1
def from_filename_argument(filename_argument)
-
f = File.expand_path(filename_argument)
-
l = f.sub!(/:(\d+)$/, "") ? $1.to_i : 1
-
[f, l]
-
end
-
end
-
end
-
end
-
end
-
1
class Pry
-
1
class Command::Exit < Pry::ClassCommand
-
1
match 'exit'
-
1
group 'Navigating Pry'
-
1
description 'Pop the previous binding.'
-
1
command_options :keep_retval => true
-
-
1
banner <<-'BANNER'
-
Usage: exit [OPTIONS] [--help]
-
Aliases: quit
-
-
Pop the previous binding (does NOT exit program). It can be useful to exit a
-
context with a user-provided value. For instance an exit value can be used to
-
determine program flow.
-
-
exit "pry this"
-
exit
-
-
https://github.com/pry/pry/wiki/State-navigation#wiki-Exit_with_value
-
BANNER
-
-
1
def process
-
if _pry_.binding_stack.one?
-
_pry_.run_command "exit-all #{arg_string}"
-
else
-
# otherwise just pop a binding and return user supplied value
-
process_pop_and_return
-
end
-
end
-
-
1
def process_pop_and_return
-
popped_object = _pry_.binding_stack.pop.eval('self')
-
-
# return a user-specified value if given otherwise return the object
-
return target.eval(arg_string) unless arg_string.empty?
-
popped_object
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::Exit)
-
1
Pry::Commands.alias_command 'quit', 'exit'
-
end
-
1
class Pry
-
1
class Command::ExitAll < Pry::ClassCommand
-
1
match 'exit-all'
-
1
group 'Navigating Pry'
-
1
description 'End the current Pry session.'
-
-
1
banner <<-'BANNER'
-
Usage: exit-all [--help]
-
Aliases: !!@
-
-
End the current Pry session (popping all bindings and returning to caller).
-
Accepts optional return value.
-
BANNER
-
-
1
def process
-
# calculate user-given value
-
exit_value = target.eval(arg_string)
-
-
# clear the binding stack
-
_pry_.binding_stack.clear
-
-
# break out of the repl loop
-
throw(:breakout, exit_value)
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::ExitAll)
-
1
Pry::Commands.alias_command '!!@', 'exit-all'
-
end
-
1
class Pry
-
1
class Command::ExitProgram < Pry::ClassCommand
-
1
match 'exit-program'
-
1
group 'Navigating Pry'
-
1
description 'End the current program.'
-
-
1
banner <<-'BANNER'
-
Usage: exit-program [--help]
-
Aliases: quit-program
-
!!!
-
-
End the current program.
-
BANNER
-
-
1
def process
-
Kernel.exit target.eval(arg_string).to_i
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::ExitProgram)
-
1
Pry::Commands.alias_command 'quit-program', 'exit-program'
-
1
Pry::Commands.alias_command '!!!', 'exit-program'
-
end
-
1
class Pry
-
1
class Command::FindMethod < Pry::ClassCommand
-
1
extend Pry::Helpers::BaseHelpers
-
-
1
match 'find-method'
-
1
group 'Context'
-
1
description 'Recursively search for a method within a Class/Module or the current namespace.'
-
1
command_options :shellwords => false
-
-
1
banner <<-'BANNER'
-
Usage: find-method [-n|-c] METHOD [NAMESPACE]
-
-
Recursively search for a method within a Class/Module or the current namespace.
-
Use the `-n` switch (the default) to search for methods whose name matches the
-
given regex. Use the `-c` switch to search for methods that contain the given
-
code.
-
-
# Find all methods whose name match /re/ inside
-
# the Pry namespace. Matches Pry#repl, etc.
-
find-method re Pry
-
-
# Find all methods that contain the code:
-
# output.puts inside the Pry namepsace.
-
find-method -c 'output.puts' Pry
-
BANNER
-
-
1
def options(opt)
-
opt.on :n, :name, "Search for a method by name"
-
opt.on :c, :content, "Search for a method based on content in Regex form"
-
end
-
-
1
def process
-
return if args.size < 1
-
klass = search_class
-
-
matches = if opts.content?
-
content_search(klass)
-
else
-
name_search(klass)
-
end
-
-
show_search_results(matches)
-
end
-
-
1
private
-
-
# @return [Regexp] The pattern to search for.
-
1
def pattern
-
@pattern ||= ::Regexp.new args[0]
-
end
-
-
# Output the result of the search.
-
#
-
# @param [Array] matches
-
1
def show_search_results(matches)
-
if matches.empty?
-
output.puts text.bold("No Methods Matched")
-
else
-
print_matches(matches)
-
end
-
end
-
-
# The class to search for methods.
-
# We only search classes, so if the search object is an
-
# instance, return its class. If no search object is given
-
# search `target_self`.
-
1
def search_class
-
klass = if args[1]
-
target.eval(args[1])
-
else
-
target_self
-
end
-
-
klass.is_a?(Module) ? klass : klass.class
-
end
-
-
# pretty-print a list of matching methods.
-
#
-
# @param [Array<Method>] matches
-
1
def print_matches(matches)
-
grouped = matches.group_by(&:owner)
-
order = grouped.keys.sort_by{ |x| x.name || x.to_s }
-
-
order.each do |klass|
-
print_matches_for_class(klass, grouped)
-
end
-
end
-
-
# Print matched methods for a class
-
1
def print_matches_for_class(klass, grouped)
-
output.puts text.bold(klass.name)
-
grouped[klass].each do |method|
-
header = method.name_with_owner
-
output.puts header + additional_info(header, method)
-
end
-
end
-
-
# Return the matched lines of method source if `-c` is given or ""
-
# if `-c` was not given
-
1
def additional_info(header, method)
-
if opts.content?
-
": " << colorize_code(matched_method_lines(header, method))
-
else
-
""
-
end
-
end
-
-
1
def matched_method_lines(header, method)
-
method.source.split(/\n/).select {|x| x =~ pattern }.join("\n#{' ' * header.length}")
-
end
-
-
# Run the given block against every constant in the provided namespace.
-
#
-
# @param [Module] klass The namespace in which to start the search.
-
# @param [Hash<Module,Boolean>] done The namespaces we've already visited (private)
-
# @yieldparam klass Each class/module in the namespace.
-
#
-
1
def recurse_namespace(klass, done={}, &block)
-
return if !(Module === klass) || done[klass]
-
-
done[klass] = true
-
-
yield klass
-
-
klass.constants.each do |name|
-
next if klass.autoload?(name)
-
begin
-
const = klass.const_get(name)
-
rescue RescuableException
-
# constant loading is an inexact science at the best of times,
-
# this often happens when a constant was .autoload? but someone
-
# tried to load it. It's now not .autoload? but will still raise
-
# a NameError when you access it.
-
else
-
recurse_namespace(const, done, &block)
-
end
-
end
-
end
-
-
# Gather all the methods in a namespace that pass the given block.
-
#
-
# @param [Module] namespace The namespace in which to search.
-
# @yieldparam [Method] method The method to test
-
# @yieldreturn [Boolean]
-
# @return [Array<Method>]
-
#
-
1
def search_all_methods(namespace)
-
done = Hash.new{ |h,k| h[k] = {} }
-
matches = []
-
-
recurse_namespace(namespace) do |klass|
-
(Pry::Method.all_from_class(klass) + Pry::Method.all_from_obj(klass)).each do |method|
-
next if done[method.owner][method.name]
-
done[method.owner][method.name] = true
-
-
matches << method if yield method
-
end
-
end
-
-
matches
-
end
-
-
# Search for all methods with a name that matches the given regex
-
# within a namespace.
-
#
-
# @param [Module] namespace The namespace to search
-
# @return [Array<Method>]
-
#
-
1
def name_search(namespace)
-
search_all_methods(namespace) do |meth|
-
meth.name =~ pattern
-
end
-
end
-
-
# Search for all methods who's implementation matches the given regex
-
# within a namespace.
-
#
-
# @param [Module] namespace The namespace to search
-
# @return [Array<Method>]
-
#
-
1
def content_search(namespace)
-
search_all_methods(namespace) do |meth|
-
begin
-
meth.source =~ pattern
-
rescue RescuableException
-
false
-
end
-
end
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::FindMethod)
-
end
-
1
class Pry
-
1
class Command::FixIndent < Pry::ClassCommand
-
1
match 'fix-indent'
-
1
group 'Input and Output'
-
-
1
description "Correct the indentation for contents of the input buffer"
-
-
1
banner <<-USAGE
-
Usage: fix-indent
-
USAGE
-
-
1
def process
-
indented_str = Pry::Indent.indent(eval_string)
-
eval_string.replace indented_str
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::FixIndent)
-
end
-
1
class Pry
-
1
class Command::GemCd < Pry::ClassCommand
-
1
match 'gem-cd'
-
1
group 'Gems'
-
1
description "Change working directory to specified gem's directory."
-
1
command_options :argument_required => true
-
-
1
banner <<-'BANNER'
-
Usage: gem-cd GEM_NAME
-
-
Change the current working directory to that in which the given gem is
-
installed.
-
BANNER
-
-
1
def process(gem)
-
Dir.chdir(Rubygem.spec(gem).full_gem_path)
-
output.puts(Dir.pwd)
-
end
-
-
1
def complete(str)
-
Rubygem.complete(str)
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::GemCd)
-
end
-
1
class Pry
-
1
class Command::GemInstall < Pry::ClassCommand
-
1
match 'gem-install'
-
1
group 'Gems'
-
1
description 'Install a gem and refresh the gem cache.'
-
1
command_options :argument_required => true
-
-
1
banner <<-'BANNER'
-
Usage: gem-install GEM_NAME
-
-
Installs the given gem, refreshes the gem cache, and requires the gem for you
-
based on a best guess from the gem name.
-
-
gem-install pry-stack_explorer
-
BANNER
-
-
1
def setup
-
require 'rubygems/dependency_installer' unless defined? Gem::DependencyInstaller
-
end
-
-
1
def process(gem)
-
Rubygem.install(gem)
-
output.puts "Gem `#{ text.green(gem) }` installed."
-
require gem
-
rescue LoadError
-
require_path = gem.split('-').join('/')
-
require require_path
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::GemInstall)
-
end
-
1
class Pry
-
1
class Command::GemList < Pry::ClassCommand
-
1
match 'gem-list'
-
1
group 'Gems'
-
1
description 'List and search installed gems.'
-
-
1
banner <<-'BANNER'
-
Usage: gem-list [REGEX]
-
-
List all installed gems, when a regex is provided, limit the output to those
-
that match the regex.
-
BANNER
-
-
1
def process(pattern = nil)
-
pattern = Regexp.compile(pattern || '')
-
gems = Rubygem.list(pattern).group_by(&:name)
-
-
gems.each do |gem, specs|
-
specs.sort! do |a,b|
-
Gem::Version.new(b.version) <=> Gem::Version.new(a.version)
-
end
-
-
versions = specs.each_with_index.map do |spec, index|
-
index == 0 ? text.bright_green(spec.version.to_s) : text.green(spec.version.to_s)
-
end
-
-
output.puts "#{text.default gem} (#{versions.join ', '})"
-
end
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::GemList)
-
end
-
1
class Pry
-
1
class Command::GemOpen < Pry::ClassCommand
-
1
match 'gem-open'
-
1
group 'Gems'
-
1
description 'Opens the working directory of the gem in your editor.'
-
1
command_options :argument_required => true
-
-
1
banner <<-'BANNER'
-
Usage: gem-open GEM_NAME
-
-
Change the current working directory to that in which the given gem is
-
installed, and then opens your text editor.
-
-
gem-open pry-exception_explorer
-
BANNER
-
-
1
def process(gem)
-
Dir.chdir(Rubygem.spec(gem).full_gem_path) do
-
Pry::Editor.invoke_editor(".", 0, false)
-
end
-
end
-
-
1
def complete(str)
-
Rubygem.complete(str)
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::GemOpen)
-
end
-
1
class Pry
-
1
class Command::Gist < Pry::ClassCommand
-
1
match 'gist'
-
1
group 'Misc'
-
1
description 'Upload code, docs, history to https://gist.github.com/.'
-
1
command_options :requires_gem => "gist"
-
-
1
banner <<-'BANNER'
-
Usage: gist [OPTIONS] [--help]
-
-
The gist command enables you to gist code from files and methods to github.
-
-
gist -i 20 --lines 1..3
-
gist Pry#repl --lines 1..-1
-
gist Rakefile --lines 5
-
BANNER
-
-
1
def setup
-
require 'gist'
-
end
-
-
1
def options(opt)
-
CodeCollector.inject_options(opt)
-
opt.on :login, "Authenticate the gist gem with GitHub"
-
opt.on :p, :public, "Create a public gist (default: false)", :default => false
-
opt.on :clip, "Copy the selected content to clipboard instead, do NOT gist it", :default => false
-
end
-
-
1
def process
-
return ::Gist.login! if opts.present?(:login)
-
cc = CodeCollector.new(args, opts, _pry_)
-
-
if cc.content =~ /\A\s*\z/
-
raise CommandError, "Found no code to gist."
-
end
-
-
if opts.present?(:clip)
-
clipboard_content(cc.content)
-
else
-
# we're overriding the default behavior of the 'in' option (as
-
# defined on CodeCollector) with our local behaviour.
-
content = opts.present?(:in) ? input_content : cc.content
-
gist_content content, cc.file
-
end
-
end
-
-
1
def clipboard_content(content)
-
::Gist.copy(content)
-
output.puts "Copied content to clipboard!"
-
end
-
-
1
def input_content
-
content = ""
-
CodeCollector.input_expression_ranges.each do |range|
-
input_expressions = _pry_.input_array[range] || []
-
Array(input_expressions).each_with_index do |code, index|
-
corrected_index = index + range.first
-
if code && code != ""
-
content << code
-
if code !~ /;\Z/
-
content << "#{comment_expression_result_for_gist(_pry_.config.gist.inspecter.call(_pry_.output_array[corrected_index]))}"
-
end
-
end
-
end
-
end
-
-
content
-
end
-
-
1
def comment_expression_result_for_gist(result)
-
content = ""
-
result.lines.each_with_index do |line, index|
-
if index == 0
-
content << "# => #{line}"
-
else
-
content << "# #{line}"
-
end
-
end
-
-
content
-
end
-
-
1
def gist_content(content, filename)
-
response = ::Gist.gist(content, :filename => filename || "pry_gist.rb", :public => !!opts[:p])
-
if response
-
url = response['html_url']
-
message = "Gist created at URL #{url}"
-
begin
-
::Gist.copy(url)
-
message << ", which is now in the clipboard."
-
rescue ::Gist::ClipboardError
-
end
-
-
output.puts message
-
end
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::Gist)
-
1
Pry::Commands.alias_command 'clipit', 'gist --clip'
-
end
-
1
class Pry
-
1
class Command::Help < Pry::ClassCommand
-
1
match 'help'
-
1
group 'Help'
-
1
description 'Show a list of commands or information about a specific command.'
-
-
1
banner <<-'BANNER'
-
Usage: help [COMMAND]
-
-
With no arguments, help lists all the available commands along with their
-
descriptions. When given a command name as an argument, shows the help
-
for that command.
-
BANNER
-
-
# We only want to show commands that have descriptions, so that the
-
# easter eggs don't show up.
-
1
def visible_commands
-
visible = {}
-
commands.each do |key, command|
-
visible[key] = command if command.description && !command.description.empty?
-
end
-
visible
-
end
-
-
# Get a hash of available commands grouped by the "group" name.
-
1
def command_groups
-
visible_commands.values.group_by(&:group)
-
end
-
-
1
def process
-
if args.empty?
-
display_index(command_groups)
-
else
-
display_search(args.first)
-
end
-
end
-
-
# Display the index view, with headings and short descriptions per command.
-
#
-
# @param [Hash<String, Array<Commands>>] groups
-
1
def display_index(groups)
-
help_text = []
-
-
sorted_group_names(groups).each do |group_name|
-
commands = sorted_commands(groups[group_name])
-
-
if commands.any?
-
help_text << help_text_for_commands(group_name, commands)
-
end
-
end
-
-
_pry_.pager.page help_text.join("\n\n")
-
end
-
-
# Given a group name and an array of commands,
-
# return the help string for those commands.
-
#
-
# @param [String] name The group name.
-
# @param [Array<Pry::Command>] commands
-
# @return [String] The generated help string.
-
1
def help_text_for_commands(name, commands)
-
"#{text.bold(name.capitalize)}\n" << commands.map do |command|
-
" #{command.options[:listing].to_s.ljust(18)} #{command.description.capitalize}"
-
end.join("\n")
-
end
-
-
# @param [Hash] groups
-
# @return [Array<String>] An array of sorted group names.
-
1
def sorted_group_names(groups)
-
groups.keys.sort_by(&method(:group_sort_key))
-
end
-
-
# Sort an array of commands by their `listing` name.
-
#
-
# @param [Array<Pry::Command>] commands The commands to sort
-
# @return [Array<Pry::Command>] commands sorted by listing name.
-
1
def sorted_commands(commands)
-
commands.sort_by{ |command| command.options[:listing].to_s }
-
end
-
-
# Display help for an individual command or group.
-
#
-
# @param [String] search The string to search for.
-
1
def display_search(search)
-
if command = command_set.find_command_for_help(search)
-
display_command(command)
-
else
-
display_filtered_search_results(search)
-
end
-
end
-
-
# Display help for a searched item, filtered first by group
-
# and if that fails, filtered by command name.
-
#
-
# @param [String] search The string to search for.
-
1
def display_filtered_search_results(search)
-
groups = search_hash(search, command_groups)
-
-
if groups.size > 0
-
display_index(groups)
-
else
-
display_filtered_commands(search)
-
end
-
end
-
-
# Display help for a searched item, filtered by group
-
#
-
# @param [String] search The string to search for.
-
1
def display_filtered_commands(search)
-
filtered = search_hash(search, visible_commands)
-
raise CommandError, "No help found for '#{args.first}'" if filtered.empty?
-
-
if filtered.size == 1
-
display_command(filtered.values.first)
-
else
-
display_index({"'#{search}' commands" => filtered.values})
-
end
-
end
-
-
# Display help for an individual command.
-
#
-
# @param [Pry::Command] command
-
1
def display_command(command)
-
_pry_.pager.page command.new.help
-
end
-
-
# Find a subset of a hash that matches the user's search term.
-
#
-
# If there's an exact match a Hash of one element will be returned,
-
# otherwise a sub-Hash with every key that matches the search will
-
# be returned.
-
#
-
# @param [String] search the search term
-
# @param [Hash] hash the hash to search
-
1
def search_hash(search, hash)
-
matching = {}
-
-
hash.each_pair do |key, value|
-
next unless key.is_a?(String)
-
if normalize(key) == normalize(search)
-
return {key => value}
-
elsif normalize(key).start_with?(normalize(search))
-
matching[key] = value
-
end
-
end
-
-
matching
-
end
-
-
# Clean search terms to make it easier to search group names
-
#
-
# @param [String] key
-
# @return [String]
-
1
def normalize(key)
-
key.downcase.gsub(/pry\W+/, '')
-
end
-
-
1
def group_sort_key(group_name)
-
[%w(Help Context Editing Introspection Input_and_output Navigating_pry Gems Basic Commands).index(group_name.gsub(' ', '_')) || 99, group_name]
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::Help)
-
end
-
1
class Pry
-
1
class Command::Hist < Pry::ClassCommand
-
1
match 'hist'
-
1
group 'Editing'
-
1
description 'Show and replay Readline history.'
-
-
1
banner <<-'BANNER'
-
Usage: hist [--head|--tail]
-
hist --all
-
hist --head N
-
hist --tail N
-
hist --show START..END
-
hist --grep PATTERN
-
hist --clear
-
hist --replay START..END
-
hist --save [START..END] FILE
-
Aliases: history
-
-
Show and replay Readline history.
-
BANNER
-
-
1
def options(opt)
-
opt.on :a, :all, "Display all history"
-
opt.on :H, :head, "Display the first N items", :optional_argument => true, :as => Integer
-
opt.on :T, :tail, "Display the last N items", :optional_argument => true, :as => Integer
-
opt.on :s, :show, "Show the given range of lines", :optional_argument => true, :as => Range
-
opt.on :G, :grep, "Show lines matching the given pattern", :argument => true, :as => String
-
opt.on :c, :clear , "Clear the current session's history"
-
opt.on :r, :replay, "Replay a line or range of lines", :argument => true, :as => Range
-
opt.on :save, "Save history to a file", :argument => true, :as => Range
-
opt.on :e, :'exclude-pry', "Exclude Pry commands from the history"
-
opt.on :n, :'no-numbers', "Omit line numbers"
-
end
-
-
1
def process
-
@history = find_history
-
-
if opts.present?(:show)
-
@history = @history.between(opts[:show])
-
end
-
-
if opts.present?(:grep)
-
@history = @history.grep(opts[:grep])
-
end
-
-
@history = case
-
when opts.present?(:head)
-
@history.take_lines(1, opts[:head] || 10)
-
when opts.present?(:tail)
-
@history.take_lines(-(opts[:tail] || 10), opts[:tail] || 10)
-
when opts.present?(:show)
-
@history.between(opts[:show])
-
else
-
@history
-
end
-
-
if opts.present?(:'exclude-pry')
-
@history = @history.select do |loc|
-
!command_set.valid_command?(loc.line)
-
end
-
end
-
-
if opts.present?(:save)
-
process_save
-
elsif opts.present?(:clear)
-
process_clear
-
elsif opts.present?(:replay)
-
process_replay
-
else
-
process_display
-
end
-
end
-
-
1
private
-
-
1
def process_display
-
unless opts.present?(:'no-numbers')
-
@history = @history.with_line_numbers
-
end
-
-
_pry_.pager.open do |pager|
-
@history.print_to_output(pager, true)
-
end
-
end
-
-
1
def process_save
-
case opts[:save]
-
when Range
-
@history = @history.between(opts[:save])
-
-
unless args.first
-
raise CommandError, "Must provide a file name."
-
end
-
-
file_name = File.expand_path(args.first)
-
when String
-
file_name = File.expand_path(opts[:save])
-
end
-
-
output.puts "Saving history in #{file_name}..."
-
-
File.open(file_name, 'w') { |f| f.write(@history.raw) }
-
-
output.puts "History saved."
-
end
-
-
1
def process_clear
-
Pry.history.clear
-
output.puts "History cleared."
-
end
-
-
1
def process_replay
-
@history = @history.between(opts[:r])
-
replay_sequence = @history.raw
-
-
# If we met follow-up "hist" call, check for the "--replay" option
-
# presence. If "hist" command is called with other options, proceed
-
# further.
-
check_for_juxtaposed_replay(replay_sequence)
-
-
replay_sequence.lines.each do |line|
-
_pry_.eval line, :generated => true
-
end
-
end
-
-
# Checks +replay_sequence+ for the presence of neighboring replay calls.
-
# @example
-
# [1] pry(main)> hist --show 46894
-
# 46894: hist --replay 46675..46677
-
# [2] pry(main)> hist --show 46675..46677
-
# 46675: 1+1
-
# 46676: a = 100
-
# 46677: hist --tail
-
# [3] pry(main)> hist --replay 46894
-
# Error: Replay index 46894 points out to another replay call: `hist -r 46675..46677`
-
# [4] pry(main)>
-
#
-
# @raise [Pry::CommandError] If +replay_sequence+ contains another
-
# "hist --replay" call
-
# @param [String] replay_sequence The sequence of commands to be replayed
-
# (per saltum)
-
# @return [Boolean] `false` if +replay_sequence+ does not contain another
-
# "hist --replay" call
-
1
def check_for_juxtaposed_replay(replay_sequence)
-
if replay_sequence =~ /\Ahist(?:ory)?\b/
-
# Create *fresh* instance of Options for parsing of "hist" command.
-
_slop = self.slop
-
_slop.parse replay_sequence.split(' ')[1..-1]
-
-
if _slop.present?(:r)
-
replay_sequence = replay_sequence.split("\n").join('; ')
-
index = opts[:r]
-
index = index.min if index.min == index.max || index.max.nil?
-
-
raise CommandError, "Replay index #{ index } points out to another replay call: `#{ replay_sequence }`"
-
end
-
else
-
false
-
end
-
end
-
-
# Finds history depending on the given switch.
-
#
-
# @return [Pry::Code] if it finds `--all` (or `-a`) switch, returns all
-
# entries in history. Without the switch returns only the entries from the
-
# current Pry session.
-
1
def find_history
-
h = if opts.present?(:all)
-
Pry.history.to_a
-
else
-
Pry.history.to_a.last(Pry.history.session_line_count)
-
end
-
# The last value in history will be the 'hist' command itself.
-
Pry::Code(h[0..-2])
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::Hist)
-
1
Pry::Commands.alias_command 'history', 'hist'
-
end
-
1
class Pry
-
1
class Command::ImportSet < Pry::ClassCommand
-
1
match 'import-set'
-
1
group 'Commands'
-
# TODO: Provide a better description with examples and a general conception
-
# of this command.
-
1
description 'Import a Pry command set.'
-
-
1
banner <<-'BANNER'
-
Import a Pry command set.
-
BANNER
-
-
1
def process(command_set_name)
-
raise CommandError, "Provide a command set name" if command_set.nil?
-
-
set = target.eval(arg_string)
-
_pry_.commands.import set
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::ImportSet)
-
end
-
1
class Pry
-
1
class Command::InstallCommand < Pry::ClassCommand
-
1
match 'install-command'
-
1
group 'Commands'
-
1
description 'Install a disabled command.'
-
-
1
banner <<-'BANNER'
-
Usage: install-command COMMAND
-
-
Installs the gems necessary to run the given COMMAND. You will generally not
-
need to run this unless told to by an error message.
-
BANNER
-
-
1
def process(name)
-
require 'rubygems/dependency_installer' unless defined? Gem::DependencyInstaller
-
command = find_command(name)
-
-
unless command
-
output.puts "Command #{ text.green(name) } is not found"
-
return
-
end
-
-
if command_dependencies_met?(command.options)
-
output.puts "Dependencies for #{ text.green(name) } are met. Nothing to do"
-
return
-
end
-
-
output.puts "Attempting to install #{ text.green(name) } command..."
-
gems_to_install = Array(command.options[:requires_gem])
-
-
gems_to_install.each do |g|
-
next if Rubygem.installed?(g)
-
output.puts "Installing #{ text.green(g) } gem..."
-
Rubygem.install(g)
-
end
-
-
gems_to_install.each do |g|
-
begin
-
require g
-
rescue LoadError
-
fail_msg = "Required gem #{ text.green(g) } installed but not found."
-
fail_msg += " Aborting command installation\n"
-
fail_msg += 'Tips: 1. Check your PATH; 2. Run `bundle update`'
-
raise CommandError, fail_msg
-
end
-
end
-
-
output.puts "Installation of #{ text.green(name) } successful! Type `help #{name}` for information"
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::InstallCommand)
-
end
-
1
class Pry
-
1
class Command::JumpTo < Pry::ClassCommand
-
1
match 'jump-to'
-
1
group 'Navigating Pry'
-
1
description 'Jump to a binding further up the stack.'
-
-
1
banner <<-'BANNER'
-
Jump to a binding further up the stack, popping all bindings below.
-
BANNER
-
-
1
def process(break_level)
-
break_level = break_level.to_i
-
nesting_level = _pry_.binding_stack.size - 1
-
-
case break_level
-
when nesting_level
-
output.puts "Already at nesting level #{nesting_level}"
-
when (0...nesting_level)
-
_pry_.binding_stack.slice!(break_level + 1, _pry_.binding_stack.size)
-
-
else
-
max_nest_level = nesting_level - 1
-
output.puts "Invalid nest level. Must be between 0 and #{max_nest_level}. Got #{break_level}."
-
end
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::JumpTo)
-
end
-
1
class Pry::Command::ListInspectors < Pry::ClassCommand
-
1
match 'list-inspectors'
-
1
group 'Input and Output'
-
1
description 'List the inspector procs available for use.'
-
1
banner <<-BANNER
-
Usage: list-inspectors
-
-
List the inspector procs available to print return values. You can use
-
change-inspector to switch between them.
-
BANNER
-
-
1
def process
-
output.puts heading("Available inspectors") + "\n"
-
inspector_map.each do |name, inspector|
-
output.write "Name: #{text.bold(name)}"
-
output.puts selected_inspector?(inspector) ? selected_text : ""
-
output.puts inspector[:description]
-
output.puts
-
end
-
end
-
-
1
private
-
1
def inspector_map
-
Pry::Inspector::MAP
-
end
-
-
1
def selected_text
-
text.red " (selected) "
-
end
-
-
1
def selected_inspector?(inspector)
-
_pry_.print == inspector[:value]
-
end
-
1
Pry::Commands.add_command(self)
-
end
-
1
class Pry::Command::ListPrompts < Pry::ClassCommand
-
1
match 'list-prompts'
-
1
group 'Input and Output'
-
1
description 'List the prompts available for use.'
-
1
banner <<-BANNER
-
Usage: list-prompts
-
-
List the available prompts. You can use change-prompt to switch between
-
them.
-
BANNER
-
-
1
def process
-
output.puts heading("Available prompts") + "\n"
-
prompt_map.each do |name, prompt|
-
output.write "Name: #{text.bold(name)}"
-
output.puts selected_prompt?(prompt) ? selected_text : ""
-
output.puts prompt[:description]
-
output.puts
-
end
-
end
-
-
1
private
-
1
def prompt_map
-
Pry::Prompt::MAP
-
end
-
-
1
def selected_text
-
text.red " (selected) "
-
end
-
-
1
def selected_prompt?(prompt)
-
_pry_.prompt == prompt[:value]
-
end
-
1
Pry::Commands.add_command(self)
-
end
-
1
require 'pry/commands/ls/ls_entity'
-
1
class Pry
-
1
class Command::Ls < Pry::ClassCommand
-
1
DEFAULT_OPTIONS = {
-
:heading_color => :bright_blue,
-
:public_method_color => :default,
-
:private_method_color => :blue,
-
:protected_method_color => :blue,
-
:method_missing_color => :bright_red,
-
:local_var_color => :yellow,
-
:pry_var_color => :default, # e.g. _, _pry_, _file_
-
:instance_var_color => :blue, # e.g. @foo
-
:class_var_color => :bright_blue, # e.g. @@foo
-
:global_var_color => :default, # e.g. $CODERAY_DEBUG, $eventmachine_library
-
:builtin_global_color => :cyan, # e.g. $stdin, $-w, $PID
-
:pseudo_global_color => :cyan, # e.g. $~, $1..$9, $LAST_MATCH_INFO
-
:constant_color => :default, # e.g. VERSION, ARGF
-
:class_constant_color => :blue, # e.g. Object, Kernel
-
:exception_constant_color => :magenta, # e.g. Exception, RuntimeError
-
:unloaded_constant_color => :yellow, # Any constant that is still in .autoload? state
-
:separator => " ",
-
:ceiling => [Object, Module, Class]
-
}
-
-
-
1
match 'ls'
-
1
group 'Context'
-
1
description 'Show the list of vars and methods in the current scope.'
-
1
command_options :shellwords => false, :interpolate => false
-
-
1
banner <<-'BANNER'
-
Usage: ls [-m|-M|-p|-pM] [-q|-v] [-c|-i] [Object]
-
ls [-g] [-l]
-
-
ls shows you which methods, constants and variables are accessible to Pry. By
-
default it shows you the local variables defined in the current shell, and any
-
public methods or instance variables defined on the current object.
-
-
The colours used are configurable using Pry.config.ls.*_color, and the separator
-
is Pry.config.ls.separator.
-
-
Pry.config.ls.ceiling is used to hide methods defined higher up in the
-
inheritance chain, this is by default set to [Object, Module, Class] so that
-
methods defined on all Objects are omitted. The -v flag can be used to ignore
-
this setting and show all methods, while the -q can be used to set the ceiling
-
much lower and show only methods defined on the object or its direct class.
-
-
Also check out `find-method` command (run `help find-method`).
-
BANNER
-
-
-
1
def options(opt)
-
opt.on :m, :methods, "Show public methods defined on the Object"
-
opt.on :M, "instance-methods", "Show public methods defined in a Module or Class"
-
opt.on :p, :ppp, "Show public, protected (in yellow) and private (in green) methods"
-
opt.on :q, :quiet, "Show only methods defined on object.singleton_class and object.class"
-
opt.on :v, :verbose, "Show methods and constants on all super-classes (ignores Pry.config.ls.ceiling)"
-
opt.on :g, :globals, "Show global variables, including those builtin to Ruby (in cyan)"
-
opt.on :l, :locals, "Show hash of local vars, sorted by descending size"
-
opt.on :c, :constants, "Show constants, highlighting classes (in blue), and exceptions (in purple).\n" <<
-
" " * 32 << "Constants that are pending autoload? are also shown (in yellow)"
-
opt.on :i, :ivars, "Show instance variables (in blue) and class variables (in bright blue)"
-
opt.on :G, :grep, "Filter output by regular expression", :argument => true
-
-
if jruby?
-
opt.on :J, "all-java", "Show all the aliases for methods from java (default is to show only prettiest)"
-
end
-
end
-
-
# Exclude -q, -v and --grep because they,
-
# don't specify what the user wants to see.
-
1
def no_user_opts?
-
!(opts[:methods] || opts['instance-methods'] || opts[:ppp] ||
-
opts[:globals] || opts[:locals] || opts[:constants] || opts[:ivars])
-
end
-
-
1
def process
-
@interrogatee = args.empty? ? target_self : target.eval(args.join(' '))
-
raise_errors_if_arguments_are_weird
-
ls_entity = LsEntity.new({
-
:interrogatee => @interrogatee,
-
:no_user_opts => no_user_opts?,
-
:opts => opts,
-
:args => args,
-
:_pry_ => _pry_
-
})
-
-
_pry_.pager.page ls_entity.entities_table
-
end
-
-
1
private
-
-
1
def error_list
-
any_args = args.any?
-
non_mod_interrogatee = !(Module === @interrogatee)
-
[
-
['-l does not make sense with a specified Object', :locals, any_args],
-
['-g does not make sense with a specified Object', :globals, any_args],
-
['-q does not make sense with -v', :quiet, opts.present?(:verbose)],
-
['-M only makes sense with a Module or a Class', 'instance-methods', non_mod_interrogatee],
-
['-c only makes sense with a Module or a Class', :constants, any_args && non_mod_interrogatee]
-
]
-
end
-
-
1
def raise_errors_if_arguments_are_weird
-
error_list.each do |message, option, invalid_expr|
-
raise Pry::CommandError, message if opts.present?(option) && invalid_expr
-
end
-
end
-
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::Ls)
-
end
-
1
require 'pry/commands/ls/interrogatable'
-
-
1
class Pry
-
1
class Command::Ls < Pry::ClassCommand
-
1
class Constants < Pry::Command::Ls::Formatter
-
1
include Pry::Command::Ls::Interrogatable
-
-
-
1
def initialize(interrogatee, no_user_opts, opts, _pry_)
-
super(_pry_)
-
@interrogatee = interrogatee
-
@no_user_opts = no_user_opts
-
@default_switch = opts[:constants]
-
@verbose_switch = opts[:verbose]
-
end
-
-
1
def correct_opts?
-
super || (@no_user_opts && interrogating_a_module?)
-
end
-
-
1
def output_self
-
mod = interrogatee_mod
-
constants = WrappedModule.new(mod).constants(@verbose_switch)
-
output_section('constants', grep.regexp[format(mod, constants)])
-
end
-
-
1
private
-
-
1
def format(mod, constants)
-
constants.sort_by(&:downcase).map do |name|
-
if const = (!mod.autoload?(name) && (mod.const_get(name) || true) rescue nil)
-
if (const < Exception rescue false)
-
color(:exception_constant, name)
-
elsif (Module === mod.const_get(name) rescue false)
-
color(:class_constant, name)
-
else
-
color(:constant, name)
-
end
-
else
-
color(:unloaded_constant, name)
-
end
-
end
-
end
-
-
end
-
end
-
end
-
1
class Pry
-
1
class Command::Ls < Pry::ClassCommand
-
1
class Formatter
-
1
attr_writer :grep
-
1
attr_reader :_pry_
-
-
1
def initialize(_pry_)
-
@_pry_ = _pry_
-
@target = _pry_.current_context
-
end
-
-
1
def write_out
-
return false unless correct_opts?
-
output_self
-
end
-
-
1
private
-
-
1
def color(type, str)
-
Pry::Helpers::Text.send _pry_.config.ls["#{type}_color"], str
-
end
-
-
# Add a new section to the output.
-
# Outputs nothing if the section would be empty.
-
1
def output_section(heading, body)
-
return '' if body.compact.empty?
-
fancy_heading = Pry::Helpers::Text.bold(color(:heading, heading))
-
Pry::Helpers.tablify_or_one_line(fancy_heading, body)
-
end
-
-
1
def format_value(value)
-
Pry::ColorPrinter.pp(value, '')
-
end
-
-
1
def correct_opts?
-
@default_switch
-
end
-
-
1
def output_self
-
raise NotImplementedError
-
end
-
-
1
def grep
-
@grep || proc { |x| x }
-
end
-
-
end
-
end
-
end
-
1
class Pry
-
1
class Command::Ls < Pry::ClassCommand
-
1
class Globals < Pry::Command::Ls::Formatter
-
-
# Taken from "puts global_variables.inspect".
-
1
BUILTIN_GLOBALS =
-
%w($" $$ $* $, $-0 $-F $-I $-K $-W $-a $-d $-i $-l $-p $-v $-w $. $/ $\\
-
$: $; $< $= $> $0 $ARGV $CONSOLE $DEBUG $DEFAULT_INPUT $DEFAULT_OUTPUT
-
$FIELD_SEPARATOR $FILENAME $FS $IGNORECASE $INPUT_LINE_NUMBER
-
$INPUT_RECORD_SEPARATOR $KCODE $LOADED_FEATURES $LOAD_PATH $NR $OFS
-
$ORS $OUTPUT_FIELD_SEPARATOR $OUTPUT_RECORD_SEPARATOR $PID $PROCESS_ID
-
$PROGRAM_NAME $RS $VERBOSE $deferr $defout $stderr $stdin $stdout)
-
-
# `$SAFE` and `$?` are thread-local, the exception stuff only works in a
-
# rescue clause, everything else is basically a local variable with a `$`
-
# in its name.
-
1
PSEUDO_GLOBALS =
-
%w($! $' $& $` $@ $? $+ $_ $~ $1 $2 $3 $4 $5 $6 $7 $8 $9
-
$CHILD_STATUS $SAFE $ERROR_INFO $ERROR_POSITION $LAST_MATCH_INFO
-
$LAST_PAREN_MATCH $LAST_READ_LINE $MATCH $POSTMATCH $PREMATCH)
-
-
1
def initialize(opts, _pry_)
-
super(_pry_)
-
@default_switch = opts[:globals]
-
end
-
-
1
def output_self
-
variables = format(@target.eval('global_variables'))
-
output_section('global variables', grep.regexp[variables])
-
end
-
-
1
private
-
-
1
def format(globals)
-
globals.map(&:to_s).sort_by(&:downcase).map do |name|
-
if PSEUDO_GLOBALS.include?(name)
-
color(:pseudo_global, name)
-
elsif BUILTIN_GLOBALS.include?(name)
-
color(:builtin_global, name)
-
else
-
color(:global_var, name)
-
end
-
end
-
end
-
-
end
-
end
-
end
-
1
class Pry
-
1
class Command::Ls < Pry::ClassCommand
-
1
class Grep
-
-
1
def initialize(grep_regexp)
-
@grep_regexp = grep_regexp
-
end
-
-
1
def regexp
-
proc { |x|
-
if x.instance_of?(Array)
-
x.grep(@grep_regexp)
-
else
-
x =~ @grep_regexp
-
end
-
}
-
end
-
-
end
-
end
-
end
-
1
require 'pry/commands/ls/interrogatable'
-
-
1
class Pry
-
1
class Command::Ls < Pry::ClassCommand
-
1
class InstanceVars < Pry::Command::Ls::Formatter
-
1
include Pry::Command::Ls::Interrogatable
-
-
1
def initialize(interrogatee, no_user_opts, opts, _pry_)
-
super(_pry_)
-
@interrogatee = interrogatee
-
@no_user_opts = no_user_opts
-
@default_switch = opts[:ivars]
-
end
-
-
1
def correct_opts?
-
super || @no_user_opts
-
end
-
-
1
def output_self
-
ivars = if Object === @interrogatee
-
Pry::Method.safe_send(@interrogatee, :instance_variables)
-
else
-
[] #TODO: BasicObject support
-
end
-
kvars = Pry::Method.safe_send(interrogatee_mod, :class_variables)
-
ivars_out = output_section('instance variables', format(:instance_var, ivars))
-
kvars_out = output_section('class variables', format(:class_var, kvars))
-
ivars_out + kvars_out
-
end
-
-
1
private
-
-
1
def format(type, vars)
-
vars.sort_by { |var| var.to_s.downcase }.map { |var| color(type, var) }
-
end
-
-
end
-
end
-
end
-
1
module Pry::Command::Ls::Interrogatable
-
-
1
private
-
-
1
def interrogating_a_module?
-
Module === @interrogatee
-
end
-
-
1
def interrogatee_mod
-
if interrogating_a_module?
-
@interrogatee
-
else
-
singleton = Pry::Method.singleton_class_of(@interrogatee)
-
singleton.ancestors.grep(::Class).reject { |c| c == singleton }.first
-
end
-
end
-
-
end
-
1
module Pry::Command::Ls::JRubyHacks
-
-
1
private
-
-
# JRuby creates lots of aliases for methods imported from java in an attempt
-
# to make life easier for ruby programmers. (e.g. getFooBar becomes
-
# get_foo_bar and foo_bar, and maybe foo_bar? if it returns a Boolean). The
-
# full transformations are in the assignAliases method of:
-
# https://github.com/jruby/jruby/blob/master/src/org/jruby/javasupport/JavaClass.java
-
#
-
# This has the unfortunate side-effect of making the output of ls even more
-
# incredibly verbose than it normally would be for these objects; and so we
-
# filter out all but the nicest of these aliases here.
-
#
-
# TODO: This is a little bit vague, better heuristics could be used.
-
# JRuby also has a lot of scala-specific logic, which we don't copy.
-
1
def trim_jruby_aliases(methods)
-
grouped = methods.group_by do |m|
-
m.name.sub(/\A(is|get|set)(?=[A-Z_])/, '').gsub(/[_?=]/, '').downcase
-
end
-
-
grouped.map do |key, values|
-
values = values.sort_by do |m|
-
rubbishness(m.name)
-
end
-
-
found = []
-
values.select do |x|
-
(!found.any? { |y| x == y }) && found << x
-
end
-
end.flatten(1)
-
end
-
-
# When removing jruby aliases, we want to keep the alias that is
-
# "least rubbish" according to this metric.
-
1
def rubbishness(name)
-
name.each_char.map { |x|
-
case x
-
when /[A-Z]/
-
1
-
when '?', '=', '!'
-
-2
-
else
-
0
-
end
-
}.inject(&:+) + (name.size / 100.0)
-
end
-
-
end
-
1
class Pry
-
1
class Command::Ls < Pry::ClassCommand
-
1
class LocalNames < Pry::Command::Ls::Formatter
-
-
1
def initialize(no_user_opts, args, _pry_)
-
super(_pry_)
-
@no_user_opts = no_user_opts
-
@args = args
-
@sticky_locals = _pry_.sticky_locals
-
end
-
-
1
def correct_opts?
-
super || (@no_user_opts && @args.empty?)
-
end
-
-
1
def output_self
-
local_vars = grep.regexp[@target.eval('local_variables')]
-
output_section('locals', format(local_vars))
-
end
-
-
1
private
-
-
1
def format(locals)
-
locals.sort_by(&:downcase).map do |name|
-
if @sticky_locals.include?(name.to_sym)
-
color(:pry_var, name)
-
else
-
color(:local_var, name)
-
end
-
end
-
end
-
-
end
-
end
-
end
-
1
class Pry
-
1
class Command::Ls < Pry::ClassCommand
-
1
class LocalVars < Pry::Command::Ls::Formatter
-
-
1
def initialize(opts, _pry_)
-
super(_pry_)
-
@default_switch = opts[:locals]
-
@sticky_locals = _pry_.sticky_locals
-
end
-
-
1
def output_self
-
name_value_pairs = @target.eval('local_variables').reject { |e|
-
@sticky_locals.keys.include?(e.to_sym)
-
}.map { |name|
-
[name, (@target.eval(name.to_s))]
-
}
-
format(name_value_pairs).join('')
-
end
-
-
1
private
-
-
1
def format(name_value_pairs)
-
name_value_pairs.sort_by { |name, value|
-
value.to_s.size
-
}.reverse.map { |name, value|
-
colorized_assignment_style(name, format_value(value))
-
}
-
end
-
-
1
def colorized_assignment_style(lhs, rhs, desired_width = 7)
-
colorized_lhs = color(:local_var, lhs)
-
color_escape_padding = colorized_lhs.size - lhs.size
-
pad = desired_width + color_escape_padding
-
"%-#{pad}s = %s" % [color(:local_var, colorized_lhs), rhs]
-
end
-
-
end
-
end
-
end
-
1
require 'pry/commands/ls/grep'
-
1
require 'pry/commands/ls/formatter'
-
1
require 'pry/commands/ls/globals'
-
1
require 'pry/commands/ls/constants'
-
1
require 'pry/commands/ls/methods'
-
1
require 'pry/commands/ls/self_methods'
-
1
require 'pry/commands/ls/instance_vars'
-
1
require 'pry/commands/ls/local_names'
-
1
require 'pry/commands/ls/local_vars'
-
-
1
class Pry
-
1
class Command::Ls < Pry::ClassCommand
-
-
1
class LsEntity
-
1
attr_reader :_pry_
-
-
1
def initialize(opts)
-
@interrogatee = opts[:interrogatee]
-
@no_user_opts = opts[:no_user_opts]
-
@opts = opts[:opts]
-
@args = opts[:args]
-
@grep = Grep.new(Regexp.new(opts[:opts][:G] || '.'))
-
@_pry_ = opts.delete(:_pry_)
-
end
-
-
1
def entities_table
-
entities.map(&:write_out).reject { |o| !o }.join('')
-
end
-
-
1
private
-
-
1
def grep(entity)
-
entity.tap { |o| o.grep = @grep }
-
end
-
-
1
def globals
-
grep Globals.new(@opts, _pry_)
-
end
-
-
1
def constants
-
grep Constants.new(@interrogatee, @no_user_opts, @opts, _pry_)
-
end
-
-
1
def methods
-
grep(Methods.new(@interrogatee, @no_user_opts, @opts, _pry_))
-
end
-
-
1
def self_methods
-
grep SelfMethods.new(@interrogatee, @no_user_opts, @opts, _pry_)
-
end
-
-
1
def instance_vars
-
grep InstanceVars.new(@interrogatee, @no_user_opts, @opts, _pry_)
-
end
-
-
1
def local_names
-
grep LocalNames.new(@no_user_opts, @args, _pry_)
-
end
-
-
1
def local_vars
-
LocalVars.new(@opts, _pry_)
-
end
-
-
1
def entities
-
[globals, constants, methods, self_methods, instance_vars, local_names,
-
local_vars]
-
end
-
end
-
end
-
end
-
1
require 'pry/commands/ls/methods_helper'
-
1
require 'pry/commands/ls/interrogatable'
-
-
1
class Pry
-
1
class Command::Ls < Pry::ClassCommand
-
1
class Methods < Pry::Command::Ls::Formatter
-
-
1
include Pry::Command::Ls::Interrogatable
-
1
include Pry::Command::Ls::MethodsHelper
-
-
1
def initialize(interrogatee, no_user_opts, opts, _pry_)
-
super(_pry_)
-
@interrogatee = interrogatee
-
@no_user_opts = no_user_opts
-
@default_switch = opts[:methods]
-
@instance_methods_switch = opts['instance-methods']
-
@ppp_switch = opts[:ppp]
-
@jruby_switch = opts['all-java']
-
@quiet_switch = opts[:quiet]
-
@verbose_switch = opts[:verbose]
-
end
-
-
1
def output_self
-
methods = all_methods.group_by(&:owner)
-
# Reverse the resolution order so that the most useful information
-
# appears right by the prompt.
-
resolution_order.take_while(&below_ceiling).reverse.map do |klass|
-
methods_here = (methods[klass] || []).select { |m| grep.regexp[m.name] }
-
heading = "#{ Pry::WrappedModule.new(klass).method_prefix }methods"
-
output_section(heading, format(methods_here))
-
end.join('')
-
end
-
-
1
private
-
-
1
def correct_opts?
-
super || @instance_methods_switch || @ppp_switch || @no_user_opts
-
end
-
-
-
# Get a lambda that can be used with `take_while` to prevent over-eager
-
# traversal of the Object's ancestry graph.
-
1
def below_ceiling
-
ceiling = if @quiet_switch
-
[Pry::Method.safe_send(interrogatee_mod, :ancestors)[1]] +
-
_pry_.config.ls.ceiling
-
elsif @verbose_switch
-
[]
-
else
-
_pry_.config.ls.ceiling.dup
-
end
-
lambda { |klass| !ceiling.include?(klass) }
-
end
-
-
end
-
end
-
end
-
1
require 'pry/commands/ls/jruby_hacks'
-
-
1
module Pry::Command::Ls::MethodsHelper
-
-
1
include Pry::Command::Ls::JRubyHacks
-
-
1
private
-
-
# Get all the methods that we'll want to output.
-
1
def all_methods(instance_methods = false)
-
methods = if instance_methods || @instance_methods_switch
-
Pry::Method.all_from_class(@interrogatee)
-
else
-
Pry::Method.all_from_obj(@interrogatee)
-
end
-
-
if Pry::Helpers::BaseHelpers.jruby? && !@jruby_switch
-
methods = trim_jruby_aliases(methods)
-
end
-
-
methods.select { |method| @ppp_switch || method.visibility == :public }
-
end
-
-
1
def resolution_order
-
if @instance_methods_switch
-
Pry::Method.instance_resolution_order(@interrogatee)
-
else
-
Pry::Method.resolution_order(@interrogatee)
-
end
-
end
-
-
1
def format(methods)
-
methods.sort_by(&:name).map do |method|
-
if method.name == 'method_missing'
-
color(:method_missing, 'method_missing')
-
elsif method.visibility == :private
-
color(:private_method, method.name)
-
elsif method.visibility == :protected
-
color(:protected_method, method.name)
-
else
-
color(:public_method, method.name)
-
end
-
end
-
end
-
-
end
-
1
require 'pry/commands/ls/interrogatable'
-
1
require 'pry/commands/ls/methods_helper'
-
-
1
class Pry
-
1
class Command::Ls < Pry::ClassCommand
-
1
class SelfMethods < Pry::Command::Ls::Formatter
-
1
include Pry::Command::Ls::Interrogatable
-
1
include Pry::Command::Ls::MethodsHelper
-
-
1
def initialize(interrogatee, no_user_opts, opts, _pry_)
-
super(_pry_)
-
@interrogatee = interrogatee
-
@no_user_opts = no_user_opts
-
end
-
-
1
def output_self
-
methods = all_methods(true).select do |m|
-
m.owner == @interrogatee && grep.regexp[m.name]
-
end
-
heading = "#{ Pry::WrappedModule.new(@interrogatee).method_prefix }methods"
-
output_section(heading, format(methods))
-
end
-
-
1
private
-
-
1
def correct_opts?
-
@no_user_opts && interrogating_a_module?
-
end
-
-
end
-
end
-
end
-
1
class Pry
-
1
class Command::Nesting < Pry::ClassCommand
-
1
match 'nesting'
-
1
group 'Navigating Pry'
-
1
description 'Show nesting information.'
-
-
1
banner <<-'BANNER'
-
Show nesting information.
-
BANNER
-
-
1
def process
-
output.puts 'Nesting status:'
-
output.puts '--'
-
_pry_.binding_stack.each_with_index do |obj, level|
-
if level == 0
-
output.puts "#{level}. #{Pry.view_clip(obj.eval('self'))} (Pry top level)"
-
else
-
output.puts "#{level}. #{Pry.view_clip(obj.eval('self'))}"
-
end
-
end
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::Nesting)
-
end
-
1
class Pry
-
1
class Command::Play < Pry::ClassCommand
-
1
match 'play'
-
1
group 'Editing'
-
1
description 'Playback a string variable, method, line, or file as input.'
-
-
1
banner <<-'BANNER'
-
Usage: play [OPTIONS] [--help]
-
-
The play command enables you to replay code from files and methods as if they
-
were entered directly in the Pry REPL.
-
-
play --lines 149..153 # assumes current context
-
play -i 20 --lines 1..3 # assumes lines of the input expression at 20
-
play -o 4 # the output of of an expression at 4
-
play Pry#repl -l 1..-1 # play the contents of Pry#repl method
-
play -e 2 # play from specified line until end of valid expression
-
play hello.rb # play a file
-
play Rakefile -l 5 # play line 5 of a file
-
play -d hi # play documentation of hi method
-
play hi --open # play hi method and leave it open
-
-
https://github.com/pry/pry/wiki/User-Input#wiki-Play
-
BANNER
-
-
1
def options(opt)
-
CodeCollector.inject_options(opt)
-
-
opt.on :open, 'Plays the selected content except the last line. Useful' \
-
' for replaying methods and leaving the method definition' \
-
' "open". `amend-line` can then be used to' \
-
' modify the method.'
-
-
opt.on :e, :expression=, 'Executes until end of valid expression', :as => Integer
-
opt.on :p, :print, 'Prints executed code'
-
end
-
-
1
def process
-
@cc = CodeCollector.new(args, opts, _pry_)
-
-
perform_play
-
show_input
-
end
-
-
1
def perform_play
-
eval_string << content_after_options
-
run "fix-indent"
-
end
-
-
1
def show_input
-
if opts.present?(:print) or !Pry::Code.complete_expression?(eval_string)
-
run "show-input"
-
end
-
end
-
-
-
1
def content_after_options
-
if opts.present?(:open)
-
restrict_to_lines(content, (0..-2))
-
elsif opts.present?(:expression)
-
content_at_expression
-
else
-
content
-
end
-
end
-
-
1
def content_at_expression
-
code_object.expression_at(opts[:expression])
-
end
-
-
1
def code_object
-
Pry::Code.new(content)
-
end
-
-
1
def should_use_default_file?
-
!args.first && !opts.present?(:in) && !opts.present?(:out)
-
end
-
-
1
def content
-
if should_use_default_file?
-
file_content
-
else
-
@cc.content
-
end
-
end
-
-
# The file to play from when no code object is specified.
-
# e.g `play --lines 4..10`
-
1
def default_file
-
target.eval("__FILE__") && File.expand_path(target.eval("__FILE__"))
-
end
-
-
1
def file_content
-
if default_file && File.exists?(default_file)
-
@cc.restrict_to_lines(File.read(default_file), @cc.line_range)
-
else
-
raise CommandError, "File does not exist! File was: #{default_file.inspect}"
-
end
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::Play)
-
end
-
1
class Pry
-
1
class Command::PryBacktrace < Pry::ClassCommand
-
1
match 'pry-backtrace'
-
1
group 'Context'
-
1
description 'Show the backtrace for the Pry session.'
-
-
1
banner <<-BANNER
-
Usage: pry-backtrace [OPTIONS] [--help]
-
-
Show the backtrace for the position in the code where Pry was started. This can
-
be used to infer the behavior of the program immediately before it entered Pry,
-
just like the backtrace property of an exception.
-
-
NOTE: if you are looking for the backtrace of the most recent exception raised,
-
just type: `_ex_.backtrace` instead.
-
See: https://github.com/pry/pry/wiki/Special-Locals
-
BANNER
-
-
1
def process
-
_pry_.pager.page text.bold('Backtrace:') << "\n--\n" << _pry_.backtrace.join("\n")
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::PryBacktrace)
-
end
-
1
class Pry
-
1
class Command::Version < Pry::ClassCommand
-
1
match 'pry-version'
-
1
group 'Misc'
-
1
description 'Show Pry version.'
-
-
1
banner <<-'BANNER'
-
Show Pry version.
-
BANNER
-
-
1
def process
-
output.puts "Pry version: #{Pry::VERSION} on Ruby #{RUBY_VERSION}."
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::Version)
-
end
-
1
class Pry
-
# N.B. using a regular expresion here so that "raise-up 'foo'" does the right thing.
-
1
class Command::RaiseUp < Pry::ClassCommand
-
1
match(/raise-up(!?\b.*)/)
-
1
group 'Context'
-
1
description 'Raise an exception out of the current pry instance.'
-
1
command_options :listing => 'raise-up'
-
-
1
banner <<-BANNER
-
Raise up, like exit, allows you to quit pry. Instead of returning a value
-
however, it raises an exception. If you don't provide the exception to be
-
raised, it will use the most recent exception (in pry `_ex_`).
-
-
When called as raise-up! (with an exclamation mark), this command raises the
-
exception through any nested prys you have created by "cd"ing into objects.
-
-
raise-up "get-me-out-of-here"
-
-
# This is equivalent to the command above.
-
raise "get-me-out-of-here"
-
raise-up
-
BANNER
-
-
1
def process
-
return _pry.pager.page help if captures[0] =~ /(-h|--help)\b/
-
# Handle 'raise-up', 'raise-up "foo"', 'raise-up RuntimeError, 'farble' in a rubyesque manner
-
target.eval("_pry_.raise_up#{captures[0]}")
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::RaiseUp)
-
end
-
1
class Pry
-
1
class Command::ReloadCode < Pry::ClassCommand
-
1
match 'reload-code'
-
1
group 'Misc'
-
1
description 'Reload the source file that contains the specified code object.'
-
-
1
banner <<-'BANNER'
-
Reload the source file that contains the specified code object.
-
-
e.g reload-code MyClass#my_method #=> reload a method
-
reload-code MyClass #=> reload a class
-
reload-code my-command #=> reload a pry command
-
reload-code self #=> reload the current object
-
reload-code #=> reload the current file or object
-
BANNER
-
-
1
def process
-
if !args.empty?
-
reload_object(args.join(" "))
-
elsif internal_binding?(target)
-
reload_object("self")
-
else
-
reload_current_file
-
end
-
end
-
-
1
private
-
-
1
def current_file
-
File.expand_path target.eval("__FILE__")
-
end
-
-
1
def reload_current_file
-
if !File.exists?(current_file)
-
raise CommandError, "Current file: #{current_file} cannot be found on disk!"
-
end
-
-
load current_file
-
output.puts "The current file: #{current_file} was reloaded!"
-
end
-
-
1
def reload_object(identifier)
-
code_object = Pry::CodeObject.lookup(identifier, _pry_)
-
check_for_reloadability(code_object, identifier)
-
load code_object.source_file
-
output.puts "#{identifier} was reloaded!"
-
end
-
-
1
def check_for_reloadability(code_object, identifier)
-
if !code_object || !code_object.source_file
-
raise CommandError, "Cannot locate #{identifier}!"
-
elsif !File.exists?(code_object.source_file)
-
raise CommandError,
-
"Cannot reload #{identifier} as it has no associated file on disk. " \
-
"File found was: #{code_object.source_file}"
-
end
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::ReloadCode)
-
1
Pry::Commands.alias_command 'reload-method', 'reload-code'
-
end
-
1
class Pry
-
1
class Command::Reset < Pry::ClassCommand
-
1
match 'reset'
-
1
group 'Context'
-
1
description 'Reset the REPL to a clean state.'
-
-
1
banner <<-'BANNER'
-
Reset the REPL to a clean state.
-
BANNER
-
-
1
def process
-
output.puts 'Pry reset.'
-
exec 'pry'
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::Reset)
-
end
-
1
class Pry
-
1
class Command::Ri < Pry::ClassCommand
-
1
match 'ri'
-
1
group 'Introspection'
-
1
description 'View ri documentation.'
-
-
1
banner <<-'BANNER'
-
Usage: ri [spec]
-
-
View ri documentation. Relies on the "rdoc" gem being installed.
-
See also "show-doc" command.
-
-
ri Array#each
-
BANNER
-
-
1
def process(spec)
-
# Lazily load RI
-
require 'rdoc/ri/driver'
-
-
unless defined? RDoc::RI::PryDriver
-
-
# Subclass RI so that it formats its output nicely, and uses `lesspipe`.
-
subclass = Class.new(RDoc::RI::Driver) # the hard way.
-
-
subclass.class_eval do
-
def initialize(pager, opts)
-
@pager = pager
-
super opts
-
end
-
def page
-
paging_text = StringIO.new
-
yield paging_text
-
@pager.page(paging_text.string)
-
end
-
-
def formatter(io)
-
if @formatter_klass
-
@formatter_klass.new
-
else
-
RDoc::Markup::ToAnsi.new
-
end
-
end
-
end
-
-
RDoc::RI.const_set :PryDriver, subclass # hook it up!
-
end
-
-
# Spin-up an RI insance.
-
ri = RDoc::RI::PryDriver.new _pry_.pager, :use_stdout => true, :interactive => false
-
-
begin
-
ri.display_names [spec] # Get the documentation (finally!)
-
rescue RDoc::RI::Driver::NotFoundError => e
-
output.puts "error: '#{e.name}' not found"
-
end
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::Ri)
-
end
-
1
require 'pry/commands/code_collector'
-
-
1
class Pry
-
1
class Command::SaveFile < Pry::ClassCommand
-
1
match 'save-file'
-
1
group 'Input and Output'
-
1
description 'Export to a file using content from the REPL.'
-
-
1
banner <<-'BANNER'
-
Usage: save-file [OPTIONS] --to [FILE]
-
-
Export to a file using content from the REPL.
-
-
save-file my_method --to hello.rb
-
save-file -i 1..10 --to hello.rb --append
-
save-file show-method --to my_command.rb
-
save-file sample_file.rb --lines 2..10 --to output_file.rb
-
BANNER
-
-
1
def options(opt)
-
CodeCollector.inject_options(opt)
-
-
opt.on :to=, "Specify the output file path"
-
opt.on :a, :append, "Append output to file"
-
end
-
-
1
def process
-
@cc = CodeCollector.new(args, opts, _pry_)
-
raise CommandError, "Found no code to save." if @cc.content.empty?
-
-
if !file_name
-
display_content
-
else
-
save_file
-
end
-
end
-
-
1
def file_name
-
opts[:to] || nil
-
end
-
-
1
def save_file
-
File.open(file_name, mode) do |f|
-
f.puts @cc.content
-
end
-
output.puts "#{file_name} successfully saved"
-
end
-
-
1
def display_content
-
output.puts @cc.content
-
output.puts "\n\n--\nPlease use `--to FILE` to export to a file."
-
output.puts "No file saved!\n--"
-
end
-
-
1
def mode
-
opts.present?(:append) ? "a" : "w"
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::SaveFile)
-
end
-
1
class Pry
-
1
class Command::ShellCommand < Pry::ClassCommand
-
1
match(/\.(.*)/)
-
1
group 'Input and Output'
-
1
description "All text following a '.' is forwarded to the shell."
-
1
command_options :listing => '.<shell command>', :use_prefix => false,
-
:takes_block => true
-
-
1
banner <<-'BANNER'
-
Usage: .COMMAND_NAME
-
-
All text following a "." is forwarded to the shell.
-
-
.ls -aF
-
.uname
-
BANNER
-
-
1
def process(cmd)
-
if cmd =~ /^cd\s*(.*)/i
-
process_cd parse_destination($1)
-
else
-
pass_block(cmd)
-
if command_block
-
command_block.call `#{cmd}`
-
else
-
_pry_.config.system.call(output, cmd, _pry_)
-
end
-
end
-
end
-
-
1
private
-
-
1
def parse_destination(dest)
-
return "~" if dest.empty?
-
return dest unless dest == "-"
-
state.old_pwd || raise(CommandError, "No prior directory available")
-
end
-
-
1
def process_cd(dest)
-
state.old_pwd = Dir.pwd
-
Dir.chdir File.expand_path(dest)
-
rescue Errno::ENOENT
-
raise CommandError, "No such directory: #{dest}"
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::ShellCommand)
-
end
-
1
class Pry
-
1
class Command::ShellMode < Pry::ClassCommand
-
1
match 'shell-mode'
-
1
group 'Input and Output'
-
1
description 'Toggle shell mode. Bring in pwd prompt and file completion.'
-
-
1
banner <<-'BANNER'
-
Toggle shell mode. Bring in pwd prompt and file completion.
-
BANNER
-
-
1
def process
-
case _pry_.prompt
-
when Pry::SHELL_PROMPT
-
_pry_.pop_prompt
-
_pry_.custom_completions = _pry_.config.file_completions
-
else
-
_pry_.push_prompt Pry::SHELL_PROMPT
-
_pry_.custom_completions = _pry_.config.command_completions
-
end
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::ShellMode)
-
1
Pry::Commands.alias_command 'file-mode', 'shell-mode'
-
end
-
1
require 'pry/commands/show_info'
-
-
1
class Pry
-
1
class Command::ShowDoc < Command::ShowInfo
-
1
include Pry::Helpers::DocumentationHelpers
-
-
1
match 'show-doc'
-
1
group 'Introspection'
-
1
description 'Show the documentation for a method or class.'
-
-
1
banner <<-BANNER
-
Usage: show-doc [OPTIONS] [METH]
-
Aliases: ?
-
-
Show the documentation for a method or class. Tries instance methods first and
-
then methods by default.
-
-
show-doc hi_method # docs for hi_method
-
show-doc Pry # for Pry class
-
show-doc Pry -a # for all definitions of Pry class (all monkey patches)
-
BANNER
-
-
# The docs for code_object prepared for display.
-
1
def content_for(code_object)
-
Code.new(render_doc_markup_for(code_object),
-
start_line_for(code_object), :text).
-
with_line_numbers(use_line_numbers?).to_s
-
end
-
-
# process the markup (if necessary) and apply colors
-
1
def render_doc_markup_for(code_object)
-
docs = docs_for(code_object)
-
-
if code_object.command?
-
# command '--help' shouldn't use markup highlighting
-
docs
-
else
-
if docs.empty?
-
raise CommandError, "No docs found for: #{
-
obj_name ? obj_name : 'current context'
-
}"
-
end
-
process_comment_markup(docs)
-
end
-
end
-
-
# Return docs for the code_object, adjusting for whether the code_object
-
# has yard docs available, in which case it returns those.
-
# (note we only have to check yard docs for modules since they can
-
# have multiple docs, but methods can only be doc'd once so we
-
# dont need to check them)
-
1
def docs_for(code_object)
-
if code_object.module_with_yard_docs?
-
# yard docs
-
code_object.yard_doc
-
else
-
# normal docs (i.e comments above method/module/command)
-
code_object.doc
-
end
-
end
-
-
# Which sections to include in the 'header', can toggle: :owner,
-
# :signature and visibility.
-
1
def header_options
-
super.merge :signature => true
-
end
-
-
# figure out start line of docs by back-calculating based on
-
# number of lines in the comment and the start line of the code_object
-
# @return [Fixnum] start line of docs
-
1
def start_line_for(code_object)
-
if code_object.command? || opts.present?(:'base-one')
-
1
-
else
-
code_object.source_line.nil? ? 1 :
-
(code_object.source_line - code_object.doc.lines.count)
-
end
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::ShowDoc)
-
1
Pry::Commands.alias_command '?', 'show-doc'
-
end
-
1
class Pry
-
1
class Command::ShowInfo < Pry::ClassCommand
-
1
extend Pry::Helpers::BaseHelpers
-
-
1
command_options :shellwords => false, :interpolate => false
-
-
1
def options(opt)
-
opt.on :s, :super, "Select the 'super' method. Can be repeated to traverse the ancestors", :as => :count
-
opt.on :l, "line-numbers", "Show line numbers"
-
opt.on :b, "base-one", "Show line numbers but start numbering at 1 (useful for `amend-line` and `play` commands)"
-
opt.on :a, :all, "Show all definitions and monkeypatches of the module/class"
-
end
-
-
1
def process
-
code_object = Pry::CodeObject.lookup(obj_name, _pry_, :super => opts[:super])
-
raise CommandError, no_definition_message if !code_object
-
@original_code_object = code_object
-
-
if show_all_modules?(code_object)
-
# show all monkey patches for a module
-
-
result = content_and_headers_for_all_module_candidates(code_object)
-
else
-
# show a specific code object
-
co = code_object_with_accessible_source(code_object)
-
result = content_and_header_for_code_object(co)
-
end
-
-
set_file_and_dir_locals(code_object.source_file)
-
_pry_.pager.page result
-
end
-
-
# This method checks whether the `code_object` is a WrappedModule,
-
# if it is, then it returns the first candidate (monkeypatch) with
-
# accessible source (or docs). If `code_object` is not a WrappedModule (i.e a
-
# method or a command) then the `code_object` itself is just
-
# returned.
-
#
-
# @return [Pry::WrappedModule, Pry::Method, Pry::Command]
-
1
def code_object_with_accessible_source(code_object)
-
if code_object.is_a?(WrappedModule)
-
candidate = code_object.candidates.find(&:source)
-
if candidate
-
return candidate
-
else
-
raise CommandError, no_definition_message if !valid_superclass?(code_object)
-
-
@used_super = true
-
code_object_with_accessible_source(code_object.super)
-
end
-
else
-
code_object
-
end
-
end
-
-
1
def valid_superclass?(code_object)
-
code_object.super && code_object.super.wrapped != Object
-
end
-
-
1
def content_and_header_for_code_object(code_object)
-
header(code_object) << content_for(code_object)
-
end
-
-
1
def content_and_headers_for_all_module_candidates(mod)
-
result = "Found #{mod.number_of_candidates} candidates for `#{mod.name}` definition:\n"
-
mod.number_of_candidates.times do |v|
-
candidate = mod.candidate(v)
-
begin
-
result << "\nCandidate #{v+1}/#{mod.number_of_candidates}: #{candidate.source_file} @ line #{candidate.source_line}:\n"
-
content = content_for(candidate)
-
-
result << "Number of lines: #{content.lines.count}\n\n" << content
-
rescue Pry::RescuableException
-
result << "\nNo content found.\n"
-
next
-
end
-
end
-
result
-
end
-
-
1
def no_definition_message
-
"Couldn't locate a definition for #{obj_name}!"
-
end
-
-
# Generate a header (meta-data information) for all the code
-
# object types: methods, modules, commands, procs...
-
1
def header(code_object)
-
file_name, line_num = file_and_line_for(code_object)
-
h = "\n#{Pry::Helpers::Text.bold('From:')} #{file_name} "
-
h << code_object_header(code_object, line_num)
-
h << "\n#{Pry::Helpers::Text.bold('Number of lines:')} " <<
-
"#{content_for(code_object).lines.count}\n\n"
-
h << Helpers::Text.bold('** Warning:') << " Cannot find code for #{@original_code_object.nonblank_name}. Showing superclass #{code_object.nonblank_name} instead. **\n\n" if @used_super
-
h
-
end
-
-
1
def code_object_header(code_object, line_num)
-
if code_object.real_method_object?
-
method_header(code_object, line_num)
-
-
# It sucks we have to test for both Pry::WrappedModule and WrappedModule::Candidate,
-
# probably indicates a deep refactor needs to happen in those classes.
-
elsif code_object.is_a?(Pry::WrappedModule) || code_object.is_a?(Pry::WrappedModule::Candidate)
-
module_header(code_object, line_num)
-
else
-
""
-
end
-
end
-
-
1
def method_header(code_object, line_num)
-
h = ""
-
h << (code_object.c_method? ? "(C Method):" : "@ line #{line_num}:")
-
h << method_sections(code_object)[:owner]
-
h << method_sections(code_object)[:visibility]
-
h << method_sections(code_object)[:signature]
-
h
-
end
-
-
1
def module_header(code_object, line_num)
-
h = ""
-
h << "@ line #{line_num}:\n"
-
h << text.bold(code_object.module? ? "Module" : "Class")
-
h << " #{text.bold('name:')} #{code_object.nonblank_name}"
-
-
if code_object.number_of_candidates > 1
-
h << (text.bold("\nNumber of monkeypatches: ") << code_object.number_of_candidates.to_s)
-
h << ". Use the `-a` option to display all available monkeypatches"
-
end
-
h
-
end
-
-
1
def method_sections(code_object)
-
{
-
:owner => "\n#{text.bold("Owner:")} #{code_object.owner || "N/A"}\n",
-
:visibility => "#{text.bold("Visibility:")} #{code_object.visibility}",
-
:signature => "\n#{text.bold("Signature:")} #{code_object.signature}"
-
}.merge(header_options) { |key, old, new| (new && old).to_s }
-
end
-
-
1
def header_options
-
{
-
:owner => true,
-
:visibility => true,
-
:signature => nil
-
}
-
end
-
-
1
def show_all_modules?(code_object)
-
code_object.is_a?(Pry::WrappedModule) && opts.present?(:all)
-
end
-
-
1
def obj_name
-
@obj_name ||= args.empty? ? nil : args.join(' ')
-
end
-
-
1
def use_line_numbers?
-
opts.present?(:b) || opts.present?(:l)
-
end
-
-
1
def start_line_for(code_object)
-
if opts.present?(:'base-one')
-
1
-
else
-
code_object.source_line || 1
-
end
-
end
-
-
# takes into account possible yard docs, and returns yard_file / yard_line
-
# Also adjusts for start line of comments (using start_line_for), which it has to infer
-
# by subtracting number of lines of comment from start line of code_object
-
1
def file_and_line_for(code_object)
-
if code_object.module_with_yard_docs?
-
[code_object.yard_file, code_object.yard_line]
-
else
-
[code_object.source_file, start_line_for(code_object)]
-
end
-
end
-
-
1
def complete(input)
-
if input =~ /([^ ]*)#([a-z0-9_]*)\z/
-
prefix, search = [$1, $2]
-
methods = begin
-
Pry::Method.all_from_class(binding.eval(prefix))
-
rescue RescuableException
-
return super
-
end
-
methods.map do |method|
-
[prefix, method.name].join('#') if method.name.start_with?(search)
-
end.compact
-
else
-
super
-
end
-
end
-
end
-
end
-
1
class Pry
-
1
class Command::ShowInput < Pry::ClassCommand
-
1
match 'show-input'
-
1
group 'Editing'
-
1
description 'Show the contents of the input buffer for the current multi-line expression.'
-
-
1
banner <<-'BANNER'
-
Show the contents of the input buffer for the current multi-line expression.
-
BANNER
-
-
1
def process
-
output.puts Code.new(eval_string).with_line_numbers
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::ShowInput)
-
end
-
1
require 'pry/commands/show_info'
-
-
1
class Pry
-
1
class Command::ShowSource < Command::ShowInfo
-
1
match 'show-source'
-
1
group 'Introspection'
-
1
description 'Show the source for a method or class.'
-
-
1
banner <<-'BANNER'
-
Usage: show-source [OPTIONS] [METH|CLASS]
-
Aliases: $, show-method
-
-
Show the source for a method or class. Tries instance methods first and then
-
methods by default.
-
-
show-source hi_method
-
show-source hi_method
-
show-source Pry#rep # source for Pry#rep method
-
show-source Pry # for Pry class
-
show-source Pry -a # for all Pry class definitions (all monkey patches)
-
show-source Pry.foo -e # for class of the return value of expression `Pry.foo`
-
show-source Pry --super # for superclass of Pry (Object class)
-
-
https://github.com/pry/pry/wiki/Source-browsing#wiki-Show_method
-
BANNER
-
-
1
def options(opt)
-
opt.on :e, :eval, "evaluate the command's argument as a ruby expression and show the class its return value"
-
super(opt)
-
end
-
-
1
def process
-
if opts.present?(:e)
-
obj = target.eval(args.first)
-
self.args = Array.new(1) { Module === obj ? obj.name : obj.class.name }
-
end
-
super
-
end
-
-
# The source for code_object prepared for display.
-
1
def content_for(code_object)
-
Code.new(code_object.source, start_line_for(code_object)).
-
with_line_numbers(use_line_numbers?).highlighted
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::ShowSource)
-
1
Pry::Commands.alias_command 'show-method', 'show-source'
-
1
Pry::Commands.alias_command '$', 'show-source'
-
end
-
1
class Pry
-
1
class Command::SimplePrompt < Pry::ClassCommand
-
1
match 'simple-prompt'
-
1
group 'prompts'
-
1
description 'Toggle the simple prompt.'
-
-
1
banner <<-'BANNER'
-
Toggle the simple prompt.
-
BANNER
-
-
1
def process
-
case _pry_.prompt
-
when Pry::SIMPLE_PROMPT
-
_pry_.pop_prompt
-
else
-
_pry_.push_prompt Pry::SIMPLE_PROMPT
-
end
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::SimplePrompt)
-
end
-
1
class Pry
-
1
class Command::Stat < Pry::ClassCommand
-
1
match 'stat'
-
1
group 'Introspection'
-
1
description 'View method information and set _file_ and _dir_ locals.'
-
1
command_options :shellwords => false
-
-
1
banner <<-'BANNER'
-
Usage: stat [OPTIONS] [METH]
-
-
Show method information for method METH and set _file_ and _dir_ locals.
-
-
stat hello_method
-
BANNER
-
-
1
def options(opt)
-
method_options(opt)
-
end
-
-
1
def process
-
meth = method_object
-
aliases = meth.aliases
-
-
output.puts unindent <<-EOS
-
Method Information:
-
--
-
Name: #{meth.name}
-
Alias#{ "es" if aliases.length > 1 }: #{ aliases.any? ? aliases.join(", ") : "None." }
-
Owner: #{meth.owner ? meth.owner : "Unknown"}
-
Visibility: #{meth.visibility}
-
Type: #{meth.is_a?(::Method) ? "Bound" : "Unbound"}
-
Arity: #{meth.arity}
-
Method Signature: #{meth.signature}
-
Source Location: #{meth.source_location ? meth.source_location.join(":") : "Not found."}
-
EOS
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::Stat)
-
end
-
1
class Pry
-
1
class Command::SwitchTo < Pry::ClassCommand
-
1
match 'switch-to'
-
1
group 'Navigating Pry'
-
1
description 'Start a new subsession on a binding in the current stack.'
-
-
1
banner <<-'BANNER'
-
Start a new subsession on a binding in the current stack (numbered by nesting).
-
BANNER
-
-
1
def process(selection)
-
selection = selection.to_i
-
-
if selection < 0 || selection > _pry_.binding_stack.size - 1
-
raise CommandError, "Invalid binding index #{selection} - use `nesting` command to view valid indices."
-
else
-
Pry.start(_pry_.binding_stack[selection])
-
end
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::SwitchTo)
-
end
-
1
class Pry
-
1
class Command::ToggleColor < Pry::ClassCommand
-
1
match 'toggle-color'
-
1
group 'Misc'
-
1
description 'Toggle syntax highlighting.'
-
-
1
banner <<-'BANNER'
-
Usage: toggle-color
-
-
Toggle syntax highlighting.
-
BANNER
-
-
1
def process
-
_pry_.color = color_toggle
-
output.puts "Syntax highlighting #{_pry_.color ? "on" : "off"}"
-
end
-
-
1
def color_toggle
-
!_pry_.color
-
end
-
-
1
Pry::Commands.add_command(self)
-
end
-
end
-
1
class Pry
-
1
class Command::WatchExpression < Pry::ClassCommand
-
1
require 'pry/commands/watch_expression/expression.rb'
-
-
1
match 'watch'
-
1
group 'Context'
-
1
description 'Watch the value of an expression and print a notification whenever it changes.'
-
1
command_options :use_prefix => false
-
-
1
banner <<-'BANNER'
-
Usage: watch [EXPRESSION]
-
watch
-
watch --delete [INDEX]
-
-
watch [EXPRESSION] adds an expression to the list of those being watched.
-
It will be re-evaluated every time you hit enter in pry. If its value has
-
changed, the new value will be printed to the console.
-
-
This is useful if you are step-through debugging and want to see how
-
something changes over time. It's also useful if you're trying to write
-
a method inside pry and want to check that it gives the right answers
-
every time you redefine it.
-
-
watch on its own displays all the currently watched expressions and their
-
values, and watch --delete [INDEX] allows you to delete expressions from
-
the list being watched.
-
BANNER
-
-
1
def options(opt)
-
opt.on :d, :delete,
-
"Delete the watch expression with the given index. If no index is given; clear all watch expressions.",
-
:optional_argument => true, :as => Integer
-
opt.on :l, :list,
-
"Show all current watch expressions and their values. Calling watch with no expressions or options will also show the watch expressions."
-
end
-
-
1
def process
-
case
-
when opts.present?(:delete)
-
delete opts[:delete]
-
when opts.present?(:list) || args.empty?
-
list
-
else
-
add_hook
-
add_expression(args)
-
end
-
end
-
-
1
private
-
-
1
def expressions
-
_pry_.config.watch_expressions ||= []
-
end
-
-
1
def delete(index)
-
if index
-
output.puts "Deleting watch expression ##{index}: #{expressions[index-1]}"
-
expressions.delete_at(index-1)
-
else
-
output.puts "Deleting all watched expressions"
-
expressions.clear
-
end
-
end
-
-
1
def list
-
if expressions.empty?
-
output.puts "No watched expressions"
-
else
-
_pry_.pager.open do |pager|
-
pager.puts "Listing all watched expressions:"
-
pager.puts ""
-
expressions.each_with_index do |expr, index|
-
pager.print text.with_line_numbers(expr.to_s, index+1)
-
end
-
pager.puts ""
-
end
-
end
-
end
-
-
1
def eval_and_print_changed(output)
-
expressions.each do |expr|
-
expr.eval!
-
if expr.changed?
-
output.puts "#{text.blue "watch"}: #{expr.to_s}"
-
end
-
end
-
end
-
-
1
def add_expression(arguments)
-
expressions << Expression.new(_pry_, target, arg_string)
-
output.puts "Watching #{Code.new(arg_string).highlighted}"
-
end
-
-
1
def add_hook
-
hook = [:after_eval, :watch_expression]
-
unless _pry_.hooks.hook_exists?(*hook)
-
_pry_.hooks.add_hook(*hook) do |_, _pry_|
-
eval_and_print_changed _pry_.output
-
end
-
end
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::WatchExpression)
-
end
-
1
class Pry
-
1
class Command::WatchExpression
-
1
class Expression
-
1
attr_reader :target, :source, :value, :previous_value, :_pry_
-
-
1
def initialize(_pry_, target, source)
-
@_pry_ = _pry_
-
@target = target
-
@source = Code.new(source).strip
-
end
-
-
1
def eval!
-
@previous_value = @value
-
@value = Pry::ColorPrinter.pp(target_eval(target, source), "")
-
end
-
-
1
def to_s
-
"#{Code.new(source).highlighted.strip} => #{value}"
-
end
-
-
# Has the value of the expression changed?
-
#
-
# We use the pretty-printed string represenation to detect differences
-
# as this avoids problems with dup (causes too many differences) and == (causes too few)
-
1
def changed?
-
(value != previous_value)
-
end
-
-
1
private
-
-
1
def target_eval(target, source)
-
target.eval(source)
-
rescue => e
-
e
-
end
-
end
-
end
-
end
-
1
class Pry
-
1
class Command::Whereami < Pry::ClassCommand
-
-
1
class << self
-
1
attr_accessor :method_size_cutoff
-
end
-
-
1
@method_size_cutoff = 30
-
-
1
match 'whereami'
-
1
description 'Show code surrounding the current context.'
-
1
group 'Context'
-
-
1
banner <<-'BANNER'
-
Usage: whereami [-qn] [LINES]
-
-
Describe the current location. If you use `binding.pry` inside a method then
-
whereami will print out the source for that method.
-
-
If a number is passed, then LINES lines before and after the current line will be
-
shown instead of the method itself.
-
-
The `-q` flag can be used to suppress error messages in the case that there's
-
no code to show. This is used by pry in the default before_session hook to show
-
you when you arrive at a `binding.pry`.
-
-
The `-n` flag can be used to hide line numbers so that code can be copy/pasted
-
effectively.
-
-
When pry was started on an Object and there is no associated method, whereami
-
will instead output a brief description of the current object.
-
BANNER
-
-
1
def setup
-
@file = expand_path(target.eval('__FILE__'))
-
@line = target.eval('__LINE__')
-
@method = Pry::Method.from_binding(target)
-
end
-
-
1
def options(opt)
-
opt.on :q, :quiet, "Don't display anything in case of an error"
-
opt.on :n, :"no-line-numbers", "Do not display line numbers"
-
opt.on :m, :"method", "Show the complete source for the current method."
-
opt.on :c, :"class", "Show the complete source for the current class or module."
-
opt.on :f, :"file", "Show the complete source for the current file."
-
end
-
-
1
def code
-
@code ||= if opts.present?(:m)
-
method_code or raise CommandError, "Cannot find method code."
-
elsif opts.present?(:c)
-
class_code or raise CommandError, "Cannot find class code."
-
elsif opts.present?(:f)
-
Pry::Code.from_file(@file)
-
elsif args.any?
-
code_window
-
else
-
default_code
-
end
-
end
-
-
1
def code?
-
!!code
-
rescue MethodSource::SourceNotFoundError
-
false
-
end
-
-
1
def bad_option_combination?
-
[opts.present?(:m), opts.present?(:f),
-
opts.present?(:c), args.any?].count(true) > 1
-
end
-
-
1
def location
-
"#{@file} @ line #{@line} #{@method && @method.name_with_owner}"
-
end
-
-
1
def process
-
if bad_option_combination?
-
raise CommandError, "Only one of -m, -c, -f, and LINES may be specified."
-
end
-
-
if nothing_to_do?
-
return
-
elsif internal_binding?(target)
-
handle_internal_binding
-
return
-
end
-
-
set_file_and_dir_locals(@file)
-
-
out = "\n#{text.bold('From:')} #{location}:\n\n" <<
-
code.with_line_numbers(use_line_numbers?).with_marker(marker).highlighted << "\n"
-
-
_pry_.pager.page out
-
end
-
-
1
private
-
-
1
def nothing_to_do?
-
opts.quiet? && (internal_binding?(target) || !code?)
-
end
-
-
1
def use_line_numbers?
-
!opts.present?(:n)
-
end
-
-
1
def marker
-
!opts.present?(:n) && @line
-
end
-
-
1
def top_level?
-
target_self == Pry.main
-
end
-
-
1
def handle_internal_binding
-
if top_level?
-
output.puts "At the top level."
-
else
-
output.puts "Inside #{Pry.view_clip(target_self)}."
-
end
-
end
-
-
1
def small_method?
-
@method.source_range.count < self.class.method_size_cutoff
-
end
-
-
1
def default_code
-
if method_code && small_method?
-
method_code
-
else
-
code_window
-
end
-
end
-
-
1
def code_window
-
Pry::Code.from_file(@file).around(@line, window_size)
-
end
-
-
1
def method_code
-
return @method_code if @method_code
-
-
if valid_method?
-
@method_code = Pry::Code.from_method(@method)
-
end
-
end
-
-
# This either returns the `target_self`
-
# or it returns the class of `target_self` if `target_self` is not a class.
-
# @return [Pry::WrappedModule]
-
1
def target_class
-
target_self.is_a?(Module) ? Pry::WrappedModule(target_self) :
-
Pry::WrappedModule(target_self.class)
-
end
-
-
1
def class_code
-
return @class_code if @class_code
-
-
mod = @method ? Pry::WrappedModule(@method.owner) : target_class
-
-
idx = mod.candidates.find_index { |v| expand_path(v.source_file) == @file }
-
@class_code = idx && Pry::Code.from_module(mod, idx)
-
end
-
-
1
def valid_method?
-
@method && @method.source? && expand_path(@method.source_file) == @file &&
-
@method.source_range.include?(@line)
-
end
-
-
1
def expand_path(f)
-
return if !f
-
-
if Pry.eval_path == f
-
f
-
else
-
File.expand_path(f)
-
end
-
end
-
-
1
def window_size
-
if args.empty?
-
_pry_.config.default_window_size
-
else
-
args.first.to_i
-
end
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::Whereami)
-
1
Pry::Commands.alias_command '@', 'whereami'
-
end
-
1
class Pry
-
1
class Command::Wtf < Pry::ClassCommand
-
1
match(/wtf([?!]*)/)
-
1
group 'Context'
-
1
description 'Show the backtrace of the most recent exception.'
-
1
options :listing => 'wtf?'
-
-
1
banner <<-'BANNER'
-
Usage: wtf[?|!]
-
-
Show's a few lines of the backtrace of the most recent exception (also available
-
as `_ex_.backtrace`). If you want to see more lines, add more question marks or
-
exclamation marks.
-
-
wtf?
-
wtf?!???!?!?
-
-
# To see the entire backtrace, pass the `-v` or `--verbose` flag.
-
wtf -v
-
BANNER
-
-
1
def options(opt)
-
opt.on :v, :verbose, "Show the full backtrace"
-
end
-
-
1
def process
-
raise Pry::CommandError, "No most-recent exception" unless exception
-
-
output.puts "#{text.bold('Exception:')} #{exception.class}: #{exception}\n--"
-
if opts.verbose?
-
output.puts with_line_numbers(backtrace)
-
else
-
output.puts with_line_numbers(backtrace.first(size_of_backtrace))
-
end
-
end
-
-
1
private
-
-
1
def exception
-
_pry_.last_exception
-
end
-
-
1
def with_line_numbers(bt)
-
Pry::Code.new(bt, 0, :text).with_line_numbers.to_s
-
end
-
-
1
def backtrace
-
exception.backtrace
-
end
-
-
1
def size_of_backtrace
-
[captures[0].size, 0.5].max * 10
-
end
-
end
-
-
1
Pry::Commands.add_command(Pry::Command::Wtf)
-
end
-
1
class Pry::Config
-
1
require_relative 'config/behavior'
-
1
require_relative 'config/default'
-
1
require_relative 'config/convenience'
-
1
include Pry::Config::Behavior
-
-
1
def self.shortcuts
-
2
Convenience::SHORTCUTS
-
end
-
-
#
-
# FIXME
-
# @param [Pry::Hooks] hooks
-
#
-
1
def hooks=(hooks)
-
if hooks.is_a?(Hash)
-
warn "Hash-based hooks are now deprecated! Use a `Pry::Hooks` object " \
-
"instead! http://rubydoc.info/github/pry/pry/master/Pry/Hooks"
-
self["hooks"] = Pry::Hooks.from_hash(hooks)
-
else
-
self["hooks"] = hooks
-
end
-
end
-
end
-
1
module Pry::Config::Behavior
-
1
ASSIGNMENT = "=".freeze
-
1
NODUP = [TrueClass, FalseClass, NilClass, Symbol, Numeric, Module, Proc].freeze
-
1
INSPECT_REGEXP = /#{Regexp.escape "default=#<"}/
-
-
1
module Builder
-
1
def from_hash(hash, default = nil)
-
2
new(default).tap do |config|
-
2
config.merge!(hash)
-
end
-
end
-
end
-
-
1
def self.included(klass)
-
2
unless defined?(RESERVED_KEYS)
-
1
const_set :RESERVED_KEYS, instance_methods(false).map(&:to_s).freeze
-
end
-
2
klass.extend(Builder)
-
end
-
-
1
def initialize(default = Pry.config)
-
4
@default = default
-
4
@lookup = {}
-
end
-
-
#
-
# @return [Pry::Config::Behavior]
-
# returns the default used if a matching value for a key isn't found in self
-
#
-
1
def default
-
@default
-
end
-
-
1
def [](key)
-
3
@lookup[key.to_s]
-
end
-
-
1
def []=(key, value)
-
6
key = key.to_s
-
6
if RESERVED_KEYS.include?(key)
-
raise ArgumentError, "few things are reserved by pry, but using '#{key}' as a configuration key is."
-
end
-
6
@lookup[key] = value
-
end
-
-
1
def method_missing(name, *args, &block)
-
5
key = name.to_s
-
5
if key[-1] == ASSIGNMENT
-
1
short_key = key[0..-2]
-
1
self[short_key] = args[0]
-
4
elsif key?(key)
-
3
self[key]
-
1
elsif @default.respond_to?(name)
-
1
value = @default.public_send(name, *args, &block)
-
# FIXME: refactor Pry::Hook so that it stores config on the config object,
-
# so that we can use the normal strategy.
-
1
self[key] = value = value.dup if key == 'hooks'
-
1
value
-
else
-
nil
-
end
-
end
-
-
1
def merge!(other)
-
2
other = try_convert_to_hash(other)
-
2
raise TypeError, "unable to convert argument into a Hash" unless other
-
2
other.each do |key, value|
-
3
self[key] = value
-
end
-
end
-
-
1
def ==(other)
-
@lookup == try_convert_to_hash(other)
-
end
-
1
alias_method :eql?, :==
-
-
1
def respond_to_missing?(key, include_private=false)
-
key?(key) or @default.respond_to?(key) or super(key, include_private)
-
end
-
-
1
def key?(key)
-
4
key = key.to_s
-
4
@lookup.key?(key)
-
end
-
-
1
def clear
-
@lookup.clear
-
true
-
end
-
1
alias_method :refresh, :clear
-
-
1
def forget(key)
-
@lookup.delete(key.to_s)
-
end
-
-
1
def keys
-
@lookup.keys
-
end
-
-
1
def to_hash
-
@lookup.dup
-
end
-
1
alias_method :to_h, :to_hash
-
-
-
1
def inspect
-
key_str = keys.map { |key| "'#{key}'" }.join(",")
-
"#<#{_clip_inspect(self)} local_keys=[#{key_str}] default=#{@default.inspect}>"
-
end
-
-
1
def pretty_print(q)
-
q.text inspect[1..-1].gsub(INSPECT_REGEXP, "default=<")
-
end
-
-
1
private
-
1
def _clip_inspect(obj)
-
"#{obj.class}:0x%x" % obj.object_id << 1
-
end
-
-
1
def _dup(value)
-
if NODUP.any? { |klass| klass === value }
-
value
-
else
-
value.dup
-
end
-
end
-
-
1
def try_convert_to_hash(obj)
-
2
if Hash === obj
-
2
obj
-
elsif obj.respond_to?(:to_h)
-
obj.to_h
-
elsif obj.respond_to?(:to_hash)
-
obj.to_hash
-
else
-
nil
-
end
-
end
-
end
-
1
module Pry::Config::Convenience
-
1
SHORTCUTS = [
-
:input,
-
:output,
-
:commands,
-
:print,
-
:exception_handler,
-
:hooks,
-
:color,
-
:pager,
-
:editor,
-
:memory_size,
-
:extra_sticky_locals
-
]
-
-
-
1
def config_shortcut(*names)
-
2
names.each do |name|
-
22
reader = name
-
22
setter = "#{name}="
-
22
define_method(reader) { config.public_send(name) }
-
22
define_method(setter) { |value| config.public_send(setter, value) }
-
end
-
end
-
end
-
1
class Pry::Config::Default
-
1
include Pry::Config::Behavior
-
-
1
default = {
-
input: proc {
-
lazy_readline
-
},
-
output: proc {
-
$stdout
-
},
-
commands: proc {
-
Pry::Commands
-
},
-
prompt_name: proc {
-
Pry::DEFAULT_PROMPT_NAME
-
},
-
prompt: proc {
-
Pry::DEFAULT_PROMPT
-
},
-
prompt_safe_objects: proc {
-
Pry::DEFAULT_PROMPT_SAFE_OBJECTS
-
},
-
print: proc {
-
Pry::DEFAULT_PRINT
-
},
-
quiet: proc {
-
false
-
},
-
exception_handler: proc {
-
Pry::DEFAULT_EXCEPTION_HANDLER
-
},
-
exception_whitelist: proc {
-
Pry::DEFAULT_EXCEPTION_WHITELIST
-
},
-
hooks: proc {
-
Pry::DEFAULT_HOOKS
-
},
-
pager: proc {
-
true
-
},
-
system: proc {
-
Pry::DEFAULT_SYSTEM
-
},
-
color: proc {
-
Pry::Helpers::BaseHelpers.use_ansi_codes?
-
},
-
default_window_size: proc {
-
5
-
},
-
editor: proc {
-
Pry.default_editor_for_platform
-
}, # TODO: Pry::Platform.editor
-
should_load_rc: proc {
-
true
-
},
-
should_load_local_rc: proc {
-
true
-
},
-
should_trap_interrupts: proc {
-
Pry::Helpers::BaseHelpers.jruby?
-
}, # TODO: Pry::Platform.jruby?
-
disable_auto_reload: proc {
-
false
-
},
-
command_prefix: proc {
-
""
-
},
-
auto_indent: proc {
-
Pry::Helpers::BaseHelpers.use_ansi_codes?
-
},
-
correct_indent: proc {
-
true
-
},
-
collision_warning: proc {
-
false
-
},
-
output_prefix: proc {
-
"=> "
-
},
-
requires: proc {
-
[]
-
},
-
should_load_requires: proc {
-
true
-
},
-
should_load_plugins: proc {
-
1
true
-
},
-
windows_console_warning: proc {
-
true
-
},
-
control_d_handler: proc {
-
Pry::DEFAULT_CONTROL_D_HANDLER
-
},
-
memory_size: proc {
-
100
-
},
-
extra_sticky_locals: proc {
-
{}
-
},
-
command_completions: proc {
-
proc { commands.keys }
-
},
-
file_completions: proc {
-
proc { Dir["."] }
-
},
-
ls: proc {
-
Pry::Config.from_hash(Pry::Command::Ls::DEFAULT_OPTIONS)
-
},
-
completer: proc {
-
require "pry/input_completer"
-
Pry::InputCompleter
-
}
-
}
-
-
1
def initialize
-
1
super(nil)
-
1
configure_gist
-
1
configure_history
-
end
-
-
1
default.each do |key, value|
-
36
define_method(key) do
-
1
if default[key].equal?(value)
-
1
default[key] = instance_eval(&value)
-
end
-
1
default[key]
-
end
-
end
-
-
1
private
-
# TODO:
-
# all of this configure_* stuff is a relic of old code.
-
# we should try move this code to being command-local.
-
1
def configure_gist
-
1
self["gist"] = Pry::Config.from_hash(inspecter: proc(&:pretty_inspect))
-
end
-
-
1
def configure_history
-
1
self["history"] = Pry::Config.from_hash "should_save" => true,
-
"should_load" => true
-
1
history.file = File.expand_path("~/.pry_history") rescue nil
-
1
if history.file.nil?
-
self.should_load_rc = false
-
history.should_save = false
-
history.should_load = false
-
end
-
end
-
-
1
def lazy_readline
-
require 'readline'
-
Readline
-
rescue LoadError
-
warn "Sorry, you can't use Pry without Readline or a compatible library."
-
warn "Possible solutions:"
-
warn " * Rebuild Ruby with Readline support using `--with-readline`"
-
warn " * Use the rb-readline gem, which is a pure-Ruby port of Readline"
-
warn " * Use the pry-coolline gem, a pure-ruby alternative to Readline"
-
raise
-
end
-
end
-
1
class Pry
-
# @return [Array] Code of the method used when implementing Pry's
-
# __binding__, along with line indication to be used with instance_eval (and
-
# friends).
-
#
-
# @see Object#__binding__
-
1
BINDING_METHOD_IMPL = [<<-METHOD, __FILE__, __LINE__ + 1]
-
# Get a binding with 'self' set to self, and no locals.
-
#
-
# The default definee is determined by the context in which the
-
# definition is eval'd.
-
#
-
# Please don't call this method directly, see {__binding__}.
-
#
-
# @return [Binding]
-
def __pry__
-
binding
-
end
-
METHOD
-
end
-
-
1
class Object
-
# Start a Pry REPL on self.
-
#
-
# If `self` is a Binding then that will be used to evaluate expressions;
-
# otherwise a new binding will be created.
-
#
-
# @param [Object] object the object or binding to pry
-
# (__deprecated__, use `object.pry`)
-
# @param [Hash] hash the options hash
-
# @example With a binding
-
# binding.pry
-
# @example On any object
-
# "dummy".pry
-
# @example With options
-
# def my_method
-
# binding.pry :quiet => true
-
# end
-
# my_method()
-
# @see Pry.start
-
1
def pry(object=nil, hash={})
-
if object.nil? || Hash === object
-
Pry.start(self, object || {})
-
else
-
Pry.start(object, hash)
-
end
-
end
-
-
# Return a binding object for the receiver.
-
#
-
# The `self` of the binding is set to the current object, and it contains no
-
# local variables.
-
#
-
# The default definee (http://yugui.jp/articles/846) is set such that:
-
#
-
# * If `self` is a class or module, then new methods created in the binding
-
# will be defined in that class or module (as in `class Foo; end`).
-
# * If `self` is a normal object, then new methods created in the binding will
-
# be defined on its singleton class (as in `class << self; end`).
-
# * If `self` doesn't have a real singleton class (i.e. it is a Fixnum, Float,
-
# Symbol, nil, true, or false), then new methods will be created on the
-
# object's class (as in `self.class.class_eval{ }`)
-
#
-
# Newly created constants, including classes and modules, will also be added
-
# to the default definee.
-
#
-
# @return [Binding]
-
1
def __binding__
-
# If you ever feel like changing this method, be careful about variables
-
# that you use. They shouldn't be inserted into the binding that will
-
# eventually be returning.
-
-
# When you're cd'd into a class, methods you define should be added to it.
-
if is_a?(Module)
-
# class_eval sets both self and the default definee to this class.
-
return class_eval "binding"
-
end
-
-
unless respond_to?(:__pry__)
-
# The easiest way to check whether an object has a working singleton class
-
# is to try and define a method on it. (just checking for the presence of
-
# the singleton class gives false positives for `true` and `false`).
-
# __pry__ is just the closest method we have to hand, and using
-
# it has the nice property that we can memoize this check.
-
begin
-
# instance_eval sets the default definee to the object's singleton class
-
instance_eval(*Pry::BINDING_METHOD_IMPL)
-
-
# If we can't define methods on the Object's singleton_class. Then we fall
-
# back to setting the default definee to be the Object's class. That seems
-
# nicer than having a REPL in which you can't define methods.
-
rescue TypeError, Pry::FrozenObjectException
-
# class_eval sets the default definee to self.class
-
self.class.class_eval(*Pry::BINDING_METHOD_IMPL)
-
end
-
end
-
-
__pry__
-
end
-
end
-
-
1
class BasicObject
-
# Return a binding object for the receiver.
-
#
-
# The `self` of the binding is set to the current object, and it contains no
-
# local variables.
-
#
-
# The default definee (http://yugui.jp/articles/846) is set such that new
-
# methods defined will be added to the singleton class of the BasicObject.
-
#
-
# @return [Binding]
-
1
def __binding__
-
# BasicObjects don't have respond_to?, so we just define the method
-
# every time. As they also don't have `.freeze`, this call won't
-
# fail as it can for normal Objects.
-
(class << self; self; end).class_eval <<-EOF, __FILE__, __LINE__ + 1
-
# Get a binding with 'self' set to self, and no locals.
-
#
-
# The default definee is determined by the context in which the
-
# definition is eval'd.
-
#
-
# Please don't call this method directly, see {__binding__}.
-
#
-
# @return [Binding]
-
def __pry__
-
::Kernel.binding
-
end
-
EOF
-
self.__pry__
-
end
-
end
-
1
class Pry
-
1
class Editor
-
1
include Pry::Helpers::BaseHelpers
-
1
include Pry::Helpers::CommandHelpers
-
-
1
attr_reader :_pry_
-
-
1
def initialize(_pry_)
-
@_pry_ = _pry_
-
end
-
-
1
def edit_tempfile_with_content(initial_content, line=1)
-
temp_file do |f|
-
f.puts(initial_content)
-
f.flush
-
f.close(false)
-
invoke_editor(f.path, line, true)
-
File.read(f.path)
-
end
-
end
-
-
1
def invoke_editor(file, line, blocking=true)
-
raise CommandError, "Please set Pry.config.editor or export $VISUAL or $EDITOR" unless _pry_.config.editor
-
-
editor_invocation = build_editor_invocation_string(file, line, blocking)
-
return nil unless editor_invocation
-
-
if jruby?
-
open_editor_on_jruby(editor_invocation)
-
else
-
open_editor(editor_invocation)
-
end
-
end
-
-
1
private
-
-
# Generate the string that's used to start the editor. This includes
-
# all the flags we want as well as the file and line number we
-
# want to open at.
-
1
def build_editor_invocation_string(file, line, blocking)
-
-
if _pry_.config.editor.respond_to?(:call)
-
args = [file, line, blocking][0...(_pry_.config.editor.arity)]
-
_pry_.config.editor.call(*args)
-
else
-
sanitized_file = if windows?
-
file.gsub(/\//, '\\')
-
else
-
Shellwords.escape(file)
-
end
-
-
"#{_pry_.config.editor} #{blocking_flag_for_editor(blocking)} #{start_line_syntax_for_editor(sanitized_file, line)}"
-
end
-
end
-
-
# Start the editor running, using the calculated invocation string
-
1
def open_editor(editor_invocation)
-
# Note we dont want to use Pry.config.system here as that
-
# may be invoked non-interactively (i.e via Open4), whereas we want to
-
# ensure the editor is always interactive
-
system(*Shellwords.split(editor_invocation)) or raise CommandError, "`#{editor_invocation}` gave exit status: #{$?.exitstatus}"
-
end
-
-
# We need JRuby specific code here cos just shelling out using
-
# system() appears to be pretty broken :/
-
1
def open_editor_on_jruby(editor_invocation)
-
begin
-
require 'spoon'
-
pid = Spoon.spawnp(*Shellwords.split(editor_invocation))
-
Process.waitpid(pid)
-
rescue FFI::NotFoundError
-
system(editor_invocation)
-
end
-
end
-
-
# Some editors that run outside the terminal allow you to control whether or
-
# not to block the process from which they were launched (in this case, Pry).
-
# For those editors, return the flag that produces the desired behavior.
-
1
def blocking_flag_for_editor(blocking)
-
case editor_name
-
when /^emacsclient/
-
'--no-wait' unless blocking
-
when /^[gm]vim/
-
'--nofork' if blocking
-
when /^jedit/
-
'-wait' if blocking
-
when /^mate/, /^subl/, /^redcar/
-
'-w' if blocking
-
end
-
end
-
-
# Return the syntax for a given editor for starting the editor
-
# and moving to a particular line within that file
-
1
def start_line_syntax_for_editor(file_name, line_number)
-
# special case for 1st line
-
return file_name if line_number <= 1
-
-
case editor_name
-
when /^[gm]?vi/, /^emacs/, /^nano/, /^pico/, /^gedit/, /^kate/
-
"+#{line_number} #{file_name}"
-
when /^mate/, /^geany/
-
"-l #{line_number} #{file_name}"
-
when /^subl/
-
"#{file_name}:#{line_number}"
-
when /^uedit32/
-
"#{file_name}/#{line_number}"
-
when /^jedit/
-
"#{file_name} +line:#{line_number}"
-
when /^redcar/
-
"-l#{line_number} #{file_name}"
-
else
-
if windows?
-
"#{file_name}"
-
else
-
"+#{line_number} #{file_name}"
-
end
-
end
-
end
-
-
# Get the name of the binary that Pry.config.editor points to.
-
#
-
# This is useful for deciding which flags we pass to the editor as
-
# we can just use the program's name and ignore any absolute paths.
-
#
-
# @example
-
# Pry.config.editor="/home/conrad/bin/textmate -w"
-
# editor_name
-
# # => textmate
-
#
-
1
def editor_name
-
File.basename(_pry_.config.editor).split(" ").first
-
end
-
-
end
-
end
-
1
class Pry
-
-
# As a REPL, we often want to catch any unexpected exceptions that may have
-
# been raised; however we don't want to go overboard and prevent the user
-
# from exiting Pry when they want to.
-
1
module RescuableException
-
1
def self.===(exception)
-
case exception
-
# Catch when the user hits ^C (Interrupt < SignalException), and assume
-
# that they just wanted to stop the in-progress command (just like bash
-
# etc.)
-
when Interrupt
-
true
-
# Don't catch signals (particularly not SIGTERM) as these are unlikely
-
# to be intended for pry itself. We should also make sure that
-
# Kernel#exit works.
-
when *Pry.config.exception_whitelist
-
false
-
# All other exceptions will be caught.
-
else
-
true
-
end
-
end
-
end
-
-
# Catches SecurityErrors if $SAFE is set
-
1
module Pry::TooSafeException
-
1
def self.===(exception)
-
$SAFE > 0 && SecurityError === exception
-
end
-
end
-
-
# An Exception Tag (cf. Exceptional Ruby) that instructs Pry to show the error
-
# in a more user-friendly manner. This should be used when the exception
-
# happens within Pry itself as a direct consequence of the user typing
-
# something wrong.
-
#
-
# This allows us to distinguish between the user typing:
-
#
-
# pry(main)> def )
-
# SyntaxError: unexpected )
-
#
-
# pry(main)> method_that_evals("def )")
-
# SyntaxError: (eval):1: syntax error, unexpected ')'
-
# from ./a.rb:2 in `eval'
-
1
module UserError; end
-
-
# When we try to get a binding for an object, we try to define a method on
-
# that Object's singleton class. This doesn't work for "frozen" Object's, and
-
# the exception is just a vanilla RuntimeError.
-
1
module FrozenObjectException
-
1
def self.===(exception)
-
["can't modify frozen class/module",
-
"can't modify frozen Class",
-
].include?(exception.message)
-
end
-
end
-
-
# Don't catch these exceptions
-
1
DEFAULT_EXCEPTION_WHITELIST = [SystemExit,
-
SignalException,
-
Pry::TooSafeException]
-
-
# CommandErrors are caught by the REPL loop and displayed to the user. They
-
# indicate an exceptional condition that's fatal to the current command.
-
1
class CommandError < StandardError; end
-
1
class MethodNotFound < CommandError; end
-
-
# indicates obsolete API
-
1
class ObsoleteError < StandardError; end
-
-
# This is to keep from breaking under Rails 3.2 for people who are doing that
-
# IRB = Pry thing.
-
1
module ExtendCommandBundle
-
end
-
-
end
-
1
require "pry/helpers/base_helpers"
-
1
require "pry/helpers/options_helpers"
-
1
require "pry/helpers/command_helpers"
-
1
require "pry/helpers/text"
-
1
require "pry/helpers/table"
-
1
class Pry
-
1
module Helpers
-
-
1
module BaseHelpers
-
-
1
module_function
-
-
1
def silence_warnings
-
old_verbose = $VERBOSE
-
$VERBOSE = nil
-
begin
-
yield
-
ensure
-
$VERBOSE = old_verbose
-
end
-
end
-
-
# Acts like send but ignores any methods defined below Object or Class in the
-
# inheritance hierarchy.
-
# This is required to introspect methods on objects like Net::HTTP::Get that
-
# have overridden the `method` method.
-
1
def safe_send(obj, method, *args, &block)
-
(Module === obj ? Module : Object).instance_method(method).bind(obj).call(*args, &block)
-
end
-
1
public :safe_send
-
-
1
def find_command(name, set = Pry::Commands)
-
command_match = set.find do |_, command|
-
(listing = command.options[:listing]) == name && listing != nil
-
end
-
command_match.last if command_match
-
end
-
-
1
def not_a_real_file?(file)
-
file =~ /(\(.*\))|<.*>/ || file =~ /__unknown__/ || file == "" || file == "-e"
-
end
-
-
1
def command_dependencies_met?(options)
-
return true if !options[:requires_gem]
-
Array(options[:requires_gem]).all? do |g|
-
Rubygem.installed?(g)
-
end
-
end
-
-
1
def use_ansi_codes?
-
windows_ansi? || ENV['TERM'] && ENV['TERM'] != "dumb"
-
end
-
-
1
def colorize_code(code)
-
CodeRay.scan(code, :ruby).term
-
end
-
-
1
def highlight(string, regexp, highlight_color=:bright_yellow)
-
string.gsub(regexp) { |match| "<#{highlight_color}>#{match}</#{highlight_color}>" }
-
end
-
-
# formatting
-
1
def heading(text)
-
text = "#{text}\n--"
-
"\e[1m#{text}\e[0m"
-
end
-
-
# have fun on the Windows platform.
-
1
def windows?
-
RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
-
end
-
-
# are we able to use ansi on windows?
-
1
def windows_ansi?
-
defined?(Win32::Console) || ENV['ANSICON'] || (windows? && mri_2?)
-
end
-
-
1
def jruby?
-
RbConfig::CONFIG['ruby_install_name'] == 'jruby'
-
end
-
-
1
def jruby_19?
-
jruby? && RbConfig::CONFIG['ruby_version'] == '1.9'
-
end
-
-
1
def rbx?
-
RbConfig::CONFIG['ruby_install_name'] == 'rbx'
-
end
-
-
1
def mri?
-
RbConfig::CONFIG['ruby_install_name'] == 'ruby'
-
end
-
-
1
def mri_19?
-
mri? && RUBY_VERSION =~ /^1\.9/
-
end
-
-
1
def mri_2?
-
mri? && RUBY_VERSION =~ /^2/
-
end
-
-
1
def mri_20?
-
mri? && RUBY_VERSION =~ /^2\.0/
-
end
-
-
1
def mri_21?
-
mri? && RUBY_VERSION =~ /^2\.1/
-
end
-
-
# Send the given text through the best available pager (if Pry.config.pager is
-
# enabled). Infers where to send the output if used as a mixin.
-
# DEPRECATED.
-
1
def stagger_output(text, out = nil)
-
Pry.new.pager.page text
-
end
-
end
-
end
-
end
-
1
class Pry
-
1
module Helpers
-
-
1
module CommandHelpers
-
1
include OptionsHelpers
-
-
1
module_function
-
-
# Open a temp file and yield it to the block, closing it after
-
# @return [String] The path of the temp file
-
1
def temp_file(ext='.rb')
-
file = Tempfile.new(['pry', ext])
-
yield file
-
ensure
-
file.close(true) if file
-
end
-
-
1
def internal_binding?(target)
-
m = target.eval("::Kernel.__method__").to_s
-
# class_eval is here because of http://jira.codehaus.org/browse/JRUBY-6753
-
["__binding__", "__pry__", "class_eval"].include?(m)
-
end
-
-
1
def get_method_or_raise(name, target, opts={}, omit_help=false)
-
meth = Pry::Method.from_str(name, target, opts)
-
-
if name && !meth
-
command_error("The method '#{name}' could not be found.", omit_help, MethodNotFound)
-
end
-
-
(opts[:super] || 0).times do
-
if meth.super
-
meth = meth.super
-
else
-
command_error("'#{meth.name_with_owner}' has no super method.", omit_help, MethodNotFound)
-
end
-
end
-
-
if !meth || (!name && internal_binding?(target))
-
command_error("No method name given, and context is not a method.", omit_help, MethodNotFound)
-
end
-
-
set_file_and_dir_locals(meth.source_file)
-
meth
-
end
-
-
1
def command_error(message, omit_help, klass=CommandError)
-
message += " Type `#{command_name} --help` for help." unless omit_help
-
raise klass, message
-
end
-
-
# Remove any common leading whitespace from every line in `text`.
-
#
-
# This can be used to make a HEREDOC line up with the left margin, without
-
# sacrificing the indentation level of the source code.
-
#
-
# e.g.
-
# opt.banner unindent <<-USAGE
-
# Lorem ipsum dolor sit amet, consectetur adipisicing elit,
-
# sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
-
# "Ut enim ad minim veniam."
-
# USAGE
-
#
-
# Heavily based on textwrap.dedent from Python, which is:
-
# Copyright (C) 1999-2001 Gregory P. Ward.
-
# Copyright (C) 2002, 2003 Python Software Foundation.
-
# Written by Greg Ward <gward@python.net>
-
#
-
# Licensed under <http://docs.python.org/license.html>
-
# From <http://hg.python.org/cpython/file/6b9f0a6efaeb/Lib/textwrap.py>
-
#
-
# @param [String] text The text from which to remove indentation
-
# @return [String] The text with indentation stripped.
-
1
def unindent(text, left_padding = 0)
-
# Empty blank lines
-
text = text.sub(/^[ \t]+$/, '')
-
-
# Find the longest common whitespace to all indented lines
-
# Ignore lines containing just -- or ++ as these seem to be used by
-
# comment authors as delimeters.
-
margin = text.scan(/^[ \t]*(?!--\n|\+\+\n)(?=[^ \t\n])/).inject do |current_margin, next_indent|
-
if next_indent.start_with?(current_margin)
-
current_margin
-
elsif current_margin.start_with?(next_indent)
-
next_indent
-
else
-
""
-
end
-
end
-
-
text.gsub(/^#{margin}/, ' ' * left_padding)
-
end
-
-
# Restrict a string to the given range of lines (1-indexed)
-
# @param [String] content The string.
-
# @param [Range, Fixnum] lines The line(s) to restrict it to.
-
# @return [String] The resulting string.
-
1
def restrict_to_lines(content, lines)
-
line_range = one_index_range_or_number(lines)
-
Array(content.lines.to_a[line_range]).join
-
end
-
-
1
def one_index_number(line_number)
-
if line_number > 0
-
line_number - 1
-
else
-
line_number
-
end
-
end
-
-
# convert a 1-index range to a 0-indexed one
-
1
def one_index_range(range)
-
Range.new(one_index_number(range.begin), one_index_number(range.end))
-
end
-
-
1
def one_index_range_or_number(range_or_number)
-
case range_or_number
-
when Range
-
one_index_range(range_or_number)
-
else
-
one_index_number(range_or_number)
-
end
-
end
-
-
1
def absolute_index_number(line_number, array_length)
-
if line_number >= 0
-
line_number
-
else
-
[array_length + line_number, 0].max
-
end
-
end
-
-
1
def absolute_index_range(range_or_number, array_length)
-
case range_or_number
-
when Range
-
a = absolute_index_number(range_or_number.begin, array_length)
-
b = absolute_index_number(range_or_number.end, array_length)
-
else
-
a = b = absolute_index_number(range_or_number, array_length)
-
end
-
-
Range.new(a, b)
-
end
-
-
1
def set_file_and_dir_locals(file_name, _pry_=_pry_(), target=target())
-
return if !target or !file_name
-
_pry_.last_file = File.expand_path(file_name)
-
_pry_.inject_local("_file_", _pry_.last_file, target)
-
-
_pry_.last_dir = File.dirname(_pry_.last_file)
-
_pry_.inject_local("_dir_", _pry_.last_dir, target)
-
end
-
end
-
-
end
-
end
-
1
class Pry
-
1
module Helpers
-
-
# This class contains methods useful for extracting
-
# documentation from methods and classes.
-
1
module DocumentationHelpers
-
-
1
module_function
-
-
1
def process_rdoc(comment)
-
comment = comment.dup
-
comment.gsub(/<code>(?:\s*\n)?(.*?)\s*<\/code>/m) { CodeRay.scan($1, :ruby).term }.
-
gsub(/<em>(?:\s*\n)?(.*?)\s*<\/em>/m) { "\e[1m#{$1}\e[0m" }.
-
gsub(/<i>(?:\s*\n)?(.*?)\s*<\/i>/m) { "\e[1m#{$1}\e[0m" }.
-
gsub(/\B\+(\w+?)\+\B/) { "\e[32m#{$1}\e[0m" }.
-
gsub(/((?:^[ \t]+.+(?:\n+|\Z))+)/) { CodeRay.scan($1, :ruby).term }.
-
gsub(/`(?:\s*\n)?([^\e]*?)\s*`/) { "`#{CodeRay.scan($1, :ruby).term}`" }
-
end
-
-
1
def process_yardoc_tag(comment, tag)
-
in_tag_block = nil
-
comment.lines.map do |v|
-
if in_tag_block && v !~ /^\S/
-
Pry::Helpers::Text.strip_color Pry::Helpers::Text.strip_color(v)
-
elsif in_tag_block
-
in_tag_block = false
-
v
-
else
-
in_tag_block = true if v =~ /^@#{tag}/
-
v
-
end
-
end.join
-
end
-
-
1
def process_yardoc(comment)
-
yard_tags = ["param", "return", "option", "yield", "attr", "attr_reader", "attr_writer",
-
"deprecate", "example", "raise"]
-
(yard_tags - ["example"]).inject(comment) { |a, v| process_yardoc_tag(a, v) }.
-
gsub(/^@(#{yard_tags.join("|")})/) { "\e[33m#{$1}\e[0m" }
-
end
-
-
1
def process_comment_markup(comment)
-
process_yardoc process_rdoc(comment)
-
end
-
-
# @param [String] code
-
# @return [String]
-
1
def strip_comments_from_c_code(code)
-
code.sub(/\A\s*\/\*.*?\*\/\s*/m, '')
-
end
-
-
# Given a string that makes up a comment in a source-code file parse out the content
-
# that the user is intended to read. (i.e. without leading indentation, #-characters
-
# or shebangs)
-
#
-
# @param [String] comment
-
# @return [String]
-
1
def get_comment_content(comment)
-
comment = comment.dup
-
# Remove #!/usr/bin/ruby
-
comment.gsub!(/\A\#!.*$/, '')
-
# Remove leading empty comment lines
-
comment.gsub!(/\A\#+?$/, '')
-
comment.gsub!(/^\s*#/, '')
-
strip_leading_whitespace(comment)
-
end
-
-
# @param [String] text
-
# @return [String]
-
1
def strip_leading_whitespace(text)
-
Pry::Helpers::CommandHelpers.unindent(text)
-
end
-
end
-
end
-
end
-
1
class Pry
-
1
module Helpers
-
1
module OptionsHelpers
-
1
module_function
-
-
# Add method options to the Slop instance
-
1
def method_options(opt)
-
@method_target = target
-
opt.on :M, "instance-methods", "Operate on instance methods."
-
opt.on :m, :methods, "Operate on methods."
-
opt.on :s, :super, "Select the 'super' method. Can be repeated to traverse the ancestors.", :as => :count
-
opt.on :c, :context, "Select object context to run under.", :argument => true do |context|
-
@method_target = Pry.binding_for(target.eval(context))
-
end
-
end
-
-
# Get the method object parsed by the slop instance
-
1
def method_object
-
@method_object ||= get_method_or_raise(args.empty? ? nil : args.join(" "), @method_target,
-
:super => opts[:super],
-
:instance => opts.present?(:'instance-methods') && !opts.present?(:'methods'),
-
:methods => opts.present?(:'methods') && !opts.present?(:'instance-methods')
-
)
-
end
-
end
-
end
-
end
-
1
class Pry
-
1
module Helpers
-
1
def self.tablify_or_one_line(heading, things)
-
plain_heading = Pry::Helpers::Text.strip_color(heading)
-
attempt = Table.new(things, :column_count => things.size)
-
if attempt.fits_on_line?(Terminal.width! - plain_heading.size - 2)
-
"#{heading}: #{attempt}\n"
-
else
-
"#{heading}: \n#{tablify_to_screen_width(things, :indent => ' ')}\n"
-
end
-
end
-
-
1
def self.tablify_to_screen_width(things, options = {})
-
things = things.compact
-
if indent = options[:indent]
-
usable_width = Terminal.width! - indent.size
-
tablify(things, usable_width).to_s.gsub(/^/, indent)
-
else
-
tablify(things, Terminal.width!).to_s
-
end
-
end
-
-
1
def self.tablify(things, line_length)
-
table = Table.new(things, :column_count => things.size)
-
table.column_count -= 1 until 1 == table.column_count or
-
table.fits_on_line?(line_length)
-
table
-
end
-
-
1
class Table
-
1
attr_reader :items, :column_count
-
1
def initialize items, args = {}
-
@column_count = args[:column_count]
-
self.items = items
-
end
-
-
1
def to_s
-
rows_to_s.join("\n")
-
end
-
-
1
def rows_to_s style = :color_on
-
widths = columns.map{|e| _max_width(e)}
-
@rows_without_colors.map do |r|
-
padded = []
-
r.each_with_index do |e,i|
-
next unless e
-
item = e.ljust(widths[i])
-
item.sub! e, _recall_color_for(e) if :color_on == style
-
padded << item
-
end
-
padded.join(Pry.config.ls.separator)
-
end
-
end
-
-
1
def items= items
-
@items = items
-
_rebuild_colorless_cache
-
_recolumn
-
items
-
end
-
-
1
def column_count= n
-
@column_count = n
-
_recolumn
-
end
-
-
1
def fits_on_line? line_length
-
_max_width(rows_to_s :no_color) <= line_length
-
end
-
-
1
def columns
-
@rows_without_colors.transpose
-
end
-
-
1
def ==(other); items == other.to_a end
-
1
def to_a; items.to_a end
-
-
1
private
-
1
def _max_width(things)
-
things.compact.map(&:size).max || 0
-
end
-
-
1
def _rebuild_colorless_cache
-
@colorless_cache = {}
-
@plain_items = []
-
items.map do |e|
-
plain = Pry::Helpers::Text.strip_color(e)
-
@colorless_cache[plain] = e
-
@plain_items << plain
-
end
-
end
-
-
1
def _recolumn
-
@rows_without_colors = []
-
return if items.size.zero?
-
row_count = (items.size.to_f/column_count).ceil
-
row_count.times do |i|
-
row_indices = (0...column_count).map{|e| row_count*e+i}
-
@rows_without_colors << row_indices.map{|e| @plain_items[e]}
-
end
-
end
-
-
1
def _recall_color_for thing
-
@colorless_cache[thing]
-
end
-
end
-
-
end
-
end
-
1
class Pry
-
1
module Helpers
-
-
# The methods defined on {Text} are available to custom commands via {Pry::Command#text}.
-
1
module Text
-
-
1
COLORS =
-
{
-
"black" => 0,
-
"red" => 1,
-
"green" => 2,
-
"yellow" => 3,
-
"blue" => 4,
-
"purple" => 5,
-
"magenta" => 5,
-
"cyan" => 6,
-
"white" => 7
-
}
-
-
1
class << self
-
-
1
COLORS.each_pair do |color, value|
-
9
define_method color do |text|
-
"\033[0;#{30+value}m#{text}\033[0m"
-
end
-
-
9
define_method "bright_#{color}" do |text|
-
"\033[1;#{30+value}m#{text}\033[0m"
-
end
-
end
-
-
# Remove any color codes from _text_.
-
#
-
# @param [String, #to_s] text
-
# @return [String] _text_ stripped of any color codes.
-
1
def strip_color(text)
-
text.to_s.gsub(/\e\[.*?(\d)+m/ , '')
-
end
-
-
# Returns _text_ as bold text for use on a terminal.
-
#
-
# @param [String, #to_s] text
-
# @return [String] _text_
-
1
def bold(text)
-
"\e[1m#{text}\e[0m"
-
end
-
-
# Returns `text` in the default foreground colour.
-
# Use this instead of "black" or "white" when you mean absence of colour.
-
#
-
# @param [String, #to_s] text
-
# @return [String]
-
1
def default(text)
-
text.to_s
-
end
-
1
alias_method :bright_default, :bold
-
-
# Executes the block with `Pry.config.color` set to false.
-
# @yield
-
# @return [void]
-
1
def no_color(&block)
-
boolean = Pry.config.color
-
Pry.config.color = false
-
yield
-
ensure
-
Pry.config.color = boolean
-
end
-
-
# Executes the block with `Pry.config.pager` set to false.
-
# @yield
-
# @return [void]
-
1
def no_pager(&block)
-
boolean = Pry.config.pager
-
Pry.config.pager = false
-
yield
-
ensure
-
Pry.config.pager = boolean
-
end
-
-
# Returns _text_ in a numbered list, beginning at _offset_.
-
#
-
# @param [#each_line] text
-
# @param [Fixnum] offset
-
# @return [String]
-
1
def with_line_numbers(text, offset, color=:blue)
-
lines = text.each_line.to_a
-
max_width = (offset + lines.count).to_s.length
-
lines.each_with_index.map do |line, index|
-
adjusted_index = (index + offset).to_s.rjust(max_width)
-
"#{self.send(color, adjusted_index)}: #{line}"
-
end.join
-
end
-
-
# Returns _text_ indented by _chars_ spaces.
-
#
-
# @param [String] text
-
# @param [Fixnum] chars
-
1
def indent(text, chars)
-
text.lines.map { |l| "#{' ' * chars}#{l}" }.join
-
end
-
end
-
-
end
-
-
end
-
end
-
-
1
class Pry
-
# The History class is responsible for maintaining the user's input history,
-
# both internally and within Readline.
-
1
class History
-
1
attr_accessor :loader, :saver, :pusher, :clearer
-
-
# @return [Fixnum] Number of lines in history when Pry first loaded.
-
1
attr_reader :original_lines
-
-
1
def initialize(options={})
-
@history = []
-
@original_lines = 0
-
@file_path = options[:file_path]
-
restore_default_behavior
-
end
-
-
# Assign the default methods for loading, saving, pushing, and clearing.
-
1
def restore_default_behavior
-
Pry.config.input # force Readline to load if applicable
-
-
@loader = method(:read_from_file)
-
@saver = method(:save_to_file)
-
-
if defined?(Readline)
-
@pusher = method(:push_to_readline)
-
@clearer = method(:clear_readline)
-
else
-
@pusher = proc { }
-
@clearer = proc { }
-
end
-
end
-
-
# Load the input history using `History.loader`.
-
# @return [Integer] The number of lines loaded
-
1
def load
-
@loader.call do |line|
-
@pusher.call(line.chomp)
-
@history << line.chomp
-
@original_lines += 1
-
end
-
end
-
-
# Add a line to the input history, ignoring blank and duplicate lines.
-
# @param [String] line
-
# @return [String] The same line that was passed in
-
1
def push(line)
-
unless line.empty? || (@history.last && line == @history.last)
-
@pusher.call(line)
-
@history << line
-
@saver.call(line) if Pry.config.history.should_save
-
end
-
line
-
end
-
1
alias << push
-
-
# Clear this session's history. This won't affect the contents of the
-
# history file.
-
1
def clear
-
@clearer.call
-
@history = []
-
end
-
-
# @return [Fixnum] The number of lines in history.
-
1
def history_line_count
-
@history.count
-
end
-
-
# @return [Fixnum] The number of lines in history from just this session.
-
1
def session_line_count
-
@history.count - @original_lines
-
end
-
-
# Return an Array containing all stored history.
-
# @return [Array<String>] An Array containing all lines of history loaded
-
# or entered by the user in the current session.
-
1
def to_a
-
@history.dup
-
end
-
-
1
private
-
-
# The default loader. Yields lines from `Pry.history.config.file`.
-
1
def read_from_file
-
path = history_file_path
-
-
if File.exists?(path)
-
File.foreach(path) { |line| yield(line) }
-
end
-
rescue => error
-
warn "History file not loaded: #{error.message}"
-
end
-
-
# The default pusher. Appends the given line to Readline::HISTORY.
-
# @param [String] line
-
1
def push_to_readline(line)
-
Readline::HISTORY << line
-
end
-
-
# The default clearer. Clears Readline::HISTORY.
-
1
def clear_readline
-
Readline::HISTORY.shift until Readline::HISTORY.empty?
-
end
-
-
# The default saver. Appends the given line to `Pry.history.config.file`.
-
1
def save_to_file(line)
-
history_file.puts line if history_file
-
end
-
-
# The history file, opened for appending.
-
1
def history_file
-
if defined?(@history_file)
-
@history_file
-
else
-
@history_file = File.open(history_file_path, 'a', 0600).tap do |file|
-
file.sync = true
-
end
-
end
-
rescue Errno::EACCES
-
warn 'History not saved; unable to open your history file for writing.'
-
@history_file = false
-
end
-
-
1
def history_file_path
-
File.expand_path(@file_path || Pry.config.history.file)
-
end
-
end
-
end
-
1
class Pry
-
# A history array is an array to which you can only add elements. Older
-
# entries are removed progressively, so that the array never contains more than
-
# N elements.
-
#
-
# History arrays are used by Pry to store the output of the last commands.
-
#
-
# @example
-
# ary = Pry::HistoryArray.new 10
-
# ary << 1 << 2 << 3
-
# ary[0] # => 1
-
# ary[1] # => 2
-
# 10.times { |n| ary << n }
-
# ary[0] # => nil
-
# ary[-1] # => 9
-
1
class HistoryArray
-
1
include Enumerable
-
-
# @param [Integer] size Maximum amount of objects in the array
-
1
def initialize(size)
-
@max_size = size
-
-
@hash = {}
-
@count = 0
-
end
-
-
# Pushes an object at the end of the array
-
# @param [Object] value Object to be added
-
1
def <<(value)
-
@hash[@count] = value
-
-
if @hash.size > max_size
-
@hash.delete(@count - max_size)
-
end
-
-
@count += 1
-
-
self
-
end
-
-
# @overload [](index)
-
# @param [Integer] index Index of the item to access.
-
# @return [Object, nil] Item at that index or nil if it has been removed.
-
# @overload [](index, size)
-
# @param [Integer] index Index of the first item to access.
-
# @param [Integer] size Amount of items to access
-
# @return [Array, nil] The selected items. Nil if index is greater than
-
# the size of the array.
-
# @overload [](range)
-
# @param [Range<Integer>] range Range of indices to access.
-
# @return [Array, nil] The selected items. Nil if index is greater than
-
# the size of the array.
-
1
def [](index_or_range, size = nil)
-
if index_or_range.is_a? Integer
-
index = convert_index(index_or_range)
-
-
if size
-
end_index = index + size
-
index > @count ? nil : (index...[end_index, @count].min).map do |n|
-
@hash[n]
-
end
-
else
-
@hash[index]
-
end
-
else
-
range = convert_range(index_or_range)
-
range.begin > @count ? nil : range.map { |n| @hash[n] }
-
end
-
end
-
-
# @return [Integer] Amount of objects in the array
-
1
def size
-
@count
-
end
-
1
alias count size
-
1
alias length size
-
-
1
def empty?
-
size == 0
-
end
-
-
1
def each
-
((@count - size)...@count).each do |n|
-
yield @hash[n]
-
end
-
end
-
-
1
def to_a
-
((@count - size)...@count).map { |n| @hash[n] }
-
end
-
-
# Returns [Hash] copy of the internal @hash history
-
1
def to_h
-
@hash.dup
-
end
-
-
1
def pop!
-
@hash.delete @count - 1
-
@count -= 1
-
end
-
-
1
def inspect
-
"#<#{self.class} size=#{size} first=#{@count - size} max_size=#{max_size}>"
-
end
-
-
# @return [Integer] Maximum amount of objects in the array
-
1
attr_reader :max_size
-
-
1
private
-
1
def convert_index(n)
-
n >= 0 ? n : @count + n
-
end
-
-
1
def convert_range(range)
-
end_index = convert_index(range.end)
-
end_index += 1 unless range.exclude_end?
-
-
Range.new(convert_index(range.begin), [end_index, @count].min, true)
-
end
-
end
-
end
-
1
class Pry
-
-
# Implements a hooks system for Pry. A hook is a callable that is
-
# associated with an event. A number of events are currently
-
# provided by Pry, these include: `:when_started`, `:before_session`, `:after_session`.
-
# A hook must have a name, and is connected with an event by the
-
# `Pry::Hooks#add_hook` method.
-
# @example Adding a hook for the `:before_session` event.
-
# Pry.config.hooks.add_hook(:before_session, :say_hi) do
-
# puts "hello"
-
# end
-
1
class Hooks
-
-
# Converts a hash to a `Pry::Hooks` instance. All hooks defined
-
# this way are anonymous. This functionality is primarily to
-
# provide backwards-compatibility with the old hash-based hook
-
# system in Pry versions < 0.9.8
-
# @param [Hash] hash The hash to convert to `Pry::Hooks`.
-
# @return [Pry::Hooks] The resulting `Pry::Hooks` instance.
-
1
def self.from_hash(hash)
-
return hash if hash.instance_of?(self)
-
instance = new
-
hash.each do |k, v|
-
instance.add_hook(k, nil, v)
-
end
-
instance
-
end
-
-
1
def initialize
-
1
@hooks = {}
-
end
-
-
# Ensure that duplicates have their @hooks object
-
1
def initialize_copy(orig)
-
hooks_dup = @hooks.dup
-
@hooks.each do |k, v|
-
hooks_dup[k] = v.dup
-
end
-
-
@hooks = hooks_dup
-
end
-
-
1
def hooks
-
@hooks
-
end
-
1
protected :hooks
-
-
1
def errors
-
@errors ||= []
-
end
-
-
# Destructively merge the contents of two `Pry:Hooks` instances.
-
# @param [Pry::Hooks] other The `Pry::Hooks` instance to merge
-
# @return [Pry:Hooks] Returns the receiver.
-
# @example
-
# hooks = Pry::Hooks.new.add_hook(:before_session, :say_hi) { puts "hi!" }
-
# Pry::Hooks.new.merge!(hooks)
-
1
def merge!(other)
-
@hooks.merge!(other.dup.hooks) do |key, v1, v2|
-
merge_arrays(v1, v2)
-
end
-
-
self
-
end
-
-
1
def merge_arrays(array1, array2)
-
uniq_keeping_last(array1 + array2, &:first)
-
end
-
1
private :merge_arrays
-
-
1
def uniq_keeping_last(input, &block)
-
hash, output = {}, []
-
input.reverse.each{ |i| hash[block[i]] ||= (output.unshift i) }
-
output
-
end
-
1
private :uniq_keeping_last
-
-
# Return a new `Pry::Hooks` instance containing a merge of the contents of two `Pry:Hooks` instances,
-
# @param [Pry::Hooks] other The `Pry::Hooks` instance to merge
-
# @return [Pry::Hooks] The new hash.
-
# @example
-
# hooks = Pry::Hooks.new.add_hook(:before_session, :say_hi) { puts "hi!" }
-
# Pry::Hooks.new.merge(hooks)
-
1
def merge(other)
-
self.dup.tap do |v|
-
v.merge!(other)
-
end
-
end
-
-
# Add a new hook to be executed for the `name` even.
-
# @param [Symbol] event_name The name of the event.
-
# @param [Symbol] hook_name The name of the hook.
-
# @param [#call] callable The callable.
-
# @yield The block to use as the callable (if `callable` parameter not provided)
-
# @return [Pry:Hooks] Returns the receiver.
-
# @example
-
# Pry::Hooks.new.add_hook(:before_session, :say_hi) { puts "hi!" }
-
1
def add_hook(event_name, hook_name, callable=nil, &block)
-
1
@hooks[event_name] ||= []
-
-
# do not allow duplicates, but allow multiple `nil` hooks
-
# (anonymous hooks)
-
1
if hook_exists?(event_name, hook_name) && !hook_name.nil?
-
raise ArgumentError, "Hook with name '#{hook_name}' already defined!"
-
end
-
-
1
if !block && !callable
-
raise ArgumentError, "Must provide a block or callable."
-
end
-
-
# ensure we only have one anonymous hook
-
1
@hooks[event_name].delete_if { |h, k| h.nil? } if hook_name.nil?
-
-
1
if block
-
1
@hooks[event_name] << [hook_name, block]
-
elsif callable
-
@hooks[event_name] << [hook_name, callable]
-
end
-
-
1
self
-
end
-
-
# Execute the list of hooks for the `event_name` event.
-
# @param [Symbol] event_name The name of the event.
-
# @param [Array] args The arguments to pass to each hook function.
-
# @return [Object] The return value of the last executed hook.
-
# @example
-
# my_hooks = Pry::Hooks.new.add_hook(:before_session, :say_hi) { puts "hi!" }
-
# my_hooks.exec_hook(:before_session) #=> OUTPUT: "hi!"
-
1
def exec_hook(event_name, *args, &block)
-
@hooks[event_name] ||= []
-
-
@hooks[event_name].map do |hook_name, callable|
-
begin
-
callable.call(*args, &block)
-
rescue RescuableException => e
-
errors << e
-
e
-
end
-
end.last
-
end
-
-
# Return the number of hook functions registered for the `event_name` event.
-
# @param [Symbol] event_name The name of the event.
-
# @return [Fixnum] The number of hook functions for `event_name`.
-
# @example
-
# my_hooks = Pry::Hooks.new.add_hook(:before_session, :say_hi) { puts "hi!" }
-
# my_hooks.count(:before_session) #=> 1
-
1
def hook_count(event_name)
-
@hooks[event_name] ||= []
-
@hooks[event_name].size
-
end
-
-
# Return a specific hook for a given event.
-
# @param [Symbol] event_name The name of the event.
-
# @param [Symbol] hook_name The name of the hook
-
# @return [#call] The requested hook.
-
# @example
-
# my_hooks = Pry::Hooks.new.add_hook(:before_session, :say_hi) { puts "hi!" }
-
# my_hooks.get_hook(:before_session, :say_hi).call #=> "hi!"
-
1
def get_hook(event_name, hook_name)
-
@hooks[event_name] ||= []
-
hook = @hooks[event_name].find { |current_hook_name, callable| current_hook_name == hook_name }
-
hook.last if hook
-
end
-
-
# Return the hash of hook names / hook functions for a
-
# given event. (Note that modifying the returned hash does not
-
# alter the hooks, use add_hook/delete_hook for that).
-
# @param [Symbol] event_name The name of the event.
-
# @return [Hash] The hash of hook names / hook functions.
-
# @example
-
# my_hooks = Pry::Hooks.new.add_hook(:before_session, :say_hi) { puts "hi!" }
-
# my_hooks.get_hooks(:before_session) #=> {:say_hi=>#<Proc:0x00000101645e18@(pry):9>}
-
1
def get_hooks(event_name)
-
@hooks[event_name] ||= []
-
Hash[@hooks[event_name]]
-
end
-
-
# Delete a hook for an event.
-
# @param [Symbol] event_name The name of the event.
-
# @param [Symbol] hook_name The name of the hook.
-
# to delete.
-
# @return [#call] The deleted hook.
-
# @example
-
# my_hooks = Pry::Hooks.new.add_hook(:before_session, :say_hi) { puts "hi!" }
-
# my_hooks.delete_hook(:before_session, :say_hi)
-
1
def delete_hook(event_name, hook_name)
-
@hooks[event_name] ||= []
-
deleted_callable = nil
-
-
@hooks[event_name].delete_if do |current_hook_name, callable|
-
if current_hook_name == hook_name
-
deleted_callable = callable
-
true
-
else
-
false
-
end
-
end
-
deleted_callable
-
end
-
-
# Clear all hooks functions for a given event.
-
# @param [String] event_name The name of the event.
-
# @example
-
# my_hooks = Pry::Hooks.new.add_hook(:before_session, :say_hi) { puts "hi!" }
-
# my_hooks.delete_hook(:before_session)
-
1
def delete_hooks(event_name)
-
@hooks[event_name] = []
-
end
-
-
1
alias_method :clear, :delete_hooks
-
-
# Remove all events and hooks, clearing out the Pry::Hooks
-
# instance completely.
-
# @example
-
# my_hooks = Pry::Hooks.new.add_hook(:before_session, :say_hi) { puts "hi!" }
-
# my_hooks.clear_all
-
1
def clear_all
-
@hooks = {}
-
end
-
-
# @param [Symbol] event_name Name of the event.
-
# @param [Symbol] hook_name Name of the hook.
-
# @return [Boolean] Whether the hook by the name `hook_name`
-
1
def hook_exists?(event_name, hook_name)
-
1
!!(@hooks[event_name] && @hooks[event_name].find { |name, _| name == hook_name })
-
end
-
end
-
end
-
1
require 'coderay'
-
-
1
class Pry
-
##
-
# Pry::Indent is a class that can be used to indent a number of lines
-
# containing Ruby code similar as to how IRB does it (but better). The class
-
# works by tokenizing a string using CodeRay and then looping over those
-
# tokens. Based on the tokens in a line of code that line (or the next one)
-
# will be indented or un-indented by correctly.
-
#
-
1
class Indent
-
1
include Helpers::BaseHelpers
-
-
# Raised if {#module_nesting} would not work.
-
1
class UnparseableNestingError < StandardError; end
-
-
# @return [String] String containing the spaces to be inserted before the next line.
-
1
attr_reader :indent_level
-
-
# @return [Array<String>] The stack of open tokens.
-
1
attr_reader :stack
-
-
# The amount of spaces to insert for each indent level.
-
1
SPACES = ' '
-
-
# Hash containing all the tokens that should increase the indentation
-
# level. The keys of this hash are open tokens, the values the matching
-
# tokens that should prevent a line from being indented if they appear on
-
# the same line.
-
1
OPEN_TOKENS = {
-
'def' => 'end',
-
'class' => 'end',
-
'module' => 'end',
-
'do' => 'end',
-
'if' => 'end',
-
'unless' => 'end',
-
'while' => 'end',
-
'until' => 'end',
-
'for' => 'end',
-
'case' => 'end',
-
'begin' => 'end',
-
'[' => ']',
-
'{' => '}',
-
'(' => ')'
-
}
-
-
# Which tokens can either be open tokens, or appear as modifiers on
-
# a single-line.
-
1
SINGLELINE_TOKENS = %w(if while until unless rescue)
-
-
# Which tokens can be followed by an optional "do" keyword.
-
1
OPTIONAL_DO_TOKENS = %w(for while until)
-
-
# Collection of token types that should be ignored. Without this list
-
# keywords such as "class" inside strings would cause the code to be
-
# indented incorrectly.
-
#
-
# :pre_constant and :preserved_constant are the CodeRay 0.9.8 and 1.0.0
-
# classifications of "true", "false", and "nil".
-
1
IGNORE_TOKENS = [:space, :content, :string, :method, :ident,
-
:constant, :pre_constant, :predefined_constant]
-
-
# Tokens that indicate the end of a statement (i.e. that, if they appear
-
# directly before an "if" indicates that that if applies to the same line,
-
# not the next line)
-
#
-
# :reserved and :keywords are the CodeRay 0.9.8 and 1.0.0 respectively
-
# classifications of "super", "next", "return", etc.
-
1
STATEMENT_END_TOKENS = IGNORE_TOKENS + [:regexp, :integer, :float, :keyword,
-
:delimiter, :reserved]
-
-
# Collection of tokens that should appear dedented even though they
-
# don't affect the surrounding code.
-
1
MIDWAY_TOKENS = %w(when else elsif ensure rescue)
-
-
# Clean the indentation of a fragment of ruby.
-
#
-
# @param [String] str
-
# @return [String]
-
1
def self.indent(str)
-
new.indent(str)
-
end
-
-
# Get the module nesting at the given point in the given string.
-
#
-
# NOTE If the line specified contains a method definition, then the nesting
-
# at the start of the method definition is used. Otherwise the nesting from
-
# the end of the line is used.
-
#
-
# @param [String] str The ruby code to analyze
-
# @param [Fixnum] line_number The line number (starting from 1)
-
# @return [Array<String>]
-
1
def self.nesting_at(str, line_number)
-
indent = new
-
lines = str.split("\n")
-
n = line_number - 1
-
to_indent = lines[0...n] << (lines[n] || "").split("def").first(1)
-
indent.indent(to_indent.join("\n") << "\n")
-
indent.module_nesting
-
end
-
-
1
def initialize
-
reset
-
end
-
-
# reset internal state
-
1
def reset
-
@stack = []
-
@indent_level = ''
-
@heredoc_queue = []
-
@close_heredocs = {}
-
@string_start = nil
-
@awaiting_class = false
-
@module_nesting = []
-
self
-
end
-
-
# Indents a string and returns it. This string can either be a single line
-
# or multiple ones.
-
#
-
# @example
-
# str = <<TXT
-
# class User
-
# attr_accessor :name
-
# end
-
# TXT
-
#
-
# # This would result in the following being displayed:
-
# #
-
# # class User
-
# # attr_accessor :name
-
# # end
-
# #
-
# puts Pry::Indent.new.indent(str)
-
#
-
# @param [String] input The input string to indent.
-
# @return [String] The indented version of +input+.
-
#
-
1
def indent(input)
-
output = ''
-
prefix = indent_level
-
-
input.lines.each do |line|
-
-
if in_string?
-
tokens = tokenize("#{open_delimiters_line}\n#{line}")
-
tokens = tokens.drop_while{ |token, type| !(String === token && token.include?("\n")) }
-
previously_in_string = true
-
else
-
tokens = tokenize(line)
-
previously_in_string = false
-
end
-
-
before, after = indentation_delta(tokens)
-
-
before.times{ prefix.sub! SPACES, '' }
-
new_prefix = prefix + SPACES * after
-
-
line = prefix + line.lstrip unless previously_in_string
-
-
output += line
-
-
prefix = new_prefix
-
end
-
-
@indent_level = prefix
-
-
return output
-
end
-
-
# Get the indentation for the start of the next line.
-
#
-
# This is what's used between the prompt and the cursor in pry.
-
#
-
# @return String The correct number of spaces
-
#
-
1
def current_prefix
-
in_string? ? '' : indent_level
-
end
-
-
# Get the change in indentation indicated by the line.
-
#
-
# By convention, you remove indent from the line containing end tokens,
-
# but add indent to the line *after* that which contains the start tokens.
-
#
-
# This method returns a pair, where the first number is the number of closings
-
# on this line (i.e. the number of indents to remove before the line) and the
-
# second is the number of openings (i.e. the number of indents to add after
-
# this line)
-
#
-
# @param [Array] tokens A list of tokens to scan.
-
# @return [Array[Integer]]
-
#
-
1
def indentation_delta(tokens)
-
-
# We need to keep track of whether we've seen a "for" on this line because
-
# if the line ends with "do" then that "do" should be discounted (i.e. we're
-
# only opening one level not two) To do this robustly we want to keep track
-
# of the indent level at which we saw the for, so we can differentiate
-
# between "for x in [1,2,3] do" and "for x in ([1,2,3].map do" properly
-
seen_for_at = []
-
-
# When deciding whether an "if" token is the start of a multiline statement,
-
# or just the middle of a single-line if statement, we just look at the
-
# preceding token, which is tracked here.
-
last_token, last_kind = [nil, nil]
-
-
# delta keeps track of the total difference from the start of each line after
-
# the given token, 0 is just the level at which the current line started for
-
# reference.
-
remove_before, add_after = [0, 0]
-
-
# If the list of tokens contains a matching closing token the line should
-
# not be indented (and thus we should return true).
-
tokens.each do |token, kind|
-
is_singleline_if = (SINGLELINE_TOKENS.include?(token)) && end_of_statement?(last_token, last_kind)
-
is_optional_do = (token == "do" && seen_for_at.include?(add_after - 1))
-
-
last_token, last_kind = token, kind unless kind == :space
-
next if IGNORE_TOKENS.include?(kind)
-
-
track_module_nesting(token, kind)
-
-
seen_for_at << add_after if OPTIONAL_DO_TOKENS.include?(token)
-
-
if kind == :delimiter
-
track_delimiter(token)
-
elsif OPEN_TOKENS.keys.include?(token) && !is_optional_do && !is_singleline_if
-
@stack << token
-
add_after += 1
-
elsif token == OPEN_TOKENS[@stack.last]
-
popped = @stack.pop
-
track_module_nesting_end(popped)
-
if add_after == 0
-
remove_before += 1
-
else
-
add_after -= 1
-
end
-
elsif MIDWAY_TOKENS.include?(token)
-
if add_after == 0
-
remove_before += 1
-
add_after += 1
-
end
-
end
-
end
-
-
return [remove_before, add_after]
-
end
-
-
# If the code just before an "if" or "while" token on a line looks like the end of a statement,
-
# then we want to treat that "if" as a singleline, not multiline statement.
-
1
def end_of_statement?(last_token, last_kind)
-
(last_token =~ /^[)\]}\/]$/ || STATEMENT_END_TOKENS.include?(last_kind))
-
end
-
-
# Are we currently in the middle of a string literal.
-
#
-
# This is used to determine whether to re-indent a given line, we mustn't re-indent
-
# within string literals because to do so would actually change the value of the
-
# String!
-
#
-
# @return Boolean
-
1
def in_string?
-
!open_delimiters.empty?
-
end
-
-
# Given a string of Ruby code, use CodeRay to export the tokens.
-
#
-
# @param [String] string The Ruby to lex
-
# @return [Array] An Array of pairs of [token_value, token_type]
-
1
def tokenize(string)
-
tokens = CodeRay.scan(string, :ruby)
-
tokens = tokens.tokens.each_slice(2) if tokens.respond_to?(:tokens) # Coderay 1.0.0
-
tokens.to_a
-
end
-
-
# Update the internal state about what kind of strings are open.
-
#
-
# Most of the complication here comes from the fact that HEREDOCs can be nested. For
-
# normal strings (which can't be nested) we assume that CodeRay correctly pairs
-
# open-and-close delimiters so we don't bother checking what they are.
-
#
-
# @param [String] token The token (of type :delimiter)
-
1
def track_delimiter(token)
-
case token
-
when /^<<-(["'`]?)(.*)\\1/
-
@heredoc_queue << token
-
@close_heredocs[token] = /^\s*$2/
-
when @close_heredocs[@heredoc_queue.first]
-
@heredoc_queue.shift
-
else
-
if @string_start
-
@string_start = nil
-
else
-
@string_start = token
-
end
-
end
-
end
-
-
# All the open delimiters, in the order that they first appeared.
-
#
-
# @return [String]
-
1
def open_delimiters
-
@heredoc_queue + [@string_start].compact
-
end
-
-
# Return a string which restores the CodeRay string status to the correct value by
-
# opening HEREDOCs and strings.
-
#
-
# @return String
-
1
def open_delimiters_line
-
"puts #{open_delimiters.join(", ")}"
-
end
-
-
# Update the internal state relating to module nesting.
-
#
-
# It's responsible for adding to the @module_nesting array, which looks
-
# something like:
-
#
-
# [ ["class", "Foo"], ["module", "Bar::Baz"], ["class <<", "self"] ]
-
#
-
# A nil value in the @module_nesting array happens in two places: either
-
# when @awaiting_class is true and we're still waiting for the string to
-
# fill that space, or when a parse was rejected.
-
#
-
# At the moment this function is quite restricted about what formats it will
-
# parse, for example we disallow expressions after the class keyword. This
-
# could maybe be improved in the future.
-
#
-
# @param [String] token a token from Coderay
-
# @param [Symbol] kind the kind of that token
-
1
def track_module_nesting(token, kind)
-
if kind == :keyword && (token == "class" || token == "module")
-
@module_nesting << [token, nil]
-
@awaiting_class = true
-
elsif @awaiting_class
-
if kind == :operator && token == "<<" && @module_nesting.last[0] == "class"
-
@module_nesting.last[0] = "class <<"
-
@awaiting_class = true
-
elsif kind == :class && token =~ /\A(self|[A-Z:][A-Za-z0-9_:]*)\z/
-
@module_nesting.last[1] = token if kind == :class
-
@awaiting_class = false
-
else
-
# leave @module_nesting[-1]
-
@awaiting_class = false
-
end
-
end
-
end
-
-
# Update the internal state relating to module nesting on 'end'.
-
#
-
# If the current 'end' pairs up with a class or a module then we should
-
# pop an array off of @module_nesting
-
#
-
# @param [String] token a token from Coderay
-
# @param [Symbol] kind the kind of that token
-
1
def track_module_nesting_end(token, kind=:keyword)
-
if kind == :keyword && (token == "class" || token == "module")
-
@module_nesting.pop
-
end
-
end
-
-
# Return a list of strings which can be used to re-construct the Module.nesting at
-
# the current point in the file.
-
#
-
# Returns nil if the syntax of the file was not recognizable.
-
#
-
# @return [Array<String>]
-
1
def module_nesting
-
@module_nesting.map do |(kind, token)|
-
raise UnparseableNestingError, @module_nesting.inspect if token.nil?
-
-
"#{kind} #{token}"
-
end
-
end
-
-
# Return a string which, when printed, will rewrite the previous line with
-
# the correct indentation. Mostly useful for fixing 'end'.
-
#
-
# @param [String] prompt The user's prompt
-
# @param [String] code The code the user just typed in.
-
# @param [Fixnum] overhang (0) The number of chars to erase afterwards (i.e.,
-
# the difference in length between the old line and the new one).
-
# @return [String]
-
1
def correct_indentation(prompt, code, overhang=0)
-
prompt = prompt.delete("\001\002")
-
line_to_measure = Pry::Helpers::Text.strip_color(prompt) << code
-
whitespace = ' ' * overhang
-
-
_, cols = Terminal.screen_size
-
-
cols = cols.to_i
-
lines = (cols != 0 ? (line_to_measure.length / cols + 1) : 1).to_i
-
-
if Pry::Helpers::BaseHelpers.windows_ansi?
-
move_up = "\e[#{lines}F"
-
move_down = "\e[#{lines}E"
-
else
-
move_up = "\e[#{lines}A\e[0G"
-
move_down = "\e[#{lines}B\e[0G"
-
end
-
-
"#{move_up}#{prompt}#{colorize_code(code)}#{whitespace}#{move_down}"
-
end
-
end
-
end
-
1
require 'thread'
-
-
1
class Pry
-
# There is one InputLock per input (such as STDIN) as two REPLs on the same
-
# input makes things delirious. InputLock serializes accesses to the input so
-
# that threads to not conflict with each other. The latest thread to request
-
# ownership of the input wins.
-
1
class InputLock
-
1
class Interrupt < Exception; end
-
-
1
class << self
-
1
attr_accessor :input_locks
-
1
attr_accessor :global_lock
-
end
-
-
1
self.input_locks = {}
-
1
self.global_lock = Mutex.new
-
-
1
def self.for(input)
-
# XXX This method leaks memory, as we never unregister an input once we
-
# are done with it. Fortunately, the leak is tiny (or so we hope). In
-
# usual scenarios, we would leak the StringIO that is passed to be
-
# evaluated from the command line.
-
global_lock.synchronize do
-
input_locks[input] ||= Pry::InputLock.new
-
end
-
end
-
-
1
def initialize
-
@mutex = Mutex.new
-
@cond = ConditionVariable.new
-
@owners = []
-
@interruptible = false
-
end
-
-
# Adds ourselves to the ownership list. The last one in the list may access
-
# the input through interruptible_region().
-
1
def __with_ownership(&block)
-
@mutex.synchronize do
-
# Three cases:
-
# 1) There are no owners, in this case we are good to go.
-
# 2) The current owner of the input is not reading the input (it might
-
# just be evaluating some ruby that the user typed).
-
# The current owner will figure out that it cannot go back to reading
-
# the input since we are adding ourselves to the @owners list, which
-
# in turns makes us the current owner.
-
# 3) The owner of the input is in the interruptible region, reading from
-
# the input. It's safe to send an Interrupt exception to interrupt
-
# the owner. It will then proceed like in case 2).
-
# We wait until the owner sets the interruptible flag back
-
# to false, meaning that he's out of the interruptible region.
-
# Note that the owner may receive multiple interrupts since, but that
-
# should be okay (and trying to avoid it is futile anyway).
-
while @interruptible
-
@owners.last.raise Interrupt
-
@cond.wait(@mutex)
-
end
-
@owners << Thread.current
-
end
-
-
block.call
-
-
ensure
-
@mutex.synchronize do
-
# We are releasing any desire to have the input ownership by removing
-
# ourselves from the list.
-
@owners.delete(Thread.current)
-
-
# We need to wake up the thread at the end of the @owners list, but
-
# sadly Ruby doesn't allow us to choose which one we wake up, so we wake
-
# them all up.
-
@cond.broadcast
-
end
-
end
-
-
1
def with_ownership(&block)
-
# If we are in a nested with_ownership() call (nested pry context), we do nothing.
-
nested = @mutex.synchronize { @owners.include?(Thread.current) }
-
nested ? block.call : __with_ownership(&block)
-
end
-
-
1
def enter_interruptible_region
-
@mutex.synchronize do
-
# We patiently wait until we are the owner. This may happen as another
-
# thread calls with_ownership() because of a binding.pry happening in
-
# another thread.
-
@cond.wait(@mutex) until @owners.last == Thread.current
-
-
# We are the legitimate owner of the input. We mark ourselves as
-
# interruptible, so other threads can send us an Interrupt exception
-
# while we are blocking from reading the input.
-
@interruptible = true
-
end
-
end
-
-
1
def leave_interruptible_region
-
@mutex.synchronize do
-
# We check if we are still the owner, because we could have received an
-
# Interrupt right after the following @cond.broadcast, making us retry.
-
@interruptible = false if @owners.last == Thread.current
-
@cond.broadcast
-
end
-
rescue Interrupt
-
# We need to guard against a spurious interrupt delivered while we are
-
# trying to acquire the lock (the rescue block is no longer in our scope).
-
retry
-
end
-
-
1
def interruptible_region(&block)
-
enter_interruptible_region
-
-
# XXX Note that there is a chance that we get the interrupt right after
-
# the readline call succeeded, but we'll never know, and we will retry the
-
# call, discarding that piece of input.
-
block.call
-
-
rescue Interrupt
-
# We were asked to back off. The one requesting the interrupt will be
-
# waiting on the conditional for the interruptible flag to change to false.
-
# Note that there can be some inefficiency, as we could immediately
-
# succeed in enter_interruptible_region(), even before the one requesting
-
# the ownership has the chance to register itself as an owner.
-
# To mitigate the issue, we sleep a little bit.
-
leave_interruptible_region
-
sleep 0.01
-
retry
-
-
ensure
-
leave_interruptible_region
-
end
-
end
-
end
-
1
class Pry::Inspector
-
1
MAP = {
-
"default" => {
-
value: Pry::DEFAULT_PRINT,
-
description: <<-DESCRIPTION.each_line.map(&:lstrip!)
-
The default Pry inspector. It has paging and color support, and uses
-
pretty_inspect when printing an object.
-
DESCRIPTION
-
},
-
-
"simple" => {
-
value: Pry::SIMPLE_PRINT,
-
description: <<-DESCRIPTION.each_line.map(&:lstrip)
-
A simple inspector that uses #puts and #inspect when printing an
-
object. It has no pager, color, or pretty_inspect support.
-
DESCRIPTION
-
},
-
-
"clipped" => {
-
value: Pry::CLIPPED_PRINT,
-
description: <<-DESCRIPTION.each_line.map(&:lstrip)
-
The clipped inspector has the same features as the 'simple' inspector
-
but prints large objects as a smaller string.
-
DESCRIPTION
-
}
-
}
-
end
-
#
-
# {Pry::LastException} is a proxy class who wraps an Exception object for
-
# {Pry#last_exception}. it extends the exception object with methods that
-
# help pry commands be useful.
-
#
-
# the original exception object is not modified and method calls are forwarded
-
# to the wrapped exception object.
-
#
-
1
class Pry::LastException < BasicObject
-
1
attr_accessor :bt_index
-
-
1
def initialize(e)
-
@e = e
-
@bt_index = 0
-
@file, @line = bt_source_location_for(0)
-
end
-
-
1
def method_missing(name, *args, &block)
-
if @e.respond_to?(name)
-
@e.public_send(name, *args, &block)
-
else
-
super
-
end
-
end
-
-
1
def respond_to_missing?(name, include_private = false)
-
@e.respond_to?(name)
-
end
-
-
#
-
# @return [String]
-
# returns the path to a file for the current backtrace. see {#bt_index}.
-
#
-
1
def file
-
@file
-
end
-
-
#
-
# @return [Fixnum]
-
# returns the line for the current backtrace. see {#bt_index}.
-
#
-
1
def line
-
@line
-
end
-
-
# @return [Exception]
-
# returns the wrapped exception
-
#
-
1
def wrapped_exception
-
@e
-
end
-
-
1
def bt_source_location_for(index)
-
backtrace[index] =~ /(.*):(\d+)/
-
[$1, $2.to_i]
-
end
-
-
1
def inc_bt_index
-
@bt_index = (@bt_index + 1) % backtrace.size
-
end
-
end
-
1
require 'pry/helpers/documentation_helpers'
-
-
1
class Pry
-
1
class << self
-
# If the given object is a `Pry::Method`, return it unaltered. If it's
-
# anything else, return it wrapped in a `Pry::Method` instance.
-
1
def Method(obj)
-
if obj.is_a? Pry::Method
-
obj
-
else
-
Pry::Method.new(obj)
-
end
-
end
-
end
-
-
# This class wraps the normal `Method` and `UnboundMethod` classes
-
# to provide extra functionality useful to Pry.
-
1
class Method
-
1
require 'pry/method/weird_method_locator'
-
1
require 'pry/method/disowned'
-
1
require 'pry/method/patcher'
-
-
1
extend Helpers::BaseHelpers
-
1
include Helpers::BaseHelpers
-
1
include Helpers::DocumentationHelpers
-
1
include CodeObject::Helpers
-
-
1
class << self
-
# Given a string representing a method name and optionally a binding to
-
# search in, find and return the requested method wrapped in a `Pry::Method`
-
# instance.
-
#
-
# @param [String] name The name of the method to retrieve.
-
# @param [Binding] target The context in which to search for the method.
-
# @param [Hash] options
-
# @option options [Boolean] :instance Look for an instance method if `name` doesn't
-
# contain any context.
-
# @option options [Boolean] :methods Look for a bound/singleton method if `name` doesn't
-
# contain any context.
-
# @return [Pry::Method, nil] A `Pry::Method` instance containing the requested
-
# method, or `nil` if name is `nil` or no method could be located matching the parameters.
-
1
def from_str(name, target=TOPLEVEL_BINDING, options={})
-
if name.nil?
-
nil
-
elsif name.to_s =~ /(.+)\#(\S+)\Z/
-
context, meth_name = $1, $2
-
from_module(target.eval(context), meth_name, target)
-
elsif name.to_s =~ /(.+)(\[\])\Z/
-
context, meth_name = $1, $2
-
from_obj(target.eval(context), meth_name, target)
-
elsif name.to_s =~ /(.+)(\.|::)(\S+)\Z/
-
context, meth_name = $1, $3
-
from_obj(target.eval(context), meth_name, target)
-
elsif options[:instance]
-
from_module(target.eval("self"), name, target)
-
elsif options[:methods]
-
from_obj(target.eval("self"), name, target)
-
else
-
from_str(name, target, :instance => true) or
-
from_str(name, target, :methods => true)
-
end
-
-
rescue Pry::RescuableException
-
nil
-
end
-
-
# Given a `Binding`, try to extract the `::Method` it originated from and
-
# use it to instantiate a `Pry::Method`. Return `nil` if this isn't
-
# possible.
-
#
-
# @param [Binding] b
-
# @return [Pry::Method, nil]
-
#
-
1
def from_binding(b)
-
meth_name = b.eval('::Kernel.__method__')
-
if [:__script__, nil].include?(meth_name)
-
nil
-
else
-
method = begin
-
if Object === b.eval('self')
-
new(Kernel.instance_method(:method).bind(b.eval("self")).call(meth_name))
-
else
-
new(b.eval('class << self; self; end.instance_method(::Kernel.__method__).bind(self)'))
-
end
-
rescue NameError, NoMethodError
-
Disowned.new(b.eval('self'), meth_name.to_s)
-
end
-
-
if WeirdMethodLocator.weird_method?(method, b)
-
WeirdMethodLocator.new(method, b).get_method || method
-
else
-
method
-
end
-
end
-
end
-
-
# In order to support 2.0 Refinements we need to look up methods
-
# inside the relevant Binding.
-
# @param [Object] obj The owner/receiver of the method.
-
# @param [Symbol] method_name The name of the method.
-
# @param [Symbol] method_type The type of method: :method or :instance_method
-
# @param [Binding] target The binding where the method is looked up.
-
# @return [Method, UnboundMethod] The 'refined' method object.
-
1
def lookup_method_via_binding(obj, method_name, method_type, target=TOPLEVEL_BINDING)
-
Pry.current[:obj] = obj
-
Pry.current[:name] = method_name
-
receiver = obj.is_a?(Module) ? "Module" : "Kernel"
-
target.eval("::#{receiver}.instance_method(:#{method_type}).bind(Pry.current[:obj]).call(Pry.current[:name])")
-
ensure
-
Pry.current[:obj] = Pry.current[:name] = nil
-
end
-
-
# Given a `Class` or `Module` and the name of a method, try to
-
# instantiate a `Pry::Method` containing the instance method of
-
# that name. Return `nil` if no such method exists.
-
#
-
# @param [Class, Module] klass
-
# @param [String] name
-
# @param [Binding] target The binding where the method is looked up.
-
# @return [Pry::Method, nil]
-
1
def from_class(klass, name, target=TOPLEVEL_BINDING)
-
new(lookup_method_via_binding(klass, name, :instance_method, target)) rescue nil
-
end
-
1
alias from_module from_class
-
-
# Given an object and the name of a method, try to instantiate
-
# a `Pry::Method` containing the method of that name bound to
-
# that object. Return `nil` if no such method exists.
-
#
-
# @param [Object] obj
-
# @param [String] name
-
# @param [Binding] target The binding where the method is looked up.
-
# @return [Pry::Method, nil]
-
1
def from_obj(obj, name, target=TOPLEVEL_BINDING)
-
new(lookup_method_via_binding(obj, name, :method, target)) rescue nil
-
end
-
-
# Get all of the instance methods of a `Class` or `Module`
-
# @param [Class,Module] klass
-
# @param [Boolean] include_super Whether to include methods from ancestors.
-
# @return [Array[Pry::Method]]
-
1
def all_from_class(klass, include_super=true)
-
%w(public protected private).map do |visibility|
-
safe_send(klass, :"#{visibility}_instance_methods", include_super).map do |method_name|
-
new(safe_send(klass, :instance_method, method_name), :visibility => visibility.to_sym)
-
end
-
end.flatten(1)
-
end
-
-
#
-
# Get all of the methods on an `Object`
-
#
-
# @param [Object] obj
-
#
-
# @param [Boolean] include_super
-
# indicates whether or not to include methods from ancestors.
-
#
-
# @return [Array[Pry::Method]]
-
#
-
1
def all_from_obj(obj, include_super=true)
-
all_from_class(singleton_class_of(obj), include_super)
-
end
-
-
#
-
# @deprecated
-
# please use {#all_from_obj} instead.
-
# the `method_type` argument is ignored.
-
#
-
1
def all_from_common(obj, method_type = nil, include_super=true)
-
all_from_obj(obj, include_super)
-
end
-
-
# Get every `Class` and `Module`, in order, that will be checked when looking
-
# for an instance method to call on this object.
-
# @param [Object] obj
-
# @return [Array[Class, Module]]
-
1
def resolution_order(obj)
-
if Class === obj
-
singleton_class_resolution_order(obj) + instance_resolution_order(Class)
-
else
-
klass = singleton_class_of(obj) rescue obj.class
-
instance_resolution_order(klass)
-
end
-
end
-
-
# Get every `Class` and `Module`, in order, that will be checked when looking
-
# for methods on instances of the given `Class` or `Module`.
-
# This does not treat singleton classes of classes specially.
-
# @param [Class, Module] klass
-
# @return [Array[Class, Module]]
-
1
def instance_resolution_order(klass)
-
# include klass in case it is a singleton class,
-
([klass] + Pry::Method.safe_send(klass, :ancestors)).uniq
-
end
-
-
1
def method_definition?(name, definition_line)
-
singleton_method_definition?(name, definition_line) ||
-
instance_method_definition?(name, definition_line)
-
end
-
-
1
def singleton_method_definition?(name, definition_line)
-
/^define_singleton_method\(?\s*[:\"\']#{Regexp.escape(name)}|^def\s*self\.#{Regexp.escape(name)}/ =~ definition_line.strip
-
end
-
-
1
def instance_method_definition?(name, definition_line)
-
/^define_method\(?\s*[:\"\']#{Regexp.escape(name)}|^def\s*#{Regexp.escape(name)}/ =~ definition_line.strip
-
end
-
-
# Get the singleton classes of superclasses that could define methods on
-
# the given class object, and any modules they include.
-
# If a module is included at multiple points in the ancestry, only
-
# the lowest copy will be returned.
-
1
def singleton_class_resolution_order(klass)
-
ancestors = Pry::Method.safe_send(klass, :ancestors)
-
resolution_order = ancestors.grep(Class).map do |anc|
-
[singleton_class_of(anc), *singleton_class_of(anc).included_modules]
-
end.flatten(1)
-
-
resolution_order.reverse.uniq.reverse - Class.included_modules
-
end
-
-
1
def singleton_class_of(obj)
-
begin
-
class << obj; self; end
-
rescue TypeError # can't define singleton. Fixnum, Symbol, Float, ...
-
obj.class
-
end
-
end
-
end
-
-
# A new instance of `Pry::Method` wrapping the given `::Method`, `UnboundMethod`, or `Proc`.
-
#
-
# @param [::Method, UnboundMethod, Proc] method
-
# @param [Hash] known_info Can be used to pre-cache expensive to compute stuff.
-
# @return [Pry::Method]
-
1
def initialize(method, known_info={})
-
@method = method
-
@visibility = known_info[:visibility]
-
end
-
-
# Get the name of the method as a String, regardless of the underlying Method#name type.
-
# @return [String]
-
1
def name
-
@method.name.to_s
-
end
-
-
# Get the owner of the method as a Pry::Module
-
# @return [Pry::Module]
-
1
def wrapped_owner
-
@wrapped_owner ||= Pry::WrappedModule.new(owner)
-
end
-
-
# Get underlying object wrapped by this Pry::Method instance
-
# @return [Method, UnboundMethod, Proc]
-
1
def wrapped
-
@method
-
end
-
-
# Is the method undefined? (aka `Disowned`)
-
# @return [Boolean] false
-
1
def undefined?
-
false
-
end
-
-
# Get the name of the method including the class on which it was defined.
-
# @example
-
# method(:puts).method_name
-
# => "Kernel.puts"
-
# @return [String]
-
1
def name_with_owner
-
"#{wrapped_owner.method_prefix}#{name}"
-
end
-
-
# @return [String, nil] The source code of the method, or `nil` if it's unavailable.
-
1
def source
-
@source ||= case source_type
-
when :c
-
c_source
-
when :ruby
-
ruby_source
-
end
-
end
-
-
# Update the live copy of the method's source.
-
1
def redefine(source)
-
Patcher.new(self).patch_in_ram source
-
Pry::Method(owner.instance_method(name))
-
end
-
-
# Can we get the source code for this method?
-
# @return [Boolean]
-
1
def source?
-
!!source
-
rescue MethodSource::SourceNotFoundError
-
false
-
end
-
-
# @return [String, nil] The documentation for the method, or `nil` if it's
-
# unavailable.
-
1
def doc
-
@doc ||= case source_type
-
when :c
-
info = pry_doc_info
-
info.docstring if info
-
when :ruby
-
get_comment_content(comment)
-
end
-
end
-
-
# @return [Symbol] The source type of the method. The options are
-
# `:ruby` for Ruby methods or `:c` for methods written in C.
-
1
def source_type
-
source_location.nil? ? :c : :ruby
-
end
-
-
# @return [String, nil] The name of the file the method is defined in, or
-
# `nil` if the filename is unavailable.
-
1
def source_file
-
if source_location.nil?
-
if !rbx? and source_type == :c
-
info = pry_doc_info
-
info.file if info
-
end
-
else
-
source_location.first
-
end
-
end
-
-
# @return [Fixnum, nil] The line of code in `source_file` which begins
-
# the method's definition, or `nil` if that information is unavailable.
-
1
def source_line
-
source_location.nil? ? nil : source_location.last
-
end
-
-
# @return [Range, nil] The range of lines in `source_file` which contain
-
# the method's definition, or `nil` if that information is unavailable.
-
1
def source_range
-
source_location.nil? ? nil : (source_line)..(source_line + source.lines.count - 1)
-
end
-
-
# @return [Symbol] The visibility of the method. May be `:public`,
-
# `:protected`, or `:private`.
-
1
def visibility
-
@visibility ||= if owner.public_instance_methods.any? { |m| m.to_s == name }
-
:public
-
elsif owner.protected_instance_methods.any? { |m| m.to_s == name }
-
:protected
-
elsif owner.private_instance_methods.any? { |m| m.to_s == name }
-
:private
-
else
-
:none
-
end
-
end
-
-
# @return [String] A representation of the method's signature, including its
-
# name and parameters. Optional and "rest" parameters are marked with `*`
-
# and block parameters with `&`. If the parameter names are unavailable,
-
# they're given numbered names instead.
-
# Paraphrased from `awesome_print` gem.
-
1
def signature
-
if respond_to?(:parameters)
-
args = parameters.inject([]) do |arr, (type, name)|
-
name ||= (type == :block ? 'block' : "arg#{arr.size + 1}")
-
arr << case type
-
when :req then name.to_s
-
when :opt then "#{name}=?"
-
when :rest then "*#{name}"
-
when :block then "&#{name}"
-
else '?'
-
end
-
end
-
else
-
args = (1..arity.abs).map { |i| "arg#{i}" }
-
args[-1] = "*#{args[-1]}" if arity < 0
-
end
-
-
"#{name}(#{args.join(', ')})"
-
end
-
-
# @return [Pry::Method, nil] The wrapped method that is called when you
-
# use "super" in the body of this method.
-
1
def super(times=1)
-
if UnboundMethod === @method
-
sup = super_using_ancestors(Pry::Method.instance_resolution_order(owner), times)
-
else
-
sup = super_using_ancestors(Pry::Method.resolution_order(receiver), times)
-
sup &&= sup.bind(receiver)
-
end
-
Pry::Method.new(sup) if sup
-
end
-
-
# @return [String, nil] The original name the method was defined under,
-
# before any aliasing, or `nil` if it can't be determined.
-
1
def original_name
-
return nil if source_type != :ruby
-
method_name_from_first_line(source.lines.first)
-
end
-
-
# @return [Boolean] Was the method defined outside a source file?
-
1
def dynamically_defined?
-
!!(source_file and source_file =~ /(\(.*\))|<.*>/)
-
end
-
-
# @return [Boolean] Whether the method is unbound.
-
1
def unbound_method?
-
is_a?(::UnboundMethod)
-
end
-
-
# @return [Boolean] Whether the method is bound.
-
1
def bound_method?
-
is_a?(::Method)
-
end
-
-
# @return [Boolean] Whether the method is a singleton method.
-
1
def singleton_method?
-
wrapped_owner.singleton_class?
-
end
-
-
# @return [Boolean] Was the method defined within the Pry REPL?
-
1
def pry_method?
-
source_file == Pry.eval_path
-
end
-
-
# @return [Array<String>] All known aliases for the method.
-
1
def aliases
-
owner = @method.owner
-
# Avoid using `to_sym` on {Method#name}, which returns a `String`, because
-
# it won't be garbage collected.
-
name = @method.name
-
-
all_methods_to_compare = owner.instance_methods | owner.private_instance_methods
-
alias_list = all_methods_to_compare.combination(2).select do |pair|
-
pair.include?(name) &&
-
owner.instance_method(pair.first) == owner.instance_method(pair.last)
-
end.flatten
-
alias_list.delete(name)
-
-
alias_list.map(&:to_s)
-
end
-
-
# @return [Boolean] Is the method definitely an alias?
-
1
def alias?
-
name != original_name
-
end
-
-
# @return [Boolean]
-
1
def ==(obj)
-
if obj.is_a? Pry::Method
-
obj == @method
-
else
-
@method == obj
-
end
-
end
-
-
# @param [Class] klass
-
# @return [Boolean]
-
1
def is_a?(klass)
-
klass == Pry::Method or @method.is_a?(klass)
-
end
-
1
alias kind_of? is_a?
-
-
# @param [String, Symbol] method_name
-
# @return [Boolean]
-
1
def respond_to?(method_name)
-
super or @method.respond_to?(method_name)
-
end
-
-
# Delegate any unknown calls to the wrapped method.
-
1
def method_missing(method_name, *args, &block)
-
@method.send(method_name, *args, &block)
-
end
-
-
1
def comment
-
Pry::Code.from_file(source_file).comment_describing(source_line)
-
end
-
-
1
private
-
-
# @return [YARD::CodeObjects::MethodObject]
-
# @raise [CommandError] when the method can't be found or `pry-doc` isn't installed.
-
1
def pry_doc_info
-
if Pry.config.has_pry_doc
-
Pry::MethodInfo.info_for(@method) or raise CommandError, "Cannot locate this method: #{name}. (source_location returns nil)"
-
else
-
fail_msg = "Cannot locate this method: #{name}."
-
if mri?
-
fail_msg += ' Try `gem-install pry-doc` to get access to Ruby Core documentation.'
-
end
-
raise CommandError, fail_msg
-
end
-
end
-
-
# @param [Class, Module] ancestors The ancestors to investigate
-
# @return [Method] The unwrapped super-method
-
1
def super_using_ancestors(ancestors, times=1)
-
next_owner = self.owner
-
times.times do
-
i = ancestors.index(next_owner) + 1
-
while ancestors[i] && !(ancestors[i].method_defined?(name) || ancestors[i].private_method_defined?(name))
-
i += 1
-
end
-
next_owner = ancestors[i] or return nil
-
end
-
-
safe_send(next_owner, :instance_method, name) rescue nil
-
end
-
-
# @param [String] first_ln The first line of a method definition.
-
# @return [String, nil]
-
1
def method_name_from_first_line(first_ln)
-
return nil if first_ln.strip !~ /^def /
-
-
tokens = CodeRay.scan(first_ln, :ruby)
-
tokens = tokens.tokens.each_slice(2) if tokens.respond_to?(:tokens)
-
tokens.each_cons(2) do |t1, t2|
-
if t2.last == :method || t2.last == :ident && t1 == [".", :operator]
-
return t2.first
-
end
-
end
-
-
nil
-
end
-
-
1
def c_source
-
info = pry_doc_info
-
if info and info.source
-
strip_comments_from_c_code(info.source)
-
end
-
end
-
-
1
def ruby_source
-
# clone of MethodSource.source_helper that knows to use our
-
# hacked version of source_location for rbx core methods, and
-
# our input buffer for methods defined in (pry)
-
file, line = *source_location
-
raise SourceNotFoundError, "Could not locate source for #{name_with_owner}!" unless file
-
-
begin
-
code = Pry::Code.from_file(file).expression_at(line)
-
rescue SyntaxError => e
-
raise MethodSource::SourceNotFoundError.new(e.message)
-
end
-
strip_leading_whitespace(code)
-
end
-
end
-
end
-
1
class Pry
-
1
class Method
-
# A Disowned Method is one that's been removed from the class on which it was defined.
-
#
-
# e.g.
-
# class C
-
# def foo
-
# C.send(:undefine_method, :foo)
-
# Pry::Method.from_binding(binding)
-
# end
-
# end
-
#
-
# In this case we assume that the "owner" is the singleton class of the receiver.
-
#
-
# This occurs mainly in Sinatra applications.
-
1
class Disowned < Method
-
1
attr_reader :receiver, :name
-
-
# Create a new Disowned method.
-
#
-
# @param [Object] receiver
-
# @param [String] method_name
-
1
def initialize(receiver, method_name, binding=nil)
-
@receiver, @name = receiver, method_name
-
end
-
-
# Is the method undefined? (aka `Disowned`)
-
# @return [Boolean] true
-
1
def undefined?
-
true
-
end
-
-
# Can we get the source for this method?
-
# @return [Boolean] false
-
1
def source?
-
false
-
end
-
-
# Get the hypothesized owner of the method.
-
#
-
# @return [Object]
-
1
def owner
-
class << receiver; self; end
-
end
-
-
# Raise a more useful error message instead of trying to forward to nil.
-
1
def method_missing(meth_name, *args, &block)
-
raise "Cannot call '#{meth_name}' on an undef'd method." if method(:name).respond_to?(meth_name)
-
Object.instance_method(:method_missing).bind(self).call(meth_name, *args, &block)
-
end
-
end
-
end
-
end
-
1
class Pry
-
1
class Method
-
1
class Patcher
-
1
attr_accessor :method
-
-
1
@@source_cache = {}
-
-
1
def initialize(method)
-
@method = method
-
end
-
-
1
def self.code_for(filename)
-
@@source_cache[filename]
-
end
-
-
# perform the patch
-
1
def patch_in_ram(source)
-
if method.alias?
-
with_method_transaction do
-
redefine source
-
end
-
else
-
redefine source
-
end
-
end
-
-
1
private
-
-
1
def redefine(source)
-
@@source_cache[cache_key] = source
-
TOPLEVEL_BINDING.eval wrap(source), cache_key
-
end
-
-
1
def cache_key
-
"pry-redefined(0x#{method.owner.object_id.to_s(16)}##{method.name})"
-
end
-
-
# Run some code ensuring that at the end target#meth_name will not have changed.
-
#
-
# When we're redefining aliased methods we will overwrite the method at the
-
# unaliased name (so that super continues to work). By wrapping that code in a
-
# transation we make that not happen, which means that alias_method_chains, etc.
-
# continue to work.
-
#
-
# @param [String] meth_name The method name before aliasing
-
# @param [Module] target The owner of the method
-
-
1
def with_method_transaction
-
temp_name = "__pry_#{method.original_name}__"
-
method = self.method
-
method.owner.class_eval do
-
alias_method temp_name, method.original_name
-
yield
-
alias_method method.name, method.original_name
-
alias_method method.original_name, temp_name
-
end
-
-
ensure
-
method.send(:remove_method, temp_name) rescue nil
-
end
-
-
# Update the definition line so that it can be eval'd directly on the Method's
-
# owner instead of from the original context.
-
#
-
# In particular this takes `def self.foo` and turns it into `def foo` so that we
-
# don't end up creating the method on the singleton class of the singleton class
-
# by accident.
-
#
-
# This is necessarily done by String manipulation because we can't find out what
-
# syntax is needed for the argument list by ruby-level introspection.
-
#
-
# @param [String] line The original definition line. e.g. def self.foo(bar, baz=1)
-
# @return [String] The new definition line. e.g. def foo(bar, baz=1)
-
1
def definition_for_owner(line)
-
if line =~ /\Adef (?:.*?\.)?#{Regexp.escape(method.original_name)}(?=[\(\s;]|$)/
-
"def #{method.original_name}#{$'}"
-
else
-
raise CommandError, "Could not find original `def #{method.original_name}` line to patch."
-
end
-
end
-
-
# Apply wrap_for_owner and wrap_for_nesting successively to `source`
-
# @param [String] source
-
# @return [String] The wrapped source.
-
1
def wrap(source)
-
wrap_for_nesting(wrap_for_owner(source))
-
end
-
-
# Update the source code so that when it has the right owner when eval'd.
-
#
-
# This (combined with definition_for_owner) is backup for the case that
-
# wrap_for_nesting fails, to ensure that the method will stil be defined in
-
# the correct place.
-
#
-
# @param [String] source The source to wrap
-
# @return [String]
-
1
def wrap_for_owner(source)
-
Pry.current[:pry_owner] = method.owner
-
owner_source = definition_for_owner(source)
-
visibility_fix = "#{method.visibility.to_s} #{method.name.to_sym.inspect}"
-
"Pry.current[:pry_owner].class_eval do; #{owner_source}\n#{visibility_fix}\nend"
-
end
-
-
# Update the new source code to have the correct Module.nesting.
-
#
-
# This method uses syntactic analysis of the original source file to determine
-
# the new nesting, so that we can tell the difference between:
-
#
-
# class A; def self.b; end; end
-
# class << A; def b; end; end
-
#
-
# The resulting code should be evaluated in the TOPLEVEL_BINDING.
-
#
-
# @param [String] source The source to wrap.
-
# @return [String]
-
1
def wrap_for_nesting(source)
-
nesting = Pry::Code.from_file(method.source_file).nesting_at(method.source_line)
-
-
(nesting + [source] + nesting.map{ "end" } + [""]).join(";")
-
rescue Pry::Indent::UnparseableNestingError
-
source
-
end
-
end
-
end
-
end
-
1
class Pry
-
1
class Method
-
-
# This class is responsible for locating the *real* `Pry::Method`
-
# object captured by a binding.
-
#
-
# Given a `Binding` from inside a method and a 'seed' Pry::Method object,
-
# there are primarily two situations where the seed method doesn't match
-
# the Binding:
-
# 1. The Pry::Method is from a subclass 2. The Pry::Method represents a method of the same name
-
# while the original was renamed to something else. For 1. we
-
# search vertically up the inheritance chain,
-
# and for 2. we search laterally along the object's method table.
-
#
-
# When we locate the method that matches the Binding we wrap it in
-
# Pry::Method and return it, or return nil if we fail.
-
1
class WeirdMethodLocator
-
1
class << self
-
-
# Whether the given method object matches the associated binding.
-
# If the method object does not match the binding, then it's
-
# most likely not the method captured by the binding, and we
-
# must commence a search.
-
#
-
# @param [Pry::Method] method
-
# @param [Binding] b
-
# @return [Boolean]
-
1
def normal_method?(method, b)
-
method && (method.source_file && method.source_range rescue false) &&
-
File.expand_path(method.source_file) == File.expand_path(b.eval('__FILE__')) &&
-
method.source_range.include?(b.eval('__LINE__'))
-
end
-
-
1
def weird_method?(method, b)
-
!normal_method?(method, b)
-
end
-
end
-
-
1
attr_accessor :method
-
1
attr_accessor :target
-
-
# @param [Pry::Method] method The seed method.
-
# @param [Binding] target The Binding that captures the method
-
# we want to locate.
-
1
def initialize(method, target)
-
@method, @target = method, target
-
end
-
-
# @return [Pry::Method, nil] The Pry::Method that matches the
-
# given binding.
-
1
def get_method
-
find_method_in_superclass || find_renamed_method
-
end
-
-
# @return [Boolean] Whether the Pry::Method is unrecoverable
-
# This usually happens when the method captured by the Binding
-
# has been subsequently deleted.
-
1
def lost_method?
-
!!(get_method.nil? && renamed_method_source_location)
-
end
-
-
1
private
-
-
1
def normal_method?(method)
-
self.class.normal_method?(method, target)
-
end
-
-
1
def target_self
-
target.eval('self')
-
end
-
-
1
def target_file
-
pry_file? ? target.eval('__FILE__') : File.expand_path(target.eval('__FILE__'))
-
end
-
-
1
def target_line
-
target.eval('__LINE__')
-
end
-
-
1
def pry_file?
-
Pry.eval_path == target.eval('__FILE__')
-
end
-
-
# it's possible in some cases that the method we find by this approach is a sub-method of
-
# the one we're currently in, consider:
-
#
-
# class A; def b; binding.pry; end; end
-
# class B < A; def b; super; end; end
-
#
-
# Given that we can normally find the source_range of methods, and that we know which
-
# __FILE__ and __LINE__ the binding is at, we can hope to disambiguate these cases.
-
#
-
# This obviously won't work if the source is unavaiable for some reason, or if both
-
# methods have the same __FILE__ and __LINE__, or if we're in rbx where b.eval('__LINE__')
-
# is broken.
-
#
-
# @return [Pry::Method, nil] The Pry::Method representing the
-
# superclass method.
-
1
def find_method_in_superclass
-
guess = method
-
-
while guess
-
# needs rescue if this is a Disowned method or a C method or something...
-
# TODO: Fix up the exception handling so we don't need a bare rescue
-
if normal_method?(guess)
-
return guess
-
else
-
guess = guess.super
-
end
-
end
-
-
# Uhoh... none of the methods in the chain had the right __FILE__ and __LINE__
-
# This may be caused by rbx https://github.com/rubinius/rubinius/issues/953,
-
# or other unknown circumstances (TODO: we should warn the user when this happens)
-
nil
-
end
-
-
# This is the case where the name of a method has changed
-
# (via alias_method) so we locate the Method object for the
-
# renamed method.
-
#
-
# @return [Pry::Method, nil] The Pry::Method representing the
-
# renamed method
-
1
def find_renamed_method
-
return if !valid_file?(target_file)
-
alias_name = all_methods_for(target_self).find do |v|
-
expanded_source_location(target_self.method(v).source_location) == renamed_method_source_location
-
end
-
-
alias_name && Pry::Method(target_self.method(alias_name))
-
end
-
-
1
def expanded_source_location(sl)
-
return if !sl
-
-
if pry_file?
-
sl
-
else
-
[File.expand_path(sl.first), sl.last]
-
end
-
end
-
-
# Use static analysis to locate the start of the method definition.
-
# We have the `__FILE__` and `__LINE__` from the binding and the
-
# original name of the method so we search up until we find a
-
# def/define_method, etc defining a method of the appropriate name.
-
#
-
# @return [Array<String, Fixnum>] The `source_location` of the
-
# renamed method
-
1
def renamed_method_source_location
-
return @original_method_source_location if defined?(@original_method_source_location)
-
-
source_index = lines_for_file(target_file)[0..(target_line - 1)].rindex do |v|
-
Pry::Method.method_definition?(method.name, v)
-
end
-
-
@original_method_source_location = source_index &&
-
[target_file, index_to_line_number(source_index)]
-
end
-
-
1
def index_to_line_number(index)
-
# Pry.line_buffer is 0-indexed
-
pry_file? ? index : index + 1
-
end
-
-
1
def valid_file?(file)
-
(File.exist?(file) && !File.directory?(file)) || Pry.eval_path == file
-
end
-
-
1
def lines_for_file(file)
-
@lines_for_file ||= {}
-
@lines_for_file[file] ||= if Pry.eval_path == file
-
Pry.line_buffer
-
else
-
File.readlines(file)
-
end
-
end
-
-
1
def all_methods_for(obj)
-
obj.public_methods(false) +
-
obj.private_methods(false) +
-
obj.protected_methods(false)
-
end
-
end
-
end
-
end
-
1
require 'pry/helpers/documentation_helpers'
-
1
require 'forwardable'
-
-
1
class Pry
-
1
class WrappedModule
-
-
# This class represents a single candidate for a module/class definition.
-
# It provides access to the source, documentation, line and file
-
# for a monkeypatch (reopening) of a class/module.
-
1
class Candidate
-
1
include Pry::Helpers::DocumentationHelpers
-
1
include Pry::CodeObject::Helpers
-
1
extend Forwardable
-
-
# @return [String] The file where the module definition is located.
-
1
attr_reader :file
-
1
alias_method :source_file, :file
-
-
# @return [Fixnum] The line where the module definition is located.
-
1
attr_reader :line
-
1
alias_method :source_line, :line
-
-
# Methods to delegate to associated `Pry::WrappedModule
-
# instance`.
-
1
private_delegates = [:lines_for_file, :method_candidates,
-
:yard_docs?]
-
-
1
public_delegates = [:wrapped, :module?, :class?, :name, :nonblank_name,
-
:number_of_candidates]
-
-
1
def_delegators :@wrapper, *(private_delegates + public_delegates)
-
1
private(*private_delegates)
-
1
public(*public_delegates)
-
-
# @raise [Pry::CommandError] If `rank` is out of bounds.
-
# @param [Pry::WrappedModule] wrapper The associated
-
# `Pry::WrappedModule` instance that owns the candidates.
-
# @param [Fixnum] rank The rank of the candidate to
-
# retrieve. Passing 0 returns 'primary candidate' (the candidate with largest
-
# number of methods), passing 1 retrieves candidate with
-
# second largest number of methods, and so on, up to
-
# `Pry::WrappedModule#number_of_candidates() - 1`
-
1
def initialize(wrapper, rank)
-
@wrapper = wrapper
-
-
if number_of_candidates <= 0
-
raise CommandError, "Cannot find a definition for #{name} module!"
-
elsif rank > (number_of_candidates - 1)
-
raise CommandError, "No such module candidate. Allowed candidates range is from 0 to #{number_of_candidates - 1}"
-
end
-
-
@rank = rank
-
@file, @line = source_location
-
end
-
-
# @raise [Pry::CommandError] If source code cannot be found.
-
# @return [String] The source for the candidate, i.e the
-
# complete module/class definition.
-
1
def source
-
return nil if file.nil?
-
return @source if @source
-
-
@source = strip_leading_whitespace(Pry::Code.from_file(file).expression_at(line, number_of_lines_in_first_chunk))
-
end
-
-
# @raise [Pry::CommandError] If documentation cannot be found.
-
# @return [String] The documentation for the candidate.
-
1
def doc
-
return nil if file.nil?
-
return @doc if @doc
-
-
@doc = get_comment_content(Pry::Code.from_file(file).comment_describing(line))
-
end
-
-
# @return [Array, nil] A `[String, Fixnum]` pair representing the
-
# source location (file and line) for the candidate or `nil`
-
# if no source location found.
-
1
def source_location
-
return @source_location if @source_location
-
-
file, line = first_method_source_location
-
return nil if !file.is_a?(String)
-
-
@source_location = [file, first_line_of_module_definition(file, line)]
-
rescue Pry::RescuableException
-
nil
-
end
-
-
1
private
-
-
# Locate the first line of the module definition.
-
# @param [String] file The file that contains the module
-
# definition (somewhere).
-
# @param [Fixnum] line The module definition should appear
-
# before this line (if it exists).
-
# @return [Fixnum] The line where the module is defined. This
-
# line number is one-indexed.
-
1
def first_line_of_module_definition(file, line)
-
searchable_lines = lines_for_file(file)[0..(line - 2)]
-
searchable_lines.rindex { |v| class_regexes.any? { |r| r =~ v } } + 1
-
end
-
-
1
def class_regexes
-
mod_type_string = wrapped.class.to_s.downcase
-
[/^\s*#{mod_type_string}\s+(?:(?:\w*)::)*?#{wrapped.name.split(/::/).last}/,
-
/^\s*(::)?#{wrapped.name.split(/::/).last}\s*?=\s*?#{wrapped.class}/,
-
/^\s*(::)?#{wrapped.name.split(/::/).last}\.(class|instance)_eval/]
-
end
-
-
# This method is used by `Candidate#source_location` as a
-
# starting point for the search for the candidate's definition.
-
# @return [Array] The source location of the base method used to
-
# calculate the source location of the candidate.
-
1
def first_method_source_location
-
@first_method_source_location ||= method_candidates[@rank].first.source_location
-
end
-
-
# @return [Array] The source location of the last method in this
-
# candidate's module definition.
-
1
def last_method_source_location
-
@end_method_source_location ||= method_candidates[@rank].last.source_location
-
end
-
-
# Return the number of lines between the start of the class definition
-
# and the start of the last method. We use this value so we can
-
# quickly grab these lines from the file (without having to
-
# check each intervening line for validity, which is expensive) speeding up source extraction.
-
# @return [Fixum] Number of lines.
-
1
def number_of_lines_in_first_chunk
-
end_method_line = last_method_source_location.last
-
-
end_method_line - line
-
end
-
end
-
end
-
end
-
1
class Pry
-
# `ObjectPath` implements the resolution of "object paths", which are strings
-
# that are similar to filesystem paths but meant for traversing Ruby objects.
-
# Examples of valid object paths include:
-
#
-
# x
-
# @foo/@bar
-
# "string"/upcase
-
# Pry/Method
-
#
-
# Object paths are mostly relevant in the context of the `cd` command.
-
# @see https://github.com/pry/pry/wiki/State-navigation
-
1
class ObjectPath
-
1
SPECIAL_TERMS = ["", "::", ".", ".."]
-
-
# @param [String] path_string The object path expressed as a string.
-
# @param [Array<Binding>] current_stack The current state of the binding
-
# stack.
-
1
def initialize(path_string, current_stack)
-
@path_string = path_string
-
@current_stack = current_stack
-
end
-
-
# @return [Array<Binding>] a new stack resulting from applying the given
-
# path to the current stack.
-
1
def resolve
-
scanner = StringScanner.new(@path_string.strip)
-
stack = @current_stack.dup
-
-
begin
-
next_segment = ""
-
-
loop do
-
# Scan for as long as we don't see a slash
-
next_segment << scanner.scan(/[^\/]*/)
-
-
if complete?(next_segment) || scanner.eos?
-
scanner.getch # consume the slash
-
break
-
else
-
next_segment << scanner.getch # append the slash
-
end
-
end
-
-
case next_segment.chomp
-
when ""
-
stack = [stack.first]
-
when "::"
-
stack.push(TOPLEVEL_BINDING)
-
when "."
-
next
-
when ".."
-
stack.pop unless stack.size == 1
-
else
-
stack.push(Pry.binding_for(stack.last.eval(next_segment)))
-
end
-
rescue RescuableException => e
-
return handle_failure(next_segment, e)
-
end until scanner.eos?
-
-
stack
-
end
-
-
1
private
-
-
1
def complete?(segment)
-
SPECIAL_TERMS.include?(segment) || Pry::Code.complete_expression?(segment)
-
end
-
-
1
def handle_failure(context, err)
-
msg = [
-
"Bad object path: #{@path_string.inspect}",
-
"Failed trying to resolve: #{context.inspect}",
-
"Exception: #{err.inspect}"
-
].join("\n")
-
-
raise CommandError.new(msg).tap { |e|
-
e.set_backtrace err.backtrace
-
}
-
end
-
end
-
end
-
1
class Pry
-
1
class Output
-
1
attr_reader :_pry_
-
-
1
def initialize(_pry_)
-
@_pry_ = _pry_
-
end
-
-
1
def puts(*objs)
-
return print "\n" if objs.empty?
-
-
objs.each do |obj|
-
if ary = Array.try_convert(obj)
-
puts(*ary)
-
else
-
print "#{obj.to_s.chomp}\n"
-
end
-
end
-
-
nil
-
end
-
-
1
def print(*objs)
-
objs.each do |obj|
-
_pry_.config.output.print decolorize_maybe(obj.to_s)
-
end
-
-
nil
-
end
-
1
alias << print
-
1
alias write print
-
-
# If _pry_.config.color is currently false, removes ansi escapes from the string.
-
1
def decolorize_maybe(str)
-
if _pry_.config.color
-
str
-
else
-
Helpers::Text.strip_color str
-
end
-
end
-
-
1
def method_missing(name, *args, &block)
-
_pry_.config.output.send(name, *args, &block)
-
end
-
-
1
def respond_to_missing?(*a)
-
_pry_.config.respond_to?(*a)
-
end
-
end
-
end
-
1
require 'pry/terminal'
-
-
# A pager is an `IO`-like object that accepts text and either prints it
-
# immediately, prints it one page at a time, or streams it to an external
-
# program to print one page at a time.
-
1
class Pry::Pager
-
1
class StopPaging < StandardError
-
end
-
-
1
attr_reader :_pry_
-
-
1
def initialize(_pry_)
-
@_pry_ = _pry_
-
end
-
-
# Send the given text through the best available pager (if `Pry.config.pager` is
-
# enabled).
-
# If you want to send text through in chunks as you generate it, use `open` to
-
# get a writable object instead.
-
# @param [String] text A piece of text to run through a pager.
-
# @param [IO] output (`$stdout`) An object to send output to.
-
1
def page(text)
-
open do |pager|
-
pager << text
-
end
-
end
-
-
# Yields a pager object (`NullPager`, `SimplePager`, or `SystemPager`). All
-
# pagers accept output with `#puts`, `#print`, `#write`, and `#<<`.
-
# @param [IO] output (`$stdout`) An object to send output to.
-
1
def open
-
pager = best_available
-
yield pager
-
rescue StopPaging
-
ensure
-
pager.close if pager
-
end
-
-
1
private
-
-
1
def enabled?; !!@enabled; end
-
-
1
def output; @output; end
-
-
# Return an instance of the "best" available pager class -- `SystemPager` if
-
# possible, `SimplePager` if `SystemPager` isn't available, and `NullPager`
-
# if the user has disabled paging. All pagers accept output with `#puts`,
-
# `#print`, `#write`, and `#<<`. You must call `#close` when you're done
-
# writing output to a pager, and you must rescue `Pry::Pager::StopPaging`.
-
# These requirements can be avoided by using `.open` instead.
-
# @param [#<<] output ($stdout) An object to send output to.
-
1
def best_available
-
if !_pry_.config.pager
-
NullPager.new(_pry_.output)
-
elsif !SystemPager.available? || Pry::Helpers::BaseHelpers.jruby?
-
SimplePager.new(_pry_.output)
-
else
-
SystemPager.new(_pry_.output)
-
end
-
end
-
-
# `NullPager` is a "pager" that actually just prints all output as it comes
-
# in. Used when `Pry.config.pager` is false.
-
1
class NullPager
-
1
def initialize(out)
-
@out = out
-
end
-
-
1
def puts(str)
-
print "#{str.chomp}\n"
-
end
-
-
1
def print(str)
-
write str
-
end
-
1
alias << print
-
-
1
def write(str)
-
@out.write str
-
end
-
-
1
def close
-
end
-
-
1
private
-
-
1
def height
-
@height ||= Pry::Terminal.height!
-
end
-
-
1
def width
-
@width ||= Pry::Terminal.width!
-
end
-
end
-
-
# `SimplePager` is a straightforward pure-Ruby pager. We use it on JRuby and
-
# when we can't find a usable external pager.
-
1
class SimplePager < NullPager
-
1
def initialize(*)
-
super
-
@tracker = PageTracker.new(height - 3, width)
-
end
-
-
1
def write(str)
-
str.lines.each do |line|
-
@out.print line
-
@tracker.record line
-
-
if @tracker.page?
-
@out.print "\n"
-
@out.print "\e[0m"
-
@out.print "<page break> --- Press enter to continue " \
-
"( q<enter> to break ) --- <page break>\n"
-
raise StopPaging if Readline.readline("").chomp == "q"
-
@tracker.reset
-
end
-
end
-
end
-
end
-
-
# `SystemPager` buffers output until we're pretty sure it's at least a page
-
# long, then invokes an external pager and starts streaming output to it. If
-
# `#close` is called before then, it just prints out the buffered content.
-
1
class SystemPager < NullPager
-
1
def self.default_pager
-
pager = ENV["PAGER"] || ""
-
-
# Default to less, and make sure less is being passed the correct options
-
if pager.strip.empty? or pager =~ /^less\b/
-
pager = "less -R -F -X"
-
end
-
-
pager
-
end
-
-
1
def self.available?
-
if @system_pager.nil?
-
@system_pager = begin
-
pager_executable = default_pager.split(' ').first
-
`which #{pager_executable}`
-
$?.success?
-
rescue
-
false
-
end
-
else
-
@system_pager
-
end
-
end
-
-
1
def initialize(*)
-
super
-
@tracker = PageTracker.new(height, width)
-
@buffer = ""
-
end
-
-
1
def write(str)
-
if invoked_pager?
-
write_to_pager str
-
else
-
@tracker.record str
-
@buffer << str
-
-
if @tracker.page?
-
write_to_pager @buffer
-
end
-
end
-
rescue Errno::EPIPE
-
raise StopPaging
-
end
-
-
1
def close
-
if invoked_pager?
-
pager.close
-
else
-
@out.puts @buffer
-
end
-
end
-
-
1
private
-
-
1
def write_to_pager(text)
-
pager.write @out.decolorize_maybe(text)
-
end
-
-
1
def invoked_pager?
-
@pager
-
end
-
-
1
def pager
-
@pager ||= IO.popen(self.class.default_pager, 'w')
-
end
-
end
-
-
# `PageTracker` tracks output to determine whether it's likely to take up a
-
# whole page. This doesn't need to be super precise, but we can use it for
-
# `SimplePager` and to avoid invoking the system pager unnecessarily.
-
#
-
# One simplifying assumption is that we don't need `#page?` to return `true`
-
# on the basis of an incomplete line. Long lines should be counted as
-
# multiple lines, but we don't have to transition from `false` to `true`
-
# until we see a newline.
-
1
class PageTracker
-
1
def initialize(rows, cols)
-
@rows, @cols = rows, cols
-
reset
-
end
-
-
1
def record(str)
-
str.lines.each do |line|
-
if line.end_with? "\n"
-
@row += ((@col + line_length(line) - 1) / @cols) + 1
-
@col = 0
-
else
-
@col += line_length(line)
-
end
-
end
-
end
-
-
1
def page?
-
@row >= @rows
-
end
-
-
1
def reset
-
@row = 0
-
@col = 0
-
end
-
-
1
private
-
-
# Approximation of the printable length of a given line, without the
-
# newline and without ANSI color codes.
-
1
def line_length(line)
-
line.chomp.gsub(/\e\[[\d;]*m/, '').length
-
end
-
end
-
end
-
1
class Pry
-
1
class PluginManager
-
1
PRY_PLUGIN_PREFIX = /^pry-/
-
-
# Placeholder when no associated gem found, displays warning
-
1
class NoPlugin
-
1
def initialize(name)
-
@name = name
-
end
-
-
1
def method_missing(*args)
-
warn "Warning: The plugin '#{@name}' was not found! (no gem found)"
-
end
-
end
-
-
1
class Plugin
-
1
attr_accessor :name, :gem_name, :enabled, :spec, :active
-
-
1
def initialize(name, gem_name, spec, enabled)
-
@name, @gem_name, @enabled, @spec = name, gem_name, enabled, spec
-
end
-
-
# Disable a plugin. (prevents plugin from being loaded, cannot
-
# disable an already activated plugin)
-
1
def disable!
-
self.enabled = false
-
end
-
-
# Enable a plugin. (does not load it immediately but puts on
-
# 'white list' to be loaded)
-
1
def enable!
-
self.enabled = true
-
end
-
-
# Load the Command line options defined by this plugin (if they exist)
-
1
def load_cli_options
-
cli_options_file = File.join(spec.full_gem_path, "lib/#{spec.name}/cli.rb")
-
require cli_options_file if File.exist?(cli_options_file)
-
end
-
# Activate the plugin (require the gem - enables/loads the
-
# plugin immediately at point of call, even if plugin is
-
# disabled)
-
# Does not reload plugin if it's already active.
-
1
def activate!
-
# Create the configuration object for the plugin.
-
Pry.config.send("#{gem_name.gsub('-', '_')}=", Pry::Config.from_hash({}))
-
-
begin
-
require gem_name if !active?
-
rescue LoadError => e
-
warn "Found plugin #{gem_name}, but could not require '#{gem_name}'"
-
warn e
-
rescue => e
-
warn "require '#{gem_name}' # Failed, saying: #{e}"
-
end
-
-
self.active = true
-
self.enabled = true
-
end
-
-
1
alias active? active
-
1
alias enabled? enabled
-
end
-
-
1
def initialize
-
1
@plugins = []
-
end
-
-
# Find all installed Pry plugins and store them in an internal array.
-
1
def locate_plugins
-
1
Gem.refresh
-
1
(Gem::Specification.respond_to?(:each) ? Gem::Specification : Gem.source_index.find_name('')).each do |gem|
-
103
next if gem.name !~ PRY_PLUGIN_PREFIX
-
plugin_name = gem.name.split('-', 2).last
-
@plugins << Plugin.new(plugin_name, gem.name, gem, true) if !gem_located?(gem.name)
-
end
-
1
@plugins
-
end
-
-
# @return [Hash] A hash with all plugin names (minus the 'pry-') as
-
# keys and Plugin objects as values.
-
1
def plugins
-
1
h = Hash.new { |_, key| NoPlugin.new(key) }
-
1
@plugins.each do |plugin|
-
h[plugin.name] = plugin
-
end
-
1
h
-
end
-
-
# Require all enabled plugins, disabled plugins are skipped.
-
1
def load_plugins
-
@plugins.each do |plugin|
-
plugin.activate! if plugin.enabled?
-
end
-
end
-
-
1
private
-
1
def gem_located?(gem_name)
-
@plugins.any? { |plugin| plugin.gem_name == gem_name }
-
end
-
end
-
-
end
-
1
class Pry::Prompt
-
1
MAP = {
-
"default" => {
-
value: Pry::DEFAULT_PROMPT,
-
description: "The default Pry prompt. Includes information about the\n" \
-
"current expression number, evaluation context, and nesting\n" \
-
"level, plus a reminder that you're using Pry."
-
},
-
-
"simple" => {
-
value: Pry::SIMPLE_PROMPT,
-
description: "A simple '>>'."
-
},
-
-
"nav" => {
-
value: Pry::NAV_PROMPT,
-
description: "A prompt that displays the binding stack as a path and\n" \
-
"includes information about _in_ and _out_."
-
},
-
-
"none" => {
-
value: Pry::NO_PROMPT,
-
description: "Wave goodbye to the Pry prompt."
-
}
-
}
-
end
-
1
require 'pry/config'
-
1
class Pry
-
-
1
HOME_RC_FILE = ENV["PRYRC"] || "~/.pryrc"
-
1
LOCAL_RC_FILE = "./.pryrc"
-
-
1
class << self
-
1
extend Forwardable
-
1
attr_accessor :custom_completions
-
1
attr_accessor :current_line
-
1
attr_accessor :line_buffer
-
1
attr_accessor :eval_path
-
1
attr_accessor :cli
-
1
attr_accessor :quiet
-
1
attr_accessor :last_internal_error
-
1
attr_accessor :config
-
1
attr_writer :history
-
-
1
def_delegators :@plugin_manager, :plugins, :load_plugins, :locate_plugins
-
-
1
extend Pry::Config::Convenience
-
1
config_shortcut(*Pry::Config.shortcuts)
-
-
1
def prompt=(value)
-
config.prompt = value
-
end
-
-
1
def prompt
-
config.prompt
-
end
-
-
1
def history
-
@history ||= History.new
-
end
-
end
-
-
#
-
# @return [main]
-
# returns the special instance of Object, "main".
-
#
-
1
def self.main
-
@main ||= TOPLEVEL_BINDING.eval "self"
-
end
-
-
#
-
# @return [Pry::Config]
-
# Returns a value store for an instance of Pry running on the current thread.
-
#
-
1
def self.current
-
Thread.current[:__pry__] ||= Pry::Config.from_hash({}, nil)
-
end
-
-
# Load the given file in the context of `Pry.toplevel_binding`
-
# @param [String] file The unexpanded file path.
-
1
def self.load_file_at_toplevel(file)
-
toplevel_binding.eval(File.read(file), file)
-
rescue RescuableException => e
-
puts "Error loading #{file}: #{e}\n#{e.backtrace.first}"
-
end
-
-
# Load HOME_RC_FILE and LOCAL_RC_FILE if appropriate
-
# This method can also be used to reload the files if they have changed.
-
1
def self.load_rc_files
-
rc_files_to_load.each do |file|
-
critical_section do
-
load_file_at_toplevel(file)
-
end
-
end
-
end
-
-
# Load the local RC file (./.pryrc)
-
1
def self.rc_files_to_load
-
files = []
-
files << HOME_RC_FILE if Pry.config.should_load_rc
-
files << LOCAL_RC_FILE if Pry.config.should_load_local_rc
-
files.map { |file| real_path_to(file) }.compact.uniq
-
end
-
-
# Expand a file to its canonical name (following symlinks as appropriate)
-
1
def self.real_path_to(file)
-
expanded = Pathname.new(File.expand_path(file)).realpath.to_s
-
# For rbx 1.9 mode [see rubinius issue #2165]
-
File.exist?(expanded) ? expanded : nil
-
rescue Errno::ENOENT
-
nil
-
end
-
-
# Load any Ruby files specified with the -r flag on the command line.
-
1
def self.load_requires
-
Pry.config.requires.each do |file|
-
require file
-
end
-
end
-
-
# Trap interrupts on jruby, and make them behave like MRI so we can
-
# catch them.
-
1
def self.load_traps
-
trap('INT'){ raise Interrupt }
-
end
-
-
1
def self.load_win32console
-
begin
-
require 'win32console'
-
# The mswin and mingw versions of pry require win32console, so this should
-
# only fail on jruby (where win32console doesn't work).
-
# Instead we'll recommend ansicon, which does.
-
rescue LoadError
-
warn <<-WARNING if Pry.config.windows_console_warning
-
For a better Pry experience on Windows, please use ansicon:
-
https://github.com/adoxa/ansicon
-
If you use an alternative to ansicon and don't want to see this warning again,
-
you can add "Pry.config.windows_console_warning = false" to your .pryrc.
-
WARNING
-
end
-
end
-
-
# Do basic setup for initial session.
-
# Including: loading .pryrc, loading plugins, loading requires, and
-
# loading history.
-
1
def self.initial_session_setup
-
return unless initial_session?
-
@initial_session = false
-
-
# note these have to be loaded here rather than in pry_instance as
-
# we only want them loaded once per entire Pry lifetime.
-
load_rc_files
-
load_plugins if Pry.config.should_load_plugins
-
load_requires if Pry.config.should_load_requires
-
load_history if Pry.config.history.should_load
-
load_traps if Pry.config.should_trap_interrupts
-
load_win32console if Pry::Helpers::BaseHelpers.windows? && !Pry::Helpers::BaseHelpers.windows_ansi?
-
end
-
-
# Start a Pry REPL.
-
# This method also loads `~/.pryrc` and `./.pryrc` as necessary the
-
# first time it is invoked.
-
# @param [Object, Binding] target The receiver of the Pry session
-
# @param [Hash] options
-
# @option options (see Pry#initialize)
-
# @example
-
# Pry.start(Object.new, :input => MyInput.new)
-
1
def self.start(target=nil, options={})
-
return if ENV['DISABLE_PRY']
-
options = options.to_hash
-
-
if in_critical_section?
-
output.puts "ERROR: Pry started inside Pry."
-
output.puts "This can happen if you have a binding.pry inside a #to_s or #inspect function."
-
return
-
end
-
-
options[:target] = Pry.binding_for(target || toplevel_binding)
-
options[:hooks] = Pry::Hooks.from_hash options.delete(:hooks) if options.key?(:hooks)
-
initial_session_setup
-
-
# Unless we were given a backtrace, save the current one
-
if options[:backtrace].nil?
-
options[:backtrace] = caller
-
-
# If Pry was started via `binding.pry`, elide that from the backtrace
-
if options[:backtrace].first =~ /pry.*core_extensions.*pry/
-
options[:backtrace].shift
-
end
-
end
-
-
driver = options[:driver] || Pry::REPL
-
-
# Enter the matrix
-
driver.start(options)
-
rescue Pry::TooSafeException
-
puts "ERROR: Pry cannot work with $SAFE > 0"
-
raise
-
end
-
-
# Execute the file through the REPL loop, non-interactively.
-
# @param [String] file_name File name to load through the REPL.
-
1
def self.load_file_through_repl(file_name)
-
require "pry/repl_file_loader"
-
REPLFileLoader.new(file_name).load
-
end
-
-
#
-
# An inspector that clips the output to `max_length` chars.
-
# In case of > `max_length` chars the `#<Object...> notation is used.
-
#
-
# @param [Object] obj
-
# The object to view.
-
#
-
# @param [Hash] options
-
# @option options [Integer] :max_length (60)
-
# The maximum number of chars before clipping occurs.
-
#
-
# @option options [Boolean] :id (false)
-
# Boolean to indicate whether or not a hex reprsentation of the object ID
-
# is attached to the return value when the length of inspect is greater than
-
# value of `:max_length`.
-
#
-
# @return [String]
-
# The string representation of `obj`.
-
#
-
1
def self.view_clip(obj, options = {})
-
max = options.fetch :max_length, 60
-
id = options.fetch :id, false
-
if obj.kind_of?(Module) && obj.name.to_s != "" && obj.name.to_s.length <= max
-
obj.name.to_s
-
elsif Pry.main == obj
-
# special-case to support jruby.
-
# fixed as of https://github.com/jruby/jruby/commit/d365ebd309cf9df3dde28f5eb36ea97056e0c039
-
# we can drop in the future.
-
obj.to_s
-
elsif Pry.config.prompt_safe_objects.any? { |v| v === obj } && obj.inspect.length <= max
-
obj.inspect
-
else
-
id == true ? "#<#{obj.class}:0x%x>" % (obj.object_id << 1) : "#<#{obj.class}>"
-
end
-
rescue RescuableException
-
"unknown"
-
end
-
-
# Load Readline history if required.
-
1
def self.load_history
-
Pry.history.load
-
end
-
-
# @return [Boolean] Whether this is the first time a Pry session has
-
# been started since loading the Pry class.
-
1
def self.initial_session?
-
@initial_session
-
end
-
-
# Run a Pry command from outside a session. The commands available are
-
# those referenced by `Pry.config.commands` (the default command set).
-
# @param [String] command_string The Pry command (including arguments,
-
# if any).
-
# @param [Hash] options Optional named parameters.
-
# @return [Object] The return value of the Pry command.
-
# @option options [Object, Binding] :target The object to run the
-
# command under. Defaults to `TOPLEVEL_BINDING` (main).
-
# @option options [Boolean] :show_output Whether to show command
-
# output. Defaults to true.
-
# @example Run at top-level with no output.
-
# Pry.run_command "ls"
-
# @example Run under Pry class, returning only public methods.
-
# Pry.run_command "ls -m", :target => Pry
-
# @example Display command output.
-
# Pry.run_command "ls -av", :show_output => true
-
1
def self.run_command(command_string, options={})
-
options = {
-
:target => TOPLEVEL_BINDING,
-
:show_output => true,
-
:output => Pry.config.output,
-
:commands => Pry.config.commands
-
}.merge!(options)
-
-
# :context for compatibility with <= 0.9.11.4
-
target = options[:context] || options[:target]
-
output = options[:show_output] ? options[:output] : StringIO.new
-
-
pry = Pry.new(:output => output, :target => target, :commands => options[:commands])
-
pry.eval command_string
-
end
-
-
1
def self.default_editor_for_platform
-
return ENV['VISUAL'] if ENV['VISUAL'] and not ENV['VISUAL'].empty?
-
return ENV['EDITOR'] if ENV['EDITOR'] and not ENV['EDITOR'].empty?
-
if Helpers::BaseHelpers.windows?
-
'notepad'
-
else
-
%w(editor nano vi).detect do |editor|
-
system("which #{editor} > /dev/null 2>&1")
-
end
-
end
-
end
-
-
1
def self.auto_resize!
-
Pry.config.input # by default, load Readline
-
-
if !defined?(Readline) || Pry.config.input != Readline
-
warn "Sorry, you must be using Readline for Pry.auto_resize! to work."
-
return
-
end
-
-
if Readline::VERSION =~ /edit/i
-
warn <<-EOT
-
Readline version #{Readline::VERSION} detected - will not auto_resize! correctly.
-
For the fix, use GNU Readline instead:
-
https://github.com/guard/guard/wiki/Add-proper-Readline-support-to-Ruby-on-Mac-OS-X
-
EOT
-
return
-
end
-
-
trap :WINCH do
-
begin
-
Readline.set_screen_size(*Terminal.size!)
-
rescue => e
-
warn "\nPry.auto_resize!'s Readline.set_screen_size failed: #{e}"
-
end
-
begin
-
Readline.refresh_line
-
rescue => e
-
warn "\nPry.auto_resize!'s Readline.refresh_line failed: #{e}"
-
end
-
end
-
end
-
-
# Set all the configurable options back to their default values
-
1
def self.reset_defaults
-
1
@initial_session = true
-
1
self.config = Pry::Config.new Pry::Config::Default.new
-
1
self.cli = false
-
1
self.current_line = 1
-
1
self.line_buffer = [""]
-
1
self.eval_path = "(pry)"
-
end
-
-
# Basic initialization.
-
1
def self.init
-
1
@plugin_manager ||= PluginManager.new
-
1
reset_defaults
-
1
locate_plugins
-
end
-
-
# Return a `Binding` object for `target` or return `target` if it is
-
# already a `Binding`.
-
# In the case where `target` is top-level then return `TOPLEVEL_BINDING`
-
# @param [Object] target The object to get a `Binding` object for.
-
# @return [Binding] The `Binding` object.
-
1
def self.binding_for(target)
-
if Binding === target
-
target
-
else
-
if Pry.main == target
-
TOPLEVEL_BINDING
-
else
-
target.__binding__
-
end
-
end
-
end
-
-
1
def self.toplevel_binding
-
unless defined?(@toplevel_binding) && @toplevel_binding
-
# Grab a copy of the TOPLEVEL_BINDING without any local variables.
-
# This binding has a default definee of Object, and new methods are
-
# private (just as in TOPLEVEL_BINDING).
-
TOPLEVEL_BINDING.eval <<-RUBY
-
def self.__pry__
-
binding
-
end
-
Pry.toplevel_binding = __pry__
-
class << self; undef __pry__; end
-
RUBY
-
end
-
@toplevel_binding.eval('private')
-
@toplevel_binding
-
end
-
-
1
def self.toplevel_binding=(binding)
-
@toplevel_binding = binding
-
end
-
-
1
def self.in_critical_section?
-
Thread.current[:pry_critical_section] ||= 0
-
Thread.current[:pry_critical_section] > 0
-
end
-
-
1
def self.critical_section(&block)
-
Thread.current[:pry_critical_section] ||= 0
-
Thread.current[:pry_critical_section] += 1
-
yield
-
ensure
-
Thread.current[:pry_critical_section] -= 1
-
end
-
end
-
-
1
Pry.init
-
# -*- coding: utf-8 -*-
-
##
-
# Pry is a powerful alternative to the standard IRB shell for Ruby. It
-
# features syntax highlighting, a flexible plugin architecture, runtime
-
# invocation and source and documentation browsing.
-
#
-
# Pry can be started similar to other command line utilities by simply running
-
# the following command:
-
#
-
# pry
-
#
-
# Once inside Pry you can invoke the help message:
-
#
-
# help
-
#
-
# This will show a list of available commands and their usage. For more
-
# information about Pry you can refer to the following resources:
-
#
-
# * http://pry.github.com/
-
# * https://github.com/pry/pry
-
# * the IRC channel, which is #pry on the Freenode network
-
#
-
-
1
class Pry
-
1
attr_accessor :binding_stack
-
1
attr_accessor :custom_completions
-
1
attr_accessor :eval_string
-
1
attr_accessor :backtrace
-
1
attr_accessor :suppress_output
-
1
attr_accessor :last_result
-
1
attr_accessor :last_file
-
1
attr_accessor :last_dir
-
-
1
attr_reader :last_exception
-
1
attr_reader :command_state
-
1
attr_reader :exit_value
-
1
attr_reader :input_array
-
1
attr_reader :output_array
-
1
attr_reader :config
-
-
1
extend Pry::Config::Convenience
-
1
config_shortcut(*Pry::Config.shortcuts)
-
1
EMPTY_COMPLETIONS = [].freeze
-
-
# Create a new {Pry} instance.
-
# @param [Hash] options
-
# @option options [#readline] :input
-
# The object to use for input.
-
# @option options [#puts] :output
-
# The object to use for output.
-
# @option options [Pry::CommandBase] :commands
-
# The object to use for commands.
-
# @option options [Hash] :hooks
-
# The defined hook Procs.
-
# @option options [Array<Proc>] :prompt
-
# The array of Procs to use for prompts.
-
# @option options [Proc] :print
-
# The Proc to use for printing return values.
-
# @option options [Boolean] :quiet
-
# Omit the `whereami` banner when starting.
-
# @option options [Array<String>] :backtrace
-
# The backtrace of the session's `binding.pry` line, if applicable.
-
# @option options [Object] :target
-
# The initial context for this session.
-
1
def initialize(options={})
-
@binding_stack = []
-
@indent = Pry::Indent.new
-
@command_state = {}
-
@eval_string = ""
-
@backtrace = options.delete(:backtrace) || caller
-
target = options.delete(:target)
-
@config = Pry::Config.new
-
config.merge!(options)
-
push_prompt(config.prompt)
-
@input_array = Pry::HistoryArray.new config.memory_size
-
@output_array = Pry::HistoryArray.new config.memory_size
-
@custom_completions = config.command_completions
-
set_last_result nil
-
@input_array << nil
-
push_initial_binding(target)
-
exec_hook(:when_started, target, options, self)
-
end
-
-
# The current prompt.
-
# This is the prompt at the top of the prompt stack.
-
#
-
# @example
-
# self.prompt = Pry::SIMPLE_PROMPT
-
# self.prompt # => Pry::SIMPLE_PROMPT
-
#
-
# @return [Array<Proc>] Current prompt.
-
1
def prompt
-
prompt_stack.last
-
end
-
-
1
def prompt=(new_prompt)
-
if prompt_stack.empty?
-
push_prompt new_prompt
-
else
-
prompt_stack[-1] = new_prompt
-
end
-
end
-
-
# Initialize this instance by pushing its initial context into the binding
-
# stack. If no target is given, start at the top level.
-
1
def push_initial_binding(target=nil)
-
push_binding(target || Pry.toplevel_binding)
-
end
-
-
# The currently active `Binding`.
-
# @return [Binding] The currently active `Binding` for the session.
-
1
def current_binding
-
binding_stack.last
-
end
-
1
alias current_context current_binding # support previous API
-
-
# Push a binding for the given object onto the stack. If this instance is
-
# currently stopped, mark it as usable again.
-
1
def push_binding(object)
-
@stopped = false
-
binding_stack << Pry.binding_for(object)
-
end
-
-
#
-
# Generate completions.
-
#
-
# @param [String] input
-
# What the user has typed so far
-
#
-
# @return [Array<String>]
-
# Possible completions
-
#
-
1
def complete(str)
-
return EMPTY_COMPLETIONS unless config.completer
-
Pry.critical_section do
-
completer = config.completer.new(config.input, self)
-
completer.call str, target: current_binding, custom_completions: custom_completions.call.push(*sticky_locals.keys)
-
end
-
end
-
-
#
-
# Injects a local variable into the provided binding.
-
#
-
# @param [String] name
-
# The name of the local to inject.
-
#
-
# @param [Object] value
-
# The value to set the local to.
-
#
-
# @param [Binding] b
-
# The binding to set the local on.
-
#
-
# @return [Object]
-
# The value the local was set to.
-
#
-
1
def inject_local(name, value, b)
-
value = Proc === value ? value.call : value
-
if b.respond_to?(:local_variable_set)
-
b.local_variable_set name, value
-
else # < 2.1
-
begin
-
Pry.current[:pry_local] = value
-
b.eval "#{name} = ::Pry.current[:pry_local]"
-
ensure
-
Pry.current[:pry_local] = nil
-
end
-
end
-
end
-
-
1
undef :memory_size if method_defined? :memory_size
-
# @return [Integer] The maximum amount of objects remembered by the inp and
-
# out arrays. Defaults to 100.
-
1
def memory_size
-
@output_array.max_size
-
end
-
-
1
undef :memory_size= if method_defined? :memory_size=
-
1
def memory_size=(size)
-
@input_array = Pry::HistoryArray.new(size)
-
@output_array = Pry::HistoryArray.new(size)
-
end
-
-
# Inject all the sticky locals into the current binding.
-
1
def inject_sticky_locals!
-
sticky_locals.each_pair do |name, value|
-
inject_local(name, value, current_binding)
-
end
-
end
-
-
# Add a sticky local to this Pry instance.
-
# A sticky local is a local that persists between all bindings in a session.
-
# @param [Symbol] name The name of the sticky local.
-
# @yield The block that defines the content of the local. The local
-
# will be refreshed at each tick of the repl loop.
-
1
def add_sticky_local(name, &block)
-
config.extra_sticky_locals[name] = block
-
end
-
-
1
def sticky_locals
-
{ _in_: input_array,
-
_out_: output_array,
-
_pry_: self,
-
_ex_: last_exception && last_exception.wrapped_exception,
-
_file_: last_file,
-
_dir_: last_dir,
-
_: proc { last_result },
-
__: proc { output_array[-2] }
-
}.merge(config.extra_sticky_locals)
-
end
-
-
# Reset the current eval string. If the user has entered part of a multiline
-
# expression, this discards that input.
-
1
def reset_eval_string
-
@eval_string = ""
-
end
-
-
# Pass a line of input to Pry.
-
#
-
# This is the equivalent of `Binding#eval` but with extra Pry!
-
#
-
# In particular:
-
# 1. Pry commands will be executed immediately if the line matches.
-
# 2. Partial lines of input will be queued up until a complete expression has
-
# been accepted.
-
# 3. Output is written to `#output` in pretty colours, not returned.
-
#
-
# Once this method has raised an exception or returned false, this instance
-
# is no longer usable. {#exit_value} will return the session's breakout
-
# value if applicable.
-
#
-
# @param [String?] line The line of input; `nil` if the user types `<Ctrl-D>`
-
# @option options [Boolean] :generated Whether this line was generated automatically.
-
# Generated lines are not stored in history.
-
# @return [Boolean] Is Pry ready to accept more input?
-
# @raise [Exception] If the user uses the `raise-up` command, this method
-
# will raise that exception.
-
1
def eval(line, options={})
-
return false if @stopped
-
-
exit_value = nil
-
exception = catch(:raise_up) do
-
exit_value = catch(:breakout) do
-
handle_line(line, options)
-
# We use 'return !@stopped' here instead of 'return true' so that if
-
# handle_line has stopped this pry instance (e.g. by opening _pry_.repl and
-
# then popping all the bindings) we still exit immediately.
-
return !@stopped
-
end
-
exception = false
-
end
-
-
@stopped = true
-
@exit_value = exit_value
-
-
# TODO: make this configurable?
-
raise exception if exception
-
return false
-
end
-
-
1
def handle_line(line, options)
-
if line.nil?
-
config.control_d_handler.call(@eval_string, self)
-
return
-
end
-
-
ensure_correct_encoding!(line)
-
Pry.history << line unless options[:generated]
-
-
@suppress_output = false
-
inject_sticky_locals!
-
begin
-
if !process_command_safely(line.lstrip)
-
@eval_string << "#{line.chomp}\n" if !line.empty? || !@eval_string.empty?
-
end
-
rescue RescuableException => e
-
self.last_exception = e
-
result = e
-
-
Pry.critical_section do
-
show_result(result)
-
end
-
return
-
end
-
-
# This hook is supposed to be executed after each line of ruby code
-
# has been read (regardless of whether eval_string is yet a complete expression)
-
exec_hook :after_read, eval_string, self
-
-
begin
-
complete_expr = Pry::Code.complete_expression?(@eval_string)
-
rescue SyntaxError => e
-
output.puts "SyntaxError: #{e.message.sub(/.*syntax error, */m, '')}"
-
reset_eval_string
-
end
-
-
if complete_expr
-
if @eval_string =~ /;\Z/ || @eval_string.empty? || @eval_string =~ /\A *#.*\n\z/
-
@suppress_output = true
-
end
-
-
# A bug in jruby makes java.lang.Exception not rescued by
-
# `rescue Pry::RescuableException` clause.
-
#
-
# * https://github.com/pry/pry/issues/854
-
# * https://jira.codehaus.org/browse/JRUBY-7100
-
#
-
# Until that gets fixed upstream, treat java.lang.Exception
-
# as an additional exception to be rescued explicitly.
-
#
-
# This workaround has a side effect: java exceptions specified
-
# in `Pry.config.exception_whitelist` are ignored.
-
jruby_exceptions = []
-
if Pry::Helpers::BaseHelpers.jruby?
-
jruby_exceptions << Java::JavaLang::Exception
-
end
-
-
begin
-
# Reset eval string, in case we're evaluating Ruby that does something
-
# like open a nested REPL on this instance.
-
eval_string = @eval_string
-
reset_eval_string
-
-
result = evaluate_ruby(eval_string)
-
rescue RescuableException, *jruby_exceptions => e
-
# Eliminate following warning:
-
# warning: singleton on non-persistent Java type X
-
# (http://wiki.jruby.org/Persistence)
-
if Pry::Helpers::BaseHelpers.jruby? && e.class.respond_to?('__persistent__')
-
e.class.__persistent__ = true
-
end
-
self.last_exception = e
-
result = e
-
end
-
-
Pry.critical_section do
-
show_result(result)
-
end
-
end
-
-
throw(:breakout) if current_binding.nil?
-
end
-
1
private :handle_line
-
-
# Potentially deprecated — Use `Pry::REPL.new(pry, :target => target).start`
-
# (If nested sessions are going to exist, this method is fine, but a goal is
-
# to come up with an alternative to nested sessions altogether.)
-
1
def repl(target = nil)
-
Pry::REPL.new(self, :target => target).start
-
end
-
-
1
def evaluate_ruby(code)
-
inject_sticky_locals!
-
exec_hook :before_eval, code, self
-
-
result = current_binding.eval(code, Pry.eval_path, Pry.current_line)
-
set_last_result(result, code)
-
ensure
-
update_input_history(code)
-
exec_hook :after_eval, result, self
-
end
-
-
# Output the result or pass to an exception handler (if result is an exception).
-
1
def show_result(result)
-
if last_result_is_exception?
-
exception_handler.call(output, result, self)
-
elsif should_print?
-
print.call(output, result, self)
-
else
-
# nothin'
-
end
-
rescue RescuableException => e
-
# Being uber-paranoid here, given that this exception arose because we couldn't
-
# serialize something in the user's program, let's not assume we can serialize
-
# the exception either.
-
begin
-
output.puts "(pry) output error: #{e.inspect}"
-
rescue RescuableException => e
-
if last_result_is_exception?
-
output.puts "(pry) output error: failed to show exception"
-
else
-
output.puts "(pry) output error: failed to show result"
-
end
-
end
-
ensure
-
output.flush if output.respond_to?(:flush)
-
end
-
-
# Force `eval_string` into the encoding of `val`. [Issue #284]
-
1
def ensure_correct_encoding!(val)
-
if @eval_string.empty? &&
-
val.respond_to?(:encoding) &&
-
val.encoding != @eval_string.encoding
-
@eval_string.force_encoding(val.encoding)
-
end
-
end
-
1
private :ensure_correct_encoding!
-
-
# If the given line is a valid command, process it in the context of the
-
# current `eval_string` and binding.
-
# @param [String] val The line to process.
-
# @return [Boolean] `true` if `val` is a command, `false` otherwise
-
1
def process_command(val)
-
val = val.chomp
-
result = commands.process_line(val,
-
:target => current_binding,
-
:output => output,
-
:eval_string => @eval_string,
-
:pry_instance => self
-
)
-
-
# set a temporary (just so we can inject the value we want into eval_string)
-
Pry.current[:pry_cmd_result] = result
-
-
# note that `result` wraps the result of command processing; if a
-
# command was matched and invoked then `result.command?` returns true,
-
# otherwise it returns false.
-
if result.command?
-
if !result.void_command?
-
# the command that was invoked was non-void (had a return value) and so we make
-
# the value of the current expression equal to the return value
-
# of the command.
-
@eval_string.replace "::Pry.current[:pry_cmd_result].retval\n"
-
end
-
true
-
else
-
false
-
end
-
end
-
-
# Same as process_command, but outputs exceptions to `#output` instead of
-
# raising.
-
# @param [String] val The line to process.
-
# @return [Boolean] `true` if `val` is a command, `false` otherwise
-
1
def process_command_safely(val)
-
process_command(val)
-
rescue CommandError, Slop::InvalidOptionError, MethodSource::SourceNotFoundError => e
-
Pry.last_internal_error = e
-
output.puts "Error: #{e.message}"
-
true
-
end
-
-
# Run the specified command.
-
# @param [String] val The command (and its params) to execute.
-
# @return [Pry::Command::VOID_VALUE]
-
# @example
-
# pry_instance.run_command("ls -m")
-
1
def run_command(val)
-
commands.process_line(val,
-
:eval_string => @eval_string,
-
:target => current_binding,
-
:pry_instance => self,
-
:output => output
-
)
-
Pry::Command::VOID_VALUE
-
end
-
-
# Execute the specified hook.
-
# @param [Symbol] name The hook name to execute
-
# @param [*Object] args The arguments to pass to the hook
-
# @return [Object, Exception] The return value of the hook or the exception raised
-
#
-
# If executing a hook raises an exception, we log that and then continue sucessfully.
-
# To debug such errors, use the global variable $pry_hook_error, which is set as a
-
# result.
-
1
def exec_hook(name, *args, &block)
-
e_before = hooks.errors.size
-
hooks.exec_hook(name, *args, &block).tap do
-
hooks.errors[e_before..-1].each do |e|
-
output.puts "#{name} hook failed: #{e.class}: #{e.message}"
-
output.puts "#{e.backtrace.first}"
-
output.puts "(see _pry_.hooks.errors to debug)"
-
end
-
end
-
end
-
-
# Set the last result of an eval.
-
# This method should not need to be invoked directly.
-
# @param [Object] result The result.
-
# @param [String] code The code that was run.
-
1
def set_last_result(result, code="")
-
@last_result_is_exception = false
-
@output_array << result
-
-
self.last_result = result unless code =~ /\A\s*\z/
-
end
-
-
#
-
# Set the last exception for a session.
-
#
-
# @param [Exception] e
-
# the last exception.
-
#
-
1
def last_exception=(e)
-
last_exception = Pry::LastException.new(e)
-
@last_result_is_exception = true
-
@output_array << last_exception
-
@last_exception = last_exception
-
end
-
-
# Update Pry's internal state after evalling code.
-
# This method should not need to be invoked directly.
-
# @param [String] code The code we just eval'd
-
1
def update_input_history(code)
-
# Always push to the @input_array as the @output_array is always pushed to.
-
@input_array << code
-
if code
-
Pry.line_buffer.push(*code.each_line)
-
Pry.current_line += code.lines.count
-
end
-
end
-
-
# @return [Boolean] True if the last result is an exception that was raised,
-
# as opposed to simply an instance of Exception (like the result of
-
# Exception.new)
-
1
def last_result_is_exception?
-
@last_result_is_exception
-
end
-
-
# Whether the print proc should be invoked.
-
# Currently only invoked if the output is not suppressed.
-
# @return [Boolean] Whether the print proc should be invoked.
-
1
def should_print?
-
!@suppress_output
-
end
-
-
# Returns the appropriate prompt to use.
-
# @return [String] The prompt.
-
1
def select_prompt
-
object = current_binding.eval('self')
-
-
open_token = @indent.open_delimiters.any? ? @indent.open_delimiters.last :
-
@indent.stack.last
-
-
c = Pry::Config.from_hash({
-
:object => object,
-
:nesting_level => binding_stack.size - 1,
-
:open_token => open_token,
-
:session_line => Pry.history.session_line_count + 1,
-
:history_line => Pry.history.history_line_count + 1,
-
:expr_number => input_array.count,
-
:_pry_ => self,
-
:binding_stack => binding_stack,
-
:input_array => input_array,
-
:eval_string => @eval_string,
-
:cont => !@eval_string.empty?})
-
-
Pry.critical_section do
-
# If input buffer is empty then use normal prompt
-
if eval_string.empty?
-
generate_prompt(Array(prompt).first, c)
-
-
# Otherwise use the wait prompt (indicating multi-line expression)
-
else
-
generate_prompt(Array(prompt).last, c)
-
end
-
end
-
end
-
-
1
def generate_prompt(prompt_proc, conf)
-
if prompt_proc.arity == 1
-
prompt_proc.call(conf)
-
else
-
prompt_proc.call(conf.object, conf.nesting_level, conf._pry_)
-
end
-
end
-
1
private :generate_prompt
-
-
# the array that the prompt stack is stored in
-
1
def prompt_stack
-
@prompt_stack ||= Array.new
-
end
-
1
private :prompt_stack
-
-
# Pushes the current prompt onto a stack that it can be restored from later.
-
# Use this if you wish to temporarily change the prompt.
-
# @param [Array<Proc>] new_prompt
-
# @return [Array<Proc>] new_prompt
-
# @example
-
# new_prompt = [ proc { '>' }, proc { '>>' } ]
-
# push_prompt(new_prompt) # => new_prompt
-
1
def push_prompt(new_prompt)
-
prompt_stack.push new_prompt
-
end
-
-
# Pops the current prompt off of the prompt stack.
-
# If the prompt you are popping is the last prompt, it will not be popped.
-
# Use this to restore the previous prompt.
-
# @return [Array<Proc>] Prompt being popped.
-
# @example
-
# prompt1 = [ proc { '>' }, proc { '>>' } ]
-
# prompt2 = [ proc { '$' }, proc { '>' } ]
-
# pry = Pry.new :prompt => prompt1
-
# pry.push_prompt(prompt2)
-
# pry.pop_prompt # => prompt2
-
# pry.pop_prompt # => prompt1
-
# pry.pop_prompt # => prompt1
-
1
def pop_prompt
-
prompt_stack.size > 1 ? prompt_stack.pop : prompt
-
end
-
-
1
undef :pager if method_defined? :pager
-
# Returns the currently configured pager
-
# @example
-
# _pry_.pager.page text
-
1
def pager
-
Pry::Pager.new(self)
-
end
-
-
1
undef :output if method_defined? :output
-
# Returns an output device
-
# @example
-
# _pry_.output.puts "ohai!"
-
1
def output
-
Pry::Output.new(self)
-
end
-
-
# Raise an exception out of Pry.
-
#
-
# See Kernel#raise for documentation of parameters.
-
# See rb_make_exception for the inbuilt implementation.
-
#
-
# This is necessary so that the raise-up command can tell the
-
# difference between an exception the user has decided to raise,
-
# and a mistake in specifying that exception.
-
#
-
# (i.e. raise-up RunThymeError.new should not be the same as
-
# raise-up NameError, "unititialized constant RunThymeError")
-
#
-
1
def raise_up_common(force, *args)
-
exception = if args == []
-
last_exception || RuntimeError.new
-
elsif args.length == 1 && args.first.is_a?(String)
-
RuntimeError.new(args.first)
-
elsif args.length > 3
-
raise ArgumentError, "wrong number of arguments"
-
elsif !args.first.respond_to?(:exception)
-
raise TypeError, "exception class/object expected"
-
elsif args.length === 1
-
args.first.exception
-
else
-
args.first.exception(args[1])
-
end
-
-
raise TypeError, "exception object expected" unless exception.is_a? Exception
-
-
exception.set_backtrace(args.length === 3 ? args[2] : caller(1))
-
-
if force || binding_stack.one?
-
binding_stack.clear
-
throw :raise_up, exception
-
else
-
binding_stack.pop
-
raise exception
-
end
-
end
-
1
def raise_up(*args); raise_up_common(false, *args); end
-
1
def raise_up!(*args); raise_up_common(true, *args); end
-
-
# Convenience accessor for the `quiet` config key.
-
# @return [Boolean]
-
1
def quiet?
-
config.quiet
-
end
-
end
-
1
class Pry
-
1
module RbxPath
-
1
module_function
-
1
def is_core_path?(path)
-
Pry::Helpers::BaseHelpers.rbx? && (path.start_with?("kernel") || path.start_with?("lib")) && File.exist?(convert_path_to_full(path))
-
end
-
-
1
def convert_path_to_full(path)
-
if path.start_with?("kernel")
-
File.join File.dirname(Rubinius::KERNEL_PATH), path
-
elsif path.start_with?("lib")
-
File.join File.dirname(Rubinius::LIB_PATH), path
-
else
-
path
-
end
-
end
-
-
1
def rvm_ruby?(path)
-
!!(path =~ /\.rvm/)
-
end
-
end
-
end
-
1
require 'forwardable'
-
-
1
class Pry
-
1
class REPL
-
1
extend Forwardable
-
1
def_delegators :@pry, :input, :output
-
-
# @return [Pry] The instance of {Pry} that the user is controlling.
-
1
attr_accessor :pry
-
-
# Instantiate a new {Pry} instance with the given options, then start a
-
# {REPL} instance wrapping it.
-
# @option options See {Pry#initialize}
-
1
def self.start(options)
-
new(Pry.new(options)).start
-
end
-
-
# Create an instance of {REPL} wrapping the given {Pry}.
-
# @param [Pry] pry The instance of {Pry} that this {REPL} will control.
-
# @param [Hash] options Options for this {REPL} instance.
-
# @option options [Object] :target The initial target of the session.
-
1
def initialize(pry, options = {})
-
@pry = pry
-
@indent = Pry::Indent.new
-
-
if options[:target]
-
@pry.push_binding options[:target]
-
end
-
end
-
-
# Start the read-eval-print loop.
-
# @return [Object?] If the session throws `:breakout`, return the value
-
# thrown with it.
-
# @raise [Exception] If the session throws `:raise_up`, raise the exception
-
# thrown with it.
-
1
def start
-
prologue
-
Pry::InputLock.for(:all).with_ownership { repl }
-
ensure
-
epilogue
-
end
-
-
1
private
-
-
# Set up the repl session.
-
# @return [void]
-
1
def prologue
-
pry.exec_hook :before_session, pry.output, pry.current_binding, pry
-
-
# Clear the line before starting Pry. This fixes issue #566.
-
if pry.config.correct_indent
-
Kernel.print Pry::Helpers::BaseHelpers.windows_ansi? ? "\e[0F" : "\e[0G"
-
end
-
end
-
-
# The actual read-eval-print loop.
-
#
-
# The {REPL} instance is responsible for reading and looping, whereas the
-
# {Pry} instance is responsible for evaluating user input and printing
-
# return values and command output.
-
#
-
# @return [Object?] If the session throws `:breakout`, return the value
-
# thrown with it.
-
# @raise [Exception] If the session throws `:raise_up`, raise the exception
-
# thrown with it.
-
1
def repl
-
loop do
-
case val = read
-
when :control_c
-
output.puts ""
-
pry.reset_eval_string
-
when :no_more_input
-
output.puts "" if output.tty?
-
break
-
else
-
output.puts "" if val.nil? && output.tty?
-
return pry.exit_value unless pry.eval(val)
-
end
-
end
-
end
-
-
# Clean up after the repl session.
-
# @return [void]
-
1
def epilogue
-
pry.exec_hook :after_session, pry.output, pry.current_binding, pry
-
end
-
-
# Read a line of input from the user.
-
# @return [String] The line entered by the user.
-
# @return [nil] On `<Ctrl-D>`.
-
# @return [:control_c] On `<Ctrl+C>`.
-
# @return [:no_more_input] On EOF.
-
1
def read
-
@indent.reset if pry.eval_string.empty?
-
current_prompt = pry.select_prompt
-
indentation = pry.config.auto_indent ? @indent.current_prefix : ''
-
-
val = read_line("#{current_prompt}#{indentation}")
-
-
# Return nil for EOF, :no_more_input for error, or :control_c for <Ctrl-C>
-
return val unless String === val
-
-
if pry.config.auto_indent
-
original_val = "#{indentation}#{val}"
-
indented_val = @indent.indent(val)
-
-
if output.tty? && pry.config.correct_indent && Pry::Helpers::BaseHelpers.use_ansi_codes?
-
output.print @indent.correct_indentation(
-
current_prompt, indented_val,
-
original_val.length - indented_val.length
-
)
-
output.flush
-
end
-
else
-
indented_val = val
-
end
-
-
indented_val
-
end
-
-
# Manage switching of input objects on encountering `EOFError`s.
-
# @return [Object] Whatever the given block returns.
-
# @return [:no_more_input] Indicates that no more input can be read.
-
1
def handle_read_errors
-
should_retry = true
-
exception_count = 0
-
-
begin
-
yield
-
rescue EOFError
-
pry.config.input = Pry.config.input
-
if !should_retry
-
output.puts "Error: Pry ran out of things to read from! " \
-
"Attempting to break out of REPL."
-
return :no_more_input
-
end
-
should_retry = false
-
retry
-
-
# Handle <Ctrl+C> like Bash: empty the current input buffer, but don't
-
# quit. This is only for MRI 1.9; other versions of Ruby don't let you
-
# send Interrupt from within Readline.
-
rescue Interrupt
-
return :control_c
-
-
# If we get a random error when trying to read a line we don't want to
-
# automatically retry, as the user will see a lot of error messages
-
# scroll past and be unable to do anything about it.
-
rescue RescuableException => e
-
puts "Error: #{e.message}"
-
output.puts e.backtrace
-
exception_count += 1
-
if exception_count < 5
-
retry
-
end
-
puts "FATAL: Pry failed to get user input using `#{input}`."
-
puts "To fix this you may be able to pass input and output file " \
-
"descriptors to pry directly. e.g."
-
puts " Pry.config.input = STDIN"
-
puts " Pry.config.output = STDOUT"
-
puts " binding.pry"
-
return :no_more_input
-
end
-
end
-
-
# Returns the next line of input to be sent to the {Pry} instance.
-
# @param [String] current_prompt The prompt to use for input.
-
# @return [String?] The next line of input, or `nil` on <Ctrl-D>.
-
1
def read_line(current_prompt)
-
handle_read_errors do
-
if defined? Coolline and input.is_a? Coolline
-
input.completion_proc = proc do |cool|
-
completions = @pry.complete cool.completed_word
-
completions.compact
-
end
-
elsif input.respond_to? :completion_proc=
-
input.completion_proc = proc do |input|
-
@pry.complete input
-
end
-
end
-
-
if defined?(Readline) and input == Readline
-
input_readline(current_prompt, false) # false since we'll add it manually
-
elsif defined? Coolline and input.is_a? Coolline
-
input_readline(current_prompt)
-
else
-
if input.method(:readline).arity == 1
-
input_readline(current_prompt)
-
else
-
input_readline
-
end
-
end
-
end
-
end
-
-
1
def input_readline(*args)
-
Pry::InputLock.for(:all).interruptible_region do
-
input.readline(*args)
-
end
-
end
-
end
-
end
-
1
require 'rubygems'
-
-
1
class Pry
-
1
module Rubygem
-
-
1
class << self
-
1
def installed?(name)
-
if Gem::Specification.respond_to?(:find_all_by_name)
-
Gem::Specification.find_all_by_name(name).any?
-
else
-
Gem.source_index.find_name(name).first
-
end
-
end
-
-
# Get the gem spec object for the given gem name.
-
#
-
# @param [String] name
-
# @return [Gem::Specification]
-
1
def spec(name)
-
specs = if Gem::Specification.respond_to?(:each)
-
Gem::Specification.find_all_by_name(name)
-
else
-
Gem.source_index.find_name(name)
-
end
-
-
first_spec = specs.sort_by{ |spec| Gem::Version.new(spec.version) }.last
-
-
first_spec or raise CommandError, "Gem `#{name}` not found"
-
end
-
-
# List gems matching a pattern.
-
#
-
# @param [Regexp] pattern
-
# @return [Array<Gem::Specification>]
-
1
def list(pattern = /.*/)
-
if Gem::Specification.respond_to?(:each)
-
Gem::Specification.select{|spec| spec.name =~ pattern }
-
else
-
Gem.source_index.gems.values.select{|spec| spec.name =~ pattern }
-
end
-
end
-
-
# Completion function for gem-cd and gem-open.
-
#
-
# @param [String] so_far what the user's typed so far
-
# @return [Array<String>] completions
-
1
def complete(so_far)
-
if so_far =~ / ([^ ]*)\z/
-
self.list(%r{\A#{$2}}).map(&:name)
-
else
-
self.list.map(&:name)
-
end
-
end
-
-
# Installs a gem with all its dependencies.
-
#
-
# @param [String] name
-
# @return [void]
-
1
def install(name)
-
gemrc_opts = Gem.configuration['gem'].split(' ')
-
destination = if gemrc_opts.include?('--user-install')
-
Gem.user_dir
-
elsif File.writable?(Gem.dir)
-
Gem.dir
-
else
-
Gem.user_dir
-
end
-
installer = Gem::DependencyInstaller.new(:install_dir => destination)
-
installer.install(name)
-
rescue Errno::EACCES
-
raise CommandError,
-
"Insufficient permissions to install #{ Pry::Helpers::Text.green(name) }."
-
rescue Gem::GemNotFoundException
-
raise CommandError,
-
"Gem #{ Pry::Helpers::Text.green(name) } not found. Aborting installation."
-
else
-
Gem.refresh
-
end
-
end
-
-
end
-
end
-
1
class Pry::Terminal
-
1
class << self
-
# Return a pair of [rows, columns] which gives the size of the window.
-
#
-
# If the window size cannot be determined, return nil.
-
1
def screen_size
-
rows, cols = actual_screen_size
-
if rows.to_i != 0 && cols.to_i != 0
-
[rows.to_i, cols.to_i]
-
else
-
nil
-
end
-
end
-
-
# Return a screen size or a default if that fails.
-
1
def size! default = [27, 80]
-
screen_size || default
-
end
-
-
# Return a screen width or the default if that fails.
-
1
def width!
-
size![1]
-
end
-
-
# Return a screen height or the default if that fails.
-
1
def height!
-
size![0]
-
end
-
-
1
def actual_screen_size
-
# The best way, if possible (requires non-jruby ≥1.9 or io-console gem)
-
screen_size_according_to_io_console or
-
# Fall back to the old standby, though it might be stale:
-
screen_size_according_to_env or
-
# Fall further back, though this one is also out of date without something
-
# calling Readline.set_screen_size
-
screen_size_according_to_readline or
-
# Windows users can otherwise run ansicon and get a decent answer:
-
screen_size_according_to_ansicon_env
-
end
-
-
1
def screen_size_according_to_io_console
-
return if Pry::Helpers::BaseHelpers.jruby?
-
require 'io/console'
-
$stdout.winsize if $stdout.tty? and $stdout.respond_to?(:winsize)
-
rescue LoadError
-
# They probably don't have the io/console stdlib or the io-console gem.
-
# We'll keep trying.
-
end
-
-
1
def screen_size_according_to_env
-
size = [ENV['LINES'] || ENV['ROWS'], ENV['COLUMNS']]
-
size if nonzero_column?(size)
-
end
-
-
1
def screen_size_according_to_readline
-
if defined?(Readline) && Readline.respond_to?(:get_screen_size)
-
size = Readline.get_screen_size
-
size if nonzero_column?(size)
-
end
-
rescue Java::JavaLang::NullPointerException
-
# This rescue won't happen on jrubies later than:
-
# https://github.com/jruby/jruby/pull/436
-
nil
-
end
-
-
1
def screen_size_according_to_ansicon_env
-
return unless ENV['ANSICON'] =~ /\((.*)x(.*)\)/
-
size = [$2, $1]
-
size if nonzero_column?(size)
-
end
-
-
1
private
-
-
1
def nonzero_column?(size)
-
size[1].to_i > 0
-
end
-
end
-
end
-
1
class Pry
-
1
VERSION = "0.10.1"
-
end
-
1
require 'pry/module_candidate'
-
-
1
class Pry
-
1
class << self
-
# If the given object is a `Pry::WrappedModule`, return it unaltered. If it's
-
# anything else, return it wrapped in a `Pry::WrappedModule` instance.
-
1
def WrappedModule(obj)
-
if obj.is_a? Pry::WrappedModule
-
obj
-
else
-
Pry::WrappedModule.new(obj)
-
end
-
end
-
end
-
-
1
class WrappedModule
-
1
include Helpers::BaseHelpers
-
1
include CodeObject::Helpers
-
-
1
attr_reader :wrapped
-
-
# Convert a string to a module.
-
#
-
# @param [String] mod_name
-
# @param [Binding] target The binding where the lookup takes place.
-
# @return [Module, nil] The module or `nil` (if conversion failed).
-
# @example
-
# Pry::WrappedModule.from_str("Pry::Code")
-
1
def self.from_str(mod_name, target=TOPLEVEL_BINDING)
-
if safe_to_evaluate?(mod_name, target)
-
Pry::WrappedModule.new(target.eval(mod_name))
-
else
-
nil
-
end
-
rescue RescuableException
-
nil
-
end
-
-
1
class << self
-
1
private
-
-
# We use this method to decide whether code is safe to eval. Method's are
-
# generally not, but everything else is.
-
# TODO: is just checking != "method" enough??
-
# TODO: see duplication of this method in Pry::CodeObject
-
# @param [String] str The string to lookup.
-
# @param [Binding] target Where the lookup takes place.
-
# @return [Boolean]
-
1
def safe_to_evaluate?(str, target)
-
return true if str.strip == "self"
-
kind = target.eval("defined?(#{str})")
-
kind =~ /variable|constant/
-
end
-
end
-
-
# @raise [ArgumentError] if the argument is not a `Module`
-
# @param [Module] mod
-
1
def initialize(mod)
-
raise ArgumentError, "Tried to initialize a WrappedModule with a non-module #{mod.inspect}" unless ::Module === mod
-
@wrapped = mod
-
@memoized_candidates = []
-
@host_file_lines = nil
-
@source = nil
-
@source_location = nil
-
@doc = nil
-
end
-
-
# Returns an array of the names of the constants accessible in the wrapped
-
# module. This avoids the problem of accidentally calling the singleton
-
# method `Module.constants`.
-
# @param [Boolean] inherit Include the names of constants from included
-
# modules?
-
1
def constants(inherit = true)
-
Module.instance_method(:constants).bind(@wrapped).call(inherit)
-
end
-
-
# The prefix that would appear before methods defined on this class.
-
#
-
# i.e. the "String." or "String#" in String.new and String#initialize.
-
#
-
# @return String
-
1
def method_prefix
-
if singleton_class?
-
if Module === singleton_instance
-
"#{WrappedModule.new(singleton_instance).nonblank_name}."
-
else
-
"self."
-
end
-
else
-
"#{nonblank_name}#"
-
end
-
end
-
-
# The name of the Module if it has one, otherwise #<Class:0xf00>.
-
#
-
# @return [String]
-
1
def nonblank_name
-
if name.to_s == ""
-
wrapped.inspect
-
else
-
name
-
end
-
end
-
-
# Is this a singleton class?
-
# @return [Boolean]
-
1
def singleton_class?
-
if Pry::Method.safe_send(wrapped, :respond_to?, :singleton_class?)
-
Pry::Method.safe_send(wrapped, :singleton_class?)
-
elsif defined?(Rubinius)
-
# https://github.com/rubinius/rubinius/commit/2e71722dba53d1a92c54d5e3968d64d1042486fe singleton_class? added 30 Jul 2014
-
# https://github.com/rubinius/rubinius/commit/4310f6b2ef3c8fc88135affe697db4e29e4621c4 has been around since 2011
-
!!Rubinius::Type.singleton_class_object(wrapped)
-
else
-
wrapped != Pry::Method.safe_send(wrapped, :ancestors).first
-
end
-
end
-
-
# Is this strictly a module? (does not match classes)
-
# @return [Boolean]
-
1
def module?
-
wrapped.instance_of?(Module)
-
end
-
-
# Is this strictly a class?
-
# @return [Boolean]
-
1
def class?
-
wrapped.instance_of?(Class)
-
end
-
-
# Get the instance associated with this singleton class.
-
#
-
# @raise ArgumentError: tried to get instance of non singleton class
-
#
-
# @return [Object]
-
1
def singleton_instance
-
raise ArgumentError, "tried to get instance of non singleton class" unless singleton_class?
-
-
if Helpers::BaseHelpers.jruby?
-
wrapped.to_java.attached
-
else
-
@singleton_instance ||= ObjectSpace.each_object(wrapped).detect{ |x| (class << x; self; end) == wrapped }
-
end
-
end
-
-
# Forward method invocations to the wrapped module
-
1
def method_missing(method_name, *args, &block)
-
wrapped.send(method_name, *args, &block)
-
end
-
-
1
def respond_to?(method_name)
-
super || wrapped.respond_to?(method_name)
-
end
-
-
# Retrieve the source location of a module. Return value is in same
-
# format as Method#source_location. If the source location
-
# cannot be found this method returns `nil`.
-
#
-
# @param [Module] mod The module (or class).
-
# @return [Array<String, Fixnum>, nil] The source location of the
-
# module (or class), or `nil` if no source location found.
-
1
def source_location
-
@source_location ||= primary_candidate.source_location
-
rescue Pry::RescuableException
-
nil
-
end
-
-
# @return [String, nil] The associated file for the module (i.e
-
# the primary candidate: highest ranked monkeypatch).
-
1
def file
-
Array(source_location).first
-
end
-
1
alias_method :source_file, :file
-
-
# @return [Fixnum, nil] The associated line for the module (i.e
-
# the primary candidate: highest ranked monkeypatch).
-
1
def line
-
Array(source_location).last
-
end
-
1
alias_method :source_line, :line
-
-
# Returns documentation for the module.
-
# This documentation is for the primary candidate, if
-
# you would like documentation for other candidates use
-
# `WrappedModule#candidate` to select the candidate you're
-
# interested in.
-
# @raise [Pry::CommandError] If documentation cannot be found.
-
# @return [String] The documentation for the module.
-
1
def doc
-
@doc ||= primary_candidate.doc
-
end
-
-
# Returns the source for the module.
-
# This source is for the primary candidate, if
-
# you would like source for other candidates use
-
# `WrappedModule#candidate` to select the candidate you're
-
# interested in.
-
# @raise [Pry::CommandError] If source cannot be found.
-
# @return [String] The source for the module.
-
1
def source
-
@source ||= primary_candidate.source
-
end
-
-
# @return [String] Return the associated file for the
-
# module from YARD, if one exists.
-
1
def yard_file
-
YARD::Registry.at(name).file if yard_docs?
-
end
-
-
# @return [Fixnum] Return the associated line for the
-
# module from YARD, if one exists.
-
1
def yard_line
-
YARD::Registry.at(name).line if yard_docs?
-
end
-
-
# @return [String] Return the YARD docs for this module.
-
1
def yard_doc
-
YARD::Registry.at(name).docstring.to_s if yard_docs?
-
end
-
-
# Return a candidate for this module of specified rank. A `rank`
-
# of 0 is equivalent to the 'primary candidate', which is the
-
# module definition with the highest number of methods. A `rank`
-
# of 1 is the module definition with the second highest number of
-
# methods, and so on. Module candidates are necessary as modules
-
# can be reopened multiple times and in multiple places in Ruby,
-
# the candidate API gives you access to the module definition
-
# representing each of those reopenings.
-
# @raise [Pry::CommandError] If the `rank` is out of range. That
-
# is greater than `number_of_candidates - 1`.
-
# @param [Fixnum] rank
-
# @return [Pry::WrappedModule::Candidate]
-
1
def candidate(rank)
-
@memoized_candidates[rank] ||= Candidate.new(self, rank)
-
end
-
-
# @return [Fixnum] The number of candidate definitions for the
-
# current module.
-
1
def number_of_candidates
-
method_candidates.count
-
end
-
-
# @note On JRuby 1.9 and higher, in certain conditions, this method chucks
-
# away its ability to be quick (when there are lots of monkey patches,
-
# like in Rails). However, it should be efficient enough on other rubies.
-
# @see https://github.com/jruby/jruby/issues/525
-
# @return [Enumerator, Array] on JRuby 1.9 and higher returns Array, on
-
# other rubies returns Enumerator
-
1
def candidates
-
enum = Enumerator.new do |y|
-
(0...number_of_candidates).each do |num|
-
y.yield candidate(num)
-
end
-
end
-
Pry::Helpers::BaseHelpers.jruby_19? ? enum.to_a : enum
-
end
-
-
# @return [Boolean] Whether YARD docs are available for this module.
-
1
def yard_docs?
-
!!(defined?(YARD) && YARD::Registry.at(name))
-
end
-
-
# @param [Fixnum] times How far to travel up the ancestor chain.
-
# @return [Pry::WrappedModule, nil] The wrapped module that is the
-
# superclass.
-
# When `self` is a `Module` then return the
-
# nth ancestor, otherwise (in the case of classes) return the
-
# nth ancestor that is a class.
-
1
def super(times=1)
-
return self if times.zero?
-
-
if wrapped.is_a?(Class)
-
sup = ancestors.select { |v| v.is_a?(Class) }[times]
-
else
-
sup = ancestors[times]
-
end
-
-
Pry::WrappedModule(sup) if sup
-
end
-
-
1
private
-
-
# @return [Pry::WrappedModule::Candidate] The candidate with the
-
# highest rank, that is the 'monkey patch' of this module with the
-
# highest number of methods, which contains a source code line that
-
# defines the module. It is considered the 'canonical' definition
-
# for the module. In the absense of a suitable candidate, the
-
# candidate of rank 0 will be returned, or a CommandError raised if
-
# there are no candidates at all.
-
1
def primary_candidate
-
@primary_candidate ||= candidates.find { |c| c.file } ||
-
# This will raise an exception if there is no candidate at all.
-
candidate(0)
-
end
-
-
# @return [Array<Array<Pry::Method>>] The array of `Pry::Method` objects,
-
# there are two associated with each candidate. The first is the 'base
-
# method' for a candidate and it serves as the start point for
-
# the search in uncovering the module definition. The second is
-
# the last method defined for that candidate and it is used to
-
# speed up source code extraction.
-
1
def method_candidates
-
@method_candidates ||= all_source_locations_by_popularity.map do |group|
-
methods_sorted_by_source_line = group.last.sort_by(&:source_line)
-
[methods_sorted_by_source_line.first, methods_sorted_by_source_line.last]
-
end
-
end
-
-
# A helper method.
-
1
def all_source_locations_by_popularity
-
return @all_source_locations_by_popularity if @all_source_locations_by_popularity
-
-
ims = all_relevant_methods_for(wrapped)
-
@all_source_locations_by_popularity = ims.group_by { |v| Array(v.source_location).first }.
-
sort_by do |path, methods|
-
expanded = File.expand_path(path)
-
load_order = $LOADED_FEATURES.index{ |file| expanded.end_with?(file) }
-
-
[-methods.size, load_order || (1.0 / 0.0)]
-
end
-
end
-
-
# We only want methods that have a non-nil `source_location`. We also
-
# skip some spooky internal methods.
-
# (i.e we skip `__class_init__` because it's an odd rbx specific thing that causes tests to fail.)
-
# @return [Array<Pry::Method>]
-
1
def all_relevant_methods_for(mod)
-
methods = all_methods_for(mod).select(&:source_location).
-
reject{ |x| x.name == '__class_init__' || method_defined_by_forwardable_module?(x) }
-
-
return methods unless methods.empty?
-
-
safe_send(mod, :constants).map do |const_name|
-
if const = nested_module?(mod, const_name)
-
all_relevant_methods_for(const)
-
else
-
[]
-
end
-
end.flatten
-
end
-
-
# Return all methods (instance methods and class methods) for a
-
# given module.
-
# @return [Array<Pry::Method>]
-
1
def all_methods_for(mod)
-
Pry::Method.all_from_obj(mod, false) + Pry::Method.all_from_class(mod, false)
-
end
-
-
1
def nested_module?(parent, name)
-
return if safe_send(parent, :autoload?, name)
-
child = safe_send(parent, :const_get, name)
-
return unless Module === child
-
return unless safe_send(child, :name) == "#{safe_send(parent, :name)}::#{name}"
-
child
-
end
-
-
# Detect methods that are defined with `def_delegator` from the Forwardable
-
# module. We want to reject these methods as they screw up module
-
# extraction since the `source_location` for such methods points at forwardable.rb
-
# TODO: make this more robust as valid user-defined files called
-
# forwardable.rb are also skipped.
-
1
def method_defined_by_forwardable_module?(method)
-
method.source_location.first =~ /forwardable\.rb/
-
end
-
-
# memoized lines for file
-
1
def lines_for_file(file)
-
@lines_for_file ||= {}
-
-
if file == Pry.eval_path
-
@lines_for_file[file] ||= Pry.line_buffer.drop(1)
-
else
-
@lines_for_file[file] ||= File.readlines(file)
-
end
-
end
-
end
-
end
-
# Copyright (C) 2007, 2008, 2009, 2010 Christian Neukirchen <purl.org/net/chneukirchen>
-
#
-
# Rack is freely distributable under the terms of an MIT-style license.
-
# See COPYING or http://www.opensource.org/licenses/mit-license.php.
-
-
# The Rack main module, serving as a namespace for all core Rack
-
# modules and classes.
-
#
-
# All modules meant for use in your application are <tt>autoload</tt>ed here,
-
# so it should be enough just to <tt>require rack.rb</tt> in your code.
-
-
1
module Rack
-
# The Rack protocol version number implemented.
-
1
VERSION = [1,2]
-
-
# Return the Rack protocol version as a dotted string.
-
1
def self.version
-
VERSION.join(".")
-
end
-
-
# Return the Rack release as a dotted string.
-
1
def self.release
-
1
"1.5"
-
end
-
-
1
autoload :Builder, "rack/builder"
-
1
autoload :BodyProxy, "rack/body_proxy"
-
1
autoload :Cascade, "rack/cascade"
-
1
autoload :Chunked, "rack/chunked"
-
1
autoload :CommonLogger, "rack/commonlogger"
-
1
autoload :ConditionalGet, "rack/conditionalget"
-
1
autoload :Config, "rack/config"
-
1
autoload :ContentLength, "rack/content_length"
-
1
autoload :ContentType, "rack/content_type"
-
1
autoload :ETag, "rack/etag"
-
1
autoload :File, "rack/file"
-
1
autoload :Deflater, "rack/deflater"
-
1
autoload :Directory, "rack/directory"
-
1
autoload :ForwardRequest, "rack/recursive"
-
1
autoload :Handler, "rack/handler"
-
1
autoload :Head, "rack/head"
-
1
autoload :Lint, "rack/lint"
-
1
autoload :Lock, "rack/lock"
-
1
autoload :Logger, "rack/logger"
-
1
autoload :MethodOverride, "rack/methodoverride"
-
1
autoload :Mime, "rack/mime"
-
1
autoload :NullLogger, "rack/nulllogger"
-
1
autoload :Recursive, "rack/recursive"
-
1
autoload :Reloader, "rack/reloader"
-
1
autoload :Runtime, "rack/runtime"
-
1
autoload :Sendfile, "rack/sendfile"
-
1
autoload :Server, "rack/server"
-
1
autoload :ShowExceptions, "rack/showexceptions"
-
1
autoload :ShowStatus, "rack/showstatus"
-
1
autoload :Static, "rack/static"
-
1
autoload :URLMap, "rack/urlmap"
-
1
autoload :Utils, "rack/utils"
-
1
autoload :Multipart, "rack/multipart"
-
-
1
autoload :MockRequest, "rack/mock"
-
1
autoload :MockResponse, "rack/mock"
-
-
1
autoload :Request, "rack/request"
-
1
autoload :Response, "rack/response"
-
-
1
module Auth
-
1
autoload :Basic, "rack/auth/basic"
-
1
autoload :AbstractRequest, "rack/auth/abstract/request"
-
1
autoload :AbstractHandler, "rack/auth/abstract/handler"
-
1
module Digest
-
1
autoload :MD5, "rack/auth/digest/md5"
-
1
autoload :Nonce, "rack/auth/digest/nonce"
-
1
autoload :Params, "rack/auth/digest/params"
-
1
autoload :Request, "rack/auth/digest/request"
-
end
-
end
-
-
1
module Session
-
1
autoload :Cookie, "rack/session/cookie"
-
1
autoload :Pool, "rack/session/pool"
-
1
autoload :Memcache, "rack/session/memcache"
-
end
-
-
1
module Utils
-
1
autoload :OkJson, "rack/utils/okjson"
-
end
-
end
-
1
module Rack
-
1
class BodyProxy
-
1
def initialize(body, &block)
-
4
@body, @block, @closed = body, block, false
-
end
-
-
1
def respond_to?(*args)
-
return false if args.first.to_s =~ /^to_ary$/
-
super or @body.respond_to?(*args)
-
end
-
-
1
def close
-
return if @closed
-
@closed = true
-
begin
-
@body.close if @body.respond_to? :close
-
ensure
-
@block.call
-
end
-
end
-
-
1
def closed?
-
@closed
-
end
-
-
# N.B. This method is a special case to address the bug described by #434.
-
# We are applying this special case for #each only. Future bugs of this
-
# class will be handled by requesting users to patch their ruby
-
# implementation, to save adding too many methods in this class.
-
1
def each(*args, &block)
-
@body.each(*args, &block)
-
end
-
-
1
def method_missing(*args, &block)
-
super if args.first.to_s =~ /^to_ary$/
-
@body.__send__(*args, &block)
-
end
-
end
-
end
-
1
module Rack
-
# Rack::Builder implements a small DSL to iteratively construct Rack
-
# applications.
-
#
-
# Example:
-
#
-
# require 'rack/lobster'
-
# app = Rack::Builder.new do
-
# use Rack::CommonLogger
-
# use Rack::ShowExceptions
-
# map "/lobster" do
-
# use Rack::Lint
-
# run Rack::Lobster.new
-
# end
-
# end
-
#
-
# run app
-
#
-
# Or
-
#
-
# app = Rack::Builder.app do
-
# use Rack::CommonLogger
-
# run lambda { |env| [200, {'Content-Type' => 'text/plain'}, ['OK']] }
-
# end
-
#
-
# run app
-
#
-
# +use+ adds middleware to the stack, +run+ dispatches to an application.
-
# You can use +map+ to construct a Rack::URLMap in a convenient way.
-
-
1
class Builder
-
1
def self.parse_file(config, opts = Server::Options.new)
-
options = {}
-
if config =~ /\.ru$/
-
cfgfile = ::File.read(config)
-
if cfgfile[/^#\\(.*)/] && opts
-
options = opts.parse! $1.split(/\s+/)
-
end
-
cfgfile.sub!(/^__END__\n.*\Z/m, '')
-
app = new_from_string cfgfile, config
-
else
-
require config
-
app = Object.const_get(::File.basename(config, '.rb').capitalize)
-
end
-
return app, options
-
end
-
-
1
def self.new_from_string(builder_script, file="(rackup)")
-
eval "Rack::Builder.new {\n" + builder_script + "\n}.to_app",
-
TOPLEVEL_BINDING, file, 0
-
end
-
-
1
def initialize(default_app = nil,&block)
-
3
@use, @map, @run = [], nil, default_app
-
3
instance_eval(&block) if block_given?
-
end
-
-
1
def self.app(default_app = nil, &block)
-
self.new(default_app, &block).to_app
-
end
-
-
# Specifies middleware to use in a stack.
-
#
-
# class Middleware
-
# def initialize(app)
-
# @app = app
-
# end
-
#
-
# def call(env)
-
# env["rack.some_header"] = "setting an example"
-
# @app.call(env)
-
# end
-
# end
-
#
-
# use Middleware
-
# run lambda { |env| [200, { "Content-Type => "text/plain" }, ["OK"]] }
-
#
-
# All requests through to this application will first be processed by the middleware class.
-
# The +call+ method in this example sets an additional environment key which then can be
-
# referenced in the application if required.
-
1
def use(middleware, *args, &block)
-
3
if @map
-
mapping, @map = @map, nil
-
@use << proc { |app| generate_map app, mapping }
-
end
-
4
@use << proc { |app| middleware.new(app, *args, &block) }
-
end
-
-
# Takes an argument that is an object that responds to #call and returns a Rack response.
-
# The simplest form of this is a lambda object:
-
#
-
# run lambda { |env| [200, { "Content-Type" => "text/plain" }, ["OK"]] }
-
#
-
# However this could also be a class:
-
#
-
# class Heartbeat
-
# def self.call(env)
-
# [200, { "Content-Type" => "text/plain" }, ["OK"]]
-
# end
-
# end
-
#
-
# run Heartbeat
-
1
def run(app)
-
1
@run = app
-
end
-
-
# Creates a route within the application.
-
#
-
# Rack::Builder.app do
-
# map '/' do
-
# run Heartbeat
-
# end
-
# end
-
#
-
# The +use+ method can also be used here to specify middleware to run under a specific path:
-
#
-
# Rack::Builder.app do
-
# map '/' do
-
# use Middleware
-
# run Heartbeat
-
# end
-
# end
-
#
-
# This example includes a piece of middleware which will run before requests hit +Heartbeat+.
-
#
-
1
def map(path, &block)
-
1
@map ||= {}
-
1
@map[path] = block
-
end
-
-
1
def to_app
-
1
app = @map ? generate_map(@run, @map) : @run
-
1
fail "missing run or map statement" unless app
-
2
@use.reverse.inject(app) { |a,e| e[a] }
-
end
-
-
1
def call(env)
-
to_app.call(env)
-
end
-
-
1
private
-
-
1
def generate_map(default_app, mapping)
-
1
mapped = default_app ? {'/' => default_app} : {}
-
2
mapping.each { |r,b| mapped[r] = self.class.new(default_app, &b) }
-
1
URLMap.new(mapped)
-
end
-
end
-
end
-
1
require 'rack/utils'
-
-
1
module Rack
-
-
# Middleware that applies chunked transfer encoding to response bodies
-
# when the response does not include a Content-Length header.
-
1
class Chunked
-
1
include Rack::Utils
-
-
# A body wrapper that emits chunked responses
-
1
class Body
-
1
TERM = "\r\n"
-
1
TAIL = "0#{TERM}#{TERM}"
-
-
1
include Rack::Utils
-
-
1
def initialize(body)
-
@body = body
-
end
-
-
1
def each
-
term = TERM
-
@body.each do |chunk|
-
size = bytesize(chunk)
-
next if size == 0
-
-
chunk = chunk.dup.force_encoding(Encoding::BINARY) if chunk.respond_to?(:force_encoding)
-
yield [size.to_s(16), term, chunk, term].join
-
end
-
yield TAIL
-
end
-
-
1
def close
-
@body.close if @body.respond_to?(:close)
-
end
-
end
-
-
1
def initialize(app)
-
@app = app
-
end
-
-
1
def call(env)
-
status, headers, body = @app.call(env)
-
headers = HeaderHash.new(headers)
-
-
if env['HTTP_VERSION'] == 'HTTP/1.0' ||
-
STATUS_WITH_NO_ENTITY_BODY.include?(status) ||
-
headers['Content-Length'] ||
-
headers['Transfer-Encoding']
-
[status, headers, body]
-
else
-
headers.delete('Content-Length')
-
headers['Transfer-Encoding'] = 'chunked'
-
[status, headers, Body.new(body)]
-
end
-
end
-
end
-
end
-
1
require 'rack/utils'
-
-
1
module Rack
-
-
# Middleware that enables conditional GET using If-None-Match and
-
# If-Modified-Since. The application should set either or both of the
-
# Last-Modified or Etag response headers according to RFC 2616. When
-
# either of the conditions is met, the response body is set to be zero
-
# length and the response status is set to 304 Not Modified.
-
#
-
# Applications that defer response body generation until the body's each
-
# message is received will avoid response body generation completely when
-
# a conditional GET matches.
-
#
-
# Adapted from Michael Klishin's Merb implementation:
-
# https://github.com/wycats/merb/blob/master/merb-core/lib/merb-core/rack/middleware/conditional_get.rb
-
1
class ConditionalGet
-
1
def initialize(app)
-
1
@app = app
-
end
-
-
1
def call(env)
-
case env['REQUEST_METHOD']
-
when "GET", "HEAD"
-
status, headers, body = @app.call(env)
-
headers = Utils::HeaderHash.new(headers)
-
if status == 200 && fresh?(env, headers)
-
status = 304
-
headers.delete('Content-Type')
-
headers.delete('Content-Length')
-
body = []
-
end
-
[status, headers, body]
-
else
-
@app.call(env)
-
end
-
end
-
-
1
private
-
-
1
def fresh?(env, headers)
-
modified_since = env['HTTP_IF_MODIFIED_SINCE']
-
none_match = env['HTTP_IF_NONE_MATCH']
-
-
return false unless modified_since || none_match
-
-
success = true
-
success &&= modified_since?(to_rfc2822(modified_since), headers) if modified_since
-
success &&= etag_matches?(none_match, headers) if none_match
-
success
-
end
-
-
1
def etag_matches?(none_match, headers)
-
etag = headers['ETag'] and etag == none_match
-
end
-
-
1
def modified_since?(modified_since, headers)
-
last_modified = to_rfc2822(headers['Last-Modified']) and
-
modified_since and
-
modified_since >= last_modified
-
end
-
-
1
def to_rfc2822(since)
-
Time.rfc2822(since) rescue nil
-
end
-
end
-
end
-
1
require 'digest/md5'
-
-
1
module Rack
-
# Automatically sets the ETag header on all String bodies.
-
#
-
# The ETag header is skipped if ETag or Last-Modified headers are sent or if
-
# a sendfile body (body.responds_to :to_path) is given (since such cases
-
# should be handled by apache/nginx).
-
#
-
# On initialization, you can pass two parameters: a Cache-Control directive
-
# used when Etag is absent and a directive when it is present. The first
-
# defaults to nil, while the second defaults to "max-age=0, private, must-revalidate"
-
1
class ETag
-
1
DEFAULT_CACHE_CONTROL = "max-age=0, private, must-revalidate".freeze
-
-
1
def initialize(app, no_cache_control = nil, cache_control = DEFAULT_CACHE_CONTROL)
-
1
@app = app
-
1
@cache_control = cache_control
-
1
@no_cache_control = no_cache_control
-
end
-
-
1
def call(env)
-
status, headers, body = @app.call(env)
-
-
if etag_status?(status) && etag_body?(body) && !skip_caching?(headers)
-
digest, body = digest_body(body)
-
headers['ETag'] = %("#{digest}") if digest
-
end
-
-
unless headers['Cache-Control']
-
if digest
-
headers['Cache-Control'] = @cache_control if @cache_control
-
else
-
headers['Cache-Control'] = @no_cache_control if @no_cache_control
-
end
-
end
-
-
[status, headers, body]
-
end
-
-
1
private
-
-
1
def etag_status?(status)
-
status == 200 || status == 201
-
end
-
-
1
def etag_body?(body)
-
!body.respond_to?(:to_path)
-
end
-
-
1
def skip_caching?(headers)
-
(headers['Cache-Control'] && headers['Cache-Control'].include?('no-cache')) ||
-
headers.key?('ETag') || headers.key?('Last-Modified')
-
end
-
-
1
def digest_body(body)
-
parts = []
-
body.each { |part| parts << part }
-
string_body = parts.join
-
digest = Digest::MD5.hexdigest(string_body) unless string_body.empty?
-
[digest, parts]
-
end
-
end
-
end
-
1
require 'time'
-
1
require 'rack/utils'
-
1
require 'rack/mime'
-
-
1
module Rack
-
# Rack::File serves files below the +root+ directory given, according to the
-
# path info of the Rack request.
-
# e.g. when Rack::File.new("/etc") is used, you can access 'passwd' file
-
# as http://localhost:9292/passwd
-
#
-
# Handlers can detect if bodies are a Rack::File, and use mechanisms
-
# like sendfile on the +path+.
-
-
1
class File
-
1
SEPS = Regexp.union(*[::File::SEPARATOR, ::File::ALT_SEPARATOR].compact)
-
1
ALLOWED_VERBS = %w[GET HEAD]
-
-
1
attr_accessor :root
-
1
attr_accessor :path
-
1
attr_accessor :cache_control
-
-
1
alias :to_path :path
-
-
1
def initialize(root, headers={}, default_mime = 'text/plain')
-
1
@root = root
-
1
@headers = headers
-
1
@default_mime = default_mime
-
end
-
-
1
def call(env)
-
dup._call(env)
-
end
-
-
1
F = ::File
-
-
1
def _call(env)
-
unless ALLOWED_VERBS.include? env["REQUEST_METHOD"]
-
return fail(405, "Method Not Allowed")
-
end
-
-
path_info = Utils.unescape(env["PATH_INFO"])
-
parts = path_info.split SEPS
-
-
clean = []
-
-
parts.each do |part|
-
next if part.empty? || part == '.'
-
part == '..' ? clean.pop : clean << part
-
end
-
-
@path = F.join(@root, *clean)
-
-
available = begin
-
F.file?(@path) && F.readable?(@path)
-
rescue SystemCallError
-
false
-
end
-
-
if available
-
serving(env)
-
else
-
fail(404, "File not found: #{path_info}")
-
end
-
end
-
-
1
def serving(env)
-
last_modified = F.mtime(@path).httpdate
-
return [304, {}, []] if env['HTTP_IF_MODIFIED_SINCE'] == last_modified
-
-
headers = { "Last-Modified" => last_modified }
-
mime = Mime.mime_type(F.extname(@path), @default_mime)
-
headers["Content-Type"] = mime if mime
-
-
# Set custom headers
-
@headers.each { |field, content| headers[field] = content } if @headers
-
-
response = [ 200, headers, env["REQUEST_METHOD"] == "HEAD" ? [] : self ]
-
-
# NOTE:
-
# We check via File::size? whether this file provides size info
-
# via stat (e.g. /proc files often don't), otherwise we have to
-
# figure it out by reading the whole file into memory.
-
size = F.size?(@path) || Utils.bytesize(F.read(@path))
-
-
ranges = Rack::Utils.byte_ranges(env, size)
-
if ranges.nil? || ranges.length > 1
-
# No ranges, or multiple ranges (which we don't support):
-
# TODO: Support multiple byte-ranges
-
response[0] = 200
-
@range = 0..size-1
-
elsif ranges.empty?
-
# Unsatisfiable. Return error, and file size:
-
response = fail(416, "Byte range unsatisfiable")
-
response[1]["Content-Range"] = "bytes */#{size}"
-
return response
-
else
-
# Partial content:
-
@range = ranges[0]
-
response[0] = 206
-
response[1]["Content-Range"] = "bytes #{@range.begin}-#{@range.end}/#{size}"
-
size = @range.end - @range.begin + 1
-
end
-
-
response[1]["Content-Length"] = size.to_s
-
response
-
end
-
-
1
def each
-
F.open(@path, "rb") do |file|
-
file.seek(@range.begin)
-
remaining_len = @range.end-@range.begin+1
-
while remaining_len > 0
-
part = file.read([8192, remaining_len].min)
-
break unless part
-
remaining_len -= part.length
-
-
yield part
-
end
-
end
-
end
-
-
1
private
-
-
1
def fail(status, body)
-
body += "\n"
-
[
-
status,
-
{
-
"Content-Type" => "text/plain",
-
"Content-Length" => body.size.to_s,
-
"X-Cascade" => "pass"
-
},
-
[body]
-
]
-
end
-
-
end
-
end
-
1
module Rack
-
-
1
class Head
-
# Rack::Head returns an empty body for all HEAD requests. It leaves
-
# all other requests unchanged.
-
1
def initialize(app)
-
1
@app = app
-
end
-
-
1
def call(env)
-
status, headers, body = @app.call(env)
-
-
if env["REQUEST_METHOD"] == "HEAD"
-
body.close if body.respond_to? :close
-
[status, headers, []]
-
else
-
[status, headers, body]
-
end
-
end
-
end
-
-
end
-
1
require 'rack/utils'
-
1
require 'forwardable'
-
-
1
module Rack
-
# Rack::Lint validates your application and the requests and
-
# responses according to the Rack spec.
-
-
1
class Lint
-
1
def initialize(app)
-
@app = app
-
@content_length = nil
-
end
-
-
# :stopdoc:
-
-
1
class LintError < RuntimeError; end
-
1
module Assertion
-
1
def assert(message, &block)
-
unless block.call
-
raise LintError, message
-
end
-
end
-
end
-
1
include Assertion
-
-
## This specification aims to formalize the Rack protocol. You
-
## can (and should) use Rack::Lint to enforce it.
-
##
-
## When you develop middleware, be sure to add a Lint before and
-
## after to catch all mistakes.
-
-
## = Rack applications
-
-
## A Rack application is a Ruby object (not a class) that
-
## responds to +call+.
-
1
def call(env=nil)
-
dup._call(env)
-
end
-
-
1
def _call(env)
-
## It takes exactly one argument, the *environment*
-
assert("No env given") { env }
-
check_env env
-
-
env['rack.input'] = InputWrapper.new(env['rack.input'])
-
env['rack.errors'] = ErrorWrapper.new(env['rack.errors'])
-
-
## and returns an Array of exactly three values:
-
status, headers, @body = @app.call(env)
-
## The *status*,
-
check_status status
-
## the *headers*,
-
check_headers headers
-
-
check_hijack_response headers, env
-
-
## and the *body*.
-
check_content_type status, headers
-
check_content_length status, headers
-
@head_request = env["REQUEST_METHOD"] == "HEAD"
-
[status, headers, self]
-
end
-
-
## == The Environment
-
1
def check_env(env)
-
## The environment must be an instance of Hash that includes
-
## CGI-like headers. The application is free to modify the
-
## environment.
-
assert("env #{env.inspect} is not a Hash, but #{env.class}") {
-
env.kind_of? Hash
-
}
-
-
##
-
## The environment is required to include these variables
-
## (adopted from PEP333), except when they'd be empty, but see
-
## below.
-
-
## <tt>REQUEST_METHOD</tt>:: The HTTP request method, such as
-
## "GET" or "POST". This cannot ever
-
## be an empty string, and so is
-
## always required.
-
-
## <tt>SCRIPT_NAME</tt>:: The initial portion of the request
-
## URL's "path" that corresponds to the
-
## application object, so that the
-
## application knows its virtual
-
## "location". This may be an empty
-
## string, if the application corresponds
-
## to the "root" of the server.
-
-
## <tt>PATH_INFO</tt>:: The remainder of the request URL's
-
## "path", designating the virtual
-
## "location" of the request's target
-
## within the application. This may be an
-
## empty string, if the request URL targets
-
## the application root and does not have a
-
## trailing slash. This value may be
-
## percent-encoded when I originating from
-
## a URL.
-
-
## <tt>QUERY_STRING</tt>:: The portion of the request URL that
-
## follows the <tt>?</tt>, if any. May be
-
## empty, but is always required!
-
-
## <tt>SERVER_NAME</tt>, <tt>SERVER_PORT</tt>:: When combined with <tt>SCRIPT_NAME</tt> and <tt>PATH_INFO</tt>, these variables can be used to complete the URL. Note, however, that <tt>HTTP_HOST</tt>, if present, should be used in preference to <tt>SERVER_NAME</tt> for reconstructing the request URL. <tt>SERVER_NAME</tt> and <tt>SERVER_PORT</tt> can never be empty strings, and so are always required.
-
-
## <tt>HTTP_</tt> Variables:: Variables corresponding to the
-
## client-supplied HTTP request
-
## headers (i.e., variables whose
-
## names begin with <tt>HTTP_</tt>). The
-
## presence or absence of these
-
## variables should correspond with
-
## the presence or absence of the
-
## appropriate HTTP header in the
-
## request. See <a href="https://tools.ietf.org/html/rfc3875#section-4.1.18">
-
## RFC3875 section 4.1.18</a> for specific behavior.
-
-
## In addition to this, the Rack environment must include these
-
## Rack-specific variables:
-
-
## <tt>rack.version</tt>:: The Array representing this version of Rack. See Rack::VERSION, that corresponds to the version of this SPEC.
-
## <tt>rack.url_scheme</tt>:: +http+ or +https+, depending on the request URL.
-
## <tt>rack.input</tt>:: See below, the input stream.
-
## <tt>rack.errors</tt>:: See below, the error stream.
-
## <tt>rack.multithread</tt>:: true if the application object may be simultaneously invoked by another thread in the same process, false otherwise.
-
## <tt>rack.multiprocess</tt>:: true if an equivalent application object may be simultaneously invoked by another process, false otherwise.
-
## <tt>rack.run_once</tt>:: true if the server expects (but does not guarantee!) that the application will only be invoked this one time during the life of its containing process. Normally, this will only be true for a server based on CGI (or something similar).
-
## <tt>rack.hijack?</tt>:: present and true if the server supports connection hijacking. See below, hijacking.
-
## <tt>rack.hijack</tt>:: an object responding to #call that must be called at least once before using rack.hijack_io. It is recommended #call return rack.hijack_io as well as setting it in env if necessary.
-
## <tt>rack.hijack_io</tt>:: if rack.hijack? is true, and rack.hijack has received #call, this will contain an object resembling an IO. See hijacking.
-
##
-
-
## Additional environment specifications have approved to
-
## standardized middleware APIs. None of these are required to
-
## be implemented by the server.
-
-
## <tt>rack.session</tt>:: A hash like interface for storing request session data.
-
## The store must implement:
-
if session = env['rack.session']
-
## store(key, value) (aliased as []=);
-
assert("session #{session.inspect} must respond to store and []=") {
-
session.respond_to?(:store) && session.respond_to?(:[]=)
-
}
-
-
## fetch(key, default = nil) (aliased as []);
-
assert("session #{session.inspect} must respond to fetch and []") {
-
session.respond_to?(:fetch) && session.respond_to?(:[])
-
}
-
-
## delete(key);
-
assert("session #{session.inspect} must respond to delete") {
-
session.respond_to?(:delete)
-
}
-
-
## clear;
-
assert("session #{session.inspect} must respond to clear") {
-
session.respond_to?(:clear)
-
}
-
end
-
-
## <tt>rack.logger</tt>:: A common object interface for logging messages.
-
## The object must implement:
-
if logger = env['rack.logger']
-
## info(message, &block)
-
assert("logger #{logger.inspect} must respond to info") {
-
logger.respond_to?(:info)
-
}
-
-
## debug(message, &block)
-
assert("logger #{logger.inspect} must respond to debug") {
-
logger.respond_to?(:debug)
-
}
-
-
## warn(message, &block)
-
assert("logger #{logger.inspect} must respond to warn") {
-
logger.respond_to?(:warn)
-
}
-
-
## error(message, &block)
-
assert("logger #{logger.inspect} must respond to error") {
-
logger.respond_to?(:error)
-
}
-
-
## fatal(message, &block)
-
assert("logger #{logger.inspect} must respond to fatal") {
-
logger.respond_to?(:fatal)
-
}
-
end
-
-
## The server or the application can store their own data in the
-
## environment, too. The keys must contain at least one dot,
-
## and should be prefixed uniquely. The prefix <tt>rack.</tt>
-
## is reserved for use with the Rack core distribution and other
-
## accepted specifications and must not be used otherwise.
-
##
-
-
%w[REQUEST_METHOD SERVER_NAME SERVER_PORT
-
QUERY_STRING
-
rack.version rack.input rack.errors
-
rack.multithread rack.multiprocess rack.run_once].each { |header|
-
assert("env missing required key #{header}") { env.include? header }
-
}
-
-
## The environment must not contain the keys
-
## <tt>HTTP_CONTENT_TYPE</tt> or <tt>HTTP_CONTENT_LENGTH</tt>
-
## (use the versions without <tt>HTTP_</tt>).
-
%w[HTTP_CONTENT_TYPE HTTP_CONTENT_LENGTH].each { |header|
-
assert("env contains #{header}, must use #{header[5,-1]}") {
-
not env.include? header
-
}
-
}
-
-
## The CGI keys (named without a period) must have String values.
-
env.each { |key, value|
-
next if key.include? "." # Skip extensions
-
assert("env variable #{key} has non-string value #{value.inspect}") {
-
value.kind_of? String
-
}
-
}
-
-
##
-
## There are the following restrictions:
-
-
## * <tt>rack.version</tt> must be an array of Integers.
-
assert("rack.version must be an Array, was #{env["rack.version"].class}") {
-
env["rack.version"].kind_of? Array
-
}
-
## * <tt>rack.url_scheme</tt> must either be +http+ or +https+.
-
assert("rack.url_scheme unknown: #{env["rack.url_scheme"].inspect}") {
-
%w[http https].include? env["rack.url_scheme"]
-
}
-
-
## * There must be a valid input stream in <tt>rack.input</tt>.
-
check_input env["rack.input"]
-
## * There must be a valid error stream in <tt>rack.errors</tt>.
-
check_error env["rack.errors"]
-
## * There may be a valid hijack stream in <tt>rack.hijack_io</tt>
-
check_hijack env
-
-
## * The <tt>REQUEST_METHOD</tt> must be a valid token.
-
assert("REQUEST_METHOD unknown: #{env["REQUEST_METHOD"]}") {
-
env["REQUEST_METHOD"] =~ /\A[0-9A-Za-z!\#$%&'*+.^_`|~-]+\z/
-
}
-
-
## * The <tt>SCRIPT_NAME</tt>, if non-empty, must start with <tt>/</tt>
-
assert("SCRIPT_NAME must start with /") {
-
!env.include?("SCRIPT_NAME") ||
-
env["SCRIPT_NAME"] == "" ||
-
env["SCRIPT_NAME"] =~ /\A\//
-
}
-
## * The <tt>PATH_INFO</tt>, if non-empty, must start with <tt>/</tt>
-
assert("PATH_INFO must start with /") {
-
!env.include?("PATH_INFO") ||
-
env["PATH_INFO"] == "" ||
-
env["PATH_INFO"] =~ /\A\//
-
}
-
## * The <tt>CONTENT_LENGTH</tt>, if given, must consist of digits only.
-
assert("Invalid CONTENT_LENGTH: #{env["CONTENT_LENGTH"]}") {
-
!env.include?("CONTENT_LENGTH") || env["CONTENT_LENGTH"] =~ /\A\d+\z/
-
}
-
-
## * One of <tt>SCRIPT_NAME</tt> or <tt>PATH_INFO</tt> must be
-
## set. <tt>PATH_INFO</tt> should be <tt>/</tt> if
-
## <tt>SCRIPT_NAME</tt> is empty.
-
assert("One of SCRIPT_NAME or PATH_INFO must be set (make PATH_INFO '/' if SCRIPT_NAME is empty)") {
-
env["SCRIPT_NAME"] || env["PATH_INFO"]
-
}
-
## <tt>SCRIPT_NAME</tt> never should be <tt>/</tt>, but instead be empty.
-
assert("SCRIPT_NAME cannot be '/', make it '' and PATH_INFO '/'") {
-
env["SCRIPT_NAME"] != "/"
-
}
-
end
-
-
## === The Input Stream
-
##
-
## The input stream is an IO-like object which contains the raw HTTP
-
## POST data.
-
1
def check_input(input)
-
## When applicable, its external encoding must be "ASCII-8BIT" and it
-
## must be opened in binary mode, for Ruby 1.9 compatibility.
-
assert("rack.input #{input} does not have ASCII-8BIT as its external encoding") {
-
input.external_encoding.name == "ASCII-8BIT"
-
} if input.respond_to?(:external_encoding)
-
assert("rack.input #{input} is not opened in binary mode") {
-
input.binmode?
-
} if input.respond_to?(:binmode?)
-
-
## The input stream must respond to +gets+, +each+, +read+ and +rewind+.
-
[:gets, :each, :read, :rewind].each { |method|
-
assert("rack.input #{input} does not respond to ##{method}") {
-
input.respond_to? method
-
}
-
}
-
end
-
-
1
class InputWrapper
-
1
include Assertion
-
-
1
def initialize(input)
-
@input = input
-
end
-
-
## * +gets+ must be called without arguments and return a string,
-
## or +nil+ on EOF.
-
1
def gets(*args)
-
assert("rack.input#gets called with arguments") { args.size == 0 }
-
v = @input.gets
-
assert("rack.input#gets didn't return a String") {
-
v.nil? or v.kind_of? String
-
}
-
v
-
end
-
-
## * +read+ behaves like IO#read. Its signature is <tt>read([length, [buffer]])</tt>.
-
## If given, +length+ must be a non-negative Integer (>= 0) or +nil+, and +buffer+ must
-
## be a String and may not be nil. If +length+ is given and not nil, then this method
-
## reads at most +length+ bytes from the input stream. If +length+ is not given or nil,
-
## then this method reads all data until EOF.
-
## When EOF is reached, this method returns nil if +length+ is given and not nil, or ""
-
## if +length+ is not given or is nil.
-
## If +buffer+ is given, then the read data will be placed into +buffer+ instead of a
-
## newly created String object.
-
1
def read(*args)
-
assert("rack.input#read called with too many arguments") {
-
args.size <= 2
-
}
-
if args.size >= 1
-
assert("rack.input#read called with non-integer and non-nil length") {
-
args.first.kind_of?(Integer) || args.first.nil?
-
}
-
assert("rack.input#read called with a negative length") {
-
args.first.nil? || args.first >= 0
-
}
-
end
-
if args.size >= 2
-
assert("rack.input#read called with non-String buffer") {
-
args[1].kind_of?(String)
-
}
-
end
-
-
v = @input.read(*args)
-
-
assert("rack.input#read didn't return nil or a String") {
-
v.nil? or v.kind_of? String
-
}
-
if args[0].nil?
-
assert("rack.input#read(nil) returned nil on EOF") {
-
!v.nil?
-
}
-
end
-
-
v
-
end
-
-
## * +each+ must be called without arguments and only yield Strings.
-
1
def each(*args)
-
assert("rack.input#each called with arguments") { args.size == 0 }
-
@input.each { |line|
-
assert("rack.input#each didn't yield a String") {
-
line.kind_of? String
-
}
-
yield line
-
}
-
end
-
-
## * +rewind+ must be called without arguments. It rewinds the input
-
## stream back to the beginning. It must not raise Errno::ESPIPE:
-
## that is, it may not be a pipe or a socket. Therefore, handler
-
## developers must buffer the input data into some rewindable object
-
## if the underlying input stream is not rewindable.
-
1
def rewind(*args)
-
assert("rack.input#rewind called with arguments") { args.size == 0 }
-
assert("rack.input#rewind raised Errno::ESPIPE") {
-
begin
-
@input.rewind
-
true
-
rescue Errno::ESPIPE
-
false
-
end
-
}
-
end
-
-
## * +close+ must never be called on the input stream.
-
1
def close(*args)
-
assert("rack.input#close must not be called") { false }
-
end
-
end
-
-
## === The Error Stream
-
1
def check_error(error)
-
## The error stream must respond to +puts+, +write+ and +flush+.
-
[:puts, :write, :flush].each { |method|
-
assert("rack.error #{error} does not respond to ##{method}") {
-
error.respond_to? method
-
}
-
}
-
end
-
-
1
class ErrorWrapper
-
1
include Assertion
-
-
1
def initialize(error)
-
@error = error
-
end
-
-
## * +puts+ must be called with a single argument that responds to +to_s+.
-
1
def puts(str)
-
@error.puts str
-
end
-
-
## * +write+ must be called with a single argument that is a String.
-
1
def write(str)
-
assert("rack.errors#write not called with a String") { str.kind_of? String }
-
@error.write str
-
end
-
-
## * +flush+ must be called without arguments and must be called
-
## in order to make the error appear for sure.
-
1
def flush
-
@error.flush
-
end
-
-
## * +close+ must never be called on the error stream.
-
1
def close(*args)
-
assert("rack.errors#close must not be called") { false }
-
end
-
end
-
-
1
class HijackWrapper
-
1
include Assertion
-
1
extend Forwardable
-
-
1
REQUIRED_METHODS = [
-
:read, :write, :read_nonblock, :write_nonblock, :flush, :close,
-
:close_read, :close_write, :closed?
-
]
-
-
1
def_delegators :@io, *REQUIRED_METHODS
-
-
1
def initialize(io)
-
@io = io
-
REQUIRED_METHODS.each do |meth|
-
assert("rack.hijack_io must respond to #{meth}") { io.respond_to? meth }
-
end
-
end
-
end
-
-
## === Hijacking
-
#
-
# AUTHORS: n.b. The trailing whitespace between paragraphs is important and
-
# should not be removed. The whitespace creates paragraphs in the RDoc
-
# output.
-
#
-
## ==== Request (before status)
-
1
def check_hijack(env)
-
if env['rack.hijack?']
-
## If rack.hijack? is true then rack.hijack must respond to #call.
-
original_hijack = env['rack.hijack']
-
assert("rack.hijack must respond to call") { original_hijack.respond_to?(:call) }
-
env['rack.hijack'] = proc do
-
## rack.hijack must return the io that will also be assigned (or is
-
## already present, in rack.hijack_io.
-
io = original_hijack.call
-
HijackWrapper.new(io)
-
##
-
## rack.hijack_io must respond to:
-
## <tt>read, write, read_nonblock, write_nonblock, flush, close,
-
## close_read, close_write, closed?</tt>
-
##
-
## The semantics of these IO methods must be a best effort match to
-
## those of a normal ruby IO or Socket object, using standard
-
## arguments and raising standard exceptions. Servers are encouraged
-
## to simply pass on real IO objects, although it is recognized that
-
## this approach is not directly compatible with SPDY and HTTP 2.0.
-
##
-
## IO provided in rack.hijack_io should preference the
-
## IO::WaitReadable and IO::WaitWritable APIs wherever supported.
-
##
-
## There is a deliberate lack of full specification around
-
## rack.hijack_io, as semantics will change from server to server.
-
## Users are encouraged to utilize this API with a knowledge of their
-
## server choice, and servers may extend the functionality of
-
## hijack_io to provide additional features to users. The purpose of
-
## rack.hijack is for Rack to "get out of the way", as such, Rack only
-
## provides the minimum of specification and support.
-
env['rack.hijack_io'] = HijackWrapper.new(env['rack.hijack_io'])
-
io
-
end
-
else
-
##
-
## If rack.hijack? is false, then rack.hijack should not be set.
-
assert("rack.hijack? is false, but rack.hijack is present") { env['rack.hijack'].nil? }
-
##
-
## If rack.hijack? is false, then rack.hijack_io should not be set.
-
assert("rack.hijack? is false, but rack.hijack_io is present") { env['rack.hijack_io'].nil? }
-
end
-
end
-
-
## ==== Response (after headers)
-
## It is also possible to hijack a response after the status and headers
-
## have been sent.
-
1
def check_hijack_response(headers, env)
-
-
# this check uses headers like a hash, but the spec only requires
-
# headers respond to #each
-
headers = Rack::Utils::HeaderHash.new(headers)
-
-
## In order to do this, an application may set the special header
-
## <tt>rack.hijack</tt> to an object that responds to <tt>call</tt>
-
## accepting an argument that conforms to the <tt>rack.hijack_io</tt>
-
## protocol.
-
##
-
## After the headers have been sent, and this hijack callback has been
-
## called, the application is now responsible for the remaining lifecycle
-
## of the IO. The application is also responsible for maintaining HTTP
-
## semantics. Of specific note, in almost all cases in the current SPEC,
-
## applications will have wanted to specify the header Connection:close in
-
## HTTP/1.1, and not Connection:keep-alive, as there is no protocol for
-
## returning hijacked sockets to the web server. For that purpose, use the
-
## body streaming API instead (progressively yielding strings via each).
-
##
-
## Servers must ignore the <tt>body</tt> part of the response tuple when
-
## the <tt>rack.hijack</tt> response API is in use.
-
-
if env['rack.hijack?'] && headers['rack.hijack']
-
assert('rack.hijack header must respond to #call') {
-
headers['rack.hijack'].respond_to? :call
-
}
-
original_hijack = headers['rack.hijack']
-
headers['rack.hijack'] = proc do |io|
-
original_hijack.call HijackWrapper.new(io)
-
end
-
else
-
##
-
## The special response header <tt>rack.hijack</tt> must only be set
-
## if the request env has <tt>rack.hijack?</tt> <tt>true</tt>.
-
assert('rack.hijack header must not be present if server does not support hijacking') {
-
headers['rack.hijack'].nil?
-
}
-
end
-
end
-
## ==== Conventions
-
## * Middleware should not use hijack unless it is handling the whole
-
## response.
-
## * Middleware may wrap the IO object for the response pattern.
-
## * Middleware should not wrap the IO object for the request pattern. The
-
## request pattern is intended to provide the hijacker with "raw tcp".
-
-
## == The Response
-
-
## === The Status
-
1
def check_status(status)
-
## This is an HTTP status. When parsed as integer (+to_i+), it must be
-
## greater than or equal to 100.
-
assert("Status must be >=100 seen as integer") { status.to_i >= 100 }
-
end
-
-
## === The Headers
-
1
def check_headers(header)
-
## The header must respond to +each+, and yield values of key and value.
-
assert("headers object should respond to #each, but doesn't (got #{header.class} as headers)") {
-
header.respond_to? :each
-
}
-
header.each { |key, value|
-
## Special headers starting "rack." are for communicating with the
-
## server, and must not be sent back to the client.
-
next if key =~ /^rack\..+$/
-
-
## The header keys must be Strings.
-
assert("header key must be a string, was #{key.class}") {
-
key.kind_of? String
-
}
-
## The header must not contain a +Status+ key,
-
assert("header must not contain Status") { key.downcase != "status" }
-
## contain keys with <tt>:</tt> or newlines in their name,
-
assert("header names must not contain : or \\n") { key !~ /[:\n]/ }
-
## contain keys names that end in <tt>-</tt> or <tt>_</tt>,
-
assert("header names must not end in - or _") { key !~ /[-_]\z/ }
-
## but only contain keys that consist of
-
## letters, digits, <tt>_</tt> or <tt>-</tt> and start with a letter.
-
assert("invalid header name: #{key}") { key =~ /\A[a-zA-Z][a-zA-Z0-9_-]*\z/ }
-
-
## The values of the header must be Strings,
-
assert("a header value must be a String, but the value of " +
-
"'#{key}' is a #{value.class}") { value.kind_of? String }
-
## consisting of lines (for multiple header values, e.g. multiple
-
## <tt>Set-Cookie</tt> values) seperated by "\n".
-
value.split("\n").each { |item|
-
## The lines must not contain characters below 037.
-
assert("invalid header value #{key}: #{item.inspect}") {
-
item !~ /[\000-\037]/
-
}
-
}
-
}
-
end
-
-
## === The Content-Type
-
1
def check_content_type(status, headers)
-
headers.each { |key, value|
-
## There must not be a <tt>Content-Type</tt>, when the +Status+ is 1xx,
-
## 204, 205 or 304.
-
if key.downcase == "content-type"
-
assert("Content-Type header found in #{status} response, not allowed") {
-
not Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i
-
}
-
return
-
end
-
}
-
end
-
-
## === The Content-Length
-
1
def check_content_length(status, headers)
-
headers.each { |key, value|
-
if key.downcase == 'content-length'
-
## There must not be a <tt>Content-Length</tt> header when the
-
## +Status+ is 1xx, 204, 205 or 304.
-
assert("Content-Length header found in #{status} response, not allowed") {
-
not Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i
-
}
-
@content_length = value
-
end
-
}
-
end
-
-
1
def verify_content_length(bytes)
-
if @head_request
-
assert("Response body was given for HEAD request, but should be empty") {
-
bytes == 0
-
}
-
elsif @content_length
-
assert("Content-Length header was #{@content_length}, but should be #{bytes}") {
-
@content_length == bytes.to_s
-
}
-
end
-
end
-
-
## === The Body
-
1
def each
-
@closed = false
-
bytes = 0
-
-
## The Body must respond to +each+
-
assert("Response body must respond to each") do
-
@body.respond_to?(:each)
-
end
-
-
@body.each { |part|
-
## and must only yield String values.
-
assert("Body yielded non-string value #{part.inspect}") {
-
part.kind_of? String
-
}
-
bytes += Rack::Utils.bytesize(part)
-
yield part
-
}
-
verify_content_length(bytes)
-
-
##
-
## The Body itself should not be an instance of String, as this will
-
## break in Ruby 1.9.
-
##
-
## If the Body responds to +close+, it will be called after iteration. If
-
## the body is replaced by a middleware after action, the original body
-
## must be closed first, if it repsonds to close.
-
# XXX howto: assert("Body has not been closed") { @closed }
-
-
-
##
-
## If the Body responds to +to_path+, it must return a String
-
## identifying the location of a file whose contents are identical
-
## to that produced by calling +each+; this may be used by the
-
## server as an alternative, possibly more efficient way to
-
## transport the response.
-
-
if @body.respond_to?(:to_path)
-
assert("The file identified by body.to_path does not exist") {
-
::File.exist? @body.to_path
-
}
-
end
-
-
##
-
## The Body commonly is an Array of Strings, the application
-
## instance itself, or a File-like object.
-
end
-
-
1
def close
-
@closed = true
-
@body.close if @body.respond_to?(:close)
-
end
-
-
# :startdoc:
-
-
end
-
end
-
-
## == Thanks
-
## Some parts of this specification are adopted from PEP333: Python
-
## Web Server Gateway Interface
-
## v1.0 (http://www.python.org/dev/peps/pep-0333/). I'd like to thank
-
## everyone involved in that effort.
-
1
require 'thread'
-
1
require 'rack/body_proxy'
-
-
1
module Rack
-
# Rack::Lock locks every request inside a mutex, so that every request
-
# will effectively be executed synchronously.
-
1
class Lock
-
1
FLAG = 'rack.multithread'.freeze
-
-
1
def initialize(app, mutex = Mutex.new)
-
1
@app, @mutex = app, mutex
-
end
-
-
1
def call(env)
-
old, env[FLAG] = env[FLAG], false
-
@mutex.lock
-
response = @app.call(env)
-
body = BodyProxy.new(response[2]) { @mutex.unlock }
-
response[2] = body
-
response
-
ensure
-
@mutex.unlock unless body
-
env[FLAG] = old
-
end
-
end
-
end
-
1
module Rack
-
1
class MethodOverride
-
1
HTTP_METHODS = %w(GET HEAD PUT POST DELETE OPTIONS PATCH)
-
-
1
METHOD_OVERRIDE_PARAM_KEY = "_method".freeze
-
1
HTTP_METHOD_OVERRIDE_HEADER = "HTTP_X_HTTP_METHOD_OVERRIDE".freeze
-
-
1
def initialize(app)
-
1
@app = app
-
end
-
-
1
def call(env)
-
if env["REQUEST_METHOD"] == "POST"
-
method = method_override(env)
-
if HTTP_METHODS.include?(method)
-
env["rack.methodoverride.original_method"] = env["REQUEST_METHOD"]
-
env["REQUEST_METHOD"] = method
-
end
-
end
-
-
@app.call(env)
-
end
-
-
1
def method_override(env)
-
req = Request.new(env)
-
method = req.POST[METHOD_OVERRIDE_PARAM_KEY] ||
-
env[HTTP_METHOD_OVERRIDE_HEADER]
-
method.to_s.upcase
-
end
-
end
-
end
-
1
module Rack
-
1
module Mime
-
# Returns String with mime type if found, otherwise use +fallback+.
-
# +ext+ should be filename extension in the '.ext' format that
-
# File.extname(file) returns.
-
# +fallback+ may be any object
-
#
-
# Also see the documentation for MIME_TYPES
-
#
-
# Usage:
-
# Rack::Mime.mime_type('.foo')
-
#
-
# This is a shortcut for:
-
# Rack::Mime::MIME_TYPES.fetch('.foo', 'application/octet-stream')
-
-
1
def mime_type(ext, fallback='application/octet-stream')
-
MIME_TYPES.fetch(ext.to_s.downcase, fallback)
-
end
-
1
module_function :mime_type
-
-
# Returns true if the given value is a mime match for the given mime match
-
# specification, false otherwise.
-
#
-
# Rack::Mime.match?('text/html', 'text/*') => true
-
# Rack::Mime.match?('text/plain', '*') => true
-
# Rack::Mime.match?('text/html', 'application/json') => false
-
-
1
def match?(value, matcher)
-
v1, v2 = value.split('/', 2)
-
m1, m2 = matcher.split('/', 2)
-
-
if m1 == '*'
-
if m2.nil? || m2 == '*'
-
return true
-
elsif m2 == v2
-
return true
-
else
-
return false
-
end
-
end
-
-
return false if v1 != m1
-
-
return true if m2.nil? || m2 == '*'
-
-
m2 == v2
-
end
-
1
module_function :match?
-
-
# List of most common mime-types, selected various sources
-
# according to their usefulness in a webserving scope for Ruby
-
# users.
-
#
-
# To amend this list with your local mime.types list you can use:
-
#
-
# require 'webrick/httputils'
-
# list = WEBrick::HTTPUtils.load_mime_types('/etc/mime.types')
-
# Rack::Mime::MIME_TYPES.merge!(list)
-
#
-
# N.B. On Ubuntu the mime.types file does not include the leading period, so
-
# users may need to modify the data before merging into the hash.
-
#
-
# To add the list mongrel provides, use:
-
#
-
# require 'mongrel/handlers'
-
# Rack::Mime::MIME_TYPES.merge!(Mongrel::DirHandler::MIME_TYPES)
-
-
1
MIME_TYPES = {
-
".123" => "application/vnd.lotus-1-2-3",
-
".3dml" => "text/vnd.in3d.3dml",
-
".3g2" => "video/3gpp2",
-
".3gp" => "video/3gpp",
-
".a" => "application/octet-stream",
-
".acc" => "application/vnd.americandynamics.acc",
-
".ace" => "application/x-ace-compressed",
-
".acu" => "application/vnd.acucobol",
-
".aep" => "application/vnd.audiograph",
-
".afp" => "application/vnd.ibm.modcap",
-
".ai" => "application/postscript",
-
".aif" => "audio/x-aiff",
-
".aiff" => "audio/x-aiff",
-
".ami" => "application/vnd.amiga.ami",
-
".appcache" => "text/cache-manifest",
-
".apr" => "application/vnd.lotus-approach",
-
".asc" => "application/pgp-signature",
-
".asf" => "video/x-ms-asf",
-
".asm" => "text/x-asm",
-
".aso" => "application/vnd.accpac.simply.aso",
-
".asx" => "video/x-ms-asf",
-
".atc" => "application/vnd.acucorp",
-
".atom" => "application/atom+xml",
-
".atomcat" => "application/atomcat+xml",
-
".atomsvc" => "application/atomsvc+xml",
-
".atx" => "application/vnd.antix.game-component",
-
".au" => "audio/basic",
-
".avi" => "video/x-msvideo",
-
".bat" => "application/x-msdownload",
-
".bcpio" => "application/x-bcpio",
-
".bdm" => "application/vnd.syncml.dm+wbxml",
-
".bh2" => "application/vnd.fujitsu.oasysprs",
-
".bin" => "application/octet-stream",
-
".bmi" => "application/vnd.bmi",
-
".bmp" => "image/bmp",
-
".box" => "application/vnd.previewsystems.box",
-
".btif" => "image/prs.btif",
-
".bz" => "application/x-bzip",
-
".bz2" => "application/x-bzip2",
-
".c" => "text/x-c",
-
".c4g" => "application/vnd.clonk.c4group",
-
".cab" => "application/vnd.ms-cab-compressed",
-
".cc" => "text/x-c",
-
".ccxml" => "application/ccxml+xml",
-
".cdbcmsg" => "application/vnd.contact.cmsg",
-
".cdkey" => "application/vnd.mediastation.cdkey",
-
".cdx" => "chemical/x-cdx",
-
".cdxml" => "application/vnd.chemdraw+xml",
-
".cdy" => "application/vnd.cinderella",
-
".cer" => "application/pkix-cert",
-
".cgm" => "image/cgm",
-
".chat" => "application/x-chat",
-
".chm" => "application/vnd.ms-htmlhelp",
-
".chrt" => "application/vnd.kde.kchart",
-
".cif" => "chemical/x-cif",
-
".cii" => "application/vnd.anser-web-certificate-issue-initiation",
-
".cil" => "application/vnd.ms-artgalry",
-
".cla" => "application/vnd.claymore",
-
".class" => "application/octet-stream",
-
".clkk" => "application/vnd.crick.clicker.keyboard",
-
".clkp" => "application/vnd.crick.clicker.palette",
-
".clkt" => "application/vnd.crick.clicker.template",
-
".clkw" => "application/vnd.crick.clicker.wordbank",
-
".clkx" => "application/vnd.crick.clicker",
-
".clp" => "application/x-msclip",
-
".cmc" => "application/vnd.cosmocaller",
-
".cmdf" => "chemical/x-cmdf",
-
".cml" => "chemical/x-cml",
-
".cmp" => "application/vnd.yellowriver-custom-menu",
-
".cmx" => "image/x-cmx",
-
".com" => "application/x-msdownload",
-
".conf" => "text/plain",
-
".cpio" => "application/x-cpio",
-
".cpp" => "text/x-c",
-
".cpt" => "application/mac-compactpro",
-
".crd" => "application/x-mscardfile",
-
".crl" => "application/pkix-crl",
-
".crt" => "application/x-x509-ca-cert",
-
".csh" => "application/x-csh",
-
".csml" => "chemical/x-csml",
-
".csp" => "application/vnd.commonspace",
-
".css" => "text/css",
-
".csv" => "text/csv",
-
".curl" => "application/vnd.curl",
-
".cww" => "application/prs.cww",
-
".cxx" => "text/x-c",
-
".daf" => "application/vnd.mobius.daf",
-
".davmount" => "application/davmount+xml",
-
".dcr" => "application/x-director",
-
".dd2" => "application/vnd.oma.dd2+xml",
-
".ddd" => "application/vnd.fujixerox.ddd",
-
".deb" => "application/x-debian-package",
-
".der" => "application/x-x509-ca-cert",
-
".dfac" => "application/vnd.dreamfactory",
-
".diff" => "text/x-diff",
-
".dis" => "application/vnd.mobius.dis",
-
".djv" => "image/vnd.djvu",
-
".djvu" => "image/vnd.djvu",
-
".dll" => "application/x-msdownload",
-
".dmg" => "application/octet-stream",
-
".dna" => "application/vnd.dna",
-
".doc" => "application/msword",
-
".docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
-
".dot" => "application/msword",
-
".dp" => "application/vnd.osgi.dp",
-
".dpg" => "application/vnd.dpgraph",
-
".dsc" => "text/prs.lines.tag",
-
".dtd" => "application/xml-dtd",
-
".dts" => "audio/vnd.dts",
-
".dtshd" => "audio/vnd.dts.hd",
-
".dv" => "video/x-dv",
-
".dvi" => "application/x-dvi",
-
".dwf" => "model/vnd.dwf",
-
".dwg" => "image/vnd.dwg",
-
".dxf" => "image/vnd.dxf",
-
".dxp" => "application/vnd.spotfire.dxp",
-
".ear" => "application/java-archive",
-
".ecelp4800" => "audio/vnd.nuera.ecelp4800",
-
".ecelp7470" => "audio/vnd.nuera.ecelp7470",
-
".ecelp9600" => "audio/vnd.nuera.ecelp9600",
-
".ecma" => "application/ecmascript",
-
".edm" => "application/vnd.novadigm.edm",
-
".edx" => "application/vnd.novadigm.edx",
-
".efif" => "application/vnd.picsel",
-
".ei6" => "application/vnd.pg.osasli",
-
".eml" => "message/rfc822",
-
".eol" => "audio/vnd.digital-winds",
-
".eot" => "application/vnd.ms-fontobject",
-
".eps" => "application/postscript",
-
".es3" => "application/vnd.eszigno3+xml",
-
".esf" => "application/vnd.epson.esf",
-
".etx" => "text/x-setext",
-
".exe" => "application/x-msdownload",
-
".ext" => "application/vnd.novadigm.ext",
-
".ez" => "application/andrew-inset",
-
".ez2" => "application/vnd.ezpix-album",
-
".ez3" => "application/vnd.ezpix-package",
-
".f" => "text/x-fortran",
-
".f77" => "text/x-fortran",
-
".f90" => "text/x-fortran",
-
".fbs" => "image/vnd.fastbidsheet",
-
".fdf" => "application/vnd.fdf",
-
".fe_launch" => "application/vnd.denovo.fcselayout-link",
-
".fg5" => "application/vnd.fujitsu.oasysgp",
-
".fli" => "video/x-fli",
-
".flo" => "application/vnd.micrografx.flo",
-
".flv" => "video/x-flv",
-
".flw" => "application/vnd.kde.kivio",
-
".flx" => "text/vnd.fmi.flexstor",
-
".fly" => "text/vnd.fly",
-
".fm" => "application/vnd.framemaker",
-
".fnc" => "application/vnd.frogans.fnc",
-
".for" => "text/x-fortran",
-
".fpx" => "image/vnd.fpx",
-
".fsc" => "application/vnd.fsc.weblaunch",
-
".fst" => "image/vnd.fst",
-
".ftc" => "application/vnd.fluxtime.clip",
-
".fti" => "application/vnd.anser-web-funds-transfer-initiation",
-
".fvt" => "video/vnd.fvt",
-
".fzs" => "application/vnd.fuzzysheet",
-
".g3" => "image/g3fax",
-
".gac" => "application/vnd.groove-account",
-
".gdl" => "model/vnd.gdl",
-
".gem" => "application/octet-stream",
-
".gemspec" => "text/x-script.ruby",
-
".ghf" => "application/vnd.groove-help",
-
".gif" => "image/gif",
-
".gim" => "application/vnd.groove-identity-message",
-
".gmx" => "application/vnd.gmx",
-
".gph" => "application/vnd.flographit",
-
".gqf" => "application/vnd.grafeq",
-
".gram" => "application/srgs",
-
".grv" => "application/vnd.groove-injector",
-
".grxml" => "application/srgs+xml",
-
".gtar" => "application/x-gtar",
-
".gtm" => "application/vnd.groove-tool-message",
-
".gtw" => "model/vnd.gtw",
-
".gv" => "text/vnd.graphviz",
-
".gz" => "application/x-gzip",
-
".h" => "text/x-c",
-
".h261" => "video/h261",
-
".h263" => "video/h263",
-
".h264" => "video/h264",
-
".hbci" => "application/vnd.hbci",
-
".hdf" => "application/x-hdf",
-
".hh" => "text/x-c",
-
".hlp" => "application/winhlp",
-
".hpgl" => "application/vnd.hp-hpgl",
-
".hpid" => "application/vnd.hp-hpid",
-
".hps" => "application/vnd.hp-hps",
-
".hqx" => "application/mac-binhex40",
-
".htc" => "text/x-component",
-
".htke" => "application/vnd.kenameaapp",
-
".htm" => "text/html",
-
".html" => "text/html",
-
".hvd" => "application/vnd.yamaha.hv-dic",
-
".hvp" => "application/vnd.yamaha.hv-voice",
-
".hvs" => "application/vnd.yamaha.hv-script",
-
".icc" => "application/vnd.iccprofile",
-
".ice" => "x-conference/x-cooltalk",
-
".ico" => "image/vnd.microsoft.icon",
-
".ics" => "text/calendar",
-
".ief" => "image/ief",
-
".ifb" => "text/calendar",
-
".ifm" => "application/vnd.shana.informed.formdata",
-
".igl" => "application/vnd.igloader",
-
".igs" => "model/iges",
-
".igx" => "application/vnd.micrografx.igx",
-
".iif" => "application/vnd.shana.informed.interchange",
-
".imp" => "application/vnd.accpac.simply.imp",
-
".ims" => "application/vnd.ms-ims",
-
".ipk" => "application/vnd.shana.informed.package",
-
".irm" => "application/vnd.ibm.rights-management",
-
".irp" => "application/vnd.irepository.package+xml",
-
".iso" => "application/octet-stream",
-
".itp" => "application/vnd.shana.informed.formtemplate",
-
".ivp" => "application/vnd.immervision-ivp",
-
".ivu" => "application/vnd.immervision-ivu",
-
".jad" => "text/vnd.sun.j2me.app-descriptor",
-
".jam" => "application/vnd.jam",
-
".jar" => "application/java-archive",
-
".java" => "text/x-java-source",
-
".jisp" => "application/vnd.jisp",
-
".jlt" => "application/vnd.hp-jlyt",
-
".jnlp" => "application/x-java-jnlp-file",
-
".joda" => "application/vnd.joost.joda-archive",
-
".jp2" => "image/jp2",
-
".jpeg" => "image/jpeg",
-
".jpg" => "image/jpeg",
-
".jpgv" => "video/jpeg",
-
".jpm" => "video/jpm",
-
".js" => "application/javascript",
-
".json" => "application/json",
-
".karbon" => "application/vnd.kde.karbon",
-
".kfo" => "application/vnd.kde.kformula",
-
".kia" => "application/vnd.kidspiration",
-
".kml" => "application/vnd.google-earth.kml+xml",
-
".kmz" => "application/vnd.google-earth.kmz",
-
".kne" => "application/vnd.kinar",
-
".kon" => "application/vnd.kde.kontour",
-
".kpr" => "application/vnd.kde.kpresenter",
-
".ksp" => "application/vnd.kde.kspread",
-
".ktz" => "application/vnd.kahootz",
-
".kwd" => "application/vnd.kde.kword",
-
".latex" => "application/x-latex",
-
".lbd" => "application/vnd.llamagraphics.life-balance.desktop",
-
".lbe" => "application/vnd.llamagraphics.life-balance.exchange+xml",
-
".les" => "application/vnd.hhe.lesson-player",
-
".link66" => "application/vnd.route66.link66+xml",
-
".log" => "text/plain",
-
".lostxml" => "application/lost+xml",
-
".lrm" => "application/vnd.ms-lrm",
-
".ltf" => "application/vnd.frogans.ltf",
-
".lvp" => "audio/vnd.lucent.voice",
-
".lwp" => "application/vnd.lotus-wordpro",
-
".m3u" => "audio/x-mpegurl",
-
".m4a" => "audio/mp4a-latm",
-
".m4v" => "video/mp4",
-
".ma" => "application/mathematica",
-
".mag" => "application/vnd.ecowin.chart",
-
".man" => "text/troff",
-
".manifest" => "text/cache-manifest",
-
".mathml" => "application/mathml+xml",
-
".mbk" => "application/vnd.mobius.mbk",
-
".mbox" => "application/mbox",
-
".mc1" => "application/vnd.medcalcdata",
-
".mcd" => "application/vnd.mcd",
-
".mdb" => "application/x-msaccess",
-
".mdi" => "image/vnd.ms-modi",
-
".mdoc" => "text/troff",
-
".me" => "text/troff",
-
".mfm" => "application/vnd.mfmp",
-
".mgz" => "application/vnd.proteus.magazine",
-
".mid" => "audio/midi",
-
".midi" => "audio/midi",
-
".mif" => "application/vnd.mif",
-
".mime" => "message/rfc822",
-
".mj2" => "video/mj2",
-
".mlp" => "application/vnd.dolby.mlp",
-
".mmd" => "application/vnd.chipnuts.karaoke-mmd",
-
".mmf" => "application/vnd.smaf",
-
".mml" => "application/mathml+xml",
-
".mmr" => "image/vnd.fujixerox.edmics-mmr",
-
".mng" => "video/x-mng",
-
".mny" => "application/x-msmoney",
-
".mov" => "video/quicktime",
-
".movie" => "video/x-sgi-movie",
-
".mp3" => "audio/mpeg",
-
".mp4" => "video/mp4",
-
".mp4a" => "audio/mp4",
-
".mp4s" => "application/mp4",
-
".mp4v" => "video/mp4",
-
".mpc" => "application/vnd.mophun.certificate",
-
".mpeg" => "video/mpeg",
-
".mpg" => "video/mpeg",
-
".mpga" => "audio/mpeg",
-
".mpkg" => "application/vnd.apple.installer+xml",
-
".mpm" => "application/vnd.blueice.multipass",
-
".mpn" => "application/vnd.mophun.application",
-
".mpp" => "application/vnd.ms-project",
-
".mpy" => "application/vnd.ibm.minipay",
-
".mqy" => "application/vnd.mobius.mqy",
-
".mrc" => "application/marc",
-
".ms" => "text/troff",
-
".mscml" => "application/mediaservercontrol+xml",
-
".mseq" => "application/vnd.mseq",
-
".msf" => "application/vnd.epson.msf",
-
".msh" => "model/mesh",
-
".msi" => "application/x-msdownload",
-
".msl" => "application/vnd.mobius.msl",
-
".msty" => "application/vnd.muvee.style",
-
".mts" => "model/vnd.mts",
-
".mus" => "application/vnd.musician",
-
".mvb" => "application/x-msmediaview",
-
".mwf" => "application/vnd.mfer",
-
".mxf" => "application/mxf",
-
".mxl" => "application/vnd.recordare.musicxml",
-
".mxml" => "application/xv+xml",
-
".mxs" => "application/vnd.triscape.mxs",
-
".mxu" => "video/vnd.mpegurl",
-
".n" => "application/vnd.nokia.n-gage.symbian.install",
-
".nc" => "application/x-netcdf",
-
".ngdat" => "application/vnd.nokia.n-gage.data",
-
".nlu" => "application/vnd.neurolanguage.nlu",
-
".nml" => "application/vnd.enliven",
-
".nnd" => "application/vnd.noblenet-directory",
-
".nns" => "application/vnd.noblenet-sealer",
-
".nnw" => "application/vnd.noblenet-web",
-
".npx" => "image/vnd.net-fpx",
-
".nsf" => "application/vnd.lotus-notes",
-
".oa2" => "application/vnd.fujitsu.oasys2",
-
".oa3" => "application/vnd.fujitsu.oasys3",
-
".oas" => "application/vnd.fujitsu.oasys",
-
".obd" => "application/x-msbinder",
-
".oda" => "application/oda",
-
".odc" => "application/vnd.oasis.opendocument.chart",
-
".odf" => "application/vnd.oasis.opendocument.formula",
-
".odg" => "application/vnd.oasis.opendocument.graphics",
-
".odi" => "application/vnd.oasis.opendocument.image",
-
".odp" => "application/vnd.oasis.opendocument.presentation",
-
".ods" => "application/vnd.oasis.opendocument.spreadsheet",
-
".odt" => "application/vnd.oasis.opendocument.text",
-
".oga" => "audio/ogg",
-
".ogg" => "application/ogg",
-
".ogv" => "video/ogg",
-
".ogx" => "application/ogg",
-
".org" => "application/vnd.lotus-organizer",
-
".otc" => "application/vnd.oasis.opendocument.chart-template",
-
".otf" => "application/vnd.oasis.opendocument.formula-template",
-
".otg" => "application/vnd.oasis.opendocument.graphics-template",
-
".oth" => "application/vnd.oasis.opendocument.text-web",
-
".oti" => "application/vnd.oasis.opendocument.image-template",
-
".otm" => "application/vnd.oasis.opendocument.text-master",
-
".ots" => "application/vnd.oasis.opendocument.spreadsheet-template",
-
".ott" => "application/vnd.oasis.opendocument.text-template",
-
".oxt" => "application/vnd.openofficeorg.extension",
-
".p" => "text/x-pascal",
-
".p10" => "application/pkcs10",
-
".p12" => "application/x-pkcs12",
-
".p7b" => "application/x-pkcs7-certificates",
-
".p7m" => "application/pkcs7-mime",
-
".p7r" => "application/x-pkcs7-certreqresp",
-
".p7s" => "application/pkcs7-signature",
-
".pas" => "text/x-pascal",
-
".pbd" => "application/vnd.powerbuilder6",
-
".pbm" => "image/x-portable-bitmap",
-
".pcl" => "application/vnd.hp-pcl",
-
".pclxl" => "application/vnd.hp-pclxl",
-
".pcx" => "image/x-pcx",
-
".pdb" => "chemical/x-pdb",
-
".pdf" => "application/pdf",
-
".pem" => "application/x-x509-ca-cert",
-
".pfr" => "application/font-tdpfr",
-
".pgm" => "image/x-portable-graymap",
-
".pgn" => "application/x-chess-pgn",
-
".pgp" => "application/pgp-encrypted",
-
".pic" => "image/x-pict",
-
".pict" => "image/pict",
-
".pkg" => "application/octet-stream",
-
".pki" => "application/pkixcmp",
-
".pkipath" => "application/pkix-pkipath",
-
".pl" => "text/x-script.perl",
-
".plb" => "application/vnd.3gpp.pic-bw-large",
-
".plc" => "application/vnd.mobius.plc",
-
".plf" => "application/vnd.pocketlearn",
-
".pls" => "application/pls+xml",
-
".pm" => "text/x-script.perl-module",
-
".pml" => "application/vnd.ctc-posml",
-
".png" => "image/png",
-
".pnm" => "image/x-portable-anymap",
-
".pntg" => "image/x-macpaint",
-
".portpkg" => "application/vnd.macports.portpkg",
-
".ppd" => "application/vnd.cups-ppd",
-
".ppm" => "image/x-portable-pixmap",
-
".pps" => "application/vnd.ms-powerpoint",
-
".ppt" => "application/vnd.ms-powerpoint",
-
".prc" => "application/vnd.palm",
-
".pre" => "application/vnd.lotus-freelance",
-
".prf" => "application/pics-rules",
-
".ps" => "application/postscript",
-
".psb" => "application/vnd.3gpp.pic-bw-small",
-
".psd" => "image/vnd.adobe.photoshop",
-
".ptid" => "application/vnd.pvi.ptid1",
-
".pub" => "application/x-mspublisher",
-
".pvb" => "application/vnd.3gpp.pic-bw-var",
-
".pwn" => "application/vnd.3m.post-it-notes",
-
".py" => "text/x-script.python",
-
".pya" => "audio/vnd.ms-playready.media.pya",
-
".pyv" => "video/vnd.ms-playready.media.pyv",
-
".qam" => "application/vnd.epson.quickanime",
-
".qbo" => "application/vnd.intu.qbo",
-
".qfx" => "application/vnd.intu.qfx",
-
".qps" => "application/vnd.publishare-delta-tree",
-
".qt" => "video/quicktime",
-
".qtif" => "image/x-quicktime",
-
".qxd" => "application/vnd.quark.quarkxpress",
-
".ra" => "audio/x-pn-realaudio",
-
".rake" => "text/x-script.ruby",
-
".ram" => "audio/x-pn-realaudio",
-
".rar" => "application/x-rar-compressed",
-
".ras" => "image/x-cmu-raster",
-
".rb" => "text/x-script.ruby",
-
".rcprofile" => "application/vnd.ipunplugged.rcprofile",
-
".rdf" => "application/rdf+xml",
-
".rdz" => "application/vnd.data-vision.rdz",
-
".rep" => "application/vnd.businessobjects",
-
".rgb" => "image/x-rgb",
-
".rif" => "application/reginfo+xml",
-
".rl" => "application/resource-lists+xml",
-
".rlc" => "image/vnd.fujixerox.edmics-rlc",
-
".rld" => "application/resource-lists-diff+xml",
-
".rm" => "application/vnd.rn-realmedia",
-
".rmp" => "audio/x-pn-realaudio-plugin",
-
".rms" => "application/vnd.jcp.javame.midlet-rms",
-
".rnc" => "application/relax-ng-compact-syntax",
-
".roff" => "text/troff",
-
".rpm" => "application/x-redhat-package-manager",
-
".rpss" => "application/vnd.nokia.radio-presets",
-
".rpst" => "application/vnd.nokia.radio-preset",
-
".rq" => "application/sparql-query",
-
".rs" => "application/rls-services+xml",
-
".rsd" => "application/rsd+xml",
-
".rss" => "application/rss+xml",
-
".rtf" => "application/rtf",
-
".rtx" => "text/richtext",
-
".ru" => "text/x-script.ruby",
-
".s" => "text/x-asm",
-
".saf" => "application/vnd.yamaha.smaf-audio",
-
".sbml" => "application/sbml+xml",
-
".sc" => "application/vnd.ibm.secure-container",
-
".scd" => "application/x-msschedule",
-
".scm" => "application/vnd.lotus-screencam",
-
".scq" => "application/scvp-cv-request",
-
".scs" => "application/scvp-cv-response",
-
".sdkm" => "application/vnd.solent.sdkm+xml",
-
".sdp" => "application/sdp",
-
".see" => "application/vnd.seemail",
-
".sema" => "application/vnd.sema",
-
".semd" => "application/vnd.semd",
-
".semf" => "application/vnd.semf",
-
".setpay" => "application/set-payment-initiation",
-
".setreg" => "application/set-registration-initiation",
-
".sfd" => "application/vnd.hydrostatix.sof-data",
-
".sfs" => "application/vnd.spotfire.sfs",
-
".sgm" => "text/sgml",
-
".sgml" => "text/sgml",
-
".sh" => "application/x-sh",
-
".shar" => "application/x-shar",
-
".shf" => "application/shf+xml",
-
".sig" => "application/pgp-signature",
-
".sit" => "application/x-stuffit",
-
".sitx" => "application/x-stuffitx",
-
".skp" => "application/vnd.koan",
-
".slt" => "application/vnd.epson.salt",
-
".smi" => "application/smil+xml",
-
".snd" => "audio/basic",
-
".so" => "application/octet-stream",
-
".spf" => "application/vnd.yamaha.smaf-phrase",
-
".spl" => "application/x-futuresplash",
-
".spot" => "text/vnd.in3d.spot",
-
".spp" => "application/scvp-vp-response",
-
".spq" => "application/scvp-vp-request",
-
".src" => "application/x-wais-source",
-
".srx" => "application/sparql-results+xml",
-
".sse" => "application/vnd.kodak-descriptor",
-
".ssf" => "application/vnd.epson.ssf",
-
".ssml" => "application/ssml+xml",
-
".stf" => "application/vnd.wt.stf",
-
".stk" => "application/hyperstudio",
-
".str" => "application/vnd.pg.format",
-
".sus" => "application/vnd.sus-calendar",
-
".sv4cpio" => "application/x-sv4cpio",
-
".sv4crc" => "application/x-sv4crc",
-
".svd" => "application/vnd.svd",
-
".svg" => "image/svg+xml",
-
".svgz" => "image/svg+xml",
-
".swf" => "application/x-shockwave-flash",
-
".swi" => "application/vnd.arastra.swi",
-
".t" => "text/troff",
-
".tao" => "application/vnd.tao.intent-module-archive",
-
".tar" => "application/x-tar",
-
".tbz" => "application/x-bzip-compressed-tar",
-
".tcap" => "application/vnd.3gpp2.tcap",
-
".tcl" => "application/x-tcl",
-
".tex" => "application/x-tex",
-
".texi" => "application/x-texinfo",
-
".texinfo" => "application/x-texinfo",
-
".text" => "text/plain",
-
".tif" => "image/tiff",
-
".tiff" => "image/tiff",
-
".tmo" => "application/vnd.tmobile-livetv",
-
".torrent" => "application/x-bittorrent",
-
".tpl" => "application/vnd.groove-tool-template",
-
".tpt" => "application/vnd.trid.tpt",
-
".tr" => "text/troff",
-
".tra" => "application/vnd.trueapp",
-
".trm" => "application/x-msterminal",
-
".tsv" => "text/tab-separated-values",
-
".ttf" => "application/octet-stream",
-
".twd" => "application/vnd.simtech-mindmapper",
-
".txd" => "application/vnd.genomatix.tuxedo",
-
".txf" => "application/vnd.mobius.txf",
-
".txt" => "text/plain",
-
".ufd" => "application/vnd.ufdl",
-
".umj" => "application/vnd.umajin",
-
".unityweb" => "application/vnd.unity",
-
".uoml" => "application/vnd.uoml+xml",
-
".uri" => "text/uri-list",
-
".ustar" => "application/x-ustar",
-
".utz" => "application/vnd.uiq.theme",
-
".uu" => "text/x-uuencode",
-
".vcd" => "application/x-cdlink",
-
".vcf" => "text/x-vcard",
-
".vcg" => "application/vnd.groove-vcard",
-
".vcs" => "text/x-vcalendar",
-
".vcx" => "application/vnd.vcx",
-
".vis" => "application/vnd.visionary",
-
".viv" => "video/vnd.vivo",
-
".vrml" => "model/vrml",
-
".vsd" => "application/vnd.visio",
-
".vsf" => "application/vnd.vsf",
-
".vtu" => "model/vnd.vtu",
-
".vxml" => "application/voicexml+xml",
-
".war" => "application/java-archive",
-
".wav" => "audio/x-wav",
-
".wax" => "audio/x-ms-wax",
-
".wbmp" => "image/vnd.wap.wbmp",
-
".wbs" => "application/vnd.criticaltools.wbs+xml",
-
".wbxml" => "application/vnd.wap.wbxml",
-
".webm" => "video/webm",
-
".wm" => "video/x-ms-wm",
-
".wma" => "audio/x-ms-wma",
-
".wmd" => "application/x-ms-wmd",
-
".wmf" => "application/x-msmetafile",
-
".wml" => "text/vnd.wap.wml",
-
".wmlc" => "application/vnd.wap.wmlc",
-
".wmls" => "text/vnd.wap.wmlscript",
-
".wmlsc" => "application/vnd.wap.wmlscriptc",
-
".wmv" => "video/x-ms-wmv",
-
".wmx" => "video/x-ms-wmx",
-
".wmz" => "application/x-ms-wmz",
-
".woff" => "application/font-woff",
-
".wpd" => "application/vnd.wordperfect",
-
".wpl" => "application/vnd.ms-wpl",
-
".wps" => "application/vnd.ms-works",
-
".wqd" => "application/vnd.wqd",
-
".wri" => "application/x-mswrite",
-
".wrl" => "model/vrml",
-
".wsdl" => "application/wsdl+xml",
-
".wspolicy" => "application/wspolicy+xml",
-
".wtb" => "application/vnd.webturbo",
-
".wvx" => "video/x-ms-wvx",
-
".x3d" => "application/vnd.hzn-3d-crossword",
-
".xar" => "application/vnd.xara",
-
".xbd" => "application/vnd.fujixerox.docuworks.binder",
-
".xbm" => "image/x-xbitmap",
-
".xdm" => "application/vnd.syncml.dm+xml",
-
".xdp" => "application/vnd.adobe.xdp+xml",
-
".xdw" => "application/vnd.fujixerox.docuworks",
-
".xenc" => "application/xenc+xml",
-
".xer" => "application/patch-ops-error+xml",
-
".xfdf" => "application/vnd.adobe.xfdf",
-
".xfdl" => "application/vnd.xfdl",
-
".xhtml" => "application/xhtml+xml",
-
".xif" => "image/vnd.xiff",
-
".xls" => "application/vnd.ms-excel",
-
".xlsx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
-
".xml" => "application/xml",
-
".xo" => "application/vnd.olpc-sugar",
-
".xop" => "application/xop+xml",
-
".xpm" => "image/x-xpixmap",
-
".xpr" => "application/vnd.is-xpr",
-
".xps" => "application/vnd.ms-xpsdocument",
-
".xpw" => "application/vnd.intercon.formnet",
-
".xsl" => "application/xml",
-
".xslt" => "application/xslt+xml",
-
".xsm" => "application/vnd.syncml+xml",
-
".xspf" => "application/xspf+xml",
-
".xul" => "application/vnd.mozilla.xul+xml",
-
".xwd" => "image/x-xwindowdump",
-
".xyz" => "chemical/x-xyz",
-
".yaml" => "text/yaml",
-
".yml" => "text/yaml",
-
".zaz" => "application/vnd.zzazz.deck+xml",
-
".zip" => "application/zip",
-
".zmm" => "application/vnd.handheld-entertainment+xml",
-
}
-
end
-
end
-
1
require 'uri'
-
1
require 'stringio'
-
1
require 'rack'
-
1
require 'rack/lint'
-
1
require 'rack/utils'
-
1
require 'rack/response'
-
-
1
module Rack
-
# Rack::MockRequest helps testing your Rack application without
-
# actually using HTTP.
-
#
-
# After performing a request on a URL with get/post/put/patch/delete, it
-
# returns a MockResponse with useful helper methods for effective
-
# testing.
-
#
-
# You can pass a hash with additional configuration to the
-
# get/post/put/patch/delete.
-
# <tt>:input</tt>:: A String or IO-like to be used as rack.input.
-
# <tt>:fatal</tt>:: Raise a FatalWarning if the app writes to rack.errors.
-
# <tt>:lint</tt>:: If true, wrap the application in a Rack::Lint.
-
-
1
class MockRequest
-
1
class FatalWarning < RuntimeError
-
end
-
-
1
class FatalWarner
-
1
def puts(warning)
-
raise FatalWarning, warning
-
end
-
-
1
def write(warning)
-
raise FatalWarning, warning
-
end
-
-
1
def flush
-
end
-
-
1
def string
-
""
-
end
-
end
-
-
1
DEFAULT_ENV = {
-
"rack.version" => Rack::VERSION,
-
"rack.input" => StringIO.new,
-
"rack.errors" => StringIO.new,
-
"rack.multithread" => true,
-
"rack.multiprocess" => true,
-
"rack.run_once" => false,
-
}
-
-
1
def initialize(app)
-
@app = app
-
end
-
-
1
def get(uri, opts={}) request("GET", uri, opts) end
-
1
def post(uri, opts={}) request("POST", uri, opts) end
-
1
def put(uri, opts={}) request("PUT", uri, opts) end
-
1
def patch(uri, opts={}) request("PATCH", uri, opts) end
-
1
def delete(uri, opts={}) request("DELETE", uri, opts) end
-
1
def head(uri, opts={}) request("HEAD", uri, opts) end
-
-
1
def request(method="GET", uri="", opts={})
-
env = self.class.env_for(uri, opts.merge(:method => method))
-
-
if opts[:lint]
-
app = Rack::Lint.new(@app)
-
else
-
app = @app
-
end
-
-
errors = env["rack.errors"]
-
status, headers, body = app.call(env)
-
MockResponse.new(status, headers, body, errors)
-
ensure
-
body.close if body.respond_to?(:close)
-
end
-
-
# Return the Rack environment used for a request to +uri+.
-
1
def self.env_for(uri="", opts={})
-
1
uri = URI(uri)
-
1
uri.path = "/#{uri.path}" unless uri.path[0] == ?/
-
-
1
env = DEFAULT_ENV.dup
-
-
1
env["REQUEST_METHOD"] = opts[:method] ? opts[:method].to_s.upcase : "GET"
-
1
env["SERVER_NAME"] = uri.host || "example.org"
-
1
env["SERVER_PORT"] = uri.port ? uri.port.to_s : "80"
-
1
env["QUERY_STRING"] = uri.query.to_s
-
1
env["PATH_INFO"] = (!uri.path || uri.path.empty?) ? "/" : uri.path
-
1
env["rack.url_scheme"] = uri.scheme || "http"
-
1
env["HTTPS"] = env["rack.url_scheme"] == "https" ? "on" : "off"
-
-
1
env["SCRIPT_NAME"] = opts[:script_name] || ""
-
-
1
if opts[:fatal]
-
env["rack.errors"] = FatalWarner.new
-
else
-
1
env["rack.errors"] = StringIO.new
-
end
-
-
1
if params = opts[:params]
-
if env["REQUEST_METHOD"] == "GET"
-
params = Utils.parse_nested_query(params) if params.is_a?(String)
-
params.update(Utils.parse_nested_query(env["QUERY_STRING"]))
-
env["QUERY_STRING"] = Utils.build_nested_query(params)
-
elsif !opts.has_key?(:input)
-
opts["CONTENT_TYPE"] = "application/x-www-form-urlencoded"
-
if params.is_a?(Hash)
-
if data = Utils::Multipart.build_multipart(params)
-
opts[:input] = data
-
opts["CONTENT_LENGTH"] ||= data.length.to_s
-
opts["CONTENT_TYPE"] = "multipart/form-data; boundary=#{Utils::Multipart::MULTIPART_BOUNDARY}"
-
else
-
opts[:input] = Utils.build_nested_query(params)
-
end
-
else
-
opts[:input] = params
-
end
-
end
-
end
-
-
1
empty_str = ""
-
1
empty_str.force_encoding("ASCII-8BIT") if empty_str.respond_to? :force_encoding
-
1
opts[:input] ||= empty_str
-
1
if String === opts[:input]
-
1
rack_input = StringIO.new(opts[:input])
-
else
-
rack_input = opts[:input]
-
end
-
-
1
rack_input.set_encoding(Encoding::BINARY) if rack_input.respond_to?(:set_encoding)
-
1
env['rack.input'] = rack_input
-
-
1
env["CONTENT_LENGTH"] ||= env["rack.input"].length.to_s
-
-
1
opts.each { |field, value|
-
4
env[field] = value if String === field
-
}
-
-
1
env
-
end
-
end
-
-
# Rack::MockResponse provides useful helpers for testing your apps.
-
# Usually, you don't create the MockResponse on your own, but use
-
# MockRequest.
-
-
1
class MockResponse < Rack::Response
-
# Headers
-
1
attr_reader :original_headers
-
-
# Errors
-
1
attr_accessor :errors
-
-
1
def initialize(status, headers, body, errors=StringIO.new(""))
-
@original_headers = headers
-
@errors = errors.string if errors.respond_to?(:string)
-
@body_string = nil
-
-
super(body, status, headers)
-
end
-
-
1
def =~(other)
-
body =~ other
-
end
-
-
1
def match(other)
-
body.match other
-
end
-
-
1
def body
-
# FIXME: apparently users of MockResponse expect the return value of
-
# MockResponse#body to be a string. However, the real response object
-
# returns the body as a list.
-
#
-
# See spec_showstatus.rb:
-
#
-
# should "not replace existing messages" do
-
# ...
-
# res.body.should == "foo!"
-
# end
-
super.join
-
end
-
-
1
def empty?
-
[201, 204, 205, 304].include? status
-
end
-
end
-
end
-
1
module Rack
-
# A multipart form data parser, adapted from IOWA.
-
#
-
# Usually, Rack::Request#POST takes care of calling this.
-
1
module Multipart
-
1
autoload :UploadedFile, 'rack/multipart/uploaded_file'
-
1
autoload :Parser, 'rack/multipart/parser'
-
1
autoload :Generator, 'rack/multipart/generator'
-
-
1
EOL = "\r\n"
-
1
MULTIPART_BOUNDARY = "AaB03x"
-
1
MULTIPART = %r|\Amultipart/.*boundary=\"?([^\";,]+)\"?|n
-
1
TOKEN = /[^\s()<>,;:\\"\/\[\]?=]+/
-
1
CONDISP = /Content-Disposition:\s*#{TOKEN}\s*/i
-
1
DISPPARM = /;\s*(#{TOKEN})=("(?:\\"|[^"])*"|#{TOKEN})/
-
1
RFC2183 = /^#{CONDISP}(#{DISPPARM})+$/i
-
1
BROKEN_QUOTED = /^#{CONDISP}.*;\sfilename="(.*?)"(?:\s*$|\s*;\s*#{TOKEN}=)/i
-
1
BROKEN_UNQUOTED = /^#{CONDISP}.*;\sfilename=(#{TOKEN})/i
-
1
MULTIPART_CONTENT_TYPE = /Content-Type: (.*)#{EOL}/ni
-
1
MULTIPART_CONTENT_DISPOSITION = /Content-Disposition:.*\s+name="?([^\";]*)"?/ni
-
1
MULTIPART_CONTENT_ID = /Content-ID:\s*([^#{EOL}]*)/ni
-
-
1
class << self
-
1
def parse_multipart(env)
-
Parser.new(env).parse
-
end
-
-
1
def build_multipart(params, first = true)
-
Generator.new(params, first).dump
-
end
-
end
-
-
end
-
end
-
1
require 'rack/utils'
-
-
1
module Rack
-
# Rack::Request provides a convenient interface to a Rack
-
# environment. It is stateless, the environment +env+ passed to the
-
# constructor will be directly modified.
-
#
-
# req = Rack::Request.new(env)
-
# req.post?
-
# req.params["data"]
-
#
-
# The environment hash passed will store a reference to the Request object
-
# instantiated so that it will only instantiate if an instance of the Request
-
# object doesn't already exist.
-
-
1
class Request
-
# The environment of the request.
-
1
attr_reader :env
-
-
1
def initialize(env)
-
4
@env = env
-
end
-
-
1
def body; @env["rack.input"] end
-
5
def script_name; @env["SCRIPT_NAME"].to_s end
-
5
def path_info; @env["PATH_INFO"].to_s end
-
1
def request_method; @env["REQUEST_METHOD"] end
-
7
def query_string; @env["QUERY_STRING"].to_s end
-
1
def content_length; @env['CONTENT_LENGTH'] end
-
-
1
def content_type
-
content_type = @env['CONTENT_TYPE']
-
content_type.nil? || content_type.empty? ? nil : content_type
-
end
-
-
17
def session; @env['rack.session'] ||= {} end
-
1
def session_options; @env['rack.session.options'] ||= {} end
-
1
def logger; @env['rack.logger'] end
-
-
# The media type (type/subtype) portion of the CONTENT_TYPE header
-
# without any media type parameters. e.g., when CONTENT_TYPE is
-
# "text/plain;charset=utf-8", the media-type is "text/plain".
-
#
-
# For more information on the use of media types in HTTP, see:
-
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7
-
1
def media_type
-
content_type && content_type.split(/\s*[;,]\s*/, 2).first.downcase
-
end
-
-
# The media type parameters provided in CONTENT_TYPE as a Hash, or
-
# an empty Hash if no CONTENT_TYPE or media-type parameters were
-
# provided. e.g., when the CONTENT_TYPE is "text/plain;charset=utf-8",
-
# this method responds with the following Hash:
-
# { 'charset' => 'utf-8' }
-
1
def media_type_params
-
return {} if content_type.nil?
-
Hash[*content_type.split(/\s*[;,]\s*/)[1..-1].
-
collect { |s| s.split('=', 2) }.
-
map { |k,v| [k.downcase, v] }.flatten]
-
end
-
-
# The character set of the request body if a "charset" media type
-
# parameter was given, or nil if no "charset" was specified. Note
-
# that, per RFC2616, text/* media types that specify no explicit
-
# charset are to be considered ISO-8859-1.
-
1
def content_charset
-
media_type_params['charset']
-
end
-
-
1
def scheme
-
8
if @env['HTTPS'] == 'on'
-
'https'
-
8
elsif @env['HTTP_X_FORWARDED_SSL'] == 'on'
-
'https'
-
8
elsif @env['HTTP_X_FORWARDED_SCHEME']
-
@env['HTTP_X_FORWARDED_SCHEME']
-
8
elsif @env['HTTP_X_FORWARDED_PROTO']
-
@env['HTTP_X_FORWARDED_PROTO'].split(',')[0]
-
else
-
8
@env["rack.url_scheme"]
-
end
-
end
-
-
1
def ssl?
-
8
scheme == 'https'
-
end
-
-
1
def host_with_port
-
if forwarded = @env["HTTP_X_FORWARDED_HOST"]
-
forwarded.split(/,\s?/).last
-
else
-
@env['HTTP_HOST'] || "#{@env['SERVER_NAME'] || @env['SERVER_ADDR']}:#{@env['SERVER_PORT']}"
-
end
-
end
-
-
1
def port
-
if port = host_with_port.split(/:/)[1]
-
port.to_i
-
elsif port = @env['HTTP_X_FORWARDED_PORT']
-
port.to_i
-
elsif @env.has_key?("HTTP_X_FORWARDED_HOST")
-
DEFAULT_PORTS[scheme]
-
else
-
@env["SERVER_PORT"].to_i
-
end
-
end
-
-
1
def host
-
# Remove port number.
-
host_with_port.to_s.gsub(/:\d+\z/, '')
-
end
-
-
1
def script_name=(s); @env["SCRIPT_NAME"] = s.to_s end
-
1
def path_info=(s); @env["PATH_INFO"] = s.to_s end
-
-
-
# Checks the HTTP request method (or verb) to see if it was of type DELETE
-
1
def delete?; request_method == "DELETE" end
-
-
# Checks the HTTP request method (or verb) to see if it was of type GET
-
1
def get?; request_method == "GET" end
-
-
# Checks the HTTP request method (or verb) to see if it was of type HEAD
-
1
def head?; request_method == "HEAD" end
-
-
# Checks the HTTP request method (or verb) to see if it was of type OPTIONS
-
1
def options?; request_method == "OPTIONS" end
-
-
# Checks the HTTP request method (or verb) to see if it was of type PATCH
-
1
def patch?; request_method == "PATCH" end
-
-
# Checks the HTTP request method (or verb) to see if it was of type POST
-
1
def post?; request_method == "POST" end
-
-
# Checks the HTTP request method (or verb) to see if it was of type PUT
-
1
def put?; request_method == "PUT" end
-
-
# Checks the HTTP request method (or verb) to see if it was of type TRACE
-
1
def trace?; request_method == "TRACE" end
-
-
-
# The set of form-data media-types. Requests that do not indicate
-
# one of the media types presents in this list will not be eligible
-
# for form-data / param parsing.
-
1
FORM_DATA_MEDIA_TYPES = [
-
'application/x-www-form-urlencoded',
-
'multipart/form-data'
-
]
-
-
# The set of media-types. Requests that do not indicate
-
# one of the media types presents in this list will not be eligible
-
# for param parsing like soap attachments or generic multiparts
-
1
PARSEABLE_DATA_MEDIA_TYPES = [
-
'multipart/related',
-
'multipart/mixed'
-
]
-
-
# Default ports depending on scheme. Used to decide whether or not
-
# to include the port in a generated URI.
-
1
DEFAULT_PORTS = { 'http' => 80, 'https' => 443, 'coffee' => 80 }
-
-
# Determine whether the request body contains form-data by checking
-
# the request Content-Type for one of the media-types:
-
# "application/x-www-form-urlencoded" or "multipart/form-data". The
-
# list of form-data media types can be modified through the
-
# +FORM_DATA_MEDIA_TYPES+ array.
-
#
-
# A request body is also assumed to contain form-data when no
-
# Content-Type header is provided and the request_method is POST.
-
1
def form_data?
-
type = media_type
-
meth = env["rack.methodoverride.original_method"] || env['REQUEST_METHOD']
-
(meth == 'POST' && type.nil?) || FORM_DATA_MEDIA_TYPES.include?(type)
-
end
-
-
# Determine whether the request body contains data by checking
-
# the request media_type against registered parse-data media-types
-
1
def parseable_data?
-
4
PARSEABLE_DATA_MEDIA_TYPES.include?(media_type)
-
end
-
-
# Returns the data received in the query string.
-
1
def GET
-
if @env["rack.request.query_string"] == query_string
-
@env["rack.request.query_hash"]
-
else
-
@env["rack.request.query_string"] = query_string
-
@env["rack.request.query_hash"] = parse_query(query_string)
-
end
-
end
-
-
# Returns the data received in the request body.
-
#
-
# This method support both application/x-www-form-urlencoded and
-
# multipart/form-data.
-
1
def POST
-
4
if @env["rack.input"].nil?
-
raise "Missing rack.input"
-
4
elsif @env["rack.request.form_input"].eql? @env["rack.input"]
-
@env["rack.request.form_hash"]
-
4
elsif form_data? || parseable_data?
-
@env["rack.request.form_input"] = @env["rack.input"]
-
unless @env["rack.request.form_hash"] = parse_multipart(env)
-
form_vars = @env["rack.input"].read
-
-
# Fix for Safari Ajax postings that always append \0
-
# form_vars.sub!(/\0\z/, '') # performance replacement:
-
form_vars.slice!(-1) if form_vars[-1] == ?\0
-
-
@env["rack.request.form_vars"] = form_vars
-
@env["rack.request.form_hash"] = parse_query(form_vars)
-
-
@env["rack.input"].rewind
-
end
-
@env["rack.request.form_hash"]
-
else
-
4
{}
-
end
-
end
-
-
# The union of GET and POST data.
-
#
-
# Note that modifications will not be persisted in the env. Use update_param or delete_param if you want to destructively modify params.
-
1
def params
-
@params ||= self.GET.merge(self.POST)
-
rescue EOFError
-
self.GET.dup
-
end
-
-
# Destructively update a parameter, whether it's in GET and/or POST. Returns nil.
-
#
-
# The parameter is updated wherever it was previous defined, so GET, POST, or both. If it wasn't previously defined, it's inserted into GET.
-
#
-
# env['rack.input'] is not touched.
-
1
def update_param(k, v)
-
found = false
-
if self.GET.has_key?(k)
-
found = true
-
self.GET[k] = v
-
end
-
if self.POST.has_key?(k)
-
found = true
-
self.POST[k] = v
-
end
-
unless found
-
self.GET[k] = v
-
end
-
@params = nil
-
nil
-
end
-
-
# Destructively delete a parameter, whether it's in GET or POST. Returns the value of the deleted parameter.
-
#
-
# If the parameter is in both GET and POST, the POST value takes precedence since that's how #params works.
-
#
-
# env['rack.input'] is not touched.
-
1
def delete_param(k)
-
v = [ self.POST.delete(k), self.GET.delete(k) ].compact.first
-
@params = nil
-
v
-
end
-
-
# shortcut for request.params[key]
-
1
def [](key)
-
params[key.to_s]
-
end
-
-
# shortcut for request.params[key] = value
-
#
-
# Note that modifications will not be persisted in the env. Use update_param or delete_param if you want to destructively modify params.
-
1
def []=(key, value)
-
params[key.to_s] = value
-
end
-
-
# like Hash#values_at
-
1
def values_at(*keys)
-
keys.map{|key| params[key] }
-
end
-
-
# the referer of the client
-
1
def referer
-
@env['HTTP_REFERER']
-
end
-
1
alias referrer referer
-
-
1
def user_agent
-
@env['HTTP_USER_AGENT']
-
end
-
-
1
def cookies
-
4
hash = @env["rack.request.cookie_hash"] ||= {}
-
4
string = @env["HTTP_COOKIE"]
-
-
4
return hash if string == @env["rack.request.cookie_string"]
-
hash.clear
-
-
# According to RFC 2109:
-
# If multiple cookies satisfy the criteria above, they are ordered in
-
# the Cookie header such that those with more specific Path attributes
-
# precede those with less specific. Ordering with respect to other
-
# attributes (e.g., Domain) is unspecified.
-
cookies = Utils.parse_query(string, ';,') { |s| Rack::Utils.unescape(s) rescue s }
-
cookies.each { |k,v| hash[k] = Array === v ? v.first : v }
-
@env["rack.request.cookie_string"] = string
-
hash
-
end
-
-
1
def xhr?
-
@env["HTTP_X_REQUESTED_WITH"] == "XMLHttpRequest"
-
end
-
-
1
def base_url
-
url = "#{scheme}://#{host}"
-
url << ":#{port}" if port != DEFAULT_PORTS[scheme]
-
url
-
end
-
-
# Tries to return a remake of the original request URL as a string.
-
1
def url
-
base_url + fullpath
-
end
-
-
1
def path
-
4
script_name + path_info
-
end
-
-
1
def fullpath
-
4
query_string.empty? ? path : "#{path}?#{query_string}"
-
end
-
-
1
def accept_encoding
-
@env["HTTP_ACCEPT_ENCODING"].to_s.split(/\s*,\s*/).map do |part|
-
encoding, parameters = part.split(/\s*;\s*/, 2)
-
quality = 1.0
-
if parameters and /\Aq=([\d.]+)/ =~ parameters
-
quality = $1.to_f
-
end
-
[encoding, quality]
-
end
-
end
-
-
1
def trusted_proxy?(ip)
-
ip =~ /\A127\.0\.0\.1\Z|\A(10|172\.(1[6-9]|2[0-9]|30|31)|192\.168)\.|\A::1\Z|\Afd[0-9a-f]{2}:.+|\Alocalhost\Z|\Aunix\Z|\Aunix:/i
-
end
-
-
1
def ip
-
remote_addrs = split_ip_addresses(@env['REMOTE_ADDR'])
-
remote_addrs = reject_trusted_ip_addresses(remote_addrs)
-
-
return remote_addrs.first if remote_addrs.any?
-
-
forwarded_ips = split_ip_addresses(@env['HTTP_X_FORWARDED_FOR'])
-
-
if client_ip = @env['HTTP_CLIENT_IP']
-
# If forwarded_ips doesn't include the client_ip, it might be an
-
# ip spoofing attempt, so we ignore HTTP_CLIENT_IP
-
return client_ip if forwarded_ips.include?(client_ip)
-
end
-
-
return reject_trusted_ip_addresses(forwarded_ips).last || @env["REMOTE_ADDR"]
-
end
-
-
1
protected
-
1
def split_ip_addresses(ip_addresses)
-
ip_addresses ? ip_addresses.strip.split(/[,\s]+/) : []
-
end
-
-
1
def reject_trusted_ip_addresses(ip_addresses)
-
ip_addresses.reject { |ip| trusted_proxy?(ip) }
-
end
-
-
1
def parse_query(qs)
-
Utils.parse_nested_query(qs)
-
end
-
-
1
def parse_multipart(env)
-
Rack::Multipart.parse_multipart(env)
-
end
-
end
-
end
-
1
require 'rack/request'
-
1
require 'rack/utils'
-
1
require 'time'
-
-
1
module Rack
-
# Rack::Response provides a convenient interface to create a Rack
-
# response.
-
#
-
# It allows setting of headers and cookies, and provides useful
-
# defaults (a OK response containing HTML).
-
#
-
# You can use Response#write to iteratively generate your response,
-
# but note that this is buffered by Rack::Response until you call
-
# +finish+. +finish+ however can take a block inside which calls to
-
# +write+ are synchronous with the Rack response.
-
#
-
# Your application's +call+ should end returning Response#finish.
-
-
1
class Response
-
1
attr_accessor :length
-
-
1
def initialize(body=[], status=200, header={})
-
@status = status.to_i
-
@header = Utils::HeaderHash.new.merge(header)
-
-
@chunked = "chunked" == @header['Transfer-Encoding']
-
@writer = lambda { |x| @body << x }
-
@block = nil
-
@length = 0
-
-
@body = []
-
-
if body.respond_to? :to_str
-
write body.to_str
-
elsif body.respond_to?(:each)
-
body.each { |part|
-
write part.to_s
-
}
-
else
-
raise TypeError, "stringable or iterable required"
-
end
-
-
yield self if block_given?
-
end
-
-
1
attr_reader :header
-
1
attr_accessor :status, :body
-
-
1
def [](key)
-
header[key]
-
end
-
-
1
def []=(key, value)
-
header[key] = value
-
end
-
-
1
def set_cookie(key, value)
-
Utils.set_cookie_header!(header, key, value)
-
end
-
-
1
def delete_cookie(key, value={})
-
Utils.delete_cookie_header!(header, key, value)
-
end
-
-
1
def redirect(target, status=302)
-
self.status = status
-
self["Location"] = target
-
end
-
-
1
def finish(&block)
-
@block = block
-
-
if [204, 205, 304].include?(status.to_i)
-
header.delete "Content-Type"
-
header.delete "Content-Length"
-
close
-
[status.to_i, header, []]
-
else
-
[status.to_i, header, BodyProxy.new(self){}]
-
end
-
end
-
1
alias to_a finish # For *response
-
1
alias to_ary finish # For implicit-splat on Ruby 1.9.2
-
-
1
def each(&callback)
-
@body.each(&callback)
-
@writer = callback
-
@block.call(self) if @block
-
end
-
-
# Append to body and update Content-Length.
-
#
-
# NOTE: Do not mix #write and direct #body access!
-
#
-
1
def write(str)
-
s = str.to_s
-
@length += Rack::Utils.bytesize(s) unless @chunked
-
@writer.call s
-
-
header["Content-Length"] = @length.to_s unless @chunked
-
str
-
end
-
-
1
def close
-
body.close if body.respond_to?(:close)
-
end
-
-
1
def empty?
-
@block == nil && @body.empty?
-
end
-
-
1
alias headers header
-
-
1
module Helpers
-
1
def invalid?; status < 100 || status >= 600; end
-
-
1
def informational?; status >= 100 && status < 200; end
-
1
def successful?; status >= 200 && status < 300; end
-
2
def redirection?; status >= 300 && status < 400; end
-
1
def client_error?; status >= 400 && status < 500; end
-
1
def server_error?; status >= 500 && status < 600; end
-
-
1
def ok?; status == 200; end
-
1
def bad_request?; status == 400; end
-
1
def forbidden?; status == 403; end
-
1
def not_found?; status == 404; end
-
1
def method_not_allowed?; status == 405; end
-
1
def unprocessable?; status == 422; end
-
-
1
def redirect?; [301, 302, 303, 307].include? status; end
-
-
# Headers
-
1
attr_reader :headers, :original_headers
-
-
1
def include?(header)
-
!!headers[header]
-
end
-
-
1
def content_type
-
headers["Content-Type"]
-
end
-
-
1
def content_length
-
cl = headers["Content-Length"]
-
cl ? cl.to_i : cl
-
end
-
-
1
def location
-
headers["Location"]
-
end
-
end
-
-
1
include Helpers
-
end
-
end
-
1
module Rack
-
# Sets an "X-Runtime" response header, indicating the response
-
# time of the request, in seconds
-
#
-
# You can put it right before the application to see the processing
-
# time, or before all the other middlewares to include time for them,
-
# too.
-
1
class Runtime
-
1
def initialize(app, name = nil)
-
1
@app = app
-
1
@header_name = "X-Runtime"
-
1
@header_name << "-#{name}" if name
-
end
-
-
1
def call(env)
-
start_time = Time.now
-
status, headers, body = @app.call(env)
-
request_time = Time.now - start_time
-
-
if !headers.has_key?(@header_name)
-
headers[@header_name] = "%0.6f" % request_time
-
end
-
-
[status, headers, body]
-
end
-
end
-
end
-
1
require 'rack/file'
-
-
1
module Rack
-
-
# = Sendfile
-
#
-
# The Sendfile middleware intercepts responses whose body is being
-
# served from a file and replaces it with a server specific X-Sendfile
-
# header. The web server is then responsible for writing the file contents
-
# to the client. This can dramatically reduce the amount of work required
-
# by the Ruby backend and takes advantage of the web server's optimized file
-
# delivery code.
-
#
-
# In order to take advantage of this middleware, the response body must
-
# respond to +to_path+ and the request must include an X-Sendfile-Type
-
# header. Rack::File and other components implement +to_path+ so there's
-
# rarely anything you need to do in your application. The X-Sendfile-Type
-
# header is typically set in your web servers configuration. The following
-
# sections attempt to document
-
#
-
# === Nginx
-
#
-
# Nginx supports the X-Accel-Redirect header. This is similar to X-Sendfile
-
# but requires parts of the filesystem to be mapped into a private URL
-
# hierarachy.
-
#
-
# The following example shows the Nginx configuration required to create
-
# a private "/files/" area, enable X-Accel-Redirect, and pass the special
-
# X-Sendfile-Type and X-Accel-Mapping headers to the backend:
-
#
-
# location ~ /files/(.*) {
-
# internal;
-
# alias /var/www/$1;
-
# }
-
#
-
# location / {
-
# proxy_redirect off;
-
#
-
# proxy_set_header Host $host;
-
# proxy_set_header X-Real-IP $remote_addr;
-
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
-
#
-
# proxy_set_header X-Sendfile-Type X-Accel-Redirect;
-
# proxy_set_header X-Accel-Mapping /var/www/=/files/;
-
#
-
# proxy_pass http://127.0.0.1:8080/;
-
# }
-
#
-
# Note that the X-Sendfile-Type header must be set exactly as shown above.
-
# The X-Accel-Mapping header should specify the location on the file system,
-
# followed by an equals sign (=), followed name of the private URL pattern
-
# that it maps to. The middleware performs a simple substitution on the
-
# resulting path.
-
#
-
# See Also: http://wiki.codemongers.com/NginxXSendfile
-
#
-
# === lighttpd
-
#
-
# Lighttpd has supported some variation of the X-Sendfile header for some
-
# time, although only recent version support X-Sendfile in a reverse proxy
-
# configuration.
-
#
-
# $HTTP["host"] == "example.com" {
-
# proxy-core.protocol = "http"
-
# proxy-core.balancer = "round-robin"
-
# proxy-core.backends = (
-
# "127.0.0.1:8000",
-
# "127.0.0.1:8001",
-
# ...
-
# )
-
#
-
# proxy-core.allow-x-sendfile = "enable"
-
# proxy-core.rewrite-request = (
-
# "X-Sendfile-Type" => (".*" => "X-Sendfile")
-
# )
-
# }
-
#
-
# See Also: http://redmine.lighttpd.net/wiki/lighttpd/Docs:ModProxyCore
-
#
-
# === Apache
-
#
-
# X-Sendfile is supported under Apache 2.x using a separate module:
-
#
-
# https://tn123.org/mod_xsendfile/
-
#
-
# Once the module is compiled and installed, you can enable it using
-
# XSendFile config directive:
-
#
-
# RequestHeader Set X-Sendfile-Type X-Sendfile
-
# ProxyPassReverse / http://localhost:8001/
-
# XSendFile on
-
#
-
# === Mapping parameter
-
#
-
# The third parameter allows for an overriding extension of the
-
# X-Accel-Mapping header. Mappings should be provided in tuples of internal to
-
# external. The internal values may contain regular expression syntax, they
-
# will be matched with case indifference.
-
-
1
class Sendfile
-
1
F = ::File
-
-
1
def initialize(app, variation=nil, mappings=[])
-
1
@app = app
-
1
@variation = variation
-
1
@mappings = mappings.map do |internal, external|
-
[/^#{internal}/i, external]
-
end
-
end
-
-
1
def call(env)
-
status, headers, body = @app.call(env)
-
if body.respond_to?(:to_path)
-
case type = variation(env)
-
when 'X-Accel-Redirect'
-
path = F.expand_path(body.to_path)
-
if url = map_accel_path(env, path)
-
headers['Content-Length'] = '0'
-
headers[type] = url
-
body.close if body.respond_to?(:close)
-
body = []
-
else
-
env['rack.errors'].puts "X-Accel-Mapping header missing"
-
end
-
when 'X-Sendfile', 'X-Lighttpd-Send-File'
-
path = F.expand_path(body.to_path)
-
headers['Content-Length'] = '0'
-
headers[type] = path
-
body.close if body.respond_to?(:close)
-
body = []
-
when '', nil
-
else
-
env['rack.errors'].puts "Unknown x-sendfile variation: '#{variation}'.\n"
-
end
-
end
-
[status, headers, body]
-
end
-
-
1
private
-
1
def variation(env)
-
@variation ||
-
env['sendfile.type'] ||
-
env['HTTP_X_SENDFILE_TYPE']
-
end
-
-
1
def map_accel_path(env, path)
-
if mapping = @mappings.find { |internal,_| internal =~ path }
-
path.sub(*mapping)
-
elsif mapping = env['HTTP_X_ACCEL_MAPPING']
-
internal, external = mapping.split('=', 2).map{ |p| p.strip }
-
path.sub(/^#{internal}/i, external)
-
end
-
end
-
end
-
end
-
# AUTHOR: blink <blinketje@gmail.com>; blink#ruby-lang@irc.freenode.net
-
# bugrep: Andreas Zehnder
-
-
1
require 'time'
-
1
require 'rack/request'
-
1
require 'rack/response'
-
1
begin
-
1
require 'securerandom'
-
rescue LoadError
-
# We just won't get securerandom
-
end
-
-
1
module Rack
-
-
1
module Session
-
-
1
module Abstract
-
1
ENV_SESSION_KEY = 'rack.session'.freeze
-
1
ENV_SESSION_OPTIONS_KEY = 'rack.session.options'.freeze
-
-
# SessionHash is responsible to lazily load the session from store.
-
-
1
class SessionHash
-
1
include Enumerable
-
1
attr_writer :id
-
-
1
def self.find(env)
-
env[ENV_SESSION_KEY]
-
end
-
-
1
def self.set(env, session)
-
env[ENV_SESSION_KEY] = session
-
end
-
-
1
def self.set_options(env, options)
-
env[ENV_SESSION_OPTIONS_KEY] = options.dup
-
end
-
-
1
def initialize(store, env)
-
4
@store = store
-
4
@env = env
-
4
@loaded = false
-
end
-
-
1
def id
-
return @id if @loaded or instance_variable_defined?(:@id)
-
@id = @store.send(:extract_session_id, @env)
-
end
-
-
1
def options
-
@env[ENV_SESSION_OPTIONS_KEY]
-
end
-
-
1
def each(&block)
-
load_for_read!
-
@data.each(&block)
-
end
-
-
1
def [](key)
-
8
load_for_read!
-
8
@data[key.to_s]
-
end
-
1
alias :fetch :[]
-
-
1
def has_key?(key)
-
load_for_read!
-
@data.has_key?(key.to_s)
-
end
-
1
alias :key? :has_key?
-
1
alias :include? :has_key?
-
-
1
def []=(key, value)
-
4
load_for_write!
-
4
@data[key.to_s] = value
-
end
-
1
alias :store :[]=
-
-
1
def clear
-
load_for_write!
-
@data.clear
-
end
-
-
1
def destroy
-
clear
-
@id = @store.send(:destroy_session, @env, id, options)
-
end
-
-
1
def to_hash
-
load_for_read!
-
@data.dup
-
end
-
-
1
def update(hash)
-
load_for_write!
-
@data.update(stringify_keys(hash))
-
end
-
1
alias :merge! :update
-
-
1
def replace(hash)
-
load_for_write!
-
@data.replace(stringify_keys(hash))
-
end
-
-
1
def delete(key)
-
4
load_for_write!
-
4
@data.delete(key.to_s)
-
end
-
-
1
def inspect
-
if loaded?
-
@data.inspect
-
else
-
"#<#{self.class}:0x#{self.object_id.to_s(16)} not yet loaded>"
-
end
-
end
-
-
1
def exists?
-
return @exists if instance_variable_defined?(:@exists)
-
@data = {}
-
@exists = @store.send(:session_exists?, @env)
-
end
-
-
1
def loaded?
-
16
@loaded
-
end
-
-
1
def empty?
-
load_for_read!
-
@data.empty?
-
end
-
-
1
def keys
-
@data.keys
-
end
-
-
1
def values
-
@data.values
-
end
-
-
1
private
-
-
1
def load_for_read!
-
8
load! if !loaded? && exists?
-
end
-
-
1
def load_for_write!
-
8
load! unless loaded?
-
end
-
-
1
def load!
-
@id, session = @store.send(:load_session, @env)
-
@data = stringify_keys(session)
-
@loaded = true
-
end
-
-
1
def stringify_keys(other)
-
4
hash = {}
-
4
other.each do |key, value|
-
hash[key.to_s] = value
-
end
-
4
hash
-
end
-
end
-
-
# ID sets up a basic framework for implementing an id based sessioning
-
# service. Cookies sent to the client for maintaining sessions will only
-
# contain an id reference. Only #get_session and #set_session are
-
# required to be overwritten.
-
#
-
# All parameters are optional.
-
# * :key determines the name of the cookie, by default it is
-
# 'rack.session'
-
# * :path, :domain, :expire_after, :secure, and :httponly set the related
-
# cookie options as by Rack::Response#add_cookie
-
# * :skip will not a set a cookie in the response nor update the session state
-
# * :defer will not set a cookie in the response but still update the session
-
# state if it is used with a backend
-
# * :renew (implementation dependent) will prompt the generation of a new
-
# session id, and migration of data to be referenced at the new id. If
-
# :defer is set, it will be overridden and the cookie will be set.
-
# * :sidbits sets the number of bits in length that a generated session
-
# id will be.
-
#
-
# These options can be set on a per request basis, at the location of
-
# env['rack.session.options']. Additionally the id of the session can be
-
# found within the options hash at the key :id. It is highly not
-
# recommended to change its value.
-
#
-
# Is Rack::Utils::Context compatible.
-
#
-
# Not included by default; you must require 'rack/session/abstract/id'
-
# to use.
-
-
1
class ID
-
1
DEFAULT_OPTIONS = {
-
:key => 'rack.session',
-
:path => '/',
-
:domain => nil,
-
:expire_after => nil,
-
:secure => false,
-
:httponly => true,
-
:defer => false,
-
:renew => false,
-
:sidbits => 128,
-
:cookie_only => true,
-
1
:secure_random => (::SecureRandom rescue false)
-
}
-
-
1
attr_reader :key, :default_options
-
-
1
def initialize(app, options={})
-
1
@app = app
-
1
@default_options = self.class::DEFAULT_OPTIONS.merge(options)
-
1
@key = @default_options.delete(:key)
-
1
@cookie_only = @default_options.delete(:cookie_only)
-
1
initialize_sid
-
end
-
-
1
def call(env)
-
context(env)
-
end
-
-
1
def context(env, app=@app)
-
prepare_session(env)
-
status, headers, body = app.call(env)
-
commit_session(env, status, headers, body)
-
end
-
-
1
private
-
-
1
def initialize_sid
-
@sidbits = @default_options[:sidbits]
-
@sid_secure = @default_options[:secure_random]
-
@sid_length = @sidbits / 4
-
end
-
-
# Generate a new session id using Ruby #rand. The size of the
-
# session id is controlled by the :sidbits option.
-
# Monkey patch this to use custom methods for session id generation.
-
-
1
def generate_sid(secure = @sid_secure)
-
if secure
-
secure.hex(@sid_length)
-
else
-
"%0#{@sid_length}x" % Kernel.rand(2**@sidbits - 1)
-
end
-
rescue NotImplementedError
-
generate_sid(false)
-
end
-
-
# Sets the lazy session at 'rack.session' and places options and session
-
# metadata into 'rack.session.options'.
-
-
1
def prepare_session(env)
-
session_was = env[ENV_SESSION_KEY]
-
env[ENV_SESSION_KEY] = session_class.new(self, env)
-
env[ENV_SESSION_OPTIONS_KEY] = @default_options.dup
-
env[ENV_SESSION_KEY].merge! session_was if session_was
-
end
-
-
# Extracts the session id from provided cookies and passes it and the
-
# environment to #get_session.
-
-
1
def load_session(env)
-
sid = current_session_id(env)
-
sid, session = get_session(env, sid)
-
[sid, session || {}]
-
end
-
-
# Extract session id from request object.
-
-
1
def extract_session_id(env)
-
request = Rack::Request.new(env)
-
sid = request.cookies[@key]
-
sid ||= request.params[@key] unless @cookie_only
-
sid
-
end
-
-
# Returns the current session id from the SessionHash.
-
-
1
def current_session_id(env)
-
env[ENV_SESSION_KEY].id
-
end
-
-
# Check if the session exists or not.
-
-
1
def session_exists?(env)
-
value = current_session_id(env)
-
value && !value.empty?
-
end
-
-
# Session should be commited if it was loaded, any of specific options like :renew, :drop
-
# or :expire_after was given and the security permissions match. Skips if skip is given.
-
-
1
def commit_session?(env, session, options)
-
if options[:skip]
-
false
-
else
-
has_session = loaded_session?(session) || forced_session_update?(session, options)
-
has_session && security_matches?(env, options)
-
end
-
end
-
-
1
def loaded_session?(session)
-
!session.is_a?(session_class) || session.loaded?
-
end
-
-
1
def forced_session_update?(session, options)
-
force_options?(options) && session && !session.empty?
-
end
-
-
1
def force_options?(options)
-
options.values_at(:renew, :drop, :defer, :expire_after).any?
-
end
-
-
1
def security_matches?(env, options)
-
return true unless options[:secure]
-
request = Rack::Request.new(env)
-
request.ssl?
-
end
-
-
# Acquires the session from the environment and the session id from
-
# the session options and passes them to #set_session. If successful
-
# and the :defer option is not true, a cookie will be added to the
-
# response with the session's id.
-
-
1
def commit_session(env, status, headers, body)
-
session = env[ENV_SESSION_KEY]
-
options = session.options
-
-
if options[:drop] || options[:renew]
-
session_id = destroy_session(env, session.id || generate_sid, options)
-
return [status, headers, body] unless session_id
-
end
-
-
return [status, headers, body] unless commit_session?(env, session, options)
-
-
session.send(:load!) unless loaded_session?(session)
-
session_id ||= session.id
-
session_data = session.to_hash.delete_if { |k,v| v.nil? }
-
-
if not data = set_session(env, session_id, session_data, options)
-
env["rack.errors"].puts("Warning! #{self.class.name} failed to save session. Content dropped.")
-
elsif options[:defer] and not options[:renew]
-
env["rack.errors"].puts("Defering cookie for #{session_id}") if $VERBOSE
-
else
-
cookie = Hash.new
-
cookie[:value] = data
-
cookie[:expires] = Time.now + options[:expire_after] if options[:expire_after]
-
set_cookie(env, headers, cookie.merge!(options))
-
end
-
-
[status, headers, body]
-
end
-
-
# Sets the cookie back to the client with session id. We skip the cookie
-
# setting if the value didn't change (sid is the same) or expires was given.
-
-
1
def set_cookie(env, headers, cookie)
-
request = Rack::Request.new(env)
-
if request.cookies[@key] != cookie[:value] || cookie[:expires]
-
Utils.set_cookie_header!(headers, @key, cookie)
-
end
-
end
-
-
# Allow subclasses to prepare_session for different Session classes
-
-
1
def session_class
-
SessionHash
-
end
-
-
# All thread safety and session retrival proceedures should occur here.
-
# Should return [session_id, session].
-
# If nil is provided as the session id, generation of a new valid id
-
# should occur within.
-
-
1
def get_session(env, sid)
-
raise '#get_session not implemented.'
-
end
-
-
# All thread safety and session storage proceedures should occur here.
-
# Must return the session id if the session was saved successfully, or
-
# false if the session could not be saved.
-
-
1
def set_session(env, sid, session, options)
-
raise '#set_session not implemented.'
-
end
-
-
# All thread safety and session destroy proceedures should occur here.
-
# Should return a new session id or nil if options[:drop]
-
-
1
def destroy_session(env, sid, options)
-
raise '#destroy_session not implemented'
-
end
-
end
-
end
-
end
-
end
-
1
require 'openssl'
-
1
require 'rack/request'
-
1
require 'rack/response'
-
1
require 'rack/session/abstract/id'
-
-
1
module Rack
-
-
1
module Session
-
-
# Rack::Session::Cookie provides simple cookie based session management.
-
# By default, the session is a Ruby Hash stored as base64 encoded marshalled
-
# data set to :key (default: rack.session). The object that encodes the
-
# session data is configurable and must respond to +encode+ and +decode+.
-
# Both methods must take a string and return a string.
-
#
-
# When the secret key is set, cookie data is checked for data integrity.
-
# The old secret key is also accepted and allows graceful secret rotation.
-
#
-
# Example:
-
#
-
# use Rack::Session::Cookie, :key => 'rack.session',
-
# :domain => 'foo.com',
-
# :path => '/',
-
# :expire_after => 2592000,
-
# :secret => 'change_me',
-
# :old_secret => 'also_change_me'
-
#
-
# All parameters are optional.
-
#
-
# Example of a cookie with no encoding:
-
#
-
# Rack::Session::Cookie.new(application, {
-
# :coder => Rack::Session::Cookie::Identity.new
-
# })
-
#
-
# Example of a cookie with custom encoding:
-
#
-
# Rack::Session::Cookie.new(application, {
-
# :coder => Class.new {
-
# def encode(str); str.reverse; end
-
# def decode(str); str.reverse; end
-
# }.new
-
# })
-
#
-
-
1
class Cookie < Abstract::ID
-
# Encode session cookies as Base64
-
1
class Base64
-
1
def encode(str)
-
[str].pack('m')
-
end
-
-
1
def decode(str)
-
str.unpack('m').first
-
end
-
-
# Encode session cookies as Marshaled Base64 data
-
1
class Marshal < Base64
-
1
def encode(str)
-
super(::Marshal.dump(str))
-
end
-
-
1
def decode(str)
-
return unless str
-
::Marshal.load(super(str)) rescue nil
-
end
-
end
-
-
# N.B. Unlike other encoding methods, the contained objects must be a
-
# valid JSON composite type, either a Hash or an Array.
-
1
class JSON < Base64
-
1
def encode(obj)
-
super(::Rack::Utils::OkJson.encode(obj))
-
end
-
-
1
def decode(str)
-
return unless str
-
::Rack::Utils::OkJson.decode(super(str)) rescue nil
-
end
-
end
-
end
-
-
# Use no encoding for session cookies
-
1
class Identity
-
1
def encode(str); str; end
-
1
def decode(str); str; end
-
end
-
-
# Reverse string encoding. (trollface)
-
1
class Reverse
-
1
def encode(str); str.reverse; end
-
1
def decode(str); str.reverse; end
-
end
-
-
1
attr_reader :coder
-
-
1
def initialize(app, options={})
-
@secrets = options.values_at(:secret, :old_secret).compact
-
warn <<-MSG unless @secrets.size >= 1
-
SECURITY WARNING: No secret option provided to Rack::Session::Cookie.
-
This poses a security threat. It is strongly recommended that you
-
provide a secret to prevent exploits that may be possible from crafted
-
cookies. This will not be supported in future versions of Rack, and
-
future versions will even invalidate your existing user cookies.
-
-
Called from: #{caller[0]}.
-
MSG
-
@coder = options[:coder] ||= Base64::Marshal.new
-
super(app, options.merge!(:cookie_only => true))
-
end
-
-
1
private
-
-
1
def get_session(env, sid)
-
data = unpacked_cookie_data(env)
-
data = persistent_session_id!(data)
-
[data["session_id"], data]
-
end
-
-
1
def extract_session_id(env)
-
unpacked_cookie_data(env)["session_id"]
-
end
-
-
1
def unpacked_cookie_data(env)
-
env["rack.session.unpacked_cookie_data"] ||= begin
-
request = Rack::Request.new(env)
-
session_data = request.cookies[@key]
-
-
if @secrets.size > 0 && session_data
-
session_data, digest = session_data.split("--")
-
session_data = nil unless digest_match?(session_data, digest)
-
end
-
-
coder.decode(session_data) || {}
-
end
-
end
-
-
1
def persistent_session_id!(data, sid=nil)
-
data ||= {}
-
data["session_id"] ||= sid || generate_sid
-
data
-
end
-
-
1
def set_session(env, session_id, session, options)
-
session = session.merge("session_id" => session_id)
-
session_data = coder.encode(session)
-
-
if @secrets.first
-
session_data << "--#{generate_hmac(session_data, @secrets.first)}"
-
end
-
-
if session_data.size > (4096 - @key.size)
-
env["rack.errors"].puts("Warning! Rack::Session::Cookie data size exceeds 4K.")
-
nil
-
else
-
session_data
-
end
-
end
-
-
1
def destroy_session(env, session_id, options)
-
# Nothing to do here, data is in the client
-
generate_sid unless options[:drop]
-
end
-
-
1
def digest_match?(data, digest)
-
return unless data && digest
-
@secrets.any? do |secret|
-
Rack::Utils.secure_compare(digest, generate_hmac(data, secret))
-
end
-
end
-
-
1
def generate_hmac(data, secret)
-
OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, secret, data)
-
end
-
-
end
-
end
-
end
-
1
module Rack
-
# Rack::URLMap takes a hash mapping urls or paths to apps, and
-
# dispatches accordingly. Support for HTTP/1.1 host names exists if
-
# the URLs start with <tt>http://</tt> or <tt>https://</tt>.
-
#
-
# URLMap modifies the SCRIPT_NAME and PATH_INFO such that the part
-
# relevant for dispatch is in the SCRIPT_NAME, and the rest in the
-
# PATH_INFO. This should be taken care of when you need to
-
# reconstruct the URL in order to create links.
-
#
-
# URLMap dispatches in such a way that the longest paths are tried
-
# first, since they are most specific.
-
-
1
class URLMap
-
1
NEGATIVE_INFINITY = -1.0 / 0.0
-
1
INFINITY = 1.0 / 0.0
-
-
1
def initialize(map = {})
-
1
remap(map)
-
end
-
-
1
def remap(map)
-
1
@mapping = map.map { |location, app|
-
1
if location =~ %r{\Ahttps?://(.*?)(/.*)}
-
host, location = $1, $2
-
else
-
1
host = nil
-
end
-
-
1
unless location[0] == ?/
-
raise ArgumentError, "paths need to start with /"
-
end
-
-
1
location = location.chomp('/')
-
1
match = Regexp.new("^#{Regexp.quote(location).gsub('/', '/+')}(.*)", nil, 'n')
-
-
1
[host, location, match, app]
-
}.sort_by do |(host, location, _, _)|
-
1
[host ? -host.size : INFINITY, -location.size]
-
end
-
end
-
-
1
def call(env)
-
path = env["PATH_INFO"]
-
script_name = env['SCRIPT_NAME']
-
hHost = env['HTTP_HOST']
-
sName = env['SERVER_NAME']
-
sPort = env['SERVER_PORT']
-
-
@mapping.each do |host, location, match, app|
-
unless hHost == host \
-
|| sName == host \
-
|| (!host && (hHost == sName || hHost == sName+':'+sPort))
-
next
-
end
-
-
next unless m = match.match(path.to_s)
-
-
rest = m[1]
-
next unless !rest || rest.empty? || rest[0] == ?/
-
-
env['SCRIPT_NAME'] = (script_name + location)
-
env['PATH_INFO'] = rest
-
-
return app.call(env)
-
end
-
-
[404, {"Content-Type" => "text/plain", "X-Cascade" => "pass"}, ["Not Found: #{path}"]]
-
-
ensure
-
env['PATH_INFO'] = path
-
env['SCRIPT_NAME'] = script_name
-
end
-
end
-
end
-
-
# -*- encoding: binary -*-
-
1
require 'fileutils'
-
1
require 'set'
-
1
require 'tempfile'
-
1
require 'rack/multipart'
-
1
require 'time'
-
-
4
major, minor, patch = RUBY_VERSION.split('.').map { |v| v.to_i }
-
-
1
if major == 1 && minor < 9
-
require 'rack/backports/uri/common_18'
-
elsif major == 1 && minor == 9 && patch == 2 && RUBY_PATCHLEVEL <= 320 && RUBY_ENGINE != 'jruby'
-
require 'rack/backports/uri/common_192'
-
elsif major == 1 && minor == 9 && patch == 3 && RUBY_PATCHLEVEL < 125
-
require 'rack/backports/uri/common_193'
-
else
-
1
require 'uri/common'
-
end
-
-
1
module Rack
-
# Rack::Utils contains a grab-bag of useful methods for writing web
-
# applications adopted from all kinds of Ruby libraries.
-
-
1
module Utils
-
# URI escapes. (CGI style space to +)
-
1
def escape(s)
-
URI.encode_www_form_component(s)
-
end
-
1
module_function :escape
-
-
# Like URI escaping, but with %20 instead of +. Strictly speaking this is
-
# true URI escaping.
-
1
def escape_path(s)
-
escape(s).gsub('+', '%20')
-
end
-
1
module_function :escape_path
-
-
# Unescapes a URI escaped string with +encoding+. +encoding+ will be the
-
# target encoding of the string returned, and it defaults to UTF-8
-
1
if defined?(::Encoding)
-
1
def unescape(s, encoding = Encoding::UTF_8)
-
URI.decode_www_form_component(s, encoding)
-
end
-
else
-
def unescape(s, encoding = nil)
-
URI.decode_www_form_component(s, encoding)
-
end
-
end
-
1
module_function :unescape
-
-
1
DEFAULT_SEP = /[&;] */n
-
-
1
class << self
-
1
attr_accessor :key_space_limit
-
end
-
-
# The default number of bytes to allow parameter keys to take up.
-
# This helps prevent a rogue client from flooding a Request.
-
1
self.key_space_limit = 65536
-
-
# Stolen from Mongrel, with some small modifications:
-
# Parses a query string by breaking it up at the '&'
-
# and ';' characters. You can also use this to parse
-
# cookies by changing the characters used in the second
-
# parameter (which defaults to '&;').
-
1
def parse_query(qs, d = nil, &unescaper)
-
unescaper ||= method(:unescape)
-
-
params = KeySpaceConstrainedParams.new
-
-
(qs || '').split(d ? /[#{d}] */n : DEFAULT_SEP).each do |p|
-
next if p.empty?
-
k, v = p.split('=', 2).map(&unescaper)
-
-
if cur = params[k]
-
if cur.class == Array
-
params[k] << v
-
else
-
params[k] = [cur, v]
-
end
-
else
-
params[k] = v
-
end
-
end
-
-
return params.to_params_hash
-
end
-
1
module_function :parse_query
-
-
1
def parse_nested_query(qs, d = nil)
-
params = KeySpaceConstrainedParams.new
-
-
(qs || '').split(d ? /[#{d}] */n : DEFAULT_SEP).each do |p|
-
k, v = p.split('=', 2).map { |s| unescape(s) }
-
-
normalize_params(params, k, v)
-
end
-
-
return params.to_params_hash
-
end
-
1
module_function :parse_nested_query
-
-
1
def normalize_params(params, name, v = nil)
-
name =~ %r(\A[\[\]]*([^\[\]]+)\]*)
-
k = $1 || ''
-
after = $' || ''
-
-
return if k.empty?
-
-
if after == ""
-
params[k] = v
-
elsif after == "[]"
-
params[k] ||= []
-
raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
-
params[k] << v
-
elsif after =~ %r(^\[\]\[([^\[\]]+)\]$) || after =~ %r(^\[\](.+)$)
-
child_key = $1
-
params[k] ||= []
-
raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
-
if params_hash_type?(params[k].last) && !params[k].last.key?(child_key)
-
normalize_params(params[k].last, child_key, v)
-
else
-
params[k] << normalize_params(params.class.new, child_key, v)
-
end
-
else
-
params[k] ||= params.class.new
-
raise TypeError, "expected Hash (got #{params[k].class.name}) for param `#{k}'" unless params_hash_type?(params[k])
-
params[k] = normalize_params(params[k], after, v)
-
end
-
-
return params
-
end
-
1
module_function :normalize_params
-
-
1
def params_hash_type?(obj)
-
obj.kind_of?(KeySpaceConstrainedParams) || obj.kind_of?(Hash)
-
end
-
1
module_function :params_hash_type?
-
-
1
def build_query(params)
-
params.map { |k, v|
-
if v.class == Array
-
build_query(v.map { |x| [k, x] })
-
else
-
v.nil? ? escape(k) : "#{escape(k)}=#{escape(v)}"
-
end
-
}.join("&")
-
end
-
1
module_function :build_query
-
-
1
def build_nested_query(value, prefix = nil)
-
case value
-
when Array
-
value.map { |v|
-
build_nested_query(v, "#{prefix}[]")
-
}.join("&")
-
when Hash
-
value.map { |k, v|
-
build_nested_query(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k))
-
}.join("&")
-
when String
-
raise ArgumentError, "value must be a Hash" if prefix.nil?
-
"#{prefix}=#{escape(value)}"
-
else
-
prefix
-
end
-
end
-
1
module_function :build_nested_query
-
-
1
def q_values(q_value_header)
-
q_value_header.to_s.split(/\s*,\s*/).map do |part|
-
value, parameters = part.split(/\s*;\s*/, 2)
-
quality = 1.0
-
if md = /\Aq=([\d.]+)/.match(parameters)
-
quality = md[1].to_f
-
end
-
[value, quality]
-
end
-
end
-
1
module_function :q_values
-
-
1
def best_q_match(q_value_header, available_mimes)
-
values = q_values(q_value_header)
-
-
values.map do |req_mime, quality|
-
match = available_mimes.first { |am| Rack::Mime.match?(am, req_mime) }
-
next unless match
-
[match, quality]
-
end.compact.sort_by do |match, quality|
-
(match.split('/', 2).count('*') * -10) + quality
-
end.last.first
-
end
-
1
module_function :best_q_match
-
-
1
ESCAPE_HTML = {
-
"&" => "&",
-
"<" => "<",
-
">" => ">",
-
"'" => "'",
-
'"' => """,
-
"/" => "/"
-
}
-
1
if //.respond_to?(:encoding)
-
1
ESCAPE_HTML_PATTERN = Regexp.union(*ESCAPE_HTML.keys)
-
else
-
# On 1.8, there is a kcode = 'u' bug that allows for XSS otherwhise
-
# TODO doesn't apply to jruby, so a better condition above might be preferable?
-
ESCAPE_HTML_PATTERN = /#{Regexp.union(*ESCAPE_HTML.keys)}/n
-
end
-
-
# Escape ampersands, brackets and quotes to their HTML/XML entities.
-
1
def escape_html(string)
-
string.to_s.gsub(ESCAPE_HTML_PATTERN){|c| ESCAPE_HTML[c] }
-
end
-
1
module_function :escape_html
-
-
1
def select_best_encoding(available_encodings, accept_encoding)
-
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
-
-
expanded_accept_encoding =
-
accept_encoding.map { |m, q|
-
if m == "*"
-
(available_encodings - accept_encoding.map { |m2, _| m2 }).map { |m2| [m2, q] }
-
else
-
[[m, q]]
-
end
-
}.inject([]) { |mem, list|
-
mem + list
-
}
-
-
encoding_candidates = expanded_accept_encoding.sort_by { |_, q| -q }.map { |m, _| m }
-
-
unless encoding_candidates.include?("identity")
-
encoding_candidates.push("identity")
-
end
-
-
expanded_accept_encoding.find_all { |m, q|
-
q == 0.0
-
}.each { |m, _|
-
encoding_candidates.delete(m)
-
}
-
-
return (encoding_candidates & available_encodings)[0]
-
end
-
1
module_function :select_best_encoding
-
-
1
def set_cookie_header!(header, key, value)
-
case value
-
when Hash
-
domain = "; domain=" + value[:domain] if value[:domain]
-
path = "; path=" + value[:path] if value[:path]
-
max_age = "; max-age=" + value[:max_age] if value[:max_age]
-
# There is an RFC mess in the area of date formatting for Cookies. Not
-
# only are there contradicting RFCs and examples within RFC text, but
-
# there are also numerous conflicting names of fields and partially
-
# cross-applicable specifications.
-
#
-
# These are best described in RFC 2616 3.3.1. This RFC text also
-
# specifies that RFC 822 as updated by RFC 1123 is preferred. That is a
-
# fixed length format with space-date delimeted fields.
-
#
-
# See also RFC 1123 section 5.2.14.
-
#
-
# RFC 6265 also specifies "sane-cookie-date" as RFC 1123 date, defined
-
# in RFC 2616 3.3.1. RFC 6265 also gives examples that clearly denote
-
# the space delimited format. These formats are compliant with RFC 2822.
-
#
-
# For reference, all involved RFCs are:
-
# RFC 822
-
# RFC 1123
-
# RFC 2109
-
# RFC 2616
-
# RFC 2822
-
# RFC 2965
-
# RFC 6265
-
expires = "; expires=" +
-
rfc2822(value[:expires].clone.gmtime) if value[:expires]
-
secure = "; secure" if value[:secure]
-
httponly = "; HttpOnly" if value[:httponly]
-
value = value[:value]
-
end
-
value = [value] unless Array === value
-
cookie = escape(key) + "=" +
-
value.map { |v| escape v }.join("&") +
-
"#{domain}#{path}#{max_age}#{expires}#{secure}#{httponly}"
-
-
case header["Set-Cookie"]
-
when nil, ''
-
header["Set-Cookie"] = cookie
-
when String
-
header["Set-Cookie"] = [header["Set-Cookie"], cookie].join("\n")
-
when Array
-
header["Set-Cookie"] = (header["Set-Cookie"] + [cookie]).join("\n")
-
end
-
-
nil
-
end
-
1
module_function :set_cookie_header!
-
-
1
def delete_cookie_header!(header, key, value = {})
-
case header["Set-Cookie"]
-
when nil, ''
-
cookies = []
-
when String
-
cookies = header["Set-Cookie"].split("\n")
-
when Array
-
cookies = header["Set-Cookie"]
-
end
-
-
cookies.reject! { |cookie|
-
if value[:domain]
-
cookie =~ /\A#{escape(key)}=.*domain=#{value[:domain]}/
-
elsif value[:path]
-
cookie =~ /\A#{escape(key)}=.*path=#{value[:path]}/
-
else
-
cookie =~ /\A#{escape(key)}=/
-
end
-
}
-
-
header["Set-Cookie"] = cookies.join("\n")
-
-
set_cookie_header!(header, key,
-
{:value => '', :path => nil, :domain => nil,
-
:max_age => '0',
-
:expires => Time.at(0) }.merge(value))
-
-
nil
-
end
-
1
module_function :delete_cookie_header!
-
-
# Return the bytesize of String; uses String#size under Ruby 1.8 and
-
# String#bytesize under 1.9.
-
1
if ''.respond_to?(:bytesize)
-
1
def bytesize(string)
-
string.bytesize
-
end
-
else
-
def bytesize(string)
-
string.size
-
end
-
end
-
1
module_function :bytesize
-
-
1
def rfc2822(time)
-
time.rfc2822
-
end
-
1
module_function :rfc2822
-
-
# Modified version of stdlib time.rb Time#rfc2822 to use '%d-%b-%Y' instead
-
# of '% %b %Y'.
-
# It assumes that the time is in GMT to comply to the RFC 2109.
-
#
-
# NOTE: I'm not sure the RFC says it requires GMT, but is ambigous enough
-
# that I'm certain someone implemented only that option.
-
# Do not use %a and %b from Time.strptime, it would use localized names for
-
# weekday and month.
-
#
-
1
def rfc2109(time)
-
wday = Time::RFC2822_DAY_NAME[time.wday]
-
mon = Time::RFC2822_MONTH_NAME[time.mon - 1]
-
time.strftime("#{wday}, %d-#{mon}-%Y %H:%M:%S GMT")
-
end
-
1
module_function :rfc2109
-
-
# Parses the "Range:" header, if present, into an array of Range objects.
-
# Returns nil if the header is missing or syntactically invalid.
-
# Returns an empty array if none of the ranges are satisfiable.
-
1
def byte_ranges(env, size)
-
# See <http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35>
-
http_range = env['HTTP_RANGE']
-
return nil unless http_range && http_range =~ /bytes=([^;]+)/
-
ranges = []
-
$1.split(/,\s*/).each do |range_spec|
-
return nil unless range_spec =~ /(\d*)-(\d*)/
-
r0,r1 = $1, $2
-
if r0.empty?
-
return nil if r1.empty?
-
# suffix-byte-range-spec, represents trailing suffix of file
-
r0 = size - r1.to_i
-
r0 = 0 if r0 < 0
-
r1 = size - 1
-
else
-
r0 = r0.to_i
-
if r1.empty?
-
r1 = size - 1
-
else
-
r1 = r1.to_i
-
return nil if r1 < r0 # backwards range is syntactically invalid
-
r1 = size-1 if r1 >= size
-
end
-
end
-
ranges << (r0..r1) if r0 <= r1
-
end
-
ranges
-
end
-
1
module_function :byte_ranges
-
-
# Constant time string comparison.
-
1
def secure_compare(a, b)
-
return false unless bytesize(a) == bytesize(b)
-
-
l = a.unpack("C*")
-
-
r, i = 0, -1
-
b.each_byte { |v| r |= v ^ l[i+=1] }
-
r == 0
-
end
-
1
module_function :secure_compare
-
-
# Context allows the use of a compatible middleware at different points
-
# in a request handling stack. A compatible middleware must define
-
# #context which should take the arguments env and app. The first of which
-
# would be the request environment. The second of which would be the rack
-
# application that the request would be forwarded to.
-
1
class Context
-
1
attr_reader :for, :app
-
-
1
def initialize(app_f, app_r)
-
raise 'running context does not respond to #context' unless app_f.respond_to? :context
-
@for, @app = app_f, app_r
-
end
-
-
1
def call(env)
-
@for.context(env, @app)
-
end
-
-
1
def recontext(app)
-
self.class.new(@for, app)
-
end
-
-
1
def context(env, app=@app)
-
recontext(app).call(env)
-
end
-
end
-
-
# A case-insensitive Hash that preserves the original case of a
-
# header when set.
-
1
class HeaderHash < Hash
-
1
def self.new(hash={})
-
HeaderHash === hash ? hash : super(hash)
-
end
-
-
1
def initialize(hash={})
-
super()
-
@names = {}
-
hash.each { |k, v| self[k] = v }
-
end
-
-
1
def each
-
super do |k, v|
-
yield(k, v.respond_to?(:to_ary) ? v.to_ary.join("\n") : v)
-
end
-
end
-
-
1
def to_hash
-
hash = {}
-
each { |k,v| hash[k] = v }
-
hash
-
end
-
-
1
def [](k)
-
super(k) || super(@names[k.downcase])
-
end
-
-
1
def []=(k, v)
-
canonical = k.downcase
-
delete k if @names[canonical] && @names[canonical] != k # .delete is expensive, don't invoke it unless necessary
-
@names[k] = @names[canonical] = k
-
super k, v
-
end
-
-
1
def delete(k)
-
canonical = k.downcase
-
result = super @names.delete(canonical)
-
@names.delete_if { |name,| name.downcase == canonical }
-
result
-
end
-
-
1
def include?(k)
-
@names.include?(k) || @names.include?(k.downcase)
-
end
-
-
1
alias_method :has_key?, :include?
-
1
alias_method :member?, :include?
-
1
alias_method :key?, :include?
-
-
1
def merge!(other)
-
other.each { |k, v| self[k] = v }
-
self
-
end
-
-
1
def merge(other)
-
hash = dup
-
hash.merge! other
-
end
-
-
1
def replace(other)
-
clear
-
other.each { |k, v| self[k] = v }
-
self
-
end
-
end
-
-
1
class KeySpaceConstrainedParams
-
1
def initialize(limit = Utils.key_space_limit)
-
@limit = limit
-
@size = 0
-
@params = {}
-
end
-
-
1
def [](key)
-
@params[key]
-
end
-
-
1
def []=(key, value)
-
@size += key.size if key && !@params.key?(key)
-
raise RangeError, 'exceeded available parameter key space' if @size > @limit
-
@params[key] = value
-
end
-
-
1
def key?(key)
-
@params.key?(key)
-
end
-
-
1
def to_params_hash
-
hash = @params
-
hash.keys.each do |key|
-
value = hash[key]
-
if value.kind_of?(self.class)
-
hash[key] = value.to_params_hash
-
elsif value.kind_of?(Array)
-
value.map! {|x| x.kind_of?(self.class) ? x.to_params_hash : x}
-
end
-
end
-
hash
-
end
-
end
-
-
# Every standard HTTP code mapped to the appropriate message.
-
# Generated with:
-
# irb -ropen-uri -rnokogiri
-
# > Nokogiri::XML(open("http://www.iana.org/assignments/http-status-codes/http-status-codes.xml")).css("record").each{|r|
-
# puts "#{r.css('value').text} => '#{r.css('description').text}'"}
-
1
HTTP_STATUS_CODES = {
-
100 => 'Continue',
-
101 => 'Switching Protocols',
-
102 => 'Processing',
-
200 => 'OK',
-
201 => 'Created',
-
202 => 'Accepted',
-
203 => 'Non-Authoritative Information',
-
204 => 'No Content',
-
205 => 'Reset Content',
-
206 => 'Partial Content',
-
207 => 'Multi-Status',
-
208 => 'Already Reported',
-
226 => 'IM Used',
-
300 => 'Multiple Choices',
-
301 => 'Moved Permanently',
-
302 => 'Found',
-
303 => 'See Other',
-
304 => 'Not Modified',
-
305 => 'Use Proxy',
-
306 => 'Reserved',
-
307 => 'Temporary Redirect',
-
308 => 'Permanent Redirect',
-
400 => 'Bad Request',
-
401 => 'Unauthorized',
-
402 => 'Payment Required',
-
403 => 'Forbidden',
-
404 => 'Not Found',
-
405 => 'Method Not Allowed',
-
406 => 'Not Acceptable',
-
407 => 'Proxy Authentication Required',
-
408 => 'Request Timeout',
-
409 => 'Conflict',
-
410 => 'Gone',
-
411 => 'Length Required',
-
412 => 'Precondition Failed',
-
413 => 'Request Entity Too Large',
-
414 => 'Request-URI Too Long',
-
415 => 'Unsupported Media Type',
-
416 => 'Requested Range Not Satisfiable',
-
417 => 'Expectation Failed',
-
422 => 'Unprocessable Entity',
-
423 => 'Locked',
-
424 => 'Failed Dependency',
-
425 => 'Reserved for WebDAV advanced collections expired proposal',
-
426 => 'Upgrade Required',
-
427 => 'Unassigned',
-
428 => 'Precondition Required',
-
429 => 'Too Many Requests',
-
430 => 'Unassigned',
-
431 => 'Request Header Fields Too Large',
-
500 => 'Internal Server Error',
-
501 => 'Not Implemented',
-
502 => 'Bad Gateway',
-
503 => 'Service Unavailable',
-
504 => 'Gateway Timeout',
-
505 => 'HTTP Version Not Supported',
-
506 => 'Variant Also Negotiates (Experimental)',
-
507 => 'Insufficient Storage',
-
508 => 'Loop Detected',
-
509 => 'Unassigned',
-
510 => 'Not Extended',
-
511 => 'Network Authentication Required'
-
}
-
-
# Responses with HTTP status codes that should not have an entity body
-
1
STATUS_WITH_NO_ENTITY_BODY = Set.new((100..199).to_a << 204 << 205 << 304)
-
-
1
SYMBOL_TO_STATUS_CODE = Hash[*HTTP_STATUS_CODES.map { |code, message|
-
62
[message.downcase.gsub(/\s|-/, '_').to_sym, code]
-
}.flatten]
-
-
1
def status_code(status)
-
10
if status.is_a?(Symbol)
-
SYMBOL_TO_STATUS_CODE[status] || 500
-
else
-
10
status.to_i
-
end
-
end
-
1
module_function :status_code
-
-
1
Multipart = Rack::Multipart
-
-
end
-
end
-
1
module Rack
-
-
1
class MockSession # :nodoc:
-
1
attr_writer :cookie_jar
-
1
attr_reader :default_host
-
-
1
def initialize(app, default_host = Rack::Test::DEFAULT_HOST)
-
@app = app
-
@after_request = []
-
@default_host = default_host
-
@last_request = nil
-
@last_response = nil
-
end
-
-
1
def after_request(&block)
-
@after_request << block
-
end
-
-
1
def clear_cookies
-
@cookie_jar = Rack::Test::CookieJar.new([], @default_host)
-
end
-
-
1
def set_cookie(cookie, uri = nil)
-
cookie_jar.merge(cookie, uri)
-
end
-
-
1
def request(uri, env)
-
env["HTTP_COOKIE"] ||= cookie_jar.for(uri)
-
@last_request = Rack::Request.new(env)
-
status, headers, body = @app.call(@last_request.env)
-
-
@last_response = MockResponse.new(status, headers, body, env["rack.errors"].flush)
-
body.close if body.respond_to?(:close)
-
-
cookie_jar.merge(last_response.headers["Set-Cookie"], uri)
-
-
@after_request.each { |hook| hook.call }
-
-
if @last_response.respond_to?(:finish)
-
@last_response.finish
-
else
-
@last_response
-
end
-
end
-
-
# Return the last request issued in the session. Raises an error if no
-
# requests have been sent yet.
-
1
def last_request
-
raise Rack::Test::Error.new("No request yet. Request a page first.") unless @last_request
-
@last_request
-
end
-
-
# Return the last response received in the session. Raises an error if
-
# no requests have been sent yet.
-
1
def last_response
-
raise Rack::Test::Error.new("No response yet. Request a page first.") unless @last_response
-
@last_response
-
end
-
-
1
def cookie_jar
-
@cookie_jar ||= Rack::Test::CookieJar.new([], @default_host)
-
end
-
-
end
-
-
end
-
1
require "uri"
-
1
require "rack"
-
1
require "rack/mock_session"
-
1
require "rack/test/cookie_jar"
-
1
require "rack/test/mock_digest_request"
-
1
require "rack/test/utils"
-
1
require "rack/test/methods"
-
1
require "rack/test/uploaded_file"
-
-
1
module Rack
-
1
module Test
-
1
VERSION = "0.6.2"
-
-
1
DEFAULT_HOST = "example.org"
-
1
MULTIPART_BOUNDARY = "----------XnJLe9ZIbbGUYtzPQJ16u1"
-
-
# The common base class for exceptions raised by Rack::Test
-
1
class Error < StandardError; end
-
-
# This class represents a series of requests issued to a Rack app, sharing
-
# a single cookie jar
-
#
-
# Rack::Test::Session's methods are most often called through Rack::Test::Methods,
-
# which will automatically build a session when it's first used.
-
1
class Session
-
1
extend Forwardable
-
1
include Rack::Test::Utils
-
-
1
def_delegators :@rack_mock_session, :clear_cookies, :set_cookie, :last_response, :last_request
-
-
# Creates a Rack::Test::Session for a given Rack app or Rack::MockSession.
-
#
-
# Note: Generally, you won't need to initialize a Rack::Test::Session directly.
-
# Instead, you should include Rack::Test::Methods into your testing context.
-
# (See README.rdoc for an example)
-
1
def initialize(mock_session)
-
@headers = {}
-
-
if mock_session.is_a?(MockSession)
-
@rack_mock_session = mock_session
-
else
-
@rack_mock_session = MockSession.new(mock_session)
-
end
-
-
@default_host = @rack_mock_session.default_host
-
end
-
-
# Issue a GET request for the given URI with the given params and Rack
-
# environment. Stores the issues request object in #last_request and
-
# the app's response in #last_response. Yield #last_response to a block
-
# if given.
-
#
-
# Example:
-
# get "/"
-
1
def get(uri, params = {}, env = {}, &block)
-
env = env_for(uri, env.merge(:method => "GET", :params => params))
-
process_request(uri, env, &block)
-
end
-
-
# Issue a POST request for the given URI. See #get
-
#
-
# Example:
-
# post "/signup", "name" => "Bryan"
-
1
def post(uri, params = {}, env = {}, &block)
-
env = env_for(uri, env.merge(:method => "POST", :params => params))
-
process_request(uri, env, &block)
-
end
-
-
# Issue a PUT request for the given URI. See #get
-
#
-
# Example:
-
# put "/"
-
1
def put(uri, params = {}, env = {}, &block)
-
env = env_for(uri, env.merge(:method => "PUT", :params => params))
-
process_request(uri, env, &block)
-
end
-
-
# Issue a PATCH request for the given URI. See #get
-
#
-
# Example:
-
# patch "/"
-
1
def patch(uri, params = {}, env = {}, &block)
-
env = env_for(uri, env.merge(:method => "PATCH", :params => params))
-
process_request(uri, env, &block)
-
end
-
-
# Issue a DELETE request for the given URI. See #get
-
#
-
# Example:
-
# delete "/"
-
1
def delete(uri, params = {}, env = {}, &block)
-
env = env_for(uri, env.merge(:method => "DELETE", :params => params))
-
process_request(uri, env, &block)
-
end
-
-
# Issue an OPTIONS request for the given URI. See #get
-
#
-
# Example:
-
# options "/"
-
1
def options(uri, params = {}, env = {}, &block)
-
env = env_for(uri, env.merge(:method => "OPTIONS", :params => params))
-
process_request(uri, env, &block)
-
end
-
-
# Issue a HEAD request for the given URI. See #get
-
#
-
# Example:
-
# head "/"
-
1
def head(uri, params = {}, env = {}, &block)
-
env = env_for(uri, env.merge(:method => "HEAD", :params => params))
-
process_request(uri, env, &block)
-
end
-
-
# Issue a request to the Rack app for the given URI and optional Rack
-
# environment. Stores the issues request object in #last_request and
-
# the app's response in #last_response. Yield #last_response to a block
-
# if given.
-
#
-
# Example:
-
# request "/"
-
1
def request(uri, env = {}, &block)
-
env = env_for(uri, env)
-
process_request(uri, env, &block)
-
end
-
-
# Set a header to be included on all subsequent requests through the
-
# session. Use a value of nil to remove a previously configured header.
-
#
-
# In accordance with the Rack spec, headers will be included in the Rack
-
# environment hash in HTTP_USER_AGENT form.
-
#
-
# Example:
-
# header "User-Agent", "Firefox"
-
1
def header(name, value)
-
if value.nil?
-
@headers.delete(name)
-
else
-
@headers[name] = value
-
end
-
end
-
-
# Set the username and password for HTTP Basic authorization, to be
-
# included in subsequent requests in the HTTP_AUTHORIZATION header.
-
#
-
# Example:
-
# basic_authorize "bryan", "secret"
-
1
def basic_authorize(username, password)
-
encoded_login = ["#{username}:#{password}"].pack("m*")
-
header('Authorization', "Basic #{encoded_login}")
-
end
-
-
1
alias_method :authorize, :basic_authorize
-
-
# Set the username and password for HTTP Digest authorization, to be
-
# included in subsequent requests in the HTTP_AUTHORIZATION header.
-
#
-
# Example:
-
# digest_authorize "bryan", "secret"
-
1
def digest_authorize(username, password)
-
@digest_username = username
-
@digest_password = password
-
end
-
-
# Rack::Test will not follow any redirects automatically. This method
-
# will follow the redirect returned (including setting the Referer header
-
# on the new request) in the last response. If the last response was not
-
# a redirect, an error will be raised.
-
1
def follow_redirect!
-
unless last_response.redirect?
-
raise Error.new("Last response was not a redirect. Cannot follow_redirect!")
-
end
-
-
get(last_response["Location"], {}, { "HTTP_REFERER" => last_request.url })
-
end
-
-
1
private
-
-
1
def env_for(path, env)
-
uri = URI.parse(path)
-
uri.path = "/#{uri.path}" unless uri.path[0] == ?/
-
uri.host ||= @default_host
-
-
env = default_env.merge(env)
-
-
env["HTTP_HOST"] ||= [uri.host, (uri.port if uri.port != uri.default_port)].compact.join(":")
-
-
env.update("HTTPS" => "on") if URI::HTTPS === uri
-
env["HTTP_X_REQUESTED_WITH"] = "XMLHttpRequest" if env[:xhr]
-
-
# TODO: Remove this after Rack 1.1 has been released.
-
# Stringifying and upcasing methods has be commit upstream
-
env["REQUEST_METHOD"] ||= env[:method] ? env[:method].to_s.upcase : "GET"
-
-
if env["REQUEST_METHOD"] == "GET"
-
# merge :params with the query string
-
if params = env[:params]
-
params = parse_nested_query(params) if params.is_a?(String)
-
params.update(parse_nested_query(uri.query))
-
uri.query = build_nested_query(params)
-
end
-
elsif !env.has_key?(:input)
-
env["CONTENT_TYPE"] ||= "application/x-www-form-urlencoded"
-
-
if env[:params].is_a?(Hash)
-
if data = build_multipart(env[:params])
-
env[:input] = data
-
env["CONTENT_LENGTH"] ||= data.length.to_s
-
env["CONTENT_TYPE"] = "multipart/form-data; boundary=#{MULTIPART_BOUNDARY}"
-
else
-
env[:input] = params_to_string(env[:params])
-
end
-
else
-
env[:input] = env[:params]
-
end
-
end
-
-
env.delete(:params)
-
-
if env.has_key?(:cookie)
-
set_cookie(env.delete(:cookie), uri)
-
end
-
-
Rack::MockRequest.env_for(uri.to_s, env)
-
end
-
-
1
def process_request(uri, env)
-
uri = URI.parse(uri)
-
uri.host ||= @default_host
-
-
@rack_mock_session.request(uri, env)
-
-
if retry_with_digest_auth?(env)
-
auth_env = env.merge({
-
"HTTP_AUTHORIZATION" => digest_auth_header,
-
"rack-test.digest_auth_retry" => true
-
})
-
auth_env.delete('rack.request')
-
process_request(uri.path, auth_env)
-
else
-
yield last_response if block_given?
-
-
last_response
-
end
-
end
-
-
1
def digest_auth_header
-
challenge = last_response["WWW-Authenticate"].split(" ", 2).last
-
params = Rack::Auth::Digest::Params.parse(challenge)
-
-
params.merge!({
-
"username" => @digest_username,
-
"nc" => "00000001",
-
"cnonce" => "nonsensenonce",
-
"uri" => last_request.fullpath,
-
"method" => last_request.env["REQUEST_METHOD"],
-
})
-
-
params["response"] = MockDigestRequest.new(params).response(@digest_password)
-
-
"Digest #{params}"
-
end
-
-
1
def retry_with_digest_auth?(env)
-
last_response.status == 401 &&
-
digest_auth_configured? &&
-
!env["rack-test.digest_auth_retry"]
-
end
-
-
1
def digest_auth_configured?
-
@digest_username
-
end
-
-
1
def default_env
-
{ "rack.test" => true, "REMOTE_ADDR" => "127.0.0.1" }.merge(headers_for_env)
-
end
-
-
1
def headers_for_env
-
converted_headers = {}
-
-
@headers.each do |name, value|
-
env_key = name.upcase.gsub("-", "_")
-
env_key = "HTTP_" + env_key unless "CONTENT_TYPE" == env_key
-
converted_headers[env_key] = value
-
end
-
-
converted_headers
-
end
-
-
1
def params_to_string(params)
-
case params
-
when Hash then build_nested_query(params)
-
when nil then ""
-
else params
-
end
-
end
-
-
end
-
-
1
def self.encoding_aware_strings?
-
defined?(Encoding) && "".respond_to?(:encode)
-
end
-
-
end
-
end
-
1
require "uri"
-
1
require "time"
-
-
1
module Rack
-
1
module Test
-
-
1
class Cookie # :nodoc:
-
1
include Rack::Utils
-
-
# :api: private
-
1
attr_reader :name, :value
-
-
# :api: private
-
1
def initialize(raw, uri = nil, default_host = DEFAULT_HOST)
-
@default_host = default_host
-
uri ||= default_uri
-
-
# separate the name / value pair from the cookie options
-
@name_value_raw, options = raw.split(/[;,] */n, 2)
-
-
@name, @value = parse_query(@name_value_raw, ';').to_a.first
-
@options = parse_query(options, ';')
-
-
@options["domain"] ||= (uri.host || default_host)
-
@options["path"] ||= uri.path.sub(/\/[^\/]*\Z/, "")
-
end
-
-
1
def replaces?(other)
-
[name.downcase, domain, path] == [other.name.downcase, other.domain, other.path]
-
end
-
-
# :api: private
-
1
def raw
-
@name_value_raw
-
end
-
-
# :api: private
-
1
def empty?
-
@value.nil? || @value.empty?
-
end
-
-
# :api: private
-
1
def domain
-
@options["domain"]
-
end
-
-
1
def secure?
-
@options.has_key?("secure")
-
end
-
-
# :api: private
-
1
def path
-
@options["path"].strip || "/"
-
end
-
-
# :api: private
-
1
def expires
-
Time.parse(@options["expires"]) if @options["expires"]
-
end
-
-
# :api: private
-
1
def expired?
-
expires && expires < Time.now
-
end
-
-
# :api: private
-
1
def valid?(uri)
-
uri ||= default_uri
-
-
if uri.host.nil?
-
uri.host = @default_host
-
end
-
-
real_domain = domain =~ /^\./ ? domain[1..-1] : domain
-
(!secure? || (secure? && uri.scheme == "https")) &&
-
uri.host =~ Regexp.new("#{Regexp.escape(real_domain)}$", Regexp::IGNORECASE) &&
-
uri.path =~ Regexp.new("^#{Regexp.escape(path)}")
-
end
-
-
# :api: private
-
1
def matches?(uri)
-
! expired? && valid?(uri)
-
end
-
-
# :api: private
-
1
def <=>(other)
-
# Orders the cookies from least specific to most
-
[name, path, domain.reverse] <=> [other.name, other.path, other.domain.reverse]
-
end
-
-
1
protected
-
-
1
def default_uri
-
URI.parse("//" + @default_host + "/")
-
end
-
-
end
-
-
1
class CookieJar # :nodoc:
-
-
# :api: private
-
1
def initialize(cookies = [], default_host = DEFAULT_HOST)
-
@default_host = default_host
-
@cookies = cookies
-
@cookies.sort!
-
end
-
-
1
def [](name)
-
cookies = hash_for(nil)
-
# TODO: Should be case insensitive
-
cookies[name] && cookies[name].value
-
end
-
-
1
def []=(name, value)
-
merge("#{name}=#{Rack::Utils.escape(value)}")
-
end
-
-
1
def delete(name)
-
@cookies.reject! do |cookie|
-
cookie.name == name
-
end
-
end
-
-
1
def merge(raw_cookies, uri = nil)
-
return unless raw_cookies
-
-
if raw_cookies.is_a? String
-
raw_cookies = raw_cookies.split("\n")
-
raw_cookies.reject!{|c| c.empty? }
-
end
-
-
raw_cookies.each do |raw_cookie|
-
cookie = Cookie.new(raw_cookie, uri, @default_host)
-
self << cookie if cookie.valid?(uri)
-
end
-
end
-
-
1
def <<(new_cookie)
-
@cookies.reject! do |existing_cookie|
-
new_cookie.replaces?(existing_cookie)
-
end
-
-
@cookies << new_cookie
-
@cookies.sort!
-
end
-
-
# :api: private
-
1
def for(uri)
-
hash_for(uri).values.map { |c| c.raw }.join(';')
-
end
-
-
1
def to_hash
-
cookies = {}
-
-
hash_for(nil).each do |name, cookie|
-
cookies[name] = cookie.value
-
end
-
-
return cookies
-
end
-
-
1
protected
-
-
1
def hash_for(uri = nil)
-
cookies = {}
-
-
# The cookies are sorted by most specific first. So, we loop through
-
# all the cookies in order and add it to a hash by cookie name if
-
# the cookie can be sent to the current URI. It's added to the hash
-
# so that when we are done, the cookies will be unique by name and
-
# we'll have grabbed the most specific to the URI.
-
@cookies.each do |cookie|
-
cookies[cookie.name] = cookie if !uri || cookie.matches?(uri)
-
end
-
-
return cookies
-
end
-
-
end
-
-
end
-
end
-
1
require "forwardable"
-
-
1
module Rack
-
1
module Test
-
-
# This module serves as the primary integration point for using Rack::Test
-
# in a testing environment. It depends on an app method being defined in the
-
# same context, and provides the Rack::Test API methods (see Rack::Test::Session
-
# for their documentation).
-
#
-
# Example:
-
#
-
# class HomepageTest < Test::Unit::TestCase
-
# include Rack::Test::Methods
-
#
-
# def app
-
# MyApp.new
-
# end
-
# end
-
1
module Methods
-
1
extend Forwardable
-
-
1
def rack_mock_session(name = :default) # :nodoc:
-
return build_rack_mock_session unless name
-
-
@_rack_mock_sessions ||= {}
-
@_rack_mock_sessions[name] ||= build_rack_mock_session
-
end
-
-
1
def build_rack_mock_session # :nodoc:
-
Rack::MockSession.new(app)
-
end
-
-
1
def rack_test_session(name = :default) # :nodoc:
-
return build_rack_test_session(name) unless name
-
-
@_rack_test_sessions ||= {}
-
@_rack_test_sessions[name] ||= build_rack_test_session(name)
-
end
-
-
1
def build_rack_test_session(name) # :nodoc:
-
Rack::Test::Session.new(rack_mock_session(name))
-
end
-
-
1
def current_session # :nodoc:
-
rack_test_session(_current_session_names.last)
-
end
-
-
1
def with_session(name) # :nodoc:
-
_current_session_names.push(name)
-
yield rack_test_session(name)
-
_current_session_names.pop
-
end
-
-
1
def _current_session_names # :nodoc:
-
@_current_session_names ||= [:default]
-
end
-
-
1
METHODS = [
-
:request,
-
:get,
-
:post,
-
:put,
-
:patch,
-
:delete,
-
:options,
-
:head,
-
:follow_redirect!,
-
:header,
-
:set_cookie,
-
:clear_cookies,
-
:authorize,
-
:basic_authorize,
-
:digest_authorize,
-
:last_response,
-
:last_request
-
]
-
-
1
def_delegators :current_session, *METHODS
-
end
-
end
-
end
-
1
module Rack
-
1
module Test
-
-
1
class MockDigestRequest # :nodoc:
-
-
1
def initialize(params)
-
@params = params
-
end
-
-
1
def method_missing(sym)
-
if @params.has_key? k = sym.to_s
-
return @params[k]
-
end
-
-
super
-
end
-
-
1
def method
-
@params['method']
-
end
-
-
1
def response(password)
-
Rack::Auth::Digest::MD5.new(nil).send :digest, self, password
-
end
-
-
end
-
-
end
-
end
-
1
require "tempfile"
-
1
require "fileutils"
-
-
1
module Rack
-
1
module Test
-
-
# Wraps a Tempfile with a content type. Including one or more UploadedFile's
-
# in the params causes Rack::Test to build and issue a multipart request.
-
#
-
# Example:
-
# post "/photos", "file" => Rack::Test::UploadedFile.new("me.jpg", "image/jpeg")
-
1
class UploadedFile
-
-
# The filename, *not* including the path, of the "uploaded" file
-
1
attr_reader :original_filename
-
-
# The content type of the "uploaded" file
-
1
attr_accessor :content_type
-
-
1
def initialize(path, content_type = "text/plain", binary = false)
-
raise "#{path} file does not exist" unless ::File.exist?(path)
-
-
@content_type = content_type
-
@original_filename = ::File.basename(path)
-
-
@tempfile = Tempfile.new(@original_filename)
-
@tempfile.set_encoding(Encoding::BINARY) if @tempfile.respond_to?(:set_encoding)
-
@tempfile.binmode if binary
-
-
FileUtils.copy_file(path, @tempfile.path)
-
end
-
-
1
def path
-
@tempfile.path
-
end
-
-
1
alias_method :local_path, :path
-
-
1
def method_missing(method_name, *args, &block) #:nodoc:
-
@tempfile.__send__(method_name, *args, &block)
-
end
-
-
1
def respond_to?(method_name, include_private = false) #:nodoc:
-
@tempfile.respond_to?(method_name, include_private) || super
-
end
-
-
end
-
-
end
-
end
-
1
module Rack
-
1
module Test
-
-
1
module Utils # :nodoc:
-
1
include Rack::Utils
-
-
1
def build_nested_query(value, prefix = nil)
-
case value
-
when Array
-
value.map do |v|
-
unless unescape(prefix) =~ /\[\]$/
-
prefix = "#{prefix}[]"
-
end
-
build_nested_query(v, "#{prefix}")
-
end.join("&")
-
when Hash
-
value.map do |k, v|
-
build_nested_query(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k))
-
end.join("&")
-
when NilClass
-
prefix.to_s
-
else
-
"#{prefix}=#{escape(value)}"
-
end
-
end
-
-
1
module_function :build_nested_query
-
-
1
def build_multipart(params, first = true)
-
if first
-
unless params.is_a?(Hash)
-
raise ArgumentError, "value must be a Hash"
-
end
-
-
multipart = false
-
query = lambda { |value|
-
case value
-
when Array
-
value.each(&query)
-
when Hash
-
value.values.each(&query)
-
when UploadedFile
-
multipart = true
-
end
-
}
-
params.values.each(&query)
-
return nil unless multipart
-
end
-
-
flattened_params = Hash.new
-
-
params.each do |key, value|
-
k = first ? key.to_s : "[#{key}]"
-
-
case value
-
when Array
-
value.map do |v|
-
-
if (v.is_a?(Hash))
-
build_multipart(v, false).each { |subkey, subvalue|
-
flattened_params["#{k}[]#{subkey}"] = subvalue
-
}
-
else
-
flattened_params["#{k}[]"] = value
-
end
-
-
end
-
when Hash
-
build_multipart(value, false).each { |subkey, subvalue|
-
flattened_params[k + subkey] = subvalue
-
}
-
else
-
flattened_params[k] = value
-
end
-
end
-
-
if first
-
build_parts(flattened_params)
-
else
-
flattened_params
-
end
-
end
-
-
1
module_function :build_multipart
-
-
1
private
-
1
def build_parts(parameters)
-
parameters.map { |name, value|
-
if value.respond_to?(:original_filename)
-
build_file_part(name, value)
-
-
elsif value.is_a?(Array) and value.all? { |v| v.respond_to?(:original_filename) }
-
value.map do |v|
-
build_file_part(name, v)
-
end.join
-
-
else
-
primitive_part = build_primitive_part(name, value)
-
Rack::Test.encoding_aware_strings? ? primitive_part.force_encoding('BINARY') : primitive_part
-
end
-
-
}.join + "--#{MULTIPART_BOUNDARY}--\r"
-
end
-
-
1
def build_primitive_part(parameter_name, value)
-
unless value.is_a? Array
-
value = [value]
-
end
-
value.map do |v|
-
<<-EOF
-
--#{MULTIPART_BOUNDARY}\r
-
Content-Disposition: form-data; name="#{parameter_name}"\r
-
\r
-
#{v}\r
-
EOF
-
end.join
-
end
-
-
1
def build_file_part(parameter_name, uploaded_file)
-
::File.open(uploaded_file.path, "rb") do |physical_file|
-
physical_file.set_encoding(Encoding::BINARY) if physical_file.respond_to?(:set_encoding)
-
<<-EOF
-
--#{MULTIPART_BOUNDARY}\r
-
Content-Disposition: form-data; name="#{parameter_name}"; filename="#{escape(uploaded_file.original_filename)}"\r
-
Content-Type: #{uploaded_file.content_type}\r
-
Content-Length: #{::File.stat(uploaded_file.path).size}\r
-
\r
-
#{physical_file.read}\r
-
EOF
-
end
-
end
-
-
end
-
-
end
-
end
-
1
require 'rails/ruby_version_check'
-
-
1
require 'pathname'
-
-
1
require 'active_support'
-
1
require 'active_support/dependencies/autoload'
-
1
require 'active_support/core_ext/kernel/reporting'
-
1
require 'active_support/core_ext/module/delegation'
-
1
require 'active_support/core_ext/array/extract_options'
-
-
1
require 'rails/application'
-
1
require 'rails/version'
-
-
1
require 'active_support/railtie'
-
1
require 'action_dispatch/railtie'
-
-
# For Ruby 1.9, UTF-8 is the default internal and external encoding.
-
1
silence_warnings do
-
1
Encoding.default_external = Encoding::UTF_8
-
1
Encoding.default_internal = Encoding::UTF_8
-
end
-
-
1
module Rails
-
1
extend ActiveSupport::Autoload
-
-
1
autoload :Info
-
1
autoload :InfoController
-
1
autoload :MailersController
-
1
autoload :WelcomeController
-
-
1
class << self
-
1
attr_accessor :application, :cache, :logger
-
-
1
delegate :initialize!, :initialized?, to: :application
-
-
# The Configuration instance used to configure the Rails environment
-
1
def configuration
-
application.config
-
end
-
-
1
def backtrace_cleaner
-
@backtrace_cleaner ||= begin
-
# Relies on Active Support, so we have to lazy load to postpone definition until AS has been loaded
-
1
require 'rails/backtrace_cleaner'
-
1
Rails::BacktraceCleaner.new
-
1
end
-
end
-
-
1
def root
-
14
application && application.config.root
-
end
-
-
1
def env
-
24
@_env ||= ActiveSupport::StringInquirer.new(ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development")
-
end
-
-
1
def env=(environment)
-
@_env = ActiveSupport::StringInquirer.new(environment)
-
end
-
-
# Returns all rails groups for loading based on:
-
#
-
# * The Rails environment;
-
# * The environment variable RAILS_GROUPS;
-
# * The optional envs given as argument and the hash with group dependencies;
-
#
-
# groups assets: [:development, :test]
-
#
-
# # Returns
-
# # => [:default, :development, :assets] for Rails.env == "development"
-
# # => [:default, :production] for Rails.env == "production"
-
1
def groups(*groups)
-
1
hash = groups.extract_options!
-
1
env = Rails.env
-
1
groups.unshift(:default, env)
-
1
groups.concat ENV["RAILS_GROUPS"].to_s.split(",")
-
1
groups.concat hash.map { |k, v| k if v.map(&:to_s).include?(env) }
-
1
groups.compact!
-
1
groups.uniq!
-
1
groups
-
end
-
-
1
def public_path
-
1
application && Pathname.new(application.paths["public"].first)
-
end
-
end
-
end
-
1
require 'fileutils'
-
1
require 'active_support/core_ext/hash/keys'
-
1
require 'active_support/core_ext/object/blank'
-
1
require 'active_support/key_generator'
-
1
require 'active_support/message_verifier'
-
1
require 'rails/engine'
-
-
1
module Rails
-
# In Rails 3.0, a Rails::Application object was introduced which is nothing more than
-
# an Engine but with the responsibility of coordinating the whole boot process.
-
#
-
# == Initialization
-
#
-
# Rails::Application is responsible for executing all railties and engines
-
# initializers. It also executes some bootstrap initializers (check
-
# Rails::Application::Bootstrap) and finishing initializers, after all the others
-
# are executed (check Rails::Application::Finisher).
-
#
-
# == Configuration
-
#
-
# Besides providing the same configuration as Rails::Engine and Rails::Railtie,
-
# the application object has several specific configurations, for example
-
# "cache_classes", "consider_all_requests_local", "filter_parameters",
-
# "logger" and so forth.
-
#
-
# Check Rails::Application::Configuration to see them all.
-
#
-
# == Routes
-
#
-
# The application object is also responsible for holding the routes and reloading routes
-
# whenever the files change in development.
-
#
-
# == Middlewares
-
#
-
# The Application is also responsible for building the middleware stack.
-
#
-
# == Booting process
-
#
-
# The application is also responsible for setting up and executing the booting
-
# process. From the moment you require "config/application.rb" in your app,
-
# the booting process goes like this:
-
#
-
# 1) require "config/boot.rb" to setup load paths
-
# 2) require railties and engines
-
# 3) Define Rails.application as "class MyApp::Application < Rails::Application"
-
# 4) Run config.before_configuration callbacks
-
# 5) Load config/environments/ENV.rb
-
# 6) Run config.before_initialize callbacks
-
# 7) Run Railtie#initializer defined by railties, engines and application.
-
# One by one, each engine sets up its load paths, routes and runs its config/initializers/* files.
-
# 8) Custom Railtie#initializers added by railties, engines and applications are executed
-
# 9) Build the middleware stack and run to_prepare callbacks
-
# 10) Run config.before_eager_load and eager_load! if eager_load is true
-
# 11) Run config.after_initialize callbacks
-
#
-
# == Multiple Applications
-
#
-
# If you decide to define multiple applications, then the first application
-
# that is initialized will be set to +Rails.application+, unless you override
-
# it with a different application.
-
#
-
# To create a new application, you can instantiate a new instance of a class
-
# that has already been created:
-
#
-
# class Application < Rails::Application
-
# end
-
#
-
# first_application = Application.new
-
# second_application = Application.new(config: first_application.config)
-
#
-
# In the above example, the configuration from the first application was used
-
# to initialize the second application. You can also use the +initialize_copy+
-
# on one of the applications to create a copy of the application which shares
-
# the configuration.
-
#
-
# If you decide to define rake tasks, runners, or initializers in an
-
# application other than +Rails.application+, then you must run those
-
# these manually.
-
1
class Application < Engine
-
1
autoload :Bootstrap, 'rails/application/bootstrap'
-
1
autoload :Configuration, 'rails/application/configuration'
-
1
autoload :DefaultMiddlewareStack, 'rails/application/default_middleware_stack'
-
1
autoload :Finisher, 'rails/application/finisher'
-
1
autoload :Railties, 'rails/engine/railties'
-
1
autoload :RoutesReloader, 'rails/application/routes_reloader'
-
-
1
class << self
-
1
def inherited(base)
-
1
super
-
1
base.instance
-
end
-
-
# Makes the +new+ method public.
-
#
-
# Note that Rails::Application inherits from Rails::Engine, which
-
# inherits from Rails::Railtie and the +new+ method on Rails::Railtie is
-
# private
-
1
public :new
-
end
-
-
1
attr_accessor :assets, :sandbox
-
1
alias_method :sandbox?, :sandbox
-
1
attr_reader :reloaders
-
-
1
delegate :default_url_options, :default_url_options=, to: :routes
-
-
1
INITIAL_VARIABLES = [:config, :railties, :routes_reloader, :reloaders,
-
:routes, :helpers, :app_env_config, :secrets] # :nodoc:
-
-
1
def initialize(initial_variable_values = {}, &block)
-
1
super()
-
1
@initialized = false
-
1
@reloaders = []
-
1
@routes_reloader = nil
-
1
@app_env_config = nil
-
1
@ordered_railties = nil
-
1
@railties = nil
-
1
@message_verifiers = {}
-
-
1
Rails.application ||= self
-
-
1
add_lib_to_load_path!
-
1
ActiveSupport.run_load_hooks(:before_configuration, self)
-
-
1
initial_variable_values.each do |variable_name, value|
-
if INITIAL_VARIABLES.include?(variable_name)
-
instance_variable_set("@#{variable_name}", value)
-
end
-
end
-
-
1
instance_eval(&block) if block_given?
-
end
-
-
# Returns true if the application is initialized.
-
1
def initialized?
-
@initialized
-
end
-
-
# Implements call according to the Rack API. It simply
-
# dispatches the request to the underlying middleware stack.
-
1
def call(env)
-
env["ORIGINAL_FULLPATH"] = build_original_fullpath(env)
-
env["ORIGINAL_SCRIPT_NAME"] = env["SCRIPT_NAME"]
-
super(env)
-
end
-
-
# Reload application routes regardless if they changed or not.
-
1
def reload_routes!
-
routes_reloader.reload!
-
end
-
-
# Return the application's KeyGenerator
-
1
def key_generator
-
# number of iterations selected based on consultation with the google security
-
# team. Details at https://github.com/rails/rails/pull/6952#issuecomment-7661220
-
@caching_key_generator ||= begin
-
1
if secrets.secret_key_base
-
1
key_generator = ActiveSupport::KeyGenerator.new(secrets.secret_key_base, iterations: 1000)
-
1
ActiveSupport::CachingKeyGenerator.new(key_generator)
-
else
-
ActiveSupport::LegacyKeyGenerator.new(config.secret_token)
-
end
-
1
end
-
end
-
-
# Returns a message verifier object.
-
#
-
# This verifier can be used to generate and verify signed messages in the application.
-
#
-
# It is recommended not to use the same verifier for different things, so you can get different
-
# verifiers passing the +verifier_name+ argument.
-
#
-
# ==== Parameters
-
#
-
# * +verifier_name+ - the name of the message verifier.
-
#
-
# ==== Examples
-
#
-
# message = Rails.application.message_verifier('sensitive_data').generate('my sensible data')
-
# Rails.application.message_verifier('sensitive_data').verify(message)
-
# # => 'my sensible data'
-
#
-
# See the +ActiveSupport::MessageVerifier+ documentation for more information.
-
1
def message_verifier(verifier_name)
-
@message_verifiers[verifier_name] ||= begin
-
secret = key_generator.generate_key(verifier_name.to_s)
-
ActiveSupport::MessageVerifier.new(secret)
-
end
-
end
-
-
# Stores some of the Rails initial environment parameters which
-
# will be used by middlewares and engines to configure themselves.
-
1
def env_config
-
@app_env_config ||= begin
-
1
validate_secret_key_config!
-
-
1
super.merge({
-
"action_dispatch.parameter_filter" => config.filter_parameters,
-
"action_dispatch.redirect_filter" => config.filter_redirect,
-
"action_dispatch.secret_token" => config.secret_token,
-
"action_dispatch.secret_key_base" => secrets.secret_key_base,
-
"action_dispatch.show_exceptions" => config.action_dispatch.show_exceptions,
-
"action_dispatch.show_detailed_exceptions" => config.consider_all_requests_local,
-
"action_dispatch.logger" => Rails.logger,
-
"action_dispatch.backtrace_cleaner" => Rails.backtrace_cleaner,
-
"action_dispatch.key_generator" => key_generator,
-
"action_dispatch.http_auth_salt" => config.action_dispatch.http_auth_salt,
-
"action_dispatch.signed_cookie_salt" => config.action_dispatch.signed_cookie_salt,
-
"action_dispatch.encrypted_cookie_salt" => config.action_dispatch.encrypted_cookie_salt,
-
"action_dispatch.encrypted_signed_cookie_salt" => config.action_dispatch.encrypted_signed_cookie_salt,
-
"action_dispatch.cookies_serializer" => config.action_dispatch.cookies_serializer
-
})
-
4
end
-
end
-
-
# If you try to define a set of rake tasks on the instance, these will get
-
# passed up to the rake tasks defined on the application's class.
-
1
def rake_tasks(&block)
-
self.class.rake_tasks(&block)
-
end
-
-
# Sends the initializers to the +initializer+ method defined in the
-
# Rails::Initializable module. Each Rails::Application class has its own
-
# set of initializers, as defined by the Initializable module.
-
1
def initializer(name, opts={}, &block)
-
self.class.initializer(name, opts, &block)
-
end
-
-
# Sends any runner called in the instance of a new application up
-
# to the +runner+ method defined in Rails::Railtie.
-
1
def runner(&blk)
-
self.class.runner(&blk)
-
end
-
-
# Sends any console called in the instance of a new application up
-
# to the +console+ method defined in Rails::Railtie.
-
1
def console(&blk)
-
self.class.console(&blk)
-
end
-
-
# Sends any generators called in the instance of a new application up
-
# to the +generators+ method defined in Rails::Railtie.
-
1
def generators(&blk)
-
self.class.generators(&blk)
-
end
-
-
# Sends the +isolate_namespace+ method up to the class method.
-
1
def isolate_namespace(mod)
-
self.class.isolate_namespace(mod)
-
end
-
-
## Rails internal API
-
-
# This method is called just after an application inherits from Rails::Application,
-
# allowing the developer to load classes in lib and use them during application
-
# configuration.
-
#
-
# class MyApplication < Rails::Application
-
# require "my_backend" # in lib/my_backend
-
# config.i18n.backend = MyBackend
-
# end
-
#
-
# Notice this method takes into consideration the default root path. So if you
-
# are changing config.root inside your application definition or having a custom
-
# Rails application, you will need to add lib to $LOAD_PATH on your own in case
-
# you need to load files in lib/ during the application configuration as well.
-
1
def add_lib_to_load_path! #:nodoc:
-
1
path = File.join config.root, 'lib'
-
1
if File.exist?(path) && !$LOAD_PATH.include?(path)
-
$LOAD_PATH.unshift(path)
-
end
-
end
-
-
1
def require_environment! #:nodoc:
-
environment = paths["config/environment"].existent.first
-
require environment if environment
-
end
-
-
1
def routes_reloader #:nodoc:
-
3
@routes_reloader ||= RoutesReloader.new
-
end
-
-
# Returns an array of file paths appended with a hash of
-
# directories-extensions suitable for ActiveSupport::FileUpdateChecker
-
# API.
-
1
def watchable_args #:nodoc:
-
1
files, dirs = config.watchable_files.dup, config.watchable_dirs.dup
-
-
1
ActiveSupport::Dependencies.autoload_paths.each do |path|
-
8
dirs[path.to_s] = [:rb]
-
end
-
-
1
[files, dirs]
-
end
-
-
# Initialize the application passing the given group. By default, the
-
# group is :default
-
1
def initialize!(group=:default) #:nodoc:
-
1
raise "Application has been already initialized." if @initialized
-
1
run_initializers(group, self)
-
1
@initialized = true
-
1
self
-
end
-
-
1
def initializers #:nodoc:
-
Bootstrap.initializers_for(self) +
-
railties_initializers(super) +
-
1
Finisher.initializers_for(self)
-
end
-
-
1
def config #:nodoc:
-
163
@config ||= Application::Configuration.new(find_root_with_flag("config.ru", Dir.pwd))
-
end
-
-
1
def config=(configuration) #:nodoc:
-
@config = configuration
-
end
-
-
1
def secrets #:nodoc:
-
@secrets ||= begin
-
1
secrets = ActiveSupport::OrderedOptions.new
-
1
yaml = config.paths["config/secrets"].first
-
1
if File.exist?(yaml)
-
1
require "erb"
-
1
all_secrets = YAML.load(ERB.new(IO.read(yaml)).result) || {}
-
1
env_secrets = all_secrets[Rails.env]
-
1
secrets.merge!(env_secrets.symbolize_keys) if env_secrets
-
end
-
-
# Fallback to config.secret_key_base if secrets.secret_key_base isn't set
-
1
secrets.secret_key_base ||= config.secret_key_base
-
-
1
secrets
-
4
end
-
end
-
-
1
def secrets=(secrets) #:nodoc:
-
@secrets = secrets
-
end
-
-
1
def to_app #:nodoc:
-
self
-
end
-
-
1
def helpers_paths #:nodoc:
-
1
config.helpers_paths
-
end
-
-
1
console do
-
require "pp"
-
end
-
-
1
console do
-
unless ::Kernel.private_method_defined?(:y)
-
if RUBY_VERSION >= '2.0'
-
require "psych/y"
-
else
-
module ::Kernel
-
def y(*objects)
-
puts ::Psych.dump_stream(*objects)
-
end
-
private :y
-
end
-
end
-
end
-
end
-
-
# Return an array of railties respecting the order they're loaded
-
# and the order specified by the +railties_order+ config.
-
#
-
# While when running initializers we need engines in reverse
-
# order here when copying migrations from railties we need then in the same
-
# order as given by +railties_order+
-
1
def migration_railties # :nodoc:
-
ordered_railties.flatten - [self]
-
end
-
-
1
protected
-
-
1
alias :build_middleware_stack :app
-
-
1
def run_tasks_blocks(app) #:nodoc:
-
railties.each { |r| r.run_tasks_blocks(app) }
-
super
-
require "rails/tasks"
-
task :environment do
-
ActiveSupport.on_load(:before_initialize) { config.eager_load = false }
-
-
require_environment!
-
end
-
end
-
-
1
def run_generators_blocks(app) #:nodoc:
-
railties.each { |r| r.run_generators_blocks(app) }
-
super
-
end
-
-
1
def run_runner_blocks(app) #:nodoc:
-
railties.each { |r| r.run_runner_blocks(app) }
-
super
-
end
-
-
1
def run_console_blocks(app) #:nodoc:
-
railties.each { |r| r.run_console_blocks(app) }
-
super
-
end
-
-
# Returns the ordered railties for this application considering railties_order.
-
1
def ordered_railties #:nodoc:
-
@ordered_railties ||= begin
-
1
order = config.railties_order.map do |railtie|
-
1
if railtie == :main_app
-
self
-
elsif railtie.respond_to?(:instance)
-
railtie.instance
-
else
-
1
railtie
-
end
-
end
-
-
1
all = (railties - order)
-
1
all.push(self) unless (all + order).include?(self)
-
1
order.push(:all) unless order.include?(:all)
-
-
1
index = order.index(:all)
-
1
order[index] = all
-
1
order
-
1
end
-
end
-
-
1
def railties_initializers(current) #:nodoc:
-
1
initializers = []
-
1
ordered_railties.reverse.flatten.each do |r|
-
21
if r == self
-
1
initializers += current
-
else
-
20
initializers += r.initializers
-
end
-
end
-
1
initializers
-
end
-
-
1
def default_middleware_stack #:nodoc:
-
1
default_stack = DefaultMiddlewareStack.new(self, config, paths)
-
1
default_stack.build_stack
-
end
-
-
1
def build_original_fullpath(env) #:nodoc:
-
path_info = env["PATH_INFO"]
-
query_string = env["QUERY_STRING"]
-
script_name = env["SCRIPT_NAME"]
-
-
if query_string.present?
-
"#{script_name}#{path_info}?#{query_string}"
-
else
-
"#{script_name}#{path_info}"
-
end
-
end
-
-
1
def validate_secret_key_config! #:nodoc:
-
1
if secrets.secret_key_base.blank? && config.secret_token.blank?
-
raise "Missing `secret_key_base` for '#{Rails.env}' environment, set this value in `config/secrets.yml`"
-
end
-
end
-
end
-
end
-
1
require "active_support/notifications"
-
1
require "active_support/dependencies"
-
1
require "active_support/descendants_tracker"
-
-
1
module Rails
-
1
class Application
-
1
module Bootstrap
-
1
include Initializable
-
-
1
initializer :load_environment_hook, group: :all do end
-
-
1
initializer :load_active_support, group: :all do
-
1
require "active_support/all" unless config.active_support.bare
-
end
-
-
1
initializer :set_eager_load, group: :all do
-
1
if config.eager_load.nil?
-
warn <<-INFO
-
config.eager_load is set to nil. Please update your config/environments/*.rb files accordingly:
-
-
* development - set it to false
-
* test - set it to false (unless you use a tool that preloads your test environment)
-
* production - set it to true
-
-
INFO
-
config.eager_load = config.cache_classes
-
end
-
end
-
-
# Initialize the logger early in the stack in case we need to log some deprecation.
-
1
initializer :initialize_logger, group: :all do
-
1
Rails.logger ||= config.logger || begin
-
1
path = config.paths["log"].first
-
1
unless File.exist? File.dirname path
-
FileUtils.mkdir_p File.dirname path
-
end
-
-
1
f = File.open path, 'a'
-
1
f.binmode
-
1
f.sync = config.autoflush_log # if true make sure every write flushes
-
-
1
logger = ActiveSupport::Logger.new f
-
1
logger.formatter = config.log_formatter
-
1
logger = ActiveSupport::TaggedLogging.new(logger)
-
1
logger
-
rescue StandardError
-
logger = ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new(STDERR))
-
logger.level = ActiveSupport::Logger::WARN
-
logger.warn(
-
"Rails Error: Unable to access log file. Please ensure that #{path} exists and is chmod 0666. " +
-
"The log level has been raised to WARN and the output directed to STDERR until the problem is fixed."
-
)
-
logger
-
end
-
-
1
Rails.logger.level = ActiveSupport::Logger.const_get(config.log_level.to_s.upcase)
-
end
-
-
# Initialize cache early in the stack so railties can make use of it.
-
1
initializer :initialize_cache, group: :all do
-
1
unless Rails.cache
-
1
Rails.cache = ActiveSupport::Cache.lookup_store(config.cache_store)
-
-
1
if Rails.cache.respond_to?(:middleware)
-
1
config.middleware.insert_before("Rack::Runtime", Rails.cache.middleware)
-
end
-
end
-
end
-
-
# Sets the dependency loading mechanism.
-
1
initializer :initialize_dependency_mechanism, group: :all do
-
1
ActiveSupport::Dependencies.mechanism = config.cache_classes ? :require : :load
-
end
-
-
1
initializer :bootstrap_hook, group: :all do |app|
-
1
ActiveSupport.run_load_hooks(:before_initialize, app)
-
end
-
end
-
end
-
end
-
1
require 'active_support/core_ext/kernel/reporting'
-
1
require 'active_support/file_update_checker'
-
1
require 'rails/engine/configuration'
-
-
1
module Rails
-
1
class Application
-
1
class Configuration < ::Rails::Engine::Configuration
-
1
attr_accessor :allow_concurrency, :asset_host, :assets, :autoflush_log,
-
:cache_classes, :cache_store, :consider_all_requests_local, :console,
-
:eager_load, :exceptions_app, :file_watcher, :filter_parameters,
-
:force_ssl, :helpers_paths, :logger, :log_formatter, :log_tags,
-
:railties_order, :relative_url_root, :secret_key_base, :secret_token,
-
:serve_static_assets, :ssl_options, :static_cache_control, :session_options,
-
:time_zone, :reload_classes_only_on_change,
-
:beginning_of_week, :filter_redirect
-
-
1
attr_writer :log_level
-
1
attr_reader :encoding
-
-
1
def initialize(*)
-
1
super
-
1
self.encoding = "utf-8"
-
1
@allow_concurrency = nil
-
1
@consider_all_requests_local = false
-
1
@filter_parameters = []
-
1
@filter_redirect = []
-
1
@helpers_paths = []
-
1
@serve_static_assets = true
-
1
@static_cache_control = nil
-
1
@force_ssl = false
-
1
@ssl_options = {}
-
1
@session_store = :cookie_store
-
1
@session_options = {}
-
1
@time_zone = "UTC"
-
1
@beginning_of_week = :monday
-
1
@log_level = nil
-
1
@middleware = app_middleware
-
1
@generators = app_generators
-
1
@cache_store = [ :file_store, "#{root}/tmp/cache/" ]
-
1
@railties_order = [:all]
-
1
@relative_url_root = ENV["RAILS_RELATIVE_URL_ROOT"]
-
1
@reload_classes_only_on_change = true
-
1
@file_watcher = ActiveSupport::FileUpdateChecker
-
1
@exceptions_app = nil
-
1
@autoflush_log = true
-
1
@log_formatter = ActiveSupport::Logger::SimpleFormatter.new
-
1
@eager_load = nil
-
1
@secret_token = nil
-
1
@secret_key_base = nil
-
-
1
@assets = ActiveSupport::OrderedOptions.new
-
1
@assets.enabled = true
-
1
@assets.paths = []
-
1
@assets.precompile = [ Proc.new { |path, fn| fn =~ /app\/assets/ && !%w(.js .css).include?(File.extname(path)) },
-
/(?:\/|\\|\A)application\.(css|js)$/ ]
-
1
@assets.prefix = "/assets"
-
1
@assets.version = '1.0'
-
1
@assets.debug = false
-
1
@assets.compile = true
-
1
@assets.digest = false
-
1
@assets.cache_store = [ :file_store, "#{root}/tmp/cache/assets/#{Rails.env}/" ]
-
1
@assets.js_compressor = nil
-
1
@assets.css_compressor = nil
-
1
@assets.logger = nil
-
end
-
-
1
def encoding=(value)
-
1
@encoding = value
-
1
silence_warnings do
-
1
Encoding.default_external = value
-
1
Encoding.default_internal = value
-
end
-
end
-
-
1
def paths
-
@paths ||= begin
-
1
paths = super
-
1
paths.add "config/database", with: "config/database.yml"
-
1
paths.add "config/secrets", with: "config/secrets.yml"
-
1
paths.add "config/environment", with: "config/environment.rb"
-
1
paths.add "lib/templates"
-
1
paths.add "log", with: "log/#{Rails.env}.log"
-
1
paths.add "public"
-
1
paths.add "public/javascripts"
-
1
paths.add "public/stylesheets"
-
1
paths.add "tmp"
-
1
paths
-
25
end
-
end
-
-
# Loads and returns the entire raw configuration of database from
-
# values stored in `config/database.yml`.
-
1
def database_configuration
-
1
yaml = Pathname.new(paths["config/database"].existent.first || "")
-
-
1
config = if yaml.exist?
-
1
require "yaml"
-
1
require "erb"
-
1
YAML.load(ERB.new(yaml.read).result) || {}
-
elsif ENV['DATABASE_URL']
-
# Value from ENV['DATABASE_URL'] is set to default database connection
-
# by Active Record.
-
{}
-
else
-
raise "Could not load database configuration. No such file - #{yaml}"
-
end
-
-
1
config
-
rescue Psych::SyntaxError => e
-
raise "YAML syntax error occurred while parsing #{paths["config/database"].first}. " \
-
"Please note that YAML must be consistently indented using spaces. Tabs are not allowed. " \
-
"Error: #{e.message}"
-
rescue => e
-
raise e, "Cannot load `Rails.application.database_configuration`:\n#{e.message}", e.backtrace
-
end
-
-
1
def log_level
-
1
@log_level ||= Rails.env.production? ? :info : :debug
-
end
-
-
1
def colorize_logging
-
ActiveSupport::LogSubscriber.colorize_logging
-
end
-
-
1
def colorize_logging=(val)
-
ActiveSupport::LogSubscriber.colorize_logging = val
-
self.generators.colorize_logging = val
-
end
-
-
1
def session_store(*args)
-
3
if args.empty?
-
2
case @session_store
-
when :disabled
-
nil
-
when :active_record_store
-
begin
-
ActionDispatch::Session::ActiveRecordStore
-
rescue NameError
-
raise "`ActiveRecord::SessionStore` is extracted out of Rails into a gem. " \
-
"Please add `activerecord-session_store` to your Gemfile to use it."
-
end
-
when Symbol
-
2
ActionDispatch::Session.const_get(@session_store.to_s.camelize)
-
else
-
@session_store
-
end
-
else
-
1
@session_store = args.shift
-
1
@session_options = args.shift || {}
-
end
-
end
-
-
end
-
end
-
end
-
1
module Rails
-
1
class Application
-
1
class DefaultMiddlewareStack
-
1
attr_reader :config, :paths, :app
-
-
1
def initialize(app, config, paths)
-
1
@app = app
-
1
@config = config
-
1
@paths = paths
-
end
-
-
1
def build_stack
-
1
ActionDispatch::MiddlewareStack.new.tap do |middleware|
-
1
if config.force_ssl
-
middleware.use ::ActionDispatch::SSL, config.ssl_options
-
end
-
-
1
middleware.use ::Rack::Sendfile, config.action_dispatch.x_sendfile_header
-
-
1
if config.serve_static_assets
-
1
middleware.use ::ActionDispatch::Static, paths["public"].first, config.static_cache_control
-
end
-
-
1
if rack_cache = load_rack_cache
-
require "action_dispatch/http/rack_cache"
-
middleware.use ::Rack::Cache, rack_cache
-
end
-
-
1
middleware.use ::Rack::Lock unless allow_concurrency?
-
1
middleware.use ::Rack::Runtime
-
1
middleware.use ::Rack::MethodOverride
-
1
middleware.use ::ActionDispatch::RequestId
-
-
# Must come after Rack::MethodOverride to properly log overridden methods
-
1
middleware.use ::Rails::Rack::Logger, config.log_tags
-
1
middleware.use ::ActionDispatch::ShowExceptions, show_exceptions_app
-
1
middleware.use ::ActionDispatch::DebugExceptions, app
-
1
middleware.use ::ActionDispatch::RemoteIp, config.action_dispatch.ip_spoofing_check, config.action_dispatch.trusted_proxies
-
-
1
unless config.cache_classes
-
middleware.use ::ActionDispatch::Reloader, lambda { reload_dependencies? }
-
end
-
-
1
middleware.use ::ActionDispatch::Callbacks
-
1
middleware.use ::ActionDispatch::Cookies
-
-
1
if config.session_store
-
1
if config.force_ssl && !config.session_options.key?(:secure)
-
config.session_options[:secure] = true
-
end
-
1
middleware.use config.session_store, config.session_options
-
1
middleware.use ::ActionDispatch::Flash
-
end
-
-
1
middleware.use ::ActionDispatch::ParamsParser
-
1
middleware.use ::Rack::Head
-
1
middleware.use ::Rack::ConditionalGet
-
1
middleware.use ::Rack::ETag, "no-cache"
-
end
-
end
-
-
1
private
-
-
1
def reload_dependencies?
-
config.reload_classes_only_on_change != true || app.reloaders.map(&:updated?).any?
-
end
-
-
1
def allow_concurrency?
-
1
config.allow_concurrency.nil? ? config.cache_classes : config.allow_concurrency
-
end
-
-
1
def load_rack_cache
-
1
rack_cache = config.action_dispatch.rack_cache
-
1
return unless rack_cache
-
-
begin
-
require 'rack/cache'
-
rescue LoadError => error
-
error.message << ' Be sure to add rack-cache to your Gemfile'
-
raise
-
end
-
-
if rack_cache == true
-
{
-
metastore: "rails:/",
-
entitystore: "rails:/",
-
verbose: false
-
}
-
else
-
rack_cache
-
end
-
end
-
-
1
def show_exceptions_app
-
1
config.exceptions_app || ActionDispatch::PublicExceptions.new(Rails.public_path)
-
end
-
end
-
end
-
end
-
1
module Rails
-
1
class Application
-
1
module Finisher
-
1
include Initializable
-
-
1
initializer :add_generator_templates do
-
1
config.generators.templates.unshift(*paths["lib/templates"].existent)
-
end
-
-
1
initializer :ensure_autoload_once_paths_as_subset do
-
1
extra = ActiveSupport::Dependencies.autoload_once_paths -
-
ActiveSupport::Dependencies.autoload_paths
-
-
1
unless extra.empty?
-
abort <<-end_error
-
autoload_once_paths must be a subset of the autoload_paths.
-
Extra items in autoload_once_paths: #{extra * ','}
-
end_error
-
end
-
end
-
-
1
initializer :add_builtin_route do |app|
-
1
if Rails.env.development?
-
app.routes.append do
-
get '/rails/mailers' => "rails/mailers#index"
-
get '/rails/mailers/*path' => "rails/mailers#preview"
-
get '/rails/info/properties' => "rails/info#properties"
-
get '/rails/info/routes' => "rails/info#routes"
-
get '/rails/info' => "rails/info#index"
-
get '/' => "rails/welcome#index"
-
end
-
end
-
end
-
-
1
initializer :build_middleware_stack do
-
1
build_middleware_stack
-
end
-
-
1
initializer :define_main_app_helper do |app|
-
1
app.routes.define_mounted_helper(:main_app)
-
end
-
-
1
initializer :add_to_prepare_blocks do
-
1
config.to_prepare_blocks.each do |block|
-
ActionDispatch::Reloader.to_prepare(&block)
-
end
-
end
-
-
# This needs to happen before eager load so it happens
-
# in exactly the same point regardless of config.cache_classes
-
1
initializer :run_prepare_callbacks do
-
1
ActionDispatch::Reloader.prepare!
-
end
-
-
1
initializer :eager_load! do
-
1
if config.eager_load
-
ActiveSupport.run_load_hooks(:before_eager_load, self)
-
config.eager_load_namespaces.each(&:eager_load!)
-
end
-
end
-
-
# All initialization is done, including eager loading in production
-
1
initializer :finisher_hook do
-
1
ActiveSupport.run_load_hooks(:after_initialize, self)
-
end
-
-
# Set routes reload after the finisher hook to ensure routes added in
-
# the hook are taken into account.
-
1
initializer :set_routes_reloader_hook do
-
1
reloader = routes_reloader
-
1
reloader.execute_if_updated
-
1
self.reloaders << reloader
-
1
ActionDispatch::Reloader.to_prepare do
-
# We configure #execute rather than #execute_if_updated because if
-
# autoloaded constants are cleared we need to reload routes also in
-
# case any was used there, as in
-
#
-
# mount MailPreview => 'mail_view'
-
#
-
# This means routes are also reloaded if i18n is updated, which
-
# might not be necessary, but in order to be more precise we need
-
# some sort of reloaders dependency support, to be added.
-
reloader.execute
-
end
-
end
-
-
# Set clearing dependencies after the finisher hook to ensure paths
-
# added in the hook are taken into account.
-
1
initializer :set_clear_dependencies_hook, group: :all do
-
1
callback = lambda do
-
ActiveSupport::DescendantsTracker.clear
-
ActiveSupport::Dependencies.clear
-
end
-
-
1
if config.reload_classes_only_on_change
-
1
reloader = config.file_watcher.new(*watchable_args, &callback)
-
1
self.reloaders << reloader
-
-
# Prepend this callback to have autoloaded constants cleared before
-
# any other possible reloading, in case they need to autoload fresh
-
# constants.
-
1
ActionDispatch::Reloader.to_prepare(prepend: true) do
-
# In addition to changes detected by the file watcher, if routes
-
# or i18n have been updated we also need to clear constants,
-
# that's why we run #execute rather than #execute_if_updated, this
-
# callback has to clear autoloaded constants after any update.
-
reloader.execute
-
end
-
else
-
ActionDispatch::Reloader.to_cleanup(&callback)
-
end
-
end
-
end
-
end
-
end
-
1
require "active_support/core_ext/module/delegation"
-
-
1
module Rails
-
1
class Application
-
1
class RoutesReloader
-
1
attr_reader :route_sets, :paths
-
1
delegate :execute_if_updated, :execute, :updated?, to: :updater
-
-
1
def initialize
-
1
@paths = []
-
1
@route_sets = []
-
end
-
-
1
def reload!
-
1
clear!
-
1
load_paths
-
1
finalize!
-
ensure
-
1
revert
-
end
-
-
1
private
-
-
1
def updater
-
@updater ||= begin
-
2
updater = ActiveSupport::FileUpdateChecker.new(paths) { reload! }
-
1
updater.execute
-
1
updater
-
1
end
-
end
-
-
1
def clear!
-
1
route_sets.each do |routes|
-
1
routes.disable_clear_and_finalize = true
-
1
routes.clear!
-
end
-
end
-
-
1
def load_paths
-
2
paths.each { |path| load(path) }
-
end
-
-
1
def finalize!
-
1
route_sets.each do |routes|
-
1
routes.finalize!
-
end
-
end
-
-
1
def revert
-
1
route_sets.each do |routes|
-
1
routes.disable_clear_and_finalize = false
-
end
-
end
-
end
-
end
-
end
-
1
require 'active_support/backtrace_cleaner'
-
-
1
module Rails
-
1
class BacktraceCleaner < ActiveSupport::BacktraceCleaner
-
1
APP_DIRS_PATTERN = /^\/?(app|config|lib|test)/
-
1
RENDER_TEMPLATE_PATTERN = /:in `_render_template_\w*'/
-
-
1
def initialize
-
1
super
-
1
add_filter { |line| line.sub("#{Rails.root}/", '') }
-
1
add_filter { |line| line.sub(RENDER_TEMPLATE_PATTERN, '') }
-
1
add_filter { |line| line.sub('./', '/') } # for tests
-
-
1
add_gem_filters
-
1
add_silencer { |line| line !~ APP_DIRS_PATTERN }
-
end
-
-
1
private
-
1
def add_gem_filters
-
3
gems_paths = (Gem.path | [Gem.default_dir]).map { |p| Regexp.escape(p) }
-
1
return if gems_paths.empty?
-
-
1
gems_regexp = %r{(#{gems_paths.join('|')})/gems/([^/]+)-([\w.]+)/(.*)}
-
1
add_filter { |line| line.sub(gems_regexp, '\2 (\3) \4') }
-
end
-
end
-
end
-
1
require 'active_support/ordered_options'
-
1
require 'active_support/core_ext/object'
-
1
require 'rails/paths'
-
1
require 'rails/rack'
-
-
1
module Rails
-
1
module Configuration
-
# MiddlewareStackProxy is a proxy for the Rails middleware stack that allows
-
# you to configure middlewares in your application. It works basically as a
-
# command recorder, saving each command to be applied after initialization
-
# over the default middleware stack, so you can add, swap, or remove any
-
# middleware in Rails.
-
#
-
# You can add your own middlewares by using the +config.middleware.use+ method:
-
#
-
# config.middleware.use Magical::Unicorns
-
#
-
# This will put the <tt>Magical::Unicorns</tt> middleware on the end of the stack.
-
# You can use +insert_before+ if you wish to add a middleware before another:
-
#
-
# config.middleware.insert_before ActionDispatch::Head, Magical::Unicorns
-
#
-
# There's also +insert_after+ which will insert a middleware after another:
-
#
-
# config.middleware.insert_after ActionDispatch::Head, Magical::Unicorns
-
#
-
# Middlewares can also be completely swapped out and replaced with others:
-
#
-
# config.middleware.swap ActionDispatch::Flash, Magical::Unicorns
-
#
-
# And finally they can also be removed from the stack completely:
-
#
-
# config.middleware.delete ActionDispatch::Flash
-
#
-
1
class MiddlewareStackProxy
-
1
def initialize
-
1
@operations = []
-
end
-
-
1
def insert_before(*args, &block)
-
1
@operations << [__method__, args, block]
-
end
-
-
1
alias :insert :insert_before
-
-
1
def insert_after(*args, &block)
-
2
@operations << [__method__, args, block]
-
end
-
-
1
def swap(*args, &block)
-
@operations << [__method__, args, block]
-
end
-
-
1
def use(*args, &block)
-
1
@operations << [__method__, args, block]
-
end
-
-
1
def delete(*args, &block)
-
@operations << [__method__, args, block]
-
end
-
-
1
def unshift(*args, &block)
-
@operations << [__method__, args, block]
-
end
-
-
1
def merge_into(other) #:nodoc:
-
1
@operations.each do |operation, args, block|
-
4
other.send(operation, *args, &block)
-
end
-
1
other
-
end
-
end
-
-
1
class Generators #:nodoc:
-
1
attr_accessor :aliases, :options, :templates, :fallbacks, :colorize_logging
-
1
attr_reader :hidden_namespaces
-
-
1
def initialize
-
1
@aliases = Hash.new { |h,k| h[k] = {} }
-
3
@options = Hash.new { |h,k| h[k] = {} }
-
1
@fallbacks = {}
-
1
@templates = []
-
1
@colorize_logging = true
-
1
@hidden_namespaces = []
-
end
-
-
1
def initialize_copy(source)
-
6
@aliases = @aliases.deep_dup
-
6
@options = @options.deep_dup
-
6
@fallbacks = @fallbacks.deep_dup
-
6
@templates = @templates.dup
-
end
-
-
1
def hide_namespace(namespace)
-
1
@hidden_namespaces << namespace
-
end
-
-
1
def method_missing(method, *args)
-
6
method = method.to_s.sub(/=$/, '').to_sym
-
-
6
return @options[method] if args.empty?
-
-
6
if method == :rails || args.first.is_a?(Hash)
-
namespace, configuration = method, args.shift
-
else
-
6
namespace, configuration = args.shift, args.shift
-
6
namespace = namespace.to_sym if namespace.respond_to?(:to_sym)
-
6
@options[:rails][method] = namespace
-
end
-
-
6
if configuration
-
1
aliases = configuration.delete(:aliases)
-
1
@aliases[namespace].merge!(aliases) if aliases
-
1
@options[namespace].merge!(configuration)
-
end
-
end
-
end
-
end
-
end
-
1
require 'rails/railtie'
-
1
require 'rails/engine/railties'
-
1
require 'active_support/core_ext/module/delegation'
-
1
require 'pathname'
-
-
1
module Rails
-
# <tt>Rails::Engine</tt> allows you to wrap a specific Rails application or subset of
-
# functionality and share it with other applications or within a larger packaged application.
-
# Since Rails 3.0, every <tt>Rails::Application</tt> is just an engine, which allows for simple
-
# feature and application sharing.
-
#
-
# Any <tt>Rails::Engine</tt> is also a <tt>Rails::Railtie</tt>, so the same
-
# methods (like <tt>rake_tasks</tt> and +generators+) and configuration
-
# options that are available in railties can also be used in engines.
-
#
-
# == Creating an Engine
-
#
-
# In Rails versions prior to 3.0, your gems automatically behaved as engines, however,
-
# this coupled Rails to Rubygems. Since Rails 3.0, if you want a gem to automatically
-
# behave as an engine, you have to specify an +Engine+ for it somewhere inside
-
# your plugin's +lib+ folder (similar to how we specify a +Railtie+):
-
#
-
# # lib/my_engine.rb
-
# module MyEngine
-
# class Engine < Rails::Engine
-
# end
-
# end
-
#
-
# Then ensure that this file is loaded at the top of your <tt>config/application.rb</tt>
-
# (or in your +Gemfile+) and it will automatically load models, controllers and helpers
-
# inside +app+, load routes at <tt>config/routes.rb</tt>, load locales at
-
# <tt>config/locales/*</tt>, and load tasks at <tt>lib/tasks/*</tt>.
-
#
-
# == Configuration
-
#
-
# Besides the +Railtie+ configuration which is shared across the application, in a
-
# <tt>Rails::Engine</tt> you can access <tt>autoload_paths</tt>, <tt>eager_load_paths</tt>
-
# and <tt>autoload_once_paths</tt>, which, differently from a <tt>Railtie</tt>, are scoped to
-
# the current engine.
-
#
-
# class MyEngine < Rails::Engine
-
# # Add a load path for this specific Engine
-
# config.autoload_paths << File.expand_path("../lib/some/path", __FILE__)
-
#
-
# initializer "my_engine.add_middleware" do |app|
-
# app.middleware.use MyEngine::Middleware
-
# end
-
# end
-
#
-
# == Generators
-
#
-
# You can set up generators for engines with <tt>config.generators</tt> method:
-
#
-
# class MyEngine < Rails::Engine
-
# config.generators do |g|
-
# g.orm :active_record
-
# g.template_engine :erb
-
# g.test_framework :test_unit
-
# end
-
# end
-
#
-
# You can also set generators for an application by using <tt>config.app_generators</tt>:
-
#
-
# class MyEngine < Rails::Engine
-
# # note that you can also pass block to app_generators in the same way you
-
# # can pass it to generators method
-
# config.app_generators.orm :datamapper
-
# end
-
#
-
# == Paths
-
#
-
# Since Rails 3.0, applications and engines have more flexible path configuration (as
-
# opposed to the previous hardcoded path configuration). This means that you are not
-
# required to place your controllers at <tt>app/controllers</tt>, but in any place
-
# which you find convenient.
-
#
-
# For example, let's suppose you want to place your controllers in <tt>lib/controllers</tt>.
-
# You can set that as an option:
-
#
-
# class MyEngine < Rails::Engine
-
# paths["app/controllers"] = "lib/controllers"
-
# end
-
#
-
# You can also have your controllers loaded from both <tt>app/controllers</tt> and
-
# <tt>lib/controllers</tt>:
-
#
-
# class MyEngine < Rails::Engine
-
# paths["app/controllers"] << "lib/controllers"
-
# end
-
#
-
# The available paths in an engine are:
-
#
-
# class MyEngine < Rails::Engine
-
# paths["app"] # => ["app"]
-
# paths["app/controllers"] # => ["app/controllers"]
-
# paths["app/helpers"] # => ["app/helpers"]
-
# paths["app/models"] # => ["app/models"]
-
# paths["app/views"] # => ["app/views"]
-
# paths["lib"] # => ["lib"]
-
# paths["lib/tasks"] # => ["lib/tasks"]
-
# paths["config"] # => ["config"]
-
# paths["config/initializers"] # => ["config/initializers"]
-
# paths["config/locales"] # => ["config/locales"]
-
# paths["config/routes.rb"] # => ["config/routes.rb"]
-
# end
-
#
-
# The <tt>Application</tt> class adds a couple more paths to this set. And as in your
-
# <tt>Application</tt>, all folders under +app+ are automatically added to the load path.
-
# If you have an <tt>app/services</tt> folder for example, it will be added by default.
-
#
-
# == Endpoint
-
#
-
# An engine can be also a rack application. It can be useful if you have a rack application that
-
# you would like to wrap with +Engine+ and provide some of the +Engine+'s features.
-
#
-
# To do that, use the +endpoint+ method:
-
#
-
# module MyEngine
-
# class Engine < Rails::Engine
-
# endpoint MyRackApplication
-
# end
-
# end
-
#
-
# Now you can mount your engine in application's routes just like that:
-
#
-
# Rails.application.routes.draw do
-
# mount MyEngine::Engine => "/engine"
-
# end
-
#
-
# == Middleware stack
-
#
-
# As an engine can now be a rack endpoint, it can also have a middleware
-
# stack. The usage is exactly the same as in <tt>Application</tt>:
-
#
-
# module MyEngine
-
# class Engine < Rails::Engine
-
# middleware.use SomeMiddleware
-
# end
-
# end
-
#
-
# == Routes
-
#
-
# If you don't specify an endpoint, routes will be used as the default
-
# endpoint. You can use them just like you use an application's routes:
-
#
-
# # ENGINE/config/routes.rb
-
# MyEngine::Engine.routes.draw do
-
# get "/" => "posts#index"
-
# end
-
#
-
# == Mount priority
-
#
-
# Note that now there can be more than one router in your application, and it's better to avoid
-
# passing requests through many routers. Consider this situation:
-
#
-
# Rails.application.routes.draw do
-
# mount MyEngine::Engine => "/blog"
-
# get "/blog/omg" => "main#omg"
-
# end
-
#
-
# +MyEngine+ is mounted at <tt>/blog</tt>, and <tt>/blog/omg</tt> points to application's
-
# controller. In such a situation, requests to <tt>/blog/omg</tt> will go through +MyEngine+,
-
# and if there is no such route in +Engine+'s routes, it will be dispatched to <tt>main#omg</tt>.
-
# It's much better to swap that:
-
#
-
# Rails.application.routes.draw do
-
# get "/blog/omg" => "main#omg"
-
# mount MyEngine::Engine => "/blog"
-
# end
-
#
-
# Now, +Engine+ will get only requests that were not handled by +Application+.
-
#
-
# == Engine name
-
#
-
# There are some places where an Engine's name is used:
-
#
-
# * routes: when you mount an Engine with <tt>mount(MyEngine::Engine => '/my_engine')</tt>,
-
# it's used as default <tt>:as</tt> option
-
# * rake task for installing migrations <tt>my_engine:install:migrations</tt>
-
#
-
# Engine name is set by default based on class name. For <tt>MyEngine::Engine</tt> it will be
-
# <tt>my_engine_engine</tt>. You can change it manually using the <tt>engine_name</tt> method:
-
#
-
# module MyEngine
-
# class Engine < Rails::Engine
-
# engine_name "my_engine"
-
# end
-
# end
-
#
-
# == Isolated Engine
-
#
-
# Normally when you create controllers, helpers and models inside an engine, they are treated
-
# as if they were created inside the application itself. This means that all helpers and
-
# named routes from the application will be available to your engine's controllers as well.
-
#
-
# However, sometimes you want to isolate your engine from the application, especially if your engine
-
# has its own router. To do that, you simply need to call +isolate_namespace+. This method requires
-
# you to pass a module where all your controllers, helpers and models should be nested to:
-
#
-
# module MyEngine
-
# class Engine < Rails::Engine
-
# isolate_namespace MyEngine
-
# end
-
# end
-
#
-
# With such an engine, everything that is inside the +MyEngine+ module will be isolated from
-
# the application.
-
#
-
# Consider such controller:
-
#
-
# module MyEngine
-
# class FooController < ActionController::Base
-
# end
-
# end
-
#
-
# If an engine is marked as isolated, +FooController+ has access only to helpers from +Engine+ and
-
# <tt>url_helpers</tt> from <tt>MyEngine::Engine.routes</tt>.
-
#
-
# The next thing that changes in isolated engines is the behavior of routes. Normally, when you namespace
-
# your controllers, you also need to do namespace all your routes. With an isolated engine,
-
# the namespace is applied by default, so you can ignore it in routes:
-
#
-
# MyEngine::Engine.routes.draw do
-
# resources :articles
-
# end
-
#
-
# The routes above will automatically point to <tt>MyEngine::ArticlesController</tt>. Furthermore, you don't
-
# need to use longer url helpers like <tt>my_engine_articles_path</tt>. Instead, you should simply use
-
# <tt>articles_path</tt> as you would do with your application.
-
#
-
# To make that behavior consistent with other parts of the framework, an isolated engine also has influence on
-
# <tt>ActiveModel::Naming</tt>. When you use a namespaced model, like <tt>MyEngine::Article</tt>, it will normally
-
# use the prefix "my_engine". In an isolated engine, the prefix will be omitted in url helpers and
-
# form fields for convenience.
-
#
-
# polymorphic_url(MyEngine::Article.new) # => "articles_path"
-
#
-
# form_for(MyEngine::Article.new) do
-
# text_field :title # => <input type="text" name="article[title]" id="article_title" />
-
# end
-
#
-
# Additionally, an isolated engine will set its name according to namespace, so
-
# MyEngine::Engine.engine_name will be "my_engine". It will also set MyEngine.table_name_prefix
-
# to "my_engine_", changing the MyEngine::Article model to use the my_engine_articles table.
-
#
-
# == Using Engine's routes outside Engine
-
#
-
# Since you can now mount an engine inside application's routes, you do not have direct access to +Engine+'s
-
# <tt>url_helpers</tt> inside +Application+. When you mount an engine in an application's routes, a special helper is
-
# created to allow you to do that. Consider such a scenario:
-
#
-
# # config/routes.rb
-
# Rails.application.routes.draw do
-
# mount MyEngine::Engine => "/my_engine", as: "my_engine"
-
# get "/foo" => "foo#index"
-
# end
-
#
-
# Now, you can use the <tt>my_engine</tt> helper inside your application:
-
#
-
# class FooController < ApplicationController
-
# def index
-
# my_engine.root_url # => /my_engine/
-
# end
-
# end
-
#
-
# There is also a <tt>main_app</tt> helper that gives you access to application's routes inside Engine:
-
#
-
# module MyEngine
-
# class BarController
-
# def index
-
# main_app.foo_path # => /foo
-
# end
-
# end
-
# end
-
#
-
# Note that the <tt>:as</tt> option given to mount takes the <tt>engine_name</tt> as default, so most of the time
-
# you can simply omit it.
-
#
-
# Finally, if you want to generate a url to an engine's route using
-
# <tt>polymorphic_url</tt>, you also need to pass the engine helper. Let's
-
# say that you want to create a form pointing to one of the engine's routes.
-
# All you need to do is pass the helper as the first element in array with
-
# attributes for url:
-
#
-
# form_for([my_engine, @user])
-
#
-
# This code will use <tt>my_engine.user_path(@user)</tt> to generate the proper route.
-
#
-
# == Isolated engine's helpers
-
#
-
# Sometimes you may want to isolate engine, but use helpers that are defined for it.
-
# If you want to share just a few specific helpers you can add them to application's
-
# helpers in ApplicationController:
-
#
-
# class ApplicationController < ActionController::Base
-
# helper MyEngine::SharedEngineHelper
-
# end
-
#
-
# If you want to include all of the engine's helpers, you can use #helper method on an engine's
-
# instance:
-
#
-
# class ApplicationController < ActionController::Base
-
# helper MyEngine::Engine.helpers
-
# end
-
#
-
# It will include all of the helpers from engine's directory. Take into account that this does
-
# not include helpers defined in controllers with helper_method or other similar solutions,
-
# only helpers defined in the helpers directory will be included.
-
#
-
# == Migrations & seed data
-
#
-
# Engines can have their own migrations. The default path for migrations is exactly the same
-
# as in application: <tt>db/migrate</tt>
-
#
-
# To use engine's migrations in application you can use rake task, which copies them to
-
# application's dir:
-
#
-
# rake ENGINE_NAME:install:migrations
-
#
-
# Note that some of the migrations may be skipped if a migration with the same name already exists
-
# in application. In such a situation you must decide whether to leave that migration or rename the
-
# migration in the application and rerun copying migrations.
-
#
-
# If your engine has migrations, you may also want to prepare data for the database in
-
# the <tt>db/seeds.rb</tt> file. You can load that data using the <tt>load_seed</tt> method, e.g.
-
#
-
# MyEngine::Engine.load_seed
-
#
-
# == Loading priority
-
#
-
# In order to change engine's priority you can use +config.railties_order+ in main application.
-
# It will affect the priority of loading views, helpers, assets and all the other files
-
# related to engine or application.
-
#
-
# # load Blog::Engine with highest priority, followed by application and other railties
-
# config.railties_order = [Blog::Engine, :main_app, :all]
-
1
class Engine < Railtie
-
1
autoload :Configuration, "rails/engine/configuration"
-
-
1
class << self
-
1
attr_accessor :called_from, :isolated
-
-
1
alias :isolated? :isolated
-
1
alias :engine_name :railtie_name
-
-
1
delegate :eager_load!, to: :instance
-
-
1
def inherited(base)
-
7
unless base.abstract_railtie?
-
6
Rails::Railtie::Configuration.eager_load_namespaces << base
-
-
6
base.called_from = begin
-
6
call_stack = if Kernel.respond_to?(:caller_locations)
-
6
caller_locations.map(&:path)
-
else
-
# Remove the line number from backtraces making sure we don't leave anything behind
-
caller.map { |p| p.sub(/:\d+.*/, '') }
-
end
-
-
13
File.dirname(call_stack.detect { |p| p !~ %r[railties[\w.-]*/lib/rails|rack[\w.-]*/lib/rack] })
-
end
-
end
-
-
7
super
-
end
-
-
1
def endpoint(endpoint = nil)
-
1
@endpoint ||= nil
-
1
@endpoint = endpoint if endpoint
-
1
@endpoint
-
end
-
-
1
def isolate_namespace(mod)
-
engine_name(generate_railtie_name(mod))
-
-
self.routes.default_scope = { module: ActiveSupport::Inflector.underscore(mod.name) }
-
self.isolated = true
-
-
unless mod.respond_to?(:railtie_namespace)
-
name, railtie = engine_name, self
-
-
mod.singleton_class.instance_eval do
-
define_method(:railtie_namespace) { railtie }
-
-
unless mod.respond_to?(:table_name_prefix)
-
define_method(:table_name_prefix) { "#{name}_" }
-
end
-
-
unless mod.respond_to?(:use_relative_model_naming?)
-
class_eval "def use_relative_model_naming?; true; end", __FILE__, __LINE__
-
end
-
-
unless mod.respond_to?(:railtie_helpers_paths)
-
define_method(:railtie_helpers_paths) { railtie.helpers_paths }
-
end
-
-
unless mod.respond_to?(:railtie_routes_url_helpers)
-
define_method(:railtie_routes_url_helpers) { railtie.routes.url_helpers }
-
end
-
end
-
end
-
end
-
-
# Finds engine with given path
-
1
def find(path)
-
expanded_path = File.expand_path path
-
Rails::Engine.subclasses.each do |klass|
-
engine = klass.instance
-
return engine if File.expand_path(engine.root) == expanded_path
-
end
-
nil
-
end
-
end
-
-
1
delegate :middleware, :root, :paths, to: :config
-
1
delegate :engine_name, :isolated?, to: :class
-
-
1
def initialize
-
6
@_all_autoload_paths = nil
-
6
@_all_load_paths = nil
-
6
@app = nil
-
6
@config = nil
-
6
@env_config = nil
-
6
@helpers = nil
-
6
@routes = nil
-
6
super
-
end
-
-
# Load console and invoke the registered hooks.
-
# Check <tt>Rails::Railtie.console</tt> for more info.
-
1
def load_console(app=self)
-
require "rails/console/app"
-
require "rails/console/helpers"
-
run_console_blocks(app)
-
self
-
end
-
-
# Load Rails runner and invoke the registered hooks.
-
# Check <tt>Rails::Railtie.runner</tt> for more info.
-
1
def load_runner(app=self)
-
run_runner_blocks(app)
-
self
-
end
-
-
# Load Rake, railties tasks and invoke the registered hooks.
-
# Check <tt>Rails::Railtie.rake_tasks</tt> for more info.
-
1
def load_tasks(app=self)
-
require "rake"
-
run_tasks_blocks(app)
-
self
-
end
-
-
# Load Rails generators and invoke the registered hooks.
-
# Check <tt>Rails::Railtie.generators</tt> for more info.
-
1
def load_generators(app=self)
-
require "rails/generators"
-
run_generators_blocks(app)
-
Rails::Generators.configure!(app.config.generators)
-
self
-
end
-
-
# Eager load the application by loading all ruby
-
# files inside eager_load paths.
-
1
def eager_load!
-
config.eager_load_paths.each do |load_path|
-
matcher = /\A#{Regexp.escape(load_path.to_s)}\/(.*)\.rb\Z/
-
Dir.glob("#{load_path}/**/*.rb").sort.each do |file|
-
require_dependency file.sub(matcher, '\1')
-
end
-
end
-
end
-
-
1
def railties
-
1
@railties ||= Railties.new
-
end
-
-
# Returns a module with all the helpers defined for the engine.
-
1
def helpers
-
@helpers ||= begin
-
helpers = Module.new
-
all = ActionController::Base.all_helpers_from_path(helpers_paths)
-
ActionController::Base.modules_for_helpers(all).each do |mod|
-
helpers.send(:include, mod)
-
end
-
helpers
-
end
-
end
-
-
# Returns all registered helpers paths.
-
1
def helpers_paths
-
paths["app/helpers"].existent
-
end
-
-
# Returns the underlying rack application for this engine.
-
1
def app
-
@app ||= begin
-
1
config.middleware = config.middleware.merge_into(default_middleware_stack)
-
1
config.middleware.build(endpoint)
-
1
end
-
end
-
-
# Returns the endpoint for this engine. If none is registered,
-
# defaults to an ActionDispatch::Routing::RouteSet.
-
1
def endpoint
-
1
self.class.endpoint || routes
-
end
-
-
# Define the Rack API for this engine.
-
1
def call(env)
-
env.merge!(env_config)
-
if env['SCRIPT_NAME']
-
env.merge! "ROUTES_#{routes.object_id}_SCRIPT_NAME" => env['SCRIPT_NAME'].dup
-
end
-
app.call(env)
-
end
-
-
# Defines additional Rack env configuration that is added on each call.
-
1
def env_config
-
@env_config ||= {
-
'action_dispatch.routes' => routes
-
1
}
-
end
-
-
# Defines the routes for this engine. If a block is given to
-
# routes, it is appended to the engine.
-
1
def routes
-
15
@routes ||= ActionDispatch::Routing::RouteSet.new
-
15
@routes.append(&Proc.new) if block_given?
-
15
@routes
-
end
-
-
# Define the configuration object for the engine.
-
1
def config
-
95
@config ||= Engine::Configuration.new(find_root_with_flag("lib"))
-
end
-
-
# Load data from db/seeds.rb file. It can be used in to load engines'
-
# seeds, e.g.:
-
#
-
# Blog::Engine.load_seed
-
1
def load_seed
-
seed_file = paths["db/seeds.rb"].existent.first
-
load(seed_file) if seed_file
-
end
-
-
# Add configured load paths to ruby load paths and remove duplicates.
-
1
initializer :set_load_path, before: :bootstrap_hook do
-
6
_all_load_paths.reverse_each do |path|
-
17
$LOAD_PATH.unshift(path) if File.directory?(path)
-
end
-
6
$LOAD_PATH.uniq!
-
end
-
-
# Set the paths from which Rails will automatically load source files,
-
# and the load_once paths.
-
#
-
# This needs to be an initializer, since it needs to run once
-
# per engine and get the engine as a block parameter
-
1
initializer :set_autoload_paths, before: :bootstrap_hook do
-
6
ActiveSupport::Dependencies.autoload_paths.unshift(*_all_autoload_paths)
-
6
ActiveSupport::Dependencies.autoload_once_paths.unshift(*_all_autoload_once_paths)
-
-
# Freeze so future modifications will fail rather than do nothing mysteriously
-
6
config.autoload_paths.freeze
-
6
config.eager_load_paths.freeze
-
6
config.autoload_once_paths.freeze
-
end
-
-
1
initializer :add_routing_paths do |app|
-
6
paths = self.paths["config/routes.rb"].existent
-
-
6
if routes? || paths.any?
-
1
app.routes_reloader.paths.unshift(*paths)
-
1
app.routes_reloader.route_sets << routes
-
end
-
end
-
-
# I18n load paths are a special case since the ones added
-
# later have higher priority.
-
1
initializer :add_locales do
-
6
config.i18n.railties_load_path.concat(paths["config/locales"].existent)
-
end
-
-
1
initializer :add_view_paths do
-
6
views = paths["app/views"].existent
-
6
unless views.empty?
-
2
ActiveSupport.on_load(:action_controller){ prepend_view_path(views) if respond_to?(:prepend_view_path) }
-
2
ActiveSupport.on_load(:action_mailer){ prepend_view_path(views) }
-
end
-
end
-
-
1
initializer :load_environment_config, before: :load_environment_hook, group: :all do
-
6
paths["config/environments"].existent.each do |environment|
-
1
require environment
-
end
-
end
-
-
1
initializer :append_assets_path, group: :all do |app|
-
6
app.config.assets.paths.unshift(*paths["vendor/assets"].existent_directories)
-
6
app.config.assets.paths.unshift(*paths["lib/assets"].existent_directories)
-
6
app.config.assets.paths.unshift(*paths["app/assets"].existent_directories)
-
end
-
-
1
initializer :prepend_helpers_path do |app|
-
6
if !isolated? || (app == self)
-
6
app.config.helpers_paths.unshift(*paths["app/helpers"].existent)
-
end
-
end
-
-
1
initializer :load_config_initializers do
-
6
config.paths["config/initializers"].existent.sort.each do |initializer|
-
9
load_config_initializer(initializer)
-
end
-
end
-
-
1
initializer :engines_blank_point do
-
# We need this initializer so all extra initializers added in engines are
-
# consistently executed after all the initializers above across all engines.
-
end
-
-
1
rake_tasks do
-
next if self.is_a?(Rails::Application)
-
next unless has_migrations?
-
-
namespace railtie_name do
-
namespace :install do
-
desc "Copy migrations from #{railtie_name} to application"
-
task :migrations do
-
ENV["FROM"] = railtie_name
-
if Rake::Task.task_defined?("railties:install:migrations")
-
Rake::Task["railties:install:migrations"].invoke
-
else
-
Rake::Task["app:railties:install:migrations"].invoke
-
end
-
end
-
end
-
end
-
end
-
-
1
def routes? #:nodoc:
-
6
@routes
-
end
-
-
1
protected
-
-
1
def load_config_initializer(initializer)
-
9
ActiveSupport::Notifications.instrument('load_config_initializer.railties', initializer: initializer) do
-
9
load(initializer)
-
end
-
end
-
-
1
def run_tasks_blocks(*) #:nodoc:
-
super
-
paths["lib/tasks"].existent.sort.each { |ext| load(ext) }
-
end
-
-
1
def has_migrations? #:nodoc:
-
paths["db/migrate"].existent.any?
-
end
-
-
1
def find_root_with_flag(flag, default=nil) #:nodoc:
-
6
root_path = self.class.called_from
-
-
6
while root_path && File.directory?(root_path) && !File.exist?("#{root_path}/#{flag}")
-
13
parent = File.dirname(root_path)
-
13
root_path = parent != root_path && parent
-
end
-
-
6
root = File.exist?("#{root_path}/#{flag}") ? root_path : default
-
6
raise "Could not find root path for #{self}" unless root
-
-
6
Pathname.new File.realpath root
-
end
-
-
1
def default_middleware_stack #:nodoc:
-
ActionDispatch::MiddlewareStack.new
-
end
-
-
1
def _all_autoload_once_paths #:nodoc:
-
6
config.autoload_once_paths
-
end
-
-
1
def _all_autoload_paths #:nodoc:
-
12
@_all_autoload_paths ||= (config.autoload_paths + config.eager_load_paths + config.autoload_once_paths).uniq
-
end
-
-
1
def _all_load_paths #:nodoc:
-
6
@_all_load_paths ||= (config.paths.load_paths + _all_autoload_paths).uniq
-
end
-
end
-
end
-
1
require 'rails/railtie/configuration'
-
-
1
module Rails
-
1
class Engine
-
1
class Configuration < ::Rails::Railtie::Configuration
-
1
attr_reader :root
-
1
attr_writer :middleware, :eager_load_paths, :autoload_once_paths, :autoload_paths
-
-
1
def initialize(root=nil)
-
6
super()
-
6
@root = root
-
6
@generators = app_generators.dup
-
end
-
-
# Returns the middleware stack for the engine.
-
1
def middleware
-
5
@middleware ||= Rails::Configuration::MiddlewareStackProxy.new
-
end
-
-
# Holds generators configuration:
-
#
-
# config.generators do |g|
-
# g.orm :data_mapper, migration: true
-
# g.template_engine :haml
-
# g.test_framework :rspec
-
# end
-
#
-
# If you want to disable color in console, do:
-
#
-
# config.generators.colorize_logging = false
-
#
-
1
def generators #:nodoc:
-
2
@generators ||= Rails::Configuration::Generators.new
-
2
yield(@generators) if block_given?
-
2
@generators
-
end
-
-
1
def paths
-
@paths ||= begin
-
6
paths = Rails::Paths::Root.new(@root)
-
-
6
paths.add "app", eager_load: true, glob: "*"
-
6
paths.add "app/assets", glob: "*"
-
6
paths.add "app/controllers", eager_load: true
-
6
paths.add "app/helpers", eager_load: true
-
6
paths.add "app/models", eager_load: true
-
6
paths.add "app/mailers", eager_load: true
-
6
paths.add "app/views"
-
-
6
paths.add "app/controllers/concerns", eager_load: true
-
6
paths.add "app/models/concerns", eager_load: true
-
-
6
paths.add "lib", load_path: true
-
6
paths.add "lib/assets", glob: "*"
-
6
paths.add "lib/tasks", glob: "**/*.rake"
-
-
6
paths.add "config"
-
6
paths.add "config/environments", glob: "#{Rails.env}.rb"
-
6
paths.add "config/initializers", glob: "**/*.rb"
-
6
paths.add "config/locales", glob: "*.{rb,yml}"
-
6
paths.add "config/routes.rb"
-
-
6
paths.add "db"
-
6
paths.add "db/migrate"
-
6
paths.add "db/seeds.rb"
-
-
6
paths.add "vendor", load_path: true
-
6
paths.add "vendor/assets", glob: "*"
-
-
6
paths
-
66
end
-
end
-
-
1
def root=(value)
-
@root = paths.path = Pathname.new(value).expand_path
-
end
-
-
1
def eager_load_paths
-
12
@eager_load_paths ||= paths.eager_load
-
end
-
-
1
def autoload_once_paths
-
18
@autoload_once_paths ||= paths.autoload_once
-
end
-
-
1
def autoload_paths
-
12
@autoload_paths ||= paths.autoload_paths
-
end
-
end
-
end
-
end
-
1
module Rails
-
1
class Engine < Railtie
-
1
class Railties
-
1
include Enumerable
-
1
attr_reader :_all
-
-
1
def initialize
-
@_all ||= ::Rails::Railtie.subclasses.map(&:instance) +
-
1
::Rails::Engine.subclasses.map(&:instance)
-
end
-
-
1
def each(*args, &block)
-
_all.each(*args, &block)
-
end
-
-
1
def -(others)
-
1
_all - others
-
end
-
end
-
end
-
end
-
1
module Rails
-
# Returns the version of the currently loaded Rails as a <tt>Gem::Version</tt>
-
1
def self.gem_version
-
Gem::Version.new VERSION::STRING
-
end
-
-
1
module VERSION
-
1
MAJOR = 4
-
1
MINOR = 1
-
1
TINY = 6
-
1
PRE = nil
-
-
1
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
-
end
-
end
-
1
require 'tsort'
-
-
1
module Rails
-
1
module Initializable
-
1
def self.included(base) #:nodoc:
-
3
base.extend ClassMethods
-
end
-
-
1
class Initializer
-
1
attr_reader :name, :block
-
-
1
def initialize(name, context, options, &block)
-
172
options[:group] ||= :default
-
172
@name, @context, @options, @block = name, context, options, block
-
end
-
-
1
def before
-
12321
@options[:before]
-
end
-
-
1
def after
-
12303
@options[:after]
-
end
-
-
1
def belongs_to?(group)
-
111
@options[:group] == group || @options[:group] == :all
-
end
-
-
1
def run(*args)
-
111
@context.instance_exec(*args, &block)
-
end
-
-
1
def bind(context)
-
111
return self if @context
-
111
Initializer.new(@name, context, @options, &block)
-
end
-
end
-
-
1
class Collection < Array
-
1
include TSort
-
-
1
alias :tsort_each_node :each
-
1
def tsort_each_child(initializer, &block)
-
12432
select { |i| i.before == initializer.name || i.name == initializer.after }.each(&block)
-
end
-
-
1
def +(other)
-
53
Collection.new(to_a + other.to_a)
-
end
-
end
-
-
1
def run_initializers(group=:default, *args)
-
1
return if instance_variable_defined?(:@ran)
-
1
initializers.tsort_each do |initializer|
-
111
initializer.run(*args) if initializer.belongs_to?(group)
-
end
-
1
@ran = true
-
end
-
-
1
def initializers
-
21
@initializers ||= self.class.initializers_for(self)
-
end
-
-
1
module ClassMethods
-
1
def initializers
-
267
@initializers ||= Collection.new
-
end
-
-
1
def initializers_chain
-
23
initializers = Collection.new
-
23
ancestors.reverse_each do |klass|
-
221
next unless klass.respond_to?(:initializers)
-
51
initializers = initializers + klass.initializers
-
end
-
23
initializers
-
end
-
-
1
def initializers_for(binding)
-
134
Collection.new(initializers_chain.map { |i| i.bind(binding) })
-
end
-
-
1
def initializer(name, opts = {}, &blk)
-
61
raise ArgumentError, "A block must be passed when defining an initializer" unless blk
-
248
opts[:after] ||= initializers.last.name unless initializers.empty? || initializers.find { |i| i.name == opts[:before] }
-
61
initializers << Initializer.new(name, nil, opts, &blk)
-
end
-
end
-
end
-
end
-
1
module Rails
-
1
module Paths
-
# This object is an extended hash that behaves as root of the <tt>Rails::Paths</tt> system.
-
# It allows you to collect information about how you want to structure your application
-
# paths by a Hash like API. It requires you to give a physical path on initialization.
-
#
-
# root = Root.new "/rails"
-
# root.add "app/controllers", eager_load: true
-
#
-
# The command above creates a new root object and add "app/controllers" as a path.
-
# This means we can get a <tt>Rails::Paths::Path</tt> object back like below:
-
#
-
# path = root["app/controllers"]
-
# path.eager_load? # => true
-
# path.is_a?(Rails::Paths::Path) # => true
-
#
-
# The +Path+ object is simply an enumerable and allows you to easily add extra paths:
-
#
-
# path.is_a?(Enumerable) # => true
-
# path.to_ary.inspect # => ["app/controllers"]
-
#
-
# path << "lib/controllers"
-
# path.to_ary.inspect # => ["app/controllers", "lib/controllers"]
-
#
-
# Notice that when you add a path using +add+, the path object created already
-
# contains the path with the same path value given to +add+. In some situations,
-
# you may not want this behavior, so you can give <tt>:with</tt> as option.
-
#
-
# root.add "config/routes", with: "config/routes.rb"
-
# root["config/routes"].inspect # => ["config/routes.rb"]
-
#
-
# The +add+ method accepts the following options as arguments:
-
# eager_load, autoload, autoload_once and glob.
-
#
-
# Finally, the +Path+ object also provides a few helpers:
-
#
-
# root = Root.new "/rails"
-
# root.add "app/controllers"
-
#
-
# root["app/controllers"].expanded # => ["/rails/app/controllers"]
-
# root["app/controllers"].existent # => ["/rails/app/controllers"]
-
#
-
# Check the <tt>Rails::Paths::Path</tt> documentation for more information.
-
1
class Root
-
1
attr_accessor :path
-
-
1
def initialize(path)
-
6
@current = nil
-
6
@path = path
-
6
@root = {}
-
end
-
-
1
def []=(path, value)
-
glob = self[path] ? self[path].glob : nil
-
add(path, with: value, glob: glob)
-
end
-
-
1
def add(path, options = {})
-
141
with = Array(options.fetch(:with, path))
-
141
@root[path] = Path.new(self, path, with, options)
-
end
-
-
1
def [](path)
-
69
@root[path]
-
end
-
-
1
def values
-
24
@root.values
-
end
-
-
1
def keys
-
54
@root.keys
-
end
-
-
1
def values_at(*list)
-
54
@root.values_at(*list)
-
end
-
-
1
def all_paths
-
48
values.tap { |v| v.uniq! }
-
end
-
-
1
def autoload_once
-
147
filter_by { |p| p.autoload_once? }
-
end
-
-
1
def eager_load
-
207
filter_by { |p| p.eager_load? }
-
end
-
-
1
def autoload_paths
-
147
filter_by { |p| p.autoload? }
-
end
-
-
1
def load_paths
-
166
filter_by { |p| p.load_path? }
-
end
-
-
1
private
-
-
1
def filter_by(&block)
-
all_paths.find_all(&block).flat_map { |path|
-
54
paths = path.existent
-
133
paths - path.children.map { |p| yield(p) ? [] : p.existent }.flatten
-
24
}.uniq
-
end
-
end
-
-
1
class Path
-
1
include Enumerable
-
-
1
attr_accessor :glob
-
-
1
def initialize(root, current, paths, options = {})
-
141
@paths = paths
-
141
@current = current
-
141
@root = root
-
141
@glob = options[:glob]
-
-
141
options[:autoload_once] ? autoload_once! : skip_autoload_once!
-
141
options[:eager_load] ? eager_load! : skip_eager_load!
-
141
options[:autoload] ? autoload! : skip_autoload!
-
141
options[:load_path] ? load_path! : skip_load_path!
-
end
-
-
1
def children
-
54
keys = @root.keys.find_all { |k|
-
1269
k.start_with?(@current) && k != @current
-
}
-
54
@root.values_at(*keys.sort)
-
end
-
-
1
def first
-
13
expanded.first
-
end
-
-
1
def last
-
expanded.last
-
end
-
-
1
%w(autoload_once eager_load autoload load_path).each do |m|
-
4
class_eval <<-RUBY, __FILE__, __LINE__ + 1
-
def #{m}! # def eager_load!
-
@#{m} = true # @eager_load = true
-
end # end
-
#
-
def skip_#{m}! # def skip_eager_load!
-
@#{m} = false # @eager_load = false
-
end # end
-
#
-
def #{m}? # def eager_load?
-
@#{m} # @eager_load
-
end # end
-
RUBY
-
end
-
-
1
def each(&block)
-
154
@paths.each(&block)
-
end
-
-
1
def <<(path)
-
@paths << path
-
end
-
1
alias :push :<<
-
-
1
def concat(paths)
-
@paths.concat paths
-
end
-
-
1
def unshift(path)
-
@paths.unshift path
-
end
-
-
1
def to_ary
-
@paths
-
end
-
-
# Expands all paths against the root and return all unique values.
-
1
def expanded
-
154
raise "You need to set a path root" unless @root.path
-
154
result = []
-
-
154
each do |p|
-
154
path = File.expand_path(p, @root.path)
-
-
154
if @glob && File.directory?(path)
-
17
Dir.chdir(path) do
-
53
result.concat(Dir.glob(@glob).map { |file| File.join path, file }.sort)
-
end
-
else
-
137
result << path
-
end
-
end
-
-
154
result.uniq!
-
154
result
-
end
-
-
# Returns all expanded paths but only if they exist in the filesystem.
-
1
def existent
-
262
expanded.select { |f| File.exist?(f) }
-
end
-
-
1
def existent_directories
-
39
expanded.select { |d| File.directory?(d) }
-
end
-
-
1
alias to_a expanded
-
end
-
end
-
end
-
1
module Rails
-
1
module Rack
-
1
autoload :Debugger, "rails/rack/debugger"
-
1
autoload :Logger, "rails/rack/logger"
-
1
autoload :LogTailer, "rails/rack/log_tailer"
-
end
-
end
-
1
require 'active_support/core_ext/time/conversions'
-
1
require 'active_support/core_ext/object/blank'
-
1
require 'active_support/log_subscriber'
-
1
require 'action_dispatch/http/request'
-
1
require 'rack/body_proxy'
-
-
1
module Rails
-
1
module Rack
-
# Sets log tags, logs the request, calls the app, and flushes the logs.
-
1
class Logger < ActiveSupport::LogSubscriber
-
1
def initialize(app, taggers = nil)
-
1
@app = app
-
1
@taggers = taggers || []
-
end
-
-
1
def call(env)
-
request = ActionDispatch::Request.new(env)
-
-
if logger.respond_to?(:tagged)
-
logger.tagged(compute_tags(request)) { call_app(request, env) }
-
else
-
call_app(request, env)
-
end
-
end
-
-
1
protected
-
-
1
def call_app(request, env)
-
# Put some space between requests in development logs.
-
if development?
-
logger.debug ''
-
logger.debug ''
-
end
-
-
instrumenter = ActiveSupport::Notifications.instrumenter
-
instrumenter.start 'request.action_dispatch', request: request
-
logger.info started_request_message(request)
-
resp = @app.call(env)
-
resp[2] = ::Rack::BodyProxy.new(resp[2]) { finish(request) }
-
resp
-
rescue Exception
-
finish(request)
-
raise
-
ensure
-
ActiveSupport::LogSubscriber.flush_all!
-
end
-
-
# Started GET "/session/new" for 127.0.0.1 at 2012-09-26 14:51:42 -0700
-
1
def started_request_message(request)
-
'Started %s "%s" for %s at %s' % [
-
request.request_method,
-
request.filtered_path,
-
request.ip,
-
Time.now.to_default_s ]
-
end
-
-
1
def compute_tags(request)
-
@taggers.collect do |tag|
-
case tag
-
when Proc
-
tag.call(request)
-
when Symbol
-
request.send(tag)
-
else
-
tag
-
end
-
end
-
end
-
-
1
private
-
-
1
def finish(request)
-
instrumenter = ActiveSupport::Notifications.instrumenter
-
instrumenter.finish 'request.action_dispatch', request: request
-
end
-
-
1
def development?
-
Rails.env.development?
-
end
-
-
1
def logger
-
Rails.logger
-
end
-
end
-
end
-
end
-
1
require 'rails/initializable'
-
1
require 'rails/configuration'
-
1
require 'active_support/inflector'
-
1
require 'active_support/core_ext/module/introspection'
-
1
require 'active_support/core_ext/module/delegation'
-
-
1
module Rails
-
# Railtie is the core of the Rails framework and provides several hooks to extend
-
# Rails and/or modify the initialization process.
-
#
-
# Every major component of Rails (Action Mailer, Action Controller,
-
# Action View and Active Record) is a Railtie. Each of
-
# them is responsible for their own initialization. This makes Rails itself
-
# absent of any component hooks, allowing other components to be used in
-
# place of any of the Rails defaults.
-
#
-
# Developing a Rails extension does _not_ require any implementation of
-
# Railtie, but if you need to interact with the Rails framework during
-
# or after boot, then Railtie is needed.
-
#
-
# For example, an extension doing any of the following would require Railtie:
-
#
-
# * creating initializers
-
# * configuring a Rails framework for the application, like setting a generator
-
# * adding <tt>config.*</tt> keys to the environment
-
# * setting up a subscriber with ActiveSupport::Notifications
-
# * adding rake tasks
-
#
-
# == Creating your Railtie
-
#
-
# To extend Rails using Railtie, create a Railtie class which inherits
-
# from Rails::Railtie within your extension's namespace. This class must be
-
# loaded during the Rails boot process.
-
#
-
# The following example demonstrates an extension which can be used with or without Rails.
-
#
-
# # lib/my_gem/railtie.rb
-
# module MyGem
-
# class Railtie < Rails::Railtie
-
# end
-
# end
-
#
-
# # lib/my_gem.rb
-
# require 'my_gem/railtie' if defined?(Rails)
-
#
-
# == Initializers
-
#
-
# To add an initialization step from your Railtie to Rails boot process, you just need
-
# to create an initializer block:
-
#
-
# class MyRailtie < Rails::Railtie
-
# initializer "my_railtie.configure_rails_initialization" do
-
# # some initialization behavior
-
# end
-
# end
-
#
-
# If specified, the block can also receive the application object, in case you
-
# need to access some application specific configuration, like middleware:
-
#
-
# class MyRailtie < Rails::Railtie
-
# initializer "my_railtie.configure_rails_initialization" do |app|
-
# app.middleware.use MyRailtie::Middleware
-
# end
-
# end
-
#
-
# Finally, you can also pass <tt>:before</tt> and <tt>:after</tt> as option to initializer,
-
# in case you want to couple it with a specific step in the initialization process.
-
#
-
# == Configuration
-
#
-
# Inside the Railtie class, you can access a config object which contains configuration
-
# shared by all railties and the application:
-
#
-
# class MyRailtie < Rails::Railtie
-
# # Customize the ORM
-
# config.app_generators.orm :my_railtie_orm
-
#
-
# # Add a to_prepare block which is executed once in production
-
# # and before each request in development
-
# config.to_prepare do
-
# MyRailtie.setup!
-
# end
-
# end
-
#
-
# == Loading rake tasks and generators
-
#
-
# If your railtie has rake tasks, you can tell Rails to load them through the method
-
# rake_tasks:
-
#
-
# class MyRailtie < Rails::Railtie
-
# rake_tasks do
-
# load "path/to/my_railtie.tasks"
-
# end
-
# end
-
#
-
# By default, Rails load generators from your load path. However, if you want to place
-
# your generators at a different location, you can specify in your Railtie a block which
-
# will load them during normal generators lookup:
-
#
-
# class MyRailtie < Rails::Railtie
-
# generators do
-
# require "path/to/my_railtie_generator"
-
# end
-
# end
-
#
-
# == Application and Engine
-
#
-
# A Rails::Engine is nothing more than a Railtie with some initializers already set.
-
# And since Rails::Application is an engine, the same configuration described here
-
# can be used in both.
-
#
-
# Be sure to look at the documentation of those specific classes for more information.
-
#
-
1
class Railtie
-
1
autoload :Configuration, "rails/railtie/configuration"
-
-
1
include Initializable
-
-
1
ABSTRACT_RAILTIES = %w(Rails::Railtie Rails::Engine Rails::Application)
-
-
1
class << self
-
1
private :new
-
1
delegate :config, to: :instance
-
-
1
def subclasses
-
23
@subclasses ||= []
-
end
-
-
1
def inherited(base)
-
23
unless base.abstract_railtie?
-
21
subclasses << base
-
end
-
end
-
-
1
def rake_tasks(&blk)
-
5
@rake_tasks ||= []
-
5
@rake_tasks << blk if blk
-
5
@rake_tasks
-
end
-
-
1
def console(&blk)
-
3
@load_console ||= []
-
3
@load_console << blk if blk
-
3
@load_console
-
end
-
-
1
def runner(&blk)
-
1
@load_runner ||= []
-
1
@load_runner << blk if blk
-
1
@load_runner
-
end
-
-
1
def generators(&blk)
-
1
@generators ||= []
-
1
@generators << blk if blk
-
1
@generators
-
end
-
-
1
def abstract_railtie?
-
51
ABSTRACT_RAILTIES.include?(name)
-
end
-
-
1
def railtie_name(name = nil)
-
@railtie_name = name.to_s if name
-
@railtie_name ||= generate_railtie_name(self.name)
-
end
-
-
# Since Rails::Railtie cannot be instantiated, any methods that call
-
# +instance+ are intended to be called only on subclasses of a Railtie.
-
1
def instance
-
94
@instance ||= new
-
end
-
-
1
def respond_to_missing?(*args)
-
instance.respond_to?(*args) || super
-
end
-
-
# Allows you to configure the railtie. This is the same method seen in
-
# Railtie::Configurable, but this module is no longer required for all
-
# subclasses of Railtie so we provide the class method here.
-
1
def configure(&block)
-
instance.configure(&block)
-
end
-
-
1
protected
-
1
def generate_railtie_name(class_or_module)
-
ActiveSupport::Inflector.underscore(class_or_module).tr("/", "_")
-
end
-
-
# If the class method does not have a method, then send the method call
-
# to the Railtie instance.
-
1
def method_missing(name, *args, &block)
-
if instance.respond_to?(name)
-
instance.public_send(name, *args, &block)
-
else
-
super
-
end
-
end
-
end
-
-
1
delegate :railtie_name, to: :class
-
-
1
def initialize
-
21
if self.class.abstract_railtie?
-
raise "#{self.class.name} is abstract, you cannot instantiate it directly."
-
end
-
end
-
-
1
def configure(&block)
-
1
instance_eval(&block)
-
end
-
-
1
def config
-
84
@config ||= Railtie::Configuration.new
-
end
-
-
1
def railtie_namespace
-
@railtie_namespace ||= self.class.parents.detect { |n| n.respond_to?(:railtie_namespace) }
-
end
-
-
1
protected
-
-
1
def run_console_blocks(app) #:nodoc:
-
each_registered_block(:console) { |block| block.call(app) }
-
end
-
-
1
def run_generators_blocks(app) #:nodoc:
-
each_registered_block(:generators) { |block| block.call(app) }
-
end
-
-
1
def run_runner_blocks(app) #:nodoc:
-
each_registered_block(:runner) { |block| block.call(app) }
-
end
-
-
1
def run_tasks_blocks(app) #:nodoc:
-
extend Rake::DSL
-
each_registered_block(:rake_tasks) { |block| instance_exec(app, &block) }
-
end
-
-
1
private
-
-
1
def each_registered_block(type, &block)
-
klass = self.class
-
while klass.respond_to?(type)
-
klass.public_send(type).each(&block)
-
klass = klass.superclass
-
end
-
end
-
end
-
end
-
1
require 'rails/configuration'
-
-
1
module Rails
-
1
class Railtie
-
1
class Configuration
-
1
def initialize
-
20
@@options ||= {}
-
end
-
-
# Expose the eager_load_namespaces at "module" level for convenience.
-
1
def self.eager_load_namespaces #:nodoc:
-
6
@@eager_load_namespaces ||= []
-
end
-
-
# All namespaces that are eager loaded
-
1
def eager_load_namespaces
-
7
@@eager_load_namespaces ||= []
-
end
-
-
# Add files that should be watched for change.
-
1
def watchable_files
-
2
@@watchable_files ||= []
-
end
-
-
# Add directories that should be watched for change.
-
# The key of the hashes should be directories and the values should
-
# be an array of extensions to match in each directory.
-
1
def watchable_dirs
-
1
@@watchable_dirs ||= {}
-
end
-
-
# This allows you to modify the application's middlewares from Engines.
-
#
-
# All operations you run on the app_middleware will be replayed on the
-
# application once it is defined and the default_middlewares are
-
# created
-
1
def app_middleware
-
3
@@app_middleware ||= Rails::Configuration::MiddlewareStackProxy.new
-
end
-
-
# This allows you to modify application's generators from Railties.
-
#
-
# Values set on app_generators will become defaults for application, unless
-
# application overwrites them.
-
1
def app_generators
-
12
@@app_generators ||= Rails::Configuration::Generators.new
-
12
yield(@@app_generators) if block_given?
-
12
@@app_generators
-
end
-
-
# First configurable block to run. Called before any initializers are run.
-
1
def before_configuration(&block)
-
3
ActiveSupport.on_load(:before_configuration, yield: true, &block)
-
end
-
-
# Third configurable block to run. Does not run if +config.cache_classes+
-
# set to false.
-
1
def before_eager_load(&block)
-
1
ActiveSupport.on_load(:before_eager_load, yield: true, &block)
-
end
-
-
# Second configurable block to run. Called before frameworks initialize.
-
1
def before_initialize(&block)
-
ActiveSupport.on_load(:before_initialize, yield: true, &block)
-
end
-
-
# Last configurable block to run. Called after frameworks initialize.
-
1
def after_initialize(&block)
-
5
ActiveSupport.on_load(:after_initialize, yield: true, &block)
-
end
-
-
# Array of callbacks defined by #to_prepare.
-
1
def to_prepare_blocks
-
1
@@to_prepare_blocks ||= []
-
end
-
-
# Defines generic callbacks to run before #after_initialize. Useful for
-
# Rails::Railtie subclasses.
-
1
def to_prepare(&blk)
-
to_prepare_blocks << blk if blk
-
end
-
-
1
def respond_to?(name, include_private = false)
-
1
super || @@options.key?(name.to_sym)
-
end
-
-
1
private
-
-
1
def method_missing(name, *args, &blk)
-
151
if name.to_s =~ /=$/
-
10
@@options[$`.to_sym] = args.first
-
141
elsif @@options.key?(name)
-
141
@@options[name]
-
else
-
super
-
end
-
end
-
end
-
end
-
end
-
1
if RUBY_VERSION < '1.9.3'
-
desc = defined?(RUBY_DESCRIPTION) ? RUBY_DESCRIPTION : "ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE})"
-
abort <<-end_message
-
-
Rails 4 prefers to run on Ruby 2.0.
-
-
You're running
-
#{desc}
-
-
Please upgrade to Ruby 1.9.3 or newer to continue.
-
-
end_message
-
end
-
1
require_relative 'gem_version'
-
-
1
module Rails
-
# Returns the version of the currently loaded Rails as a string.
-
1
def self.version
-
4
VERSION::STRING
-
end
-
end
-
1
require "monitor"
-
1
require "redis/errors"
-
-
1
class Redis
-
-
1
def self.deprecate(message, trace = caller[0])
-
$stderr.puts "\n#{message} (in #{trace})"
-
end
-
-
1
attr :client
-
-
# @deprecated The preferred way to create a new client object is using `#new`.
-
# This method does not actually establish a connection to Redis,
-
# in contrary to what you might expect.
-
1
def self.connect(options = {})
-
new(options)
-
end
-
-
1
def self.current
-
@current ||= Redis.new
-
end
-
-
1
def self.current=(redis)
-
@current = redis
-
end
-
-
1
include MonitorMixin
-
-
1
def initialize(options = {})
-
@options = options.dup
-
@original_client = @client = Client.new(options)
-
-
super() # Monitor#initialize
-
end
-
-
1
def synchronize
-
mon_synchronize { yield(@client) }
-
end
-
-
# Run code with the client reconnecting
-
1
def with_reconnect(val=true, &blk)
-
synchronize do |client|
-
client.with_reconnect(val, &blk)
-
end
-
end
-
-
# Run code without the client reconnecting
-
1
def without_reconnect(&blk)
-
with_reconnect(false, &blk)
-
end
-
-
# Test whether or not the client is connected
-
1
def connected?
-
@original_client.connected?
-
end
-
-
# Authenticate to the server.
-
#
-
# @param [String] password must match the password specified in the
-
# `requirepass` directive in the configuration file
-
# @return [String] `OK`
-
1
def auth(password)
-
synchronize do |client|
-
client.call([:auth, password])
-
end
-
end
-
-
# Change the selected database for the current connection.
-
#
-
# @param [Fixnum] db zero-based index of the DB to use (0 to 15)
-
# @return [String] `OK`
-
1
def select(db)
-
synchronize do |client|
-
client.db = db
-
client.call([:select, db])
-
end
-
end
-
-
# Ping the server.
-
#
-
# @return [String] `PONG`
-
1
def ping
-
synchronize do |client|
-
client.call([:ping])
-
end
-
end
-
-
# Echo the given string.
-
#
-
# @param [String] value
-
# @return [String]
-
1
def echo(value)
-
synchronize do |client|
-
client.call([:echo, value])
-
end
-
end
-
-
# Close the connection.
-
#
-
# @return [String] `OK`
-
1
def quit
-
synchronize do |client|
-
begin
-
client.call([:quit])
-
rescue ConnectionError
-
ensure
-
client.disconnect
-
end
-
end
-
end
-
-
# Asynchronously rewrite the append-only file.
-
#
-
# @return [String] `OK`
-
1
def bgrewriteaof
-
synchronize do |client|
-
client.call([:bgrewriteaof])
-
end
-
end
-
-
# Asynchronously save the dataset to disk.
-
#
-
# @return [String] `OK`
-
1
def bgsave
-
synchronize do |client|
-
client.call([:bgsave])
-
end
-
end
-
-
# Get or set server configuration parameters.
-
#
-
# @param [Symbol] action e.g. `:get`, `:set`, `:resetstat`
-
# @return [String, Hash] string reply, or hash when retrieving more than one
-
# property with `CONFIG GET`
-
1
def config(action, *args)
-
synchronize do |client|
-
client.call([:config, action] + args) do |reply|
-
if reply.kind_of?(Array) && action == :get
-
Hash[_pairify(reply)]
-
else
-
reply
-
end
-
end
-
end
-
end
-
-
# Return the number of keys in the selected database.
-
#
-
# @return [Fixnum]
-
1
def dbsize
-
synchronize do |client|
-
client.call([:dbsize])
-
end
-
end
-
-
1
def debug(*args)
-
synchronize do |client|
-
client.call([:debug] + args)
-
end
-
end
-
-
# Remove all keys from all databases.
-
#
-
# @return [String] `OK`
-
1
def flushall
-
synchronize do |client|
-
client.call([:flushall])
-
end
-
end
-
-
# Remove all keys from the current database.
-
#
-
# @return [String] `OK`
-
1
def flushdb
-
synchronize do |client|
-
client.call([:flushdb])
-
end
-
end
-
-
# Get information and statistics about the server.
-
#
-
# @param [String, Symbol] cmd e.g. "commandstats"
-
# @return [Hash<String, String>]
-
1
def info(cmd = nil)
-
synchronize do |client|
-
client.call([:info, cmd].compact) do |reply|
-
if reply.kind_of?(String)
-
reply = Hash[reply.split("\r\n").map do |line|
-
line.split(":", 2) unless line =~ /^(#|$)/
-
end.compact]
-
-
if cmd && cmd.to_s == "commandstats"
-
# Extract nested hashes for INFO COMMANDSTATS
-
reply = Hash[reply.map do |k, v|
-
v = v.split(",").map { |e| e.split("=") }
-
[k[/^cmdstat_(.*)$/, 1], Hash[v]]
-
end]
-
end
-
end
-
-
reply
-
end
-
end
-
end
-
-
# Get the UNIX time stamp of the last successful save to disk.
-
#
-
# @return [Fixnum]
-
1
def lastsave
-
synchronize do |client|
-
client.call([:lastsave])
-
end
-
end
-
-
# Listen for all requests received by the server in real time.
-
#
-
# There is no way to interrupt this command.
-
#
-
# @yield a block to be called for every line of output
-
# @yieldparam [String] line timestamp and command that was executed
-
1
def monitor(&block)
-
synchronize do |client|
-
client.call_loop([:monitor], &block)
-
end
-
end
-
-
# Synchronously save the dataset to disk.
-
#
-
# @return [String]
-
1
def save
-
synchronize do |client|
-
client.call([:save])
-
end
-
end
-
-
# Synchronously save the dataset to disk and then shut down the server.
-
1
def shutdown
-
synchronize do |client|
-
client.with_reconnect(false) do
-
begin
-
client.call([:shutdown])
-
rescue ConnectionError
-
# This means Redis has probably exited.
-
nil
-
end
-
end
-
end
-
end
-
-
# Make the server a slave of another instance, or promote it as master.
-
1
def slaveof(host, port)
-
synchronize do |client|
-
client.call([:slaveof, host, port])
-
end
-
end
-
-
# Interact with the slowlog (get, len, reset)
-
#
-
# @param [String] subcommand e.g. `get`, `len`, `reset`
-
# @param [Fixnum] length maximum number of entries to return
-
# @return [Array<String>, Fixnum, String] depends on subcommand
-
1
def slowlog(subcommand, length=nil)
-
synchronize do |client|
-
args = [:slowlog, subcommand]
-
args << length if length
-
client.call args
-
end
-
end
-
-
# Internal command used for replication.
-
1
def sync
-
synchronize do |client|
-
client.call([:sync])
-
end
-
end
-
-
# Return the server time.
-
#
-
# @example
-
# r.time # => [ 1333093196, 606806 ]
-
#
-
# @return [Array<Fixnum>] tuple of seconds since UNIX epoch and
-
# microseconds in the current second
-
1
def time
-
synchronize do |client|
-
client.call([:time]) do |reply|
-
reply.map(&:to_i) if reply
-
end
-
end
-
end
-
-
# Remove the expiration from a key.
-
#
-
# @param [String] key
-
# @return [Boolean] whether the timeout was removed or not
-
1
def persist(key)
-
synchronize do |client|
-
client.call([:persist, key], &_boolify)
-
end
-
end
-
-
# Set a key's time to live in seconds.
-
#
-
# @param [String] key
-
# @param [Fixnum] seconds time to live
-
# @return [Boolean] whether the timeout was set or not
-
1
def expire(key, seconds)
-
synchronize do |client|
-
client.call([:expire, key, seconds], &_boolify)
-
end
-
end
-
-
# Set the expiration for a key as a UNIX timestamp.
-
#
-
# @param [String] key
-
# @param [Fixnum] unix_time expiry time specified as a UNIX timestamp
-
# @return [Boolean] whether the timeout was set or not
-
1
def expireat(key, unix_time)
-
synchronize do |client|
-
client.call([:expireat, key, unix_time], &_boolify)
-
end
-
end
-
-
# Get the time to live (in seconds) for a key.
-
#
-
# @param [String] key
-
# @return [Fixnum] remaining time to live in seconds, or -1 if the
-
# key does not exist or does not have a timeout
-
1
def ttl(key)
-
synchronize do |client|
-
client.call([:ttl, key])
-
end
-
end
-
-
# Set a key's time to live in milliseconds.
-
#
-
# @param [String] key
-
# @param [Fixnum] milliseconds time to live
-
# @return [Boolean] whether the timeout was set or not
-
1
def pexpire(key, milliseconds)
-
synchronize do |client|
-
client.call([:pexpire, key, milliseconds], &_boolify)
-
end
-
end
-
-
# Set the expiration for a key as number of milliseconds from UNIX Epoch.
-
#
-
# @param [String] key
-
# @param [Fixnum] ms_unix_time expiry time specified as number of milliseconds from UNIX Epoch.
-
# @return [Boolean] whether the timeout was set or not
-
1
def pexpireat(key, ms_unix_time)
-
synchronize do |client|
-
client.call([:pexpireat, key, ms_unix_time], &_boolify)
-
end
-
end
-
-
# Get the time to live (in milliseconds) for a key.
-
#
-
# @param [String] key
-
# @return [Fixnum] remaining time to live in milliseconds, or -1 if the
-
# key does not exist or does not have a timeout
-
1
def pttl(key)
-
synchronize do |client|
-
client.call([:pttl, key])
-
end
-
end
-
-
# Return a serialized version of the value stored at a key.
-
#
-
# @param [String] key
-
# @return [String] serialized_value
-
1
def dump(key)
-
synchronize do |client|
-
client.call([:dump, key])
-
end
-
end
-
-
# Create a key using the serialized value, previously obtained using DUMP.
-
#
-
# @param [String] key
-
# @param [String] ttl
-
# @param [String] serialized_value
-
# @return `"OK"`
-
1
def restore(key, ttl, serialized_value)
-
synchronize do |client|
-
client.call([:restore, key, ttl, serialized_value])
-
end
-
end
-
-
# Transfer a key from the connected instance to another instance.
-
#
-
# @param [String] key
-
# @param [Hash] options
-
# - `:host => String`: host of instance to migrate to
-
# - `:port => Integer`: port of instance to migrate to
-
# - `:db => Integer`: database to migrate to (default: same as source)
-
# - `:timeout => Integer`: timeout (default: same as connection timeout)
-
# @return [String] `"OK"`
-
1
def migrate(key, options)
-
host = options[:host] || raise(RuntimeError, ":host not specified")
-
port = options[:port] || raise(RuntimeError, ":port not specified")
-
db = (options[:db] || client.db).to_i
-
timeout = (options[:timeout] || client.timeout).to_i
-
-
synchronize do |client|
-
client.call([:migrate, host, port, key, db, timeout])
-
end
-
end
-
-
# Delete one or more keys.
-
#
-
# @param [String, Array<String>] keys
-
# @return [Fixnum] number of keys that were deleted
-
1
def del(*keys)
-
synchronize do |client|
-
client.call([:del] + keys)
-
end
-
end
-
-
# Determine if a key exists.
-
#
-
# @param [String] key
-
# @return [Boolean]
-
1
def exists(key)
-
synchronize do |client|
-
client.call([:exists, key], &_boolify)
-
end
-
end
-
-
# Find all keys matching the given pattern.
-
#
-
# @param [String] pattern
-
# @return [Array<String>]
-
1
def keys(pattern = "*")
-
synchronize do |client|
-
client.call([:keys, pattern]) do |reply|
-
if reply.kind_of?(String)
-
reply.split(" ")
-
else
-
reply
-
end
-
end
-
end
-
end
-
-
# Move a key to another database.
-
#
-
# @example Move a key to another database
-
# redis.set "foo", "bar"
-
# # => "OK"
-
# redis.move "foo", 2
-
# # => true
-
# redis.exists "foo"
-
# # => false
-
# redis.select 2
-
# # => "OK"
-
# redis.exists "foo"
-
# # => true
-
# redis.get "foo"
-
# # => "bar"
-
#
-
# @param [String] key
-
# @param [Fixnum] db
-
# @return [Boolean] whether the key was moved or not
-
1
def move(key, db)
-
synchronize do |client|
-
client.call([:move, key, db], &_boolify)
-
end
-
end
-
-
1
def object(*args)
-
synchronize do |client|
-
client.call([:object] + args)
-
end
-
end
-
-
# Return a random key from the keyspace.
-
#
-
# @return [String]
-
1
def randomkey
-
synchronize do |client|
-
client.call([:randomkey])
-
end
-
end
-
-
# Rename a key. If the new key already exists it is overwritten.
-
#
-
# @param [String] old_name
-
# @param [String] new_name
-
# @return [String] `OK`
-
1
def rename(old_name, new_name)
-
synchronize do |client|
-
client.call([:rename, old_name, new_name])
-
end
-
end
-
-
# Rename a key, only if the new key does not exist.
-
#
-
# @param [String] old_name
-
# @param [String] new_name
-
# @return [Boolean] whether the key was renamed or not
-
1
def renamenx(old_name, new_name)
-
synchronize do |client|
-
client.call([:renamenx, old_name, new_name], &_boolify)
-
end
-
end
-
-
# Sort the elements in a list, set or sorted set.
-
#
-
# @example Retrieve the first 2 elements from an alphabetically sorted "list"
-
# redis.sort("list", :order => "alpha", :limit => [0, 2])
-
# # => ["a", "b"]
-
# @example Store an alphabetically descending list in "target"
-
# redis.sort("list", :order => "desc alpha", :store => "target")
-
# # => 26
-
#
-
# @param [String] key
-
# @param [Hash] options
-
# - `:by => String`: use external key to sort elements by
-
# - `:limit => [offset, count]`: skip `offset` elements, return a maximum
-
# of `count` elements
-
# - `:get => [String, Array<String>]`: single key or array of keys to
-
# retrieve per element in the result
-
# - `:order => String`: combination of `ASC`, `DESC` and optionally `ALPHA`
-
# - `:store => String`: key to store the result at
-
#
-
# @return [Array<String>, Array<Array<String>>, Fixnum]
-
# - when `:get` is not specified, or holds a single element, an array of elements
-
# - when `:get` is specified, and holds more than one element, an array of
-
# elements where every element is an array with the result for every
-
# element specified in `:get`
-
# - when `:store` is specified, the number of elements in the stored result
-
1
def sort(key, options = {})
-
args = []
-
-
by = options[:by]
-
args.concat(["BY", by]) if by
-
-
limit = options[:limit]
-
args.concat(["LIMIT"] + limit) if limit
-
-
get = Array(options[:get])
-
args.concat(["GET"].product(get).flatten) unless get.empty?
-
-
order = options[:order]
-
args.concat(order.split(" ")) if order
-
-
store = options[:store]
-
args.concat(["STORE", store]) if store
-
-
synchronize do |client|
-
client.call([:sort, key] + args) do |reply|
-
if get.size > 1 && !store
-
if reply
-
reply.each_slice(get.size).to_a
-
end
-
else
-
reply
-
end
-
end
-
end
-
end
-
-
# Determine the type stored at key.
-
#
-
# @param [String] key
-
# @return [String] `string`, `list`, `set`, `zset`, `hash` or `none`
-
1
def type(key)
-
synchronize do |client|
-
client.call([:type, key])
-
end
-
end
-
-
# Decrement the integer value of a key by one.
-
#
-
# @example
-
# redis.decr("value")
-
# # => 4
-
#
-
# @param [String] key
-
# @return [Fixnum] value after decrementing it
-
1
def decr(key)
-
synchronize do |client|
-
client.call([:decr, key])
-
end
-
end
-
-
# Decrement the integer value of a key by the given number.
-
#
-
# @example
-
# redis.decrby("value", 5)
-
# # => 0
-
#
-
# @param [String] key
-
# @param [Fixnum] decrement
-
# @return [Fixnum] value after decrementing it
-
1
def decrby(key, decrement)
-
synchronize do |client|
-
client.call([:decrby, key, decrement])
-
end
-
end
-
-
# Increment the integer value of a key by one.
-
#
-
# @example
-
# redis.incr("value")
-
# # => 6
-
#
-
# @param [String] key
-
# @return [Fixnum] value after incrementing it
-
1
def incr(key)
-
synchronize do |client|
-
client.call([:incr, key])
-
end
-
end
-
-
# Increment the integer value of a key by the given integer number.
-
#
-
# @example
-
# redis.incrby("value", 5)
-
# # => 10
-
#
-
# @param [String] key
-
# @param [Fixnum] increment
-
# @return [Fixnum] value after incrementing it
-
1
def incrby(key, increment)
-
synchronize do |client|
-
client.call([:incrby, key, increment])
-
end
-
end
-
-
# Increment the numeric value of a key by the given float number.
-
#
-
# @example
-
# redis.incrbyfloat("value", 1.23)
-
# # => 1.23
-
#
-
# @param [String] key
-
# @param [Float] increment
-
# @return [Float] value after incrementing it
-
1
def incrbyfloat(key, increment)
-
synchronize do |client|
-
client.call([:incrbyfloat, key, increment], &_floatify)
-
end
-
end
-
-
# Set the string value of a key.
-
#
-
# @param [String] key
-
# @param [String] value
-
# @param [Hash] options
-
# - `:ex => Fixnum`: Set the specified expire time, in seconds.
-
# - `:px => Fixnum`: Set the specified expire time, in milliseconds.
-
# - `:nx => true`: Only set the key if it does not already exist.
-
# - `:xx => true`: Only set the key if it already exist.
-
# @return [String, Boolean] `"OK"` or true, false if `:nx => true` or `:xx => true`
-
1
def set(key, value, options = {})
-
args = []
-
-
ex = options[:ex]
-
args.concat(["EX", ex]) if ex
-
-
px = options[:px]
-
args.concat(["PX", px]) if px
-
-
nx = options[:nx]
-
args.concat(["NX"]) if nx
-
-
xx = options[:xx]
-
args.concat(["XX"]) if xx
-
-
synchronize do |client|
-
if nx || xx
-
client.call([:set, key, value.to_s] + args, &_boolify_set)
-
else
-
client.call([:set, key, value.to_s] + args)
-
end
-
end
-
end
-
-
1
alias :[]= :set
-
-
# Set the time to live in seconds of a key.
-
#
-
# @param [String] key
-
# @param [Fixnum] ttl
-
# @param [String] value
-
# @return `"OK"`
-
1
def setex(key, ttl, value)
-
synchronize do |client|
-
client.call([:setex, key, ttl, value.to_s])
-
end
-
end
-
-
# Set the time to live in milliseconds of a key.
-
#
-
# @param [String] key
-
# @param [Fixnum] ttl
-
# @param [String] value
-
# @return `"OK"`
-
1
def psetex(key, ttl, value)
-
synchronize do |client|
-
client.call([:psetex, key, ttl, value.to_s])
-
end
-
end
-
-
# Set the value of a key, only if the key does not exist.
-
#
-
# @param [String] key
-
# @param [String] value
-
# @return [Boolean] whether the key was set or not
-
1
def setnx(key, value)
-
synchronize do |client|
-
client.call([:setnx, key, value.to_s], &_boolify)
-
end
-
end
-
-
# Set one or more values.
-
#
-
# @example
-
# redis.mset("key1", "v1", "key2", "v2")
-
# # => "OK"
-
#
-
# @param [Array<String>] args array of keys and values
-
# @return `"OK"`
-
#
-
# @see #mapped_mset
-
1
def mset(*args)
-
synchronize do |client|
-
client.call([:mset] + args)
-
end
-
end
-
-
# Set one or more values.
-
#
-
# @example
-
# redis.mapped_mset({ "f1" => "v1", "f2" => "v2" })
-
# # => "OK"
-
#
-
# @param [Hash] hash keys mapping to values
-
# @return `"OK"`
-
#
-
# @see #mset
-
1
def mapped_mset(hash)
-
mset(hash.to_a.flatten)
-
end
-
-
# Set one or more values, only if none of the keys exist.
-
#
-
# @example
-
# redis.msetnx("key1", "v1", "key2", "v2")
-
# # => true
-
#
-
# @param [Array<String>] args array of keys and values
-
# @return [Boolean] whether or not all values were set
-
#
-
# @see #mapped_msetnx
-
1
def msetnx(*args)
-
synchronize do |client|
-
client.call([:msetnx] + args, &_boolify)
-
end
-
end
-
-
# Set one or more values, only if none of the keys exist.
-
#
-
# @example
-
# redis.msetnx({ "key1" => "v1", "key2" => "v2" })
-
# # => true
-
#
-
# @param [Hash] hash keys mapping to values
-
# @return [Boolean] whether or not all values were set
-
#
-
# @see #msetnx
-
1
def mapped_msetnx(hash)
-
msetnx(hash.to_a.flatten)
-
end
-
-
# Get the value of a key.
-
#
-
# @param [String] key
-
# @return [String]
-
1
def get(key)
-
synchronize do |client|
-
client.call([:get, key])
-
end
-
end
-
-
1
alias :[] :get
-
-
# Get the values of all the given keys.
-
#
-
# @example
-
# redis.mget("key1", "key1")
-
# # => ["v1", "v2"]
-
#
-
# @param [Array<String>] keys
-
# @return [Array<String>] an array of values for the specified keys
-
#
-
# @see #mapped_mget
-
1
def mget(*keys, &blk)
-
synchronize do |client|
-
client.call([:mget] + keys, &blk)
-
end
-
end
-
-
# Get the values of all the given keys.
-
#
-
# @example
-
# redis.mapped_mget("key1", "key1")
-
# # => { "key1" => "v1", "key2" => "v2" }
-
#
-
# @param [Array<String>] keys array of keys
-
# @return [Hash] a hash mapping the specified keys to their values
-
#
-
# @see #mget
-
1
def mapped_mget(*keys)
-
mget(*keys) do |reply|
-
if reply.kind_of?(Array)
-
Hash[keys.zip(reply)]
-
else
-
reply
-
end
-
end
-
end
-
-
# Overwrite part of a string at key starting at the specified offset.
-
#
-
# @param [String] key
-
# @param [Fixnum] offset byte offset
-
# @param [String] value
-
# @return [Fixnum] length of the string after it was modified
-
1
def setrange(key, offset, value)
-
synchronize do |client|
-
client.call([:setrange, key, offset, value.to_s])
-
end
-
end
-
-
# Get a substring of the string stored at a key.
-
#
-
# @param [String] key
-
# @param [Fixnum] start zero-based start offset
-
# @param [Fixnum] stop zero-based end offset. Use -1 for representing
-
# the end of the string
-
# @return [Fixnum] `0` or `1`
-
1
def getrange(key, start, stop)
-
synchronize do |client|
-
client.call([:getrange, key, start, stop])
-
end
-
end
-
-
# Sets or clears the bit at offset in the string value stored at key.
-
#
-
# @param [String] key
-
# @param [Fixnum] offset bit offset
-
# @param [Fixnum] value bit value `0` or `1`
-
# @return [Fixnum] the original bit value stored at `offset`
-
1
def setbit(key, offset, value)
-
synchronize do |client|
-
client.call([:setbit, key, offset, value])
-
end
-
end
-
-
# Returns the bit value at offset in the string value stored at key.
-
#
-
# @param [String] key
-
# @param [Fixnum] offset bit offset
-
# @return [Fixnum] `0` or `1`
-
1
def getbit(key, offset)
-
synchronize do |client|
-
client.call([:getbit, key, offset])
-
end
-
end
-
-
# Append a value to a key.
-
#
-
# @param [String] key
-
# @param [String] value value to append
-
# @return [Fixnum] length of the string after appending
-
1
def append(key, value)
-
synchronize do |client|
-
client.call([:append, key, value])
-
end
-
end
-
-
# Count the number of set bits in a range of the string value stored at key.
-
#
-
# @param [String] key
-
# @param [Fixnum] start start index
-
# @param [Fixnum] stop stop index
-
# @return [Fixnum] the number of bits set to 1
-
1
def bitcount(key, start = 0, stop = -1)
-
synchronize do |client|
-
client.call([:bitcount, key, start, stop])
-
end
-
end
-
-
# Perform a bitwise operation between strings and store the resulting string in a key.
-
#
-
# @param [String] operation e.g. `and`, `or`, `xor`, `not`
-
# @param [String] destkey destination key
-
# @param [String, Array<String>] keys one or more source keys to perform `operation`
-
# @return [Fixnum] the length of the string stored in `destkey`
-
1
def bitop(operation, destkey, *keys)
-
synchronize do |client|
-
client.call([:bitop, operation, destkey] + keys)
-
end
-
end
-
-
# Return the position of the first bit set to 1 or 0 in a string.
-
#
-
# @param [String] key
-
# @param [Fixnum] bit whether to look for the first 1 or 0 bit
-
# @param [Fixnum] start start index
-
# @param [Fixnum] stop stop index
-
# @return [Fixnum] the position of the first 1/0 bit.
-
# -1 if looking for 1 and it is not found or start and stop are given.
-
1
def bitpos(key, bit, start=nil, stop=nil)
-
if stop and not start
-
raise(ArgumentError, 'stop parameter specified without start parameter')
-
end
-
-
synchronize do |client|
-
command = [:bitpos, key, bit]
-
command << start if start
-
command << stop if stop
-
client.call(command)
-
end
-
end
-
-
# Set the string value of a key and return its old value.
-
#
-
# @param [String] key
-
# @param [String] value value to replace the current value with
-
# @return [String] the old value stored in the key, or `nil` if the key
-
# did not exist
-
1
def getset(key, value)
-
synchronize do |client|
-
client.call([:getset, key, value.to_s])
-
end
-
end
-
-
# Get the length of the value stored in a key.
-
#
-
# @param [String] key
-
# @return [Fixnum] the length of the value stored in the key, or 0
-
# if the key does not exist
-
1
def strlen(key)
-
synchronize do |client|
-
client.call([:strlen, key])
-
end
-
end
-
-
# Get the length of a list.
-
#
-
# @param [String] key
-
# @return [Fixnum]
-
1
def llen(key)
-
synchronize do |client|
-
client.call([:llen, key])
-
end
-
end
-
-
# Prepend one or more values to a list, creating the list if it doesn't exist
-
#
-
# @param [String] key
-
# @param [String, Array] string value, or array of string values to push
-
# @return [Fixnum] the length of the list after the push operation
-
1
def lpush(key, value)
-
synchronize do |client|
-
client.call([:lpush, key, value])
-
end
-
end
-
-
# Prepend a value to a list, only if the list exists.
-
#
-
# @param [String] key
-
# @param [String] value
-
# @return [Fixnum] the length of the list after the push operation
-
1
def lpushx(key, value)
-
synchronize do |client|
-
client.call([:lpushx, key, value])
-
end
-
end
-
-
# Append one or more values to a list, creating the list if it doesn't exist
-
#
-
# @param [String] key
-
# @param [String] value
-
# @return [Fixnum] the length of the list after the push operation
-
1
def rpush(key, value)
-
synchronize do |client|
-
client.call([:rpush, key, value])
-
end
-
end
-
-
# Append a value to a list, only if the list exists.
-
#
-
# @param [String] key
-
# @param [String] value
-
# @return [Fixnum] the length of the list after the push operation
-
1
def rpushx(key, value)
-
synchronize do |client|
-
client.call([:rpushx, key, value])
-
end
-
end
-
-
# Remove and get the first element in a list.
-
#
-
# @param [String] key
-
# @return [String]
-
1
def lpop(key)
-
synchronize do |client|
-
client.call([:lpop, key])
-
end
-
end
-
-
# Remove and get the last element in a list.
-
#
-
# @param [String] key
-
# @return [String]
-
1
def rpop(key)
-
synchronize do |client|
-
client.call([:rpop, key])
-
end
-
end
-
-
# Remove the last element in a list, append it to another list and return it.
-
#
-
# @param [String] source source key
-
# @param [String] destination destination key
-
# @return [nil, String] the element, or nil when the source key does not exist
-
1
def rpoplpush(source, destination)
-
synchronize do |client|
-
client.call([:rpoplpush, source, destination])
-
end
-
end
-
-
1
def _bpop(cmd, args)
-
options = {}
-
-
case args.last
-
when Hash
-
options = args.pop
-
when Integer
-
# Issue deprecation notice in obnoxious mode...
-
options[:timeout] = args.pop
-
end
-
-
if args.size > 1
-
# Issue deprecation notice in obnoxious mode...
-
end
-
-
keys = args.flatten
-
timeout = options[:timeout] || 0
-
-
synchronize do |client|
-
command = [cmd, keys, timeout]
-
timeout += client.timeout if timeout > 0
-
client.call_with_timeout(command, timeout)
-
end
-
end
-
-
# Remove and get the first element in a list, or block until one is available.
-
#
-
# @example With timeout
-
# list, element = redis.blpop("list", :timeout => 5)
-
# # => nil on timeout
-
# # => ["list", "element"] on success
-
# @example Without timeout
-
# list, element = redis.blpop("list")
-
# # => ["list", "element"]
-
# @example Blocking pop on multiple lists
-
# list, element = redis.blpop(["list", "another_list"])
-
# # => ["list", "element"]
-
#
-
# @param [String, Array<String>] keys one or more keys to perform the
-
# blocking pop on
-
# @param [Hash] options
-
# - `:timeout => Fixnum`: timeout in seconds, defaults to no timeout
-
#
-
# @return [nil, [String, String]]
-
# - `nil` when the operation timed out
-
# - tuple of the list that was popped from and element was popped otherwise
-
1
def blpop(*args)
-
_bpop(:blpop, args)
-
end
-
-
# Remove and get the last element in a list, or block until one is available.
-
#
-
# @param [String, Array<String>] keys one or more keys to perform the
-
# blocking pop on
-
# @param [Hash] options
-
# - `:timeout => Fixnum`: timeout in seconds, defaults to no timeout
-
#
-
# @return [nil, [String, String]]
-
# - `nil` when the operation timed out
-
# - tuple of the list that was popped from and element was popped otherwise
-
#
-
# @see #blpop
-
1
def brpop(*args)
-
_bpop(:brpop, args)
-
end
-
-
# Pop a value from a list, push it to another list and return it; or block
-
# until one is available.
-
#
-
# @param [String] source source key
-
# @param [String] destination destination key
-
# @param [Hash] options
-
# - `:timeout => Fixnum`: timeout in seconds, defaults to no timeout
-
#
-
# @return [nil, String]
-
# - `nil` when the operation timed out
-
# - the element was popped and pushed otherwise
-
1
def brpoplpush(source, destination, options = {})
-
case options
-
when Integer
-
# Issue deprecation notice in obnoxious mode...
-
options = { :timeout => options }
-
end
-
-
timeout = options[:timeout] || 0
-
-
synchronize do |client|
-
command = [:brpoplpush, source, destination, timeout]
-
timeout += client.timeout if timeout > 0
-
client.call_with_timeout(command, timeout)
-
end
-
end
-
-
# Get an element from a list by its index.
-
#
-
# @param [String] key
-
# @param [Fixnum] index
-
# @return [String]
-
1
def lindex(key, index)
-
synchronize do |client|
-
client.call([:lindex, key, index])
-
end
-
end
-
-
# Insert an element before or after another element in a list.
-
#
-
# @param [String] key
-
# @param [String, Symbol] where `BEFORE` or `AFTER`
-
# @param [String] pivot reference element
-
# @param [String] value
-
# @return [Fixnum] length of the list after the insert operation, or `-1`
-
# when the element `pivot` was not found
-
1
def linsert(key, where, pivot, value)
-
synchronize do |client|
-
client.call([:linsert, key, where, pivot, value])
-
end
-
end
-
-
# Get a range of elements from a list.
-
#
-
# @param [String] key
-
# @param [Fixnum] start start index
-
# @param [Fixnum] stop stop index
-
# @return [Array<String>]
-
1
def lrange(key, start, stop)
-
synchronize do |client|
-
client.call([:lrange, key, start, stop])
-
end
-
end
-
-
# Remove elements from a list.
-
#
-
# @param [String] key
-
# @param [Fixnum] count number of elements to remove. Use a positive
-
# value to remove the first `count` occurrences of `value`. A negative
-
# value to remove the last `count` occurrences of `value`. Or zero, to
-
# remove all occurrences of `value` from the list.
-
# @param [String] value
-
# @return [Fixnum] the number of removed elements
-
1
def lrem(key, count, value)
-
synchronize do |client|
-
client.call([:lrem, key, count, value])
-
end
-
end
-
-
# Set the value of an element in a list by its index.
-
#
-
# @param [String] key
-
# @param [Fixnum] index
-
# @param [String] value
-
# @return [String] `OK`
-
1
def lset(key, index, value)
-
synchronize do |client|
-
client.call([:lset, key, index, value])
-
end
-
end
-
-
# Trim a list to the specified range.
-
#
-
# @param [String] key
-
# @param [Fixnum] start start index
-
# @param [Fixnum] stop stop index
-
# @return [String] `OK`
-
1
def ltrim(key, start, stop)
-
synchronize do |client|
-
client.call([:ltrim, key, start, stop])
-
end
-
end
-
-
# Get the number of members in a set.
-
#
-
# @param [String] key
-
# @return [Fixnum]
-
1
def scard(key)
-
synchronize do |client|
-
client.call([:scard, key])
-
end
-
end
-
-
# Add one or more members to a set.
-
#
-
# @param [String] key
-
# @param [String, Array<String>] member one member, or array of members
-
# @return [Boolean, Fixnum] `Boolean` when a single member is specified,
-
# holding whether or not adding the member succeeded, or `Fixnum` when an
-
# array of members is specified, holding the number of members that were
-
# successfully added
-
1
def sadd(key, member)
-
synchronize do |client|
-
client.call([:sadd, key, member]) do |reply|
-
if member.is_a? Array
-
# Variadic: return integer
-
reply
-
else
-
# Single argument: return boolean
-
_boolify.call(reply)
-
end
-
end
-
end
-
end
-
-
# Remove one or more members from a set.
-
#
-
# @param [String] key
-
# @param [String, Array<String>] member one member, or array of members
-
# @return [Boolean, Fixnum] `Boolean` when a single member is specified,
-
# holding whether or not removing the member succeeded, or `Fixnum` when an
-
# array of members is specified, holding the number of members that were
-
# successfully removed
-
1
def srem(key, member)
-
synchronize do |client|
-
client.call([:srem, key, member]) do |reply|
-
if member.is_a? Array
-
# Variadic: return integer
-
reply
-
else
-
# Single argument: return boolean
-
_boolify.call(reply)
-
end
-
end
-
end
-
end
-
-
# Remove and return a random member from a set.
-
#
-
# @param [String] key
-
# @return [String]
-
1
def spop(key)
-
synchronize do |client|
-
client.call([:spop, key])
-
end
-
end
-
-
# Get one or more random members from a set.
-
#
-
# @param [String] key
-
# @param [Fixnum] count
-
# @return [String]
-
1
def srandmember(key, count = nil)
-
synchronize do |client|
-
if count.nil?
-
client.call([:srandmember, key])
-
else
-
client.call([:srandmember, key, count])
-
end
-
end
-
end
-
-
# Move a member from one set to another.
-
#
-
# @param [String] source source key
-
# @param [String] destination destination key
-
# @param [String] member member to move from `source` to `destination`
-
# @return [Boolean]
-
1
def smove(source, destination, member)
-
synchronize do |client|
-
client.call([:smove, source, destination, member], &_boolify)
-
end
-
end
-
-
# Determine if a given value is a member of a set.
-
#
-
# @param [String] key
-
# @param [String] member
-
# @return [Boolean]
-
1
def sismember(key, member)
-
synchronize do |client|
-
client.call([:sismember, key, member], &_boolify)
-
end
-
end
-
-
# Get all the members in a set.
-
#
-
# @param [String] key
-
# @return [Array<String>]
-
1
def smembers(key)
-
synchronize do |client|
-
client.call([:smembers, key])
-
end
-
end
-
-
# Subtract multiple sets.
-
#
-
# @param [String, Array<String>] keys keys pointing to sets to subtract
-
# @return [Array<String>] members in the difference
-
1
def sdiff(*keys)
-
synchronize do |client|
-
client.call([:sdiff] + keys)
-
end
-
end
-
-
# Subtract multiple sets and store the resulting set in a key.
-
#
-
# @param [String] destination destination key
-
# @param [String, Array<String>] keys keys pointing to sets to subtract
-
# @return [Fixnum] number of elements in the resulting set
-
1
def sdiffstore(destination, *keys)
-
synchronize do |client|
-
client.call([:sdiffstore, destination] + keys)
-
end
-
end
-
-
# Intersect multiple sets.
-
#
-
# @param [String, Array<String>] keys keys pointing to sets to intersect
-
# @return [Array<String>] members in the intersection
-
1
def sinter(*keys)
-
synchronize do |client|
-
client.call([:sinter] + keys)
-
end
-
end
-
-
# Intersect multiple sets and store the resulting set in a key.
-
#
-
# @param [String] destination destination key
-
# @param [String, Array<String>] keys keys pointing to sets to intersect
-
# @return [Fixnum] number of elements in the resulting set
-
1
def sinterstore(destination, *keys)
-
synchronize do |client|
-
client.call([:sinterstore, destination] + keys)
-
end
-
end
-
-
# Add multiple sets.
-
#
-
# @param [String, Array<String>] keys keys pointing to sets to unify
-
# @return [Array<String>] members in the union
-
1
def sunion(*keys)
-
synchronize do |client|
-
client.call([:sunion] + keys)
-
end
-
end
-
-
# Add multiple sets and store the resulting set in a key.
-
#
-
# @param [String] destination destination key
-
# @param [String, Array<String>] keys keys pointing to sets to unify
-
# @return [Fixnum] number of elements in the resulting set
-
1
def sunionstore(destination, *keys)
-
synchronize do |client|
-
client.call([:sunionstore, destination] + keys)
-
end
-
end
-
-
# Get the number of members in a sorted set.
-
#
-
# @example
-
# redis.zcard("zset")
-
# # => 4
-
#
-
# @param [String] key
-
# @return [Fixnum]
-
1
def zcard(key)
-
synchronize do |client|
-
client.call([:zcard, key])
-
end
-
end
-
-
# Add one or more members to a sorted set, or update the score for members
-
# that already exist.
-
#
-
# @example Add a single `[score, member]` pair to a sorted set
-
# redis.zadd("zset", 32.0, "member")
-
# @example Add an array of `[score, member]` pairs to a sorted set
-
# redis.zadd("zset", [[32.0, "a"], [64.0, "b"]])
-
#
-
# @param [String] key
-
# @param [[Float, String], Array<[Float, String]>] args
-
# - a single `[score, member]` pair
-
# - an array of `[score, member]` pairs
-
#
-
# @return [Boolean, Fixnum]
-
# - `Boolean` when a single pair is specified, holding whether or not it was
-
# **added** to the sorted set
-
# - `Fixnum` when an array of pairs is specified, holding the number of
-
# pairs that were **added** to the sorted set
-
1
def zadd(key, *args)
-
synchronize do |client|
-
if args.size == 1 && args[0].is_a?(Array)
-
# Variadic: return integer
-
client.call([:zadd, key] + args[0])
-
elsif args.size == 2
-
# Single pair: return boolean
-
client.call([:zadd, key, args[0], args[1]], &_boolify)
-
else
-
raise ArgumentError, "wrong number of arguments"
-
end
-
end
-
end
-
-
# Increment the score of a member in a sorted set.
-
#
-
# @example
-
# redis.zincrby("zset", 32.0, "a")
-
# # => 64.0
-
#
-
# @param [String] key
-
# @param [Float] increment
-
# @param [String] member
-
# @return [Float] score of the member after incrementing it
-
1
def zincrby(key, increment, member)
-
synchronize do |client|
-
client.call([:zincrby, key, increment, member], &_floatify)
-
end
-
end
-
-
# Remove one or more members from a sorted set.
-
#
-
# @example Remove a single member from a sorted set
-
# redis.zrem("zset", "a")
-
# @example Remove an array of members from a sorted set
-
# redis.zrem("zset", ["a", "b"])
-
#
-
# @param [String] key
-
# @param [String, Array<String>] member
-
# - a single member
-
# - an array of members
-
#
-
# @return [Boolean, Fixnum]
-
# - `Boolean` when a single member is specified, holding whether or not it
-
# was removed from the sorted set
-
# - `Fixnum` when an array of pairs is specified, holding the number of
-
# members that were removed to the sorted set
-
1
def zrem(key, member)
-
synchronize do |client|
-
client.call([:zrem, key, member]) do |reply|
-
if member.is_a? Array
-
# Variadic: return integer
-
reply
-
else
-
# Single argument: return boolean
-
_boolify.call(reply)
-
end
-
end
-
end
-
end
-
-
# Get the score associated with the given member in a sorted set.
-
#
-
# @example Get the score for member "a"
-
# redis.zscore("zset", "a")
-
# # => 32.0
-
#
-
# @param [String] key
-
# @param [String] member
-
# @return [Float] score of the member
-
1
def zscore(key, member)
-
synchronize do |client|
-
client.call([:zscore, key, member], &_floatify)
-
end
-
end
-
-
# Return a range of members in a sorted set, by index.
-
#
-
# @example Retrieve all members from a sorted set
-
# redis.zrange("zset", 0, -1)
-
# # => ["a", "b"]
-
# @example Retrieve all members and their scores from a sorted set
-
# redis.zrange("zset", 0, -1, :with_scores => true)
-
# # => [["a", 32.0], ["b", 64.0]]
-
#
-
# @param [String] key
-
# @param [Fixnum] start start index
-
# @param [Fixnum] stop stop index
-
# @param [Hash] options
-
# - `:with_scores => true`: include scores in output
-
#
-
# @return [Array<String>, Array<[String, Float]>]
-
# - when `:with_scores` is not specified, an array of members
-
# - when `:with_scores` is specified, an array with `[member, score]` pairs
-
1
def zrange(key, start, stop, options = {})
-
args = []
-
-
with_scores = options[:with_scores] || options[:withscores]
-
-
if with_scores
-
args << "WITHSCORES"
-
block = _floatify_pairs
-
end
-
-
synchronize do |client|
-
client.call([:zrange, key, start, stop] + args, &block)
-
end
-
end
-
-
# Return a range of members in a sorted set, by index, with scores ordered
-
# from high to low.
-
#
-
# @example Retrieve all members from a sorted set
-
# redis.zrevrange("zset", 0, -1)
-
# # => ["b", "a"]
-
# @example Retrieve all members and their scores from a sorted set
-
# redis.zrevrange("zset", 0, -1, :with_scores => true)
-
# # => [["b", 64.0], ["a", 32.0]]
-
#
-
# @see #zrange
-
1
def zrevrange(key, start, stop, options = {})
-
args = []
-
-
with_scores = options[:with_scores] || options[:withscores]
-
-
if with_scores
-
args << "WITHSCORES"
-
block = _floatify_pairs
-
end
-
-
synchronize do |client|
-
client.call([:zrevrange, key, start, stop] + args, &block)
-
end
-
end
-
-
# Determine the index of a member in a sorted set.
-
#
-
# @param [String] key
-
# @param [String] member
-
# @return [Fixnum]
-
1
def zrank(key, member)
-
synchronize do |client|
-
client.call([:zrank, key, member])
-
end
-
end
-
-
# Determine the index of a member in a sorted set, with scores ordered from
-
# high to low.
-
#
-
# @param [String] key
-
# @param [String] member
-
# @return [Fixnum]
-
1
def zrevrank(key, member)
-
synchronize do |client|
-
client.call([:zrevrank, key, member])
-
end
-
end
-
-
# Remove all members in a sorted set within the given indexes.
-
#
-
# @example Remove first 5 members
-
# redis.zremrangebyrank("zset", 0, 4)
-
# # => 5
-
# @example Remove last 5 members
-
# redis.zremrangebyrank("zset", -5, -1)
-
# # => 5
-
#
-
# @param [String] key
-
# @param [Fixnum] start start index
-
# @param [Fixnum] stop stop index
-
# @return [Fixnum] number of members that were removed
-
1
def zremrangebyrank(key, start, stop)
-
synchronize do |client|
-
client.call([:zremrangebyrank, key, start, stop])
-
end
-
end
-
-
# Return a range of members in a sorted set, by score.
-
#
-
# @example Retrieve members with score `>= 5` and `< 100`
-
# redis.zrangebyscore("zset", "5", "(100")
-
# # => ["a", "b"]
-
# @example Retrieve the first 2 members with score `>= 0`
-
# redis.zrangebyscore("zset", "0", "+inf", :limit => [0, 2])
-
# # => ["a", "b"]
-
# @example Retrieve members and their scores with scores `> 5`
-
# redis.zrangebyscore("zset", "(5", "+inf", :with_scores => true)
-
# # => [["a", 32.0], ["b", 64.0]]
-
#
-
# @param [String] key
-
# @param [String] min
-
# - inclusive minimum score is specified verbatim
-
# - exclusive minimum score is specified by prefixing `(`
-
# @param [String] max
-
# - inclusive maximum score is specified verbatim
-
# - exclusive maximum score is specified by prefixing `(`
-
# @param [Hash] options
-
# - `:with_scores => true`: include scores in output
-
# - `:limit => [offset, count]`: skip `offset` members, return a maximum of
-
# `count` members
-
#
-
# @return [Array<String>, Array<[String, Float]>]
-
# - when `:with_scores` is not specified, an array of members
-
# - when `:with_scores` is specified, an array with `[member, score]` pairs
-
1
def zrangebyscore(key, min, max, options = {})
-
args = []
-
-
with_scores = options[:with_scores] || options[:withscores]
-
-
if with_scores
-
args << "WITHSCORES"
-
block = _floatify_pairs
-
end
-
-
limit = options[:limit]
-
args.concat(["LIMIT"] + limit) if limit
-
-
synchronize do |client|
-
client.call([:zrangebyscore, key, min, max] + args, &block)
-
end
-
end
-
-
# Return a range of members in a sorted set, by score, with scores ordered
-
# from high to low.
-
#
-
# @example Retrieve members with score `< 100` and `>= 5`
-
# redis.zrevrangebyscore("zset", "(100", "5")
-
# # => ["b", "a"]
-
# @example Retrieve the first 2 members with score `<= 0`
-
# redis.zrevrangebyscore("zset", "0", "-inf", :limit => [0, 2])
-
# # => ["b", "a"]
-
# @example Retrieve members and their scores with scores `> 5`
-
# redis.zrevrangebyscore("zset", "+inf", "(5", :with_scores => true)
-
# # => [["b", 64.0], ["a", 32.0]]
-
#
-
# @see #zrangebyscore
-
1
def zrevrangebyscore(key, max, min, options = {})
-
args = []
-
-
with_scores = options[:with_scores] || options[:withscores]
-
-
if with_scores
-
args << ["WITHSCORES"]
-
block = _floatify_pairs
-
end
-
-
limit = options[:limit]
-
args.concat(["LIMIT"] + limit) if limit
-
-
synchronize do |client|
-
client.call([:zrevrangebyscore, key, max, min] + args, &block)
-
end
-
end
-
-
# Remove all members in a sorted set within the given scores.
-
#
-
# @example Remove members with score `>= 5` and `< 100`
-
# redis.zremrangebyscore("zset", "5", "(100")
-
# # => 2
-
# @example Remove members with scores `> 5`
-
# redis.zremrangebyscore("zset", "(5", "+inf")
-
# # => 2
-
#
-
# @param [String] key
-
# @param [String] min
-
# - inclusive minimum score is specified verbatim
-
# - exclusive minimum score is specified by prefixing `(`
-
# @param [String] max
-
# - inclusive maximum score is specified verbatim
-
# - exclusive maximum score is specified by prefixing `(`
-
# @return [Fixnum] number of members that were removed
-
1
def zremrangebyscore(key, min, max)
-
synchronize do |client|
-
client.call([:zremrangebyscore, key, min, max])
-
end
-
end
-
-
# Count the members in a sorted set with scores within the given values.
-
#
-
# @example Count members with score `>= 5` and `< 100`
-
# redis.zcount("zset", "5", "(100")
-
# # => 2
-
# @example Count members with scores `> 5`
-
# redis.zcount("zset", "(5", "+inf")
-
# # => 2
-
#
-
# @param [String] key
-
# @param [String] min
-
# - inclusive minimum score is specified verbatim
-
# - exclusive minimum score is specified by prefixing `(`
-
# @param [String] max
-
# - inclusive maximum score is specified verbatim
-
# - exclusive maximum score is specified by prefixing `(`
-
# @return [Fixnum] number of members in within the specified range
-
1
def zcount(key, min, max)
-
synchronize do |client|
-
client.call([:zcount, key, min, max])
-
end
-
end
-
-
# Intersect multiple sorted sets and store the resulting sorted set in a new
-
# key.
-
#
-
# @example Compute the intersection of `2*zsetA` with `1*zsetB`, summing their scores
-
# redis.zinterstore("zsetC", ["zsetA", "zsetB"], :weights => [2.0, 1.0], :aggregate => "sum")
-
# # => 4
-
#
-
# @param [String] destination destination key
-
# @param [Array<String>] keys source keys
-
# @param [Hash] options
-
# - `:weights => [Float, Float, ...]`: weights to associate with source
-
# sorted sets
-
# - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
-
# @return [Fixnum] number of elements in the resulting sorted set
-
1
def zinterstore(destination, keys, options = {})
-
args = []
-
-
weights = options[:weights]
-
args.concat(["WEIGHTS"] + weights) if weights
-
-
aggregate = options[:aggregate]
-
args.concat(["AGGREGATE", aggregate]) if aggregate
-
-
synchronize do |client|
-
client.call([:zinterstore, destination, keys.size] + keys + args)
-
end
-
end
-
-
# Add multiple sorted sets and store the resulting sorted set in a new key.
-
#
-
# @example Compute the union of `2*zsetA` with `1*zsetB`, summing their scores
-
# redis.zunionstore("zsetC", ["zsetA", "zsetB"], :weights => [2.0, 1.0], :aggregate => "sum")
-
# # => 8
-
#
-
# @param [String] destination destination key
-
# @param [Array<String>] keys source keys
-
# @param [Hash] options
-
# - `:weights => [Float, Float, ...]`: weights to associate with source
-
# sorted sets
-
# - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
-
# @return [Fixnum] number of elements in the resulting sorted set
-
1
def zunionstore(destination, keys, options = {})
-
args = []
-
-
weights = options[:weights]
-
args.concat(["WEIGHTS"] + weights) if weights
-
-
aggregate = options[:aggregate]
-
args.concat(["AGGREGATE", aggregate]) if aggregate
-
-
synchronize do |client|
-
client.call([:zunionstore, destination, keys.size] + keys + args)
-
end
-
end
-
-
# Get the number of fields in a hash.
-
#
-
# @param [String] key
-
# @return [Fixnum] number of fields in the hash
-
1
def hlen(key)
-
synchronize do |client|
-
client.call([:hlen, key])
-
end
-
end
-
-
# Set the string value of a hash field.
-
#
-
# @param [String] key
-
# @param [String] field
-
# @param [String] value
-
# @return [Boolean] whether or not the field was **added** to the hash
-
1
def hset(key, field, value)
-
synchronize do |client|
-
client.call([:hset, key, field, value], &_boolify)
-
end
-
end
-
-
# Set the value of a hash field, only if the field does not exist.
-
#
-
# @param [String] key
-
# @param [String] field
-
# @param [String] value
-
# @return [Boolean] whether or not the field was **added** to the hash
-
1
def hsetnx(key, field, value)
-
synchronize do |client|
-
client.call([:hsetnx, key, field, value], &_boolify)
-
end
-
end
-
-
# Set one or more hash values.
-
#
-
# @example
-
# redis.hmset("hash", "f1", "v1", "f2", "v2")
-
# # => "OK"
-
#
-
# @param [String] key
-
# @param [Array<String>] attrs array of fields and values
-
# @return `"OK"`
-
#
-
# @see #mapped_hmset
-
1
def hmset(key, *attrs)
-
synchronize do |client|
-
client.call([:hmset, key] + attrs)
-
end
-
end
-
-
# Set one or more hash values.
-
#
-
# @example
-
# redis.mapped_hmset("hash", { "f1" => "v1", "f2" => "v2" })
-
# # => "OK"
-
#
-
# @param [String] key
-
# @param [Hash] hash fields mapping to values
-
# @return `"OK"`
-
#
-
# @see #hmset
-
1
def mapped_hmset(key, hash)
-
hmset(key, hash.to_a.flatten)
-
end
-
-
# Get the value of a hash field.
-
#
-
# @param [String] key
-
# @param [String] field
-
# @return [String]
-
1
def hget(key, field)
-
synchronize do |client|
-
client.call([:hget, key, field])
-
end
-
end
-
-
# Get the values of all the given hash fields.
-
#
-
# @example
-
# redis.hmget("hash", "f1", "f2")
-
# # => ["v1", "v2"]
-
#
-
# @param [String] key
-
# @param [Array<String>] fields array of fields
-
# @return [Array<String>] an array of values for the specified fields
-
#
-
# @see #mapped_hmget
-
1
def hmget(key, *fields, &blk)
-
synchronize do |client|
-
client.call([:hmget, key] + fields, &blk)
-
end
-
end
-
-
# Get the values of all the given hash fields.
-
#
-
# @example
-
# redis.hmget("hash", "f1", "f2")
-
# # => { "f1" => "v1", "f2" => "v2" }
-
#
-
# @param [String] key
-
# @param [Array<String>] fields array of fields
-
# @return [Hash] a hash mapping the specified fields to their values
-
#
-
# @see #hmget
-
1
def mapped_hmget(key, *fields)
-
hmget(key, *fields) do |reply|
-
if reply.kind_of?(Array)
-
Hash[fields.zip(reply)]
-
else
-
reply
-
end
-
end
-
end
-
-
# Delete one or more hash fields.
-
#
-
# @param [String] key
-
# @param [String, Array<String>] field
-
# @return [Fixnum] the number of fields that were removed from the hash
-
1
def hdel(key, field)
-
synchronize do |client|
-
client.call([:hdel, key, field])
-
end
-
end
-
-
# Determine if a hash field exists.
-
#
-
# @param [String] key
-
# @param [String] field
-
# @return [Boolean] whether or not the field exists in the hash
-
1
def hexists(key, field)
-
synchronize do |client|
-
client.call([:hexists, key, field], &_boolify)
-
end
-
end
-
-
# Increment the integer value of a hash field by the given integer number.
-
#
-
# @param [String] key
-
# @param [String] field
-
# @param [Fixnum] increment
-
# @return [Fixnum] value of the field after incrementing it
-
1
def hincrby(key, field, increment)
-
synchronize do |client|
-
client.call([:hincrby, key, field, increment])
-
end
-
end
-
-
# Increment the numeric value of a hash field by the given float number.
-
#
-
# @param [String] key
-
# @param [String] field
-
# @param [Float] increment
-
# @return [Float] value of the field after incrementing it
-
1
def hincrbyfloat(key, field, increment)
-
synchronize do |client|
-
client.call([:hincrbyfloat, key, field, increment], &_floatify)
-
end
-
end
-
-
# Get all the fields in a hash.
-
#
-
# @param [String] key
-
# @return [Array<String>]
-
1
def hkeys(key)
-
synchronize do |client|
-
client.call([:hkeys, key])
-
end
-
end
-
-
# Get all the values in a hash.
-
#
-
# @param [String] key
-
# @return [Array<String>]
-
1
def hvals(key)
-
synchronize do |client|
-
client.call([:hvals, key])
-
end
-
end
-
-
# Get all the fields and values in a hash.
-
#
-
# @param [String] key
-
# @return [Hash<String, String>]
-
1
def hgetall(key)
-
synchronize do |client|
-
client.call([:hgetall, key], &_hashify)
-
end
-
end
-
-
# Post a message to a channel.
-
1
def publish(channel, message)
-
synchronize do |client|
-
client.call([:publish, channel, message])
-
end
-
end
-
-
1
def subscribed?
-
synchronize do |client|
-
client.kind_of? SubscribedClient
-
end
-
end
-
-
# Listen for messages published to the given channels.
-
1
def subscribe(*channels, &block)
-
synchronize do |client|
-
_subscription(:subscribe, channels, block)
-
end
-
end
-
-
# Stop listening for messages posted to the given channels.
-
1
def unsubscribe(*channels)
-
synchronize do |client|
-
raise RuntimeError, "Can't unsubscribe if not subscribed." unless subscribed?
-
client.unsubscribe(*channels)
-
end
-
end
-
-
# Listen for messages published to channels matching the given patterns.
-
1
def psubscribe(*channels, &block)
-
synchronize do |client|
-
_subscription(:psubscribe, channels, block)
-
end
-
end
-
-
# Stop listening for messages posted to channels matching the given patterns.
-
1
def punsubscribe(*channels)
-
synchronize do |client|
-
raise RuntimeError, "Can't unsubscribe if not subscribed." unless subscribed?
-
client.punsubscribe(*channels)
-
end
-
end
-
-
# Watch the given keys to determine execution of the MULTI/EXEC block.
-
#
-
# Using a block is optional, but is necessary for thread-safety.
-
#
-
# An `#unwatch` is automatically issued if an exception is raised within the
-
# block that is a subclass of StandardError and is not a ConnectionError.
-
#
-
# @example With a block
-
# redis.watch("key") do
-
# if redis.get("key") == "some value"
-
# redis.multi do |multi|
-
# multi.set("key", "other value")
-
# multi.incr("counter")
-
# end
-
# else
-
# redis.unwatch
-
# end
-
# end
-
# # => ["OK", 6]
-
#
-
# @example Without a block
-
# redis.watch("key")
-
# # => "OK"
-
#
-
# @param [String, Array<String>] keys one or more keys to watch
-
# @return [Object] if using a block, returns the return value of the block
-
# @return [String] if not using a block, returns `OK`
-
#
-
# @see #unwatch
-
# @see #multi
-
1
def watch(*keys)
-
synchronize do |client|
-
res = client.call([:watch] + keys)
-
-
if block_given?
-
begin
-
yield(self)
-
rescue ConnectionError
-
raise
-
rescue StandardError
-
unwatch
-
raise
-
end
-
else
-
res
-
end
-
end
-
end
-
-
# Forget about all watched keys.
-
#
-
# @return [String] `OK`
-
#
-
# @see #watch
-
# @see #multi
-
1
def unwatch
-
synchronize do |client|
-
client.call([:unwatch])
-
end
-
end
-
-
1
def pipelined
-
synchronize do |client|
-
begin
-
original, @client = @client, Pipeline.new
-
yield(self)
-
original.call_pipeline(@client)
-
ensure
-
@client = original
-
end
-
end
-
end
-
-
# Mark the start of a transaction block.
-
#
-
# Passing a block is optional.
-
#
-
# @example With a block
-
# redis.multi do |multi|
-
# multi.set("key", "value")
-
# multi.incr("counter")
-
# end # => ["OK", 6]
-
#
-
# @example Without a block
-
# redis.multi
-
# # => "OK"
-
# redis.set("key", "value")
-
# # => "QUEUED"
-
# redis.incr("counter")
-
# # => "QUEUED"
-
# redis.exec
-
# # => ["OK", 6]
-
#
-
# @yield [multi] the commands that are called inside this block are cached
-
# and written to the server upon returning from it
-
# @yieldparam [Redis] multi `self`
-
#
-
# @return [String, Array<...>]
-
# - when a block is not given, `OK`
-
# - when a block is given, an array with replies
-
#
-
# @see #watch
-
# @see #unwatch
-
1
def multi
-
synchronize do |client|
-
if !block_given?
-
client.call([:multi])
-
else
-
begin
-
pipeline = Pipeline::Multi.new
-
original, @client = @client, pipeline
-
yield(self)
-
original.call_pipeline(pipeline)
-
ensure
-
@client = original
-
end
-
end
-
end
-
end
-
-
# Execute all commands issued after MULTI.
-
#
-
# Only call this method when `#multi` was called **without** a block.
-
#
-
# @return [nil, Array<...>]
-
# - when commands were not executed, `nil`
-
# - when commands were executed, an array with their replies
-
#
-
# @see #multi
-
# @see #discard
-
1
def exec
-
synchronize do |client|
-
client.call([:exec])
-
end
-
end
-
-
# Discard all commands issued after MULTI.
-
#
-
# Only call this method when `#multi` was called **without** a block.
-
#
-
# @return `"OK"`
-
#
-
# @see #multi
-
# @see #exec
-
1
def discard
-
synchronize do |client|
-
client.call([:discard])
-
end
-
end
-
-
# Control remote script registry.
-
#
-
# @example Load a script
-
# sha = redis.script(:load, "return 1")
-
# # => <sha of this script>
-
# @example Check if a script exists
-
# redis.script(:exists, sha)
-
# # => true
-
# @example Check if multiple scripts exist
-
# redis.script(:exists, [sha, other_sha])
-
# # => [true, false]
-
# @example Flush the script registry
-
# redis.script(:flush)
-
# # => "OK"
-
# @example Kill a running script
-
# redis.script(:kill)
-
# # => "OK"
-
#
-
# @param [String] subcommand e.g. `exists`, `flush`, `load`, `kill`
-
# @param [Array<String>] args depends on subcommand
-
# @return [String, Boolean, Array<Boolean>, ...] depends on subcommand
-
#
-
# @see #eval
-
# @see #evalsha
-
1
def script(subcommand, *args)
-
subcommand = subcommand.to_s.downcase
-
-
if subcommand == "exists"
-
synchronize do |client|
-
arg = args.first
-
-
client.call([:script, :exists, arg]) do |reply|
-
reply = reply.map { |r| _boolify.call(r) }
-
-
if arg.is_a?(Array)
-
reply
-
else
-
reply.first
-
end
-
end
-
end
-
else
-
synchronize do |client|
-
client.call([:script, subcommand] + args)
-
end
-
end
-
end
-
-
1
def _eval(cmd, args)
-
script = args.shift
-
options = args.pop if args.last.is_a?(Hash)
-
options ||= {}
-
-
keys = args.shift || options[:keys] || []
-
argv = args.shift || options[:argv] || []
-
-
synchronize do |client|
-
client.call([cmd, script, keys.length] + keys + argv)
-
end
-
end
-
-
# Evaluate Lua script.
-
#
-
# @example EVAL without KEYS nor ARGV
-
# redis.eval("return 1")
-
# # => 1
-
# @example EVAL with KEYS and ARGV as array arguments
-
# redis.eval("return { KEYS, ARGV }", ["k1", "k2"], ["a1", "a2"])
-
# # => [["k1", "k2"], ["a1", "a2"]]
-
# @example EVAL with KEYS and ARGV in a hash argument
-
# redis.eval("return { KEYS, ARGV }", :keys => ["k1", "k2"], :argv => ["a1", "a2"])
-
# # => [["k1", "k2"], ["a1", "a2"]]
-
#
-
# @param [Array<String>] keys optional array with keys to pass to the script
-
# @param [Array<String>] argv optional array with arguments to pass to the script
-
# @param [Hash] options
-
# - `:keys => Array<String>`: optional array with keys to pass to the script
-
# - `:argv => Array<String>`: optional array with arguments to pass to the script
-
# @return depends on the script
-
#
-
# @see #script
-
# @see #evalsha
-
1
def eval(*args)
-
_eval(:eval, args)
-
end
-
-
# Evaluate Lua script by its SHA.
-
#
-
# @example EVALSHA without KEYS nor ARGV
-
# redis.evalsha(sha)
-
# # => <depends on script>
-
# @example EVALSHA with KEYS and ARGV as array arguments
-
# redis.evalsha(sha, ["k1", "k2"], ["a1", "a2"])
-
# # => <depends on script>
-
# @example EVALSHA with KEYS and ARGV in a hash argument
-
# redis.evalsha(sha, :keys => ["k1", "k2"], :argv => ["a1", "a2"])
-
# # => <depends on script>
-
#
-
# @param [Array<String>] keys optional array with keys to pass to the script
-
# @param [Array<String>] argv optional array with arguments to pass to the script
-
# @param [Hash] options
-
# - `:keys => Array<String>`: optional array with keys to pass to the script
-
# - `:argv => Array<String>`: optional array with arguments to pass to the script
-
# @return depends on the script
-
#
-
# @see #script
-
# @see #eval
-
1
def evalsha(*args)
-
_eval(:evalsha, args)
-
end
-
-
1
def _scan(command, cursor, args, options = {}, &block)
-
# SSCAN/ZSCAN/HSCAN already prepend the key to +args+.
-
-
args << cursor
-
-
if match = options[:match]
-
args.concat(["MATCH", match])
-
end
-
-
if count = options[:count]
-
args.concat(["COUNT", count])
-
end
-
-
synchronize do |client|
-
client.call([command] + args, &block)
-
end
-
end
-
-
# Scan the keyspace
-
#
-
# @example Retrieve the first batch of keys
-
# redis.scan(0)
-
# # => ["4", ["key:21", "key:47", "key:42"]]
-
# @example Retrieve a batch of keys matching a pattern
-
# redis.scan(4, :match => "key:1?")
-
# # => ["92", ["key:13", "key:18"]]
-
#
-
# @param [String, Integer] cursor: the cursor of the iteration
-
# @param [Hash] options
-
# - `:match => String`: only return keys matching the pattern
-
# - `:count => Integer`: return count keys at most per iteration
-
#
-
# @return [String, Array<String>] the next cursor and all found keys
-
1
def scan(cursor, options={})
-
_scan(:scan, cursor, [], options)
-
end
-
-
# Scan the keyspace
-
#
-
# @example Retrieve all of the keys (with possible duplicates)
-
# redis.scan_each.to_a
-
# # => ["key:21", "key:47", "key:42"]
-
# @example Execute block for each key matching a pattern
-
# redis.scan_each(:match => "key:1?") {|key| puts key}
-
# # => key:13
-
# # => key:18
-
#
-
# @param [Hash] options
-
# - `:match => String`: only return keys matching the pattern
-
# - `:count => Integer`: return count keys at most per iteration
-
#
-
# @return [Enumerator] an enumerator for all found keys
-
1
def scan_each(options={}, &block)
-
return to_enum(:scan_each, options) unless block_given?
-
cursor = 0
-
loop do
-
cursor, keys = scan(cursor, options)
-
keys.each(&block)
-
break if cursor == "0"
-
end
-
end
-
-
# Scan a hash
-
#
-
# @example Retrieve the first batch of key/value pairs in a hash
-
# redis.hscan("hash", 0)
-
#
-
# @param [String, Integer] cursor: the cursor of the iteration
-
# @param [Hash] options
-
# - `:match => String`: only return keys matching the pattern
-
# - `:count => Integer`: return count keys at most per iteration
-
#
-
# @return [String, Array<[String, String]>] the next cursor and all found keys
-
1
def hscan(key, cursor, options={})
-
_scan(:hscan, cursor, [key], options) do |reply|
-
[reply[0], _pairify(reply[1])]
-
end
-
end
-
-
# Scan a hash
-
#
-
# @example Retrieve all of the key/value pairs in a hash
-
# redis.hscan_each("hash").to_a
-
# # => [["key70", "70"], ["key80", "80"]]
-
#
-
# @param [Hash] options
-
# - `:match => String`: only return keys matching the pattern
-
# - `:count => Integer`: return count keys at most per iteration
-
#
-
# @return [Enumerator] an enumerator for all found keys
-
1
def hscan_each(key, options={}, &block)
-
return to_enum(:hscan_each, key, options) unless block_given?
-
cursor = 0
-
loop do
-
cursor, values = hscan(key, cursor, options)
-
values.each(&block)
-
break if cursor == "0"
-
end
-
end
-
-
# Scan a sorted set
-
#
-
# @example Retrieve the first batch of key/value pairs in a hash
-
# redis.zscan("zset", 0)
-
#
-
# @param [String, Integer] cursor: the cursor of the iteration
-
# @param [Hash] options
-
# - `:match => String`: only return keys matching the pattern
-
# - `:count => Integer`: return count keys at most per iteration
-
#
-
# @return [String, Array<[String, Float]>] the next cursor and all found
-
# members and scores
-
1
def zscan(key, cursor, options={})
-
_scan(:zscan, cursor, [key], options) do |reply|
-
[reply[0], _floatify_pairs.call(reply[1])]
-
end
-
end
-
-
# Scan a sorted set
-
#
-
# @example Retrieve all of the members/scores in a sorted set
-
# redis.zscan_each("zset").to_a
-
# # => [["key70", "70"], ["key80", "80"]]
-
#
-
# @param [Hash] options
-
# - `:match => String`: only return keys matching the pattern
-
# - `:count => Integer`: return count keys at most per iteration
-
#
-
# @return [Enumerator] an enumerator for all found scores and members
-
1
def zscan_each(key, options={}, &block)
-
return to_enum(:zscan_each, key, options) unless block_given?
-
cursor = 0
-
loop do
-
cursor, values = zscan(key, cursor, options)
-
values.each(&block)
-
break if cursor == "0"
-
end
-
end
-
-
# Scan a set
-
#
-
# @example Retrieve the first batch of keys in a set
-
# redis.sscan("set", 0)
-
#
-
# @param [String, Integer] cursor: the cursor of the iteration
-
# @param [Hash] options
-
# - `:match => String`: only return keys matching the pattern
-
# - `:count => Integer`: return count keys at most per iteration
-
#
-
# @return [String, Array<String>] the next cursor and all found members
-
1
def sscan(key, cursor, options={})
-
_scan(:sscan, cursor, [key], options)
-
end
-
-
# Scan a set
-
#
-
# @example Retrieve all of the keys in a set
-
# redis.sscan("set").to_a
-
# # => ["key1", "key2", "key3"]
-
#
-
# @param [Hash] options
-
# - `:match => String`: only return keys matching the pattern
-
# - `:count => Integer`: return count keys at most per iteration
-
#
-
# @return [Enumerator] an enumerator for all keys in the set
-
1
def sscan_each(key, options={}, &block)
-
return to_enum(:sscan_each, key, options) unless block_given?
-
cursor = 0
-
loop do
-
cursor, keys = sscan(key, cursor, options)
-
keys.each(&block)
-
break if cursor == "0"
-
end
-
end
-
-
# Add one or more members to a HyperLogLog structure.
-
#
-
# @param [String] key
-
# @param [String, Array<String>] member one member, or array of members
-
# @return [Boolean] true if at least 1 HyperLogLog internal register was altered. false otherwise.
-
1
def pfadd(key, member)
-
synchronize do |client|
-
client.call([:pfadd, key, member], &_boolify)
-
end
-
end
-
-
# Get the approximate cardinality of members added to HyperLogLog structure.
-
#
-
# @param [String] key
-
# @return [Fixnum]
-
1
def pfcount(key)
-
synchronize do |client|
-
client.call([:pfcount, key])
-
end
-
end
-
-
# Merge multiple HyperLogLog values into an unique value that will approximate the cardinality of the union of
-
# the observed Sets of the source HyperLogLog structures.
-
#
-
# @param [String] dest_key destination key
-
# @param [String, Array<String>] source_key source key, or array of keys
-
# @return [Boolean]
-
1
def pfmerge(dest_key, *source_key)
-
synchronize do |client|
-
client.call([:pfmerge, dest_key, *source_key], &_boolify_set)
-
end
-
end
-
-
1
def id
-
@original_client.id
-
end
-
-
1
def inspect
-
"#<Redis client v#{Redis::VERSION} for #{id}>"
-
end
-
-
1
def dup
-
self.class.new(@options)
-
end
-
-
1
def method_missing(command, *args)
-
synchronize do |client|
-
client.call([command] + args)
-
end
-
end
-
-
1
private
-
-
# Commands returning 1 for true and 0 for false may be executed in a pipeline
-
# where the method call will return nil. Propagate the nil instead of falsely
-
# returning false.
-
1
def _boolify
-
lambda { |value|
-
value == 1 if value
-
}
-
end
-
-
1
def _boolify_set
-
lambda { |value|
-
if value && "OK" == value
-
true
-
else
-
false
-
end
-
}
-
end
-
-
1
def _hashify
-
lambda { |array|
-
hash = Hash.new
-
array.each_slice(2) do |field, value|
-
hash[field] = value
-
end
-
hash
-
}
-
end
-
-
1
def _floatify
-
lambda { |str|
-
return unless str
-
-
if (inf = str.match(/^(-)?inf/i))
-
(inf[1] ? -1.0 : 1.0) / 0.0
-
else
-
Float(str)
-
end
-
}
-
end
-
-
1
def _floatify_pairs
-
lambda { |array|
-
return unless array
-
-
array.each_slice(2).map do |member, score|
-
[member, _floatify.call(score)]
-
end
-
}
-
end
-
-
1
def _pairify(array)
-
array.each_slice(2).to_a
-
end
-
-
1
def _subscription(method, channels, block)
-
return @client.call([method] + channels) if subscribed?
-
-
begin
-
original, @client = @client, SubscribedClient.new(@client)
-
@client.send(method, *channels, &block)
-
ensure
-
@client = original
-
end
-
end
-
-
end
-
-
1
require "redis/version"
-
1
require "redis/connection"
-
1
require "redis/client"
-
1
require "redis/pipeline"
-
1
require "redis/subscribe"
-
1
require "redis/errors"
-
1
require "socket"
-
1
require "cgi"
-
-
1
class Redis
-
1
class Client
-
-
1
DEFAULTS = {
-
:url => lambda { ENV["REDIS_URL"] },
-
:scheme => "redis",
-
:host => "127.0.0.1",
-
:port => 6379,
-
:path => nil,
-
:timeout => 5.0,
-
:password => nil,
-
:db => 0,
-
:driver => nil,
-
:id => nil,
-
:tcp_keepalive => 0,
-
:reconnect_attempts => 1,
-
:inherit_socket => false
-
}
-
-
1
def options
-
Marshal.load(Marshal.dump(@options))
-
end
-
-
1
def scheme
-
@options[:scheme]
-
end
-
-
1
def host
-
@options[:host]
-
end
-
-
1
def port
-
@options[:port]
-
end
-
-
1
def path
-
@options[:path]
-
end
-
-
1
def timeout
-
@options[:timeout]
-
end
-
-
1
def password
-
@options[:password]
-
end
-
-
1
def db
-
@options[:db]
-
end
-
-
1
def db=(db)
-
@options[:db] = db.to_i
-
end
-
-
1
def driver
-
@options[:driver]
-
end
-
-
1
def inherit_socket?
-
@options[:inherit_socket]
-
end
-
-
1
attr_accessor :logger
-
1
attr_reader :connection
-
1
attr_reader :command_map
-
-
1
def initialize(options = {})
-
@options = _parse_options(options)
-
@reconnect = true
-
@logger = @options[:logger]
-
@connection = nil
-
@command_map = {}
-
end
-
-
1
def connect
-
@pid = Process.pid
-
-
# Don't try to reconnect when the connection is fresh
-
with_reconnect(false) do
-
establish_connection
-
call [:auth, password] if password
-
call [:select, db] if db != 0
-
end
-
-
self
-
end
-
-
1
def id
-
@options[:id] || "redis://#{location}/#{db}"
-
end
-
-
1
def location
-
path || "#{host}:#{port}"
-
end
-
-
1
def call(command, &block)
-
reply = process([command]) { read }
-
raise reply if reply.is_a?(CommandError)
-
-
if block
-
block.call(reply)
-
else
-
reply
-
end
-
end
-
-
1
def call_loop(command)
-
error = nil
-
-
result = without_socket_timeout do
-
process([command]) do
-
loop do
-
reply = read
-
if reply.is_a?(CommandError)
-
error = reply
-
break
-
else
-
yield reply
-
end
-
end
-
end
-
end
-
-
# Raise error when previous block broke out of the loop.
-
raise error if error
-
-
# Result is set to the value that the provided block used to break.
-
result
-
end
-
-
1
def call_pipeline(pipeline)
-
with_reconnect pipeline.with_reconnect? do
-
begin
-
pipeline.finish(call_pipelined(pipeline.commands)).tap do
-
self.db = pipeline.db if pipeline.db
-
end
-
rescue ConnectionError => e
-
return nil if pipeline.shutdown?
-
# Assume the pipeline was sent in one piece, but execution of
-
# SHUTDOWN caused none of the replies for commands that were executed
-
# prior to it from coming back around.
-
raise e
-
end
-
end
-
end
-
-
1
def call_pipelined(commands)
-
return [] if commands.empty?
-
-
# The method #ensure_connected (called from #process) reconnects once on
-
# I/O errors. To make an effort in making sure that commands are not
-
# executed more than once, only allow reconnection before the first reply
-
# has been read. When an error occurs after the first reply has been
-
# read, retrying would re-execute the entire pipeline, thus re-issuing
-
# already successfully executed commands. To circumvent this, don't retry
-
# after the first reply has been read successfully.
-
-
result = Array.new(commands.size)
-
reconnect = @reconnect
-
-
begin
-
process(commands) do
-
result[0] = read
-
-
@reconnect = false
-
-
(commands.size - 1).times do |i|
-
result[i + 1] = read
-
end
-
end
-
ensure
-
@reconnect = reconnect
-
end
-
-
result
-
end
-
-
1
def call_with_timeout(command, timeout, &blk)
-
with_socket_timeout(timeout) do
-
call(command, &blk)
-
end
-
rescue ConnectionError
-
retry
-
end
-
-
1
def call_without_timeout(command, &blk)
-
call_with_timeout(command, 0, &blk)
-
end
-
-
1
def process(commands)
-
logging(commands) do
-
ensure_connected do
-
commands.each do |command|
-
if command_map[command.first]
-
command = command.dup
-
command[0] = command_map[command.first]
-
end
-
-
write(command)
-
end
-
-
yield if block_given?
-
end
-
end
-
end
-
-
1
def connected?
-
!! (connection && connection.connected?)
-
end
-
-
1
def disconnect
-
connection.disconnect if connected?
-
end
-
-
1
def reconnect
-
disconnect
-
connect
-
end
-
-
1
def io
-
yield
-
rescue TimeoutError => e1
-
# Add a message to the exception without destroying the original stack
-
e2 = TimeoutError.new("Connection timed out")
-
e2.set_backtrace(e1.backtrace)
-
raise e2
-
rescue Errno::ECONNRESET, Errno::EPIPE, Errno::ECONNABORTED, Errno::EBADF, Errno::EINVAL => e
-
raise ConnectionError, "Connection lost (%s)" % [e.class.name.split("::").last]
-
end
-
-
1
def read
-
io do
-
connection.read
-
end
-
end
-
-
1
def write(command)
-
io do
-
connection.write(command)
-
end
-
end
-
-
1
def with_socket_timeout(timeout)
-
connect unless connected?
-
-
begin
-
connection.timeout = timeout
-
yield
-
ensure
-
connection.timeout = self.timeout if connected?
-
end
-
end
-
-
1
def without_socket_timeout(&blk)
-
with_socket_timeout(0, &blk)
-
end
-
-
1
def with_reconnect(val=true)
-
begin
-
original, @reconnect = @reconnect, val
-
yield
-
ensure
-
@reconnect = original
-
end
-
end
-
-
1
def without_reconnect(&blk)
-
with_reconnect(false, &blk)
-
end
-
-
1
protected
-
-
1
def logging(commands)
-
return yield unless @logger && @logger.debug?
-
-
begin
-
commands.each do |name, *args|
-
logged_args = args.map do |a|
-
case
-
when a.respond_to?(:inspect) then a.inspect
-
when a.respond_to?(:to_s) then a.to_s
-
else
-
# handle poorly-behaved descendants of BasicObject
-
klass = a.instance_exec { (class << self; self end).superclass }
-
"\#<#{klass}:#{a.__id__}>"
-
end
-
end
-
@logger.debug("[Redis] command=#{name.to_s.upcase} args=#{logged_args.join(' ')}")
-
end
-
-
t1 = Time.now
-
yield
-
ensure
-
@logger.debug("[Redis] call_time=%0.2f ms" % ((Time.now - t1) * 1000)) if t1
-
end
-
end
-
-
1
def establish_connection
-
@connection = @options[:driver].connect(@options.dup)
-
-
rescue TimeoutError
-
raise CannotConnectError, "Timed out connecting to Redis on #{location}"
-
rescue Errno::ECONNREFUSED
-
raise CannotConnectError, "Error connecting to Redis on #{location} (ECONNREFUSED)"
-
end
-
-
1
def ensure_connected
-
attempts = 0
-
-
begin
-
attempts += 1
-
-
if connected?
-
unless inherit_socket? || Process.pid == @pid
-
raise InheritedError,
-
"Tried to use a connection from a child process without reconnecting. " +
-
"You need to reconnect to Redis after forking " +
-
"or set :inherit_socket to true."
-
end
-
else
-
connect
-
end
-
-
yield
-
rescue ConnectionError, InheritedError
-
disconnect
-
-
if attempts <= @options[:reconnect_attempts] && @reconnect
-
retry
-
else
-
raise
-
end
-
rescue Exception
-
disconnect
-
raise
-
end
-
end
-
-
1
def _parse_options(options)
-
defaults = DEFAULTS.dup
-
options = options.dup
-
-
defaults.keys.each do |key|
-
# Fill in defaults if needed
-
if defaults[key].respond_to?(:call)
-
defaults[key] = defaults[key].call
-
end
-
-
# Symbolize only keys that are needed
-
options[key] = options[key.to_s] if options.has_key?(key.to_s)
-
end
-
-
url = options[:url] || defaults[:url]
-
-
# Override defaults from URL if given
-
if url
-
require "uri"
-
-
uri = URI(url)
-
-
if uri.scheme == "unix"
-
defaults[:path] = uri.path
-
else
-
# Require the URL to have at least a host
-
raise ArgumentError, "invalid url" unless uri.host
-
-
defaults[:scheme] = uri.scheme
-
defaults[:host] = uri.host
-
defaults[:port] = uri.port if uri.port
-
defaults[:password] = CGI.unescape(uri.password) if uri.password
-
defaults[:db] = uri.path[1..-1].to_i if uri.path
-
end
-
end
-
-
# Use default when option is not specified or nil
-
defaults.keys.each do |key|
-
options[key] = defaults[key] if options[key].nil?
-
end
-
-
if options[:path]
-
options[:scheme] = "unix"
-
options.delete(:host)
-
options.delete(:port)
-
else
-
options[:host] = options[:host].to_s
-
options[:port] = options[:port].to_i
-
end
-
-
options[:timeout] = options[:timeout].to_f
-
options[:db] = options[:db].to_i
-
options[:driver] = _parse_driver(options[:driver]) || Connection.drivers.last
-
-
case options[:tcp_keepalive]
-
when Hash
-
[:time, :intvl, :probes].each do |key|
-
unless options[:tcp_keepalive][key].is_a?(Fixnum)
-
raise "Expected the #{key.inspect} key in :tcp_keepalive to be a Fixnum"
-
end
-
end
-
-
when Fixnum
-
if options[:tcp_keepalive] >= 60
-
options[:tcp_keepalive] = {:time => options[:tcp_keepalive] - 20, :intvl => 10, :probes => 2}
-
-
elsif options[:tcp_keepalive] >= 30
-
options[:tcp_keepalive] = {:time => options[:tcp_keepalive] - 10, :intvl => 5, :probes => 2}
-
-
elsif options[:tcp_keepalive] >= 5
-
options[:tcp_keepalive] = {:time => options[:tcp_keepalive] - 2, :intvl => 2, :probes => 1}
-
end
-
end
-
-
options
-
end
-
-
1
def _parse_driver(driver)
-
driver = driver.to_s if driver.is_a?(Symbol)
-
-
if driver.kind_of?(String)
-
begin
-
require "redis/connection/#{driver}"
-
driver = Connection.const_get(driver.capitalize)
-
rescue LoadError, NameError
-
raise RuntimeError, "Cannot load driver #{driver.inspect}"
-
end
-
end
-
-
driver
-
end
-
end
-
end
-
1
require "redis/connection/registry"
-
-
# If a connection driver was required before this file, the array
-
# Redis::Connection.drivers will contain one or more classes. The last driver
-
# in this array will be used as default driver. If this array is empty, we load
-
# the plain Ruby driver as our default. Another driver can be required at a
-
# later point in time, causing it to be the last element of the #drivers array
-
# and therefore be chosen by default.
-
1
require "redis/connection/ruby" if Redis::Connection.drivers.empty?
-
1
class Redis
-
1
module Connection
-
1
module CommandHelper
-
-
1
COMMAND_DELIMITER = "\r\n"
-
-
1
def build_command(args)
-
command = [nil]
-
-
args.each do |i|
-
if i.is_a? Array
-
i.each do |j|
-
j = j.to_s
-
command << "$#{j.bytesize}"
-
command << j
-
end
-
else
-
i = i.to_s
-
command << "$#{i.bytesize}"
-
command << i
-
end
-
end
-
-
command[0] = "*#{(command.length - 1) / 2}"
-
-
# Trailing delimiter
-
command << ""
-
command.join(COMMAND_DELIMITER)
-
end
-
-
1
protected
-
-
1
if defined?(Encoding::default_external)
-
1
def encode(string)
-
string.force_encoding(Encoding::default_external)
-
end
-
else
-
def encode(string)
-
string
-
end
-
end
-
end
-
end
-
end
-
1
class Redis
-
1
module Connection
-
-
# Store a list of loaded connection drivers in the Connection module.
-
# Redis::Client uses the last required driver by default, and will be aware
-
# of the loaded connection drivers if the user chooses to override the
-
# default connection driver.
-
1
def self.drivers
-
2
@drivers ||= []
-
end
-
end
-
end
-
1
require "redis/connection/registry"
-
1
require "redis/connection/command_helper"
-
1
require "redis/errors"
-
1
require "socket"
-
-
1
class Redis
-
1
module Connection
-
1
module SocketMixin
-
-
1
CRLF = "\r\n".freeze
-
-
1
def initialize(*args)
-
super(*args)
-
-
@timeout = nil
-
@buffer = ""
-
end
-
-
1
def timeout=(timeout)
-
if timeout && timeout > 0
-
@timeout = timeout
-
else
-
@timeout = nil
-
end
-
end
-
-
1
def read(nbytes)
-
result = @buffer.slice!(0, nbytes)
-
-
while result.bytesize < nbytes
-
result << _read_from_socket(nbytes - result.bytesize)
-
end
-
-
result
-
end
-
-
1
def gets
-
crlf = nil
-
-
while (crlf = @buffer.index(CRLF)) == nil
-
@buffer << _read_from_socket(1024)
-
end
-
-
@buffer.slice!(0, crlf + CRLF.bytesize)
-
end
-
-
1
def _read_from_socket(nbytes)
-
begin
-
read_nonblock(nbytes)
-
-
rescue Errno::EWOULDBLOCK, Errno::EAGAIN
-
if IO.select([self], nil, nil, @timeout)
-
retry
-
else
-
raise Redis::TimeoutError
-
end
-
end
-
-
rescue EOFError
-
raise Errno::ECONNRESET
-
end
-
end
-
-
1
if defined?(RUBY_ENGINE) && RUBY_ENGINE == "jruby"
-
-
require "timeout"
-
-
class TCPSocket < ::TCPSocket
-
-
include SocketMixin
-
-
def self.connect(host, port, timeout)
-
Timeout.timeout(timeout) do
-
sock = new(host, port)
-
sock
-
end
-
rescue Timeout::Error
-
raise TimeoutError
-
end
-
end
-
-
if defined?(::UNIXSocket)
-
-
class UNIXSocket < ::UNIXSocket
-
-
include SocketMixin
-
-
def self.connect(path, timeout)
-
Timeout.timeout(timeout) do
-
sock = new(path)
-
sock
-
end
-
rescue Timeout::Error
-
raise TimeoutError
-
end
-
-
# JRuby raises Errno::EAGAIN on #read_nonblock even when IO.select
-
# says it is readable (1.6.6, in both 1.8 and 1.9 mode).
-
# Use the blocking #readpartial method instead.
-
-
def _read_from_socket(nbytes)
-
readpartial(nbytes)
-
-
rescue EOFError
-
raise Errno::ECONNRESET
-
end
-
end
-
-
end
-
-
else
-
-
1
class TCPSocket < ::Socket
-
-
1
include SocketMixin
-
-
1
def self.connect_addrinfo(ai, port, timeout)
-
sock = new(::Socket.const_get(ai[0]), Socket::SOCK_STREAM, 0)
-
sockaddr = ::Socket.pack_sockaddr_in(port, ai[3])
-
-
begin
-
sock.connect_nonblock(sockaddr)
-
rescue Errno::EINPROGRESS
-
if IO.select(nil, [sock], nil, timeout) == nil
-
raise TimeoutError
-
end
-
-
begin
-
sock.connect_nonblock(sockaddr)
-
rescue Errno::EISCONN
-
end
-
end
-
-
sock
-
end
-
-
1
def self.connect(host, port, timeout)
-
# Don't pass AI_ADDRCONFIG as flag to getaddrinfo(3)
-
#
-
# From the man page for getaddrinfo(3):
-
#
-
# If hints.ai_flags includes the AI_ADDRCONFIG flag, then IPv4
-
# addresses are returned in the list pointed to by res only if the
-
# local system has at least one IPv4 address configured, and IPv6
-
# addresses are returned only if the local system has at least one
-
# IPv6 address configured. The loopback address is not considered
-
# for this case as valid as a configured address.
-
#
-
# We do want the IPv6 loopback address to be returned if applicable,
-
# even if it is the only configured IPv6 address on the machine.
-
# Also see: https://github.com/redis/redis-rb/pull/394.
-
addrinfo = ::Socket.getaddrinfo(host, nil, Socket::AF_UNSPEC, Socket::SOCK_STREAM)
-
-
# From the man page for getaddrinfo(3):
-
#
-
# Normally, the application should try using the addresses in the
-
# order in which they are returned. The sorting function used
-
# within getaddrinfo() is defined in RFC 3484 [...].
-
#
-
addrinfo.each_with_index do |ai, i|
-
begin
-
return connect_addrinfo(ai, port, timeout)
-
rescue SystemCallError
-
# Raise if this was our last attempt.
-
raise if addrinfo.length == i+1
-
end
-
end
-
end
-
end
-
-
1
class UNIXSocket < ::Socket
-
-
1
include SocketMixin
-
-
1
def self.connect(path, timeout)
-
sock = new(::Socket::AF_UNIX, Socket::SOCK_STREAM, 0)
-
sockaddr = ::Socket.pack_sockaddr_un(path)
-
-
begin
-
sock.connect_nonblock(sockaddr)
-
rescue Errno::EINPROGRESS
-
if IO.select(nil, [sock], nil, timeout) == nil
-
raise TimeoutError
-
end
-
-
begin
-
sock.connect_nonblock(sockaddr)
-
rescue Errno::EISCONN
-
end
-
end
-
-
sock
-
end
-
end
-
-
end
-
-
1
class Ruby
-
1
include Redis::Connection::CommandHelper
-
-
1
MINUS = "-".freeze
-
1
PLUS = "+".freeze
-
1
COLON = ":".freeze
-
1
DOLLAR = "$".freeze
-
1
ASTERISK = "*".freeze
-
-
1
def self.connect(config)
-
if config[:scheme] == "unix"
-
sock = UNIXSocket.connect(config[:path], config[:timeout])
-
else
-
sock = TCPSocket.connect(config[:host], config[:port], config[:timeout])
-
end
-
-
instance = new(sock)
-
instance.timeout = config[:timeout]
-
instance.set_tcp_keepalive config[:tcp_keepalive]
-
instance
-
end
-
-
4
if [:SOL_SOCKET, :SO_KEEPALIVE, :SOL_TCP, :TCP_KEEPIDLE, :TCP_KEEPINTVL, :TCP_KEEPCNT].all?{|c| Socket.const_defined? c}
-
def set_tcp_keepalive(keepalive)
-
return unless keepalive.is_a?(Hash)
-
-
@sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true)
-
@sock.setsockopt(Socket::SOL_TCP, Socket::TCP_KEEPIDLE, keepalive[:time])
-
@sock.setsockopt(Socket::SOL_TCP, Socket::TCP_KEEPINTVL, keepalive[:intvl])
-
@sock.setsockopt(Socket::SOL_TCP, Socket::TCP_KEEPCNT, keepalive[:probes])
-
end
-
-
def get_tcp_keepalive
-
{
-
:time => @sock.getsockopt(Socket::SOL_TCP, Socket::TCP_KEEPIDLE).int,
-
:intvl => @sock.getsockopt(Socket::SOL_TCP, Socket::TCP_KEEPINTVL).int,
-
:probes => @sock.getsockopt(Socket::SOL_TCP, Socket::TCP_KEEPCNT).int,
-
}
-
end
-
else
-
1
def set_tcp_keepalive(keepalive)
-
end
-
-
1
def get_tcp_keepalive
-
{
-
}
-
end
-
end
-
-
1
def initialize(sock)
-
@sock = sock
-
end
-
-
1
def connected?
-
!! @sock
-
end
-
-
1
def disconnect
-
@sock.close
-
rescue
-
ensure
-
@sock = nil
-
end
-
-
1
def timeout=(timeout)
-
if @sock.respond_to?(:timeout=)
-
@sock.timeout = timeout
-
end
-
end
-
-
1
def write(command)
-
@sock.write(build_command(command))
-
end
-
-
1
def read
-
line = @sock.gets
-
reply_type = line.slice!(0, 1)
-
format_reply(reply_type, line)
-
-
rescue Errno::EAGAIN
-
raise TimeoutError
-
end
-
-
1
def format_reply(reply_type, line)
-
case reply_type
-
when MINUS then format_error_reply(line)
-
when PLUS then format_status_reply(line)
-
when COLON then format_integer_reply(line)
-
when DOLLAR then format_bulk_reply(line)
-
when ASTERISK then format_multi_bulk_reply(line)
-
else raise ProtocolError.new(reply_type)
-
end
-
end
-
-
1
def format_error_reply(line)
-
CommandError.new(line.strip)
-
end
-
-
1
def format_status_reply(line)
-
line.strip
-
end
-
-
1
def format_integer_reply(line)
-
line.to_i
-
end
-
-
1
def format_bulk_reply(line)
-
bulklen = line.to_i
-
return if bulklen == -1
-
reply = encode(@sock.read(bulklen))
-
@sock.read(2) # Discard CRLF.
-
reply
-
end
-
-
1
def format_multi_bulk_reply(line)
-
n = line.to_i
-
return if n == -1
-
-
Array.new(n) { read }
-
end
-
end
-
end
-
end
-
-
1
Redis::Connection.drivers << Redis::Connection::Ruby
-
1
class Redis
-
# Base error for all redis-rb errors.
-
1
class BaseError < RuntimeError
-
end
-
-
# Raised by the connection when a protocol error occurs.
-
1
class ProtocolError < BaseError
-
1
def initialize(reply_type)
-
super(<<-EOS.gsub(/(?:^|\n)\s*/, " "))
-
Got '#{reply_type}' as initial reply byte.
-
If you're in a forking environment, such as Unicorn, you need to
-
connect to Redis after forking.
-
EOS
-
end
-
end
-
-
# Raised by the client when command execution returns an error reply.
-
1
class CommandError < BaseError
-
end
-
-
# Base error for connection related errors.
-
1
class BaseConnectionError < BaseError
-
end
-
-
# Raised when connection to a Redis server cannot be made.
-
1
class CannotConnectError < BaseConnectionError
-
end
-
-
# Raised when connection to a Redis server is lost.
-
1
class ConnectionError < BaseConnectionError
-
end
-
-
# Raised when performing I/O times out.
-
1
class TimeoutError < BaseConnectionError
-
end
-
-
# Raised when the connection was inherited by a child process.
-
1
class InheritedError < BaseConnectionError
-
end
-
end
-
1
class Redis
-
1
class SubscribedClient
-
1
def initialize(client)
-
@client = client
-
end
-
-
1
def call(command)
-
@client.process([command])
-
end
-
-
1
def subscribe(*channels, &block)
-
subscription("subscribe", "unsubscribe", channels, block)
-
end
-
-
1
def psubscribe(*channels, &block)
-
subscription("psubscribe", "punsubscribe", channels, block)
-
end
-
-
1
def unsubscribe(*channels)
-
call([:unsubscribe, *channels])
-
end
-
-
1
def punsubscribe(*channels)
-
call([:punsubscribe, *channels])
-
end
-
-
1
protected
-
-
1
def subscription(start, stop, channels, block)
-
sub = Subscription.new(&block)
-
-
unsubscribed = false
-
-
begin
-
@client.call_loop([start, *channels]) do |line|
-
type, *rest = line
-
sub.callbacks[type].call(*rest)
-
unsubscribed = type == stop && rest.last == 0
-
break if unsubscribed
-
end
-
ensure
-
# No need to unsubscribe here. The real client closes the connection
-
# whenever an exception is raised (see #ensure_connected).
-
end
-
end
-
end
-
-
1
class Subscription
-
1
attr :callbacks
-
-
1
def initialize
-
@callbacks = Hash.new do |hash, key|
-
hash[key] = lambda { |*_| }
-
end
-
-
yield(self)
-
end
-
-
1
def subscribe(&block)
-
@callbacks["subscribe"] = block
-
end
-
-
1
def unsubscribe(&block)
-
@callbacks["unsubscribe"] = block
-
end
-
-
1
def message(&block)
-
@callbacks["message"] = block
-
end
-
-
1
def psubscribe(&block)
-
@callbacks["psubscribe"] = block
-
end
-
-
1
def punsubscribe(&block)
-
@callbacks["punsubscribe"] = block
-
end
-
-
1
def pmessage(&block)
-
@callbacks["pmessage"] = block
-
end
-
end
-
end
-
1
class Redis
-
1
VERSION = "3.1.0"
-
end
-
1
module Ref
-
1
require File.join(File.dirname(__FILE__), "ref", "abstract_reference_value_map.rb")
-
1
require File.join(File.dirname(__FILE__), "ref", "abstract_reference_key_map.rb")
-
1
require File.join(File.dirname(__FILE__), "ref", "reference.rb")
-
1
require File.join(File.dirname(__FILE__), "ref", "reference_queue.rb")
-
1
require File.join(File.dirname(__FILE__), "ref", "safe_monitor.rb")
-
-
# Set the best implementation for weak references based on the runtime.
-
1
if defined?(RUBY_PLATFORM) && RUBY_PLATFORM == 'java'
-
# Use native Java references
-
begin
-
$LOAD_PATH.unshift(File.dirname(__FILE__))
-
require 'org/jruby/ext/ref/references'
-
ensure
-
$LOAD_PATH.shift if $LOAD_PATH.first == File.dirname(__FILE__)
-
end
-
else
-
1
require File.join(File.dirname(__FILE__), "ref", "soft_reference.rb")
-
1
if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'ironruby'
-
# IronRuby has it's own implementation of weak references.
-
require File.join(File.dirname(__FILE__), "ref", "weak_reference", "iron_ruby.rb")
-
elsif defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx'
-
# If using Rubinius set the implementation to use WeakRef since it is very efficient and using finalizers is not.
-
require File.join(File.dirname(__FILE__), "ref", "weak_reference", "weak_ref.rb")
-
elsif defined?(::ObjectSpace::WeakMap)
-
# Ruby 2.0 has a working implementation of weakref.rb backed by the new ObjectSpace::WeakMap
-
1
require File.join(File.dirname(__FILE__), "ref", "weak_reference", "weak_ref.rb")
-
elsif defined?(::ObjectSpace._id2ref)
-
# If ObjectSpace can lookup objects from their object_id, then use the pure ruby implementation.
-
require File.join(File.dirname(__FILE__), "ref", "weak_reference", "pure_ruby.rb")
-
else
-
# Otherwise, wrap the standard library WeakRef class
-
require File.join(File.dirname(__FILE__), "ref", "weak_reference", "weak_ref.rb")
-
end
-
end
-
-
1
require File.join(File.dirname(__FILE__), "ref", "soft_key_map.rb")
-
1
require File.join(File.dirname(__FILE__), "ref", "soft_value_map.rb")
-
1
require File.join(File.dirname(__FILE__), "ref", "strong_reference.rb")
-
1
require File.join(File.dirname(__FILE__), "ref", "weak_key_map.rb")
-
1
require File.join(File.dirname(__FILE__), "ref", "weak_value_map.rb")
-
-
# Used for testing
-
1
autoload :Mock, File.join(File.dirname(__FILE__), "ref", "mock.rb")
-
end
-
1
module Ref
-
# Abstract base class for WeakKeyMap and SoftKeyMap.
-
#
-
# The classes behave similar to Hashes, but the keys in the map are not strong references
-
# and can be reclaimed by the garbage collector at any time. When a key is reclaimed, the
-
# map entry will be removed.
-
1
class AbstractReferenceKeyMap
-
1
class << self
-
1
def reference_class=(klass) #:nodoc:
-
2
@reference_class = klass
-
end
-
-
1
def reference_class #:nodoc:
-
raise NotImplementedError.new("#{name} is an abstract class and cannot be instantiated") unless @reference_class
-
@reference_class
-
end
-
end
-
-
# Create a new map. Values added to the hash will be cleaned up by the garbage
-
# collector if there are no other reference except in the map.
-
1
def initialize
-
@values = {}
-
@references_to_keys_map = {}
-
@lock = SafeMonitor.new
-
@reference_cleanup = lambda{|object_id| remove_reference_to(object_id)}
-
end
-
-
# Get a value from the map by key. If the value has been reclaimed by the garbage
-
# collector, this will return nil.
-
1
def [](key)
-
rkey = ref_key(key)
-
@values[rkey] if rkey
-
end
-
-
# Add a key/value to the map.
-
1
def []=(key, value)
-
ObjectSpace.define_finalizer(key, @reference_cleanup)
-
@lock.synchronize do
-
@references_to_keys_map[key.__id__] = self.class.reference_class.new(key)
-
@values[key.__id__] = value
-
end
-
end
-
-
# Remove the value associated with the key from the map.
-
1
def delete(key)
-
rkey = ref_key(key)
-
if rkey
-
@references_to_keys_map.delete(rkey)
-
@values.delete(rkey)
-
else
-
nil
-
end
-
end
-
-
# Get an array of keys that have not yet been garbage collected.
-
1
def keys
-
@values.keys.collect{|rkey| @references_to_keys_map[rkey].object}.compact
-
end
-
-
# Turn the map into an arry of [key, value] entries.
-
1
def to_a
-
array = []
-
each{|k,v| array << [k, v]}
-
array
-
end
-
-
# Iterate through all the key/value pairs in the map that have not been reclaimed
-
# by the garbage collector.
-
1
def each
-
@references_to_keys_map.each do |rkey, ref|
-
key = ref.object
-
yield(key, @values[rkey]) if key
-
end
-
end
-
-
# Clear the map of all key/value pairs.
-
1
def clear
-
@lock.synchronize do
-
@values.clear
-
@references_to_keys_map.clear
-
end
-
end
-
-
# Merge the values from another hash into this map.
-
1
def merge!(other_hash)
-
other_hash.each do |key, value|
-
self[key] = value
-
end
-
end
-
-
1
def inspect
-
live_entries = {}
-
each do |key, value|
-
live_entries[key] = value
-
end
-
live_entries.inspect
-
end
-
-
1
private
-
-
1
def ref_key (key)
-
ref = @references_to_keys_map[key.__id__]
-
if ref && ref.object
-
ref.referenced_object_id
-
else
-
nil
-
end
-
end
-
-
1
def remove_reference_to(object_id)
-
@lock.synchronize do
-
@references_to_keys_map.delete(object_id)
-
@values.delete(object_id)
-
end
-
end
-
end
-
end
-
1
module Ref
-
# Abstract base class for WeakValueMap and SoftValueMap.
-
#
-
# The classes behave similar to Hashes, but the values in the map are not strong references
-
# and can be reclaimed by the garbage collector at any time. When a value is reclaimed, the
-
# map entry will be removed.
-
1
class AbstractReferenceValueMap
-
1
class << self
-
1
def reference_class=(klass) #:nodoc:
-
2
@reference_class = klass
-
end
-
-
1
def reference_class #:nodoc:
-
raise NotImplementedError.new("#{name} is an abstract class and cannot be instantiated") unless @reference_class
-
@reference_class
-
end
-
end
-
-
# Create a new map. Values added to the map will be cleaned up by the garbage
-
# collector if there are no other reference except in the map.
-
1
def initialize
-
@references = {}
-
@references_to_keys_map = {}
-
@lock = SafeMonitor.new
-
@reference_cleanup = lambda{|object_id| remove_reference_to(object_id)}
-
end
-
-
# Get a value from the map by key. If the value has been reclaimed by the garbage
-
# collector, this will return nil.
-
1
def [](key)
-
ref = @references[key]
-
value = ref.object if ref
-
value
-
end
-
-
# Add a key/value to the map.
-
1
def []=(key, value)
-
ObjectSpace.define_finalizer(value, @reference_cleanup)
-
key = key.dup if key.is_a?(String)
-
@lock.synchronize do
-
@references[key] = self.class.reference_class.new(value)
-
keys_for_id = @references_to_keys_map[value.__id__]
-
unless keys_for_id
-
keys_for_id = []
-
@references_to_keys_map[value.__id__] = keys_for_id
-
end
-
keys_for_id << key
-
end
-
value
-
end
-
-
# Remove the entry associated with the key from the map.
-
1
def delete(key)
-
ref = @references.delete(key)
-
if ref
-
keys_to_id = @references_to_keys_map[ref.referenced_object_id]
-
if keys_to_id
-
keys_to_id.delete(key)
-
@references_to_keys_map.delete(ref.referenced_object_id) if keys_to_id.empty?
-
end
-
ref.object
-
else
-
nil
-
end
-
end
-
-
# Get the list of all values that have not yet been garbage collected.
-
1
def values
-
vals = []
-
each{|k,v| vals << v}
-
vals
-
end
-
-
# Turn the map into an arry of [key, value] entries
-
1
def to_a
-
array = []
-
each{|k,v| array << [k, v]}
-
array
-
end
-
-
# Iterate through all the key/value pairs in the map that have not been reclaimed
-
# by the garbage collector.
-
1
def each
-
@references.each do |key, ref|
-
value = ref.object
-
yield(key, value) if value
-
end
-
end
-
-
# Clear the map of all key/value pairs.
-
1
def clear
-
@lock.synchronize do
-
@references.clear
-
@references_to_keys_map.clear
-
end
-
end
-
-
# Merge the values from another hash into this map.
-
1
def merge!(other_hash)
-
other_hash.each do |key, value|
-
self[key] = value
-
end
-
end
-
-
1
def inspect
-
live_entries = {}
-
each do |key, value|
-
live_entries[key] = value
-
end
-
live_entries.inspect
-
end
-
-
1
private
-
-
1
def remove_reference_to(object_id)
-
@lock.synchronize do
-
keys = @references_to_keys_map[object_id]
-
if keys
-
keys.each do |key|
-
@references.delete(key)
-
end
-
@references_to_keys_map.delete(object_id)
-
end
-
end
-
end
-
end
-
end
-
1
module Ref
-
# This class serves as a generic reference mechanism to other objects. The
-
# actual reference can be either a WeakReference, SoftReference, or StrongReference.
-
1
class Reference
-
# The object id of the object being referenced.
-
1
attr_reader :referenced_object_id
-
-
# Create a new reference to an object.
-
1
def initialize(obj)
-
raise NotImplementedError.new("cannot instantiate a generic reference")
-
end
-
-
# Get the referenced object. This could be nil if the reference
-
# is a WeakReference or a SoftReference and the object has been reclaimed by the garbage collector.
-
1
def object
-
raise NotImplementedError
-
end
-
-
1
def inspect
-
obj = object
-
"<##{self.class.name}: #{obj ? obj.inspect : "##{referenced_object_id} (not accessible)"}>"
-
end
-
end
-
end
-
1
module Ref
-
# This class provides a simple thread safe container to hold a reference queue. Instances
-
# of WeakReference can be added to the queue and as the objects pointed to by those references
-
# are cleaned up by the garbage collector, the references will be added to the queue.
-
#
-
# The reason for using a reference queue is that it tends to be more efficient than adding
-
# individual finalizers to objects and the cleanup code can be handled by a thread outside
-
# of garbage collection.
-
#
-
# In general, you should create your own subclass of WeakReference that contains the logic
-
# needed to complete the cleanup. The object pointed to will have already been cleaned up
-
# and the reference cannot maintain a reference to the object.
-
#
-
# === Example usage:
-
#
-
# class MyRef < Ref::WeakReference
-
# def cleanup
-
# # Do something...
-
# end
-
# end
-
#
-
# queue = Ref::ReferenceQueue.new
-
# ref = MyRef.new(Object.new)
-
# queue.monitor(ref)
-
# queue.shift # = nil
-
# ObjectSpace.garbage_collect
-
# r = queue.shift # = ref
-
# r.cleanup
-
1
class ReferenceQueue
-
1
def initialize
-
@queue = []
-
@references = {}
-
@lock = SafeMonitor.new
-
@finalizer = lambda do |object_id|
-
@lock.synchronize do
-
ref = @references.delete(object_id)
-
@queue.push(ref) if ref
-
end
-
end
-
end
-
-
# Monitor a reference. When the object the reference points to is garbage collected,
-
# the reference will be added to the queue.
-
1
def monitor(reference)
-
obj = reference.object
-
if obj
-
@lock.synchronize do
-
@references[reference.referenced_object_id] = reference
-
end
-
ObjectSpace.define_finalizer(obj, @finalizer)
-
else
-
push(reference)
-
end
-
end
-
-
# Add a reference to the queue.
-
1
def push(reference)
-
if reference
-
@lock.synchronize do
-
@queue.push(reference)
-
end
-
end
-
end
-
-
# Pull the last reference off the queue. Returns +nil+ if their are no references.
-
1
def pop
-
@lock.synchronize do
-
@queue.pop
-
end
-
end
-
-
# Pull the next reference off the queue. Returns +nil+ if there are no references.
-
1
def shift
-
@lock.synchronize do
-
@queue.shift
-
end
-
end
-
-
# Return +true+ if the queue is empty.
-
1
def empty?
-
@queue.empty?
-
end
-
-
# Get the current size of the queue.
-
1
def size
-
@queue.size
-
end
-
end
-
end
-
1
begin
-
1
require 'thread'
-
rescue LoadError
-
# Threads not available. Monitor will do nothing.
-
end
-
-
1
module Ref
-
# The Monitor class in Ruby 1.8 has some bugs and also threads may not be available on all
-
# runtimes. This class provides a simple, safe re-entrant mutex as an alternative.
-
1
class SafeMonitor
-
1
def initialize
-
1
@owner = nil
-
1
@count = 0
-
1
@mutex = defined?(Mutex) ? Mutex.new : nil
-
end
-
-
# Acquire an exclusive lock.
-
1
def lock
-
if @mutex
-
if @owner != Thread.current.object_id
-
@mutex.lock
-
@owner = Thread.current.object_id
-
end
-
@count += 1
-
end
-
true
-
end
-
-
# Release the exclusive lock.
-
1
def unlock
-
if @mutex
-
if @owner == Thread.current.object_id
-
@count -= 1
-
if @count == 0
-
@owner = nil
-
@mutex.unlock
-
end
-
end
-
end
-
end
-
-
# Run a block of code with an exclusive lock.
-
1
def synchronize
-
lock
-
yield
-
ensure
-
unlock
-
end
-
end
-
end
-
1
module Ref
-
# Implementation of a map in which only softly referenced keys are kept to the map values.
-
# This allows the garbage collector to reclaim these objects if the only reference to them
-
# is the soft reference in the map.
-
#
-
# This is often useful for cache implementations since the map can be allowed to grow
-
# without bound and the garbage collector can be relied on to clean it up as necessary.
-
# One must be careful, though, when accessing entries since they can be collected at
-
# any time until there is a strong reference to the key.
-
#
-
# === Example usage:
-
#
-
# cache = Ref::SoftKeyMap.new
-
# obj = MyObject.find_by_whatever
-
# obj_info = Service.lookup_object_info(obj)
-
# cache[obj] = Service.lookup_object_info(obj)
-
# cache[obj] # The values looked up from the service
-
# obj = nil
-
# ObjectSpace.garbage_collect
-
# cache.keys # empty array since the keys and values have been reclaimed
-
#
-
# See AbstractReferenceKeyMap for details.
-
1
class SoftKeyMap < AbstractReferenceKeyMap
-
1
self.reference_class = SoftReference
-
end
-
end
-
1
module Ref
-
# A SoftReference represents a reference to an object that is not seen by
-
# the tracing phase of the garbage collector. This allows the referenced
-
# object to be garbage collected as if nothing is referring to it.
-
#
-
# A SoftReference differs from a WeakReference in that the garbage collector
-
# is not so eager to reclaim soft references so they should persist longer.
-
#
-
# === Example usage:
-
#
-
# foo = Object.new
-
# ref = Ref::SoftReference.new(foo)
-
# ref.object # should be foo
-
# ObjectSpace.garbage_collect
-
# ref.object # should be foo
-
# ObjectSpace.garbage_collect
-
# ObjectSpace.garbage_collect
-
# ref.object # should be nil
-
1
class SoftReference < Reference
-
1
@@strong_references = [{}]
-
1
@@gc_flag_set = false
-
-
# Number of garbage collection cycles after an object is used before a reference to it can be reclaimed.
-
1
MIN_GC_CYCLES = 10
-
-
1
@@lock = SafeMonitor.new
-
-
1
@@finalizer = lambda do |object_id|
-
@@lock.synchronize do
-
while @@strong_references.size >= MIN_GC_CYCLES do
-
@@strong_references.shift
-
end
-
@@strong_references.push({}) if @@strong_references.size < MIN_GC_CYCLES
-
@@gc_flag_set = false
-
end
-
end
-
-
# Create a new soft reference to an object.
-
1
def initialize(obj)
-
@referenced_object_id = obj.__id__
-
@weak_reference = WeakReference.new(obj)
-
add_strong_reference(obj)
-
end
-
-
# Get the referenced object. If the object has been reclaimed by the
-
# garbage collector, then this will return nil.
-
1
def object
-
obj = @weak_reference.object
-
# add a temporary strong reference each time the object is referenced.
-
add_strong_reference(obj) if obj
-
obj
-
end
-
-
1
private
-
# Create a strong reference to the object. This reference will live
-
# for three passes of the garbage collector.
-
1
def add_strong_reference(obj) #:nodoc:
-
@@lock.synchronize do
-
@@strong_references.last[obj] = true
-
unless @@gc_flag_set
-
@@gc_flag_set = true
-
ObjectSpace.define_finalizer(Object.new, @@finalizer)
-
end
-
end
-
end
-
end
-
end
-
1
module Ref
-
# Implementation of a map in which soft references are kept to the map values.
-
# This allows the garbage collector to reclaim these objects if the
-
# only reference to them is the soft reference in the map.
-
#
-
# This is often useful for cache implementations since the map can be allowed to grow
-
# without bound and the garbage collector can be relied on to clean it up as necessary.
-
# One must be careful, though, when accessing entries since the values can be collected
-
# at any time until there is a strong reference to them.
-
#
-
# === Example usage:
-
#
-
# cache = Ref::SoftValueMap.new
-
# foo = "foo"
-
# cache["strong"] = foo # add a value with a strong reference
-
# cache["soft"] = "bar" # add a value without a strong reference
-
# cache["strong"] # "foo"
-
# cache["soft"] # "bar"
-
# ObjectSpace.garbage_collect
-
# ObjectSpace.garbage_collect
-
# cache["strong"] # "foo"
-
# cache["soft"] # nil
-
#
-
# See AbstractReferenceValueMap for details.
-
1
class SoftValueMap < AbstractReferenceValueMap
-
1
self.reference_class = SoftReference
-
end
-
end
-
1
module Ref
-
# This implementation of Reference holds a strong reference to an object. The
-
# referenced object will not be garbage collected as long as the strong reference
-
# exists.
-
1
class StrongReference < Reference
-
# Create a new strong reference to an object.
-
1
def initialize(obj)
-
@obj = obj
-
@referenced_object_id = obj.__id__
-
end
-
-
# Get the referenced object.
-
1
def object
-
@obj
-
end
-
end
-
end
-
1
module Ref
-
# Implementation of a map in which only weakly referenced keys are kept to the map values.
-
# This allows the garbage collector to reclaim these objects if the only reference to them
-
# is the weak reference in the map.
-
#
-
# This is often useful for cache implementations since the map can be allowed to grow
-
# without bound and the garbage collector can be relied on to clean it up as necessary.
-
# One must be careful, though, when accessing entries since they can be collected at
-
# any time until there is a strong reference to the key.
-
#
-
# === Example usage:
-
#
-
# cache = Ref::WeakKeyMap.new
-
# obj = MyObject.find_by_whatever
-
# obj_info = Service.lookup_object_info(obj)
-
# cache[obj] = Service.lookup_object_info(obj)
-
# cache[obj] # The values looked up from the service
-
# obj = nil
-
# ObjectSpace.garbage_collect
-
# cache.keys # empty array since the keys and values have been reclaimed
-
#
-
# See AbstractReferenceKeyMap for details.
-
1
class WeakKeyMap < AbstractReferenceKeyMap
-
1
self.reference_class = WeakReference
-
end
-
end
-
1
require 'weakref'
-
-
1
module Ref
-
1
class WeakReference < Reference
-
# This implementation of a weak reference simply wraps the standard WeakRef implementation
-
# that comes with the Ruby standard library.
-
1
def initialize(obj) #:nodoc:
-
@referenced_object_id = obj.__id__
-
@ref = ::WeakRef.new(obj)
-
end
-
-
1
def object #:nodoc:
-
@ref.__getobj__
-
rescue => e
-
# Jruby implementation uses RefError while MRI uses WeakRef::RefError
-
if (defined?(RefError) && e.is_a?(RefError)) || (defined?(::WeakRef::RefError) && e.is_a?(::WeakRef::RefError))
-
nil
-
else
-
raise e
-
end
-
end
-
end
-
end
-
1
module Ref
-
# Implementation of a map in which weak references are kept to the map values.
-
# This allows the garbage collector to reclaim these objects if the
-
# only reference to them is the weak reference in the map.
-
#
-
# This is often useful for cache implementations since the map can be allowed to grow
-
# without bound and the garbage collector can be relied on to clean it up as necessary.
-
# One must be careful, though, when accessing entries since the values can be collected
-
# at any time until there is a strong reference to them.
-
#
-
# === Example usage:
-
#
-
# cache = Ref::WeakValueMap.new
-
# foo = "foo"
-
# cache["strong"] = foo # add a value with a strong reference
-
# cache["weak"] = "bar" # add a value without a strong reference
-
# cache["strong"] # "foo"
-
# cache["weak"] # "bar"
-
# ObjectSpace.garbage_collect
-
# cache["strong"] # "foo"
-
# cache["weak"] # nil
-
#
-
# See AbstractReferenceValueMap for details.
-
1
class WeakValueMap < AbstractReferenceValueMap
-
1
self.reference_class = WeakReference
-
end
-
end
-
1
RSpec::Support.require_rspec_core "formatters/helpers"
-
1
require 'stringio'
-
-
1
module RSpec
-
1
module Core
-
1
module Formatters
-
# RSpec's built-in formatters are all subclasses of RSpec::Core::Formatters::BaseTextFormatter.
-
#
-
# @see RSpec::Core::Formatters::BaseTextFormatter
-
# @see RSpec::Core::Reporter
-
# @see RSpec::Core::Formatters::Protocol
-
1
class BaseFormatter
-
# all formatters inheriting from this formatter will receive these notifications
-
1
Formatters.register self, :start, :example_group_started, :close
-
1
attr_accessor :example_group
-
1
attr_reader :output
-
-
# @api public
-
# @param output [IO] the formatter output
-
# @see RSpec::Core::Formatters::Protocol#initialize
-
1
def initialize(output)
-
1
@output = output || StringIO.new
-
1
@example_group = nil
-
end
-
-
# @api public
-
#
-
# @param notification [StartNotification]
-
# @see RSpec::Core::Formatters::Protocol#start
-
1
def start(notification)
-
1
start_sync_output
-
1
@example_count = notification.count
-
end
-
-
# @api public
-
#
-
# @param notification [GroupNotification] containing example_group subclass of `RSpec::Core::ExampleGroup`
-
# @see RSpec::Core::Formatters::Protocol#example_group_started
-
1
def example_group_started(notification)
-
5
@example_group = notification.group
-
end
-
-
# @api public
-
#
-
# @param notification [NullNotification]
-
# @see RSpec::Core::Formatters::Protocol#close
-
1
def close(_notification)
-
restore_sync_output
-
end
-
-
1
private
-
-
1
def start_sync_output
-
1
@old_sync, output.sync = output.sync, true if output_supports_sync
-
end
-
-
1
def restore_sync_output
-
output.sync = @old_sync if output_supports_sync && !output.closed?
-
end
-
-
1
def output_supports_sync
-
1
output.respond_to?(:sync=)
-
end
-
end
-
end
-
end
-
end
-
1
RSpec::Support.require_rspec_core "formatters/base_formatter"
-
1
RSpec::Support.require_rspec_core "formatters/console_codes"
-
-
1
module RSpec
-
1
module Core
-
1
module Formatters
-
# Base for all of RSpec's built-in formatters. See RSpec::Core::Formatters::BaseFormatter
-
# to learn more about all of the methods called by the reporter.
-
#
-
# @see RSpec::Core::Formatters::BaseFormatter
-
# @see RSpec::Core::Reporter
-
1
class BaseTextFormatter < BaseFormatter
-
1
Formatters.register self,
-
:message, :dump_summary, :dump_failures, :dump_pending, :seed
-
-
# @method message
-
# @api public
-
#
-
# Used by the reporter to send messages to the output stream.
-
#
-
# @param notification [MessageNotification] containing message
-
1
def message(notification)
-
output.puts notification.message
-
end
-
-
# @method dump_failures
-
# @api public
-
#
-
# Dumps detailed information about each example failure.
-
#
-
# @param notification [NullNotification]
-
1
def dump_failures(notification)
-
1
return if notification.failure_notifications.empty?
-
1
output.puts notification.fully_formatted_failed_examples
-
end
-
-
# @method dump_summary
-
# @api public
-
#
-
# This method is invoked after the dumping of examples and failures. Each parameter
-
# is assigned to a corresponding attribute.
-
#
-
# @param summary [SummaryNotification] containing duration, example_count,
-
# failure_count and pending_count
-
1
def dump_summary(summary)
-
1
output.puts summary.fully_formatted
-
end
-
-
# @private
-
1
def dump_pending(notification)
-
1
return if notification.pending_examples.empty?
-
1
output.puts notification.fully_formatted_pending_examples
-
end
-
-
# @private
-
1
def seed(notification)
-
1
return unless notification.seed_used?
-
output.puts notification.fully_formatted
-
end
-
-
# @api public
-
#
-
# Invoked at the very end, `close` allows the formatter to clean
-
# up resources, e.g. open streams, etc.
-
#
-
# @param notification [NullNotification]
-
1
def close(_notification)
-
1
return unless IO === output
-
1
return if output.closed? || output == $stdout
-
-
output.close
-
end
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Core
-
1
module Formatters
-
# ConsoleCodes provides helpers for formatting console output
-
# with ANSI codes, e.g. color's and bold.
-
1
module ConsoleCodes
-
# @private
-
1
VT100_CODES =
-
{
-
:black => 30,
-
:red => 31,
-
:green => 32,
-
:yellow => 33,
-
:blue => 34,
-
:magenta => 35,
-
:cyan => 36,
-
:white => 37,
-
:bold => 1,
-
}
-
# @private
-
1
VT100_CODE_VALUES = VT100_CODES.invert
-
-
1
module_function
-
-
# Fetches the correct code for the supplied symbol, or checks
-
# that a code is valid. Defaults to white (37).
-
#
-
# @param code_or_symbol [Symbol, Fixnum] Symbol or code to check
-
# @return [Fixnum] a console code
-
1
def console_code_for(code_or_symbol)
-
31
if RSpec.configuration.respond_to?(:"#{code_or_symbol}_color")
-
12
console_code_for configuration_color(code_or_symbol)
-
19
elsif VT100_CODE_VALUES.key?(code_or_symbol)
-
code_or_symbol
-
else
-
19
VT100_CODES.fetch(code_or_symbol) do
-
console_code_for(:white)
-
end
-
end
-
end
-
-
# Wraps a piece of text in ANSI codes with the supplied code. Will
-
# only apply the control code if `RSpec.configuration.color_enabled?`
-
# returns true.
-
#
-
# @param text [String] the text to wrap
-
# @param code_or_symbol [Symbol, Fixnum] the desired control code
-
# @return [String] the wrapped text
-
1
def wrap(text, code_or_symbol)
-
19
if RSpec.configuration.color_enabled?
-
19
"\e[#{console_code_for(code_or_symbol)}m#{text}\e[0m"
-
else
-
text
-
end
-
end
-
-
# @private
-
1
def configuration_color(code)
-
12
RSpec.configuration.__send__(:"#{code}_color")
-
end
-
end
-
end
-
end
-
end
-
1
RSpec::Support.require_rspec_core "formatters/base_text_formatter"
-
-
1
module RSpec
-
1
module Core
-
1
module Formatters
-
# @private
-
1
class ProgressFormatter < BaseTextFormatter
-
1
Formatters.register self, :example_passed, :example_pending, :example_failed, :start_dump
-
-
1
def example_passed(_notification)
-
3
output.print ConsoleCodes.wrap('.', :success)
-
end
-
-
1
def example_pending(_notification)
-
2
output.print ConsoleCodes.wrap('*', :pending)
-
end
-
-
1
def example_failed(_notification)
-
1
output.print ConsoleCodes.wrap('F', :failure)
-
end
-
-
1
def start_dump(_notification)
-
1
output.puts
-
end
-
end
-
end
-
end
-
end
-
1
require 'rspec/mocks'
-
-
1
module RSpec
-
1
module Core
-
1
module MockingAdapters
-
# @private
-
1
module RSpec
-
1
include ::RSpec::Mocks::ExampleMethods
-
-
1
def self.framework_name
-
1
:rspec
-
end
-
-
1
def self.configuration
-
1
::RSpec::Mocks.configuration
-
end
-
-
1
def setup_mocks_for_rspec
-
4
::RSpec::Mocks.setup
-
end
-
-
1
def verify_mocks_for_rspec
-
3
::RSpec::Mocks.verify
-
end
-
-
1
def teardown_mocks_for_rspec
-
4
::RSpec::Mocks.teardown
-
end
-
end
-
end
-
end
-
end
-
1
require 'rspec/support'
-
1
RSpec::Support.require_rspec_support "caller_filter"
-
1
RSpec::Support.require_rspec_support "warnings"
-
-
1
require 'rspec/matchers'
-
-
7
RSpec::Support.define_optimized_require_for_rspec(:expectations) { |f| require_relative(f) }
-
-
%w[
-
expectation_target
-
configuration
-
fail_with
-
handler
-
version
-
6
].each { |file| RSpec::Support.require_rspec_expectations(file) }
-
-
1
module RSpec
-
# RSpec::Expectations provides a simple, readable API to express
-
# the expected outcomes in a code example. To express an expected
-
# outcome, wrap an object or block in `expect`, call `to` or `to_not`
-
# (aliased as `not_to`) and pass it a matcher object:
-
#
-
# expect(order.total).to eq(Money.new(5.55, :USD))
-
# expect(list).to include(user)
-
# expect(message).not_to match(/foo/)
-
# expect { do_something }.to raise_error
-
#
-
# The last form (the block form) is needed to match against ruby constructs
-
# that are not objects, but can only be observed when executing a block
-
# of code. This includes raising errors, throwing symbols, yielding,
-
# and changing values.
-
#
-
# When `expect(...).to` is invoked with a matcher, it turns around
-
# and calls `matcher.matches?(<object wrapped by expect>)`. For example,
-
# in the expression:
-
#
-
# expect(order.total).to eq(Money.new(5.55, :USD))
-
#
-
# ...`eq(Money.new(5.55, :USD))` returns a matcher object, and it results
-
# in the equivalent of `eq.matches?(order.total)`. If `matches?` returns
-
# `true`, the expectation is met and execution continues. If `false`, then
-
# the spec fails with the message returned by `eq.failure_message`.
-
#
-
# Given the expression:
-
#
-
# expect(order.entries).not_to include(entry)
-
#
-
# ...the `not_to` method (also available as `to_not`) invokes the equivalent of
-
# `include.matches?(order.entries)`, but it interprets `false` as success, and
-
# `true` as a failure, using the message generated by
-
# `include.failure_message_when_negated`.
-
#
-
# rspec-expectations ships with a standard set of useful matchers, and writing
-
# your own matchers is quite simple.
-
#
-
# See [RSpec::Matchers](../RSpec/Matchers) for more information about the
-
# built-in matchers that ship with rspec-expectations, and how to write your
-
# own custom matchers.
-
1
module Expectations
-
# Exception raised when an expectation fails.
-
#
-
# @note We subclass Exception so that in a stub implementation if
-
# the user sets an expectation, it can't be caught in their
-
# code by a bare `rescue`.
-
# @api public
-
1
ExpectationNotMetError = Class.new(::Exception)
-
end
-
end
-
1
module RSpec
-
1
module Expectations
-
# Wraps the target of an expectation.
-
#
-
# @example
-
# expect(something) # => ExpectationTarget wrapping something
-
# expect { do_something } # => ExpectationTarget wrapping the block
-
#
-
# # used with `to`
-
# expect(actual).to eq(3)
-
#
-
# # with `not_to`
-
# expect(actual).not_to eq(3)
-
#
-
# @note `ExpectationTarget` is not intended to be instantiated
-
# directly by users. Use `expect` instead.
-
1
class ExpectationTarget
-
# @private
-
# Used as a sentinel value to be able to tell when the user
-
# did not pass an argument. We can't use `nil` for that because
-
# `nil` is a valid value to pass.
-
1
UndefinedValue = Module.new
-
-
# @api private
-
1
def initialize(value)
-
4
@target = value
-
end
-
-
# @private
-
1
def self.for(value, block)
-
4
if UndefinedValue.equal?(value)
-
1
unless block
-
raise ArgumentError, "You must pass either an argument or a block to `expect`."
-
end
-
1
BlockExpectationTarget.new(block)
-
3
elsif block
-
raise ArgumentError, "You cannot pass both an argument and a block to `expect`."
-
else
-
3
new(value)
-
end
-
end
-
-
# Runs the given expectation, passing if `matcher` returns true.
-
# @example
-
# expect(value).to eq(5)
-
# expect { perform }.to raise_error
-
# @param [Matcher]
-
# matcher
-
# @param [String or Proc] message optional message to display when the expectation fails
-
# @return [Boolean] true if the expectation succeeds (else raises)
-
# @see RSpec::Matchers
-
1
def to(matcher=nil, message=nil, &block)
-
4
prevent_operator_matchers(:to) unless matcher
-
4
RSpec::Expectations::PositiveExpectationHandler.handle_matcher(@target, matcher, message, &block)
-
end
-
-
# Runs the given expectation, passing if `matcher` returns false.
-
# @example
-
# expect(value).not_to eq(5)
-
# @param [Matcher]
-
# matcher
-
# @param [String or Proc] message optional message to display when the expectation fails
-
# @return [Boolean] false if the negative expectation succeeds (else raises)
-
# @see RSpec::Matchers
-
1
def not_to(matcher=nil, message=nil, &block)
-
prevent_operator_matchers(:not_to) unless matcher
-
RSpec::Expectations::NegativeExpectationHandler.handle_matcher(@target, matcher, message, &block)
-
end
-
1
alias to_not not_to
-
-
1
private
-
-
1
def prevent_operator_matchers(verb)
-
raise ArgumentError, "The expect syntax does not support operator matchers, " \
-
"so you must pass a matcher to `##{verb}`."
-
end
-
end
-
-
# @private
-
# Validates the provided matcher to ensure it supports block
-
# expectations, in order to avoid user confusion when they
-
# use a block thinking the expectation will be on the return
-
# value of the block rather than the block itself.
-
1
class BlockExpectationTarget < ExpectationTarget
-
1
def to(matcher, message=nil, &block)
-
1
enforce_block_expectation(matcher)
-
1
super
-
end
-
-
1
def not_to(matcher, message=nil, &block)
-
enforce_block_expectation(matcher)
-
super
-
end
-
1
alias to_not not_to
-
-
1
private
-
-
1
def enforce_block_expectation(matcher)
-
1
return if supports_block_expectations?(matcher)
-
-
raise ExpectationNotMetError, "You must pass an argument rather than " \
-
"a block to use the provided matcher (#{description_of matcher}), or " \
-
"the matcher must implement `supports_block_expectations?`."
-
end
-
-
1
def supports_block_expectations?(matcher)
-
1
matcher.supports_block_expectations?
-
rescue NoMethodError
-
false
-
end
-
-
1
def description_of(matcher)
-
matcher.description
-
rescue NoMethodError
-
matcher.inspect
-
end
-
end
-
end
-
end
-
1
RSpec::Support.require_rspec_support 'differ'
-
-
1
module RSpec
-
1
module Expectations
-
1
class << self
-
# @private
-
1
def differ
-
1
RSpec::Support::Differ.new(
-
:object_preparer => lambda { |object| RSpec::Matchers::Composable.surface_descriptions_in(object) },
-
:color => RSpec::Matchers.configuration.color?
-
)
-
end
-
-
# Raises an RSpec::Expectations::ExpectationNotMetError with message.
-
# @param [String] message
-
# @param [Object] expected
-
# @param [Object] actual
-
#
-
# Adds a diff to the failure message when `expected` and `actual` are
-
# both present.
-
1
def fail_with(message, expected=nil, actual=nil)
-
1
unless message
-
raise ArgumentError, "Failure message is nil. Does your matcher define the " \
-
"appropriate failure_message[_when_negated] method to return a string?"
-
end
-
-
1
diff = differ.diff(actual, expected)
-
1
message = "#{message}\nDiff:#{diff}" unless diff.empty?
-
-
1
raise RSpec::Expectations::ExpectationNotMetError, message
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Expectations
-
# @private
-
1
module ExpectationHelper
-
1
def self.check_message(msg)
-
4
unless msg.nil? || msg.respond_to?(:to_str) || msg.respond_to?(:call)
-
::Kernel.warn [
-
"WARNING: ignoring the provided expectation message argument (",
-
msg.inspect,
-
") since it is not a string or a proc."
-
].join
-
end
-
end
-
-
# Returns an RSpec-3+ compatible matcher, wrapping a legacy one
-
# in an adapter if necessary.
-
#
-
# @private
-
1
def self.modern_matcher_from(matcher)
-
LegacyMatcherAdapter::RSpec2.wrap(matcher) ||
-
4
LegacyMatcherAdapter::RSpec1.wrap(matcher) || matcher
-
end
-
-
1
def self.setup(handler, matcher, message)
-
4
check_message(message)
-
4
::RSpec::Matchers.last_expectation_handler = handler
-
4
::RSpec::Matchers.last_matcher = modern_matcher_from(matcher)
-
end
-
-
1
def self.handle_failure(matcher, message, failure_message_method)
-
1
message = message.call if message.respond_to?(:call)
-
1
message ||= matcher.__send__(failure_message_method)
-
-
1
if matcher.respond_to?(:diffable?) && matcher.diffable?
-
::RSpec::Expectations.fail_with message, matcher.expected, matcher.actual
-
else
-
1
::RSpec::Expectations.fail_with message
-
end
-
end
-
end
-
-
# @private
-
1
class PositiveExpectationHandler
-
1
def self.handle_matcher(actual, initial_matcher, message=nil, &block)
-
4
matcher = ExpectationHelper.setup(self, initial_matcher, message)
-
-
4
return ::RSpec::Matchers::BuiltIn::PositiveOperatorMatcher.new(actual) unless initial_matcher
-
4
matcher.matches?(actual, &block) || ExpectationHelper.handle_failure(matcher, message, :failure_message)
-
end
-
-
1
def self.verb
-
"should"
-
end
-
-
1
def self.should_method
-
:should
-
end
-
-
1
def self.opposite_should_method
-
:should_not
-
end
-
end
-
-
# @private
-
1
class NegativeExpectationHandler
-
1
def self.handle_matcher(actual, initial_matcher, message=nil, &block)
-
matcher = ExpectationHelper.setup(self, initial_matcher, message)
-
-
return ::RSpec::Matchers::BuiltIn::NegativeOperatorMatcher.new(actual) unless initial_matcher
-
!(does_not_match?(matcher, actual, &block) || ExpectationHelper.handle_failure(matcher, message, :failure_message_when_negated))
-
end
-
-
1
def self.does_not_match?(matcher, actual, &block)
-
if matcher.respond_to?(:does_not_match?)
-
matcher.does_not_match?(actual, &block)
-
else
-
!matcher.matches?(actual, &block)
-
end
-
end
-
-
1
def self.verb
-
"should not"
-
end
-
-
1
def self.should_method
-
:should_not
-
end
-
-
1
def self.opposite_should_method
-
:should
-
end
-
end
-
-
# Wraps a matcher written against one of the legacy protocols in
-
# order to present the current protocol.
-
#
-
# @private
-
1
class LegacyMatcherAdapter < Matchers::MatcherDelegator
-
1
def initialize(matcher)
-
super
-
::RSpec.warn_deprecation(<<-EOS.gsub(/^\s+\|/, ''), :type => "legacy_matcher")
-
|#{matcher.class.name || matcher.inspect} implements a legacy RSpec matcher
-
|protocol. For the current protocol you should expose the failure messages
-
|via the `failure_message` and `failure_message_when_negated` methods.
-
|(Used from #{CallerFilter.first_non_rspec_line})
-
EOS
-
end
-
-
1
def self.wrap(matcher)
-
8
new(matcher) if interface_matches?(matcher)
-
end
-
-
# Starting in RSpec 1.2 (and continuing through all 2.x releases),
-
# the failure message protocol was:
-
# * `failure_message_for_should`
-
# * `failure_message_for_should_not`
-
# @private
-
1
class RSpec2 < self
-
1
def failure_message
-
base_matcher.failure_message_for_should
-
end
-
-
1
def failure_message_when_negated
-
base_matcher.failure_message_for_should_not
-
end
-
-
1
def self.interface_matches?(matcher)
-
(
-
!matcher.respond_to?(:failure_message) &&
-
4
matcher.respond_to?(:failure_message_for_should)
-
) || (
-
!matcher.respond_to?(:failure_message_when_negated) &&
-
4
matcher.respond_to?(:failure_message_for_should_not)
-
4
)
-
end
-
end
-
-
# Before RSpec 1.2, the failure message protocol was:
-
# * `failure_message`
-
# * `negative_failure_message`
-
# @private
-
1
class RSpec1 < self
-
1
def failure_message
-
base_matcher.failure_message
-
end
-
-
1
def failure_message_when_negated
-
base_matcher.negative_failure_message
-
end
-
-
# Note: `failure_message` is part of the RSpec 3 protocol
-
# (paired with `failure_message_when_negated`), so we don't check
-
# for `failure_message` here.
-
1
def self.interface_matches?(matcher)
-
!matcher.respond_to?(:failure_message_when_negated) &&
-
4
matcher.respond_to?(:negative_failure_message)
-
end
-
end
-
end
-
-
# RSpec 3.0 was released with the class name misspelled. For SemVer compatibility,
-
# we will provide this misspelled alias until 4.0.
-
# @deprecated Use LegacyMatcherAdapter instead.
-
# @private
-
1
LegacyMacherAdapter = LegacyMatcherAdapter
-
end
-
end
-
1
module RSpec
-
1
module Expectations
-
# @api private
-
# Provides methods for enabling and disabling the available
-
# syntaxes provided by rspec-expectations.
-
1
module Syntax
-
1
module_function
-
-
# @api private
-
# Determines where we add `should` and `should_not`.
-
1
def default_should_host
-
4
@default_should_host ||= ::Object.ancestors.last
-
end
-
-
# @api private
-
# Instructs rspec-expectations to warn on first usage of `should` or `should_not`.
-
# Enabled by default. This is largely here to facilitate testing.
-
1
def warn_about_should!
-
1
@warn_about_should = true
-
end
-
-
# @api private
-
# Generates a deprecation warning for the given method if no warning
-
# has already been issued.
-
1
def warn_about_should_unless_configured(method_name)
-
return unless @warn_about_should
-
-
RSpec.deprecate(
-
"Using `#{method_name}` from rspec-expectations' old `:should` syntax without explicitly enabling the syntax",
-
:replacement => "the new `:expect` syntax or explicitly enable `:should`"
-
)
-
-
@warn_about_should = false
-
end
-
-
# @api private
-
# Enables the `should` syntax.
-
1
def enable_should(syntax_host=default_should_host)
-
2
@warn_about_should = false if syntax_host == default_should_host
-
2
return if should_enabled?(syntax_host)
-
-
1
syntax_host.module_exec do
-
1
def should(matcher=nil, message=nil, &block)
-
::RSpec::Expectations::Syntax.warn_about_should_unless_configured(__method__)
-
::RSpec::Expectations::PositiveExpectationHandler.handle_matcher(self, matcher, message, &block)
-
end
-
-
1
def should_not(matcher=nil, message=nil, &block)
-
::RSpec::Expectations::Syntax.warn_about_should_unless_configured(__method__)
-
::RSpec::Expectations::NegativeExpectationHandler.handle_matcher(self, matcher, message, &block)
-
end
-
end
-
end
-
-
# @api private
-
# Disables the `should` syntax.
-
1
def disable_should(syntax_host=default_should_host)
-
return unless should_enabled?(syntax_host)
-
-
syntax_host.module_exec do
-
undef should
-
undef should_not
-
end
-
end
-
-
# @api private
-
# Enables the `expect` syntax.
-
1
def enable_expect(syntax_host=::RSpec::Matchers)
-
1
return if expect_enabled?(syntax_host)
-
-
1
syntax_host.module_exec do
-
1
def expect(value=::RSpec::Expectations::ExpectationTarget::UndefinedValue, &block)
-
4
::RSpec::Expectations::ExpectationTarget.for(value, block)
-
end
-
end
-
end
-
-
# @api private
-
# Disables the `expect` syntax.
-
1
def disable_expect(syntax_host=::RSpec::Matchers)
-
return unless expect_enabled?(syntax_host)
-
-
syntax_host.module_exec do
-
undef expect
-
end
-
end
-
-
# @api private
-
# Indicates whether or not the `should` syntax is enabled.
-
1
def should_enabled?(syntax_host=default_should_host)
-
3
syntax_host.method_defined?(:should)
-
end
-
-
# @api private
-
# Indicates whether or not the `expect` syntax is enabled.
-
1
def expect_enabled?(syntax_host=::RSpec::Matchers)
-
2
syntax_host.method_defined?(:expect)
-
end
-
end
-
end
-
end
-
-
1
if defined?(BasicObject)
-
# The legacy `:should` syntax adds the following methods directly to
-
# `BasicObject` so that they are available off of any object. Note, however,
-
# that this syntax does not always play nice with delegate/proxy objects.
-
# We recommend you use the non-monkeypatching `:expect` syntax instead.
-
1
class BasicObject
-
# @method should
-
# Passes if `matcher` returns true. Available on every `Object`.
-
# @example
-
# actual.should eq expected
-
# actual.should match /expression/
-
# @param [Matcher]
-
# matcher
-
# @param [String] message optional message to display when the expectation fails
-
# @return [Boolean] true if the expectation succeeds (else raises)
-
# @note This is only available when you have enabled the `:should` syntax.
-
# @see RSpec::Matchers
-
-
# @method should_not
-
# Passes if `matcher` returns false. Available on every `Object`.
-
# @example
-
# actual.should_not eq expected
-
# @param [Matcher]
-
# matcher
-
# @param [String] message optional message to display when the expectation fails
-
# @return [Boolean] false if the negative expectation succeeds (else raises)
-
# @note This is only available when you have enabled the `:should` syntax.
-
# @see RSpec::Matchers
-
end
-
end
-
1
module RSpec
-
1
module Expectations
-
# @private
-
1
module Version
-
1
STRING = '3.1.2'
-
end
-
end
-
end
-
1
require 'rspec/support'
-
1
RSpec::Support.require_rspec_support 'matcher_definition'
-
9
RSpec::Support.define_optimized_require_for_rspec(:matchers) { |f| require_relative(f) }
-
-
%w[
-
pretty
-
composable
-
built_in
-
generated_descriptions
-
dsl
-
matcher_delegator
-
aliased_matcher
-
8
].each { |file| RSpec::Support.require_rspec_matchers(file) }
-
-
# RSpec's top level namespace. All of rspec-expectations is contained
-
# in the `RSpec::Expectations` and `RSpec::Matchers` namespaces.
-
1
module RSpec
-
# RSpec::Matchers provides a number of useful matchers we use to define
-
# expectations. Any object that implements the [matcher protocol](Matchers/MatcherProtocol)
-
# can be used as a matcher.
-
#
-
# ## Predicates
-
#
-
# In addition to matchers that are defined explicitly, RSpec will create
-
# custom matchers on the fly for any arbitrary predicate, giving your specs a
-
# much more natural language feel.
-
#
-
# A Ruby predicate is a method that ends with a "?" and returns true or false.
-
# Common examples are `empty?`, `nil?`, and `instance_of?`.
-
#
-
# All you need to do is write `expect(..).to be_` followed by the predicate
-
# without the question mark, and RSpec will figure it out from there.
-
# For example:
-
#
-
# expect([]).to be_empty # => [].empty?() | passes
-
# expect([]).not_to be_empty # => [].empty?() | fails
-
#
-
# In addtion to prefixing the predicate matchers with "be_", you can also use "be_a_"
-
# and "be_an_", making your specs read much more naturally:
-
#
-
# expect("a string").to be_an_instance_of(String) # =>"a string".instance_of?(String) # passes
-
#
-
# expect(3).to be_a_kind_of(Fixnum) # => 3.kind_of?(Numeric) | passes
-
# expect(3).to be_a_kind_of(Numeric) # => 3.kind_of?(Numeric) | passes
-
# expect(3).to be_an_instance_of(Fixnum) # => 3.instance_of?(Fixnum) | passes
-
# expect(3).not_to be_an_instance_of(Numeric) # => 3.instance_of?(Numeric) | fails
-
#
-
# RSpec will also create custom matchers for predicates like `has_key?`. To
-
# use this feature, just state that the object should have_key(:key) and RSpec will
-
# call has_key?(:key) on the target. For example:
-
#
-
# expect(:a => "A").to have_key(:a)
-
# expect(:a => "A").to have_key(:b) # fails
-
#
-
# You can use this feature to invoke any predicate that begins with "has_", whether it is
-
# part of the Ruby libraries (like `Hash#has_key?`) or a method you wrote on your own class.
-
#
-
# Note that RSpec does not provide composable aliases for these dynamic predicate
-
# matchers. You can easily define your own aliases, though:
-
#
-
# RSpec::Matchers.alias_matcher :a_user_who_is_an_admin, :be_an_admin
-
# expect(user_list).to include(a_user_who_is_an_admin)
-
#
-
# ## Custom Matchers
-
#
-
# When you find that none of the stock matchers provide a natural feeling
-
# expectation, you can very easily write your own using RSpec's matcher DSL
-
# or writing one from scratch.
-
#
-
# ### Matcher DSL
-
#
-
# Imagine that you are writing a game in which players can be in various
-
# zones on a virtual board. To specify that bob should be in zone 4, you
-
# could say:
-
#
-
# expect(bob.current_zone).to eql(Zone.new("4"))
-
#
-
# But you might find it more expressive to say:
-
#
-
# expect(bob).to be_in_zone("4")
-
#
-
# and/or
-
#
-
# expect(bob).not_to be_in_zone("3")
-
#
-
# You can create such a matcher like so:
-
#
-
# RSpec::Matchers.define :be_in_zone do |zone|
-
# match do |player|
-
# player.in_zone?(zone)
-
# end
-
# end
-
#
-
# This will generate a <tt>be_in_zone</tt> method that returns a matcher
-
# with logical default messages for failures. You can override the failure
-
# messages and the generated description as follows:
-
#
-
# RSpec::Matchers.define :be_in_zone do |zone|
-
# match do |player|
-
# player.in_zone?(zone)
-
# end
-
#
-
# failure_message do |player|
-
# # generate and return the appropriate string.
-
# end
-
#
-
# failure_message_when_negated do |player|
-
# # generate and return the appropriate string.
-
# end
-
#
-
# description do
-
# # generate and return the appropriate string.
-
# end
-
# end
-
#
-
# Each of the message-generation methods has access to the block arguments
-
# passed to the <tt>create</tt> method (in this case, <tt>zone</tt>). The
-
# failure message methods (<tt>failure_message</tt> and
-
# <tt>failure_message_when_negated</tt>) are passed the actual value (the
-
# receiver of <tt>expect(..)</tt> or <tt>expect(..).not_to</tt>).
-
#
-
# ### Custom Matcher from scratch
-
#
-
# You could also write a custom matcher from scratch, as follows:
-
#
-
# class BeInZone
-
# def initialize(expected)
-
# @expected = expected
-
# end
-
#
-
# def matches?(target)
-
# @target = target
-
# @target.current_zone.eql?(Zone.new(@expected))
-
# end
-
#
-
# def failure_message
-
# "expected #{@target.inspect} to be in Zone #{@expected}"
-
# end
-
#
-
# def failure_message_when_negated
-
# "expected #{@target.inspect} not to be in Zone #{@expected}"
-
# end
-
# end
-
#
-
# ... and a method like this:
-
#
-
# def be_in_zone(expected)
-
# BeInZone.new(expected)
-
# end
-
#
-
# And then expose the method to your specs. This is normally done
-
# by including the method and the class in a module, which is then
-
# included in your spec:
-
#
-
# module CustomGameMatchers
-
# class BeInZone
-
# # ...
-
# end
-
#
-
# def be_in_zone(expected)
-
# # ...
-
# end
-
# end
-
#
-
# describe "Player behaviour" do
-
# include CustomGameMatchers
-
# # ...
-
# end
-
#
-
# or you can include in globally in a spec_helper.rb file <tt>require</tt>d
-
# from your spec file(s):
-
#
-
# RSpec::configure do |config|
-
# config.include(CustomGameMatchers)
-
# end
-
#
-
# ### Making custom matchers composable
-
#
-
# RSpec's built-in matchers are designed to be composed, in expressions like:
-
#
-
# expect(["barn", 2.45]).to contain_exactly(
-
# a_value_within(0.1).of(2.5),
-
# a_string_starting_with("bar")
-
# )
-
#
-
# Custom matchers can easily participate in composed matcher expressions like these.
-
# Include {RSpec::Matchers::Composable} in your custom matcher to make it support
-
# being composed (matchers defined using the DSL have this included automatically).
-
# Within your matcher's `matches?` method (or the `match` block, if using the DSL),
-
# use `values_match?(expected, actual)` rather than `expected == actual`.
-
# Under the covers, `values_match?` is able to match arbitrary
-
# nested data structures containing a mix of both matchers and non-matcher objects.
-
# It uses `===` and `==` to perform the matching, considering the values to
-
# match if either returns `true`. The `Composable` mixin also provides some helper
-
# methods for surfacing the matcher descriptions within your matcher's description
-
# or failure messages.
-
#
-
# RSpec's built-in matchers each have a number of aliases that rephrase the matcher
-
# from a verb phrase (such as `be_within`) to a noun phrase (such as `a_value_within`),
-
# which reads better when the matcher is passed as an argument in a composed matcher
-
# expressions, and also uses the noun-phrase wording in the matcher's `description`,
-
# for readable failure messages. You can alias your custom matchers in similar fashion
-
# using {RSpec::Matchers.alias_matcher}.
-
1
module Matchers
-
# @method expect
-
# Supports `expect(actual).to matcher` syntax by wrapping `actual` in an
-
# `ExpectationTarget`.
-
# @example
-
# expect(actual).to eq(expected)
-
# expect(actual).not_to eq(expected)
-
# @return [ExpectationTarget]
-
# @see ExpectationTarget#to
-
# @see ExpectationTarget#not_to
-
-
# Defines a matcher alias. The returned matcher's `description` will be overriden
-
# to reflect the phrasing of the new name, which will be used in failure messages
-
# when passed as an argument to another matcher in a composed matcher expression.
-
#
-
# @param new_name [Symbol] the new name for the matcher
-
# @param old_name [Symbol] the original name for the matcher
-
# @param options [Hash] options for the aliased matcher
-
# @option options [Class] :klass the ruby class to use as the decorator. (Not normally used).
-
# @yield [String] optional block that, when given is used to define the overriden
-
# description. The yielded arg is the original description. If no block is
-
# provided, a default description override is used based on the old and
-
# new names.
-
#
-
# @example
-
#
-
# RSpec::Matchers.alias_matcher :a_list_that_sums_to, :sum_to
-
# sum_to(3).description # => "sum to 3"
-
# a_list_that_sums_to(3).description # => "a list that sums to 3"
-
#
-
# @example
-
#
-
# RSpec::Matchers.alias_matcher :a_list_sorted_by, :be_sorted_by do |description|
-
# description.sub("be sorted by", "a list sorted by")
-
# end
-
#
-
# be_sorted_by(:age).description # => "be sorted by age"
-
# a_list_sorted_by(:age).description # => "a list sorted by age"
-
#
-
# @!macro [attach] alias_matcher
-
# @!parse
-
# alias $1 $2
-
1
def self.alias_matcher(new_name, old_name, options={}, &description_override)
-
description_override ||= lambda do |old_desc|
-
old_desc.gsub(Pretty.split_words(old_name), Pretty.split_words(new_name))
-
57
end
-
113
klass = options.fetch(:klass) { AliasedMatcher }
-
-
57
define_method(new_name) do |*args, &block|
-
matcher = __send__(old_name, *args, &block)
-
klass.new(matcher, description_override)
-
end
-
end
-
-
# Defines a negated matcher. The returned matcher's `description` and `failure_message`
-
# will be overriden to reflect the phrasing of the new name, and the match logic will
-
# be based on the original matcher but negated.
-
#
-
# @param negated_name [Symbol] the name for the negated matcher
-
# @param base_name [Symbol] the name of the original matcher that will be negated
-
# @yield [String] optional block that, when given is used to define the overriden
-
# description. The yielded arg is the original description. If no block is
-
# provided, a default description override is used based on the old and
-
# new names.
-
#
-
# @example
-
#
-
# RSpec::Matchers.define_negated_matcher :a_value_not_between, :a_value_between
-
# a_value_between(3, 5).description # => "a value between 3 and 5"
-
# a_value_not_between(3, 5).description # => "a value not between 3 and 5"
-
1
def self.define_negated_matcher(negated_name, base_name, &description_override)
-
alias_matcher(negated_name, base_name, :klass => AliasedNegatedMatcher, &description_override)
-
end
-
-
# Passes if actual is truthy (anything but false or nil)
-
1
def be_truthy
-
BuiltIn::BeTruthy.new
-
end
-
1
alias_matcher :a_truthy_value, :be_truthy
-
-
# Passes if actual is falsey (false or nil)
-
1
def be_falsey
-
BuiltIn::BeFalsey.new
-
end
-
1
alias_matcher :be_falsy, :be_falsey
-
1
alias_matcher :a_falsey_value, :be_falsey
-
1
alias_matcher :a_falsy_value, :be_falsey
-
-
# Passes if actual is nil
-
1
def be_nil
-
BuiltIn::BeNil.new
-
end
-
1
alias_matcher :a_nil_value, :be_nil
-
-
# @example
-
# expect(actual).to be_truthy
-
# expect(actual).to be_falsey
-
# expect(actual).to be_nil
-
# expect(actual).to be_[arbitrary_predicate](*args)
-
# expect(actual).not_to be_nil
-
# expect(actual).not_to be_[arbitrary_predicate](*args)
-
#
-
# Given true, false, or nil, will pass if actual value is true, false or
-
# nil (respectively). Given no args means the caller should satisfy an if
-
# condition (to be or not to be).
-
#
-
# Predicates are any Ruby method that ends in a "?" and returns true or
-
# false. Given be_ followed by arbitrary_predicate (without the "?"),
-
# RSpec will match convert that into a query against the target object.
-
#
-
# The arbitrary_predicate feature will handle any predicate prefixed with
-
# "be_an_" (e.g. be_an_instance_of), "be_a_" (e.g. be_a_kind_of) or "be_"
-
# (e.g. be_empty), letting you choose the prefix that best suits the
-
# predicate.
-
1
def be(*args)
-
args.empty? ? Matchers::BuiltIn::Be.new : equal(*args)
-
end
-
1
alias_matcher :a_value, :be, :klass => AliasedMatcherWithOperatorSupport
-
-
# passes if target.kind_of?(klass)
-
1
def be_a(klass)
-
be_a_kind_of(klass)
-
end
-
1
alias_method :be_an, :be_a
-
-
# Passes if actual.instance_of?(expected)
-
#
-
# @example
-
#
-
# expect(5).to be_an_instance_of(Fixnum)
-
# expect(5).not_to be_an_instance_of(Numeric)
-
# expect(5).not_to be_an_instance_of(Float)
-
1
def be_an_instance_of(expected)
-
BuiltIn::BeAnInstanceOf.new(expected)
-
end
-
1
alias_method :be_instance_of, :be_an_instance_of
-
1
alias_matcher :an_instance_of, :be_an_instance_of
-
-
# Passes if actual.kind_of?(expected)
-
#
-
# @example
-
#
-
# expect(5).to be_a_kind_of(Fixnum)
-
# expect(5).to be_a_kind_of(Numeric)
-
# expect(5).not_to be_a_kind_of(Float)
-
1
def be_a_kind_of(expected)
-
BuiltIn::BeAKindOf.new(expected)
-
end
-
1
alias_method :be_kind_of, :be_a_kind_of
-
1
alias_matcher :a_kind_of, :be_a_kind_of
-
-
# Passes if actual.between?(min, max). Works with any Comparable object,
-
# including String, Symbol, Time, or Numeric (Fixnum, Bignum, Integer,
-
# Float, Complex, and Rational).
-
#
-
# By default, `be_between` is inclusive (i.e. passes when given either the max or min value),
-
# but you can make it `exclusive` by chaining that off the matcher.
-
#
-
# @example
-
#
-
# expect(5).to be_between(1, 10)
-
# expect(11).not_to be_between(1, 10)
-
# expect(10).not_to be_between(1, 10).exclusive
-
1
def be_between(min, max)
-
BuiltIn::BeBetween.new(min, max)
-
end
-
1
alias_matcher :a_value_between, :be_between
-
-
# Passes if actual == expected +/- delta
-
#
-
# @example
-
#
-
# expect(result).to be_within(0.5).of(3.0)
-
# expect(result).not_to be_within(0.5).of(3.0)
-
1
def be_within(delta)
-
BuiltIn::BeWithin.new(delta)
-
end
-
1
alias_matcher :a_value_within, :be_within
-
1
alias_matcher :within, :be_within
-
-
# Applied to a proc, specifies that its execution will cause some value to
-
# change.
-
#
-
# @param [Object] receiver
-
# @param [Symbol] message the message to send the receiver
-
#
-
# You can either pass <tt>receiver</tt> and <tt>message</tt>, or a block,
-
# but not both.
-
#
-
# When passing a block, it must use the `{ ... }` format, not
-
# do/end, as `{ ... }` binds to the `change` method, whereas do/end
-
# would errantly bind to the `expect(..).to` or `expect(...).not_to` method.
-
#
-
# You can chain any of the following off of the end to specify details
-
# about the change:
-
#
-
# * `from`
-
# * `to`
-
#
-
# or any one of:
-
#
-
# * `by`
-
# * `by_at_least`
-
# * `by_at_most`
-
#
-
# @example
-
#
-
# expect {
-
# team.add_player(player)
-
# }.to change(roster, :count)
-
#
-
# expect {
-
# team.add_player(player)
-
# }.to change(roster, :count).by(1)
-
#
-
# expect {
-
# team.add_player(player)
-
# }.to change(roster, :count).by_at_least(1)
-
#
-
# expect {
-
# team.add_player(player)
-
# }.to change(roster, :count).by_at_most(1)
-
#
-
# string = "string"
-
# expect {
-
# string.reverse!
-
# }.to change { string }.from("string").to("gnirts")
-
#
-
# string = "string"
-
# expect {
-
# string
-
# }.not_to change { string }.from("string")
-
#
-
# expect {
-
# person.happy_birthday
-
# }.to change(person, :birthday).from(32).to(33)
-
#
-
# expect {
-
# employee.develop_great_new_social_networking_app
-
# }.to change(employee, :title).from("Mail Clerk").to("CEO")
-
#
-
# expect {
-
# doctor.leave_office
-
# }.to change(doctor, :sign).from(/is in/).to(/is out/)
-
#
-
# user = User.new(:type => "admin")
-
# expect {
-
# user.symbolize_type
-
# }.to change(user, :type).from(String).to(Symbol)
-
#
-
# == Notes
-
#
-
# Evaluates `receiver.message` or `block` before and after it
-
# evaluates the block passed to `expect`.
-
#
-
# `expect( ... ).not_to change` supports the form that specifies `from`
-
# (which specifies what you expect the starting, unchanged value to be)
-
# but does not support forms with subsequent calls to `by`, `by_at_least`,
-
# `by_at_most` or `to`.
-
1
def change(receiver=nil, message=nil, &block)
-
1
BuiltIn::Change.new(receiver, message, &block)
-
end
-
1
alias_matcher :a_block_changing, :change
-
1
alias_matcher :changing, :change
-
-
# Passes if actual contains all of the expected regardless of order.
-
# This works for collections. Pass in multiple args and it will only
-
# pass if all args are found in collection.
-
#
-
# @note This is also available using the `=~` operator with `should`,
-
# but `=~` is not supported with `expect`.
-
#
-
# @example
-
#
-
# expect([1, 2, 3]).to contain_exactly(1, 2, 3)
-
# expect([1, 2, 3]).to contain_exactly(1, 3, 2)
-
#
-
# @see #match_array
-
1
def contain_exactly(*items)
-
BuiltIn::ContainExactly.new(items)
-
end
-
1
alias_matcher :a_collection_containing_exactly, :contain_exactly
-
1
alias_matcher :containing_exactly, :contain_exactly
-
-
# Passes if actual covers expected. This works for
-
# Ranges. You can also pass in multiple args
-
# and it will only pass if all args are found in Range.
-
#
-
# @example
-
# expect(1..10).to cover(5)
-
# expect(1..10).to cover(4, 6)
-
# expect(1..10).to cover(4, 6, 11) # fails
-
# expect(1..10).not_to cover(11)
-
# expect(1..10).not_to cover(5) # fails
-
#
-
# ### Warning:: Ruby >= 1.9 only
-
1
def cover(*values)
-
BuiltIn::Cover.new(*values)
-
end
-
1
alias_matcher :a_range_covering, :cover
-
1
alias_matcher :covering, :cover
-
-
# Matches if the actual value ends with the expected value(s). In the case
-
# of a string, matches against the last `expected.length` characters of the
-
# actual string. In the case of an array, matches against the last
-
# `expected.length` elements of the actual array.
-
#
-
# @example
-
#
-
# expect("this string").to end_with "string"
-
# expect([0, 1, 2, 3, 4]).to end_with 4
-
# expect([0, 2, 3, 4, 4]).to end_with 3, 4
-
1
def end_with(*expected)
-
BuiltIn::EndWith.new(*expected)
-
end
-
1
alias_matcher :a_collection_ending_with, :end_with
-
1
alias_matcher :a_string_ending_with, :end_with
-
1
alias_matcher :ending_with, :end_with
-
-
# Passes if <tt>actual == expected</tt>.
-
#
-
# See http://www.ruby-doc.org/core/classes/Object.html#M001057 for more
-
# information about equality in Ruby.
-
#
-
# @example
-
#
-
# expect(5).to eq(5)
-
# expect(5).not_to eq(3)
-
1
def eq(expected)
-
BuiltIn::Eq.new(expected)
-
end
-
1
alias_matcher :an_object_eq_to, :eq
-
1
alias_matcher :eq_to, :eq
-
-
# Passes if `actual.eql?(expected)`
-
#
-
# See http://www.ruby-doc.org/core/classes/Object.html#M001057 for more
-
# information about equality in Ruby.
-
#
-
# @example
-
#
-
# expect(5).to eql(5)
-
# expect(5).not_to eql(3)
-
1
def eql(expected)
-
BuiltIn::Eql.new(expected)
-
end
-
1
alias_matcher :an_object_eql_to, :eql
-
1
alias_matcher :eql_to, :eql
-
-
# Passes if <tt>actual.equal?(expected)</tt> (object identity).
-
#
-
# See http://www.ruby-doc.org/core/classes/Object.html#M001057 for more
-
# information about equality in Ruby.
-
#
-
# @example
-
#
-
# expect(5).to equal(5) # Fixnums are equal
-
# expect("5").not_to equal("5") # Strings that look the same are not the same object
-
1
def equal(expected)
-
BuiltIn::Equal.new(expected)
-
end
-
1
alias_matcher :an_object_equal_to, :equal
-
1
alias_matcher :equal_to, :equal
-
-
# Passes if `actual.exist?` or `actual.exists?`
-
#
-
# @example
-
# expect(File).to exist("path/to/file")
-
1
def exist(*args)
-
BuiltIn::Exist.new(*args)
-
end
-
1
alias_matcher :an_object_existing, :exist
-
1
alias_matcher :existing, :exist
-
-
# Passes if actual's attribute values match the expected attributes hash.
-
# This works no matter how you define your attribute readers.
-
#
-
# @example
-
#
-
# Person = Struct.new(:name, :age)
-
# person = Person.new("Bob", 32)
-
#
-
# expect(person).to have_attributes(:name => "Bob", :age => 32)
-
# expect(person).to have_attributes(:name => a_string_starting_with("B"), :age => (a_value > 30) )
-
#
-
# @note It will fail if actual doesn't respond to any of the expected attributes.
-
#
-
# @example
-
#
-
# expect(person).to have_attributes(:color => "red")
-
1
def have_attributes(expected)
-
BuiltIn::HaveAttributes.new(expected)
-
end
-
1
alias_matcher :an_object_having_attributes, :have_attributes
-
-
# Passes if actual includes expected. This works for
-
# collections and Strings. You can also pass in multiple args
-
# and it will only pass if all args are found in collection.
-
#
-
# @example
-
#
-
# expect([1,2,3]).to include(3)
-
# expect([1,2,3]).to include(2,3)
-
# expect([1,2,3]).to include(2,3,4) # fails
-
# expect([1,2,3]).not_to include(4)
-
# expect("spread").to include("read")
-
# expect("spread").not_to include("red")
-
1
def include(*expected)
-
BuiltIn::Include.new(*expected)
-
end
-
1
alias_matcher :a_collection_including, :include
-
1
alias_matcher :a_string_including, :include
-
1
alias_matcher :a_hash_including, :include
-
1
alias_matcher :including, :include
-
-
# Passes if actual all expected objects pass. This works for
-
# any enumerable object.
-
#
-
# @example
-
#
-
# expect([1, 3, 5]).to all be_odd
-
# expect([1, 3, 6]).to all be_odd # fails
-
#
-
# @note The negative form `not_to all` is not supported. Instead
-
# use `not_to include` or pass a negative form of a matcher
-
# as the argument (e.g. `all exclude(:foo)`).
-
#
-
# @note You can also use this with compound matchers as well.
-
#
-
# @example
-
# expect([1, 3, 5]).to all( be_odd.and be_an(Integer) )
-
1
def all(expected)
-
BuiltIn::All.new(expected)
-
end
-
-
# Given a `Regexp` or `String`, passes if `actual.match(pattern)`
-
# Given an arbitrary nested data structure (e.g. arrays and hashes),
-
# matches if `expected === actual` || `actual == expected` for each
-
# pair of elements.
-
#
-
# @example
-
#
-
# expect(email).to match(/^([^\s]+)((?:[-a-z0-9]+\.)+[a-z]{2,})$/i)
-
# expect(email).to match("@example.com")
-
#
-
# @example
-
#
-
# hash = {
-
# :a => {
-
# :b => ["foo", 5],
-
# :c => { :d => 2.05 }
-
# }
-
# }
-
#
-
# expect(hash).to match(
-
# :a => {
-
# :b => a_collection_containing_exactly(
-
# a_string_starting_with("f"),
-
# an_instance_of(Fixnum)
-
# ),
-
# :c => { :d => (a_value < 3) }
-
# }
-
# )
-
#
-
# @note The `match_regex` alias is deprecated and is not recommended for use.
-
# It was added in 2.12.1 to facilitate its use from within custom
-
# matchers (due to how the custom matcher DSL was evaluated in 2.x,
-
# `match` could not be used there), but is no longer needed in 3.x.
-
1
def match(expected)
-
BuiltIn::Match.new(expected)
-
end
-
1
alias_matcher :match_regex, :match
-
1
alias_matcher :an_object_matching, :match
-
1
alias_matcher :a_string_matching, :match
-
1
alias_matcher :matching, :match
-
-
# An alternate form of `contain_exactly` that accepts
-
# the expected contents as a single array arg rather
-
# that splatted out as individual items.
-
#
-
# @example
-
#
-
# expect(results).to contain_exactly(1, 2)
-
# # is identical to:
-
# expect(results).to match_array([1, 2])
-
#
-
# @see #contain_exactly
-
1
def match_array(items)
-
contain_exactly(*items)
-
end
-
-
# With no arg, passes if the block outputs `to_stdout` or `to_stderr`.
-
# With a string, passes if the blocks outputs that specific string `to_stdout` or `to_stderr`.
-
# With a regexp or matcher, passes if the blocks outputs a string `to_stdout` or `to_stderr` that matches.
-
#
-
# @example
-
#
-
# expect { print 'foo' }.to output.to_stdout
-
# expect { print 'foo' }.to output('foo').to_stdout
-
# expect { print 'foo' }.to output(/foo/).to_stdout
-
#
-
# expect { do_something }.to_not output.to_stdout
-
#
-
# expect { warn('foo') }.to output.to_stderr
-
# expect { warn('foo') }.to output('foo').to_stderr
-
# expect { warn('foo') }.to output(/foo/).to_stderr
-
#
-
# expect { do_something }.to_not output.to_stderr
-
#
-
# @note This matcher works by temporarily replacing `$stdout` or `$stderr`,
-
# so it's not able to intercept stream output that explicitly uses `STDOUT`/`STDERR`
-
# or that uses a reference to `$stdout`/`$stderr` that was stored before the
-
# matcher is used.
-
1
def output(expected=nil)
-
BuiltIn::Output.new(expected)
-
end
-
1
alias_matcher :a_block_outputting, :output
-
-
# With no args, matches if any error is raised.
-
# With a named error, matches only if that specific error is raised.
-
# With a named error and messsage specified as a String, matches only if both match.
-
# With a named error and messsage specified as a Regexp, matches only if both match.
-
# Pass an optional block to perform extra verifications on the exception matched
-
#
-
# @example
-
#
-
# expect { do_something_risky }.to raise_error
-
# expect { do_something_risky }.to raise_error(PoorRiskDecisionError)
-
# expect { do_something_risky }.to raise_error(PoorRiskDecisionError) { |error| expect(error.data).to eq 42 }
-
# expect { do_something_risky }.to raise_error(PoorRiskDecisionError, "that was too risky")
-
# expect { do_something_risky }.to raise_error(PoorRiskDecisionError, /oo ri/)
-
#
-
# expect { do_something_risky }.not_to raise_error
-
1
def raise_error(error=Exception, message=nil, &block)
-
BuiltIn::RaiseError.new(error, message, &block)
-
end
-
1
alias_method :raise_exception, :raise_error
-
-
1
alias_matcher :a_block_raising, :raise_error do |desc|
-
desc.sub("raise", "a block raising")
-
end
-
-
1
alias_matcher :raising, :raise_error do |desc|
-
desc.sub("raise", "raising")
-
end
-
-
# Matches if the target object responds to all of the names
-
# provided. Names can be Strings or Symbols.
-
#
-
# @example
-
#
-
# expect("string").to respond_to(:length)
-
#
-
1
def respond_to(*names)
-
BuiltIn::RespondTo.new(*names)
-
end
-
1
alias_matcher :an_object_responding_to, :respond_to
-
1
alias_matcher :responding_to, :respond_to
-
-
# Passes if the submitted block returns true. Yields target to the
-
# block.
-
#
-
# Generally speaking, this should be thought of as a last resort when
-
# you can't find any other way to specify the behaviour you wish to
-
# specify.
-
#
-
# If you do find yourself in such a situation, you could always write
-
# a custom matcher, which would likely make your specs more expressive.
-
#
-
# @example
-
#
-
# expect(5).to satisfy { |n| n > 3 }
-
1
def satisfy(&block)
-
BuiltIn::Satisfy.new(&block)
-
end
-
1
alias_matcher :an_object_satisfying, :satisfy
-
1
alias_matcher :satisfying, :satisfy
-
-
# Matches if the actual value starts with the expected value(s). In the
-
# case of a string, matches against the first `expected.length` characters
-
# of the actual string. In the case of an array, matches against the first
-
# `expected.length` elements of the actual array.
-
#
-
# @example
-
#
-
# expect("this string").to start_with "this s"
-
# expect([0, 1, 2, 3, 4]).to start_with 0
-
# expect([0, 2, 3, 4, 4]).to start_with 0, 1
-
1
def start_with(*expected)
-
BuiltIn::StartWith.new(*expected)
-
end
-
1
alias_matcher :a_collection_starting_with, :start_with
-
1
alias_matcher :a_string_starting_with, :start_with
-
1
alias_matcher :starting_with, :start_with
-
-
# Given no argument, matches if a proc throws any Symbol.
-
#
-
# Given a Symbol, matches if the given proc throws the specified Symbol.
-
#
-
# Given a Symbol and an arg, matches if the given proc throws the
-
# specified Symbol with the specified arg.
-
#
-
# @example
-
#
-
# expect { do_something_risky }.to throw_symbol
-
# expect { do_something_risky }.to throw_symbol(:that_was_risky)
-
# expect { do_something_risky }.to throw_symbol(:that_was_risky, 'culprit')
-
#
-
# expect { do_something_risky }.not_to throw_symbol
-
# expect { do_something_risky }.not_to throw_symbol(:that_was_risky)
-
# expect { do_something_risky }.not_to throw_symbol(:that_was_risky, 'culprit')
-
1
def throw_symbol(expected_symbol=nil, expected_arg=nil)
-
BuiltIn::ThrowSymbol.new(expected_symbol, expected_arg)
-
end
-
-
1
alias_matcher :a_block_throwing, :throw_symbol do |desc|
-
desc.sub("throw", "a block throwing")
-
end
-
-
1
alias_matcher :throwing, :throw_symbol do |desc|
-
desc.sub("throw", "throwing")
-
end
-
-
# Passes if the method called in the expect block yields, regardless
-
# of whether or not arguments are yielded.
-
#
-
# @example
-
#
-
# expect { |b| 5.tap(&b) }.to yield_control
-
# expect { |b| "a".to_sym(&b) }.not_to yield_control
-
#
-
# @note Your expect block must accept a parameter and pass it on to
-
# the method-under-test as a block.
-
# @note This matcher is not designed for use with methods that yield
-
# multiple times.
-
1
def yield_control
-
BuiltIn::YieldControl.new
-
end
-
1
alias_matcher :a_block_yielding_control, :yield_control
-
1
alias_matcher :yielding_control, :yield_control
-
-
# Passes if the method called in the expect block yields with
-
# no arguments. Fails if it does not yield, or yields with arguments.
-
#
-
# @example
-
#
-
# expect { |b| User.transaction(&b) }.to yield_with_no_args
-
# expect { |b| 5.tap(&b) }.not_to yield_with_no_args # because it yields with `5`
-
# expect { |b| "a".to_sym(&b) }.not_to yield_with_no_args # because it does not yield
-
#
-
# @note Your expect block must accept a parameter and pass it on to
-
# the method-under-test as a block.
-
# @note This matcher is not designed for use with methods that yield
-
# multiple times.
-
1
def yield_with_no_args
-
BuiltIn::YieldWithNoArgs.new
-
end
-
1
alias_matcher :a_block_yielding_with_no_args, :yield_with_no_args
-
1
alias_matcher :yielding_with_no_args, :yield_with_no_args
-
-
# Given no arguments, matches if the method called in the expect
-
# block yields with arguments (regardless of what they are or how
-
# many there are).
-
#
-
# Given arguments, matches if the method called in the expect block
-
# yields with arguments that match the given arguments.
-
#
-
# Argument matching is done using `===` (the case match operator)
-
# and `==`. If the expected and actual arguments match with either
-
# operator, the matcher will pass.
-
#
-
# @example
-
#
-
# expect { |b| 5.tap(&b) }.to yield_with_args # because #tap yields an arg
-
# expect { |b| 5.tap(&b) }.to yield_with_args(5) # because 5 == 5
-
# expect { |b| 5.tap(&b) }.to yield_with_args(Fixnum) # because Fixnum === 5
-
# expect { |b| File.open("f.txt", &b) }.to yield_with_args(/txt/) # because /txt/ === "f.txt"
-
#
-
# expect { |b| User.transaction(&b) }.not_to yield_with_args # because it yields no args
-
# expect { |b| 5.tap(&b) }.not_to yield_with_args(1, 2, 3)
-
#
-
# @note Your expect block must accept a parameter and pass it on to
-
# the method-under-test as a block.
-
# @note This matcher is not designed for use with methods that yield
-
# multiple times.
-
1
def yield_with_args(*args)
-
BuiltIn::YieldWithArgs.new(*args)
-
end
-
1
alias_matcher :a_block_yielding_with_args, :yield_with_args
-
1
alias_matcher :yielding_with_args, :yield_with_args
-
-
# Designed for use with methods that repeatedly yield (such as
-
# iterators). Passes if the method called in the expect block yields
-
# multiple times with arguments matching those given.
-
#
-
# Argument matching is done using `===` (the case match operator)
-
# and `==`. If the expected and actual arguments match with either
-
# operator, the matcher will pass.
-
#
-
# @example
-
#
-
# expect { |b| [1, 2, 3].each(&b) }.to yield_successive_args(1, 2, 3)
-
# expect { |b| { :a => 1, :b => 2 }.each(&b) }.to yield_successive_args([:a, 1], [:b, 2])
-
# expect { |b| [1, 2, 3].each(&b) }.not_to yield_successive_args(1, 2)
-
#
-
# @note Your expect block must accept a parameter and pass it on to
-
# the method-under-test as a block.
-
1
def yield_successive_args(*args)
-
BuiltIn::YieldSuccessiveArgs.new(*args)
-
end
-
1
alias_matcher :a_block_yielding_successive_args, :yield_successive_args
-
1
alias_matcher :yielding_successive_args, :yield_successive_args
-
-
# Delegates to {RSpec::Expectations.configuration}.
-
# This is here because rspec-core's `expect_with` option
-
# looks for a `configuration` method on the mixin
-
# (`RSpec::Matchers`) to yield to a block.
-
# @return [RSpec::Expectations::Configuration] the configuration object
-
1
def self.configuration
-
4
Expectations.configuration
-
end
-
-
1
private
-
-
1
BE_PREDICATE_REGEX = /^(be_(?:an?_)?)(.*)/
-
1
HAS_REGEX = /^(?:have_)(.*)/
-
-
1
def method_missing(method, *args, &block)
-
case method.to_s
-
when BE_PREDICATE_REGEX
-
BuiltIn::BePredicate.new(method, *args, &block)
-
when HAS_REGEX
-
BuiltIn::Has.new(method, *args, &block)
-
else
-
super
-
end
-
end
-
-
# @api private
-
1
def self.is_a_matcher?(obj)
-
1
return true if ::RSpec::Matchers::BuiltIn::BaseMatcher === obj
-
1
begin
-
1
return false if obj.respond_to?(:i_respond_to_everything_so_im_not_really_a_matcher)
-
rescue NoMethodError
-
# Some objects, like BasicObject, don't implemented standard
-
# reflection methods.
-
return false
-
end
-
1
return false unless obj.respond_to?(:matches?)
-
-
obj.respond_to?(:failure_message) ||
-
obj.respond_to?(:failure_message_for_should) # support legacy matchers
-
end
-
-
1
::RSpec::Support.register_matcher_definition do |obj|
-
is_a_matcher?(obj)
-
end
-
-
# @api private
-
1
def self.is_a_describable_matcher?(obj)
-
is_a_matcher?(obj) && obj.respond_to?(:description)
-
end
-
end
-
end
-
1
module RSpec
-
1
module Matchers
-
# Decorator that wraps a matcher and overrides `description`
-
# using the provided block in order to support an alias
-
# of a matcher. This is intended for use when composing
-
# matchers, so that you can use an expression like
-
# `include( a_value_within(0.1).of(3) )` rather than
-
# `include( be_within(0.1).of(3) )`, and have the corresponding
-
# description read naturally.
-
#
-
# @api private
-
1
class AliasedMatcher < MatcherDelegator
-
1
def initialize(base_matcher, description_block)
-
@description_block = description_block
-
super(base_matcher)
-
end
-
-
# Forward messages on to the wrapped matcher.
-
# Since many matchers provide a fluent interface
-
# (e.g. `a_value_within(0.1).of(3)`), we need to wrap
-
# the returned value if it responds to `description`,
-
# so that our override can be applied when it is eventually
-
# used.
-
1
def method_missing(*)
-
return_val = super
-
return return_val unless RSpec::Matchers.is_a_matcher?(return_val)
-
self.class.new(return_val, @description_block)
-
end
-
-
# Provides the description of the aliased matcher. Aliased matchers
-
# are designed to behave identically to the original matcher except
-
# for the description and failure messages. The description is different
-
# to reflect the aliased name.
-
#
-
# @api private
-
1
def description
-
@description_block.call(super)
-
end
-
-
# Provides the failure_message of the aliased matcher. Aliased matchers
-
# are designed to behave identically to the original matcher except
-
# for the description and failure messages. The failure_message is different
-
# to reflect the aliased name.
-
#
-
# @api private
-
1
def failure_message
-
@description_block.call(super)
-
end
-
-
# Provides the failure_message_when_negated of the aliased matcher. Aliased matchers
-
# are designed to behave identically to the original matcher except
-
# for the description and failure messages. The failure_message_when_negated is different
-
# to reflect the aliased name.
-
#
-
# @api private
-
1
def failure_message_when_negated
-
@description_block.call(super)
-
end
-
end
-
-
# Decorator used for matchers that have special implementations of
-
# operators like `==` and `===`.
-
# @private
-
1
class AliasedMatcherWithOperatorSupport < AliasedMatcher
-
# We undef these so that they get delegated via `method_missing`.
-
1
undef ==
-
1
undef ===
-
end
-
-
# @private
-
1
class AliasedNegatedMatcher < AliasedMatcher
-
1
def matches?(*args, &block)
-
if @base_matcher.respond_to?(:does_not_match?)
-
@base_matcher.does_not_match?(*args, &block)
-
else
-
!super
-
end
-
end
-
-
1
def does_not_match?(*args, &block)
-
@base_matcher.matches?(*args, &block)
-
end
-
-
1
def failure_message
-
optimal_failure_message(__method__, :failure_message_when_negated)
-
end
-
-
1
def failure_message_when_negated
-
optimal_failure_message(__method__, :failure_message)
-
end
-
-
1
private
-
-
1
DefaultFailureMessages = BuiltIn::BaseMatcher::DefaultFailureMessages
-
-
# For a matcher that uses the default failure messages, we prefer to
-
# use the override provided by the `description_block`, because it
-
# includes the phrasing that the user has expressed a preference for
-
# by going through the effort of defining a negated matcher.
-
#
-
# However, if the override didn't actually change anything, then we
-
# should return the opposite failure message instead -- the overriden
-
# message is going to be confusing if we return it as-is, as it represents
-
# the non-negated failure message for a negated match (or vice versa).
-
1
def optimal_failure_message(same, inverted)
-
if DefaultFailureMessages.has_default_failure_messages?(@base_matcher)
-
base_message = @base_matcher.__send__(same)
-
overriden = @description_block.call(base_message)
-
return overriden if overriden != base_message
-
end
-
-
@base_matcher.__send__(inverted)
-
end
-
end
-
end
-
end
-
1
RSpec::Support.require_rspec_matchers "built_in/base_matcher"
-
-
1
module RSpec
-
1
module Matchers
-
# Container module for all built-in matchers. The matcher classes are here
-
# (rather than directly under `RSpec::Matchers`) in order to prevent name
-
# collisions, since `RSpec::Matchers` gets included into the user's namespace.
-
#
-
# Autoloading is used to delay when the matcher classes get loaded, allowing
-
# rspec-matchers to boot faster, and avoiding loading matchers the user is
-
# not using.
-
1
module BuiltIn
-
1
autoload :BeAKindOf, 'rspec/matchers/built_in/be_kind_of'
-
1
autoload :BeAnInstanceOf, 'rspec/matchers/built_in/be_instance_of'
-
1
autoload :BeBetween, 'rspec/matchers/built_in/be_between'
-
1
autoload :Be, 'rspec/matchers/built_in/be'
-
1
autoload :BeComparedTo, 'rspec/matchers/built_in/be'
-
1
autoload :BeFalsey, 'rspec/matchers/built_in/be'
-
1
autoload :BeNil, 'rspec/matchers/built_in/be'
-
1
autoload :BePredicate, 'rspec/matchers/built_in/be'
-
1
autoload :BeTruthy, 'rspec/matchers/built_in/be'
-
1
autoload :BeWithin, 'rspec/matchers/built_in/be_within'
-
1
autoload :Change, 'rspec/matchers/built_in/change'
-
1
autoload :Compound, 'rspec/matchers/built_in/compound'
-
1
autoload :ContainExactly, 'rspec/matchers/built_in/contain_exactly'
-
1
autoload :Cover, 'rspec/matchers/built_in/cover'
-
1
autoload :EndWith, 'rspec/matchers/built_in/start_and_end_with'
-
1
autoload :Eq, 'rspec/matchers/built_in/eq'
-
1
autoload :Eql, 'rspec/matchers/built_in/eql'
-
1
autoload :Equal, 'rspec/matchers/built_in/equal'
-
1
autoload :Exist, 'rspec/matchers/built_in/exist'
-
1
autoload :Has, 'rspec/matchers/built_in/has'
-
1
autoload :HaveAttributes, 'rspec/matchers/built_in/have_attributes'
-
1
autoload :Include, 'rspec/matchers/built_in/include'
-
1
autoload :All, 'rspec/matchers/built_in/all'
-
1
autoload :Match, 'rspec/matchers/built_in/match'
-
1
autoload :NegativeOperatorMatcher, 'rspec/matchers/built_in/operators'
-
1
autoload :OperatorMatcher, 'rspec/matchers/built_in/operators'
-
1
autoload :Output, 'rspec/matchers/built_in/output'
-
1
autoload :PositiveOperatorMatcher, 'rspec/matchers/built_in/operators'
-
1
autoload :RaiseError, 'rspec/matchers/built_in/raise_error'
-
1
autoload :RespondTo, 'rspec/matchers/built_in/respond_to'
-
1
autoload :Satisfy, 'rspec/matchers/built_in/satisfy'
-
1
autoload :StartWith, 'rspec/matchers/built_in/start_and_end_with'
-
1
autoload :ThrowSymbol, 'rspec/matchers/built_in/throw_symbol'
-
1
autoload :YieldControl, 'rspec/matchers/built_in/yield'
-
1
autoload :YieldSuccessiveArgs, 'rspec/matchers/built_in/yield'
-
1
autoload :YieldWithArgs, 'rspec/matchers/built_in/yield'
-
1
autoload :YieldWithNoArgs, 'rspec/matchers/built_in/yield'
-
end
-
end
-
end
-
1
module RSpec
-
1
module Matchers
-
1
module BuiltIn
-
# @api private
-
#
-
# Used _internally_ as a base class for matchers that ship with
-
# rspec-expectations and rspec-rails.
-
#
-
# ### Warning:
-
#
-
# This class is for internal use, and subject to change without notice. We
-
# strongly recommend that you do not base your custom matchers on this
-
# class. If/when this changes, we will announce it and remove this warning.
-
1
class BaseMatcher
-
1
include RSpec::Matchers::Pretty
-
1
include RSpec::Matchers::Composable
-
-
# @api private
-
# Used to detect when no arg is passed to `initialize`.
-
# `nil` cannot be used because it's a valid value to pass.
-
1
UNDEFINED = Object.new.freeze
-
-
# @private
-
1
attr_reader :actual, :expected, :rescued_exception
-
-
1
def initialize(expected=UNDEFINED)
-
@expected = expected unless UNDEFINED.equal?(expected)
-
end
-
-
# @api private
-
# Indicates if the match is successful. Delegates to `match`, which
-
# should be defined on a subclass. Takes care of consistently
-
# initializing the `actual` attribute.
-
1
def matches?(actual)
-
@actual = actual
-
match(expected, actual)
-
end
-
-
# @api private
-
# Used to wrap a block of code that will indicate failure by
-
# raising one of the named exceptions.
-
#
-
# This is used by rspec-rails for some of its matchers that
-
# wrap rails' assertions.
-
1
def match_unless_raises(*exceptions)
-
2
exceptions.unshift Exception if exceptions.empty?
-
2
begin
-
2
yield
-
1
true
-
1
rescue *exceptions => @rescued_exception
-
1
false
-
end
-
end
-
-
# @api private
-
# Generates a "pretty" description using the logic in {Pretty}.
-
# @return [String]
-
1
def description
-
return name_to_sentence unless defined?(@expected)
-
"#{name_to_sentence}#{to_sentence @expected}"
-
end
-
-
# @api private
-
# Matchers are not diffable by default. Override this to make your
-
# subclass diffable.
-
1
def diffable?
-
1
false
-
end
-
-
# @api private
-
# Most matchers are value matchers (i.e. meant to work with `expect(value)`)
-
# rather than block matchers (i.e. meant to work with `expect { }`), so
-
# this defaults to false. Block matchers must override this to return true.
-
1
def supports_block_expectations?
-
false
-
end
-
-
# @api private
-
1
def expects_call_stack_jump?
-
false
-
end
-
-
1
private
-
-
1
def assert_ivars(*expected_ivars)
-
return unless (expected_ivars - present_ivars).any?
-
raise "#{self.class.name} needs to supply#{to_sentence expected_ivars}"
-
end
-
-
1
if RUBY_VERSION.to_f < 1.9
-
def present_ivars
-
instance_variables.map { |v| v.to_sym }
-
end
-
else
-
1
alias present_ivars instance_variables
-
end
-
-
# @api private
-
# Provides default implementations of failure messages, based on the `description`.
-
1
module DefaultFailureMessages
-
# @api private
-
# Provides a good generic failure message. Based on `description`.
-
# When subclassing, if you are not satisfied with this failure message
-
# you often only need to override `description`.
-
# @return [String]
-
1
def failure_message
-
"expected #{actual.inspect} to #{description}"
-
end
-
-
# @api private
-
# Provides a good generic negative failure message. Based on `description`.
-
# When subclassing, if you are not satisfied with this failure message
-
# you often only need to override `description`.
-
# @return [String]
-
1
def failure_message_when_negated
-
"expected #{actual.inspect} not to #{description}"
-
end
-
-
# @private
-
1
def self.has_default_failure_messages?(matcher)
-
matcher.method(:failure_message).owner == self &&
-
matcher.method(:failure_message_when_negated).owner == self
-
rescue NameError
-
false
-
end
-
end
-
-
1
include DefaultFailureMessages
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Matchers
-
1
module BuiltIn
-
# @api private
-
# Provides the implementation for `be_truthy`.
-
# Not intended to be instantiated directly.
-
1
class BeTruthy < BaseMatcher
-
# @api private
-
# @return [String]
-
1
def failure_message
-
"expected: truthy value\n got: #{actual.inspect}"
-
end
-
-
# @api private
-
# @return [String]
-
1
def failure_message_when_negated
-
"expected: falsey value\n got: #{actual.inspect}"
-
end
-
-
1
private
-
-
1
def match(_, actual)
-
!!actual
-
end
-
end
-
-
# @api private
-
# Provides the implementation for `be_falsey`.
-
# Not intended to be instantiated directly.
-
1
class BeFalsey < BaseMatcher
-
# @api private
-
# @return [String]
-
1
def failure_message
-
"expected: falsey value\n got: #{actual.inspect}"
-
end
-
-
# @api private
-
# @return [String]
-
1
def failure_message_when_negated
-
"expected: truthy value\n got: #{actual.inspect}"
-
end
-
-
1
private
-
-
1
def match(_, actual)
-
!actual
-
end
-
end
-
-
# @api private
-
# Provides the implementation for `be_nil`.
-
# Not intended to be instantiated directly.
-
1
class BeNil < BaseMatcher
-
# @api private
-
# @return [String]
-
1
def failure_message
-
"expected: nil\n got: #{actual.inspect}"
-
end
-
-
# @api private
-
# @return [String]
-
1
def failure_message_when_negated
-
"expected: not nil\n got: nil"
-
end
-
-
1
private
-
-
1
def match(_, actual)
-
actual.nil?
-
end
-
end
-
-
# @private
-
1
module BeHelpers
-
1
private
-
-
1
def args_to_s
-
@args.empty? ? "" : parenthesize(inspected_args.join(', '))
-
end
-
-
1
def parenthesize(string)
-
"(#{string})"
-
end
-
-
1
def inspected_args
-
@args.map { |a| a.inspect }
-
end
-
-
1
def expected_to_sentence
-
split_words(@expected)
-
end
-
-
1
def args_to_sentence
-
to_sentence(@args)
-
end
-
end
-
-
# @api private
-
# Provides the implementation for `be`.
-
# Not intended to be instantiated directly.
-
1
class Be < BaseMatcher
-
1
include BeHelpers
-
-
1
def initialize(*args)
-
@args = args
-
end
-
-
# @api private
-
# @return [String]
-
1
def failure_message
-
"expected #{@actual.inspect} to evaluate to true"
-
end
-
-
# @api private
-
# @return [String]
-
1
def failure_message_when_negated
-
"expected #{@actual.inspect} to evaluate to false"
-
end
-
-
1
[:==, :<, :<=, :>=, :>, :===, :=~].each do |operator|
-
7
define_method operator do |operand|
-
BeComparedTo.new(operand, operator)
-
end
-
end
-
-
1
private
-
-
1
def match(_, actual)
-
!!actual
-
end
-
end
-
-
# @api private
-
# Provides the implementation of `be <operator> value`.
-
# Not intended to be instantiated directly.
-
1
class BeComparedTo < BaseMatcher
-
1
include BeHelpers
-
-
1
def initialize(operand, operator)
-
@expected, @operator = operand, operator
-
@args = []
-
end
-
-
1
def matches?(actual)
-
@actual = actual
-
@actual.__send__ @operator, @expected
-
end
-
-
# @api private
-
# @return [String]
-
1
def failure_message
-
"expected: #{@operator} #{@expected.inspect}\n got: #{@operator.to_s.gsub(/./, ' ')} #{@actual.inspect}"
-
end
-
-
# @api private
-
# @return [String]
-
1
def failure_message_when_negated
-
message = "`expect(#{@actual.inspect}).not_to be #{@operator} #{@expected.inspect}`"
-
if [:<, :>, :<=, :>=].include?(@operator)
-
message + " not only FAILED, it is a bit confusing."
-
else
-
message
-
end
-
end
-
-
# @api private
-
# @return [String]
-
1
def description
-
"be #{@operator} #{expected_to_sentence}#{args_to_sentence}"
-
end
-
end
-
-
# @api private
-
# Provides the implementation of `be_<predicate>`.
-
# Not intended to be instantiated directly.
-
1
class BePredicate < BaseMatcher
-
1
include BeHelpers
-
-
1
def initialize(*args, &block)
-
@expected = parse_expected(args.shift)
-
@args = args
-
@block = block
-
end
-
-
1
def matches?(actual, &block)
-
@actual = actual
-
@block ||= block
-
predicate_accessible? && predicate_matches?
-
end
-
-
1
def does_not_match?(actual, &block)
-
@actual = actual
-
@block ||= block
-
predicate_accessible? && !predicate_matches?
-
end
-
-
# @api private
-
# @return [String]
-
1
def failure_message
-
failure_message_expecting(true)
-
end
-
-
# @api private
-
# @return [String]
-
1
def failure_message_when_negated
-
failure_message_expecting(false)
-
end
-
-
# @api private
-
# @return [String]
-
1
def description
-
"#{prefix_to_sentence}#{expected_to_sentence}#{args_to_sentence}"
-
end
-
-
1
private
-
-
1
def predicate_accessible?
-
!private_predicate? && predicate_exists?
-
end
-
-
# support 1.8.7, evaluate once at load time for performance
-
1
if String === methods.first
-
def private_predicate?
-
@actual.private_methods.include? predicate.to_s
-
end
-
else
-
1
def private_predicate?
-
@actual.private_methods.include? predicate
-
end
-
end
-
-
1
def predicate_exists?
-
actual.respond_to?(predicate) || actual.respond_to?(present_tense_predicate)
-
end
-
-
1
def predicate_matches?
-
method_name = actual.respond_to?(predicate) ? predicate : present_tense_predicate
-
@predicate_matches = actual.__send__(method_name, *@args, &@block)
-
end
-
-
1
def predicate
-
:"#{@expected}?"
-
end
-
-
1
def present_tense_predicate
-
:"#{@expected}s?"
-
end
-
-
1
def parse_expected(expected)
-
@prefix, expected = prefix_and_expected(expected)
-
expected
-
end
-
-
1
def prefix_and_expected(symbol)
-
Matchers::BE_PREDICATE_REGEX.match(symbol.to_s).captures.compact
-
end
-
-
1
def prefix_to_sentence
-
split_words(@prefix)
-
end
-
-
1
def failure_message_expecting(value)
-
validity_message ||
-
"expected `#{@actual.inspect}.#{predicate}#{args_to_s}` to return #{value}, got #{@predicate_matches.inspect}"
-
end
-
-
1
def validity_message
-
if private_predicate?
-
"expected #{@actual} to respond to `#{predicate}` but `#{predicate}` is a private method"
-
elsif !predicate_exists?
-
"expected #{@actual} to respond to `#{predicate}`"
-
end
-
end
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Matchers
-
1
module BuiltIn
-
# @api private
-
# Provides the implementation for `change`.
-
# Not intended to be instantiated directly.
-
1
class Change < BaseMatcher
-
# @api public
-
# Specifies the delta of the expected change.
-
1
def by(expected_delta)
-
1
ChangeRelatively.new(@change_details, expected_delta, :by) do |actual_delta|
-
1
values_match?(expected_delta, actual_delta)
-
end
-
end
-
-
# @api public
-
# Specifies a minimum delta of the expected change.
-
1
def by_at_least(minimum)
-
ChangeRelatively.new(@change_details, minimum, :by_at_least) do |actual_delta|
-
actual_delta >= minimum
-
end
-
end
-
-
# @api public
-
# Specifies a maximum delta of the expected change.
-
1
def by_at_most(maximum)
-
ChangeRelatively.new(@change_details, maximum, :by_at_most) do |actual_delta|
-
actual_delta <= maximum
-
end
-
end
-
-
# @api public
-
# Specifies the new value you expect.
-
1
def to(value)
-
ChangeToValue.new(@change_details, value)
-
end
-
-
# @api public
-
# Specifies the original value.
-
1
def from(value)
-
ChangeFromValue.new(@change_details, value)
-
end
-
-
# @private
-
1
def matches?(event_proc)
-
@event_proc = event_proc
-
return false unless Proc === event_proc
-
raise_block_syntax_error if block_given?
-
@change_details.perform_change(event_proc)
-
@change_details.changed?
-
end
-
-
1
def does_not_match?(event_proc)
-
raise_block_syntax_error if block_given?
-
!matches?(event_proc) && Proc === event_proc
-
end
-
-
# @api private
-
# @return [String]
-
1
def failure_message
-
"expected #{@change_details.message} to have changed, but #{positive_failure_reason}"
-
end
-
-
# @api private
-
# @return [String]
-
1
def failure_message_when_negated
-
"expected #{@change_details.message} not to have changed, but #{negative_failure_reason}"
-
end
-
-
# @api private
-
# @return [String]
-
1
def description
-
"change #{@change_details.message}"
-
end
-
-
# @private
-
1
def supports_block_expectations?
-
true
-
end
-
-
1
private
-
-
1
def initialize(receiver=nil, message=nil, &block)
-
1
@change_details = ChangeDetails.new(receiver, message, &block)
-
end
-
-
1
def raise_block_syntax_error
-
raise SyntaxError, "The block passed to the `change` matcher must " \
-
"use `{ ... }` instead of do/end"
-
end
-
-
1
def positive_failure_reason
-
return "was not given a block" unless Proc === @event_proc
-
"is still #{description_of @change_details.actual_before}"
-
end
-
-
1
def negative_failure_reason
-
return "was not given a block" unless Proc === @event_proc
-
"did change from #{description_of @change_details.actual_before} to #{description_of @change_details.actual_after}"
-
end
-
end
-
-
# Used to specify a relative change.
-
# @api private
-
1
class ChangeRelatively < BaseMatcher
-
1
def initialize(change_details, expected_delta, relativity, &comparer)
-
1
@change_details = change_details
-
1
@expected_delta = expected_delta
-
1
@relativity = relativity
-
1
@comparer = comparer
-
end
-
-
# @private
-
1
def failure_message
-
"expected #{@change_details.message} to have changed #{@relativity.to_s.gsub("_", " ")} #{description_of @expected_delta}, but #{failure_reason}"
-
end
-
-
# @private
-
1
def matches?(event_proc)
-
1
@event_proc = event_proc
-
1
return false unless Proc === event_proc
-
1
@change_details.perform_change(event_proc)
-
1
@comparer.call(@change_details.actual_delta)
-
end
-
-
# @private
-
1
def does_not_match?(_event_proc)
-
raise NotImplementedError, "`expect { }.not_to change { }.#{@relativity}()` is not supported"
-
end
-
-
# @private
-
1
def description
-
"change #{@change_details.message} #{@relativity.to_s.gsub("_", " ")} #{description_of @expected_delta}"
-
end
-
-
# @private
-
1
def supports_block_expectations?
-
1
true
-
end
-
-
1
private
-
-
1
def failure_reason
-
return "was not given a block" unless Proc === @event_proc
-
"was changed by #{description_of @change_details.actual_delta}"
-
end
-
end
-
-
# @api private
-
# Base class for specifying a change from and/or to specific values.
-
1
class SpecificValuesChange < BaseMatcher
-
# @private
-
1
MATCH_ANYTHING = ::Object.ancestors.last
-
-
1
def initialize(change_details, from, to)
-
@change_details = change_details
-
@expected_before = from
-
@expected_after = to
-
end
-
-
# @private
-
1
def matches?(event_proc)
-
@event_proc = event_proc
-
return false unless Proc === event_proc
-
@change_details.perform_change(event_proc)
-
@change_details.changed? && matches_before? && matches_after?
-
end
-
-
# @private
-
1
def description
-
"change #{@change_details.message} #{change_description}"
-
end
-
-
# @private
-
1
def failure_message
-
return not_given_a_block_failure unless Proc === @event_proc
-
return before_value_failure unless matches_before?
-
return did_not_change_failure unless @change_details.changed?
-
after_value_failure
-
end
-
-
# @private
-
1
def supports_block_expectations?
-
true
-
end
-
-
1
private
-
-
1
def matches_before?
-
values_match?(@expected_before, @change_details.actual_before)
-
end
-
-
1
def matches_after?
-
values_match?(@expected_after, @change_details.actual_after)
-
end
-
-
1
def before_value_failure
-
"expected #{@change_details.message} to have initially been #{description_of @expected_before}, but was #{description_of @change_details.actual_before}"
-
end
-
-
1
def after_value_failure
-
"expected #{@change_details.message} to have changed to #{description_of @expected_after}, but is now #{description_of @change_details.actual_after}"
-
end
-
-
1
def did_not_change_failure
-
"expected #{@change_details.message} to have changed #{change_description}, but did not change"
-
end
-
-
1
def did_change_failure
-
"expected #{@change_details.message} not to have changed, but did change from #{description_of @change_details.actual_before} to #{description_of @change_details.actual_after}"
-
end
-
-
1
def not_given_a_block_failure
-
"expected #{@change_details.message} to have changed #{change_description}, but was not given a block"
-
end
-
end
-
-
# @api private
-
# Used to specify a change from a specific value
-
# (and, optionally, to a specific value).
-
1
class ChangeFromValue < SpecificValuesChange
-
1
def initialize(change_details, expected_before)
-
@description_suffix = nil
-
super(change_details, expected_before, MATCH_ANYTHING)
-
end
-
-
# @api public
-
# Specifies the new value you expect.
-
1
def to(value)
-
@expected_after = value
-
@description_suffix = " to #{description_of value}"
-
self
-
end
-
-
# @private
-
1
def does_not_match?(event_proc)
-
if @description_suffix
-
raise NotImplementedError, "`expect { }.not_to change { }.to()` is not supported"
-
end
-
-
@event_proc = event_proc
-
return false unless Proc === event_proc
-
@change_details.perform_change(event_proc)
-
!@change_details.changed? && matches_before?
-
end
-
-
# @private
-
1
def failure_message_when_negated
-
return not_given_a_block_failure unless Proc === @event_proc
-
return before_value_failure unless matches_before?
-
did_change_failure
-
end
-
-
1
private
-
-
1
def change_description
-
"from #{description_of @expected_before}#{@description_suffix}"
-
end
-
end
-
-
# @api private
-
# Used to specify a change to a specific value
-
# (and, optionally, from a specific value).
-
1
class ChangeToValue < SpecificValuesChange
-
1
def initialize(change_details, expected_after)
-
@description_suffix = nil
-
super(change_details, MATCH_ANYTHING, expected_after)
-
end
-
-
# @api public
-
# Specifies the original value.
-
1
def from(value)
-
@expected_before = value
-
@description_suffix = " from #{description_of value}"
-
self
-
end
-
-
# @private
-
1
def does_not_match?(_event_proc)
-
raise NotImplementedError, "`expect { }.not_to change { }.to()` is not supported"
-
end
-
-
1
private
-
-
1
def change_description
-
"to #{description_of @expected_after}#{@description_suffix}"
-
end
-
end
-
-
# @private
-
# Encapsulates the details of the before/after values.
-
1
class ChangeDetails
-
1
attr_reader :message, :actual_before, :actual_after
-
-
1
def initialize(receiver=nil, message=nil, &block)
-
1
if receiver && !message
-
raise(
-
ArgumentError,
-
"`change` requires either an object and message " \
-
"(`change(obj, :msg)`) or a block (`change { }`). " \
-
"You passed an object but no message."
-
)
-
end
-
1
@message = message ? "##{message}" : "result"
-
3
@value_proc = block || lambda { receiver.__send__(message) }
-
end
-
-
1
def perform_change(event_proc)
-
1
@actual_before = evaluate_value_proc
-
1
event_proc.call
-
1
@actual_after = evaluate_value_proc
-
end
-
-
1
def changed?
-
@actual_before != @actual_after
-
end
-
-
1
def actual_delta
-
1
@actual_after - @actual_before
-
end
-
-
1
private
-
-
1
def evaluate_value_proc
-
2
case val = @value_proc.call
-
when IO # enumerable, but we don't want to dup it.
-
val
-
when Enumerable, String
-
val.dup
-
else
-
2
val
-
end
-
end
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Matchers
-
1
module BuiltIn
-
# @api private
-
# Provides the implementation for `contain_exactly` and `match_array`.
-
# Not intended to be instantiated directly.
-
1
class ContainExactly < BaseMatcher
-
# @api private
-
# @return [String]
-
1
def failure_message
-
if Array === actual
-
message = "expected collection contained: #{safe_sort(surface_descriptions_in expected).inspect}\n"
-
message += "actual collection contained: #{safe_sort(actual).inspect}\n"
-
message += "the missing elements were: #{safe_sort(surface_descriptions_in missing_items).inspect}\n" unless missing_items.empty?
-
message += "the extra elements were: #{safe_sort(extra_items).inspect}\n" unless extra_items.empty?
-
message
-
else
-
"expected a collection that can be converted to an array with " \
-
"`#to_ary` or `#to_a`, but got #{actual.inspect}"
-
end
-
end
-
-
# @api private
-
# @return [String]
-
1
def failure_message_when_negated
-
"expected #{actual.inspect} not to contain exactly#{to_sentence(surface_descriptions_in expected)}"
-
end
-
-
# @api private
-
# @return [String]
-
1
def description
-
"contain exactly#{to_sentence(surface_descriptions_in expected)}"
-
end
-
-
1
private
-
-
1
def match(_expected, _actual)
-
return false unless convert_actual_to_an_array
-
match_when_sorted? || (extra_items.empty? && missing_items.empty?)
-
end
-
-
# This cannot always work (e.g. when dealing with unsortable items,
-
# or matchers as expected items), but it's practically free compared to
-
# the slowness of the full matching algorithm, and in common cases this
-
# works, so it's worth a try.
-
1
def match_when_sorted?
-
values_match?(safe_sort(expected), safe_sort(actual))
-
end
-
-
1
def convert_actual_to_an_array
-
if actual.respond_to?(:to_ary)
-
@actual = actual.to_ary
-
elsif enumerable?(actual) && actual.respond_to?(:to_a)
-
@actual = actual.to_a
-
else
-
return false
-
end
-
end
-
-
1
def safe_sort(array)
-
array.sort rescue array
-
end
-
-
1
def missing_items
-
@missing_items ||= best_solution.unmatched_expected_indexes.map do |index|
-
expected[index]
-
end
-
end
-
-
1
def extra_items
-
@extra_items ||= best_solution.unmatched_actual_indexes.map do |index|
-
actual[index]
-
end
-
end
-
-
1
def best_solution
-
@best_solution ||= pairings_maximizer.find_best_solution
-
end
-
-
1
def pairings_maximizer
-
@pairings_maximizer ||= begin
-
expected_matches = Hash[Array.new(expected.size) { |i| [i, []] }]
-
actual_matches = Hash[Array.new(actual.size) { |i| [i, []] }]
-
-
expected.each_with_index do |e, ei|
-
actual.each_with_index do |a, ai|
-
next unless values_match?(e, a)
-
-
expected_matches[ei] << ai
-
actual_matches[ai] << ei
-
end
-
end
-
-
PairingsMaximizer.new(expected_matches, actual_matches)
-
end
-
end
-
-
# Once we started supporting composing matchers, the algorithm for this matcher got
-
# much more complicated. Consider this expression:
-
#
-
# expect(["fool", "food"]).to contain_exactly(/foo/, /fool/)
-
#
-
# This should pass (because we can pair /fool/ with "fool" and /foo/ with "food"), but
-
# the original algorithm used by this matcher would pair the first elements it could
-
# (/foo/ with "fool"), which would leave /fool/ and "food" unmatched. When we have
-
# an expected element which is a matcher that matches a superset of actual items
-
# compared to another expected element matcher, we need to consider every possible pairing.
-
#
-
# This class is designed to maximize the number of actual/expected pairings -- or,
-
# conversely, to minimize the number of unpaired items. It's essentially a brute
-
# force solution, but with a few heuristics applied to reduce the size of the
-
# problem space:
-
#
-
# * Any items which match none of the items in the other list are immediately
-
# placed into the `unmatched_expected_indexes` or `unmatched_actual_indexes` array.
-
# The extra items and missing items in the matcher failure message are derived
-
# from these arrays.
-
# * Any items which reciprocally match only each other are paired up and not
-
# considered further.
-
#
-
# What's left is only the items which match multiple items from the other list
-
# (or vice versa). From here, it performs a brute-force depth-first search,
-
# looking for a solution which pairs all elements in both lists, or, barring that,
-
# that produces the fewest unmatched items.
-
#
-
# @private
-
1
class PairingsMaximizer
-
1
Solution = Struct.new(:unmatched_expected_indexes, :unmatched_actual_indexes,
-
:indeterminate_expected_indexes, :indeterminate_actual_indexes) do
-
1
def worse_than?(other)
-
unmatched_item_count > other.unmatched_item_count
-
end
-
-
1
def candidate?
-
indeterminate_expected_indexes.empty? &&
-
indeterminate_actual_indexes.empty?
-
end
-
-
1
def ideal?
-
candidate? && (
-
unmatched_expected_indexes.empty? ||
-
unmatched_actual_indexes.empty?
-
)
-
end
-
-
1
def unmatched_item_count
-
unmatched_expected_indexes.count + unmatched_actual_indexes.count
-
end
-
-
1
def +(derived_candidate_solution)
-
self.class.new(
-
unmatched_expected_indexes + derived_candidate_solution.unmatched_expected_indexes,
-
unmatched_actual_indexes + derived_candidate_solution.unmatched_actual_indexes,
-
# Ignore the indeterminate indexes: by the time we get here,
-
# we've dealt with all indeterminates.
-
[], []
-
)
-
end
-
end
-
-
1
attr_reader :expected_to_actual_matched_indexes, :actual_to_expected_matched_indexes, :solution
-
-
1
def initialize(expected_to_actual_matched_indexes, actual_to_expected_matched_indexes)
-
@expected_to_actual_matched_indexes = expected_to_actual_matched_indexes
-
@actual_to_expected_matched_indexes = actual_to_expected_matched_indexes
-
-
unmatched_expected_indexes, indeterminate_expected_indexes =
-
categorize_indexes(expected_to_actual_matched_indexes, actual_to_expected_matched_indexes)
-
-
unmatched_actual_indexes, indeterminate_actual_indexes =
-
categorize_indexes(actual_to_expected_matched_indexes, expected_to_actual_matched_indexes)
-
-
@solution = Solution.new(unmatched_expected_indexes, unmatched_actual_indexes,
-
indeterminate_expected_indexes, indeterminate_actual_indexes)
-
end
-
-
1
def find_best_solution
-
return solution if solution.candidate?
-
best_solution_so_far = NullSolution
-
-
expected_index = solution.indeterminate_expected_indexes.first
-
actuals = expected_to_actual_matched_indexes[expected_index]
-
-
actuals.each do |actual_index|
-
solution = best_solution_for_pairing(expected_index, actual_index)
-
return solution if solution.ideal?
-
best_solution_so_far = solution if best_solution_so_far.worse_than?(solution)
-
end
-
-
best_solution_so_far
-
end
-
-
1
private
-
-
# @private
-
# Starting solution that is worse than any other real solution.
-
1
NullSolution = Class.new do
-
1
def self.worse_than?(_other)
-
true
-
end
-
end
-
-
1
def categorize_indexes(indexes_to_categorize, other_indexes)
-
unmatched = []
-
indeterminate = []
-
-
indexes_to_categorize.each_pair do |index, matches|
-
if matches.empty?
-
unmatched << index
-
elsif !reciprocal_single_match?(matches, index, other_indexes)
-
indeterminate << index
-
end
-
end
-
-
return unmatched, indeterminate
-
end
-
-
1
def reciprocal_single_match?(matches, index, other_list)
-
return false unless matches.one?
-
other_list[matches.first] == [index]
-
end
-
-
1
def best_solution_for_pairing(expected_index, actual_index)
-
modified_expecteds = apply_pairing_to(
-
solution.indeterminate_expected_indexes,
-
expected_to_actual_matched_indexes, actual_index)
-
-
modified_expecteds.delete(expected_index)
-
-
modified_actuals = apply_pairing_to(
-
solution.indeterminate_actual_indexes,
-
actual_to_expected_matched_indexes, expected_index)
-
-
modified_actuals.delete(actual_index)
-
-
solution + self.class.new(modified_expecteds, modified_actuals).find_best_solution
-
end
-
-
1
def apply_pairing_to(indeterminates, original_matches, other_list_index)
-
indeterminates.inject({}) do |accum, index|
-
accum[index] = original_matches[index] - [other_list_index]
-
accum
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
1
require 'rspec/support'
-
-
1
module RSpec
-
1
module Matchers
-
1
module BuiltIn
-
# @api private
-
# Provides the implementation for operator matchers.
-
# Not intended to be instantiated directly.
-
# Only available for use with `should`.
-
1
class OperatorMatcher
-
1
class << self
-
# @private
-
1
def registry
-
4
@registry ||= {}
-
end
-
-
# @private
-
1
def register(klass, operator, matcher)
-
2
registry[klass] ||= {}
-
2
registry[klass][operator] = matcher
-
end
-
-
# @private
-
1
def unregister(klass, operator)
-
registry[klass] && registry[klass].delete(operator)
-
end
-
-
# @private
-
1
def get(klass, operator)
-
klass.ancestors.each do |ancestor|
-
matcher = registry[ancestor] && registry[ancestor][operator]
-
return matcher if matcher
-
end
-
-
nil
-
end
-
end
-
-
1
register Enumerable, '=~', BuiltIn::ContainExactly
-
-
1
def initialize(actual)
-
@actual = actual
-
end
-
-
# @private
-
1
def self.use_custom_matcher_or_delegate(operator)
-
7
define_method(operator) do |expected|
-
if !has_non_generic_implementation_of?(operator) && (matcher = OperatorMatcher.get(@actual.class, operator))
-
@actual.__send__(::RSpec::Matchers.last_expectation_handler.should_method, matcher.new(expected))
-
else
-
eval_match(@actual, operator, expected)
-
end
-
end
-
-
7
negative_operator = operator.sub(/^=/, '!')
-
7
if negative_operator != operator && respond_to?(negative_operator)
-
2
define_method(negative_operator) do |_expected|
-
opposite_should = ::RSpec::Matchers.last_expectation_handler.opposite_should_method
-
raise "RSpec does not support `#{::RSpec::Matchers.last_expectation_handler.should_method} #{negative_operator} expected`. " \
-
"Use `#{opposite_should} #{operator} expected` instead."
-
end
-
end
-
end
-
-
1
['==', '===', '=~', '>', '>=', '<', '<='].each do |operator|
-
7
use_custom_matcher_or_delegate operator
-
end
-
-
# @private
-
1
def fail_with_message(message)
-
RSpec::Expectations.fail_with(message, @expected, @actual)
-
end
-
-
# @api private
-
# @return [String]
-
1
def description
-
"#{@operator} #{@expected.inspect}"
-
end
-
-
1
private
-
-
1
def has_non_generic_implementation_of?(op)
-
Support.method_handle_for(@actual, op).owner != ::Kernel
-
rescue NameError
-
false
-
end
-
-
1
def eval_match(actual, operator, expected)
-
::RSpec::Matchers.last_matcher = self
-
@operator, @expected = operator, expected
-
__delegate_operator(actual, operator, expected)
-
end
-
end
-
-
# @private
-
# Handles operator matcher for `should`.
-
1
class PositiveOperatorMatcher < OperatorMatcher
-
1
def __delegate_operator(actual, operator, expected)
-
if actual.__send__(operator, expected)
-
true
-
elsif ['==', '===', '=~'].include?(operator)
-
fail_with_message("expected: #{expected.inspect}\n got: #{actual.inspect} (using #{operator})")
-
else
-
fail_with_message("expected: #{operator} #{expected.inspect}\n got: #{operator.gsub(/./, ' ')} #{actual.inspect}")
-
end
-
end
-
end
-
-
# @private
-
# Handles operator matcher for `should_not`.
-
1
class NegativeOperatorMatcher < OperatorMatcher
-
1
def __delegate_operator(actual, operator, expected)
-
return false unless actual.__send__(operator, expected)
-
fail_with_message("expected not: #{operator} #{expected.inspect}\n got: #{operator.gsub(/./, ' ')} #{actual.inspect}")
-
end
-
end
-
end
-
end
-
end
-
1
RSpec::Support.require_rspec_support "fuzzy_matcher"
-
-
1
module RSpec
-
1
module Matchers
-
# Mixin designed to support the composable matcher features
-
# of RSpec 3+. Mix it into your custom matcher classes to
-
# allow them to be used in a composable fashion.
-
#
-
# @api public
-
1
module Composable
-
# Creates a compound `and` expectation. The matcher will
-
# only pass if both sub-matchers pass.
-
# This can be chained together to form an arbitrarily long
-
# chain of matchers.
-
#
-
# @example
-
# expect(alphabet).to start_with("a").and end_with("z")
-
# expect(alphabet).to start_with("a") & end_with("z")
-
#
-
# @note The negative form (`expect(...).not_to matcher.and other`)
-
# is not supported at this time.
-
1
def and(matcher)
-
BuiltIn::Compound::And.new self, matcher
-
end
-
1
alias & and
-
-
# Creates a compound `or` expectation. The matcher will
-
# pass if either sub-matcher passes.
-
# This can be chained together to form an arbitrarily long
-
# chain of matchers.
-
#
-
# @example
-
# expect(stoplight.color).to eq("red").or eq("green").or eq("yellow")
-
# expect(stoplight.color).to eq("red") | eq("green") | eq("yellow")
-
#
-
# @note The negative form (`expect(...).not_to matcher.or other`)
-
# is not supported at this time.
-
1
def or(matcher)
-
BuiltIn::Compound::Or.new self, matcher
-
end
-
1
alias | or
-
-
# Delegates to `#matches?`. Allows matchers to be used in composable
-
# fashion and also supports using matchers in case statements.
-
1
def ===(value)
-
matches?(value)
-
end
-
-
1
private
-
-
# This provides a generic way to fuzzy-match an expected value against
-
# an actual value. It understands nested data structures (e.g. hashes
-
# and arrays) and is able to match against a matcher being used as
-
# the expected value or within the expected value at any level of
-
# nesting.
-
#
-
# Within a custom matcher you are encouraged to use this whenever your
-
# matcher needs to match two values, unless it needs more precise semantics.
-
# For example, the `eq` matcher _does not_ use this as it is meant to
-
# use `==` (and only `==`) for matching.
-
#
-
# @param expected [Object] what is expected
-
# @param actual [Object] the actual value
-
#
-
# @!visibility public
-
1
def values_match?(expected, actual)
-
1
expected = with_matchers_cloned(expected)
-
1
Support::FuzzyMatcher.values_match?(expected, actual)
-
end
-
-
# Returns the description of the given object in a way that is
-
# aware of composed matchers. If the object is a matcher with
-
# a `description` method, returns the description; otherwise
-
# returns `object.inspect`.
-
#
-
# You are encouraged to use this in your custom matcher's
-
# `description`, `failure_message` or
-
# `failure_message_when_negated` implementation if you are
-
# supporting matcher arguments.
-
#
-
# @!visibility public
-
1
def description_of(object)
-
return object.description if Matchers.is_a_describable_matcher?(object)
-
object.inspect
-
end
-
-
# Transforms the given data structue (typically a hash or array)
-
# into a new data structure that, when `#inspect` is called on it,
-
# will provide descriptions of any contained matchers rather than
-
# the normal `#inspect` output.
-
#
-
# You are encouraged to use this in your custom matcher's
-
# `description`, `failure_message` or
-
# `failure_message_when_negated` implementation if you are
-
# supporting any arguments which may be a data structure
-
# containing matchers.
-
#
-
# @!visibility public
-
1
def surface_descriptions_in(item)
-
if Matchers.is_a_describable_matcher?(item)
-
DescribableItem.new(item)
-
elsif Hash === item
-
Hash[surface_descriptions_in(item.to_a)]
-
elsif Struct === item
-
item.inspect
-
elsif enumerable?(item)
-
begin
-
item.map { |subitem| surface_descriptions_in(subitem) }
-
rescue IOError # STDOUT is enumerable but `map` raises an error
-
item.inspect
-
end
-
else
-
item
-
end
-
end
-
-
# @private
-
# Historically, a single matcher instance was only checked
-
# against a single value. Given that the matcher was only
-
# used once, it's been common to memoize some intermediate
-
# calculation that is derived from the `actual` value in
-
# order to reuse that intermediate result in the failure
-
# message.
-
#
-
# This can cause a problem when using such a matcher as an
-
# argument to another matcher in a composed matcher expression,
-
# since the matcher instance may be checked against multiple
-
# values and produce invalid results due to the memoization.
-
#
-
# To deal with this, we clone any matchers in `expected` via
-
# this method when using `values_match?`, so that any memoization
-
# does not "leak" between checks.
-
1
def with_matchers_cloned(object)
-
1
if Matchers.is_a_matcher?(object)
-
object.clone
-
1
elsif Hash === object
-
Hash[with_matchers_cloned(object.to_a)]
-
1
elsif Struct === object
-
object
-
1
elsif enumerable?(object)
-
begin
-
object.map { |subobject| with_matchers_cloned(subobject) }
-
rescue IOError # STDOUT is enumerable but `map` raises an error
-
object
-
end
-
else
-
1
object
-
end
-
end
-
-
1
if String.ancestors.include?(Enumerable) # 1.8.7
-
# Strings are not enumerable on 1.9, and on 1.8 they are an infinitely
-
# nested enumerable: since ruby lacks a character class, it yields
-
# 1-character strings, which are themselves enumerable, composed of a
-
# a single 1-character string, which is an enumerable, etc.
-
#
-
# @api private
-
def enumerable?(item)
-
return false if String === item
-
Enumerable === item
-
end
-
else
-
# @api private
-
1
def enumerable?(item)
-
1
Enumerable === item
-
end
-
end
-
1
module_function :surface_descriptions_in, :enumerable?
-
-
# Wraps an item in order to surface its `description` via `inspect`.
-
# @api private
-
1
DescribableItem = Struct.new(:item) do
-
1
def inspect
-
"(#{item.description})"
-
end
-
-
1
def pretty_print(pp)
-
pp.text "(#{item.description})"
-
end
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Matchers
-
# Defines the custom matcher DSL.
-
1
module DSL
-
# Defines a custom matcher.
-
# @see RSpec::Matchers
-
1
def define(name, &declarations)
-
define_method name do |*expected|
-
RSpec::Matchers::DSL::Matcher.new(name, declarations, self, *expected)
-
end
-
end
-
1
alias_method :matcher, :define
-
-
2
RSpec.configure { |c| c.extend self } if RSpec.respond_to?(:configure)
-
-
# Contains the methods that are available from within the
-
# `RSpec::Matchers.define` DSL for creating custom matchers.
-
1
module Macros
-
# Stores the block that is used to determine whether this matcher passes
-
# or fails. The block should return a boolean value. When the matcher is
-
# passed to `expect(...).to` and the block returns `true`, then the expectation
-
# passes. Similarly, when the matcher is passed to `expect(...).not_to` and the
-
# block returns `false`, then the expectation passes.
-
#
-
# @example
-
#
-
# RSpec::Matchers.define :be_even do
-
# match do |actual|
-
# actual.even?
-
# end
-
# end
-
#
-
# expect(4).to be_even # passes
-
# expect(3).not_to be_even # passes
-
# expect(3).to be_even # fails
-
# expect(4).not_to be_even # fails
-
#
-
# @yield [Object] actual the actual value (i.e. the value wrapped by `expect`)
-
1
def match(&match_block)
-
define_user_override(:matches?, match_block) do |actual|
-
begin
-
@actual = actual
-
super(*actual_arg_for(match_block))
-
rescue RSpec::Expectations::ExpectationNotMetError
-
false
-
end
-
end
-
end
-
-
# Use this to define the block for a negative expectation (`expect(...).not_to`)
-
# when the positive and negative forms require different handling. This
-
# is rarely necessary, but can be helpful, for example, when specifying
-
# asynchronous processes that require different timeouts.
-
#
-
# @yield [Object] actual the actual value (i.e. the value wrapped by `expect`)
-
1
def match_when_negated(&match_block)
-
define_user_override(:does_not_match?, match_block) do |actual|
-
@actual = actual
-
super(*actual_arg_for(match_block))
-
end
-
end
-
-
# Use this instead of `match` when the block will raise an exception
-
# rather than returning false to indicate a failure.
-
#
-
# @example
-
#
-
# RSpec::Matchers.define :accept_as_valid do |candidate_address|
-
# match_unless_raises ValidationException do |validator|
-
# validator.validate(candidate_address)
-
# end
-
# end
-
#
-
# expect(email_validator).to accept_as_valid("person@company.com")
-
#
-
# @yield [Object] actual the actual object (i.e. the value wrapped by `expect`)
-
1
def match_unless_raises(expected_exception=Exception, &match_block)
-
define_user_override(:matches?, match_block) do |actual|
-
@actual = actual
-
begin
-
super(*actual_arg_for(match_block))
-
rescue expected_exception => @rescued_exception
-
false
-
else
-
true
-
end
-
end
-
end
-
-
# Customizes the failure messsage to use when this matcher is
-
# asked to positively match. Only use this when the message
-
# generated by default doesn't suit your needs.
-
#
-
# @example
-
#
-
# RSpec::Matchers.define :have_strength do |expected|
-
# match { your_match_logic }
-
#
-
# failure_message do |actual|
-
# "Expected strength of #{expected}, but had #{actual.strength}"
-
# end
-
# end
-
#
-
# @yield [Object] actual the actual object (i.e. the value wrapped by `expect`)
-
1
def failure_message(&definition)
-
define_user_override(__method__, definition)
-
end
-
-
# Customize the failure messsage to use when this matcher is asked
-
# to negatively match. Only use this when the message generated by
-
# default doesn't suit your needs.
-
#
-
# @example
-
#
-
# RSpec::Matchers.define :have_strength do |expected|
-
# match { your_match_logic }
-
#
-
# failure_message_when_negated do |actual|
-
# "Expected not to have strength of #{expected}, but did"
-
# end
-
# end
-
#
-
# @yield [Object] actual the actual object (i.e. the value wrapped by `expect`)
-
1
def failure_message_when_negated(&definition)
-
define_user_override(__method__, definition)
-
end
-
-
# Customize the description to use for one-liners. Only use this when
-
# the description generated by default doesn't suit your needs.
-
#
-
# @example
-
#
-
# RSpec::Matchers.define :qualify_for do |expected|
-
# match { your_match_logic }
-
#
-
# description do
-
# "qualify for #{expected}"
-
# end
-
# end
-
#
-
# @yield [Object] actual the actual object (i.e. the value wrapped by `expect`)
-
1
def description(&definition)
-
define_user_override(__method__, definition)
-
end
-
-
# Tells the matcher to diff the actual and expected values in the failure
-
# message.
-
1
def diffable
-
define_method(:diffable?) { true }
-
end
-
-
# Declares that the matcher can be used in a block expectation.
-
# Users will not be able to use your matcher in a block
-
# expectation without declaring this.
-
# (e.g. `expect { do_something }.to matcher`).
-
1
def supports_block_expectations
-
define_method(:supports_block_expectations?) { true }
-
end
-
-
# Convenience for defining methods on this matcher to create a fluent
-
# interface. The trick about fluent interfaces is that each method must
-
# return self in order to chain methods together. `chain` handles that
-
# for you. If the method is invoked and the
-
# `include_chain_clauses_in_custom_matcher_descriptions` config option
-
# hash been enabled, the chained method name and args will be added to the
-
# default description and failure message.
-
#
-
# @example
-
#
-
# RSpec::Matchers.define :have_errors_on do |key|
-
# chain :with do |message|
-
# @message = message
-
# end
-
#
-
# match do |actual|
-
# actual.errors[key] == @message
-
# end
-
# end
-
#
-
# expect(minor).to have_errors_on(:age).with("Not old enough to participate")
-
1
def chain(name, &definition)
-
define_user_override(name, definition) do |*args, &block|
-
super(*args, &block)
-
@chained_method_clauses.push([name, args])
-
self
-
end
-
end
-
-
1
private
-
-
# Does the following:
-
#
-
# - Defines the named method usign a user-provided block
-
# in @user_method_defs, which is included as an ancestor
-
# in the singleton class in which we eval the `define` block.
-
# - Defines an overriden definition for the same method
-
# usign the provided `our_def` block.
-
# - Provides a default `our_def` block for the common case
-
# of needing to call the user's definition with `@actual`
-
# as an arg, but only if their block's arity can handle it.
-
#
-
# This compiles the user block into an actual method, allowing
-
# them to use normal method constructs like `return`
-
# (e.g. for a early guard statement), while allowing us to define
-
# an override that can provide the wrapped handling
-
# (e.g. assigning `@actual`, rescueing errors, etc) and
-
# can `super` to the user's definition.
-
1
def define_user_override(method_name, user_def, &our_def)
-
@user_method_defs.__send__(:define_method, method_name, &user_def)
-
our_def ||= lambda { super(*actual_arg_for(user_def)) }
-
define_method(method_name, &our_def)
-
end
-
-
# Defines deprecated macro methods from RSpec 2 for backwards compatibility.
-
# @deprecated Use the methods from {Macros} instead.
-
1
module Deprecated
-
# @deprecated Use {Macros#match} instead.
-
1
def match_for_should(&definition)
-
RSpec.deprecate("`match_for_should`", :replacement => "`match`")
-
match(&definition)
-
end
-
-
# @deprecated Use {Macros#match_when_negated} instead.
-
1
def match_for_should_not(&definition)
-
RSpec.deprecate("`match_for_should_not`", :replacement => "`match_when_negated`")
-
match_when_negated(&definition)
-
end
-
-
# @deprecated Use {Macros#failure_message} instead.
-
1
def failure_message_for_should(&definition)
-
RSpec.deprecate("`failure_message_for_should`", :replacement => "`failure_message`")
-
failure_message(&definition)
-
end
-
-
# @deprecated Use {Macros#failure_message_when_negated} instead.
-
1
def failure_message_for_should_not(&definition)
-
RSpec.deprecate("`failure_message_for_should_not`", :replacement => "`failure_message_when_negated`")
-
failure_message_when_negated(&definition)
-
end
-
end
-
end
-
-
# Defines default implementations of the matcher
-
# protocol methods for custom matchers. You can
-
# override any of these using the {RSpec::Matchers::DSL::Macros Macros} methods
-
# from within an `RSpec::Matchers.define` block.
-
1
module DefaultImplementations
-
1
include BuiltIn::BaseMatcher::DefaultFailureMessages
-
-
# @api private
-
# Used internally by objects returns by `should` and `should_not`.
-
1
def diffable?
-
false
-
end
-
-
# The default description.
-
1
def description
-
"#{name_to_sentence}#{to_sentence expected}#{chained_method_clause_sentences}"
-
end
-
-
# Matchers do not support block expectations by default. You
-
# must opt-in.
-
1
def supports_block_expectations?
-
false
-
end
-
-
# Most matchers do not expect call stack jumps.
-
1
def expects_call_stack_jump?
-
false
-
end
-
-
1
private
-
-
1
def chained_method_clause_sentences
-
return '' unless Expectations.configuration.include_chain_clauses_in_custom_matcher_descriptions?
-
-
@chained_method_clauses.map do |(method_name, method_args)|
-
" #{split_words(method_name)}#{to_sentence(method_args)}"
-
end.join
-
end
-
end
-
-
# The class used for custom matchers. The block passed to
-
# `RSpec::Matchers.define` will be evaluated in the context
-
# of the singleton class of an instance, and will have the
-
# {RSpec::Matchers::DSL::Macros Macros} methods available.
-
1
class Matcher
-
# Provides default implementations for the matcher protocol methods.
-
1
include DefaultImplementations
-
-
# Allows expectation expressions to be used in the match block.
-
1
include RSpec::Matchers
-
-
# Converts matcher name and expected args to an English expresion.
-
1
include RSpec::Matchers::Pretty
-
-
# Supports the matcher composability features of RSpec 3+.
-
1
include Composable
-
-
# Makes the macro methods available to an `RSpec::Matchers.define` block.
-
1
extend Macros
-
1
extend Macros::Deprecated
-
-
# Exposes the value being matched against -- generally the object
-
# object wrapped by `expect`.
-
1
attr_reader :actual
-
-
# Exposes the exception raised during the matching by `match_unless_raises`.
-
# Could be useful to extract details for a failure message.
-
1
attr_reader :rescued_exception
-
-
# @api private
-
1
def initialize(name, declarations, matcher_execution_context, *expected)
-
@name = name
-
@actual = nil
-
@expected_as_array = expected
-
@matcher_execution_context = matcher_execution_context
-
@chained_method_clauses = []
-
-
class << self
-
# See `Macros#define_user_override` above, for an explanation.
-
include(@user_method_defs = Module.new)
-
self
-
end.class_exec(*expected, &declarations)
-
end
-
-
# Provides the expected value. This will return an array if
-
# multiple arguments were passed to the matcher; otherwise it
-
# will return a single value.
-
# @see #expected_as_array
-
1
def expected
-
if expected_as_array.size == 1
-
expected_as_array[0]
-
else
-
expected_as_array
-
end
-
end
-
-
# Returns the expected value as an an array. This exists primarily
-
# to aid in upgrading from RSpec 2.x, since in RSpec 2, `expected`
-
# always returned an array.
-
# @see #expected
-
1
attr_reader :expected_as_array
-
-
# Adds the name (rather than a cryptic hex number)
-
# so we can identify an instance of
-
# the matcher in error messages (e.g. for `NoMethodError`)
-
1
def inspect
-
"#<#{self.class.name} #{name}>"
-
end
-
-
1
if RUBY_VERSION.to_f >= 1.9
-
# Indicates that this matcher responds to messages
-
# from the `@matcher_execution_context` as well.
-
# Also, supports getting a method object for such methods.
-
1
def respond_to_missing?(method, include_private=false)
-
super || @matcher_execution_context.respond_to?(method, include_private)
-
end
-
else # for 1.8.7
-
# Indicates that this matcher responds to messages
-
# from the `@matcher_execution_context` as well.
-
def respond_to?(method, include_private=false)
-
super || @matcher_execution_context.respond_to?(method, include_private)
-
end
-
end
-
-
1
private
-
-
1
def actual_arg_for(block)
-
block.arity.zero? ? [] : [@actual]
-
end
-
-
# Takes care of forwarding unhandled messages to the
-
# `@matcher_execution_context` (typically the current
-
# running `RSpec::Core::Example`). This is needed by
-
# rspec-rails so that it can define matchers that wrap
-
# Rails' test helper methods, but it's also a useful
-
# feature in its own right.
-
1
def method_missing(method, *args, &block)
-
if @matcher_execution_context.respond_to?(method)
-
@matcher_execution_context.__send__ method, *args, &block
-
else
-
super(method, *args, &block)
-
end
-
end
-
end
-
end
-
end
-
end
-
-
1
RSpec::Matchers.extend RSpec::Matchers::DSL
-
1
module RSpec
-
1
module Matchers
-
1
class << self
-
# @private
-
1
attr_accessor :last_matcher, :last_expectation_handler
-
end
-
-
# @api private
-
# Used by rspec-core to clear the state used to generate
-
# descriptions after an example.
-
1
def self.clear_generated_description
-
4
self.last_matcher = nil
-
4
self.last_expectation_handler = nil
-
end
-
-
# @api private
-
# Generates an an example description based on the last expectation.
-
# Used by rspec-core's one-liner syntax.
-
1
def self.generated_description
-
return nil if last_expectation_handler.nil?
-
"#{last_expectation_handler.verb} #{last_description}"
-
end
-
-
1
private
-
-
1
def self.last_description
-
last_matcher.respond_to?(:description) ? last_matcher.description : <<-MESSAGE
-
When you call a matcher in an example without a String, like this:
-
-
specify { expect(object).to matcher }
-
-
or this:
-
-
it { is_expected.to matcher }
-
-
RSpec expects the matcher to have a #description method. You should either
-
add a String to the example this matcher is being used in, or give it a
-
description method. Then you won't have to suffer this lengthy warning again.
-
MESSAGE
-
end
-
end
-
end
-
1
module RSpec
-
1
module Matchers
-
# Provides the necessary plumbing to wrap a matcher with a decorator.
-
# @private
-
1
class MatcherDelegator
-
1
include Composable
-
1
attr_reader :base_matcher
-
-
1
def initialize(base_matcher)
-
@base_matcher = base_matcher
-
end
-
-
1
def method_missing(*args, &block)
-
base_matcher.__send__(*args, &block)
-
end
-
-
1
if ::RUBY_VERSION.to_f > 1.8
-
1
def respond_to_missing?(name, include_all=false)
-
super || base_matcher.respond_to?(name, include_all)
-
end
-
else
-
def respond_to?(name, include_all=false)
-
super || base_matcher.respond_to?(name, include_all)
-
end
-
end
-
-
1
def initialize_copy(other)
-
@base_matcher = @base_matcher.clone
-
super
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Matchers
-
# @api private
-
# Contains logic to facilitate converting ruby symbols and
-
# objects to english phrases.
-
1
module Pretty
-
# @api private
-
# Converts a symbol into an english expression.
-
1
def split_words(sym)
-
sym.to_s.gsub(/_/, ' ')
-
end
-
1
module_function :split_words
-
-
# @api private
-
# Converts a collection of objects into an english expression.
-
1
def to_sentence(words)
-
return " #{words.inspect}" if !words || Struct === words
-
words = Array(words).map { |w| to_word(w) }
-
case words.length
-
when 0
-
""
-
when 1
-
" #{words[0]}"
-
when 2
-
" #{words[0]} and #{words[1]}"
-
else
-
" #{words[0...-1].join(', ')}, and #{words[-1]}"
-
end
-
end
-
-
# @api private
-
# Converts the given item to string suitable for use in a list expression.
-
1
def to_word(item)
-
is_matcher_with_description?(item) ? item.description : item.inspect
-
end
-
-
# @private
-
# Provides an English expression for the matcher name.
-
1
def name_to_sentence
-
split_words(name)
-
end
-
-
# @api private
-
# Provides a name for the matcher.
-
1
def name
-
defined?(@name) ? @name : underscore(self.class.name.split("::").last)
-
end
-
-
# @private
-
# Borrowed from ActiveSupport
-
1
def underscore(camel_cased_word)
-
word = camel_cased_word.to_s.dup
-
word.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
-
word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
-
word.tr!("-", "_")
-
word.downcase!
-
word
-
end
-
-
1
private
-
-
1
def is_matcher_with_description?(object)
-
RSpec::Matchers.is_a_matcher?(object) && object.respond_to?(:description)
-
end
-
-
# `{ :a => 5, :b => 2 }.inspect` produces:
-
# {:a=>5, :b=>2}
-
# ...but it looks much better as:
-
# {:a => 5, :b => 2}
-
#
-
# This is idempotent and safe to run on a string multiple times.
-
1
def improve_hash_formatting(inspect_string)
-
inspect_string.gsub(/(\S)=>(\S)/, '\1 => \2')
-
end
-
end
-
end
-
end
-
1
require 'rspec/support'
-
1
RSpec::Support.require_rspec_support 'caller_filter'
-
1
RSpec::Support.require_rspec_support 'warnings'
-
1
RSpec::Support.require_rspec_support 'ruby_features'
-
-
31
RSpec::Support.define_optimized_require_for_rspec(:mocks) { |f| require_relative f }
-
-
%w[
-
instance_method_stasher
-
method_double
-
argument_matchers
-
example_methods
-
proxy
-
test_double
-
argument_list_matcher
-
message_expectation
-
order_group
-
error_generator
-
space
-
mutate_const
-
targets
-
syntax
-
configuration
-
verifying_double
-
version
-
18
].each { |name| RSpec::Support.require_rspec_mocks name }
-
-
# Share the top-level RSpec namespace, because we are a core supported
-
# extension.
-
1
module RSpec
-
# Contains top-level utility methods. While this contains a few
-
# public methods, these are not generally meant to be called from
-
# a test or example. They exist primarily for integration with
-
# test frameworks (such as rspec-core).
-
1
module Mocks
-
# Performs per-test/example setup. This should be called before
-
# an test or example begins.
-
1
def self.setup
-
4
@space_stack << (@space = space.new_scope)
-
end
-
-
# Verifies any message expectations that were set during the
-
# test or example. This should be called at the end of an example.
-
1
def self.verify
-
3
space.verify_all
-
end
-
-
# Cleans up all test double state (including any methods that were
-
# redefined on partial doubles). This _must_ be called after
-
# each example, even if an error was raised during the example.
-
1
def self.teardown
-
4
space.reset_all
-
4
@space_stack.pop
-
4
@space = @space_stack.last || @root_space
-
end
-
-
# Adds an allowance (stub) on `subject`
-
#
-
# @param subject the subject to which the message will be added
-
# @param message a symbol, representing the message that will be
-
# added.
-
# @param opts a hash of options, :expected_from is used to set the
-
# original call site
-
# @yield an optional implementation for the allowance
-
#
-
# @example Defines the implementation of `foo` on `bar`, using the passed block
-
# x = 0
-
# RSpec::Mocks.allow_message(bar, :foo) { x += 1 }
-
1
def self.allow_message(subject, message, opts={}, &block)
-
space.proxy_for(subject).add_stub(message, opts, &block)
-
end
-
-
# Sets a message expectation on `subject`.
-
# @param subject the subject on which the message will be expected
-
# @param message a symbol, representing the message that will be
-
# expected.
-
# @param opts a hash of options, :expected_from is used to set the
-
# original call site
-
# @yield an optional implementation for the expectation
-
#
-
# @example Expect the message `foo` to receive `bar`, then call it
-
# RSpec::Mocks.expect_message(bar, :foo)
-
# bar.foo
-
1
def self.expect_message(subject, message, opts={}, &block)
-
space.proxy_for(subject).add_message_expectation(message, opts, &block)
-
end
-
-
# Call the passed block and verify mocks after it has executed. This allows
-
# mock usage in arbitrary places, such as a `before(:all)` hook.
-
1
def self.with_temporary_scope
-
setup
-
-
begin
-
yield
-
verify
-
ensure
-
teardown
-
end
-
end
-
-
1
class << self
-
# @private
-
1
attr_reader :space
-
end
-
1
@space_stack = []
-
1
@root_space = @space = RSpec::Mocks::RootSpace.new
-
-
# @private
-
1
IGNORED_BACKTRACE_LINE = 'this backtrace line is ignored'
-
-
# To speed up boot time a bit, delay loading optional or rarely
-
# used features until their first use.
-
1
autoload :AnyInstance, "rspec/mocks/any_instance"
-
1
autoload :ExpectChain, "rspec/mocks/message_chain"
-
1
autoload :StubChain, "rspec/mocks/message_chain"
-
1
autoload :MarshalExtension, "rspec/mocks/marshal_extension"
-
-
# Namespace for mock-related matchers.
-
1
module Matchers
-
1
autoload :HaveReceived, "rspec/mocks/matchers/have_received"
-
1
autoload :Receive, "rspec/mocks/matchers/receive"
-
1
autoload :ReceiveMessageChain, "rspec/mocks/matchers/receive_message_chain"
-
1
autoload :ReceiveMessages, "rspec/mocks/matchers/receive_messages"
-
end
-
end
-
end
-
%w[
-
any_instance/chain
-
any_instance/stub_chain
-
any_instance/stub_chain_chain
-
any_instance/expect_chain_chain
-
any_instance/expectation_chain
-
any_instance/message_chains
-
any_instance/recorder
-
any_instance/proxy
-
9
].each { |f| RSpec::Support.require_rspec_mocks(f) }
-
1
module RSpec
-
1
module Mocks
-
# @private
-
1
module AnyInstance
-
# @private
-
1
class Chain
-
1
def initialize(recorder, *args, &block)
-
2
@recorder = recorder
-
2
@expectation_args = args
-
2
@expectation_block = block
-
2
@argument_list_matcher = ArgumentListMatcher::MATCH_ALL
-
end
-
-
# @private
-
#
-
# Provides convenience methods for recording customizations on message
-
# expectations.
-
1
module Customizations
-
# @macro [attach] record
-
# @method $1(*args, &block)
-
# Records the `$1` message for playback against an instance that
-
# invokes a method stubbed or mocked using `any_instance`.
-
#
-
# @see RSpec::Mocks::MessageExpectation#$1
-
#
-
1
def self.record(method_name)
-
14
define_method(method_name) do |*args, &block|
-
2
record(method_name, *args, &block)
-
end
-
end
-
-
1
record :and_return
-
1
record :and_raise
-
1
record :and_throw
-
1
record :and_yield
-
1
record :and_call_original
-
1
record :with
-
1
record :once
-
1
record :twice
-
1
record :thrice
-
1
record :exactly
-
1
record :times
-
1
record :never
-
1
record :at_least
-
1
record :at_most
-
end
-
-
1
include Customizations
-
-
# @private
-
1
def playback!(instance)
-
2
message_expectation = create_message_expectation_on(instance)
-
2
messages.inject(message_expectation) do |object, message|
-
2
object.__send__(*message.first, &message.last)
-
end
-
end
-
-
# @private
-
1
def constrained_to_any_of?(*constraints)
-
constraints.any? do |constraint|
-
messages.any? do |message|
-
message.first.first == constraint
-
end
-
end
-
end
-
-
# @private
-
1
def matches_args?(*args)
-
@argument_list_matcher.args_match?(*args)
-
end
-
-
# @private
-
1
def expectation_fulfilled!
-
@expectation_fulfilled = true
-
end
-
-
1
def never
-
ErrorGenerator.raise_double_negation_error("expect_any_instance_of(MyClass)") if negated?
-
super
-
end
-
-
1
def with(*args, &block)
-
@argument_list_matcher = ArgumentListMatcher.new(*args)
-
super
-
end
-
-
1
private
-
-
1
def negated?
-
messages.any? { |(message, *_), _| message == :never }
-
end
-
-
1
def messages
-
6
@messages ||= []
-
end
-
-
1
def last_message
-
2
messages.last.first.first unless messages.empty?
-
end
-
-
1
def record(rspec_method_name, *args, &block)
-
2
verify_invocation_order(rspec_method_name, *args, &block)
-
2
messages << [args.unshift(rspec_method_name), block]
-
2
self
-
end
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Mocks
-
1
module AnyInstance
-
# @private
-
1
class ExpectChainChain < StubChain
-
1
def initialize(*args)
-
super
-
@expectation_fulfilled = false
-
end
-
-
1
def expectation_fulfilled?
-
@expectation_fulfilled
-
end
-
-
1
def playback!(instance)
-
super.tap { @expectation_fulfilled = true }
-
end
-
-
1
private
-
-
1
def create_message_expectation_on(instance)
-
::RSpec::Mocks::ExpectChain.expect_chain_on(instance, *@expectation_args, &@expectation_block)
-
end
-
-
1
def invocation_order
-
@invocation_order ||= {
-
:and_return => [nil],
-
:and_raise => [nil],
-
:and_yield => [nil]
-
}
-
end
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Mocks
-
1
module AnyInstance
-
# @private
-
1
class ExpectationChain < Chain
-
1
def expectation_fulfilled?
-
@expectation_fulfilled || constrained_to_any_of?(:never)
-
end
-
-
1
def initialize(*args, &block)
-
@expectation_fulfilled = false
-
super
-
end
-
-
1
private
-
-
1
def verify_invocation_order(_rspec_method_name, *_args, &_block)
-
end
-
end
-
-
# @private
-
1
class PositiveExpectationChain < ExpectationChain
-
1
private
-
-
1
def create_message_expectation_on(instance)
-
proxy = ::RSpec::Mocks.space.proxy_for(instance)
-
method_name, opts = @expectation_args
-
opts = (opts || {}).merge(:expected_form => IGNORED_BACKTRACE_LINE)
-
-
me = proxy.add_message_expectation(method_name, opts, &@expectation_block)
-
if RSpec::Mocks.configuration.yield_receiver_to_any_instance_implementation_blocks?
-
me.and_yield_receiver_to_implementation
-
end
-
-
me
-
end
-
-
1
def invocation_order
-
@invocation_order ||= {
-
:with => [nil],
-
:and_return => [:with, nil],
-
:and_raise => [:with, nil]
-
}
-
end
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Mocks
-
1
module AnyInstance
-
# @private
-
1
class MessageChains
-
1
def initialize
-
244
@chains_by_method_name = Hash.new { |h, k| h[k] = [] }
-
end
-
-
# @private
-
1
def [](method_name)
-
@chains_by_method_name[method_name]
-
end
-
-
# @private
-
1
def add(method_name, chain)
-
2
@chains_by_method_name[method_name] << chain
-
2
chain
-
end
-
-
# @private
-
1
def remove_stub_chains_for!(method_name)
-
@chains_by_method_name[method_name].reject! do |chain|
-
StubChain === chain
-
end
-
end
-
-
# @private
-
1
def has_expectation?(method_name)
-
2
@chains_by_method_name[method_name].find do |chain|
-
2
ExpectationChain === chain
-
end
-
end
-
-
# @private
-
1
def each_unfulfilled_expectation_matching(method_name, *args)
-
122
@chains_by_method_name[method_name].each do |chain|
-
2
yield chain if !chain.expectation_fulfilled? && chain.matches_args?(*args)
-
end
-
end
-
-
# @private
-
1
def all_expectations_fulfilled?
-
@chains_by_method_name.all? do |_method_name, chains|
-
chains.all? { |chain| chain.expectation_fulfilled? }
-
end
-
end
-
-
# @private
-
1
def unfulfilled_expectations
-
@chains_by_method_name.map do |method_name, chains|
-
method_name.to_s if ExpectationChain === chains.last unless chains.last.expectation_fulfilled?
-
end.compact
-
end
-
-
# @private
-
1
def received_expected_message!(method_name)
-
@chains_by_method_name[method_name].each do |chain|
-
chain.expectation_fulfilled!
-
end
-
end
-
-
# @private
-
1
def playback!(instance, method_name)
-
2
raise_if_second_instance_to_receive_message(instance)
-
2
@chains_by_method_name[method_name].each do |chain|
-
2
chain.playback!(instance)
-
end
-
end
-
-
1
private
-
-
1
def raise_if_second_instance_to_receive_message(instance)
-
2
@instance_with_expectation ||= instance if ExpectationChain === instance
-
2
return unless ExpectationChain === instance
-
return if @instance_with_expectation.equal?(instance)
-
-
raise RSpec::Mocks::MockExpectationError,
-
"Exactly one instance should have received the following " \
-
"message(s) but didn't: #{unfulfilled_expectations.sort.join(', ')}"
-
end
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Mocks
-
1
module AnyInstance
-
# @private
-
# The `AnyInstance::Recorder` is responsible for redefining the klass's
-
# instance method in order to add any stubs/expectations the first time
-
# the method is called. It's not capable of updating a stub on an instance
-
# that's already been previously stubbed (either directly, or via
-
# `any_instance`).
-
#
-
# This proxy sits in front of the recorder and delegates both to it
-
# and to the `RSpec::Mocks::Proxy` for each already mocked or stubbed
-
# instance of the class, in order to propogates changes to the instances.
-
#
-
# Note that unlike `RSpec::Mocks::Proxy`, this proxy class is stateless
-
# and is not persisted in `RSpec::Mocks.space`.
-
#
-
# Proxying for the message expectation fluent interface (typically chained
-
# off of the return value of one of these methods) is provided by the
-
# `FluentInterfaceProxy` class below.
-
1
class Proxy
-
1
def initialize(recorder, target_proxies)
-
2
@recorder = recorder
-
2
@target_proxies = target_proxies
-
end
-
-
1
def klass
-
@recorder.klass
-
end
-
-
1
def stub(method_name_or_method_map, &block)
-
2
if Hash === method_name_or_method_map
-
method_name_or_method_map.each do |method_name, return_value|
-
stub(method_name).and_return(return_value)
-
end
-
else
-
2
perform_proxying(__method__, [method_name_or_method_map], block) do |proxy|
-
proxy.add_stub(method_name_or_method_map, &block)
-
end
-
end
-
end
-
-
1
def unstub(method_name)
-
perform_proxying(__method__, [method_name], nil) do |proxy|
-
proxy.remove_stub_if_present(method_name)
-
end
-
end
-
-
1
def stub_chain(*chain, &block)
-
perform_proxying(__method__, chain, block) do |proxy|
-
Mocks::StubChain.stub_chain_on(proxy.object, *chain, &block)
-
end
-
end
-
-
1
def expect_chain(*chain, &block)
-
perform_proxying(__method__, chain, block) do |proxy|
-
Mocks::ExpectChain.expect_chain_on(proxy.object, *chain, &block)
-
end
-
end
-
-
1
def should_receive(method_name, &block)
-
perform_proxying(__method__, [method_name], block) do |proxy|
-
# Yeah, this is a bit odd...but if we used `add_message_expectation`
-
# then it would act like `expect_every_instance_of(klass).to receive`.
-
# The any_instance recorder takes care of validating that an instance
-
# received the message.
-
proxy.add_stub(method_name, &block)
-
end
-
end
-
-
1
def should_not_receive(method_name, &block)
-
perform_proxying(__method__, [method_name], block) do |proxy|
-
proxy.add_message_expectation(method_name, &block).never
-
end
-
end
-
-
1
private
-
-
1
def perform_proxying(method_name, args, block, &target_proxy_block)
-
2
recorder_value = @recorder.__send__(method_name, *args, &block)
-
2
proxy_values = @target_proxies.map(&target_proxy_block)
-
2
FluentInterfaceProxy.new([recorder_value] + proxy_values)
-
end
-
end
-
-
# @private
-
# Delegates messages to each of the given targets in order to
-
# provide the fluent interface that is available off of message
-
# expectations when dealing with `any_instance`.
-
#
-
# `targets` will typically contain 1 of the `AnyInstance::Recorder`
-
# return values and N `MessageExpectation` instances (one per instance
-
# of the `any_instance` klass).
-
1
class FluentInterfaceProxy
-
1
def initialize(targets)
-
4
@targets = targets
-
end
-
-
1
if RUBY_VERSION.to_f > 1.8
-
1
def respond_to_missing?(method_name, include_private=false)
-
super || @targets.first.respond_to?(method_name, include_private)
-
end
-
else
-
def respond_to?(method_name, include_private=false)
-
super || @targets.first.respond_to?(method_name, include_private)
-
end
-
end
-
-
1
def method_missing(*args, &block)
-
4
return_values = @targets.map { |t| t.__send__(*args, &block) }
-
2
FluentInterfaceProxy.new(return_values)
-
end
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Mocks
-
1
module AnyInstance
-
# Given a class `TheClass`, `TheClass.any_instance` returns a `Recorder`,
-
# which records stubs and message expectations for later playback on
-
# instances of `TheClass`.
-
#
-
# Further constraints are stored in instances of [Chain](Chain).
-
#
-
# @see AnyInstance
-
# @see Chain
-
1
class Recorder
-
# @private
-
1
attr_reader :message_chains, :stubs, :klass
-
-
1
def initialize(klass)
-
122
@message_chains = MessageChains.new
-
124
@stubs = Hash.new { |hash, key| hash[key] = [] }
-
122
@observed_methods = []
-
122
@played_methods = {}
-
122
@klass = klass
-
122
@expectation_set = false
-
end
-
-
# Initializes the recording a stub to be played back against any
-
# instance of this object that invokes the submitted method.
-
#
-
# @see Methods#stub
-
1
def stub(method_name, &block)
-
2
observe!(method_name)
-
2
message_chains.add(method_name, StubChain.new(self, method_name, &block))
-
end
-
-
# Initializes the recording a stub chain to be played back against any
-
# instance of this object that invokes the method matching the first
-
# argument.
-
#
-
# @see Methods#stub_chain
-
1
def stub_chain(*method_names_and_optional_return_values, &block)
-
normalize_chain(*method_names_and_optional_return_values) do |method_name, args|
-
observe!(method_name)
-
message_chains.add(method_name, StubChainChain.new(self, *args, &block))
-
end
-
end
-
-
# @private
-
1
def expect_chain(*method_names_and_optional_return_values, &block)
-
@expectation_set = true
-
normalize_chain(*method_names_and_optional_return_values) do |method_name, args|
-
observe!(method_name)
-
message_chains.add(method_name, ExpectChainChain.new(self, *args, &block))
-
end
-
end
-
-
# Initializes the recording a message expectation to be played back
-
# against any instance of this object that invokes the submitted
-
# method.
-
#
-
# @see Methods#should_receive
-
1
def should_receive(method_name, &block)
-
@expectation_set = true
-
observe!(method_name)
-
message_chains.add(method_name, PositiveExpectationChain.new(self, method_name, &block))
-
end
-
-
# The opposite of `should_receive`
-
#
-
# @see Methods#should_not_receive
-
1
def should_not_receive(method_name, &block)
-
should_receive(method_name, &block).never
-
end
-
-
# Removes any previously recorded stubs, stub_chains or message
-
# expectations that use `method_name`.
-
#
-
# @see Methods#unstub
-
1
def unstub(method_name)
-
unless @observed_methods.include?(method_name.to_sym)
-
raise RSpec::Mocks::MockExpectationError, "The method `#{method_name}` was not stubbed or was already unstubbed"
-
end
-
message_chains.remove_stub_chains_for!(method_name)
-
stubs[method_name].clear
-
stop_observing!(method_name) unless message_chains.has_expectation?(method_name)
-
end
-
-
# @api private
-
#
-
# Used internally to verify that message expectations have been
-
# fulfilled.
-
1
def verify
-
61
return unless @expectation_set
-
return if message_chains.all_expectations_fulfilled?
-
-
raise RSpec::Mocks::MockExpectationError,
-
"Exactly one instance should have received the following " \
-
"message(s) but didn't: #{message_chains.unfulfilled_expectations.sort.join(', ')}"
-
end
-
-
# @private
-
1
def stop_all_observation!
-
124
@observed_methods.each { |method_name| restore_method!(method_name) }
-
end
-
-
# @private
-
1
def playback!(instance, method_name)
-
2
RSpec::Mocks.space.ensure_registered(instance)
-
2
message_chains.playback!(instance, method_name)
-
2
@played_methods[method_name] = instance
-
2
received_expected_message!(method_name) if message_chains.has_expectation?(method_name)
-
end
-
-
# @private
-
1
def instance_that_received(method_name)
-
@played_methods[method_name]
-
end
-
-
# @private
-
1
def build_alias_method_name(method_name)
-
8
"__#{method_name}_without_any_instance__"
-
end
-
-
# @private
-
1
def already_observing?(method_name)
-
460
@observed_methods.include?(method_name) || super_class_observing?(method_name)
-
end
-
-
# @private
-
1
def notify_received_message(_object, message, args, _blk)
-
122
has_expectation = false
-
-
122
message_chains.each_unfulfilled_expectation_matching(message, *args) do |expectation|
-
has_expectation = true
-
expectation.expectation_fulfilled!
-
end
-
-
122
return unless has_expectation
-
-
restore_method!(message)
-
mark_invoked!(message)
-
end
-
-
1
protected
-
-
1
def stop_observing!(method_name)
-
restore_method!(method_name)
-
@observed_methods.delete(method_name)
-
super_class_observers_for(method_name).each do |ancestor|
-
::RSpec::Mocks.space.
-
any_instance_recorder_for(ancestor).stop_observing!(method_name)
-
end
-
end
-
-
1
private
-
-
1
def ancestor_is_an_observer?(method_name)
-
458
lambda do |ancestor|
-
914
unless ancestor == @klass
-
::RSpec::Mocks.space.
-
456
any_instance_recorder_for(ancestor).already_observing?(method_name)
-
end
-
end
-
end
-
-
1
def super_class_observers_for(method_name)
-
@klass.ancestors.select(&ancestor_is_an_observer?(method_name))
-
end
-
-
1
def super_class_observing?(method_name)
-
458
@klass.ancestors.any?(&ancestor_is_an_observer?(method_name))
-
end
-
-
1
def normalize_chain(*args)
-
args.shift.to_s.split('.').map { |s| s.to_sym }.reverse.each { |a| args.unshift a }
-
yield args.first, args
-
end
-
-
1
def received_expected_message!(method_name)
-
message_chains.received_expected_message!(method_name)
-
restore_method!(method_name)
-
mark_invoked!(method_name)
-
end
-
-
1
def restore_method!(method_name)
-
2
if public_protected_or_private_method_defined?(build_alias_method_name(method_name))
-
2
restore_original_method!(method_name)
-
else
-
remove_dummy_method!(method_name)
-
end
-
end
-
-
1
def restore_original_method!(method_name)
-
2
return unless @klass.instance_method(method_name).owner == @klass
-
-
2
alias_method_name = build_alias_method_name(method_name)
-
2
@klass.class_exec do
-
2
remove_method method_name
-
2
alias_method method_name, alias_method_name
-
2
remove_method alias_method_name
-
end
-
end
-
-
1
def remove_dummy_method!(method_name)
-
@klass.class_exec do
-
remove_method method_name
-
end
-
end
-
-
1
def backup_method!(method_name)
-
2
alias_method_name = build_alias_method_name(method_name)
-
2
@klass.class_exec do
-
2
alias_method alias_method_name, method_name
-
2
end if public_protected_or_private_method_defined?(method_name)
-
end
-
-
1
def public_protected_or_private_method_defined?(method_name)
-
6
MethodReference.method_defined_at_any_visibility?(@klass, method_name)
-
end
-
-
1
def observe!(method_name)
-
2
allow_no_prepended_module_definition_of(method_name)
-
-
2
if RSpec::Mocks.configuration.verify_partial_doubles?
-
2
unless public_protected_or_private_method_defined?(method_name)
-
raise MockExpectationError,
-
"#{@klass} does not implement ##{method_name}"
-
end
-
end
-
-
2
stop_observing!(method_name) if already_observing?(method_name)
-
2
@observed_methods << method_name
-
2
backup_method!(method_name)
-
2
recorder = self
-
2
@klass.__send__(:define_method, method_name) do |*args, &blk|
-
2
recorder.playback!(self, method_name)
-
2
__send__(method_name, *args, &blk)
-
end
-
end
-
-
1
def mark_invoked!(method_name)
-
backup_method!(method_name)
-
recorder = self
-
@klass.__send__(:define_method, method_name) do |*_args, &_blk|
-
invoked_instance = recorder.instance_that_received(method_name)
-
inspect = "#<#{self.class}:#{object_id} #{instance_variables.map { |name| "#{name}=#{instance_variable_get name}" }.join(', ')}>"
-
raise RSpec::Mocks::MockExpectationError, "The message '#{method_name}' was received by #{inspect} but has already been received by #{invoked_instance}"
-
end
-
end
-
-
1
if Support::RubyFeatures.module_prepends_supported?
-
1
def allow_no_prepended_module_definition_of(method_name)
-
2
prepended_modules = RSpec::Mocks::Proxy.prepended_modules_of(@klass)
-
2
problem_mod = prepended_modules.find { |mod| mod.method_defined?(method_name) }
-
2
return unless problem_mod
-
-
raise RSpec::Mocks::MockExpectationError,
-
"Using `any_instance` to stub a method (#{method_name}) that has been " \
-
"defined on a prepended module (#{problem_mod}) is not supported."
-
end
-
else
-
def allow_no_prepended_module_definition_of(_method_name)
-
# nothing to do; prepends aren't supported on this version of ruby
-
end
-
end
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Mocks
-
1
module AnyInstance
-
# @private
-
1
class StubChain < Chain
-
# @private
-
1
def expectation_fulfilled?
-
2
true
-
end
-
-
1
private
-
-
1
def create_message_expectation_on(instance)
-
2
proxy = ::RSpec::Mocks.space.proxy_for(instance)
-
2
method_name, opts = @expectation_args
-
2
opts = (opts || {}).merge(:expected_form => IGNORED_BACKTRACE_LINE)
-
-
2
stub = proxy.add_stub(method_name, opts, &@expectation_block)
-
2
@recorder.stubs[stub.message] << stub
-
-
2
if RSpec::Mocks.configuration.yield_receiver_to_any_instance_implementation_blocks?
-
2
stub.and_yield_receiver_to_implementation
-
end
-
-
2
stub
-
end
-
-
1
def invocation_order
-
@invocation_order ||= {
-
:with => [nil],
-
:and_return => [:with, nil],
-
:and_raise => [:with, nil],
-
:and_yield => [:with, nil],
-
:and_call_original => [:with, nil]
-
2
}
-
end
-
-
1
def verify_invocation_order(rspec_method_name, *_args, &_block)
-
2
return if invocation_order[rspec_method_name].include?(last_message)
-
raise NoMethodError, "Undefined method #{rspec_method_name}"
-
end
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Mocks
-
1
module AnyInstance
-
# @private
-
1
class StubChainChain < StubChain
-
1
def initialize(*args)
-
super
-
@expectation_fulfilled = false
-
end
-
-
1
private
-
-
1
def create_message_expectation_on(instance)
-
::RSpec::Mocks::StubChain.stub_chain_on(instance, *@expectation_args, &@expectation_block)
-
end
-
-
1
def invocation_order
-
@invocation_order ||= {
-
:and_return => [nil],
-
:and_raise => [nil],
-
:and_yield => [nil]
-
}
-
end
-
end
-
end
-
end
-
end
-
# We intentionally do not use the `RSpec::Support.require...` methods
-
# here so that this file can be loaded individually, as documented
-
# below.
-
1
require 'rspec/mocks/argument_matchers'
-
1
require 'rspec/support/fuzzy_matcher'
-
-
1
module RSpec
-
1
module Mocks
-
# Wrapper for matching arguments against a list of expected values. Used by
-
# the `with` method on a `MessageExpectation`:
-
#
-
# expect(object).to receive(:message).with(:a, 'b', 3)
-
# object.message(:a, 'b', 3)
-
#
-
# Values passed to `with` can be literal values or argument matchers that
-
# match against the real objects .e.g.
-
#
-
# expect(object).to receive(:message).with(hash_including(:a => 'b'))
-
#
-
# Can also be used directly to match the contents of any `Array`. This
-
# enables 3rd party mocking libs to take advantage of rspec's argument
-
# matching without using the rest of rspec-mocks.
-
#
-
# require 'rspec/mocks/argument_list_matcher'
-
# include RSpec::Mocks::ArgumentMatchers
-
#
-
# arg_list_matcher = RSpec::Mocks::ArgumentListMatcher.new(123, hash_including(:a => 'b'))
-
# arg_list_matcher.args_match?(123, :a => 'b')
-
#
-
# This class is immutable.
-
#
-
# @see ArgumentMatchers
-
1
class ArgumentListMatcher
-
# @private
-
1
attr_reader :expected_args
-
-
# @api public
-
# @param [Array] expected_args a list of expected literals and/or argument matchers
-
#
-
# Initializes an `ArgumentListMatcher` with a collection of literal
-
# values and/or argument matchers.
-
#
-
# @see ArgumentMatchers
-
# @see #args_match?
-
1
def initialize(*expected_args)
-
1
@expected_args = expected_args
-
-
1
@matchers = case expected_args.first
-
1
when ArgumentMatchers::AnyArgsMatcher then Array
-
when ArgumentMatchers::NoArgsMatcher then []
-
else expected_args
-
end
-
end
-
-
# @api public
-
# @param [Array] args
-
#
-
# Matches each element in the `expected_args` against the element in the same
-
# position of the arguments passed to `new`.
-
#
-
# @see #initialize
-
1
def args_match?(*args)
-
2
Support::FuzzyMatcher.values_match?(@matchers, args)
-
end
-
-
# Value that will match all argument lists.
-
#
-
# @private
-
1
MATCH_ALL = new(ArgumentMatchers::AnyArgsMatcher.new)
-
end
-
end
-
end
-
# This cannot take advantage of our relative requires, since this file is a
-
# dependency of `rspec/mocks/argument_list_matcher.rb`. See comment there for
-
# details.
-
1
require 'rspec/support/matcher_definition'
-
-
1
module RSpec
-
1
module Mocks
-
# ArgumentMatchers are placeholders that you can include in message
-
# expectations to match arguments against a broader check than simple
-
# equality.
-
#
-
# With the exception of `any_args` and `no_args`, they all match against
-
# the arg in same position in the argument list.
-
#
-
# @see ArgumentListMatcher
-
1
module ArgumentMatchers
-
# Matches any args at all. Supports a more explicit variation of
-
# `expect(object).to receive(:message)`
-
#
-
# @example
-
#
-
# expect(object).to receive(:message).with(any_args)
-
1
def any_args
-
AnyArgsMatcher.new
-
end
-
-
# Matches any argument at all.
-
#
-
# @example
-
#
-
# expect(object).to receive(:message).with(anything)
-
1
def anything
-
AnyArgMatcher.new
-
end
-
-
# Matches no arguments.
-
#
-
# @example
-
#
-
# expect(object).to receive(:message).with(no_args)
-
1
def no_args
-
NoArgsMatcher.new
-
end
-
-
# Matches if the actual argument responds to the specified messages.
-
#
-
# @example
-
#
-
# expect(object).to receive(:message).with(duck_type(:hello))
-
# expect(object).to receive(:message).with(duck_type(:hello, :goodbye))
-
1
def duck_type(*args)
-
DuckTypeMatcher.new(*args)
-
end
-
-
# Matches a boolean value.
-
#
-
# @example
-
#
-
# expect(object).to receive(:message).with(boolean())
-
1
def boolean
-
BooleanMatcher.new
-
end
-
-
# Matches a hash that includes the specified key(s) or key/value pairs.
-
# Ignores any additional keys.
-
#
-
# @example
-
#
-
# expect(object).to receive(:message).with(hash_including(:key => val))
-
# expect(object).to receive(:message).with(hash_including(:key))
-
# expect(object).to receive(:message).with(hash_including(:key, :key2 => val2))
-
1
def hash_including(*args)
-
HashIncludingMatcher.new(ArgumentMatchers.anythingize_lonely_keys(*args))
-
end
-
-
# Matches an array that includes the specified items at least once.
-
# Ignores duplicates and additional values
-
#
-
# @example
-
#
-
# expect(object).to receive(:message).with(array_including(1,2,3))
-
# expect(object).to receive(:message).with(array_including([1,2,3]))
-
1
def array_including(*args)
-
actually_an_array = Array === args.first && args.count == 1 ? args.first : args
-
ArrayIncludingMatcher.new(actually_an_array)
-
end
-
-
# Matches a hash that doesn't include the specified key(s) or key/value.
-
#
-
# @example
-
#
-
# expect(object).to receive(:message).with(hash_excluding(:key => val))
-
# expect(object).to receive(:message).with(hash_excluding(:key))
-
# expect(object).to receive(:message).with(hash_excluding(:key, :key2 => :val2))
-
1
def hash_excluding(*args)
-
HashExcludingMatcher.new(ArgumentMatchers.anythingize_lonely_keys(*args))
-
end
-
-
1
alias_method :hash_not_including, :hash_excluding
-
-
# Matches if `arg.instance_of?(klass)`
-
#
-
# @example
-
#
-
# expect(object).to receive(:message).with(instance_of(Thing))
-
1
def instance_of(klass)
-
InstanceOf.new(klass)
-
end
-
-
1
alias_method :an_instance_of, :instance_of
-
-
# Matches if `arg.kind_of?(klass)`
-
# @example
-
#
-
# expect(object).to receive(:message).with(kind_of(Thing))
-
1
def kind_of(klass)
-
KindOf.new(klass)
-
end
-
-
1
alias_method :a_kind_of, :kind_of
-
-
# @private
-
1
def self.anythingize_lonely_keys(*args)
-
hash = args.last.class == Hash ? args.delete_at(-1) : {}
-
args.each { | arg | hash[arg] = AnyArgMatcher.new }
-
hash
-
end
-
-
# @private
-
1
class AnyArgsMatcher
-
1
def description
-
"any args"
-
end
-
end
-
-
# @private
-
1
class AnyArgMatcher
-
1
def ===(_other)
-
true
-
end
-
-
1
def description
-
"anything"
-
end
-
end
-
-
# @private
-
1
class NoArgsMatcher
-
1
def description
-
"no args"
-
end
-
end
-
-
# @private
-
1
class BooleanMatcher
-
1
def ===(value)
-
true == value || false == value
-
end
-
-
1
def description
-
"boolean"
-
end
-
end
-
-
# @private
-
1
class BaseHashMatcher
-
1
def initialize(expected)
-
@expected = expected
-
end
-
-
1
def ===(predicate, actual)
-
@expected.__send__(predicate) do |k, v|
-
actual.key?(k) && Support::FuzzyMatcher.values_match?(v, actual[k])
-
end
-
rescue NoMethodError
-
false
-
end
-
-
1
def description(name)
-
"#{name}(#{@expected.inspect.sub(/^\{/, "").sub(/\}$/, "")})"
-
end
-
end
-
-
# @private
-
1
class HashIncludingMatcher < BaseHashMatcher
-
1
def ===(actual)
-
super(:all?, actual)
-
end
-
-
1
def description
-
super("hash_including")
-
end
-
end
-
-
# @private
-
1
class HashExcludingMatcher < BaseHashMatcher
-
1
def ===(actual)
-
super(:none?, actual)
-
end
-
-
1
def description
-
super("hash_not_including")
-
end
-
end
-
-
# @private
-
1
class ArrayIncludingMatcher
-
1
def initialize(expected)
-
@expected = expected
-
end
-
-
1
def ===(actual)
-
Set.new(actual).superset?(Set.new(@expected))
-
end
-
-
1
def description
-
"array_including(#{@expected.join(", ")})"
-
end
-
end
-
-
# @private
-
1
class DuckTypeMatcher
-
1
def initialize(*methods_to_respond_to)
-
@methods_to_respond_to = methods_to_respond_to
-
end
-
-
1
def ===(value)
-
@methods_to_respond_to.all? { |message| value.respond_to?(message) }
-
end
-
-
1
def description
-
"duck_type(#{@methods_to_respond_to.map(&:inspect).join(', ')})"
-
end
-
end
-
-
# @private
-
1
class InstanceOf
-
1
def initialize(klass)
-
@klass = klass
-
end
-
-
1
def ===(actual)
-
actual.instance_of?(@klass)
-
end
-
-
1
def description
-
"an_instance_of(#{@klass.name})"
-
end
-
end
-
-
# @private
-
1
class KindOf
-
1
def initialize(klass)
-
@klass = klass
-
end
-
-
1
def ===(actual)
-
actual.kind_of?(@klass)
-
end
-
-
1
def description
-
"kind of #{@klass.name}"
-
end
-
end
-
-
1
matcher_namespace = name + '::'
-
1
::RSpec::Support.register_matcher_definition do |object|
-
# This is the best we have for now. We should tag all of our matchers
-
# with a module or something so we can test for it directly.
-
#
-
# (Note Module#parent in ActiveSupport is defined in a similar way.)
-
begin
-
object.class.name.include?(matcher_namespace)
-
rescue NoMethodError
-
# Some objects, like BasicObject, don't implemented standard
-
# reflection methods.
-
false
-
end
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Mocks
-
# Raised when a message expectation is not satisfied.
-
1
MockExpectationError = Class.new(Exception)
-
-
# Raised when a test double is used after it has been torn
-
# down (typically at the end of an rspec-core example).
-
1
ExpiredTestDoubleError = Class.new(MockExpectationError)
-
-
# Raised when doubles or partial doubles are used outside of the per-test lifecycle.
-
1
OutsideOfExampleError = Class.new(StandardError)
-
-
# @private
-
1
UnsupportedMatcherError = Class.new(StandardError)
-
# @private
-
1
NegationUnsupportedError = Class.new(StandardError)
-
# @private
-
1
VerifyingDoubleNotDefinedError = Class.new(StandardError)
-
-
# @private
-
1
class ErrorGenerator
-
1
attr_writer :opts
-
-
1
def initialize(target, name)
-
2
@target = target
-
2
@name = name
-
end
-
-
# @private
-
1
def opts
-
@opts ||= {}
-
end
-
-
# @private
-
1
def raise_unexpected_message_error(message, *args)
-
__raise "#{intro} received unexpected message :#{message}#{arg_message(*args)}"
-
end
-
-
# @private
-
1
def raise_unexpected_message_args_error(expectation, *args)
-
expected_args = format_args(*expectation.expected_args)
-
actual_args = format_received_args(*args)
-
__raise "#{intro} received #{expectation.message.inspect} with " \
-
"unexpected arguments\n expected: #{expected_args}\n" \
-
" got: #{actual_args}"
-
end
-
-
# @private
-
1
def raise_missing_default_stub_error(expectation, *args)
-
expected_args = format_args(*expectation.expected_args)
-
actual_args = format_received_args(*args)
-
__raise "#{intro} received #{expectation.message.inspect} with " \
-
"unexpected arguments\n expected: #{expected_args}\n" \
-
" got: #{actual_args}\n Please stub a default value " \
-
"first if message might be received with other args as well. \n"
-
end
-
-
# @private
-
1
def raise_similar_message_args_error(expectation, *args_for_multiple_calls)
-
expected_args = format_args(*expectation.expected_args)
-
actual_args = args_for_multiple_calls.map { |a| format_received_args(*a) }.join(", ")
-
__raise "#{intro} received #{expectation.message.inspect} with " \
-
"unexpected arguments\n expected: #{expected_args}\n" \
-
" got: #{actual_args}"
-
end
-
-
# rubocop:disable Style/ParameterLists
-
# @private
-
1
def raise_expectation_error(message, expected_received_count, argument_list_matcher, actual_received_count, expectation_count_type, *args)
-
expected_part = expected_part_of_expectation_error(expected_received_count, expectation_count_type, argument_list_matcher)
-
received_part = received_part_of_expectation_error(actual_received_count, *args)
-
__raise "(#{intro}).#{message}#{format_args(*args)}\n #{expected_part}\n #{received_part}"
-
end
-
# rubocop:enable Style/ParameterLists
-
-
# @private
-
1
def raise_unimplemented_error(doubled_module, method_name)
-
__raise "%s does not implement: %s" % [
-
doubled_module.description,
-
method_name
-
]
-
end
-
-
# @private
-
1
def raise_non_public_error(method_name, visibility)
-
raise NoMethodError, "%s method `%s' called on %s" % [
-
visibility, method_name, intro
-
]
-
end
-
-
# @private
-
1
def raise_invalid_arguments_error(verifier)
-
__raise verifier.error_message
-
end
-
-
# @private
-
1
def raise_expired_test_double_error
-
raise ExpiredTestDoubleError,
-
"#{intro} was originally created in one example but has leaked into " \
-
"another example and can no longer be used. rspec-mocks' doubles are " \
-
"designed to only last for one example, and you need to create a new " \
-
"one in each example you wish to use it for."
-
end
-
-
# @private
-
1
def received_part_of_expectation_error(actual_received_count, *args)
-
"received: #{count_message(actual_received_count)}" +
-
actual_method_call_args_description(actual_received_count, args)
-
end
-
-
# @private
-
1
def expected_part_of_expectation_error(expected_received_count, expectation_count_type, argument_list_matcher)
-
"expected: #{count_message(expected_received_count, expectation_count_type)}" +
-
expected_method_call_args_description(argument_list_matcher.expected_args)
-
end
-
-
# @private
-
1
def actual_method_call_args_description(count, args)
-
method_call_args_description(args) ||
-
if count > 0 && args.length > 0
-
" with arguments: #{args.inspect.gsub(/\A\[(.+)\]\z/, '(\1)')}"
-
else
-
""
-
end
-
end
-
-
# @private
-
1
def expected_method_call_args_description(args)
-
method_call_args_description(args) ||
-
if args.length > 0
-
" with arguments: #{format_args(*args)}"
-
else
-
""
-
end
-
end
-
-
# @private
-
1
def method_call_args_description(args)
-
case args.first
-
when ArgumentMatchers::AnyArgsMatcher then " with any arguments"
-
when ArgumentMatchers::NoArgsMatcher then " with no arguments"
-
end
-
end
-
-
# @private
-
1
def describe_expectation(message, expected_received_count, _actual_received_count, *args)
-
"have received #{message}#{format_args(*args)} #{count_message(expected_received_count)}"
-
end
-
-
# @private
-
1
def raise_out_of_order_error(message)
-
__raise "#{intro} received :#{message} out of order"
-
end
-
-
# @private
-
1
def raise_block_failed_error(message, detail)
-
__raise "#{intro} received :#{message} but passed block failed with: #{detail}"
-
end
-
-
# @private
-
1
def raise_missing_block_error(args_to_yield)
-
__raise "#{intro} asked to yield |#{arg_list(*args_to_yield)}| but no block was passed"
-
end
-
-
# @private
-
1
def raise_wrong_arity_error(args_to_yield, signature)
-
__raise "#{intro} yielded |#{arg_list(*args_to_yield)}| to block with #{signature.description}"
-
end
-
-
# @private
-
1
def raise_only_valid_on_a_partial_double(method)
-
__raise "#{intro} is a pure test double. `#{method}` is only " \
-
"available on a partial double."
-
end
-
-
# @private
-
1
def raise_expectation_on_unstubbed_method(method)
-
__raise "#{intro} expected to have received #{method}, but that " \
-
"object is not a spy or method has not been stubbed."
-
end
-
-
# @private
-
1
def raise_expectation_on_mocked_method(method)
-
__raise "#{intro} expected to have received #{method}, but that " \
-
"method has been mocked instead of stubbed or spied."
-
end
-
-
1
def self.raise_double_negation_error(wrapped_expression)
-
raise "Isn't life confusing enough? You've already set a " \
-
"negative message expectation and now you are trying to " \
-
"negate it again with `never`. What does an expression like " \
-
"`#{wrapped_expression}.not_to receive(:msg).never` even mean?"
-
end
-
-
1
private
-
-
1
def intro
-
if @name
-
"Double #{@name.inspect}"
-
elsif TestDouble === @target
-
"Double"
-
elsif Class === @target
-
"<#{@target.inspect} (class)>"
-
elsif @target
-
@target
-
else
-
"nil"
-
end
-
end
-
-
1
def __raise(message)
-
message = opts[:message] unless opts[:message].nil?
-
Kernel.raise(RSpec::Mocks::MockExpectationError, message)
-
end
-
-
1
def arg_message(*args)
-
" with " + format_args(*args)
-
end
-
-
1
def format_args(*args)
-
args.empty? ? "(no args)" : "(" + arg_list(*args) + ")"
-
end
-
-
1
def arg_list(*args)
-
args.map { |arg| arg_has_valid_description(arg) ? arg.description : arg.inspect }.join(", ")
-
end
-
-
1
def arg_has_valid_description(arg)
-
return false unless arg.respond_to?(:description)
-
-
!arg.description.nil? && !arg.description.empty?
-
end
-
-
1
def format_received_args(*args)
-
args.empty? ? "(no args)" : "(" + received_arg_list(*args) + ")"
-
end
-
-
1
def received_arg_list(*args)
-
args.map(&:inspect).join(", ")
-
end
-
-
1
def count_message(count, expectation_count_type=nil)
-
return "at least #{times(count.abs)}" if count < 0 || expectation_count_type == :at_least
-
return "at most #{times(count)}" if expectation_count_type == :at_most
-
times(count)
-
end
-
-
1
def times(count)
-
"#{count} time#{count == 1 ? '' : 's'}"
-
end
-
end
-
end
-
end
-
1
RSpec::Support.require_rspec_mocks 'object_reference'
-
-
1
module RSpec
-
1
module Mocks
-
# Contains methods intended to be used from within code examples.
-
# Mix this in to your test context (such as a test framework base class)
-
# to use rspec-mocks with your test framework. If you're using rspec-core,
-
# it'll take care of doing this for you.
-
1
module ExampleMethods
-
1
include RSpec::Mocks::ArgumentMatchers
-
-
# @overload double()
-
# @overload double(name)
-
# @param name [String/Symbol] used to clarify intent
-
# @overload double(stubs)
-
# @param stubs (Hash) hash of message/return-value pairs
-
# @overload double(name, stubs)
-
# @param name [String/Symbol] used to clarify intent
-
# @param stubs (Hash) hash of message/return-value pairs
-
# @return (Double)
-
#
-
# Constructs an instance of [RSpec::Mocks::Double](RSpec::Mocks::Double) configured
-
# with an optional name, used for reporting in failure messages, and an optional
-
# hash of message/return-value pairs.
-
#
-
# @example
-
#
-
# book = double("book", :title => "The RSpec Book")
-
# book.title #=> "The RSpec Book"
-
#
-
# card = double("card", :suit => "Spades", :rank => "A")
-
# card.suit #=> "Spades"
-
# card.rank #=> "A"
-
#
-
1
def double(*args)
-
ExampleMethods.declare_double(Double, *args)
-
end
-
-
# @overload instance_double(doubled_class)
-
# @param doubled_class [String, Class]
-
# @overload instance_double(doubled_class, stubs)
-
# @param doubled_class [String, Class]
-
# @param stubs [Hash] hash of message/return-value pairs
-
# @return InstanceVerifyingDouble
-
#
-
# Constructs a test double against a specific class. If the given class
-
# name has been loaded, only instance methods defined on the class are
-
# allowed to be stubbed. In all other ways it behaves like a
-
# [double](double).
-
1
def instance_double(doubled_class, *args)
-
ref = ObjectReference.for(doubled_class)
-
ExampleMethods.declare_verifying_double(InstanceVerifyingDouble, ref, *args)
-
end
-
-
# @overload class_double(doubled_class)
-
# @param doubled_class [String, Module]
-
# @overload class_double(doubled_class, stubs)
-
# @param doubled_class [String, Module]
-
# @param stubs [Hash] hash of message/return-value pairs
-
# @return ClassVerifyingDouble
-
#
-
# Constructs a test double against a specific class. If the given class
-
# name has been loaded, only class methods defined on the class are
-
# allowed to be stubbed. In all other ways it behaves like a
-
# [double](double).
-
1
def class_double(doubled_class, *args)
-
ref = ObjectReference.for(doubled_class)
-
ExampleMethods.declare_verifying_double(ClassVerifyingDouble, ref, *args)
-
end
-
-
# @overload object_double(object_or_name)
-
# @param object_or_name [String, Object]
-
# @overload object_double(object_or_name, stubs)
-
# @param object_or_name [String, Object]
-
# @param stubs [Hash] hash of message/return-value pairs
-
# @return ObjectVerifyingDouble
-
#
-
# Constructs a test double against a specific object. Only the methods
-
# the object responds to are allowed to be stubbed. If a String argument
-
# is provided, it is assumed to reference a constant object which is used
-
# for verification. In all other ways it behaves like a [double](double).
-
1
def object_double(object_or_name, *args)
-
ref = ObjectReference.for(object_or_name, :allow_direct_object_refs)
-
ExampleMethods.declare_verifying_double(ObjectVerifyingDouble, ref, *args)
-
end
-
-
# @overload spy()
-
# @overload spy(name)
-
# @param name [String/Symbol] used to clarify intent
-
# @overload spy(stubs)
-
# @param stubs (Hash) hash of message/return-value pairs
-
# @overload spy(name, stubs)
-
# @param name [String/Symbol] used to clarify intent
-
# @param stubs (Hash) hash of message/return-value pairs
-
# @return (Double)
-
#
-
# Constructs a test double that is optimized for use with
-
# `have_received`. With a normal double one has to stub methods in order
-
# to be able to spy them. A spy automatically spies on all methods.
-
1
def spy(*args)
-
double(*args).as_null_object
-
end
-
-
# @overload instance_spy(doubled_class)
-
# @param doubled_class [String, Class]
-
# @overload instance_spy(doubled_class, stubs)
-
# @param doubled_class [String, Class]
-
# @param stubs [Hash] hash of message/return-value pairs
-
# @return InstanceVerifyingDouble
-
#
-
# Constructs a test double that is optimized for use with `have_received`
-
# against a specific class. If the given class name has been loaded, only
-
# instance methods defined on the class are allowed to be stubbed. With
-
# a normal double one has to stub methods in order to be able to spy
-
# them. An instance_spy automatically spies on all instance methods to
-
# which the class responds.
-
1
def instance_spy(*args)
-
instance_double(*args).as_null_object
-
end
-
-
# @overload object_spy(object_or_name)
-
# @param object_or_name [String, Object]
-
# @overload object_spy(object_or_name, stubs)
-
# @param object_or_name [String, Object]
-
# @param stubs [Hash] hash of message/return-value pairs
-
# @return ObjectVerifyingDouble
-
#
-
# Constructs a test double that is optimized for use with `have_received`
-
# against a specific object. Only instance methods defined on the object
-
# are allowed to be stubbed. With a normal double one has to stub
-
# methods in order to be able to spy them. An object_spy automatically
-
# spies on all methods to which the object responds.
-
1
def object_spy(*args)
-
object_double(*args).as_null_object
-
end
-
-
# @overload class_spy(doubled_class)
-
# @param doubled_class [String, Module]
-
# @overload class_spy(doubled_class, stubs)
-
# @param doubled_class [String, Module]
-
# @param stubs [Hash] hash of message/return-value pairs
-
# @return ClassVerifyingDouble
-
#
-
# Constructs a test double that is optimized for use with `have_received`
-
# against a specific class. If the given class name has been loaded,
-
# only class methods defined on the class are allowed to be stubbed.
-
# With a normal double one has to stub methods in order to be able to spy
-
# them. An class_spy automatically spies on all class methods to which the
-
# class responds.
-
1
def class_spy(*args)
-
class_double(*args).as_null_object
-
end
-
-
# Disables warning messages about expectations being set on nil.
-
#
-
# By default warning messages are issued when expectations are set on
-
# nil. This is to prevent false-positives and to catch potential bugs
-
# early on.
-
1
def allow_message_expectations_on_nil
-
RSpec::Mocks.space.proxy_for(nil).warn_about_expectations = false
-
end
-
-
# Stubs the named constant with the given value.
-
# Like method stubs, the constant will be restored
-
# to its original value (or lack of one, if it was
-
# undefined) when the example completes.
-
#
-
# @param constant_name [String] The fully qualified name of the constant. The current
-
# constant scoping at the point of call is not considered.
-
# @param value [Object] The value to make the constant refer to. When the
-
# example completes, the constant will be restored to its prior state.
-
# @param options [Hash] Stubbing options.
-
# @option options :transfer_nested_constants [Boolean, Array<Symbol>] Determines
-
# what nested constants, if any, will be transferred from the original value
-
# of the constant to the new value of the constant. This only works if both
-
# the original and new values are modules (or classes).
-
# @return [Object] the stubbed value of the constant
-
#
-
# @example
-
#
-
# stub_const("MyClass", Class.new) # => Replaces (or defines) MyClass with a new class object.
-
# stub_const("SomeModel::PER_PAGE", 5) # => Sets SomeModel::PER_PAGE to 5.
-
#
-
# class CardDeck
-
# SUITS = [:Spades, :Diamonds, :Clubs, :Hearts]
-
# NUM_CARDS = 52
-
# end
-
#
-
# stub_const("CardDeck", Class.new)
-
# CardDeck::SUITS # => uninitialized constant error
-
# CardDeck::NUM_CARDS # => uninitialized constant error
-
#
-
# stub_const("CardDeck", Class.new, :transfer_nested_constants => true)
-
# CardDeck::SUITS # => our suits array
-
# CardDeck::NUM_CARDS # => 52
-
#
-
# stub_const("CardDeck", Class.new, :transfer_nested_constants => [:SUITS])
-
# CardDeck::SUITS # => our suits array
-
# CardDeck::NUM_CARDS # => uninitialized constant error
-
1
def stub_const(constant_name, value, options={})
-
ConstantMutator.stub(constant_name, value, options)
-
end
-
-
# Hides the named constant with the given value. The constant will be
-
# undefined for the duration of the test.
-
#
-
# Like method stubs, the constant will be restored to its original value
-
# when the example completes.
-
#
-
# @param constant_name [String] The fully qualified name of the constant.
-
# The current constant scoping at the point of call is not considered.
-
#
-
# @example
-
#
-
# hide_const("MyClass") # => MyClass is now an undefined constant
-
1
def hide_const(constant_name)
-
ConstantMutator.hide(constant_name)
-
end
-
-
# Verifies that the given object received the expected message during the
-
# course of the test. On a spy objects or as null object doubles this
-
# works for any method, on other objects the method must have
-
# been stubbed beforehand in order for messages to be verified.
-
#
-
# Stubbing and verifying messages received in this way implements the
-
# Test Spy pattern.
-
#
-
# @param method_name [Symbol] name of the method expected to have been
-
# called.
-
#
-
# @example
-
#
-
# invitation = double('invitation', accept: true)
-
# user.accept_invitation(invitation)
-
# expect(invitation).to have_received(:accept)
-
#
-
# # You can also use most message expectations:
-
# expect(invitation).to have_received(:accept).with(mailer).once
-
1
def have_received(method_name, &block)
-
Matchers::HaveReceived.new(method_name, &block)
-
end
-
-
# @method expect
-
# Used to wrap an object in preparation for setting a mock expectation
-
# on it.
-
#
-
# @example
-
#
-
# expect(obj).to receive(:foo).with(5).and_return(:return_value)
-
#
-
# @note This method is usually provided by rspec-expectations. However,
-
# if you use rspec-mocks without rspec-expectations, there's a definition
-
# of it that is made available here. If you disable the `:expect` syntax
-
# this method will be undefined.
-
-
# @method allow
-
# Used to wrap an object in preparation for stubbing a method
-
# on it.
-
#
-
# @example
-
#
-
# allow(dbl).to receive(:foo).with(5).and_return(:return_value)
-
#
-
# @note If you disable the `:expect` syntax this method will be undefined.
-
-
# @method expect_any_instance_of
-
# Used to wrap a class in preparation for setting a mock expectation
-
# on instances of it.
-
#
-
# @example
-
#
-
# expect_any_instance_of(MyClass).to receive(:foo)
-
#
-
# @note If you disable the `:expect` syntax this method will be undefined.
-
-
# @method allow_any_instance_of
-
# Used to wrap a class in preparation for stubbing a method
-
# on instances of it.
-
#
-
# @example
-
#
-
# allow_any_instance_of(MyClass).to receive(:foo)
-
#
-
# @note This is only available when you have enabled the `expect` syntax.
-
-
# @method receive
-
# Used to specify a message that you expect or allow an object
-
# to receive. The object returned by `receive` supports the same
-
# fluent interface that `should_receive` and `stub` have always
-
# supported, allowing you to constrain the arguments or number of
-
# times, and configure how the object should respond to the message.
-
#
-
# @example
-
#
-
# expect(obj).to receive(:hello).with("world").exactly(3).times
-
#
-
# @note If you disable the `:expect` syntax this method will be undefined.
-
-
# @method receive_messages
-
# Shorthand syntax used to setup message(s), and their return value(s),
-
# that you expect or allow an object to receive. The method takes a hash
-
# of messages and their respective return values. Unlike with `receive`,
-
# you cannot apply further customizations using a block or the fluent
-
# interface.
-
#
-
# @example
-
#
-
# allow(obj).to receive_messages(:speak => "Hello World")
-
# allow(obj).to receive_messages(:speak => "Hello", :meow => "Meow")
-
#
-
# @note If you disable the `:expect` syntax this method will be undefined.
-
-
# @method receive_message_chain
-
# @overload receive_message_chain(method1, method2)
-
# @overload receive_message_chain("method1.method2")
-
# @overload receive_message_chain(method1, method_to_value_hash)
-
#
-
# stubs/mocks a chain of messages on an object or test double.
-
#
-
# ## Warning:
-
#
-
# Chains can be arbitrarily long, which makes it quite painless to
-
# violate the Law of Demeter in violent ways, so you should consider any
-
# use of `receive_message_chain` a code smell. Even though not all code smells
-
# indicate real problems (think fluent interfaces), `receive_message_chain` still
-
# results in brittle examples. For example, if you write
-
# `allow(foo).to receive_message_chain(:bar, :baz => 37)` in a spec and then the
-
# implementation calls `foo.baz.bar`, the stub will not work.
-
#
-
# @example
-
#
-
# allow(double).to receive_message_chain("foo.bar") { :baz }
-
# allow(double).to receive_message_chain(:foo, :bar => :baz)
-
# allow(double).to receive_message_chain(:foo, :bar) { :baz }
-
#
-
# # Given any of ^^ these three forms ^^:
-
# double.foo.bar # => :baz
-
#
-
# # Common use in Rails/ActiveRecord:
-
# allow(Article).to receive_message_chain("recent.published") { [Article.new] }
-
#
-
# @note If you disable the `:expect` syntax this method will be undefined.
-
-
# @private
-
1
def self.included(klass)
-
1
klass.class_exec do
-
# This gets mixed in so that if `RSpec::Matchers` is included in
-
# `klass` later, it's definition of `expect` will take precedence.
-
1
include ExpectHost unless method_defined?(:expect)
-
end
-
end
-
-
# @private
-
1
def self.declare_verifying_double(type, ref, *args)
-
if RSpec::Mocks.configuration.verify_doubled_constant_names? &&
-
!ref.defined?
-
-
raise VerifyingDoubleNotDefinedError,
-
"#{ref.description} is not a defined constant. " \
-
"Perhaps you misspelt it? " \
-
"Disable check with verify_doubled_constant_names configuration option."
-
end
-
-
declare_double(type, ref, *args)
-
end
-
-
# @private
-
1
def self.declare_double(type, *args)
-
args << {} unless Hash === args.last
-
type.new(*args)
-
end
-
-
# This module exists to host the `expect` method for cases where
-
# rspec-mocks is used w/o rspec-expectations.
-
1
module ExpectHost
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Mocks
-
# @private
-
1
class InstanceMethodStasher
-
1
def initialize(object, method)
-
2
@object = object
-
2
@method = method
-
4
@klass = (class << object; self; end)
-
-
2
@original_method = nil
-
2
@method_is_stashed = false
-
end
-
-
1
attr_reader :original_method
-
-
1
if RUBY_VERSION.to_f < 1.9
-
# @private
-
def method_is_stashed?
-
@method_is_stashed
-
end
-
-
# @private
-
def stash
-
return if !method_defined_directly_on_klass? || @method_is_stashed
-
-
@klass.__send__(:alias_method, stashed_method_name, @method)
-
@method_is_stashed = true
-
end
-
-
# @private
-
def stashed_method_name
-
"obfuscated_by_rspec_mocks__#{@method}"
-
end
-
private :stashed_method_name
-
-
# @private
-
def restore
-
return unless @method_is_stashed
-
-
if @klass.__send__(:method_defined?, @method)
-
@klass.__send__(:undef_method, @method)
-
end
-
@klass.__send__(:alias_method, @method, stashed_method_name)
-
@klass.__send__(:remove_method, stashed_method_name)
-
@method_is_stashed = false
-
end
-
else
-
-
# @private
-
1
def method_is_stashed?
-
2
!!@original_method
-
end
-
-
# @private
-
1
def stash
-
2
return unless method_defined_directly_on_klass?
-
@original_method ||= ::RSpec::Support.method_handle_for(@object, @method)
-
end
-
-
# @private
-
1
def restore
-
return unless @original_method
-
-
if @klass.__send__(:method_defined?, @method)
-
@klass.__send__(:undef_method, @method)
-
end
-
-
handle_restoration_failures do
-
@klass.__send__(:define_method, @method, @original_method)
-
end
-
-
@original_method = nil
-
end
-
end
-
-
1
if RUBY_DESCRIPTION.include?('2.0.0p247') || RUBY_DESCRIPTION.include?('2.0.0p195')
-
# ruby 2.0.0-p247 and 2.0.0-p195 both have a bug that we can't work around :(.
-
# https://bugs.ruby-lang.org/issues/8686
-
def handle_restoration_failures
-
yield
-
rescue TypeError
-
RSpec.warn_with(
-
"RSpec failed to properly restore a partial double (#{@object.inspect}) " \
-
"to its original state due to a known bug in MRI 2.0.0-p195 & p247 " \
-
"(https://bugs.ruby-lang.org/issues/8686). This object may remain " \
-
"screwed up for the rest of this process. Please upgrade to 2.0.0-p353 or above.",
-
:call_site => nil, :use_spec_location_as_call_site => true
-
)
-
end
-
else
-
1
def handle_restoration_failures
-
# No known reasons for restoration to fail on other rubies.
-
yield
-
end
-
end
-
-
1
private
-
-
# @private
-
1
def method_defined_directly_on_klass?
-
2
method_defined_on_klass? && method_owned_by_klass?
-
end
-
-
# @private
-
1
def method_defined_on_klass?(klass=@klass)
-
4
MethodReference.method_defined_at_any_visibility?(klass, @method)
-
end
-
-
1
def method_owned_by_klass?
-
2
owner = @klass.instance_method(@method).owner
-
-
# On Ruby 2.0.0+ the owner of a method on a class which has been
-
# `prepend`ed may actually be an instance, e.g.
-
# `#<MyClass:0x007fbb94e3cd10>`, rather than the expected `MyClass`.
-
2
owner = owner.class unless Module === owner
-
-
# On some 1.9s (e.g. rubinius) aliased methods
-
# can report the wrong owner. Example:
-
# class MyClass
-
# class << self
-
# alias alternate_new new
-
# end
-
# end
-
#
-
# MyClass.owner(:alternate_new) returns `Class` when incorrect,
-
# but we need to consider the owner to be `MyClass` because
-
# it is not actually available on `Class` but is on `MyClass`.
-
# Hence, we verify that the owner actually has the method defined.
-
# If the given owner does not have the method defined, we assume
-
# that the method is actually owned by @klass.
-
2
owner == @klass || !(method_defined_on_klass?(owner))
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Mocks
-
1
module Matchers
-
# @private
-
1
class ExpectationCustomization
-
1
attr_accessor :block
-
-
1
def initialize(method_name, args, block)
-
2
@method_name = method_name
-
2
@args = args
-
2
@block = block
-
end
-
-
1
def playback_onto(expectation)
-
2
expectation.__send__(@method_name, *@args, &@block)
-
end
-
end
-
end
-
end
-
end
-
1
RSpec::Support.require_rspec_mocks 'matchers/expectation_customization'
-
-
1
module RSpec
-
1
module Mocks
-
1
module Matchers
-
# @private
-
1
class Receive
-
1
def initialize(message, block)
-
2
@message = message
-
2
@block = block
-
2
@recorded_customizations = []
-
end
-
-
1
def name
-
"receive"
-
end
-
-
1
def setup_expectation(subject, &block)
-
warn_if_any_instance("expect", subject)
-
setup_mock_proxy_method_substitute(subject, :add_message_expectation, block)
-
end
-
1
alias matches? setup_expectation
-
-
1
def setup_negative_expectation(subject, &block)
-
# ensure `never` goes first for cases like `never.and_return(5)`,
-
# where `and_return` is meant to raise an error
-
@recorded_customizations.unshift ExpectationCustomization.new(:never, [], nil)
-
-
warn_if_any_instance("expect", subject)
-
-
setup_expectation(subject, &block)
-
end
-
1
alias does_not_match? setup_negative_expectation
-
-
1
def setup_allowance(subject, &block)
-
warn_if_any_instance("allow", subject)
-
setup_mock_proxy_method_substitute(subject, :add_stub, block)
-
end
-
-
1
def setup_any_instance_expectation(subject, &block)
-
setup_any_instance_method_substitute(subject, :should_receive, block)
-
end
-
-
1
def setup_any_instance_negative_expectation(subject, &block)
-
setup_any_instance_method_substitute(subject, :should_not_receive, block)
-
end
-
-
1
def setup_any_instance_allowance(subject, &block)
-
2
setup_any_instance_method_substitute(subject, :stub, block)
-
end
-
-
1
MessageExpectation.public_instance_methods(false).each do |method|
-
47
next if method_defined?(method)
-
-
46
define_method(method) do |*args, &block|
-
2
@recorded_customizations << ExpectationCustomization.new(method, args, block)
-
2
self
-
end
-
end
-
-
1
private
-
-
1
def warn_if_any_instance(expression, subject)
-
return unless AnyInstance::Proxy === subject
-
-
RSpec.warning(
-
"`#{expression}(#{subject.klass}.any_instance).to` " \
-
"is probably not what you meant, it does not operate on " \
-
"any instance of `#{subject.klass}`. " \
-
"Use `#{expression}_any_instance_of(#{subject.klass}).to` instead."
-
)
-
end
-
-
1
def setup_mock_proxy_method_substitute(subject, method, block)
-
proxy = ::RSpec::Mocks.space.proxy_for(subject)
-
setup_method_substitute(proxy, method, block)
-
end
-
-
1
def setup_any_instance_method_substitute(subject, method, block)
-
2
proxy = ::RSpec::Mocks.space.any_instance_proxy_for(subject)
-
2
setup_method_substitute(proxy, method, block)
-
end
-
-
1
def setup_method_substitute(host, method, block, *args)
-
2
args << @message.to_sym
-
2
block = move_block_to_last_customization(block)
-
-
2
expectation = host.__send__(method, *args, &(@block || block))
-
-
2
@recorded_customizations.each do |customization|
-
2
customization.playback_onto(expectation)
-
end
-
2
expectation
-
end
-
-
1
def move_block_to_last_customization(block)
-
2
last = @recorded_customizations.last
-
2
return block unless last
-
-
2
last.block ||= block
-
nil
-
end
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Mocks
-
# A message expectation that only allows concrete return values to be set
-
# for a message. While this same effect can be achieved using a standard
-
# MessageExpecation, this version is much faster and so can be used as an
-
# optimization.
-
#
-
# @private
-
1
class SimpleMessageExpectation
-
1
def initialize(message, response, error_generator, backtrace_line=nil)
-
@message, @response, @error_generator, @backtrace_line = message.to_sym, response, error_generator, backtrace_line
-
@received = false
-
end
-
-
1
def invoke(*_)
-
@received = true
-
@response
-
end
-
-
1
def matches?(message, *_)
-
@message == message.to_sym
-
end
-
-
1
def called_max_times?
-
false
-
end
-
-
1
def verify_messages_received
-
InsertOntoBacktrace.line(@backtrace_line) do
-
unless @received
-
@error_generator.raise_expectation_error(@message, 1, ArgumentListMatcher::MATCH_ALL, 0, nil)
-
end
-
end
-
end
-
end
-
-
# @private
-
1
class MessageExpectation
-
# @private
-
1
attr_accessor :error_generator, :implementation
-
1
attr_reader :message
-
1
attr_reader :orig_object
-
1
attr_writer :expected_received_count, :expected_from, :argument_list_matcher
-
1
protected :expected_received_count=, :expected_from=, :error_generator, :error_generator=, :implementation=
-
-
# rubocop:disable Style/ParameterLists
-
# @private
-
1
def initialize(error_generator, expectation_ordering, expected_from, method_double,
-
type=:expectation, opts={}, &implementation_block)
-
2
@error_generator = error_generator
-
2
@error_generator.opts = opts
-
2
@expected_from = expected_from
-
2
@method_double = method_double
-
2
@orig_object = @method_double.object
-
2
@message = @method_double.method_name
-
2
@actual_received_count = 0
-
2
@expected_received_count = type == :expectation ? 1 : :any
-
2
@argument_list_matcher = ArgumentListMatcher::MATCH_ALL
-
2
@order_group = expectation_ordering
-
2
@order_group.register(self) unless type == :stub
-
2
@expectation_type = type
-
2
@ordered = false
-
2
@at_least = @at_most = @exactly = nil
-
2
@args_to_yield = []
-
2
@failed_fast = nil
-
2
@eval_context = nil
-
2
@yield_receiver_to_implementation_block = false
-
-
2
@implementation = Implementation.new
-
2
self.inner_implementation_action = implementation_block
-
end
-
# rubocop:enable Style/ParameterLists
-
-
# @private
-
1
def expected_args
-
@argument_list_matcher.expected_args
-
end
-
-
# @overload and_return(value)
-
# @overload and_return(first_value, second_value)
-
#
-
# Tells the object to return a value when it receives the message. Given
-
# more than one value, the first value is returned the first time the
-
# message is received, the second value is returned the next time, etc,
-
# etc.
-
#
-
# If the message is received more times than there are values, the last
-
# value is received for every subsequent call.
-
#
-
# @example
-
#
-
# allow(counter).to receive(:count).and_return(1)
-
# counter.count # => 1
-
# counter.count # => 1
-
#
-
# allow(counter).to receive(:count).and_return(1,2,3)
-
# counter.count # => 1
-
# counter.count # => 2
-
# counter.count # => 3
-
# counter.count # => 3
-
# counter.count # => 3
-
# # etc
-
1
def and_return(first_value, *values)
-
2
if negative?
-
raise "`and_return` is not supported with negative message expectations"
-
end
-
-
2
if block_given?
-
raise ArgumentError, "Implementation blocks aren't supported with `and_return`"
-
end
-
-
2
values.unshift(first_value)
-
2
@expected_received_count = [@expected_received_count, values.size].max unless ignoring_args? || (@expected_received_count == 0 && @at_least)
-
2
self.terminal_implementation_action = AndReturnImplementation.new(values)
-
-
nil
-
end
-
-
1
def and_yield_receiver_to_implementation
-
2
@yield_receiver_to_implementation_block = true
-
2
self
-
end
-
-
1
def yield_receiver_to_implementation_block?
-
2
@yield_receiver_to_implementation_block
-
end
-
-
# Tells the object to delegate to the original unmodified method
-
# when it receives the message.
-
#
-
# @note This is only available on partial doubles.
-
#
-
# @example
-
#
-
# expect(counter).to receive(:increment).and_call_original
-
# original_count = counter.count
-
# counter.increment
-
# expect(counter.count).to eq(original_count + 1)
-
1
def and_call_original
-
and_wrap_original do |original, *args, &block|
-
original.call(*args, &block)
-
end
-
end
-
-
# Decorates the stubbed method with the supplied block. The original
-
# unmodified method is passed to the block along with any method call
-
# arguments so you can delegate to it, whilst still being able to
-
# change what args are passed to it and/or change the return value.
-
#
-
# @note This is only available on partial doubles.
-
#
-
# @example
-
#
-
# expect(api).to receive(:large_list).and_wrap_original do |original_method, *args, &block|
-
# original_method.call(*args, &block).first(10)
-
# end
-
#
-
1
def and_wrap_original(&block)
-
if RSpec::Mocks::TestDouble === @method_double.object
-
@error_generator.raise_only_valid_on_a_partial_double(:and_call_original)
-
else
-
warn_about_stub_override if implementation.inner_action
-
@implementation = AndWrapOriginalImplementation.new(@method_double.original_method, block)
-
@yield_receiver_to_implementation_block = false
-
end
-
end
-
-
# @overload and_raise
-
# @overload and_raise(ExceptionClass)
-
# @overload and_raise(ExceptionClass, message)
-
# @overload and_raise(exception_instance)
-
#
-
# Tells the object to raise an exception when the message is received.
-
#
-
# @note
-
#
-
# When you pass an exception class, the MessageExpectation will raise
-
# an instance of it, creating it with `exception` and passing `message`
-
# if specified. If the exception class initializer requires more than
-
# one parameters, you must pass in an instance and not the class,
-
# otherwise this method will raise an ArgumentError exception.
-
#
-
# @example
-
#
-
# allow(car).to receive(:go).and_raise
-
# allow(car).to receive(:go).and_raise(OutOfGas)
-
# allow(car).to receive(:go).and_raise(OutOfGas, "At least 2 oz of gas needed to drive")
-
# allow(car).to receive(:go).and_raise(OutOfGas.new(2, :oz))
-
1
def and_raise(exception=RuntimeError, message=nil)
-
if exception.respond_to?(:exception)
-
exception = message ? exception.exception(message) : exception.exception
-
end
-
-
self.terminal_implementation_action = Proc.new { raise exception }
-
nil
-
end
-
-
# @overload and_throw(symbol)
-
# @overload and_throw(symbol, object)
-
#
-
# Tells the object to throw a symbol (with the object if that form is
-
# used) when the message is received.
-
#
-
# @example
-
#
-
# allow(car).to receive(:go).and_throw(:out_of_gas)
-
# allow(car).to receive(:go).and_throw(:out_of_gas, :level => 0.1)
-
1
def and_throw(*args)
-
self.terminal_implementation_action = Proc.new { throw(*args) }
-
nil
-
end
-
-
# Tells the object to yield one or more args to a block when the message
-
# is received.
-
#
-
# @example
-
#
-
# stream.stub(:open).and_yield(StringIO.new)
-
1
def and_yield(*args, &block)
-
yield @eval_context = Object.new if block
-
@args_to_yield << args
-
self.initial_implementation_action = AndYieldImplementation.new(@args_to_yield, @eval_context, @error_generator)
-
self
-
end
-
-
# @private
-
1
def matches?(message, *args)
-
2
@message == message && @argument_list_matcher.args_match?(*args)
-
end
-
-
# @private
-
1
def invoke(parent_stub, *args, &block)
-
2
invoke_incrementing_actual_calls_by(1, parent_stub, *args, &block)
-
end
-
-
# @private
-
1
def invoke_without_incrementing_received_count(parent_stub, *args, &block)
-
invoke_incrementing_actual_calls_by(0, parent_stub, *args, &block)
-
end
-
-
# @private
-
1
def negative?
-
4
@expected_received_count == 0 && !@at_least
-
end
-
-
# @private
-
1
def called_max_times?
-
@expected_received_count != :any &&
-
!@at_least &&
-
@expected_received_count > 0 &&
-
@actual_received_count >= @expected_received_count
-
end
-
-
# @private
-
1
def matches_name_but_not_args(message, *args)
-
@message == message && !@argument_list_matcher.args_match?(*args)
-
end
-
-
# @private
-
1
def verify_messages_received
-
InsertOntoBacktrace.line(@expected_from) do
-
generate_error unless expected_messages_received? || failed_fast?
-
end
-
end
-
-
# @private
-
1
def expected_messages_received?
-
ignoring_args? || matches_exact_count? || matches_at_least_count? || matches_at_most_count?
-
end
-
-
1
def ensure_expected_ordering_received!
-
@order_group.verify_invocation_order(self) if @ordered
-
true
-
end
-
-
# @private
-
1
def ignoring_args?
-
2
@expected_received_count == :any
-
end
-
-
# @private
-
1
def matches_at_least_count?
-
@at_least && @actual_received_count >= @expected_received_count
-
end
-
-
# @private
-
1
def matches_at_most_count?
-
@at_most && @actual_received_count <= @expected_received_count
-
end
-
-
# @private
-
1
def matches_exact_count?
-
@expected_received_count == @actual_received_count
-
end
-
-
# @private
-
1
def similar_messages
-
@similar_messages ||= []
-
end
-
-
# @private
-
1
def advise(*args)
-
similar_messages << args
-
end
-
-
# @private
-
1
def generate_error
-
if similar_messages.empty?
-
@error_generator.raise_expectation_error(@message, @expected_received_count, @argument_list_matcher, @actual_received_count, expectation_count_type, *expected_args)
-
else
-
@error_generator.raise_similar_message_args_error(self, *@similar_messages)
-
end
-
end
-
-
1
def expectation_count_type
-
return :at_least if @at_least
-
return :at_most if @at_most
-
nil
-
end
-
-
# @private
-
1
def description
-
@error_generator.describe_expectation(@message, @expected_received_count, @actual_received_count, *expected_args)
-
end
-
-
1
def raise_out_of_order_error
-
@error_generator.raise_out_of_order_error @message
-
end
-
-
# Constrains a stub or message expectation to invocations with specific
-
# arguments.
-
#
-
# With a stub, if the message might be received with other args as well,
-
# you should stub a default value first, and then stub or mock the same
-
# message using `with` to constrain to specific arguments.
-
#
-
# A message expectation will fail if the message is received with different
-
# arguments.
-
#
-
# @example
-
#
-
# allow(cart).to receive(:add) { :failure }
-
# allow(cart).to receive(:add).with(Book.new(:isbn => 1934356379)) { :success }
-
# cart.add(Book.new(:isbn => 1234567890))
-
# # => :failure
-
# cart.add(Book.new(:isbn => 1934356379))
-
# # => :success
-
#
-
# expect(cart).to receive(:add).with(Book.new(:isbn => 1934356379)) { :success }
-
# cart.add(Book.new(:isbn => 1234567890))
-
# # => failed expectation
-
# cart.add(Book.new(:isbn => 1934356379))
-
# # => passes
-
1
def with(*args, &block)
-
if args.empty?
-
raise ArgumentError,
-
"`with` must have at least one argument. Use `no_args` matcher to set the expectation of receiving no arguments."
-
end
-
-
self.inner_implementation_action = block
-
@argument_list_matcher = ArgumentListMatcher.new(*args)
-
self
-
end
-
-
# Constrain a message expectation to be received a specific number of
-
# times.
-
#
-
# @example
-
#
-
# expect(dealer).to receive(:deal_card).exactly(10).times
-
1
def exactly(n, &block)
-
self.inner_implementation_action = block
-
set_expected_received_count :exactly, n
-
self
-
end
-
-
# Constrain a message expectation to be received at least a specific
-
# number of times.
-
#
-
# @example
-
#
-
# expect(dealer).to receive(:deal_card).at_least(9).times
-
1
def at_least(n, &block)
-
set_expected_received_count :at_least, n
-
-
if n == 0
-
raise "at_least(0) has been removed, use allow(...).to receive(:message) instead"
-
end
-
-
self.inner_implementation_action = block
-
-
self
-
end
-
-
# Constrain a message expectation to be received at most a specific
-
# number of times.
-
#
-
# @example
-
#
-
# expect(dealer).to receive(:deal_card).at_most(10).times
-
1
def at_most(n, &block)
-
self.inner_implementation_action = block
-
set_expected_received_count :at_most, n
-
self
-
end
-
-
# Syntactic sugar for `exactly`, `at_least` and `at_most`
-
#
-
# @example
-
#
-
# expect(dealer).to receive(:deal_card).exactly(10).times
-
# expect(dealer).to receive(:deal_card).at_least(10).times
-
# expect(dealer).to receive(:deal_card).at_most(10).times
-
1
def times(&block)
-
self.inner_implementation_action = block
-
self
-
end
-
-
# Expect a message not to be received at all.
-
#
-
# @example
-
#
-
# expect(car).to receive(:stop).never
-
1
def never
-
ErrorGenerator.raise_double_negation_error("expect(obj)") if negative?
-
@expected_received_count = 0
-
self
-
end
-
-
# Expect a message to be received exactly one time.
-
#
-
# @example
-
#
-
# expect(car).to receive(:go).once
-
1
def once(&block)
-
self.inner_implementation_action = block
-
set_expected_received_count :exactly, 1
-
self
-
end
-
-
# Expect a message to be received exactly two times.
-
#
-
# @example
-
#
-
# expect(car).to receive(:go).twice
-
1
def twice(&block)
-
self.inner_implementation_action = block
-
set_expected_received_count :exactly, 2
-
self
-
end
-
-
# Expect a message to be received exactly three times.
-
#
-
# @example
-
#
-
# expect(car).to receive(:go).thrice
-
1
def thrice(&block)
-
self.inner_implementation_action = block
-
set_expected_received_count :exactly, 3
-
self
-
end
-
-
# Expect messages to be received in a specific order.
-
#
-
# @example
-
#
-
# expect(api).to receive(:prepare).ordered
-
# expect(api).to receive(:run).ordered
-
# expect(api).to receive(:finish).ordered
-
1
def ordered(&block)
-
self.inner_implementation_action = block
-
additional_expected_calls.times do
-
@order_group.register(self)
-
end
-
@ordered = true
-
self
-
end
-
-
# @private
-
1
def additional_expected_calls
-
return 0 if @expectation_type == :stub || !@exactly
-
@expected_received_count - 1
-
end
-
-
# @private
-
1
def ordered?
-
2
@ordered
-
end
-
-
# @private
-
1
def negative_expectation_for?(message)
-
@message == message && negative?
-
end
-
-
# @private
-
1
def actual_received_count_matters?
-
@at_least || @at_most || @exactly
-
end
-
-
# @private
-
1
def increase_actual_received_count!
-
@actual_received_count += 1
-
end
-
-
1
private
-
-
1
def invoke_incrementing_actual_calls_by(increment, parent_stub, *args, &block)
-
2
args.unshift(orig_object) if yield_receiver_to_implementation_block?
-
-
2
if negative? || ((@exactly || @at_most) && (@actual_received_count == @expected_received_count))
-
@actual_received_count += increment
-
@failed_fast = true
-
# args are the args we actually received, @argument_list_matcher is the
-
# list of args we were expecting
-
@error_generator.raise_expectation_error(@message, @expected_received_count, @argument_list_matcher, @actual_received_count, expectation_count_type, *args)
-
end
-
-
2
@order_group.handle_order_constraint self
-
-
2
begin
-
2
if implementation.present?
-
2
implementation.call(*args, &block)
-
elsif parent_stub
-
parent_stub.invoke(nil, *args, &block)
-
end
-
ensure
-
2
@actual_received_count += increment
-
end
-
end
-
-
1
def failed_fast?
-
@failed_fast
-
end
-
-
1
def set_expected_received_count(relativity, n)
-
@at_least = (relativity == :at_least)
-
@at_most = (relativity == :at_most)
-
@exactly = (relativity == :exactly)
-
@expected_received_count = case n
-
when Numeric then n
-
when :once then 1
-
when :twice then 2
-
when :thrice then 3
-
end
-
end
-
-
1
def initial_implementation_action=(action)
-
implementation.initial_action = action
-
end
-
-
1
def inner_implementation_action=(action)
-
2
return unless action
-
warn_about_stub_override if implementation.inner_action
-
implementation.inner_action = action
-
end
-
-
1
def terminal_implementation_action=(action)
-
2
implementation.terminal_action = action
-
end
-
-
1
def warn_about_stub_override
-
RSpec.warning(
-
"You're overriding a previous stub implementation of `#{@message}`. " \
-
"Called from #{CallerFilter.first_non_rspec_line}."
-
)
-
end
-
end
-
-
# Handles the implementation of an `and_yield` declaration.
-
# @private
-
1
class AndYieldImplementation
-
1
def initialize(args_to_yield, eval_context, error_generator)
-
@args_to_yield = args_to_yield
-
@eval_context = eval_context
-
@error_generator = error_generator
-
end
-
-
1
def call(*_args_to_ignore, &block)
-
return if @args_to_yield.empty? && @eval_context.nil?
-
-
@error_generator.raise_missing_block_error @args_to_yield unless block
-
value = nil
-
block_signature = Support::BlockSignature.new(block)
-
-
@args_to_yield.each do |args|
-
unless Support::StrictSignatureVerifier.new(block_signature, args).valid?
-
@error_generator.raise_wrong_arity_error(args, block_signature)
-
end
-
-
value = @eval_context ? @eval_context.instance_exec(*args, &block) : block.call(*args)
-
end
-
value
-
end
-
end
-
-
# Handles the implementation of an `and_return` implementation.
-
# @private
-
1
class AndReturnImplementation
-
1
def initialize(values_to_return)
-
2
@values_to_return = values_to_return
-
end
-
-
1
def call(*_args_to_ignore, &_block)
-
2
if @values_to_return.size > 1
-
@values_to_return.shift
-
else
-
2
@values_to_return.first
-
end
-
end
-
end
-
-
# Represents a configured implementation. Takes into account
-
# any number of sub-implementations.
-
# @private
-
1
class Implementation
-
1
attr_accessor :initial_action, :inner_action, :terminal_action
-
-
1
def call(*args, &block)
-
actions.map do |action|
-
2
action.call(*args, &block)
-
2
end.last
-
end
-
-
1
def present?
-
2
actions.any?
-
end
-
-
1
private
-
-
1
def actions
-
4
[initial_action, inner_action, terminal_action].compact
-
end
-
end
-
-
# Represents an `and_call_original` implementation.
-
# @private
-
1
class AndWrapOriginalImplementation
-
1
def initialize(method, block)
-
@method = method
-
@block = block
-
end
-
-
1
CannotModifyFurtherError = Class.new(StandardError)
-
-
1
def initial_action=(_value)
-
raise cannot_modify_further_error
-
end
-
-
1
def inner_action=(_value)
-
raise cannot_modify_further_error
-
end
-
-
1
def terminal_action=(_value)
-
raise cannot_modify_further_error
-
end
-
-
1
def present?
-
true
-
end
-
-
1
def inner_action
-
true
-
end
-
-
1
def call(*args, &block)
-
@block.call(@method, *args, &block)
-
end
-
-
1
private
-
-
1
def cannot_modify_further_error
-
CannotModifyFurtherError.new "This method has already been configured " \
-
"to call the original implementation, and cannot be modified further."
-
end
-
end
-
-
# Insert original locations into stacktraces
-
#
-
# @private
-
1
class InsertOntoBacktrace
-
1
def self.line(location)
-
yield
-
rescue RSpec::Mocks::MockExpectationError => error
-
error.backtrace.insert(0, location)
-
Kernel.raise error
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Mocks
-
# @private
-
1
class MethodDouble
-
# @private
-
1
attr_reader :method_name, :object, :expectations, :stubs
-
-
# @private
-
1
def initialize(object, method_name, proxy)
-
2
@method_name = method_name
-
2
@object = object
-
2
@proxy = proxy
-
-
2
@original_visibility = nil
-
2
@method_stasher = InstanceMethodStasher.new(object, method_name)
-
2
@method_is_proxied = false
-
2
@expectations = []
-
2
@stubs = []
-
end
-
-
1
def original_method
-
# If original method is not present, uses the `method_missing`
-
# handler of the object. This accounts for cases where the user has not
-
# correctly defined `respond_to?`, and also 1.8 which does not provide
-
# method handles for missing methods even if `respond_to?` is correct.
-
@original_method ||=
-
@method_stasher.original_method ||
-
@proxy.original_method_handle_for(method_name) ||
-
Proc.new do |*args, &block|
-
@object.__send__(:method_missing, @method_name, *args, &block)
-
6
end
-
end
-
-
1
alias_method :save_original_method!, :original_method
-
-
# @private
-
1
def visibility
-
4
@proxy.visibility_for(@method_name)
-
end
-
-
# @private
-
1
def object_singleton_class
-
16
class << @object; self; end
-
end
-
-
# @private
-
1
def configure_method
-
2
@original_visibility = [visibility, method_name]
-
2
@method_stasher.stash unless @method_is_proxied
-
2
define_proxy_method
-
end
-
-
# @private
-
1
def define_proxy_method
-
2
return if @method_is_proxied
-
-
2
save_original_method!
-
2
definition_target.class_exec(self, method_name, visibility) do |method_double, method_name, visibility|
-
2
define_method(method_name) do |*args, &block|
-
2
method_double.proxy_method_invoked(self, *args, &block)
-
end
-
2
__send__(visibility, method_name)
-
end
-
-
2
@method_is_proxied = true
-
end
-
-
# The implementation of the proxied method. Subclasses may override this
-
# method to perform additional operations.
-
#
-
# @private
-
1
def proxy_method_invoked(_obj, *args, &block)
-
2
@proxy.message_received method_name, *args, &block
-
end
-
-
# @private
-
1
def restore_original_method
-
2
return show_frozen_warning if object_singleton_class.frozen?
-
2
return unless @method_is_proxied
-
-
2
definition_target.__send__(:remove_method, @method_name)
-
-
2
@method_stasher.restore if @method_stasher.method_is_stashed?
-
2
restore_original_visibility
-
-
2
@method_is_proxied = false
-
end
-
-
# @private
-
1
def show_frozen_warning
-
RSpec.warn_with(
-
"WARNING: rspec-mocks was unable to restore the original `#{@method_name}` " \
-
"method on #{@object.inspect} because it has been frozen. If you reuse this " \
-
"object, `#{@method_name}` will continue to respond with its stub implementation.",
-
:call_site => nil,
-
:use_spec_location_as_call_site => true
-
)
-
end
-
-
# @private
-
1
def restore_original_visibility
-
return unless @original_visibility &&
-
2
MethodReference.method_defined_at_any_visibility?(object_singleton_class, @method_name)
-
-
2
object_singleton_class.__send__(*@original_visibility)
-
end
-
-
# @private
-
1
def verify
-
1
expectations.each { |e| e.verify_messages_received }
-
end
-
-
# @private
-
1
def reset
-
2
restore_original_method
-
2
clear
-
end
-
-
# @private
-
1
def clear
-
2
expectations.clear
-
2
stubs.clear
-
end
-
-
# The type of message expectation to create has been extracted to its own
-
# method so that subclasses can override it.
-
#
-
# @private
-
1
def message_expectation_class
-
MessageExpectation
-
end
-
-
# @private
-
1
def add_expectation(error_generator, expectation_ordering, expected_from, opts, &implementation)
-
configure_method
-
expectation = message_expectation_class.new(error_generator, expectation_ordering,
-
expected_from, self, :expectation, opts, &implementation)
-
expectations << expectation
-
expectation
-
end
-
-
# @private
-
1
def build_expectation(error_generator, expectation_ordering)
-
expected_from = IGNORED_BACKTRACE_LINE
-
message_expectation_class.new(error_generator, expectation_ordering, expected_from, self)
-
end
-
-
# @private
-
1
def add_stub(error_generator, expectation_ordering, expected_from, opts={}, &implementation)
-
2
configure_method
-
2
stub = message_expectation_class.new(error_generator, expectation_ordering, expected_from,
-
self, :stub, opts, &implementation)
-
2
stubs.unshift stub
-
2
stub
-
end
-
-
# A simple stub can only return a concrete value for a message, and
-
# cannot match on arguments. It is used as an optimization over
-
# `add_stub` / `add_expectation` where it is known in advance that this
-
# is all that will be required of a stub, such as when passing attributes
-
# to the `double` example method. They do not stash or restore existing method
-
# definitions.
-
#
-
# @private
-
1
def add_simple_stub(method_name, response)
-
setup_simple_method_double method_name, response, stubs
-
end
-
-
# @private
-
1
def add_simple_expectation(method_name, response, error_generator, backtrace_line)
-
setup_simple_method_double method_name, response, expectations, error_generator, backtrace_line
-
end
-
-
# @private
-
1
def setup_simple_method_double(method_name, response, collection, error_generator=nil, backtrace_line=nil)
-
define_proxy_method
-
-
me = SimpleMessageExpectation.new(method_name, response, error_generator, backtrace_line)
-
collection.unshift me
-
me
-
end
-
-
# @private
-
1
def add_default_stub(*args, &implementation)
-
return if stubs.any?
-
add_stub(*args, &implementation)
-
end
-
-
# @private
-
1
def remove_stub
-
raise_method_not_stubbed_error if stubs.empty?
-
remove_stub_if_present
-
end
-
-
# @private
-
1
def remove_stub_if_present
-
expectations.empty? ? reset : stubs.clear
-
end
-
-
# @private
-
1
def raise_method_not_stubbed_error
-
raise MockExpectationError, "The method `#{method_name}` was not stubbed or was already unstubbed"
-
end
-
-
# In Ruby 2.0.0 and above prepend will alter the method lookup chain.
-
# We use an object's singleton class to define method doubles upon,
-
# however if the object has had it's singleton class (as opposed to
-
# it's actual class) prepended too then the the method lookup chain
-
# will look in the prepended module first, **before** the singleton
-
# class.
-
#
-
# This code works around that by providing a mock definition target
-
# that is either the singleton class, or if necessary, a prepended module
-
# of our own.
-
#
-
1
if Support::RubyFeatures.module_prepends_supported?
-
-
1
private
-
-
# We subclass `Module` in order to be able to easily detect our prepended module.
-
1
RSpecPrependedModule = Class.new(Module)
-
-
1
def definition_target
-
4
@definition_target ||= usable_rspec_prepended_module || object_singleton_class
-
end
-
-
1
def usable_rspec_prepended_module
-
2
@proxy.prepended_modules_of_singleton_class.each do |mod|
-
# If we have one of our modules prepended before one of the user's
-
# modules that defines the method, use that, since our module's
-
# definition will take precedence.
-
return mod if RSpecPrependedModule === mod
-
-
# If we hit a user module with the method defined first,
-
# we must create a new prepend module, even if one exists later,
-
# because ours will only take precedence if it comes first.
-
return new_rspec_prepended_module if mod.method_defined?(method_name)
-
end
-
-
nil
-
end
-
-
1
def new_rspec_prepended_module
-
RSpecPrependedModule.new.tap do |mod|
-
object_singleton_class.__send__ :prepend, mod
-
end
-
end
-
-
else
-
-
private
-
-
def definition_target
-
object_singleton_class
-
end
-
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Mocks
-
# Represents a method on an object that may or may not be defined.
-
# The method may be an instance method on a module or a method on
-
# any object.
-
#
-
# @private
-
1
class MethodReference
-
1
def initialize(object_reference, method_name)
-
@object_reference = object_reference
-
@method_name = method_name
-
end
-
-
# A method is implemented if sending the message does not result in
-
# a `NoMethodError`. It might be dynamically implemented by
-
# `method_missing`.
-
1
def implemented?
-
@object_reference.when_loaded do |m|
-
method_implemented?(m)
-
end
-
end
-
-
# Returns true if we definitively know that sending the method
-
# will result in a `NoMethodError`.
-
#
-
# This is not simply the inverse of `implemented?`: there are
-
# cases when we don't know if a method is implemented and
-
# both `implemented?` and `unimplemented?` will return false.
-
1
def unimplemented?
-
@object_reference.when_loaded do |_m|
-
return !implemented?
-
end
-
-
# If it's not loaded, then it may be implemented but we can't check.
-
false
-
end
-
-
# A method is defined if we are able to get a `Method` object for it.
-
# In that case, we can assert against metadata like the arity.
-
1
def defined?
-
@object_reference.when_loaded do |m|
-
method_defined?(m)
-
end
-
end
-
-
1
def with_signature
-
return unless (original = original_method)
-
yield Support::MethodSignature.new(original)
-
end
-
-
1
def visibility
-
@object_reference.when_loaded do |m|
-
return visibility_from(m)
-
end
-
-
# When it's not loaded, assume it's public. We don't want to
-
# wrongly treat the method as private.
-
:public
-
end
-
-
1
private
-
-
1
def original_method
-
@object_reference.when_loaded do |m|
-
self.defined? && find_method(m)
-
end
-
end
-
-
1
def self.instance_method_visibility_for(klass, method_name)
-
16
if klass.public_method_defined?(method_name)
-
16
:public
-
elsif klass.private_method_defined?(method_name)
-
:private
-
elsif klass.protected_method_defined?(method_name)
-
:protected
-
end
-
end
-
-
1
class << self
-
1
alias method_defined_at_any_visibility? instance_method_visibility_for
-
end
-
-
1
def self.method_visibility_for(object, method_name)
-
8
instance_method_visibility_for(class << object; self; end, method_name).tap do |vis|
-
# If the method is not defined on the class, `instance_method_visibility_for`
-
# returns `nil`. However, it may be handled dynamically by `method_missing`,
-
# so here we check `respond_to` (passing false to not check private methods).
-
#
-
# This only considers the public case, but I don't think it's possible to
-
# write `method_missing` in such a way that it handles a dynamic message
-
# with private or protected visibility. Ruby doesn't provide you with
-
# the caller info.
-
4
return :public if vis.nil? && object.respond_to?(method_name, false)
-
end
-
end
-
end
-
-
# @private
-
1
class InstanceMethodReference < MethodReference
-
1
private
-
-
1
def method_implemented?(mod)
-
MethodReference.method_defined_at_any_visibility?(mod, @method_name)
-
end
-
-
# Ideally, we'd use `respond_to?` for `method_implemented?` but we need a
-
# reference to an instance to do that and we don't have one. Note that
-
# we may get false negatives: if the method is implemented via
-
# `method_missing`, we'll return `false` even though it meets our
-
# definition of "implemented". However, it's the best we can do.
-
1
alias method_defined? method_implemented?
-
-
# works around the fact that repeated calls for method parameters will
-
# falsely return empty arrays on JRuby in certain circumstances, this
-
# is necessary here because we can't dup/clone UnboundMethods.
-
#
-
# This is necessary due to a bug in JRuby prior to 1.7.5 fixed in:
-
# https://github.com/jruby/jruby/commit/99a0613fe29935150d76a9a1ee4cf2b4f63f4a27
-
1
if RUBY_PLATFORM == 'java' && JRUBY_VERSION.split('.')[-1].to_i < 5
-
def find_method(mod)
-
mod.dup.instance_method(@method_name)
-
end
-
else
-
1
def find_method(mod)
-
mod.instance_method(@method_name)
-
end
-
end
-
-
1
def visibility_from(mod)
-
MethodReference.instance_method_visibility_for(mod, @method_name)
-
end
-
end
-
-
# @private
-
1
class ObjectMethodReference < MethodReference
-
1
private
-
-
1
def method_implemented?(object)
-
object.respond_to?(@method_name, true)
-
end
-
-
1
def method_defined?(object)
-
(class << object; self; end).method_defined?(@method_name)
-
end
-
-
1
def find_method(object)
-
object.method(@method_name)
-
end
-
-
1
def visibility_from(object)
-
MethodReference.method_visibility_for(object, @method_name)
-
end
-
end
-
end
-
end
-
1
RSpec::Support.require_rspec_support 'recursive_const_methods'
-
-
1
module RSpec
-
1
module Mocks
-
# Provides information about constants that may (or may not)
-
# have been mutated by rspec-mocks.
-
1
class Constant
-
1
extend Support::RecursiveConstMethods
-
-
# @api private
-
1
def initialize(name)
-
@name = name
-
@previously_defined = false
-
@stubbed = false
-
@hidden = false
-
end
-
-
# @return [String] The fully qualified name of the constant.
-
1
attr_reader :name
-
-
# @return [Object, nil] The original value (e.g. before it
-
# was mutated by rspec-mocks) of the constant, or
-
# nil if the constant was not previously defined.
-
1
attr_accessor :original_value
-
-
# @private
-
1
attr_writer :previously_defined, :stubbed, :hidden
-
-
# @return [Boolean] Whether or not the constant was defined
-
# before the current example.
-
1
def previously_defined?
-
@previously_defined
-
end
-
-
# @return [Boolean] Whether or not rspec-mocks has mutated
-
# (stubbed or hidden) this constant.
-
1
def mutated?
-
@stubbed || @hidden
-
end
-
-
# @return [Boolean] Whether or not rspec-mocks has stubbed
-
# this constant.
-
1
def stubbed?
-
@stubbed
-
end
-
-
# @return [Boolean] Whether or not rspec-mocks has hidden
-
# this constant.
-
1
def hidden?
-
@hidden
-
end
-
-
# The default `to_s` isn't very useful, so a custom version is provided.
-
1
def to_s
-
"#<#{self.class.name} #{name}>"
-
end
-
1
alias inspect to_s
-
-
# @private
-
1
def self.unmutated(name)
-
const = new(name)
-
const.previously_defined = recursive_const_defined?(name)
-
const.stubbed = false
-
const.hidden = false
-
const.original_value = recursive_const_get(name) if const.previously_defined?
-
-
const
-
end
-
-
# Queries rspec-mocks to find out information about the named constant.
-
#
-
# @param [String] name the name of the constant
-
# @return [Constant] an object contaning information about the named
-
# constant.
-
1
def self.original(name)
-
mutator = ::RSpec::Mocks.space.constant_mutator_for(name)
-
mutator ? mutator.to_constant : unmutated(name)
-
end
-
end
-
-
# Provides a means to stub constants.
-
1
class ConstantMutator
-
1
extend Support::RecursiveConstMethods
-
-
# Stubs a constant.
-
#
-
# @param (see ExampleMethods#stub_const)
-
# @option (see ExampleMethods#stub_const)
-
# @return (see ExampleMethods#stub_const)
-
#
-
# @see ExampleMethods#stub_const
-
# @note It's recommended that you use `stub_const` in your
-
# examples. This is an alternate public API that is provided
-
# so you can stub constants in other contexts (e.g. helper
-
# classes).
-
1
def self.stub(constant_name, value, options={})
-
mutator = if recursive_const_defined?(constant_name, &raise_on_invalid_const)
-
DefinedConstantReplacer
-
else
-
UndefinedConstantSetter
-
end
-
-
mutate(mutator.new(constant_name, value, options[:transfer_nested_constants]))
-
value
-
end
-
-
# Hides a constant.
-
#
-
# @param (see ExampleMethods#hide_const)
-
#
-
# @see ExampleMethods#hide_const
-
# @note It's recommended that you use `hide_const` in your
-
# examples. This is an alternate public API that is provided
-
# so you can hide constants in other contexts (e.g. helper
-
# classes).
-
1
def self.hide(constant_name)
-
mutate(ConstantHider.new(constant_name, nil, {}))
-
nil
-
end
-
-
# Contains common functionality used by all of the constant mutators.
-
#
-
# @private
-
1
class BaseMutator
-
1
include Support::RecursiveConstMethods
-
-
1
attr_reader :original_value, :full_constant_name
-
-
1
def initialize(full_constant_name, mutated_value, transfer_nested_constants)
-
@full_constant_name = normalize_const_name(full_constant_name)
-
@mutated_value = mutated_value
-
@transfer_nested_constants = transfer_nested_constants
-
@context_parts = @full_constant_name.split('::')
-
@const_name = @context_parts.pop
-
@reset_performed = false
-
end
-
-
1
def to_constant
-
const = Constant.new(full_constant_name)
-
const.original_value = original_value
-
-
const
-
end
-
-
1
def idempotently_reset
-
reset unless @reset_performed
-
@reset_performed = true
-
end
-
end
-
-
# Hides a defined constant for the duration of an example.
-
#
-
# @private
-
1
class ConstantHider < BaseMutator
-
1
def mutate
-
return unless (@defined = recursive_const_defined?(full_constant_name))
-
@context = recursive_const_get(@context_parts.join('::'))
-
@original_value = get_const_defined_on(@context, @const_name)
-
-
@context.__send__(:remove_const, @const_name)
-
end
-
-
1
def to_constant
-
return Constant.unmutated(full_constant_name) unless @defined
-
-
const = super
-
const.hidden = true
-
const.previously_defined = true
-
-
const
-
end
-
-
1
def reset
-
return unless @defined
-
@context.const_set(@const_name, @original_value)
-
end
-
end
-
-
# Replaces a defined constant for the duration of an example.
-
#
-
# @private
-
1
class DefinedConstantReplacer < BaseMutator
-
1
def initialize(*args)
-
super
-
@constants_to_transfer = []
-
end
-
-
1
def mutate
-
@context = recursive_const_get(@context_parts.join('::'))
-
@original_value = get_const_defined_on(@context, @const_name)
-
-
@constants_to_transfer = verify_constants_to_transfer!
-
-
@context.__send__(:remove_const, @const_name)
-
@context.const_set(@const_name, @mutated_value)
-
-
transfer_nested_constants
-
end
-
-
1
def to_constant
-
const = super
-
const.stubbed = true
-
const.previously_defined = true
-
-
const
-
end
-
-
1
def reset
-
@constants_to_transfer.each do |const|
-
@mutated_value.__send__(:remove_const, const)
-
end
-
-
@context.__send__(:remove_const, @const_name)
-
@context.const_set(@const_name, @original_value)
-
end
-
-
1
def transfer_nested_constants
-
@constants_to_transfer.each do |const|
-
@mutated_value.const_set(const, get_const_defined_on(original_value, const))
-
end
-
end
-
-
1
def verify_constants_to_transfer!
-
return [] unless should_transfer_nested_constants?
-
-
{ @original_value => "the original value", @mutated_value => "the stubbed value" }.each do |value, description|
-
next if value.respond_to?(:constants)
-
-
raise ArgumentError,
-
"Cannot transfer nested constants for #{@full_constant_name} " \
-
"since #{description} is not a class or module and only classes " \
-
"and modules support nested constants."
-
end
-
-
if Array === @transfer_nested_constants
-
@transfer_nested_constants = @transfer_nested_constants.map(&:to_s) if RUBY_VERSION == '1.8.7'
-
undefined_constants = @transfer_nested_constants - constants_defined_on(@original_value)
-
-
if undefined_constants.any?
-
available_constants = constants_defined_on(@original_value) - @transfer_nested_constants
-
raise ArgumentError,
-
"Cannot transfer nested constant(s) #{undefined_constants.join(' and ')} " \
-
"for #{@full_constant_name} since they are not defined. Did you mean " \
-
"#{available_constants.join(' or ')}?"
-
end
-
-
@transfer_nested_constants
-
else
-
constants_defined_on(@original_value)
-
end
-
end
-
-
1
def should_transfer_nested_constants?
-
return true if @transfer_nested_constants
-
return false unless RSpec::Mocks.configuration.transfer_nested_constants?
-
@original_value.respond_to?(:constants) && @mutated_value.respond_to?(:constants)
-
end
-
end
-
-
# Sets an undefined constant for the duration of an example.
-
#
-
# @private
-
1
class UndefinedConstantSetter < BaseMutator
-
1
def mutate
-
@parent = @context_parts.inject(Object) do |klass, name|
-
if const_defined_on?(klass, name)
-
get_const_defined_on(klass, name)
-
else
-
ConstantMutator.stub(name_for(klass, name), Module.new)
-
end
-
end
-
-
@parent.const_set(@const_name, @mutated_value)
-
end
-
-
1
def to_constant
-
const = super
-
const.stubbed = true
-
const.previously_defined = false
-
-
const
-
end
-
-
1
def reset
-
@parent.__send__(:remove_const, @const_name)
-
end
-
-
1
private
-
-
1
def name_for(parent, name)
-
root = if parent == Object
-
''
-
else
-
parent.name
-
end
-
root + '::' + name
-
end
-
end
-
-
# Uses the mutator to mutate (stub or hide) a constant. Ensures that
-
# the mutator is correctly registered so it can be backed out at the end
-
# of the test.
-
#
-
# @private
-
1
def self.mutate(mutator)
-
::RSpec::Mocks.space.register_constant_mutator(mutator)
-
mutator.mutate
-
end
-
-
# Used internally by the constant stubbing to raise a helpful
-
# error when a constant like "A::B::C" is stubbed and A::B is
-
# not a module (and thus, it's impossible to define "A::B::C"
-
# since only modules can have nested constants).
-
#
-
# @api private
-
1
def self.raise_on_invalid_const
-
lambda do |const_name, failed_name|
-
raise "Cannot stub constant #{failed_name} on #{const_name} " \
-
"since #{const_name} is not a module."
-
end
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Mocks
-
# @private
-
1
class ObjectReference
-
# Returns an appropriate Object or Module reference based
-
# on the given argument.
-
1
def self.for(object_module_or_name, allow_direct_object_refs=false)
-
case object_module_or_name
-
when Module then DirectModuleReference.new(object_module_or_name)
-
when String then NamedObjectReference.new(object_module_or_name)
-
else
-
if allow_direct_object_refs
-
DirectObjectReference.new(object_module_or_name)
-
else
-
raise ArgumentError,
-
"Module or String expected, got #{object_module_or_name.inspect}"
-
end
-
end
-
end
-
end
-
-
# Used when an object is passed to `object_double`.
-
# Represents a reference to that object.
-
#
-
# @private
-
1
class DirectObjectReference
-
1
def initialize(object)
-
2
@object = object
-
end
-
-
1
def description
-
@object.inspect
-
end
-
-
1
def const_to_replace
-
raise ArgumentError,
-
"Can not perform constant replacement with an object."
-
end
-
-
1
def defined?
-
true
-
end
-
-
1
def when_loaded
-
yield @object
-
end
-
end
-
-
# Used when a module is passed to `class_double` or `instance_double`.
-
# Represents a reference to that module.
-
#
-
# @private
-
1
class DirectModuleReference < DirectObjectReference
-
1
def const_to_replace
-
@object.name
-
end
-
1
alias description const_to_replace
-
end
-
-
# Used when a string is passed to `class_double`, `instance_double`
-
# or `object_double`.
-
# Represents a reference to the object named (via a constant lookup)
-
# by the string.
-
#
-
# @private
-
1
class NamedObjectReference
-
1
def initialize(const_name)
-
@const_name = const_name
-
end
-
-
1
def defined?
-
!!object
-
end
-
-
1
def const_to_replace
-
@const_name
-
end
-
1
alias description const_to_replace
-
-
1
def when_loaded(&_block)
-
yield object if object
-
end
-
-
1
private
-
-
1
def object
-
@object ||= Constant.original(@const_name).original_value
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Mocks
-
# @private
-
1
class OrderGroup
-
1
def initialize
-
4
@expectations = []
-
4
@invocation_order = []
-
4
@index = 0
-
end
-
-
# @private
-
1
def register(expectation)
-
@expectations << expectation
-
end
-
-
1
def invoked(message)
-
2
@invocation_order << message
-
end
-
-
# @private
-
1
def ready_for?(expectation)
-
remaining_expectations.find(&:ordered?) == expectation
-
end
-
-
# @private
-
1
def consume
-
remaining_expectations.each_with_index do |expectation, index|
-
next unless expectation.ordered?
-
-
@index += index + 1
-
return expectation
-
end
-
nil
-
end
-
-
# @private
-
1
def handle_order_constraint(expectation)
-
2
return unless expectation.ordered? && remaining_expectations.include?(expectation)
-
return consume if ready_for?(expectation)
-
expectation.raise_out_of_order_error
-
end
-
-
1
def verify_invocation_order(expectation)
-
expectation.raise_out_of_order_error unless expectations_invoked_in_order?
-
true
-
end
-
-
1
def clear
-
@index = 0
-
@invocation_order.clear
-
@expectations.clear
-
end
-
-
1
def empty?
-
@expectations.empty?
-
end
-
-
1
private
-
-
1
def remaining_expectations
-
@expectations[@index..-1] || []
-
end
-
-
1
def expectations_invoked_in_order?
-
invoked_expectations == expected_invocations
-
end
-
-
1
def invoked_expectations
-
@expectations.select { |e| e.ordered? && @invocation_order.include?(e) }
-
end
-
-
1
def expected_invocations
-
@invocation_order.map { |invocation| expectation_for(invocation) }.compact
-
end
-
-
1
def expectation_for(message)
-
@expectations.find { |e| message == e }
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Mocks
-
# @private
-
1
class Proxy
-
1
SpecificMessage = Struct.new(:object, :message, :args) do
-
1
def ==(expectation)
-
expectation.orig_object == object && expectation.matches?(message, *args)
-
end
-
end
-
-
# @private
-
1
def ensure_implemented(*_args)
-
# noop for basic proxies, see VerifyingProxy for behaviour.
-
end
-
-
# @private
-
1
def initialize(object, order_group, name=nil, options={})
-
2
@object = object
-
2
@order_group = order_group
-
2
@name = name
-
2
@error_generator = ErrorGenerator.new(object, name)
-
2
@messages_received = []
-
2
@options = options
-
2
@null_object = false
-
2
@method_doubles = Hash.new { |h, k| h[k] = MethodDouble.new(@object, k, self) }
-
end
-
-
# @private
-
1
attr_reader :object
-
-
# @private
-
1
def null_object?
-
@null_object
-
end
-
-
# @private
-
# Tells the object to ignore any messages that aren't explicitly set as
-
# stubs or message expectations.
-
1
def as_null_object
-
@null_object = true
-
@object
-
end
-
-
# @private
-
1
def original_method_handle_for(_message)
-
nil
-
end
-
-
# @private
-
1
def add_message_expectation(method_name, opts={}, &block)
-
location = opts.fetch(:expected_from) { CallerFilter.first_non_rspec_line }
-
meth_double = method_double_for(method_name)
-
-
if null_object? && !block
-
meth_double.add_default_stub(@error_generator, @order_group, location, opts) do
-
@object
-
end
-
end
-
-
meth_double.add_expectation @error_generator, @order_group, location, opts, &block
-
end
-
-
# @private
-
1
def add_simple_expectation(method_name, response, location)
-
method_double_for(method_name).add_simple_expectation method_name, response, @error_generator, location
-
end
-
-
# @private
-
1
def build_expectation(method_name)
-
meth_double = method_double_for(method_name)
-
-
meth_double.build_expectation(
-
@error_generator,
-
@order_group
-
)
-
end
-
-
# @private
-
1
def replay_received_message_on(expectation, &block)
-
expected_method_name = expectation.message
-
meth_double = method_double_for(expected_method_name)
-
-
if meth_double.expectations.any?
-
@error_generator.raise_expectation_on_mocked_method(expected_method_name)
-
end
-
-
unless null_object? || meth_double.stubs.any?
-
@error_generator.raise_expectation_on_unstubbed_method(expected_method_name)
-
end
-
-
@messages_received.each do |(actual_method_name, args, _)|
-
next unless expectation.matches?(actual_method_name, *args)
-
-
expectation.invoke(nil)
-
block.call(*args) if block
-
end
-
end
-
-
# @private
-
1
def check_for_unexpected_arguments(expectation)
-
@messages_received.each do |(method_name, args, _)|
-
next unless expectation.matches_name_but_not_args(method_name, *args)
-
-
raise_unexpected_message_args_error(expectation, *args)
-
end
-
end
-
-
# @private
-
1
def add_stub(method_name, opts={}, &implementation)
-
4
location = opts.fetch(:expected_from) { CallerFilter.first_non_rspec_line }
-
2
method_double_for(method_name).add_stub @error_generator, @order_group, location, opts, &implementation
-
end
-
-
# @private
-
1
def add_simple_stub(method_name, response)
-
method_double_for(method_name).add_simple_stub method_name, response
-
end
-
-
# @private
-
1
def remove_stub(method_name)
-
method_double_for(method_name).remove_stub
-
end
-
-
# @private
-
1
def remove_stub_if_present(method_name)
-
method_double_for(method_name).remove_stub_if_present
-
end
-
-
# @private
-
1
def verify
-
2
@method_doubles.each_value { |d| d.verify }
-
end
-
-
# @private
-
1
def reset
-
2
@messages_received.clear
-
end
-
-
# @private
-
1
def received_message?(method_name, *args, &block)
-
@messages_received.any? { |array| array == [method_name, args, block] }
-
end
-
-
# @private
-
1
def has_negative_expectation?(message)
-
method_double_for(message).expectations.find { |expectation| expectation.negative_expectation_for?(message) }
-
end
-
-
# @private
-
1
def record_message_received(message, *args, &block)
-
2
@order_group.invoked SpecificMessage.new(object, message, args)
-
2
@messages_received << [message, args, block]
-
end
-
-
# @private
-
1
def message_received(message, *args, &block)
-
2
record_message_received message, *args, &block
-
-
2
expectation = find_matching_expectation(message, *args)
-
2
stub = find_matching_method_stub(message, *args)
-
-
2
if (stub && expectation && expectation.called_max_times?) || (stub && !expectation)
-
2
expectation.increase_actual_received_count! if expectation && expectation.actual_received_count_matters?
-
2
if (expectation = find_almost_matching_expectation(message, *args))
-
expectation.advise(*args) unless expectation.expected_messages_received?
-
end
-
2
stub.invoke(nil, *args, &block)
-
elsif expectation
-
expectation.invoke(stub, *args, &block)
-
elsif (expectation = find_almost_matching_expectation(message, *args))
-
expectation.advise(*args) if null_object? unless expectation.expected_messages_received?
-
-
if null_object? || !has_negative_expectation?(message)
-
raise_unexpected_message_args_error(expectation, *args)
-
end
-
elsif (stub = find_almost_matching_stub(message, *args))
-
stub.advise(*args)
-
raise_missing_default_stub_error(stub, *args)
-
elsif Class === @object
-
@object.superclass.__send__(message, *args, &block)
-
else
-
@object.__send__(:method_missing, message, *args, &block)
-
end
-
end
-
-
# @private
-
1
def raise_unexpected_message_error(method_name, *args)
-
@error_generator.raise_unexpected_message_error method_name, *args
-
end
-
-
# @private
-
1
def raise_unexpected_message_args_error(expectation, *args)
-
@error_generator.raise_unexpected_message_args_error(expectation, *args)
-
end
-
-
# @private
-
1
def raise_missing_default_stub_error(expectation, *args)
-
@error_generator.raise_missing_default_stub_error(expectation, *args)
-
end
-
-
# @private
-
1
def visibility_for(_method_name)
-
# This is the default (for test doubles). Subclasses override this.
-
:public
-
end
-
-
1
if Support::RubyFeatures.module_prepends_supported?
-
1
def self.prepended_modules_of(klass)
-
4
ancestors = klass.ancestors
-
-
# `|| 0` is necessary for Ruby 2.0, where the singleton class
-
# is only in the ancestor list when there are prepended modules.
-
4
singleton_index = ancestors.index(klass) || 0
-
-
4
ancestors[0, singleton_index]
-
end
-
-
1
def prepended_modules_of_singleton_class
-
2
@prepended_modules_of_singleton_class ||= RSpec::Mocks::Proxy.prepended_modules_of(@object.singleton_class)
-
end
-
end
-
-
1
private
-
-
1
def method_double_for(message)
-
8
@method_doubles[message.to_sym]
-
end
-
-
1
def find_matching_expectation(method_name, *args)
-
2
find_best_matching_expectation_for(method_name) do |expectation|
-
expectation.matches?(method_name, *args)
-
end
-
end
-
-
1
def find_almost_matching_expectation(method_name, *args)
-
2
find_best_matching_expectation_for(method_name) do |expectation|
-
expectation.matches_name_but_not_args(method_name, *args)
-
end
-
end
-
-
1
def find_best_matching_expectation_for(method_name)
-
4
first_match = nil
-
-
4
method_double_for(method_name).expectations.each do |expectation|
-
next unless yield expectation
-
return expectation unless expectation.called_max_times?
-
first_match ||= expectation
-
end
-
-
4
first_match
-
end
-
-
1
def find_matching_method_stub(method_name, *args)
-
4
method_double_for(method_name).stubs.find { |stub| stub.matches?(method_name, *args) }
-
end
-
-
1
def find_almost_matching_stub(method_name, *args)
-
method_double_for(method_name).stubs.find { |stub| stub.matches_name_but_not_args(method_name, *args) }
-
end
-
end
-
-
# @private
-
1
class TestDoubleProxy < Proxy
-
1
def reset
-
@method_doubles.clear
-
object.__disallow_further_usage!
-
super
-
end
-
end
-
-
# @private
-
1
class PartialDoubleProxy < Proxy
-
1
def original_method_handle_for(message)
-
2
if any_instance_class_recorder_observing_method?(@object.class, message)
-
2
message = ::RSpec::Mocks.space.
-
any_instance_recorder_for(@object.class).
-
build_alias_method_name(message)
-
end
-
-
2
::RSpec::Support.method_handle_for(@object, message)
-
rescue NameError
-
nil
-
end
-
-
# @private
-
1
def add_simple_expectation(method_name, response, location)
-
method_double_for(method_name).configure_method
-
super
-
end
-
-
# @private
-
1
def add_simple_stub(method_name, response)
-
method_double_for(method_name).configure_method
-
super
-
end
-
-
# @private
-
1
def visibility_for(method_name)
-
# We fall back to :public because by default we allow undefined methods
-
# to be stubbed, and when we do so, we make them public.
-
4
MethodReference.method_visibility_for(@object, method_name) || :public
-
end
-
-
1
def reset
-
4
@method_doubles.each_value { |d| d.reset }
-
2
super
-
end
-
-
1
def message_received(message, *args, &block)
-
2
RSpec::Mocks.space.any_instance_recorders_from_ancestry_of(object).each do |subscriber|
-
122
subscriber.notify_received_message(object, message, args, block)
-
end
-
2
super
-
end
-
-
1
private
-
-
1
def any_instance_class_recorder_observing_method?(klass, method_name)
-
4
only_return_existing = true
-
4
recorder = ::RSpec::Mocks.space.any_instance_recorder_for(klass, only_return_existing)
-
4
return true if recorder && recorder.already_observing?(method_name)
-
-
2
superklass = klass.superclass
-
2
return false if superklass.nil?
-
2
any_instance_class_recorder_observing_method?(superklass, method_name)
-
end
-
end
-
-
# @private
-
# When we mock or stub a method on a class, we have to treat it a bit different,
-
# because normally singleton method definitions only affect the object on which
-
# they are defined, but on classes they affect subclasses, too. As a result,
-
# we need some special handling to get the original method.
-
1
module PartialClassDoubleProxyMethods
-
1
def initialize(source_space, *args)
-
@source_space = source_space
-
super(*args)
-
end
-
-
# Consider this situation:
-
#
-
# class A; end
-
# class B < A; end
-
#
-
# allow(A).to receive(:new)
-
# expect(B).to receive(:new).and_call_original
-
#
-
# When getting the original definition for `B.new`, we cannot rely purely on
-
# using `B.method(:new)` before our redefinition is defined on `B`, because
-
# `B.method(:new)` will return a method that will execute the stubbed version
-
# of the method on `A` since singleton methods on classes are in the lookup
-
# hierarchy.
-
#
-
# To do it properly, we need to find the original definition of `new` from `A`
-
# from _before_ `A` was stubbed, and we need to rebind it to `B` so that it will
-
# run with the proper `self`.
-
#
-
# That's what this method (together with `original_unbound_method_handle_from_ancestor_for`)
-
# does.
-
1
def original_method_handle_for(message)
-
unbound_method = superclass_proxy &&
-
superclass_proxy.original_unbound_method_handle_from_ancestor_for(message.to_sym)
-
-
return super unless unbound_method
-
unbound_method.bind(object)
-
end
-
-
1
protected
-
-
1
def original_unbound_method_handle_from_ancestor_for(message)
-
method_double = @method_doubles.fetch(message) do
-
# The fact that there is no method double for this message indicates
-
# that it has not been redefined by rspec-mocks. We need to continue
-
# looking up the ancestor chain.
-
return superclass_proxy &&
-
superclass_proxy.original_unbound_method_handle_from_ancestor_for(message)
-
end
-
-
method_double.original_method.unbind
-
end
-
-
1
def superclass_proxy
-
return @superclass_proxy if defined?(@superclass_proxy)
-
-
if (superclass = object.superclass)
-
@superclass_proxy = @source_space.proxy_for(superclass)
-
else
-
@superclass_proxy = nil
-
end
-
end
-
end
-
-
# @private
-
1
class PartialClassDoubleProxy < PartialDoubleProxy
-
1
include PartialClassDoubleProxyMethods
-
end
-
-
# @private
-
1
class ProxyForNil < PartialDoubleProxy
-
1
def initialize(order_group)
-
@warn_about_expectations = true
-
super(nil, order_group)
-
end
-
-
1
attr_accessor :warn_about_expectations
-
1
alias warn_about_expectations? warn_about_expectations
-
-
1
def add_message_expectation(method_name, opts={}, &block)
-
warn(method_name) if warn_about_expectations?
-
super
-
end
-
-
1
def add_negative_message_expectation(location, method_name, &implementation)
-
warn(method_name) if warn_about_expectations?
-
super
-
end
-
-
1
def add_stub(method_name, opts={}, &implementation)
-
warn(method_name) if warn_about_expectations?
-
super
-
end
-
-
1
private
-
-
1
def warn(method_name)
-
source = CallerFilter.first_non_rspec_line
-
Kernel.warn("An expectation of :#{method_name} was set on nil. Called from #{source}. Use allow_message_expectations_on_nil to disable warnings.")
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Mocks
-
# @private
-
# Provides a default space implementation for outside
-
# the scope of an example. Called "root" because it serves
-
# as the root of the space stack.
-
1
class RootSpace
-
1
def proxy_for(*_args)
-
raise_lifecycle_message
-
end
-
-
1
def any_instance_recorder_for(*_args)
-
raise_lifecycle_message
-
end
-
-
1
def any_instance_proxy_for(*_args)
-
raise_lifecycle_message
-
end
-
-
1
def register_constant_mutator(_mutator)
-
raise_lifecycle_message
-
end
-
-
1
def any_instance_recorders_from_ancestry_of(_object)
-
raise_lifecycle_message
-
end
-
-
1
def reset_all
-
end
-
-
1
def verify_all
-
end
-
-
1
def registered?(_object)
-
false
-
end
-
-
1
def new_scope
-
4
Space.new
-
end
-
-
1
private
-
-
1
def raise_lifecycle_message
-
raise OutsideOfExampleError,
-
"The use of doubles or partial doubles from rspec-mocks outside of the per-test lifecycle is not supported."
-
end
-
end
-
-
# @private
-
1
class Space
-
1
attr_reader :proxies, :any_instance_recorders, :proxy_mutex, :any_instance_mutex
-
-
1
def initialize
-
4
@proxies = {}
-
4
@any_instance_recorders = {}
-
4
@constant_mutators = []
-
4
@expectation_ordering = OrderGroup.new
-
4
@proxy_mutex = new_mutex
-
4
@any_instance_mutex = new_mutex
-
end
-
-
1
def new_scope
-
NestedSpace.new(self)
-
end
-
-
1
def verify_all
-
4
proxies.values.each { |proxy| proxy.verify }
-
64
any_instance_recorders.each_value { |recorder| recorder.verify }
-
end
-
-
1
def reset_all
-
6
proxies.each_value { |proxy| proxy.reset }
-
4
@constant_mutators.reverse.each { |mut| mut.idempotently_reset }
-
126
any_instance_recorders.each_value { |recorder| recorder.stop_all_observation! }
-
4
any_instance_recorders.clear
-
end
-
-
1
def register_constant_mutator(mutator)
-
@constant_mutators << mutator
-
end
-
-
1
def constant_mutator_for(name)
-
@constant_mutators.find { |m| m.full_constant_name == name }
-
end
-
-
1
def any_instance_recorder_for(klass, only_return_existing=false)
-
464
any_instance_mutex.synchronize do
-
464
id = klass.__id__
-
464
any_instance_recorders.fetch(id) do
-
124
return nil if only_return_existing
-
122
any_instance_recorder_not_found_for(id, klass)
-
end
-
end
-
end
-
-
1
def any_instance_proxy_for(klass)
-
2
AnyInstance::Proxy.new(any_instance_recorder_for(klass), proxies_of(klass))
-
end
-
-
1
def proxies_of(klass)
-
2
proxies.values.select { |proxy| klass === proxy.object }
-
end
-
-
1
def proxy_for(object)
-
4
proxy_mutex.synchronize do
-
4
id = id_for(object)
-
6
proxies.fetch(id) { proxy_not_found_for(id, object) }
-
end
-
end
-
-
1
alias ensure_registered proxy_for
-
-
1
def registered?(object)
-
proxies.key?(id_for object)
-
end
-
-
1
def any_instance_recorders_from_ancestry_of(object)
-
# Optimization: `any_instance` is a feature we generally
-
# recommend not using, so we can often early exit here
-
# without doing an O(N) linear search over the number of
-
# ancestors in the object's class hierarchy.
-
2
return [] if any_instance_recorders.empty?
-
-
# We access the ancestors through the singleton class, to avoid calling
-
# `class` in case `class` has been stubbed.
-
4
(class << object; ancestors; end).map do |klass|
-
130
any_instance_recorders[klass.__id__]
-
2
end.compact
-
end
-
-
1
private
-
-
# We don't want to depend on the stdlib ourselves, but if the user is
-
# using threads then a Mutex will be available to us. If not, we don't
-
# need to synchronize anyway.
-
1
def new_mutex
-
8
defined?(::Mutex) ? ::Mutex.new : FakeMutex
-
end
-
-
# @private
-
1
module FakeMutex
-
1
def self.synchronize
-
yield
-
end
-
end
-
-
1
def proxy_not_found_for(id, object)
-
2
proxies[id] = case object
-
when NilClass then ProxyForNil.new(@expectation_ordering)
-
when TestDouble then object.__build_mock_proxy_unless_expired(@expectation_ordering)
-
when Class
-
if RSpec::Mocks.configuration.verify_partial_doubles?
-
VerifyingPartialClassDoubleProxy.new(self, object, @expectation_ordering)
-
else
-
PartialClassDoubleProxy.new(self, object, @expectation_ordering)
-
end
-
else
-
2
if RSpec::Mocks.configuration.verify_partial_doubles?
-
2
VerifyingPartialDoubleProxy.new(object, @expectation_ordering)
-
else
-
PartialDoubleProxy.new(object, @expectation_ordering)
-
end
-
end
-
end
-
-
1
def any_instance_recorder_not_found_for(id, klass)
-
122
any_instance_recorders[id] = AnyInstance::Recorder.new(klass)
-
end
-
-
1
if defined?(::BasicObject) && !::BasicObject.method_defined?(:__id__) # for 1.9.2
-
require 'securerandom'
-
-
def id_for(object)
-
id = object.__id__
-
-
return id if object.equal?(::ObjectSpace._id2ref(id))
-
# this suggests that object.__id__ is proxying through to some wrapped object
-
-
object.instance_exec do
-
@__id_for_rspec_mocks_space ||= ::SecureRandom.uuid
-
end
-
end
-
else
-
1
def id_for(object)
-
4
object.__id__
-
end
-
end
-
end
-
-
# @private
-
1
class NestedSpace < Space
-
1
def initialize(parent)
-
@parent = parent
-
super()
-
end
-
-
1
def proxies_of(klass)
-
super + @parent.proxies_of(klass)
-
end
-
-
1
def constant_mutator_for(name)
-
super || @parent.constant_mutator_for(name)
-
end
-
-
1
def registered?(object)
-
super || @parent.registered?(object)
-
end
-
-
1
private
-
-
1
def proxy_not_found_for(id, object)
-
@parent.proxies[id] || super
-
end
-
-
1
def any_instance_recorder_not_found_for(id, klass)
-
@parent.any_instance_recorders[id] || super
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Mocks
-
# @api private
-
# Provides methods for enabling and disabling the available syntaxes
-
# provided by rspec-mocks.
-
1
module Syntax
-
# @private
-
1
def self.warn_about_should!
-
1
@warn_about_should = true
-
end
-
-
# @private
-
1
def self.warn_unless_should_configured(method_name , replacement="the new `:expect` syntax or explicitly enable `:should`")
-
if @warn_about_should
-
RSpec.deprecate(
-
"Using `#{method_name}` from rspec-mocks' old `:should` syntax without explicitly enabling the syntax",
-
:replacement => replacement
-
)
-
-
@warn_about_should = false
-
end
-
end
-
-
# @api private
-
# Enables the should syntax (`dbl.stub`, `dbl.should_receive`, etc).
-
1
def self.enable_should(syntax_host=default_should_syntax_host)
-
1
@warn_about_should = false if syntax_host == default_should_syntax_host
-
1
return if should_enabled?(syntax_host)
-
-
1
syntax_host.class_exec do
-
1
def should_receive(message, opts={}, &block)
-
::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__)
-
::RSpec::Mocks.expect_message(self, message, opts, &block)
-
end
-
-
1
def should_not_receive(message, &block)
-
::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__)
-
::RSpec::Mocks.expect_message(self, message, {}, &block).never
-
end
-
-
1
def stub(message_or_hash, opts={}, &block)
-
::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__)
-
if ::Hash === message_or_hash
-
message_or_hash.each { |message, value| stub(message).and_return value }
-
else
-
::RSpec::Mocks.allow_message(self, message_or_hash, opts, &block)
-
end
-
end
-
-
1
def unstub(message)
-
::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__, "`allow(...).to_receive(...).and_call_original` or explicitly enable `:should`")
-
::RSpec::Mocks.space.proxy_for(self).remove_stub(message)
-
end
-
-
1
def stub_chain(*chain, &blk)
-
::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__)
-
::RSpec::Mocks::StubChain.stub_chain_on(self, *chain, &blk)
-
end
-
-
1
def as_null_object
-
::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__)
-
@_null_object = true
-
::RSpec::Mocks.space.proxy_for(self).as_null_object
-
end
-
-
1
def null_object?
-
::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__)
-
defined?(@_null_object)
-
end
-
-
1
def received_message?(message, *args, &block)
-
::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__)
-
::RSpec::Mocks.space.proxy_for(self).received_message?(message, *args, &block)
-
end
-
-
1
unless Class.respond_to? :any_instance
-
1
Class.class_exec do
-
1
def any_instance
-
::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__)
-
::RSpec::Mocks.space.any_instance_proxy_for(self)
-
end
-
end
-
end
-
end
-
end
-
-
# @api private
-
# Disables the should syntax (`dbl.stub`, `dbl.should_receive`, etc).
-
1
def self.disable_should(syntax_host=default_should_syntax_host)
-
return unless should_enabled?(syntax_host)
-
-
syntax_host.class_exec do
-
undef should_receive
-
undef should_not_receive
-
undef stub
-
undef unstub
-
undef stub_chain
-
undef as_null_object
-
undef null_object?
-
undef received_message?
-
end
-
-
Class.class_exec do
-
undef any_instance
-
end
-
end
-
-
# @api private
-
# Enables the expect syntax (`expect(dbl).to receive`, `allow(dbl).to receive`, etc).
-
1
def self.enable_expect(syntax_host=::RSpec::Mocks::ExampleMethods)
-
1
return if expect_enabled?(syntax_host)
-
-
1
syntax_host.class_exec do
-
1
def receive(method_name, &block)
-
2
Matchers::Receive.new(method_name, block)
-
end
-
-
1
def receive_messages(message_return_value_hash)
-
matcher = Matchers::ReceiveMessages.new(message_return_value_hash)
-
matcher.warn_about_block if block_given?
-
matcher
-
end
-
-
1
def receive_message_chain(*messages, &block)
-
Matchers::ReceiveMessageChain.new(messages, &block)
-
end
-
-
1
def allow(target)
-
AllowanceTarget.new(target)
-
end
-
-
1
def expect_any_instance_of(klass)
-
AnyInstanceExpectationTarget.new(klass)
-
end
-
-
1
def allow_any_instance_of(klass)
-
2
AnyInstanceAllowanceTarget.new(klass)
-
end
-
end
-
-
1
RSpec::Mocks::ExampleMethods::ExpectHost.class_exec do
-
1
def expect(target)
-
ExpectationTarget.new(target)
-
end
-
end
-
end
-
-
# @api private
-
# Disables the expect syntax (`expect(dbl).to receive`, `allow(dbl).to receive`, etc).
-
1
def self.disable_expect(syntax_host=::RSpec::Mocks::ExampleMethods)
-
return unless expect_enabled?(syntax_host)
-
-
syntax_host.class_exec do
-
undef receive
-
undef receive_messages
-
undef receive_message_chain
-
undef allow
-
undef expect_any_instance_of
-
undef allow_any_instance_of
-
end
-
-
RSpec::Mocks::ExampleMethods::ExpectHost.class_exec do
-
undef expect
-
end
-
end
-
-
# @api private
-
# Indicates whether or not the should syntax is enabled.
-
1
def self.should_enabled?(syntax_host=default_should_syntax_host)
-
1
syntax_host.method_defined?(:should_receive)
-
end
-
-
# @api private
-
# Indicates whether or not the expect syntax is enabled.
-
1
def self.expect_enabled?(syntax_host=::RSpec::Mocks::ExampleMethods)
-
1
syntax_host.method_defined?(:allow)
-
end
-
-
# @api private
-
# Determines where the methods like `should_receive`, and `stub` are added.
-
1
def self.default_should_syntax_host
-
# JRuby 1.7.4 introduces a regression whereby `defined?(::BasicObject) => nil`
-
# yet `BasicObject` still exists and patching onto ::Object breaks things
-
# e.g. SimpleDelegator expectations won't work
-
#
-
# See: https://github.com/jruby/jruby/issues/814
-
2
if defined?(JRUBY_VERSION) && JRUBY_VERSION == '1.7.4' && RUBY_VERSION.to_f > 1.8
-
return ::BasicObject
-
end
-
-
# On 1.8.7, Object.ancestors.last == Kernel but
-
# things blow up if we include `RSpec::Mocks::Methods`
-
# into Kernel...not sure why.
-
2
return Object unless defined?(::BasicObject)
-
-
# MacRuby has BasicObject but it's not the root class.
-
2
return Object unless Object.ancestors.last == ::BasicObject
-
-
2
::BasicObject
-
end
-
end
-
end
-
end
-
-
1
if defined?(BasicObject)
-
# The legacy `:should` syntax adds the following methods directly to
-
# `BasicObject` so that they are available off of any object. Note, however,
-
# that this syntax does not always play nice with delegate/proxy objects.
-
# We recommend you use the non-monkeypatching `:expect` syntax instead.
-
# @see Class
-
1
class BasicObject
-
# @method should_receive
-
# Sets an expectation that this object should receive a message before
-
# the end of the example.
-
#
-
# @example
-
#
-
# logger = double('logger')
-
# thing_that_logs = ThingThatLogs.new(logger)
-
# logger.should_receive(:log)
-
# thing_that_logs.do_something_that_logs_a_message
-
#
-
# @note This is only available when you have enabled the `should` syntax.
-
# @see RSpec::Mocks::ExampleMethods#expect
-
-
# @method should_not_receive
-
# Sets and expectation that this object should _not_ receive a message
-
# during this example.
-
# @see RSpec::Mocks::ExampleMethods#expect
-
-
# @method stub
-
# Tells the object to respond to the message with the specified value.
-
#
-
# @example
-
#
-
# counter.stub(:count).and_return(37)
-
# counter.stub(:count => 37)
-
# counter.stub(:count) { 37 }
-
#
-
# @note This is only available when you have enabled the `should` syntax.
-
# @see RSpec::Mocks::ExampleMethods#allow
-
-
# @method unstub
-
# Removes a stub. On a double, the object will no longer respond to
-
# `message`. On a real object, the original method (if it exists) is
-
# restored.
-
#
-
# This is rarely used, but can be useful when a stub is set up during a
-
# shared `before` hook for the common case, but you want to replace it
-
# for a special case.
-
#
-
# @note This is only available when you have enabled the `should` syntax.
-
-
# @method stub_chain
-
# @overload stub_chain(method1, method2)
-
# @overload stub_chain("method1.method2")
-
# @overload stub_chain(method1, method_to_value_hash)
-
#
-
# Stubs a chain of methods.
-
#
-
# ## Warning:
-
#
-
# Chains can be arbitrarily long, which makes it quite painless to
-
# violate the Law of Demeter in violent ways, so you should consider any
-
# use of `stub_chain` a code smell. Even though not all code smells
-
# indicate real problems (think fluent interfaces), `stub_chain` still
-
# results in brittle examples. For example, if you write
-
# `foo.stub_chain(:bar, :baz => 37)` in a spec and then the
-
# implementation calls `foo.baz.bar`, the stub will not work.
-
#
-
# @example
-
#
-
# double.stub_chain("foo.bar") { :baz }
-
# double.stub_chain(:foo, :bar => :baz)
-
# double.stub_chain(:foo, :bar) { :baz }
-
#
-
# # Given any of ^^ these three forms ^^:
-
# double.foo.bar # => :baz
-
#
-
# # Common use in Rails/ActiveRecord:
-
# Article.stub_chain("recent.published") { [Article.new] }
-
#
-
# @note This is only available when you have enabled the `should` syntax.
-
# @see RSpec::Mocks::ExampleMethods#receive_message_chain
-
-
# @method as_null_object
-
# Tells the object to respond to all messages. If specific stub values
-
# are declared, they'll work as expected. If not, the receiver is
-
# returned.
-
#
-
# @note This is only available when you have enabled the `should` syntax.
-
-
# @method null_object?
-
# Returns true if this object has received `as_null_object`
-
#
-
# @note This is only available when you have enabled the `should` syntax.
-
end
-
end
-
-
# The legacy `:should` syntax adds the `any_instance` to `Class`.
-
# We generally recommend you use the newer `:expect` syntax instead,
-
# which allows you to stub any instance of a class using
-
# `allow_any_instance_of(klass)` or mock any instance using
-
# `expect_any_instance_of(klass)`.
-
# @see BasicObject
-
1
class Class
-
# @method any_instance
-
# Used to set stubs and message expectations on any instance of a given
-
# class. Returns a [Recorder](Recorder), which records messages like
-
# `stub` and `should_receive` for later playback on instances of the
-
# class.
-
#
-
# @example
-
#
-
# Car.any_instance.should_receive(:go)
-
# race = Race.new
-
# race.cars << Car.new
-
# race.go # assuming this delegates to all of its cars
-
# # this example would pass
-
#
-
# Account.any_instance.stub(:balance) { Money.new(:USD, 25) }
-
# Account.new.balance # => Money.new(:USD, 25))
-
#
-
# @return [Recorder]
-
#
-
# @note This is only available when you have enabled the `should` syntax.
-
# @see RSpec::Mocks::ExampleMethods#expect_any_instance_of
-
# @see RSpec::Mocks::ExampleMethods#allow_any_instance_of
-
end
-
1
module RSpec
-
1
module Mocks
-
# @private
-
1
class TargetBase
-
1
def initialize(target)
-
2
@target = target
-
end
-
-
1
def self.delegate_to(matcher_method)
-
4
define_method(:to) do |matcher, &block|
-
2
unless matcher_allowed?(matcher)
-
raise_unsupported_matcher(:to, matcher)
-
end
-
2
define_matcher(matcher, matcher_method, &block)
-
end
-
end
-
-
1
def self.delegate_not_to(matcher_method, options={})
-
4
method_name = options.fetch(:from)
-
4
define_method(method_name) do |matcher, &block|
-
case matcher
-
when Matchers::Receive
-
define_matcher(matcher, matcher_method, &block)
-
when Matchers::ReceiveMessages, Matchers::ReceiveMessageChain
-
raise_negation_unsupported(method_name, matcher)
-
else
-
raise_unsupported_matcher(method_name, matcher)
-
end
-
end
-
end
-
-
1
def self.disallow_negation(method_name)
-
4
define_method(method_name) do |matcher, *_args|
-
raise_negation_unsupported(method_name, matcher)
-
end
-
end
-
-
1
private
-
-
1
def matcher_allowed?(matcher)
-
2
matcher.class.name.start_with?("RSpec::Mocks::Matchers".freeze)
-
end
-
-
1
def define_matcher(matcher, name, &block)
-
2
matcher.__send__(name, @target, &block)
-
end
-
-
1
def raise_unsupported_matcher(method_name, matcher)
-
raise UnsupportedMatcherError,
-
"only the `receive` or `receive_messages` matchers are supported " \
-
"with `#{expression}(...).#{method_name}`, but you have provided: #{matcher}"
-
end
-
-
1
def raise_negation_unsupported(method_name, matcher)
-
raise NegationUnsupportedError,
-
"`#{expression}(...).#{method_name} #{matcher.name}` is not supported since it " \
-
"doesn't really make sense. What would it even mean?"
-
end
-
-
1
def expression
-
self.class::EXPRESSION
-
end
-
end
-
-
# @private
-
1
class AllowanceTarget < TargetBase
-
1
EXPRESSION = :allow
-
1
delegate_to :setup_allowance
-
1
disallow_negation :not_to
-
1
disallow_negation :to_not
-
end
-
-
# @private
-
1
class ExpectationTarget < TargetBase
-
1
EXPRESSION = :expect
-
1
delegate_to :setup_expectation
-
1
delegate_not_to :setup_negative_expectation, :from => :not_to
-
1
delegate_not_to :setup_negative_expectation, :from => :to_not
-
end
-
-
# @private
-
1
class AnyInstanceAllowanceTarget < TargetBase
-
1
EXPRESSION = :allow_any_instance_of
-
1
delegate_to :setup_any_instance_allowance
-
1
disallow_negation :not_to
-
1
disallow_negation :to_not
-
end
-
-
# @private
-
1
class AnyInstanceExpectationTarget < TargetBase
-
1
EXPRESSION = :expect_any_instance_of
-
1
delegate_to :setup_any_instance_expectation
-
1
delegate_not_to :setup_any_instance_negative_expectation, :from => :not_to
-
1
delegate_not_to :setup_any_instance_negative_expectation, :from => :to_not
-
end
-
end
-
end
-
1
module RSpec
-
1
module Mocks
-
# Implements the methods needed for a pure test double. RSpec::Mocks::Double
-
# includes this module, and it is provided for cases where you want a
-
# pure test double without subclassing RSpec::Mocks::Double.
-
1
module TestDouble
-
# Creates a new test double with a `name` (that will be used in error
-
# messages only)
-
1
def initialize(name=nil, stubs={})
-
@__expired = false
-
if Hash === name && stubs.empty?
-
stubs = name
-
@name = nil
-
else
-
@name = name
-
end
-
assign_stubs(stubs)
-
end
-
-
# Tells the object to respond to all messages. If specific stub values
-
# are declared, they'll work as expected. If not, the receiver is
-
# returned.
-
1
def as_null_object
-
__mock_proxy.as_null_object
-
end
-
-
# Returns true if this object has received `as_null_object`
-
1
def null_object?
-
__mock_proxy.null_object?
-
end
-
-
# This allows for comparing the mock to other objects that proxy such as
-
# ActiveRecords belongs_to proxy objects. By making the other object run
-
# the comparison, we're sure the call gets delegated to the proxy
-
# target.
-
1
def ==(other)
-
other == __mock_proxy
-
end
-
-
# @private
-
1
def inspect
-
"#<#{self.class}:#{'0x%x' % object_id} @name=#{@name.inspect}>"
-
end
-
-
# @private
-
1
def to_s
-
inspect.gsub('<', '[').gsub('>', ']')
-
end
-
-
# @private
-
1
def respond_to?(message, incl_private=false)
-
__mock_proxy.null_object? ? true : super
-
end
-
-
# @private
-
1
def __build_mock_proxy_unless_expired(order_group)
-
__raise_expired_error || __build_mock_proxy(order_group)
-
end
-
-
# @private
-
1
def __disallow_further_usage!
-
@__expired = true
-
end
-
-
# Override for default freeze implementation to prevent freezing of test
-
# doubles.
-
1
def freeze
-
RSpec.warn_with("WARNING: you attempted to freeze a test double. This is explicitly a no-op as freezing doubles can lead to undesired behaviour when resetting tests.")
-
end
-
-
1
private
-
-
1
def method_missing(message, *args, &block)
-
proxy = __mock_proxy
-
proxy.record_message_received(message, *args, &block)
-
-
if proxy.null_object?
-
case message
-
when :to_int then return 0
-
when :to_a, :to_ary then return nil
-
when :to_str then return to_s
-
else return self
-
end
-
end
-
-
# Defined private and protected methods will still trigger `method_missing`
-
# when called publicly. We want ruby's method visibility error to get raised,
-
# so we simply delegate to `super` in that case.
-
# ...well, we would delegate to `super`, but there's a JRuby
-
# bug, so we raise our own visibility error instead:
-
# https://github.com/jruby/jruby/issues/1398
-
visibility = proxy.visibility_for(message)
-
if visibility == :private || visibility == :protected
-
ErrorGenerator.new(self, @name).raise_non_public_error(
-
message, visibility
-
)
-
end
-
-
# Required wrapping doubles in an Array on Ruby 1.9.2
-
raise NoMethodError if [:to_a, :to_ary].include? message
-
proxy.raise_unexpected_message_error(message, *args)
-
end
-
-
1
def assign_stubs(stubs)
-
stubs.each_pair do |message, response|
-
__mock_proxy.add_simple_stub(message, response)
-
end
-
end
-
-
1
def __mock_proxy
-
::RSpec::Mocks.space.proxy_for(self)
-
end
-
-
1
def __build_mock_proxy(order_group)
-
TestDoubleProxy.new(self, order_group, @name)
-
end
-
-
1
def __raise_expired_error
-
return false unless @__expired
-
ErrorGenerator.new(self, @name).raise_expired_test_double_error
-
end
-
-
1
def initialize_copy(other)
-
as_null_object if other.null_object?
-
super
-
end
-
end
-
-
# A generic test double object. `double`, `instance_double` and friends
-
# return an instance of this.
-
1
class Double
-
1
include TestDouble
-
end
-
end
-
end
-
1
RSpec::Support.require_rspec_mocks 'verifying_proxy'
-
1
require 'stringio'
-
-
1
module RSpec
-
1
module Mocks
-
# @private
-
1
module VerifyingDouble
-
1
def respond_to?(message, include_private=false)
-
return super unless null_object?
-
-
method_ref = __mock_proxy.method_reference[message]
-
-
case method_ref.visibility
-
when :public then true
-
when :private then include_private
-
when :protected then include_private || RUBY_VERSION.to_f < 2.0
-
else !method_ref.unimplemented?
-
end
-
end
-
-
1
def method_missing(message, *args, &block)
-
# Null object conditional is an optimization. If not a null object,
-
# validity of method expectations will have been checked at definition
-
# time.
-
if null_object?
-
if @__sending_message == message
-
__mock_proxy.ensure_implemented(message)
-
else
-
__mock_proxy.ensure_publicly_implemented(message, self)
-
end
-
end
-
-
super
-
end
-
-
# Redefining `__send__` causes ruby to issue a warning.
-
1
old, $stderr = $stderr, StringIO.new
-
1
def __send__(name, *args, &block)
-
@__sending_message = name
-
super
-
ensure
-
@__sending_message = nil
-
end
-
1
$stderr = old
-
-
1
def send(name, *args, &block)
-
__send__(name, *args, &block)
-
end
-
-
1
def initialize(*args)
-
super
-
@__sending_message = nil
-
end
-
end
-
-
# A mock providing a custom proxy that can verify the validity of any
-
# method stubs or expectations against the public instance methods of the
-
# given class.
-
#
-
# @private
-
1
class InstanceVerifyingDouble
-
1
include TestDouble
-
1
include VerifyingDouble
-
-
1
def initialize(doubled_module, *args)
-
@doubled_module = doubled_module
-
-
super(
-
"#{doubled_module.description} (instance)",
-
*args
-
)
-
end
-
-
1
def __build_mock_proxy(order_group)
-
VerifyingProxy.new(self, order_group, @name,
-
@doubled_module,
-
InstanceMethodReference
-
)
-
end
-
end
-
-
# An awkward module necessary because we cannot otherwise have
-
# ClassVerifyingDouble inherit from Module and still share these methods.
-
#
-
# @private
-
1
module ObjectVerifyingDoubleMethods
-
1
include TestDouble
-
1
include VerifyingDouble
-
-
1
def as_stubbed_const(options={})
-
ConstantMutator.stub(@doubled_module.const_to_replace, self, options)
-
self
-
end
-
-
1
private
-
-
1
def initialize(doubled_module, *args)
-
@doubled_module = doubled_module
-
super(doubled_module.description, *args)
-
end
-
-
1
def __build_mock_proxy(order_group)
-
VerifyingProxy.new(self, order_group, @name,
-
@doubled_module,
-
ObjectMethodReference
-
)
-
end
-
end
-
-
# Similar to an InstanceVerifyingDouble, except that it verifies against
-
# public methods of the given object.
-
#
-
# @private
-
1
class ObjectVerifyingDouble
-
1
include ObjectVerifyingDoubleMethods
-
end
-
-
# Effectively the same as an ObjectVerifyingDouble (since a class is a type
-
# of object), except with Module in the inheritance chain so that
-
# transferring nested constants to work.
-
#
-
# @private
-
1
class ClassVerifyingDouble < Module
-
1
include ObjectVerifyingDoubleMethods
-
end
-
end
-
end
-
1
RSpec::Support.require_rspec_support 'method_signature_verifier'
-
-
1
module RSpec
-
1
module Mocks
-
# A message expectation that knows about the real implementation of the
-
# message being expected, so that it can verify that any expectations
-
# have the valid arguments.
-
# @api private
-
1
class VerifyingMessageExpectation < MessageExpectation
-
# A level of indirection is used here rather than just passing in the
-
# method itself, since method look up is expensive and we only want to
-
# do it if actually needed.
-
#
-
# Conceptually the method reference makes more sense as a constructor
-
# argument since it should be immutable, but it is significantly more
-
# straight forward to build the object in pieces so for now it stays as
-
# an accessor.
-
1
attr_accessor :method_reference
-
-
1
def initialize(*args)
-
2
super
-
end
-
-
# @private
-
1
def with(*args, &block)
-
unless ArgumentMatchers::AnyArgsMatcher === args.first
-
expected_args = if ArgumentMatchers::NoArgsMatcher === args.first
-
[]
-
elsif args.length > 0
-
args
-
else
-
# No arguments given, this will raise.
-
super
-
end
-
-
validate_expected_arguments!(expected_args)
-
end
-
super
-
end
-
-
1
private
-
-
1
def validate_expected_arguments!(actual_args)
-
return if method_reference.nil?
-
-
method_reference.with_signature do |signature|
-
verifier = Support::LooseSignatureVerifier.new(
-
signature,
-
actual_args
-
)
-
-
unless verifier.valid?
-
# Fail fast is required, otherwise the message expecation will fail
-
# as well ("expected method not called") and clobber this one.
-
@failed_fast = true
-
@error_generator.raise_invalid_arguments_error(verifier)
-
end
-
end
-
end
-
end
-
end
-
end
-
1
RSpec::Support.require_rspec_mocks 'verifying_message_expecation'
-
1
RSpec::Support.require_rspec_mocks 'method_reference'
-
-
1
module RSpec
-
1
module Mocks
-
# @private
-
1
module VerifyingProxyMethods
-
1
def add_stub(method_name, opts={}, &implementation)
-
2
ensure_implemented(method_name)
-
2
super
-
end
-
-
1
def add_simple_stub(method_name, *args)
-
ensure_implemented(method_name)
-
super
-
end
-
-
1
def add_message_expectation(method_name, opts={}, &block)
-
ensure_implemented(method_name)
-
super
-
end
-
-
1
def ensure_implemented(method_name)
-
2
return unless method_reference[method_name].unimplemented?
-
-
@error_generator.raise_unimplemented_error(
-
@doubled_module,
-
method_name
-
)
-
end
-
-
1
def ensure_publicly_implemented(method_name, _object)
-
ensure_implemented(method_name)
-
visibility = method_reference[method_name].visibility
-
-
return if visibility == :public
-
@error_generator.raise_non_public_error(method_name, visibility)
-
end
-
end
-
-
# A verifying proxy mostly acts like a normal proxy, except that it
-
# contains extra logic to try and determine the validity of any expectation
-
# set on it. This includes whether or not methods have been defined and the
-
# validatiy of arguments on method calls.
-
#
-
# In all other ways this behaves like a normal proxy. It only adds the
-
# verification behaviour to specific methods then delegates to the parent
-
# implementation.
-
#
-
# These checks are only activated if the doubled class has already been
-
# loaded, otherwise they are disabled. This allows for testing in
-
# isolation.
-
#
-
# @private
-
1
class VerifyingProxy < TestDoubleProxy
-
1
include VerifyingProxyMethods
-
-
1
def initialize(object, order_group, name, doubled_module, method_reference_class)
-
super(object, order_group, name)
-
@object = object
-
@doubled_module = doubled_module
-
@method_reference_class = method_reference_class
-
-
# A custom method double is required to pass through a way to lookup
-
# methods to determine their parameters. This is only relevant if the doubled
-
# class is loaded.
-
@method_doubles = Hash.new do |h, k|
-
h[k] = VerifyingMethodDouble.new(@object, k, self, method_reference[k])
-
end
-
end
-
-
1
def method_reference
-
@method_reference ||= Hash.new do |h, k|
-
h[k] = @method_reference_class.new(@doubled_module, k)
-
end
-
end
-
-
1
def visibility_for(method_name)
-
method_reference[method_name].visibility
-
end
-
end
-
-
# @private
-
1
class VerifyingPartialDoubleProxy < PartialDoubleProxy
-
1
include VerifyingProxyMethods
-
-
1
def initialize(object, expectation_ordering)
-
2
super(object, expectation_ordering)
-
2
@doubled_module = DirectObjectReference.new(object)
-
-
# A custom method double is required to pass through a way to lookup
-
# methods to determine their parameters.
-
2
@method_doubles = Hash.new do |h, k|
-
2
h[k] = VerifyingExistingMethodDouble.new(object, k, self)
-
end
-
end
-
-
1
def method_reference
-
2
@method_doubles
-
end
-
end
-
-
# @private
-
1
class VerifyingPartialClassDoubleProxy < VerifyingPartialDoubleProxy
-
1
include PartialClassDoubleProxyMethods
-
end
-
-
# @private
-
1
class VerifyingMethodDouble < MethodDouble
-
1
def initialize(object, method_name, proxy, method_reference)
-
2
super(object, method_name, proxy)
-
2
@method_reference = method_reference
-
end
-
-
1
def message_expectation_class
-
2
VerifyingMessageExpectation
-
end
-
-
1
def add_expectation(*args, &block)
-
# explict params necessary for 1.8.7 see #626
-
super(*args, &block).tap { |x| x.method_reference = @method_reference }
-
end
-
-
1
def proxy_method_invoked(obj, *args, &block)
-
2
validate_arguments!(args)
-
2
super
-
end
-
-
1
private
-
-
1
def validate_arguments!(actual_args)
-
2
@method_reference.with_signature do |signature|
-
2
verifier = Support::StrictSignatureVerifier.new(signature, actual_args)
-
2
raise ArgumentError, verifier.error_message unless verifier.valid?
-
end
-
end
-
end
-
-
# A VerifyingMethodDouble fetches the method to verify against from the
-
# original object, using a MethodReference. This works for pure doubles,
-
# but when the original object is itself the one being modified we need to
-
# collapse the reference and the method double into a single object so that
-
# we can access the original pristine method definition.
-
#
-
# @private
-
1
class VerifyingExistingMethodDouble < VerifyingMethodDouble
-
1
def initialize(object, method_name, proxy)
-
2
super(object, method_name, proxy, self)
-
-
2
@valid_method = object.respond_to?(method_name, true)
-
-
# Trigger an eager find of the original method since if we find it any
-
# later we end up getting a stubbed method with incorrect arity.
-
2
save_original_method!
-
end
-
-
1
def with_signature
-
2
yield Support::MethodSignature.new(original_method)
-
end
-
-
1
def unimplemented?
-
2
!@valid_method
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Mocks
-
# Version information for RSpec mocks.
-
1
module Version
-
# Version of RSpec mocks currently in use in SemVer format.
-
1
STRING = '3.1.2'
-
end
-
end
-
end
-
# Namespace for all core RSpec projects.
-
1
module RSpec
-
# Namespace for rspec-rails code.
-
1
module Rails
-
# Railtie to hook into Rails.
-
1
class Railtie < ::Rails::Railtie
-
# Rails-3.0.1 requires config.app_generators instead of 3.0.0's config.generators
-
1
generators = config.respond_to?(:app_generators) ? config.app_generators : config.generators
-
1
generators.integration_tool :rspec
-
1
generators.test_framework :rspec
-
-
1
rake_tasks do
-
load "rspec/rails/tasks/rspec.rake"
-
end
-
end
-
end
-
end
-
1
require 'rspec/core'
-
1
require 'rails/version'
-
1
require 'rspec/rails/extensions'
-
1
require 'rspec/rails/view_rendering'
-
1
require 'rspec/rails/adapters'
-
1
require 'rspec/rails/matchers'
-
1
require 'rspec/rails/fixture_support'
-
1
require 'rspec/rails/example'
-
1
require 'rspec/rails/vendor/capybara'
-
1
require 'rspec/rails/configuration'
-
1
require 'rspec/rails/feature_check'
-
1
module RSpec
-
1
module Rails
-
# Fake class to document RSpec Rails configuration options. In practice,
-
# these are dynamically added to the normal RSpec configuration object.
-
1
class Configuration
-
# @!method infer_spec_type_from_file_location!
-
# Automatically tag specs in conventional directories with matching `type`
-
# metadata so that they have relevant helpers available to them. See
-
# `RSpec::Rails::DIRECTORY_MAPPINGS` for details on which metadata is
-
# applied to each directory.
-
-
# @!method render_views=(val)
-
#
-
# When set to `true`, controller specs will render the relevant view as
-
# well. Defaults to `false`.
-
-
# @!method render_views(val)
-
# Enables view rendering for controllers specs.
-
-
# @!method render_views?
-
# Reader for currently value of `render_views` setting.
-
end
-
-
# Mappings used by `infer_spec_type_from_file_location!`.
-
#
-
# @api private
-
1
DIRECTORY_MAPPINGS = {
-
:controller => %w[spec controllers],
-
:helper => %w[spec helpers],
-
:mailer => %w[spec mailers],
-
:model => %w[spec models],
-
:request => %w[spec (requests|integration|api)],
-
:routing => %w[spec routing],
-
:view => %w[spec views],
-
:feature => %w[spec features]
-
}
-
-
# @private
-
1
def self.initialize_configuration(config)
-
1
config.backtrace_exclusion_patterns << /vendor\//
-
1
config.backtrace_exclusion_patterns << %r{ lib/rspec/rails }
-
-
1
config.include RSpec::Rails::ControllerExampleGroup, :type => :controller
-
1
config.include RSpec::Rails::HelperExampleGroup, :type => :helper
-
1
config.include RSpec::Rails::ModelExampleGroup, :type => :model
-
1
config.include RSpec::Rails::RequestExampleGroup, :type => :request
-
1
config.include RSpec::Rails::RoutingExampleGroup, :type => :routing
-
1
config.include RSpec::Rails::ViewExampleGroup, :type => :view
-
1
config.include RSpec::Rails::FeatureExampleGroup, :type => :feature
-
-
1
if defined?(ActionMailer)
-
1
config.include RSpec::Rails::MailerExampleGroup, :type => :mailer
-
end
-
-
# controller settings
-
1
config.add_setting :infer_base_class_for_anonymous_controllers, :default => true
-
-
# fixture support
-
1
config.include RSpec::Rails::FixtureSupport
-
1
config.add_setting :use_transactional_fixtures, :alias_with => :use_transactional_examples
-
1
config.add_setting :use_instantiated_fixtures
-
1
config.add_setting :global_fixtures
-
1
config.add_setting :fixture_path
-
-
# This allows us to expose `render_views` as a config option even though it
-
# breaks the convention of other options by using `render_views` as a
-
# command (i.e. `render_views = true`), where it would normally be used
-
# as a getter. This makes it easier for rspec-rails users because we use
-
# `render_views` directly in example groups, so this aligns the two APIs,
-
# but requires this workaround:
-
1
config.add_setting :rendering_views, :default => false
-
-
1
def config.render_views=(val)
-
self.rendering_views = val
-
end
-
-
1
def config.render_views
-
self.rendering_views = true
-
end
-
-
1
def config.render_views?
-
8
rendering_views
-
end
-
-
1
def config.infer_spec_type_from_file_location!
-
1
DIRECTORY_MAPPINGS.each do |type, dir_parts|
-
8
escaped_path = Regexp.compile(dir_parts.join('[\\\/]') + '[\\\/]')
-
8
define_derived_metadata(:file_path => escaped_path) do |metadata|
-
11
metadata[:type] ||= type
-
end
-
end
-
end
-
end
-
-
1
initialize_configuration RSpec.configuration
-
end
-
end
-
1
require 'rspec/rails/example/rails_example_group'
-
1
require 'rspec/rails/example/controller_example_group'
-
1
require 'rspec/rails/example/request_example_group'
-
1
require 'rspec/rails/example/helper_example_group'
-
1
require 'rspec/rails/example/view_example_group'
-
1
require 'rspec/rails/example/mailer_example_group'
-
1
require 'rspec/rails/example/routing_example_group'
-
1
require 'rspec/rails/example/model_example_group'
-
1
require 'rspec/rails/example/feature_example_group'
-
1
module RSpec
-
1
module Rails
-
# Container module for controller spec functionality.
-
1
module ControllerExampleGroup
-
1
extend ActiveSupport::Concern
-
1
include RSpec::Rails::RailsExampleGroup
-
1
include ActionController::TestCase::Behavior
-
1
include RSpec::Rails::ViewRendering
-
1
include RSpec::Rails::Matchers::RedirectTo
-
1
include RSpec::Rails::Matchers::RenderTemplate
-
1
include RSpec::Rails::Matchers::RoutingMatchers
-
1
include RSpec::Rails::AssertionDelegator.new(ActionDispatch::Assertions::RoutingAssertions)
-
-
# Class-level DSL for controller specs.
-
1
module ClassMethods
-
# @private
-
1
def controller_class
-
4
described_class
-
end
-
-
# Supports a simple DSL for specifying behavior of ApplicationController.
-
# Creates an anonymous subclass of ApplicationController and evals the
-
# `body` in that context. Also sets up implicit routes for this
-
# controller, that are separate from those defined in "config/routes.rb".
-
#
-
# @note Due to Ruby 1.8 scoping rules in anonymous subclasses, constants
-
# defined in `ApplicationController` must be fully qualified (e.g.
-
# `ApplicationController::AccessDenied`) in the block passed to the
-
# `controller` method. Any instance methods, filters, etc, that are
-
# defined in `ApplicationController`, however, are accessible from
-
# within the block.
-
#
-
# @example
-
# describe ApplicationController do
-
# controller do
-
# def index
-
# raise ApplicationController::AccessDenied
-
# end
-
# end
-
#
-
# describe "handling AccessDenied exceptions" do
-
# it "redirects to the /401.html page" do
-
# get :index
-
# response.should redirect_to("/401.html")
-
# end
-
# end
-
# end
-
#
-
# If you would like to spec a subclass of ApplicationController, call
-
# controller like so:
-
#
-
# controller(ApplicationControllerSubclass) do
-
# # ....
-
# end
-
1
def controller(base_class = nil, &body)
-
if RSpec.configuration.infer_base_class_for_anonymous_controllers?
-
base_class ||= controller_class
-
end
-
base_class ||= defined?(ApplicationController) ? ApplicationController : ActionController::Base
-
-
new_controller_class = Class.new(base_class) do
-
def self.name
-
root_controller = defined?(ApplicationController) ? ApplicationController : ActionController::Base
-
if superclass == root_controller || superclass.abstract?
-
"AnonymousController"
-
else
-
superclass.to_s
-
end
-
end
-
end
-
new_controller_class.class_exec(&body)
-
(class << self; self; end).__send__(:define_method, :controller_class) { new_controller_class }
-
-
before do
-
@orig_routes = routes
-
resource_name = if @controller.respond_to?(:controller_name)
-
@controller.controller_name.to_sym
-
else
-
:anonymous
-
end
-
resource_path = if @controller.respond_to?(:controller_path)
-
@controller.controller_path
-
else
-
resource_name.to_s
-
end
-
resource_module = resource_path.rpartition('/').first.presence
-
resource_as = 'anonymous_' + resource_path.tr('/', '_')
-
self.routes = ActionDispatch::Routing::RouteSet.new.tap do |r|
-
r.draw do
-
resources resource_name,
-
:as => resource_as,
-
:module => resource_module,
-
:path => resource_path
-
end
-
end
-
end
-
-
after do
-
self.routes = @orig_routes
-
@orig_routes = nil
-
end
-
end
-
-
# Specifies the routeset that will be used for the example group. This
-
# is most useful when testing Rails engines.
-
#
-
# @example
-
# describe MyEngine::PostsController do
-
# routes { MyEngine::Engine.routes }
-
#
-
# # ...
-
# end
-
1
def routes(&blk)
-
before do
-
self.routes = blk.call
-
end
-
end
-
end
-
-
1
attr_reader :controller, :routes
-
-
# @private
-
#
-
# RSpec Rails uses this to make Rails routes easily available to specs.
-
1
def routes=(routes)
-
4
@routes = routes
-
4
assertion_instance.instance_variable_set(:@routes, routes)
-
end
-
-
# @private
-
1
module BypassRescue
-
1
def rescue_with_handler(exception)
-
raise exception
-
end
-
end
-
-
# Extends the controller with a module that overrides
-
# `rescue_with_handler` to raise the exception passed to it. Use this to
-
# specify that an action _should_ raise an exception given appropriate
-
# conditions.
-
#
-
# @example
-
# describe ProfilesController do
-
# it "raises a 403 when a non-admin user tries to view another user's profile" do
-
# profile = create_profile
-
# login_as profile.user
-
#
-
# expect do
-
# bypass_rescue
-
# get :show, :id => profile.id + 1
-
# end.to raise_error(/403 Forbidden/)
-
# end
-
# end
-
1
def bypass_rescue
-
controller.extend(BypassRescue)
-
end
-
-
# If method is a named_route, delegates to the RouteSet associated with
-
# this controller.
-
1
def method_missing(method, *args, &block)
-
1
if route_available?(method)
-
1
controller.send(method, *args, &block)
-
else
-
super
-
end
-
end
-
-
1
included do
-
1
subject { controller }
-
-
1
before do
-
4
self.routes = ::Rails.application.routes
-
end
-
-
1
around do |ex|
-
4
previous_allow_forgery_protection_value = ActionController::Base.allow_forgery_protection
-
4
begin
-
4
ActionController::Base.allow_forgery_protection = false
-
4
ex.call
-
ensure
-
4
ActionController::Base.allow_forgery_protection = previous_allow_forgery_protection_value
-
end
-
end
-
end
-
-
1
private
-
-
1
def route_available?(method)
-
1
(defined?(@routes) && route_defined?(routes, method)) ||
-
1
(defined?(@orig_routes) && route_defined?(@orig_routes, method))
-
end
-
-
1
def route_defined?(routes, method)
-
1
return false if routes.nil?
-
-
1
if routes.named_routes.respond_to?(:route_defined?)
-
routes.named_routes.route_defined?(method)
-
else
-
1
routes.named_routes.helpers.include?(method)
-
end
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Rails
-
# Container module for routing spec functionality.
-
1
module FeatureExampleGroup
-
1
extend ActiveSupport::Concern
-
1
include RSpec::Rails::RailsExampleGroup
-
-
# Default host to be used in Rails route helpers if none is specified.
-
1
DEFAULT_HOST = "www.example.com"
-
-
1
included do
-
app = ::Rails.application
-
if app.respond_to?(:routes)
-
include app.routes.url_helpers if app.routes.respond_to?(:url_helpers)
-
include app.routes.mounted_helpers if app.routes.respond_to?(:mounted_helpers)
-
-
if respond_to?(:default_url_options)
-
default_url_options[:host] ||= ::RSpec::Rails::FeatureExampleGroup::DEFAULT_HOST
-
end
-
end
-
end
-
-
# Shim to check for presence of Capybara. Will delegate if present, raise
-
# if not. We assume here that in most cases `visit` will be the first
-
# Capybara method called in a spec.
-
1
def visit(*)
-
if defined?(super)
-
super
-
else
-
raise "Capybara not loaded, please add it to your Gemfile:\n\ngem \"capybara\""
-
end
-
end
-
end
-
end
-
end
-
1
require 'rspec/rails/view_assigns'
-
-
1
module RSpec
-
1
module Rails
-
# Container module for helper specs.
-
1
module HelperExampleGroup
-
1
extend ActiveSupport::Concern
-
1
include RSpec::Rails::RailsExampleGroup
-
1
include ActionView::TestCase::Behavior
-
1
include RSpec::Rails::ViewAssigns
-
-
# @private
-
1
module ClassMethods
-
1
def determine_default_helper_class(_ignore)
-
described_class
-
end
-
end
-
-
# Returns an instance of ActionView::Base with the helper being specified
-
# mixed in, along with any of the built-in rails helpers.
-
1
def helper
-
_view.tap do |v|
-
v.extend(ApplicationHelper) if defined?(ApplicationHelper)
-
v.assign(view_assigns)
-
end
-
end
-
-
1
private
-
-
1
def _controller_path(example)
-
example.example_group.described_class.to_s.sub(/Helper/, '').underscore
-
end
-
-
1
included do
-
before do |example|
-
controller.controller_path = _controller_path(example)
-
end
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Rails
-
# Container module for mailer spec functionality. It is only available if
-
# ActionMailer has been loaded before it.
-
1
module MailerExampleGroup
-
# This blank module is only necessary for YARD processing. It doesn't
-
# handle the conditional `defined?` check below very well.
-
end
-
end
-
end
-
-
1
if defined?(ActionMailer)
-
1
module RSpec
-
1
module Rails
-
# Container module for mailer spec functionality.
-
1
module MailerExampleGroup
-
1
extend ActiveSupport::Concern
-
1
include RSpec::Rails::RailsExampleGroup
-
1
include ActionMailer::TestCase::Behavior
-
-
1
included do
-
include ::Rails.application.routes.url_helpers
-
options = ::Rails.configuration.action_mailer.default_url_options
-
options.each { |key, value| default_url_options[key] = value } if options
-
end
-
-
# Class-level DSL for mailer specs.
-
1
module ClassMethods
-
# Alias for `described_class`.
-
1
def mailer_class
-
described_class
-
end
-
end
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Rails
-
# Container class for model spec functionality. Does not provide anything
-
# special over the common RailsExampleGroup currently.
-
1
module ModelExampleGroup
-
1
extend ActiveSupport::Concern
-
1
include RSpec::Rails::RailsExampleGroup
-
end
-
end
-
end
-
# Temporary workaround to resolve circular dependency between rspec-rails' spec
-
# suite and ammeter.
-
1
require 'rspec/rails/matchers'
-
-
1
module RSpec
-
1
module Rails
-
# Common rails example functionality.
-
1
module RailsExampleGroup
-
1
extend ActiveSupport::Concern
-
1
include RSpec::Rails::SetupAndTeardownAdapter
-
1
include RSpec::Rails::MinitestLifecycleAdapter if ::Rails::VERSION::STRING >= '4'
-
1
include RSpec::Rails::MinitestAssertionAdapter
-
1
include RSpec::Rails::Matchers
-
end
-
end
-
end
-
1
module RSpec
-
1
module Rails
-
# Container class for request spec functionality.
-
1
module RequestExampleGroup
-
1
extend ActiveSupport::Concern
-
1
include RSpec::Rails::RailsExampleGroup
-
1
include ActionDispatch::Integration::Runner
-
1
include ActionDispatch::Assertions
-
1
include RSpec::Rails::Matchers::RedirectTo
-
1
include RSpec::Rails::Matchers::RenderTemplate
-
1
include ActionController::TemplateAssertions
-
-
# Delegates to `Rails.application`.
-
1
def app
-
::Rails.application
-
end
-
-
1
included do
-
before do
-
@routes = ::Rails.application.routes
-
end
-
end
-
end
-
end
-
end
-
1
require "action_dispatch/testing/assertions/routing"
-
-
1
module RSpec
-
1
module Rails
-
# Container module for routing spec functionality.
-
1
module RoutingExampleGroup
-
1
extend ActiveSupport::Concern
-
1
include RSpec::Rails::RailsExampleGroup
-
1
include RSpec::Rails::Matchers::RoutingMatchers
-
1
include RSpec::Rails::Matchers::RoutingMatchers::RouteHelpers
-
1
include RSpec::Rails::AssertionDelegator.new(ActionDispatch::Assertions::RoutingAssertions)
-
-
# Class-level DSL for route specs.
-
1
module ClassMethods
-
# Specifies the routeset that will be used for the example group. This
-
# is most useful when testing Rails engines.
-
#
-
# @example
-
# describe MyEngine::PostsController do
-
# routes { MyEngine::Engine.routes }
-
#
-
# it "routes posts#index" do
-
# expect(:get => "/posts").to
-
# route_to(:controller => "my_engine/posts", :action => "index")
-
# end
-
# end
-
1
def routes(&blk)
-
before do
-
self.routes = blk.call
-
end
-
end
-
end
-
-
1
included do
-
before do
-
self.routes = ::Rails.application.routes
-
end
-
end
-
-
1
attr_reader :routes
-
-
# @private
-
1
def routes=(routes)
-
@routes = routes
-
assertion_instance.instance_variable_set(:@routes, routes)
-
end
-
-
1
private
-
-
1
def method_missing(m, *args, &block)
-
routes.url_helpers.respond_to?(m) ? routes.url_helpers.send(m, *args) : super
-
end
-
end
-
end
-
end
-
1
require 'rspec/rails/view_assigns'
-
-
1
module RSpec
-
1
module Rails
-
# Container class for view spec functionality.
-
1
module ViewExampleGroup
-
1
extend ActiveSupport::Concern
-
1
include RSpec::Rails::RailsExampleGroup
-
1
include ActionView::TestCase::Behavior
-
1
include RSpec::Rails::ViewAssigns
-
1
include RSpec::Rails::Matchers::RenderTemplate
-
-
# @private
-
1
module ClassMethods
-
1
def _default_helper
-
base = metadata[:description].split('/')[0..-2].join('/')
-
(base.camelize + 'Helper').constantize if base
-
rescue NameError
-
nil
-
end
-
-
1
def _default_helpers
-
helpers = [_default_helper].compact
-
helpers << ApplicationHelper if Object.const_defined?('ApplicationHelper')
-
helpers
-
end
-
end
-
-
# DSL exposed to view specs.
-
1
module ExampleMethods
-
# @overload render
-
# @overload render({:partial => path_to_file})
-
# @overload render({:partial => path_to_file}, {... locals ...})
-
# @overload render({:partial => path_to_file}, {... locals ...}) do ... end
-
#
-
# Delegates to ActionView::Base#render, so see documentation on that
-
# for more info.
-
#
-
# The only addition is that you can call render with no arguments, and
-
# RSpec will pass the top level description to render:
-
#
-
# describe "widgets/new.html.erb" do
-
# it "shows all the widgets" do
-
# render # => view.render(:file => "widgets/new.html.erb")
-
# # ...
-
# end
-
# end
-
1
def render(options = {}, local_assigns = {}, &block)
-
options = _default_render_options if Hash === options && options.empty?
-
super(options, local_assigns, &block)
-
end
-
-
# The instance of `ActionView::Base` that is used to render the template.
-
# Use this to stub methods _before_ calling `render`.
-
#
-
# describe "widgets/new.html.erb" do
-
# it "shows all the widgets" do
-
# view.stub(:foo) { "foo" }
-
# render
-
# # ...
-
# end
-
# end
-
1
def view
-
_view
-
end
-
-
# Simulates the presence of a template on the file system by adding a
-
# Rails' FixtureResolver to the front of the view_paths list. Designed to
-
# help isolate view examples from partials rendered by the view template
-
# that is the subject of the example.
-
#
-
# stub_template("widgets/_widget.html.erb" => "This content.")
-
1
def stub_template(hash)
-
view.view_paths.unshift(ActionView::FixtureResolver.new(hash))
-
end
-
-
# Provides access to the params hash that will be available within the
-
# view.
-
#
-
# params[:foo] = 'bar'
-
1
def params
-
controller.params
-
end
-
-
# @deprecated Use `view` instead.
-
1
def template
-
RSpec.deprecate("template", :replacement => "view")
-
view
-
end
-
-
# @deprecated Use `rendered` instead.
-
1
def response
-
# `assert_template` expects `response` to implement a #body method
-
# like an `ActionDispatch::Response` does to force the view to
-
# render. For backwards compatibility, we use #response as an alias
-
# for #rendered, but it needs to implement #body to avoid
-
# `assert_template` raising a `NoMethodError`.
-
unless rendered.respond_to?(:body)
-
def rendered.body
-
self
-
end
-
end
-
-
rendered
-
end
-
-
1
private
-
-
1
def _default_render_options
-
if ::Rails::VERSION::STRING >= '3.2'
-
# pluck the handler, format, and locale out of, eg, posts/index.de.html.haml
-
template, *components = _default_file_to_render.split('.')
-
handler, format, locale = *components.reverse
-
-
render_options = { :template => template }
-
render_options[:handlers] = [handler] if handler
-
render_options[:formats] = [format] if format
-
render_options[:locales] = [locale] if locale
-
-
render_options
-
else
-
{ :template => _default_file_to_render }
-
end
-
end
-
-
1
def _path_parts
-
_default_file_to_render.split("/")
-
end
-
-
1
def _controller_path
-
_path_parts[0..-2].join("/")
-
end
-
-
1
def _inferred_action
-
_path_parts.last.split(".").first
-
end
-
-
1
def _include_controller_helpers
-
helpers = controller._helpers
-
view.singleton_class.class_exec do
-
include helpers unless included_modules.include?(helpers)
-
end
-
end
-
end
-
-
1
included do
-
include ExampleMethods
-
-
helper(*_default_helpers)
-
-
before do
-
_include_controller_helpers
-
if view.lookup_context.respond_to?(:prefixes)
-
# rails 3.1
-
view.lookup_context.prefixes << _controller_path
-
end
-
-
# fixes bug with differing formats
-
view.lookup_context.view_paths.each(&:clear_cache)
-
-
controller.controller_path = _controller_path
-
controller.request.path_parameters[:controller] = _controller_path
-
controller.request.path_parameters[:action] = _inferred_action unless _inferred_action =~ /^_/
-
end
-
-
let(:_default_file_to_render) do |example|
-
example.example_group.top_level_description
-
end
-
end
-
end
-
end
-
end
-
1
require 'rspec/rails/extensions/active_record/proxy'
-
1
RSpec.configure do |rspec|
-
# Delay this in order to give users a chance to configure `expect_with`...
-
1
rspec.before(:suite) do
-
1
if defined?(RSpec::Matchers) && RSpec::Matchers.configuration.syntax.include?(:should) && defined?(ActiveRecord::Associations)
-
# In Rails 3.0, it was AssociationProxy.
-
# In 3.1+, it's CollectionProxy.
-
1
const_name = [:CollectionProxy, :AssociationProxy].find do |const|
-
1
ActiveRecord::Associations.const_defined?(const)
-
end
-
-
1
proxy_class = ActiveRecord::Associations.const_get(const_name)
-
-
1
RSpec::Matchers.configuration.add_should_and_should_not_to proxy_class
-
end
-
end
-
end
-
# @api private
-
1
module RSpec
-
1
module Rails
-
# Disable some cops until https://github.com/bbatsov/rubocop/issues/1310
-
# rubocop:disable Style/IndentationConsistency
-
1
module FeatureCheck
-
# rubocop:disable Style/IndentationWidth
-
1
module_function
-
# rubocop:enable Style/IndentationWidth
-
-
1
def can_check_pending_migrations?
-
has_active_record_migration? &&
-
::ActiveRecord::Migration.respond_to?(:check_pending!)
-
end
-
-
1
def can_maintain_test_schema?
-
has_active_record_migration? &&
-
::ActiveRecord::Migration.respond_to?(:maintain_test_schema!)
-
end
-
-
1
def has_active_job?
-
defined?(::ActiveJob)
-
end
-
-
1
def has_active_record?
-
defined?(::ActiveRecord)
-
end
-
-
1
def has_active_record_migration?
-
has_active_record? && defined?(::ActiveRecord::Migration)
-
end
-
end
-
# rubocop:enable Style/IndentationConsistency
-
end
-
end
-
1
module RSpec
-
1
module Rails
-
# @private
-
1
module FixtureSupport
-
1
if defined?(ActiveRecord::TestFixtures)
-
1
extend ActiveSupport::Concern
-
1
include RSpec::Rails::SetupAndTeardownAdapter
-
1
include RSpec::Rails::MinitestLifecycleAdapter if ::ActiveRecord::VERSION::STRING > '4'
-
1
include RSpec::Rails::MinitestAssertionAdapter
-
1
include ActiveRecord::TestFixtures
-
-
1
included do
-
# TODO: (DC 2011-06-25) this is necessary because fixture_file_upload
-
# accesses fixture_path directly on ActiveSupport::TestCase. This is
-
# fixed in rails by https://github.com/rails/rails/pull/1861, which
-
# should be part of the 3.1 release, at which point we can include
-
# these lines for rails < 3.1.
-
1
ActiveSupport::TestCase.class_exec do
-
1
include ActiveRecord::TestFixtures
-
1
self.fixture_path = RSpec.configuration.fixture_path
-
end
-
# /TODO
-
-
1
self.fixture_path = RSpec.configuration.fixture_path
-
1
self.use_transactional_fixtures = RSpec.configuration.use_transactional_fixtures
-
1
self.use_instantiated_fixtures = RSpec.configuration.use_instantiated_fixtures
-
1
fixtures RSpec.configuration.global_fixtures if RSpec.configuration.global_fixtures
-
end
-
end
-
end
-
end
-
end
-
1
require 'rspec/core/warnings'
-
1
require 'rspec/expectations'
-
-
1
module RSpec
-
1
module Rails
-
# Container module for Rails specific matchers.
-
1
module Matchers
-
end
-
end
-
end
-
-
1
begin
-
1
require 'test/unit/assertionfailederror'
-
rescue LoadError
-
1
module Test
-
1
module Unit
-
1
class AssertionFailedError < StandardError
-
end
-
end
-
end
-
end
-
-
1
require 'rspec/rails/matchers/have_rendered'
-
1
require 'rspec/rails/matchers/redirect_to'
-
1
require 'rspec/rails/matchers/routing_matchers'
-
1
require 'rspec/rails/matchers/be_new_record'
-
1
require 'rspec/rails/matchers/be_a_new'
-
1
require 'rspec/rails/matchers/relation_match_array'
-
1
require 'rspec/rails/matchers/be_valid'
-
1
require 'rspec/rails/matchers/have_http_status'
-
1
module RSpec
-
1
module Rails
-
1
module Matchers
-
# @api private
-
#
-
# Matcher class for `be_a_new`. Should not be instantiated directly.
-
#
-
# @see RSpec::Rails::Matchers#be_a_new
-
1
class BeANew < RSpec::Matchers::BuiltIn::BaseMatcher
-
# @private
-
1
def initialize(expected)
-
1
@expected = expected
-
end
-
-
# @private
-
1
def matches?(actual)
-
1
@actual = actual
-
1
actual.is_a?(expected) && actual.new_record? && attributes_match?(actual)
-
end
-
-
# @api public
-
# @see RSpec::Rails::Matchers#be_a_new
-
1
def with(expected_attributes)
-
attributes.merge!(expected_attributes)
-
self
-
end
-
-
# @private
-
1
def failure_message
-
[].tap do |message|
-
unless actual.is_a?(expected) && actual.new_record?
-
message << "expected #{actual.inspect} to be a new #{expected.inspect}"
-
end
-
unless attributes_match?(actual)
-
if unmatched_attributes.size > 1
-
message << "attributes #{unmatched_attributes.inspect} were not set on #{actual.inspect}"
-
else
-
message << "attribute #{unmatched_attributes.inspect} was not set on #{actual.inspect}"
-
end
-
end
-
end.join(' and ')
-
end
-
-
1
private
-
-
1
def attributes
-
1
@attributes ||= {}
-
end
-
-
1
def attributes_match?(actual)
-
1
attributes.stringify_keys.all? do |key, value|
-
actual.attributes[key].eql?(value)
-
end
-
end
-
-
1
def unmatched_attributes
-
attributes.stringify_keys.reject do |key, value|
-
actual.attributes[key].eql?(value)
-
end
-
end
-
end
-
-
# Passes if actual is an instance of `model_class` and returns `false` for
-
# `persisted?`. Typically used to specify instance variables assigned to
-
# views by controller actions
-
#
-
# Use the `with` method to specify the specific attributes to match on the
-
# new record.
-
#
-
# @example
-
# get :new
-
# assigns(:thing).should be_a_new(Thing)
-
#
-
# post :create, :thing => { :name => "Illegal Value" }
-
# assigns(:thing).should be_a_new(Thing).with(:name => nil)
-
1
def be_a_new(model_class)
-
1
BeANew.new(model_class)
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Rails
-
1
module Matchers
-
# @private
-
1
class BeANewRecord < RSpec::Matchers::BuiltIn::BaseMatcher
-
1
def matches?(actual)
-
!actual.persisted?
-
end
-
-
1
def failure_message
-
"expected #{actual.inspect} to be a new record, but was persisted"
-
end
-
-
1
def failure_message_when_negated
-
"expected #{actual.inspect} to be persisted, but was a new record"
-
end
-
end
-
-
# Passes if actual returns `false` for `persisted?`.
-
#
-
# @example
-
# get :new
-
# expect(assigns(:thing)).to be_new_record
-
1
def be_new_record
-
BeANewRecord.new
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Rails
-
1
module Matchers
-
# @private
-
1
class BeValid < RSpec::Matchers::BuiltIn::Be
-
1
def initialize(*args)
-
@args = args
-
end
-
-
1
def matches?(actual)
-
@actual = actual
-
actual.valid?(*@args)
-
end
-
-
1
def failure_message
-
message = "expected #{actual.inspect} to be valid"
-
-
if actual.respond_to?(:errors)
-
errors = if actual.errors.respond_to?(:full_messages)
-
actual.errors.full_messages
-
else
-
actual.errors
-
end
-
-
message << ", but got errors: #{errors.map(&:to_s).join(', ')}"
-
end
-
-
message
-
end
-
-
1
def failure_message_when_negated
-
"expected #{actual.inspect} not to be valid"
-
end
-
end
-
-
# Passes if the given model instance's `valid?` method is true, meaning
-
# all of the `ActiveModel::Validations` passed and no errors exist. If a
-
# message is not given, a default message is shown listing each error.
-
#
-
# @example
-
# thing = Thing.new
-
# expect(thing).to be_valid
-
1
def be_valid(*args)
-
BeValid.new(*args)
-
end
-
end
-
end
-
end
-
# The following code inspired and modified from Rails' `assert_response`:
-
#
-
# https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/testing/assertions/response.rb#L22-L38
-
#
-
# Thank you to all the Rails devs who did the heavy lifting on this!
-
-
1
module RSpec
-
1
module Rails
-
1
module Matchers
-
# Namespace for various implementations of `have_http_status`.
-
#
-
# @api private
-
1
module HaveHttpStatus
-
# Instantiates an instance of the proper matcher based on the provided
-
# `target`.
-
#
-
# @param target [Object] expected http status or code
-
# @return response matcher instance
-
1
def self.matcher_for_status(target)
-
if GenericStatus.valid_statuses.include?(target)
-
GenericStatus.new(target)
-
elsif Symbol === target
-
SymbolicStatus.new(target)
-
else
-
NumericCode.new(target)
-
end
-
end
-
-
# @api private
-
# Conversion function to coerce the provided object into an
-
# `ActionDispatch::TestResponse`.
-
#
-
# @param obj [Object] object to convert to a response
-
# @return [ActionDispatch::TestResponse]
-
1
def as_test_response(obj)
-
if ::ActionDispatch::Response === obj
-
::ActionDispatch::TestResponse.from_response(obj)
-
elsif ::ActionDispatch::TestResponse === obj
-
obj
-
elsif obj.respond_to?(:status_code) && obj.respond_to?(:response_headers)
-
# Acts As Capybara Session
-
# Hack to support `Capybara::Session` without having to load
-
# Capybara or catch `NameError`s for the undefined constants
-
::ActionDispatch::TestResponse.new.tap do |resp|
-
resp.status = obj.status_code
-
resp.headers = obj.response_headers
-
resp.body = obj.body
-
end
-
else
-
raise TypeError, "Invalid response type: #{obj}"
-
end
-
end
-
1
module_function :as_test_response
-
-
# @return [String, nil] a formatted failure message if
-
# `@invalid_response` is present, `nil` otherwise
-
1
def invalid_response_type_message
-
return unless @invalid_response
-
"expected a response object, but an instance of " \
-
"#{@invalid_response.class} was received"
-
end
-
-
# @api private
-
# Provides an implementation for `have_http_status` matching against
-
# numeric http status codes.
-
#
-
# Not intended to be instantiated directly.
-
#
-
# @example
-
# expect(response).to have_http_status(404)
-
#
-
# @see RSpec::Rails::Matchers.have_http_status
-
1
class NumericCode < RSpec::Matchers::BuiltIn::BaseMatcher
-
1
include HaveHttpStatus
-
-
1
def initialize(code)
-
@expected = code.to_i
-
@actual = nil
-
@invalid_response = nil
-
end
-
-
# @param [Object] response object providing an http code to match
-
# @return [Boolean] `true` if the numeric code matched the `response` code
-
1
def matches?(response)
-
test_response = as_test_response(response)
-
@actual = test_response.response_code
-
expected == @actual
-
rescue TypeError => _ignored
-
@invalid_response = response
-
false
-
end
-
-
# @return [String]
-
1
def description
-
"respond with numeric status code #{expected}"
-
end
-
-
# @return [String] explaining why the match failed
-
1
def failure_message
-
invalid_response_type_message ||
-
"expected the response to have status code #{expected.inspect}" \
-
" but it was #{actual.inspect}"
-
end
-
-
# @return [String] explaining why the match failed
-
1
def failure_message_when_negated
-
invalid_response_type_message ||
-
"expected the response not to have status code " \
-
"#{expected.inspect} but it did"
-
end
-
end
-
-
# @api private
-
# Provides an implementation for `have_http_status` matching against
-
# Rack symbol http status codes.
-
#
-
# Not intended to be instantiated directly.
-
#
-
# @example
-
# expect(response).to have_http_status(:created)
-
#
-
# @see RSpec::Rails::Matchers.have_http_status
-
# @see https://github.com/rack/rack/blob/master/lib/rack/utils.rb `Rack::Utils::SYMBOL_TO_STATUS_CODE`
-
1
class SymbolicStatus < RSpec::Matchers::BuiltIn::BaseMatcher
-
1
include HaveHttpStatus
-
-
1
def initialize(status)
-
@expected_status = status
-
@actual = nil
-
@invalid_response = nil
-
set_expected_code!
-
end
-
-
# @param [Object] response object providing an http code to match
-
# @return [Boolean] `true` if Rack's associated numeric HTTP code matched
-
# the `response` code
-
1
def matches?(response)
-
test_response = as_test_response(response)
-
@actual = test_response.response_code
-
expected == @actual
-
rescue TypeError => _ignored
-
@invalid_response = response
-
false
-
end
-
-
# @return [String]
-
1
def description
-
"respond with status code #{pp_expected}"
-
end
-
-
# @return [String] explaining why the match failed
-
1
def failure_message
-
invalid_response_type_message ||
-
"expected the response to have status code #{pp_expected} but it" \
-
" was #{pp_actual}"
-
end
-
-
# @return [String] explaining why the match failed
-
1
def failure_message_when_negated
-
invalid_response_type_message ||
-
"expected the response not to have status code #{pp_expected} " \
-
"but it did"
-
end
-
-
# The initialized expected status symbol
-
1
attr_reader :expected_status
-
1
private :expected_status
-
-
1
private
-
-
# @return [Symbol] representing the actual http numeric code
-
1
def actual_status
-
return unless actual
-
@actual_status ||= compute_status_from(actual)
-
end
-
-
# Reverse lookup of the Rack status code symbol based on the numeric
-
# http code
-
#
-
# @param code [Fixnum] http status code to look up
-
# @return [Symbol] representing the http numeric code
-
1
def compute_status_from(code)
-
status, _ = Rack::Utils::SYMBOL_TO_STATUS_CODE.find do |_, c|
-
c == code
-
end
-
status
-
end
-
-
# @return [String] pretty format the actual response status
-
1
def pp_actual
-
pp_status(actual_status, actual)
-
end
-
-
# @return [String] pretty format the expected status and associated code
-
1
def pp_expected
-
pp_status(expected_status, expected)
-
end
-
-
# @return [String] pretty format the actual response status
-
1
def pp_status(status, code)
-
if status
-
"#{status.inspect} (#{code})"
-
else
-
code.to_s
-
end
-
end
-
-
# Sets `expected` to the numeric http code based on the Rack
-
# `expected_status` status
-
#
-
# @see Rack::Utils::SYMBOL_TO_STATUS_CODE
-
# @raise [ArgumentError] if an associated code could not be found
-
1
def set_expected_code!
-
@expected ||=
-
Rack::Utils::SYMBOL_TO_STATUS_CODE.fetch(expected_status) do
-
raise ArgumentError,
-
"Invalid HTTP status: #{expected_status.inspect}"
-
end
-
end
-
end
-
-
# @api private
-
# Provides an implementation for `have_http_status` matching against
-
# `ActionDispatch::TestResponse` http status category queries.
-
#
-
# Not intended to be instantiated directly.
-
#
-
# @example
-
# expect(response).to have_http_status(:success)
-
# expect(response).to have_http_status(:error)
-
# expect(response).to have_http_status(:missing)
-
# expect(response).to have_http_status(:redirect)
-
#
-
# @see RSpec::Rails::Matchers.have_http_status
-
# @see ActionDispatch::TestResponse
-
1
class GenericStatus < RSpec::Matchers::BuiltIn::BaseMatcher
-
1
include HaveHttpStatus
-
-
# @return [Array<Symbol>] of status codes which represent a HTTP status
-
# code "group"
-
# @see https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/testing/test_response.rb `ActionDispatch::TestResponse`
-
1
def self.valid_statuses
-
[:error, :success, :missing, :redirect]
-
end
-
-
1
def initialize(type)
-
unless self.class.valid_statuses.include?(type)
-
raise ArgumentError, "Invalid generic HTTP status: #{type.inspect}"
-
end
-
@expected = type
-
@actual = nil
-
@invalid_response = nil
-
end
-
-
# @return [Boolean] `true` if Rack's associated numeric HTTP code matched
-
# the `response` code
-
1
def matches?(response)
-
test_response = as_test_response(response)
-
@actual = test_response.response_code
-
test_response.send("#{expected}?")
-
rescue TypeError => _ignored
-
@invalid_response = response
-
false
-
end
-
-
# @return [String]
-
1
def description
-
"respond with #{type_message}"
-
end
-
-
# @return [String] explaining why the match failed
-
1
def failure_message
-
invalid_response_type_message ||
-
"expected the response to have #{type_message} but it was #{actual}"
-
end
-
-
# @return [String] explaining why the match failed
-
1
def failure_message_when_negated
-
invalid_response_type_message ||
-
"expected the response not to have #{type_message} but it was #{actual}"
-
end
-
-
1
private
-
-
# @return [String] formating the expected status and associated code(s)
-
1
def type_message
-
@type_message ||= (expected == :error ? "an error" : "a #{expected}") +
-
" status code (#{type_codes})"
-
end
-
-
# @return [String] formatting the associated code(s) for the various
-
# status code "groups"
-
# @see https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/testing/test_response.rb `ActionDispatch::TestResponse`
-
# @see https://github.com/rack/rack/blob/master/lib/rack/response.rb `Rack::Response`
-
1
def type_codes
-
# At the time of this commit the most recent version of
-
# `ActionDispatch::TestResponse` defines the following aliases:
-
#
-
# alias_method :success?, :successful?
-
# alias_method :missing?, :not_found?
-
# alias_method :redirect?, :redirection?
-
# alias_method :error?, :server_error?
-
#
-
# It's parent `ActionDispatch::Response` includes
-
# `Rack::Response::Helpers` which defines the aliased methods as:
-
#
-
# def successful?; status >= 200 && status < 300; end
-
# def redirection?; status >= 300 && status < 400; end
-
# def server_error?; status >= 500 && status < 600; end
-
# def not_found?; status == 404; end
-
#
-
# @see https://github.com/rails/rails/blob/ca200378/actionpack/lib/action_dispatch/testing/test_response.rb#L17-L27
-
# @see https://github.com/rails/rails/blob/ca200378/actionpack/lib/action_dispatch/http/response.rb#L74
-
# @see https://github.com/rack/rack/blob/ce4a3959/lib/rack/response.rb#L119-L122
-
@type_codes ||= case expected
-
when :error
-
"5xx"
-
when :success
-
"2xx"
-
when :missing
-
"404"
-
when :redirect
-
"3xx"
-
end
-
end
-
end
-
end
-
-
# @api public
-
# Passes if `response` has a matching HTTP status code.
-
#
-
# The following symbolic status codes are allowed:
-
#
-
# - `Rack::Utils::SYMBOL_TO_STATUS_CODE`
-
# - One of the defined `ActionDispatch::TestResponse` aliases:
-
# - `:error`
-
# - `:missing`
-
# - `:redirect`
-
# - `:success`
-
#
-
# @example Accepts numeric and symbol statuses
-
# expect(response).to have_http_status(404)
-
# expect(response).to have_http_status(:created)
-
# expect(response).to have_http_status(:success)
-
# expect(response).to have_http_status(:error)
-
# expect(response).to have_http_status(:missing)
-
# expect(response).to have_http_status(:redirect)
-
#
-
# @example Works with standard `response` objects and Capybara's `page`
-
# expect(response).to have_http_status(404)
-
# expect(page).to have_http_status(:created)
-
#
-
# @see https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/testing/test_response.rb `ActionDispatch::TestResponse`
-
# @see https://github.com/rack/rack/blob/master/lib/rack/utils.rb `Rack::Utils::SYMBOL_TO_STATUS_CODE`
-
1
def have_http_status(target)
-
raise ArgumentError, "Invalid HTTP status: nil" unless target
-
HaveHttpStatus.matcher_for_status(target)
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Rails
-
1
module Matchers
-
# Matcher for template rendering.
-
1
module RenderTemplate
-
# @private
-
1
class RenderTemplateMatcher < RSpec::Matchers::BuiltIn::BaseMatcher
-
1
def initialize(scope, expected, message = nil)
-
1
@expected = Symbol === expected ? expected.to_s : expected
-
1
@message = message
-
1
@scope = scope
-
end
-
-
# @api private
-
1
def matches?(*)
-
1
match_unless_raises ActiveSupport::TestCase::Assertion do
-
1
@scope.assert_template expected, @message
-
end
-
end
-
-
# @api private
-
1
def failure_message
-
rescued_exception.message
-
end
-
-
# @api private
-
1
def failure_message_when_negated
-
"expected not to render #{expected.inspect}, but did"
-
end
-
end
-
-
# Delegates to `assert_template`.
-
#
-
# @example
-
# expect(response).to have_rendered("new")
-
1
def have_rendered(options, message = nil)
-
1
RenderTemplateMatcher.new(self, options, message)
-
end
-
-
1
alias_method :render_template, :have_rendered
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Rails
-
1
module Matchers
-
# Matcher for redirects.
-
1
module RedirectTo
-
# @private
-
1
class RedirectTo < RSpec::Matchers::BuiltIn::BaseMatcher
-
1
def initialize(scope, expected)
-
1
@expected = expected
-
1
@scope = scope
-
end
-
-
1
def matches?(_)
-
1
match_unless_raises ActiveSupport::TestCase::Assertion do
-
1
@scope.assert_redirected_to(@expected)
-
end
-
end
-
-
1
def failure_message
-
1
rescued_exception.message
-
end
-
-
1
def failure_message_when_negated
-
"expected not to redirect to #{@expected.inspect}, but did"
-
end
-
end
-
-
# Delegates to `assert_redirected_to`.
-
#
-
# @example
-
# expect(response).to redirect_to(:action => "new")
-
1
def redirect_to(target)
-
1
RedirectTo.new(self, target)
-
end
-
end
-
end
-
end
-
end
-
1
if defined?(ActiveRecord::Relation)
-
1
RSpec::Matchers::BuiltIn::OperatorMatcher.register(ActiveRecord::Relation, '=~', RSpec::Matchers::BuiltIn::ContainExactly)
-
end
-
1
module RSpec
-
1
module Rails
-
1
module Matchers
-
# Matchers to help with specs for routing code.
-
1
module RoutingMatchers
-
1
extend RSpec::Matchers::DSL
-
-
# @private
-
1
class RouteToMatcher < RSpec::Matchers::BuiltIn::BaseMatcher
-
1
def initialize(scope, *expected)
-
@scope = scope
-
@expected = expected[1] || {}
-
if Hash === expected[0]
-
@expected.merge!(expected[0])
-
else
-
controller, action = expected[0].split('#')
-
@expected.merge!(:controller => controller, :action => action)
-
end
-
end
-
-
1
def matches?(verb_to_path_map)
-
@actual = @verb_to_path_map = verb_to_path_map
-
# assert_recognizes does not consider ActionController::RoutingError an
-
# assertion failure, so we have to capture that and Assertion here.
-
match_unless_raises ActiveSupport::TestCase::Assertion, ActionController::RoutingError do
-
path, query = *verb_to_path_map.values.first.split('?')
-
@scope.assert_recognizes(
-
@expected,
-
{ :method => verb_to_path_map.keys.first, :path => path },
-
Rack::Utils.parse_nested_query(query)
-
)
-
end
-
end
-
-
1
def failure_message
-
rescued_exception.message
-
end
-
-
1
def failure_message_when_negated
-
"expected #{@actual.inspect} not to route to #{@expected.inspect}"
-
end
-
-
1
def description
-
"route #{@actual.inspect} to #{@expected.inspect}"
-
end
-
end
-
-
# Delegates to `assert_recognizes`. Supports short-hand controller/action
-
# declarations (e.g. `"controller#action"`).
-
#
-
# @example
-
#
-
# expect(:get => "/things/special").to route_to(
-
# :controller => "things",
-
# :action => "special"
-
# )
-
#
-
# expect(:get => "/things/special").to route_to("things#special")
-
#
-
# @see http://api.rubyonrails.org/classes/ActionDispatch/Assertions/RoutingAssertions.html#method-i-assert_recognizes
-
1
def route_to(*expected)
-
RouteToMatcher.new(self, *expected)
-
end
-
-
# @private
-
1
class BeRoutableMatcher < RSpec::Matchers::BuiltIn::BaseMatcher
-
1
def initialize(scope)
-
@scope = scope
-
end
-
-
1
def matches?(path)
-
@actual = path
-
match_unless_raises ActionController::RoutingError do
-
@routing_options = @scope.routes.recognize_path(
-
path.values.first, :method => path.keys.first
-
)
-
end
-
end
-
-
1
def failure_message
-
"expected #{@actual.inspect} to be routable"
-
end
-
-
1
def failure_message_when_negated
-
"expected #{@actual.inspect} not to be routable, but it routes to #{@routing_options.inspect}"
-
end
-
end
-
-
# Passes if the route expression is recognized by the Rails router based on
-
# the declarations in `config/routes.rb`. Delegates to
-
# `RouteSet#recognize_path`.
-
#
-
# @example You can use route helpers provided by rspec-rails.
-
# expect(:get => "/a/path").to be_routable
-
# expect(:post => "/another/path").to be_routable
-
# expect(:put => "/yet/another/path").to be_routable
-
1
def be_routable
-
BeRoutableMatcher.new(self)
-
end
-
-
# Helpers for matching different route types.
-
1
module RouteHelpers
-
# @!method get
-
# @!method post
-
# @!method put
-
# @!method patch
-
# @!method delete
-
# @!method options
-
# @!method head
-
#
-
# Shorthand method for matching this type of route.
-
1
%w[get post put patch delete options head].each do |method|
-
7
define_method method do |path|
-
{ method.to_sym => path }
-
end
-
end
-
end
-
end
-
end
-
end
-
end
-
1
begin
-
1
require 'capybara/rspec'
-
rescue LoadError
-
end
-
-
1
begin
-
1
require 'capybara/rails'
-
rescue LoadError
-
end
-
-
1
if defined?(Capybara)
-
1
require 'rspec/support/version_checker'
-
1
RSpec::Support::VersionChecker.new('capybara', Capybara::VERSION, '2.2.0').check_version!
-
-
1
RSpec.configure do |c|
-
1
if defined?(Capybara::DSL)
-
1
c.include Capybara::DSL, :type => :feature
-
end
-
-
1
if defined?(Capybara::RSpecMatchers)
-
1
c.include Capybara::RSpecMatchers, :type => :view
-
1
c.include Capybara::RSpecMatchers, :type => :helper
-
1
c.include Capybara::RSpecMatchers, :type => :mailer
-
1
c.include Capybara::RSpecMatchers, :type => :controller
-
1
c.include Capybara::RSpecMatchers, :type => :feature
-
end
-
-
1
unless defined?(Capybara::RSpecMatchers) || defined?(Capybara::DSL)
-
c.include Capybara, :type => :request
-
c.include Capybara, :type => :controller
-
end
-
end
-
end
-
1
module RSpec
-
1
module Rails
-
# Helpers for making instance variables available to views.
-
1
module ViewAssigns
-
# Assigns a value to an instance variable in the scope of the
-
# view being rendered.
-
#
-
# @example
-
#
-
# assign(:widget, stub_model(Widget))
-
1
def assign(key, value)
-
_encapsulated_assigns[key] = value
-
end
-
-
# Compat-shim for AbstractController::Rendering#view_assigns
-
#
-
# _assigns was deprecated in favor of view_assigns after
-
# Rails-3.0.0 was released. Since we are not able to predict when
-
# the _assigns/view_assigns patch will be released (I thought it
-
# would have been in 3.0.1, but 3.0.1 bypassed this change for a
-
# security fix), this bit ensures that we do the right thing without
-
# knowing anything about the Rails version we are dealing with.
-
#
-
# Once that change _is_ released, this can be changed to something
-
# that checks for the Rails version when the module is being
-
# interpreted, as it was before commit dd0095.
-
1
def view_assigns
-
super.merge(_encapsulated_assigns)
-
rescue
-
_assigns
-
end
-
-
# @private
-
1
def _assigns
-
super.merge(_encapsulated_assigns)
-
end
-
-
1
private
-
-
1
def _encapsulated_assigns
-
@_encapsulated_assigns ||= {}
-
end
-
end
-
end
-
end
-
1
RSpec::Support.require_rspec_support 'encoded_string'
-
1
RSpec::Support.require_rspec_support 'hunk_generator'
-
-
1
require 'pp'
-
-
1
module RSpec
-
1
module Support
-
# rubocop:disable ClassLength
-
1
class Differ
-
1
def diff(actual, expected)
-
1
diff = ""
-
-
1
if actual && expected
-
if all_strings?(actual, expected)
-
if any_multiline_strings?(actual, expected)
-
diff = diff_as_string(coerce_to_string(actual), coerce_to_string(expected))
-
end
-
elsif no_procs?(actual, expected) && no_numbers?(actual, expected)
-
diff = diff_as_object(actual, expected)
-
end
-
end
-
-
1
diff.to_s
-
end
-
-
# rubocop:disable MethodLength
-
1
def diff_as_string(actual, expected)
-
@encoding = pick_encoding actual, expected
-
-
@actual = EncodedString.new(actual, @encoding)
-
@expected = EncodedString.new(expected, @encoding)
-
-
output = EncodedString.new("\n", @encoding)
-
-
hunks.each_cons(2) do |prev_hunk, current_hunk|
-
begin
-
if current_hunk.overlaps?(prev_hunk)
-
add_old_hunk_to_hunk(current_hunk, prev_hunk)
-
else
-
add_to_output(output, prev_hunk.diff(format_type).to_s)
-
end
-
ensure
-
add_to_output(output, "\n")
-
end
-
end
-
-
finalize_output(output, hunks.last.diff(format_type).to_s) if hunks.last
-
-
color_diff output
-
rescue Encoding::CompatibilityError
-
handle_encoding_errors
-
end
-
# rubocop:enable MethodLength
-
-
1
def diff_as_object(actual, expected)
-
actual_as_string = object_to_string(actual)
-
expected_as_string = object_to_string(expected)
-
diff_as_string(actual_as_string, expected_as_string)
-
end
-
-
1
attr_reader :color
-
1
alias_method :color?, :color
-
-
1
def initialize(opts={})
-
1
@color = opts.fetch(:color, false)
-
1
@object_preparer = opts.fetch(:object_preparer, lambda { |string| string })
-
end
-
-
1
private
-
-
1
def no_procs?(*args)
-
safely_flatten(args).none? { |a| Proc === a }
-
end
-
-
1
def all_strings?(*args)
-
safely_flatten(args).all? { |a| String === a }
-
end
-
-
1
def any_multiline_strings?(*args)
-
all_strings?(*args) && safely_flatten(args).any? { |a| multiline?(a) }
-
end
-
-
1
def no_numbers?(*args)
-
safely_flatten(args).none? { |a| Numeric === a }
-
end
-
-
1
def coerce_to_string(string_or_array)
-
return string_or_array unless Array === string_or_array
-
diffably_stringify(string_or_array).join("\n")
-
end
-
-
1
def diffably_stringify(array)
-
array.map do |entry|
-
if Array === entry
-
entry.inspect
-
else
-
entry.to_s.gsub("\n", "\\n")
-
end
-
end
-
end
-
-
1
if String.method_defined?(:encoding)
-
1
def multiline?(string)
-
string.include?("\n".encode(string.encoding))
-
end
-
else
-
def multiline?(string)
-
string.include?("\n")
-
end
-
end
-
-
1
def hunks
-
@hunks ||= HunkGenerator.new(@actual, @expected).hunks
-
end
-
-
1
def finalize_output(output, final_line)
-
add_to_output(output, final_line)
-
add_to_output(output, "\n")
-
end
-
-
1
def add_to_output(output, string)
-
output << string
-
end
-
-
1
def add_old_hunk_to_hunk(hunk, oldhunk)
-
hunk.merge(oldhunk)
-
end
-
-
1
def safely_flatten(array)
-
array = array.flatten(1) until (array == array.flatten(1))
-
array
-
end
-
-
1
def format_type
-
:unified
-
end
-
-
1
def color(text, color_code)
-
"\e[#{color_code}m#{text}\e[0m"
-
end
-
-
1
def red(text)
-
color(text, 31)
-
end
-
-
1
def green(text)
-
color(text, 32)
-
end
-
-
1
def blue(text)
-
color(text, 34)
-
end
-
-
1
def normal(text)
-
color(text, 0)
-
end
-
-
1
def color_diff(diff)
-
return diff unless color?
-
-
diff.lines.map do |line|
-
case line[0].chr
-
when "+"
-
green line
-
when "-"
-
red line
-
when "@"
-
line[1].chr == "@" ? blue(line) : normal(line)
-
else
-
normal(line)
-
end
-
end.join
-
end
-
-
1
def object_to_string(object)
-
object = @object_preparer.call(object)
-
case object
-
when Hash
-
object.keys.sort_by { |k| k.to_s }.map do |key|
-
pp_key = PP.singleline_pp(key, "")
-
pp_value = PP.singleline_pp(object[key], "")
-
-
"#{pp_key} => #{pp_value},"
-
end.join("\n")
-
when String
-
object =~ /\n/ ? object : object.inspect
-
else
-
PP.pp(object, "")
-
end
-
end
-
-
1
if String.method_defined?(:encoding)
-
1
def pick_encoding(source_a, source_b)
-
Encoding.compatible?(source_a, source_b) || Encoding.default_external
-
end
-
else
-
def pick_encoding(_source_a, _source_b)
-
end
-
end
-
-
1
def handle_encoding_errors
-
if @actual.source_encoding != @expected.source_encoding
-
"Could not produce a diff because the encoding of the actual string " \
-
"(#{@actual.source_encoding}) differs from the encoding of the expected " \
-
"string (#{@expected.source_encoding})"
-
else
-
"Could not produce a diff because of the encoding of the string " \
-
"(#{@expected.source_encoding})"
-
end
-
end
-
end
-
# rubocop:enable ClassLength
-
end
-
end
-
1
module RSpec
-
1
module Support
-
# Provides a means to fuzzy-match between two arbitrary objects.
-
# Understands array/hash nesting. Uses `===` or `==` to
-
# perform the matching.
-
1
module FuzzyMatcher
-
# @api private
-
1
def self.values_match?(expected, actual)
-
3
if Array === expected && Enumerable === actual && !(Struct === actual)
-
return arrays_match?(expected, actual.to_a)
-
elsif Hash === expected && Hash === actual
-
return hashes_match?(expected, actual)
-
elsif actual == expected
-
1
return true
-
end
-
-
2
begin
-
2
expected === actual
-
rescue ArgumentError
-
# Some objects, like 0-arg lambdas on 1.9+, raise
-
# ArgumentError for `expected === actual`.
-
false
-
end
-
end
-
-
# @private
-
1
def self.arrays_match?(expected_list, actual_list)
-
return false if expected_list.size != actual_list.size
-
-
expected_list.zip(actual_list).all? do |expected, actual|
-
values_match?(expected, actual)
-
end
-
end
-
-
# @private
-
1
def self.hashes_match?(expected_hash, actual_hash)
-
return false if expected_hash.size != actual_hash.size
-
-
expected_hash.all? do |expected_key, expected_value|
-
actual_value = actual_hash.fetch(expected_key) { return false }
-
values_match?(expected_value, actual_value)
-
end
-
end
-
-
1
private_class_method :arrays_match?, :hashes_match?
-
end
-
end
-
end
-
1
require 'diff/lcs'
-
1
require 'diff/lcs/hunk'
-
-
1
module RSpec
-
1
module Support
-
# @private
-
1
class HunkGenerator
-
1
def initialize(actual, expected)
-
@actual = actual
-
@expected = expected
-
end
-
-
1
def hunks
-
@file_length_difference = 0
-
@hunks ||= diffs.map do |piece|
-
build_hunk(piece)
-
end
-
end
-
-
1
private
-
-
1
def diffs
-
Diff::LCS.diff(expected_lines, actual_lines)
-
end
-
-
1
def expected_lines
-
@expected.split("\n").map! { |e| e.chomp }
-
end
-
-
1
def actual_lines
-
@actual.split("\n").map! { |e| e.chomp }
-
end
-
-
1
def build_hunk(piece)
-
Diff::LCS::Hunk.new(
-
expected_lines, actual_lines, piece, context_lines, @file_length_difference
-
).tap do |h|
-
@file_length_difference = h.file_length_difference
-
end
-
end
-
-
1
def context_lines
-
3
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Support
-
# @private
-
1
def self.matcher_definitions
-
2
@matcher_definitions ||= []
-
end
-
-
# Used internally to break cyclic dependency between mocks, expectations,
-
# and support. We don't currently have a consistent implementation of our
-
# matchers, though we are considering changing that:
-
# https://github.com/rspec/rspec-mocks/issues/513
-
#
-
# @private
-
1
def self.register_matcher_definition(&block)
-
2
matcher_definitions << block
-
end
-
-
# Remove a previously registered matcher. Useful for cleaning up after
-
# yourself in specs.
-
#
-
# @private
-
1
def self.deregister_matcher_definition(&block)
-
matcher_definitions.delete(block)
-
end
-
-
# @private
-
1
def self.is_a_matcher?(object)
-
matcher_definitions.any? { |md| md.call(object) }
-
end
-
end
-
end
-
1
require 'rspec/support'
-
1
RSpec::Support.require_rspec_support "ruby_features"
-
1
RSpec::Support.require_rspec_support "matcher_definition"
-
-
1
module RSpec
-
1
module Support
-
# Extracts info about the number of arguments and allowed/required
-
# keyword args of a given method.
-
#
-
# @private
-
1
class MethodSignature
-
1
attr_reader :min_non_kw_args, :max_non_kw_args
-
-
1
def initialize(method)
-
2
@method = method
-
2
classify_parameters
-
end
-
-
1
def non_kw_args_arity_description
-
case max_non_kw_args
-
when min_non_kw_args then min_non_kw_args.to_s
-
when INFINITY then "#{min_non_kw_args} or more"
-
else "#{min_non_kw_args} to #{max_non_kw_args}"
-
end
-
end
-
-
1
def valid_non_kw_args?(positional_arg_count)
-
min_non_kw_args <= positional_arg_count &&
-
2
positional_arg_count <= max_non_kw_args
-
end
-
-
1
if RubyFeatures.optional_and_splat_args_supported?
-
1
def description
-
@description ||= begin
-
parts = []
-
-
unless non_kw_args_arity_description == "0"
-
parts << "arity of #{non_kw_args_arity_description}"
-
end
-
-
if @optional_kw_args.any?
-
parts << "optional keyword args (#{@optional_kw_args.map(&:inspect).join(", ")})"
-
end
-
-
if @required_kw_args.any?
-
parts << "required keyword args (#{@required_kw_args.map(&:inspect).join(", ")})"
-
end
-
-
parts << "any additional keyword args" if @allows_any_kw_args
-
-
parts.join(" and ")
-
end
-
end
-
-
1
def missing_kw_args_from(given_kw_args)
-
2
@required_kw_args - given_kw_args
-
end
-
-
1
def invalid_kw_args_from(given_kw_args)
-
2
return [] if @allows_any_kw_args
-
2
given_kw_args - @allowed_kw_args
-
end
-
-
1
def has_kw_args_in?(args)
-
2
Hash === args.last && could_contain_kw_args?(args)
-
end
-
-
# Without considering what the last arg is, could it
-
# contain keyword arguments?
-
1
def could_contain_kw_args?(args)
-
return false if args.count <= min_non_kw_args
-
@allows_any_kw_args || @allowed_kw_args.any?
-
end
-
-
1
def classify_parameters
-
2
optional_non_kw_args = @min_non_kw_args = 0
-
2
@optional_kw_args, @required_kw_args = [], []
-
2
@allows_any_kw_args = false
-
-
2
@method.parameters.each do |(type, name)|
-
case type
-
# def foo(a:)
-
when :keyreq then @required_kw_args << name
-
# def foo(a: 1)
-
when :key then @optional_kw_args << name
-
# def foo(**kw_args)
-
when :keyrest then @allows_any_kw_args = true
-
# def foo(a)
-
when :req then @min_non_kw_args += 1
-
# def foo(a = 1)
-
when :opt then optional_non_kw_args += 1
-
# def foo(*a)
-
when :rest then optional_non_kw_args = INFINITY
-
end
-
end
-
-
2
@max_non_kw_args = @min_non_kw_args + optional_non_kw_args
-
2
@allowed_kw_args = @required_kw_args + @optional_kw_args
-
end
-
else
-
def description
-
"arity of #{non_kw_args_arity_description}"
-
end
-
-
def missing_kw_args_from(_given_kw_args)
-
[]
-
end
-
-
def invalid_kw_args_from(_given_kw_args)
-
[]
-
end
-
-
def has_kw_args_in?(_args)
-
false
-
end
-
-
def classify_parameters
-
arity = @method.arity
-
if arity < 0
-
# `~` inverts the one's complement and gives us the
-
# number of required args
-
@min_non_kw_args = ~arity
-
@max_non_kw_args = INFINITY
-
else
-
@min_non_kw_args = arity
-
@max_non_kw_args = arity
-
end
-
end
-
end
-
-
1
INFINITY = 1 / 0.0
-
end
-
-
# Deals with the slightly different semantics of block arguments.
-
# For methods, arguments are required unless a default value is provided.
-
# For blocks, arguments are optional, even if no default value is provided.
-
#
-
# However, we want to treat block args as required since you virtually
-
# always want to pass a value for each received argument and our
-
# `and_yield` has treated block args as required for many years.
-
#
-
# @api private
-
1
class BlockSignature < MethodSignature
-
1
if RubyFeatures.optional_and_splat_args_supported?
-
1
def classify_parameters
-
super
-
@min_non_kw_args = @max_non_kw_args unless @max_non_kw_args == INFINITY
-
end
-
end
-
end
-
-
# Abstract base class for signature verifiers.
-
#
-
# @api private
-
1
class MethodSignatureVerifier
-
1
attr_reader :non_kw_args, :kw_args
-
-
1
def initialize(signature, args)
-
2
@signature = signature
-
2
@non_kw_args, @kw_args = split_args(*args)
-
end
-
-
1
def valid?
-
missing_kw_args.empty? &&
-
2
invalid_kw_args.empty? &&
-
valid_non_kw_args?
-
end
-
-
1
def error_message
-
if missing_kw_args.any?
-
"Missing required keyword arguments: %s" % [
-
missing_kw_args.join(", ")
-
]
-
elsif invalid_kw_args.any?
-
"Invalid keyword arguments provided: %s" % [
-
invalid_kw_args.join(", ")
-
]
-
elsif !valid_non_kw_args?
-
"Wrong number of arguments. Expected %s, got %s." % [
-
@signature.non_kw_args_arity_description,
-
non_kw_args.length
-
]
-
end
-
end
-
-
1
private
-
-
1
def valid_non_kw_args?
-
2
@signature.valid_non_kw_args?(non_kw_args.length)
-
end
-
-
1
def missing_kw_args
-
2
@signature.missing_kw_args_from(kw_args)
-
end
-
-
1
def invalid_kw_args
-
2
@signature.invalid_kw_args_from(kw_args)
-
end
-
-
1
def split_args(*args)
-
2
kw_args = if @signature.has_kw_args_in?(args)
-
args.pop.keys
-
else
-
2
[]
-
end
-
-
2
[args, kw_args]
-
end
-
end
-
-
# Figures out wether a given method can accept various arguments.
-
# Surprisingly non-trivial.
-
#
-
# @private
-
1
StrictSignatureVerifier = MethodSignatureVerifier
-
-
# Allows matchers to be used instead of providing keyword arguments. In
-
# practice, when this happens only the arity of the method is verified.
-
#
-
# @private
-
1
class LooseSignatureVerifier < MethodSignatureVerifier
-
1
private
-
-
1
def split_args(*args)
-
if RSpec::Support.is_a_matcher?(args.last) && @signature.could_contain_kw_args?(args)
-
args.pop
-
@signature = SignatureWithKeywordArgumentsMatcher.new(@signature)
-
end
-
-
super(*args)
-
end
-
-
# If a matcher is used in a signature in place of keyword arguments, all
-
# keyword argument validation needs to be skipped since the matcher is
-
# opaque.
-
#
-
# Instead, keyword arguments will be validated when the method is called
-
# and they are actually known.
-
#
-
# @private
-
1
class SignatureWithKeywordArgumentsMatcher
-
1
def initialize(signature)
-
@signature = signature
-
end
-
-
1
def missing_kw_args_from(_kw_args)
-
[]
-
end
-
-
1
def invalid_kw_args_from(_kw_args)
-
[]
-
end
-
-
1
def non_kw_args_arity_description
-
@signature.non_kw_args_arity_description
-
end
-
-
1
def valid_non_kw_args?(*args)
-
@signature.valid_non_kw_args?(*args)
-
end
-
-
1
def has_kw_args_in?(args)
-
@signature.has_kw_args_in?(args)
-
end
-
end
-
end
-
end
-
end
-
1
module RSpec
-
1
module Support
-
1
LibraryVersionTooLowError = Class.new(StandardError)
-
-
# @private
-
1
class VersionChecker
-
1
def initialize(library_name, library_version, min_patch_level)
-
1
@library_name, @library_version = library_name, library_version
-
1
@min_patch_level = min_patch_level
-
-
1
@major, @minor, @patch = parse_version(library_version)
-
1
@min_major, @min_minor, @min_patch = parse_version(min_patch_level)
-
-
1
@comparison_result = compare_version
-
end
-
-
1
def check_version!
-
1
raise_too_low_error if too_low?
-
end
-
-
1
private
-
-
1
def too_low?
-
1
@comparison_result == :too_low
-
end
-
-
1
def raise_too_low_error
-
raise LibraryVersionTooLowError,
-
"You are using #{@library_name} #{@library_version}. " \
-
"RSpec requires version #{version_requirement}."
-
end
-
-
1
def compare_version
-
case
-
when @major < @min_major then :too_low
-
when @major > @min_major then :ok
-
when @minor < @min_minor then :too_low
-
1
when @minor > @min_minor then :ok
-
when @patch < @min_patch then :too_low
-
else :ok
-
1
end
-
end
-
-
1
def version_requirement
-
">= #{@min_patch_level}"
-
end
-
-
1
def parse_version(version)
-
8
version.split('.').map { |v| v.to_i }
-
end
-
end
-
end
-
end
-
1
require 'delegate'
-
1
require 'singleton'
-
1
require 'tempfile'
-
1
require 'fileutils'
-
1
require 'stringio'
-
1
require 'zlib'
-
1
require 'zip/dos_time'
-
1
require 'zip/ioextras'
-
1
require 'rbconfig'
-
1
require 'zip/entry'
-
1
require 'zip/extra_field'
-
1
require 'zip/entry_set'
-
1
require 'zip/central_directory'
-
1
require 'zip/file'
-
1
require 'zip/input_stream'
-
1
require 'zip/output_stream'
-
1
require 'zip/decompressor'
-
1
require 'zip/compressor'
-
1
require 'zip/null_decompressor'
-
1
require 'zip/null_compressor'
-
1
require 'zip/null_input_stream'
-
1
require 'zip/pass_thru_compressor'
-
1
require 'zip/pass_thru_decompressor'
-
1
require 'zip/inflater'
-
1
require 'zip/deflater'
-
1
require 'zip/streamable_stream'
-
1
require 'zip/streamable_directory'
-
1
require 'zip/constants'
-
1
require 'zip/errors'
-
1
if defined? JRUBY_VERSION
-
require 'jruby'
-
JRuby.objectspace = true
-
end
-
-
1
module Zip
-
1
extend self
-
1
attr_accessor :unicode_names, :on_exists_proc, :continue_on_exists_proc, :sort_entries, :default_compression, :write_zip64_support
-
-
1
def reset!
-
1
@_ran_once = false
-
1
@unicode_names = false
-
1
@on_exists_proc = false
-
1
@continue_on_exists_proc = false
-
1
@sort_entries = false
-
1
@default_compression = ::Zlib::DEFAULT_COMPRESSION
-
1
@write_zip64_support = false
-
end
-
-
1
def setup
-
yield self unless @_ran_once
-
@_ran_once = true
-
end
-
-
1
reset!
-
end
-
-
# Copyright (C) 2002, 2003 Thomas Sondergaard
-
# rubyzip is free software; you can redistribute it and/or
-
# modify it under the terms of the ruby license.
-
1
module Zip
-
1
class CentralDirectory
-
1
include Enumerable
-
-
1
END_OF_CDS = 0x06054b50
-
1
ZIP64_END_OF_CDS = 0x06064b50
-
1
ZIP64_EOCD_LOCATOR = 0x07064b50
-
1
MAX_END_OF_CDS_SIZE = 65536 + 18
-
1
STATIC_EOCD_SIZE = 22
-
-
1
attr_reader :comment
-
-
# Returns an Enumerable containing the entries.
-
1
def entries
-
@entry_set.entries
-
end
-
-
1
def initialize(entries = EntrySet.new, comment = '') #:nodoc:
-
super()
-
@entry_set = entries.kind_of?(EntrySet) ? entries : EntrySet.new(entries)
-
@comment = comment
-
end
-
-
1
def write_to_stream(io) #:nodoc:
-
cdir_offset = io.tell
-
@entry_set.each { |entry| entry.write_c_dir_entry(io) }
-
eocd_offset = io.tell
-
cdir_size = eocd_offset - cdir_offset
-
if ::Zip.write_zip64_support
-
need_zip64_eocd = cdir_offset > 0xFFFFFFFF || cdir_size > 0xFFFFFFFF || @entry_set.size > 0xFFFF
-
need_zip64_eocd ||= @entry_set.any? { |entry| entry.extra['Zip64'] }
-
if need_zip64_eocd
-
write_64_e_o_c_d(io, cdir_offset, cdir_size)
-
write_64_eocd_locator(io, eocd_offset)
-
end
-
end
-
write_e_o_c_d(io, cdir_offset, cdir_size)
-
end
-
-
1
def write_e_o_c_d(io, offset, cdir_size) #:nodoc:
-
tmp = [
-
END_OF_CDS,
-
0, # @numberOfThisDisk
-
0, # @numberOfDiskWithStartOfCDir
-
@entry_set ? [@entry_set.size, 0xFFFF].min : 0,
-
@entry_set ? [@entry_set.size, 0xFFFF].min : 0,
-
[cdir_size, 0xFFFFFFFF].min,
-
[offset, 0xFFFFFFFF].min,
-
@comment ? @comment.length : 0
-
]
-
io << tmp.pack('VvvvvVVv')
-
io << @comment
-
end
-
-
1
private :write_e_o_c_d
-
-
1
def write_64_e_o_c_d(io, offset, cdir_size) #:nodoc:
-
tmp = [
-
ZIP64_END_OF_CDS,
-
44, # size of zip64 end of central directory record (excludes signature and field itself)
-
VERSION_MADE_BY,
-
VERSION_NEEDED_TO_EXTRACT_ZIP64,
-
0, # @numberOfThisDisk
-
0, # @numberOfDiskWithStartOfCDir
-
@entry_set ? @entry_set.size : 0, # number of entries on this disk
-
@entry_set ? @entry_set.size : 0, # number of entries total
-
cdir_size, # size of central directory
-
offset, # offset of start of central directory in its disk
-
]
-
io << tmp.pack('VQ<vvVVQ<Q<Q<Q<')
-
end
-
-
1
private :write_64_e_o_c_d
-
-
1
def write_64_eocd_locator(io, zip64_eocd_offset)
-
tmp = [
-
ZIP64_EOCD_LOCATOR,
-
0, # number of disk containing the start of zip64 eocd record
-
zip64_eocd_offset, # offset of the start of zip64 eocd record in its disk
-
1 # total number of disks
-
]
-
io << tmp.pack('VVQ<V')
-
end
-
-
1
private :write_64_eocd_locator
-
-
1
def read_64_e_o_c_d(buf) #:nodoc:
-
buf = get_64_e_o_c_d(buf)
-
@size_of_zip64_e_o_c_d = Entry.read_zip_64_long(buf)
-
@version_made_by = Entry.read_zip_short(buf)
-
@version_needed_for_extract = Entry.read_zip_short(buf)
-
@number_of_this_disk = Entry.read_zip_long(buf)
-
@number_of_disk_with_start_of_cdir = Entry.read_zip_long(buf)
-
@total_number_of_entries_in_cdir_on_this_disk = Entry.read_zip_64_long(buf)
-
@size = Entry.read_zip_64_long(buf)
-
@size_in_bytes = Entry.read_zip_64_long(buf)
-
@cdir_offset = Entry.read_zip_64_long(buf)
-
@zip_64_extensible = buf.slice!(0, buf.bytesize)
-
raise Error, "Zip consistency problem while reading eocd structure" unless buf.size == 0
-
end
-
-
1
def read_e_o_c_d(buf) #:nodoc:
-
buf = get_e_o_c_d(buf)
-
@number_of_this_disk = Entry.read_zip_short(buf)
-
@number_of_disk_with_start_of_cdir = Entry.read_zip_short(buf)
-
@total_number_of_entries_in_cdir_on_this_disk = Entry.read_zip_short(buf)
-
@size = Entry.read_zip_short(buf)
-
@size_in_bytes = Entry.read_zip_long(buf)
-
@cdir_offset = Entry.read_zip_long(buf)
-
comment_length = Entry.read_zip_short(buf)
-
@comment = if comment_length.to_i <= 0
-
buf.slice!(0, buf.size)
-
else
-
buf.read(comment_length)
-
end
-
raise Error, "Zip consistency problem while reading eocd structure" unless buf.size == 0
-
end
-
-
1
def read_central_directory_entries(io) #:nodoc:
-
begin
-
io.seek(@cdir_offset, IO::SEEK_SET)
-
rescue Errno::EINVAL
-
raise Error, "Zip consistency problem while reading central directory entry"
-
end
-
@entry_set = EntrySet.new
-
@size.times do
-
@entry_set << Entry.read_c_dir_entry(io)
-
end
-
end
-
-
1
def read_from_stream(io) #:nodoc:
-
buf = start_buf(io)
-
if self.zip64_file?(buf)
-
read_64_e_o_c_d(buf)
-
else
-
read_e_o_c_d(buf)
-
end
-
read_central_directory_entries(io)
-
end
-
-
1
def get_e_o_c_d(buf) #:nodoc:
-
sig_index = buf.rindex([END_OF_CDS].pack('V'))
-
raise Error, "Zip end of central directory signature not found" unless sig_index
-
buf = buf.slice!((sig_index + 4)..(buf.bytesize))
-
-
def buf.read(count)
-
slice!(0, count)
-
end
-
-
buf
-
end
-
-
1
def zip64_file?(buf)
-
buf.rindex([ZIP64_END_OF_CDS].pack('V')) && buf.rindex([ZIP64_EOCD_LOCATOR].pack('V'))
-
end
-
-
1
def start_buf(io)
-
begin
-
io.seek(-MAX_END_OF_CDS_SIZE, IO::SEEK_END)
-
rescue Errno::EINVAL
-
io.seek(0, IO::SEEK_SET)
-
end
-
io.read
-
end
-
-
1
def get_64_e_o_c_d(buf) #:nodoc:
-
zip_64_start = buf.rindex([ZIP64_END_OF_CDS].pack('V'))
-
raise Error, "Zip64 end of central directory signature not found" unless zip_64_start
-
zip_64_locator = buf.rindex([ZIP64_EOCD_LOCATOR].pack('V'))
-
raise Error, "Zip64 end of central directory signature locator not found" unless zip_64_locator
-
buf = buf.slice!((zip_64_start + 4)..zip_64_locator)
-
-
def buf.read(count)
-
slice!(0, count)
-
end
-
-
buf
-
end
-
-
# For iterating over the entries.
-
1
def each(&proc)
-
@entry_set.each(&proc)
-
end
-
-
# Returns the number of entries in the central directory (and
-
# consequently in the zip archive).
-
1
def size
-
@entry_set.size
-
end
-
-
1
def self.read_from_stream(io) #:nodoc:
-
cdir = new
-
cdir.read_from_stream(io)
-
return cdir
-
rescue Error
-
return nil
-
end
-
-
1
def ==(other) #:nodoc:
-
return false unless other.kind_of?(CentralDirectory)
-
@entry_set.entries.sort == other.entries.sort && comment == other.comment
-
end
-
end
-
end
-
-
# Copyright (C) 2002, 2003 Thomas Sondergaard
-
# rubyzip is free software; you can redistribute it and/or
-
# modify it under the terms of the ruby license.
-
1
module Zip
-
1
class Compressor #:nodoc:all
-
1
def finish
-
end
-
end
-
end
-
-
# Copyright (C) 2002, 2003 Thomas Sondergaard
-
# rubyzip is free software; you can redistribute it and/or
-
# modify it under the terms of the ruby license.
-
1
module Zip
-
1
RUNNING_ON_WINDOWS = RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/i
-
-
1
CENTRAL_DIRECTORY_ENTRY_SIGNATURE = 0x02014b50
-
1
CDIR_ENTRY_STATIC_HEADER_LENGTH = 46
-
-
1
LOCAL_ENTRY_SIGNATURE = 0x04034b50
-
1
LOCAL_ENTRY_STATIC_HEADER_LENGTH = 30
-
1
LOCAL_ENTRY_TRAILING_DESCRIPTOR_LENGTH = 4+4+4
-
1
VERSION_MADE_BY = 52 # this library's version
-
1
VERSION_NEEDED_TO_EXTRACT = 20
-
1
VERSION_NEEDED_TO_EXTRACT_ZIP64 = 45
-
-
1
FILE_TYPE_FILE = 010
-
1
FILE_TYPE_DIR = 004
-
1
FILE_TYPE_SYMLINK = 012
-
-
1
FSTYPE_FAT = 0
-
1
FSTYPE_AMIGA = 1
-
1
FSTYPE_VMS = 2
-
1
FSTYPE_UNIX = 3
-
1
FSTYPE_VM_CMS = 4
-
1
FSTYPE_ATARI = 5
-
1
FSTYPE_HPFS = 6
-
1
FSTYPE_MAC = 7
-
1
FSTYPE_Z_SYSTEM = 8
-
1
FSTYPE_CPM = 9
-
1
FSTYPE_TOPS20 = 10
-
1
FSTYPE_NTFS = 11
-
1
FSTYPE_QDOS = 12
-
1
FSTYPE_ACORN = 13
-
1
FSTYPE_VFAT = 14
-
1
FSTYPE_MVS = 15
-
1
FSTYPE_BEOS = 16
-
1
FSTYPE_TANDEM = 17
-
1
FSTYPE_THEOS = 18
-
1
FSTYPE_MAC_OSX = 19
-
1
FSTYPE_ATHEOS = 30
-
-
1
FSTYPES = {
-
FSTYPE_FAT => 'FAT'.freeze,
-
FSTYPE_AMIGA => 'Amiga'.freeze,
-
FSTYPE_VMS => 'VMS (Vax or Alpha AXP)'.freeze,
-
FSTYPE_UNIX => 'Unix'.freeze,
-
FSTYPE_VM_CMS => 'VM/CMS'.freeze,
-
FSTYPE_ATARI => 'Atari ST'.freeze,
-
FSTYPE_HPFS => 'OS/2 or NT HPFS'.freeze,
-
FSTYPE_MAC => 'Macintosh'.freeze,
-
FSTYPE_Z_SYSTEM => 'Z-System'.freeze,
-
FSTYPE_CPM => 'CP/M'.freeze,
-
FSTYPE_TOPS20 => 'TOPS-20'.freeze,
-
FSTYPE_NTFS => 'NTFS'.freeze,
-
FSTYPE_QDOS => 'SMS/QDOS'.freeze,
-
FSTYPE_ACORN => 'Acorn RISC OS'.freeze,
-
FSTYPE_VFAT => 'Win32 VFAT'.freeze,
-
FSTYPE_MVS => 'MVS'.freeze,
-
FSTYPE_BEOS => 'BeOS'.freeze,
-
FSTYPE_TANDEM => 'Tandem NSK'.freeze,
-
FSTYPE_THEOS => 'Theos'.freeze,
-
FSTYPE_MAC_OSX => 'Mac OS/X (Darwin)'.freeze,
-
FSTYPE_ATHEOS => 'AtheOS'.freeze,
-
}.freeze
-
end
-
1
module Zip
-
1
class Decompressor #:nodoc:all
-
1
CHUNK_SIZE = 32768
-
1
def initialize(input_stream)
-
super()
-
@input_stream=input_stream
-
end
-
end
-
end
-
-
# Copyright (C) 2002, 2003 Thomas Sondergaard
-
# rubyzip is free software; you can redistribute it and/or
-
# modify it under the terms of the ruby license.
-
1
module Zip
-
1
class Deflater < Compressor #:nodoc:all
-
-
1
def initialize(output_stream, level = Zip.default_compression)
-
super()
-
@output_stream = output_stream
-
@zlib_deflater = ::Zlib::Deflate.new(level, -::Zlib::MAX_WBITS)
-
@size = 0
-
@crc = ::Zlib.crc32
-
end
-
-
1
def << (data)
-
val = data.to_s
-
@crc = Zlib::crc32(val, @crc)
-
@size += val.bytesize
-
@output_stream << @zlib_deflater.deflate(data)
-
end
-
-
1
def finish
-
@output_stream << @zlib_deflater.finish until @zlib_deflater.finished?
-
end
-
-
1
attr_reader :size, :crc
-
end
-
end
-
-
# Copyright (C) 2002, 2003 Thomas Sondergaard
-
# rubyzip is free software; you can redistribute it and/or
-
# modify it under the terms of the ruby license.
-
1
module Zip
-
1
class DOSTime < Time #:nodoc:all
-
-
#MS-DOS File Date and Time format as used in Interrupt 21H Function 57H:
-
-
# Register CX, the Time:
-
# Bits 0-4 2 second increments (0-29)
-
# Bits 5-10 minutes (0-59)
-
# bits 11-15 hours (0-24)
-
-
# Register DX, the Date:
-
# Bits 0-4 day (1-31)
-
# bits 5-8 month (1-12)
-
# bits 9-15 year (four digit year minus 1980)
-
-
1
def to_binary_dos_time
-
(sec/2) +
-
(min << 5) +
-
(hour << 11)
-
end
-
-
1
def to_binary_dos_date
-
(day) +
-
(month << 5) +
-
((year - 1980) << 9)
-
end
-
-
# Dos time is only stored with two seconds accuracy
-
1
def dos_equals(other)
-
to_i/2 == other.to_i/2
-
end
-
-
1
def self.parse_binary_dos_format(binaryDosDate, binaryDosTime)
-
second = 2 * (0b11111 & binaryDosTime)
-
minute = (0b11111100000 & binaryDosTime) >> 5
-
hour = (0b1111100000000000 & binaryDosTime) >> 11
-
day = (0b11111 & binaryDosDate)
-
month = (0b111100000 & binaryDosDate) >> 5
-
year = ((0b1111111000000000 & binaryDosDate) >> 9) + 1980
-
begin
-
self.local(year, month, day, hour, minute, second)
-
end
-
end
-
end
-
end
-
-
# Copyright (C) 2002, 2003 Thomas Sondergaard
-
# rubyzip is free software; you can redistribute it and/or
-
# modify it under the terms of the ruby license.
-
1
module Zip
-
1
class Entry
-
1
STORED = 0
-
1
DEFLATED = 8
-
# Language encoding flag (EFS) bit
-
1
EFS = 0b100000000000
-
-
1
attr_accessor :comment, :compressed_size, :crc, :extra, :compression_method,
-
:name, :size, :local_header_offset, :zipfile, :fstype, :external_file_attributes,
-
:gp_flags, :header_signature, :follow_symlinks,
-
:restore_times, :restore_permissions, :restore_ownership,
-
:unix_uid, :unix_gid, :unix_perms,
-
:dirty
-
1
attr_reader :ftype, :filepath # :nodoc:
-
-
1
def set_default_vars_values
-
@local_header_offset = 0
-
@local_header_size = nil # not known until local entry is created or read
-
@internal_file_attributes = 1
-
@external_file_attributes = 0
-
@header_signature = ::Zip::CENTRAL_DIRECTORY_ENTRY_SIGNATURE
-
-
@version_needed_to_extract = VERSION_NEEDED_TO_EXTRACT
-
@version = VERSION_MADE_BY
-
-
@ftype = nil # unspecified or unknown
-
@filepath = nil
-
@gp_flags = 0
-
if ::Zip.unicode_names
-
@gp_flags |= EFS
-
@version = 63
-
end
-
@follow_symlinks = false
-
-
@restore_times = true
-
@restore_permissions = false
-
@restore_ownership = false
-
# BUG: need an extra field to support uid/gid's
-
@unix_uid = nil
-
@unix_gid = nil
-
@unix_perms = nil
-
#@posix_acl = nil
-
#@ntfs_acl = nil
-
@dirty = false
-
end
-
-
1
def check_name(name)
-
if name.start_with?('/')
-
raise ::Zip::EntryNameError, "Illegal ZipEntry name '#{name}', name must not start with /"
-
end
-
end
-
-
1
def initialize(*args)
-
name = args[1] || ''
-
check_name(name)
-
-
set_default_vars_values
-
@fstype = ::Zip::RUNNING_ON_WINDOWS ? ::Zip::FSTYPE_FAT : ::Zip::FSTYPE_UNIX
-
-
@zipfile = args[0] || ''
-
@name = name
-
@comment = args[2] || ''
-
@extra = args[3] || ''
-
@compressed_size = args[4] || 0
-
@crc = args[5] || 0
-
@compression_method = args[6] || ::Zip::Entry::DEFLATED
-
@size = args[7] || 0
-
@time = args[8] || ::Zip::DOSTime.now
-
-
@ftype = name_is_directory? ? :directory : :file
-
@extra = ::Zip::ExtraField.new(@extra.to_s) unless ::Zip::ExtraField === @extra
-
end
-
-
1
def time
-
if @extra['UniversalTime']
-
@extra['UniversalTime'].mtime
-
else
-
# Standard time field in central directory has local time
-
# under archive creator. Then, we can't get timezone.
-
@time
-
end
-
end
-
-
1
alias :mtime :time
-
-
1
def time=(value)
-
unless @extra.member?('UniversalTime')
-
@extra.create('UniversalTime')
-
end
-
@extra['UniversalTime'].mtime = value
-
@time = value
-
end
-
-
1
def file_type_is?(type)
-
raise InternalError, "current filetype is unknown: #{self.inspect}" unless @ftype
-
@ftype == type
-
end
-
-
# Dynamic checkers
-
1
%w(directory file symlink).each do |k|
-
3
define_method "#{k}?" do
-
file_type_is?(k.to_sym)
-
end
-
end
-
-
1
def name_is_directory? #:nodoc:all
-
@name.end_with?('/')
-
end
-
-
1
def local_entry_offset #:nodoc:all
-
local_header_offset + @local_header_size
-
end
-
-
1
def name_size
-
@name ? @name.bytesize : 0
-
end
-
-
1
def extra_size
-
@extra ? @extra.local_size : 0
-
end
-
-
1
def comment_size
-
@comment ? @comment.bytesize : 0
-
end
-
-
1
def calculate_local_header_size #:nodoc:all
-
LOCAL_ENTRY_STATIC_HEADER_LENGTH + name_size + extra_size
-
end
-
-
# check before rewriting an entry (after file sizes are known)
-
# that we didn't change the header size (and thus clobber file data or something)
-
1
def verify_local_header_size!
-
return if @local_header_size.nil?
-
new_size = calculate_local_header_size
-
raise Error, "local header size changed (#{@local_header_size} -> #{new_size})" if @local_header_size != new_size
-
end
-
-
1
def cdir_header_size #:nodoc:all
-
CDIR_ENTRY_STATIC_HEADER_LENGTH + name_size +
-
(@extra ? @extra.c_dir_size : 0) + comment_size
-
end
-
-
1
def next_header_offset #:nodoc:all
-
local_entry_offset + self.compressed_size
-
end
-
-
# Extracts entry to file dest_path (defaults to @name).
-
1
def extract(dest_path = @name, &block)
-
block ||= proc { ::Zip.on_exists_proc }
-
-
if directory? || file? || symlink?
-
self.__send__("create_#{@ftype}", dest_path, &block)
-
else
-
raise RuntimeError, "unknown file type #{self.inspect}"
-
end
-
-
self
-
end
-
-
1
def to_s
-
@name
-
end
-
-
1
protected
-
-
1
class << self
-
1
def read_zip_short(io) # :nodoc:
-
io.read(2).unpack('v')[0]
-
end
-
-
1
def read_zip_long(io) # :nodoc:
-
io.read(4).unpack('V')[0]
-
end
-
-
1
def read_zip_64_long(io) # :nodoc:
-
io.read(8).unpack('Q<')[0]
-
end
-
-
1
def read_c_dir_entry(io) #:nodoc:all
-
path = if io.is_a?(::IO)
-
io.path
-
else
-
io
-
end
-
entry = new(path)
-
entry.read_c_dir_entry(io)
-
entry
-
rescue Error
-
nil
-
end
-
-
1
def read_local_entry(io)
-
entry = self.new(io)
-
entry.read_local_entry(io)
-
entry
-
rescue Error
-
nil
-
end
-
-
end
-
-
1
public
-
-
1
def unpack_local_entry(buf)
-
@header_signature,
-
@version,
-
@fstype,
-
@gp_flags,
-
@compression_method,
-
@last_mod_time,
-
@last_mod_date,
-
@crc,
-
@compressed_size,
-
@size,
-
@name_length,
-
@extra_length = buf.unpack('VCCvvvvVVVvv')
-
end
-
-
1
def read_local_entry(io) #:nodoc:all
-
@local_header_offset = io.tell
-
-
static_sized_fields_buf = io.read(::Zip::LOCAL_ENTRY_STATIC_HEADER_LENGTH)
-
-
unless static_sized_fields_buf.bytesize == ::Zip::LOCAL_ENTRY_STATIC_HEADER_LENGTH
-
raise Error, "Premature end of file. Not enough data for zip entry local header"
-
end
-
-
unpack_local_entry(static_sized_fields_buf)
-
-
unless @header_signature == ::Zip::LOCAL_ENTRY_SIGNATURE
-
raise ::Zip::Error, "Zip local header magic not found at location '#{local_header_offset}'"
-
end
-
set_time(@last_mod_date, @last_mod_time)
-
-
@name = io.read(@name_length)
-
extra = io.read(@extra_length)
-
-
@name.gsub!('\\', '/')
-
-
if extra && extra.bytesize != @extra_length
-
raise ::Zip::Error, "Truncated local zip entry header"
-
else
-
if ::Zip::ExtraField === @extra
-
@extra.merge(extra)
-
else
-
@extra = ::Zip::ExtraField.new(extra)
-
end
-
end
-
parse_zip64_extra(true)
-
@local_header_size = calculate_local_header_size
-
end
-
-
1
def pack_local_entry
-
zip64 = @extra['Zip64']
-
[::Zip::LOCAL_ENTRY_SIGNATURE,
-
@version_needed_to_extract, # version needed to extract
-
@gp_flags, # @gp_flags ,
-
@compression_method,
-
@time.to_binary_dos_time, # @last_mod_time ,
-
@time.to_binary_dos_date, # @last_mod_date ,
-
@crc,
-
(zip64 && zip64.compressed_size) ? 0xFFFFFFFF : @compressed_size,
-
(zip64 && zip64.original_size) ? 0xFFFFFFFF : @size,
-
name_size,
-
@extra ? @extra.local_size : 0].pack('VvvvvvVVVvv')
-
end
-
-
1
def write_local_entry(io, rewrite = false) #:nodoc:all
-
prep_zip64_extra(true)
-
verify_local_header_size! if rewrite
-
@local_header_offset = io.tell
-
-
io << pack_local_entry
-
-
io << @name
-
io << @extra.to_local_bin if @extra
-
@local_header_size = io.tell - @local_header_offset
-
end
-
-
1
def unpack_c_dir_entry(buf)
-
@header_signature,
-
@version, # version of encoding software
-
@fstype, # filesystem type
-
@version_needed_to_extract,
-
@gp_flags,
-
@compression_method,
-
@last_mod_time,
-
@last_mod_date,
-
@crc,
-
@compressed_size,
-
@size,
-
@name_length,
-
@extra_length,
-
@comment_length,
-
_, # diskNumberStart
-
@internal_file_attributes,
-
@external_file_attributes,
-
@local_header_offset,
-
@name,
-
@extra,
-
@comment = buf.unpack('VCCvvvvvVVVvvvvvVV')
-
end
-
-
1
def set_ftype_from_c_dir_entry
-
@ftype = case @fstype
-
when ::Zip::FSTYPE_UNIX
-
@unix_perms = (@external_file_attributes >> 16) & 07777
-
case (@external_file_attributes >> 28)
-
when ::Zip::FILE_TYPE_DIR
-
:directory
-
when ::Zip::FILE_TYPE_FILE
-
:file
-
when ::Zip::FILE_TYPE_SYMLINK
-
:symlink
-
else
-
#best case guess for whether it is a file or not
-
#Otherwise this would be set to unknown and that entry would never be able to extracted
-
if name_is_directory?
-
:directory
-
else
-
:file
-
end
-
end
-
else
-
if name_is_directory?
-
:directory
-
else
-
:file
-
end
-
end
-
end
-
-
1
def check_c_dir_entry_static_header_length(buf)
-
unless buf.bytesize == ::Zip::CDIR_ENTRY_STATIC_HEADER_LENGTH
-
raise Error, 'Premature end of file. Not enough data for zip cdir entry header'
-
end
-
end
-
-
1
def check_c_dir_entry_signature
-
unless header_signature == ::Zip::CENTRAL_DIRECTORY_ENTRY_SIGNATURE
-
raise Error, "Zip local header magic not found at location '#{local_header_offset}'"
-
end
-
end
-
-
1
def check_c_dir_entry_comment_size
-
unless @comment && @comment.bytesize == @comment_length
-
raise ::Zip::Error, "Truncated cdir zip entry header"
-
end
-
end
-
-
1
def read_c_dir_extra_field(io)
-
if @extra.is_a?(::Zip::ExtraField)
-
@extra.merge(io.read(@extra_length))
-
else
-
@extra = ::Zip::ExtraField.new(io.read(@extra_length))
-
end
-
end
-
-
1
def read_c_dir_entry(io) #:nodoc:all
-
static_sized_fields_buf = io.read(::Zip::CDIR_ENTRY_STATIC_HEADER_LENGTH)
-
check_c_dir_entry_static_header_length(static_sized_fields_buf)
-
unpack_c_dir_entry(static_sized_fields_buf)
-
check_c_dir_entry_signature
-
set_time(@last_mod_date, @last_mod_time)
-
@name = io.read(@name_length).gsub('\\', '/')
-
read_c_dir_extra_field(io)
-
@comment = io.read(@comment_length)
-
check_c_dir_entry_comment_size
-
set_ftype_from_c_dir_entry
-
parse_zip64_extra(false)
-
end
-
-
1
def file_stat(path) # :nodoc:
-
if @follow_symlinks
-
::File::stat(path)
-
else
-
::File::lstat(path)
-
end
-
end
-
-
1
def get_extra_attributes_from_path(path) # :nodoc:
-
unless Zip::RUNNING_ON_WINDOWS
-
stat = file_stat(path)
-
@unix_uid = stat.uid
-
@unix_gid = stat.gid
-
@unix_perms = stat.mode & 07777
-
end
-
end
-
-
1
def set_unix_permissions_on_path(dest_path)
-
# BUG: does not update timestamps into account
-
# ignore setuid/setgid bits by default. honor if @restore_ownership
-
unix_perms_mask = 01777
-
unix_perms_mask = 07777 if @restore_ownership
-
::FileUtils.chmod(@unix_perms & unix_perms_mask, dest_path) if @restore_permissions && @unix_perms
-
::FileUtils.chown(@unix_uid, @unix_gid, dest_path) if @restore_ownership && @unix_uid && @unix_gid && ::Process.egid == 0
-
# File::utimes()
-
end
-
-
1
def set_extra_attributes_on_path(dest_path) # :nodoc:
-
return unless (file? || directory?)
-
-
case @fstype
-
when ::Zip::FSTYPE_UNIX
-
set_unix_permissions_on_path(dest_path)
-
end
-
end
-
-
1
def pack_c_dir_entry
-
zip64 = @extra['Zip64']
-
[
-
@header_signature,
-
@version, # version of encoding software
-
@fstype, # filesystem type
-
@version_needed_to_extract, # @versionNeededToExtract ,
-
@gp_flags, # @gp_flags ,
-
@compression_method,
-
@time.to_binary_dos_time, # @last_mod_time ,
-
@time.to_binary_dos_date, # @last_mod_date ,
-
@crc,
-
(zip64 && zip64.compressed_size) ? 0xFFFFFFFF : @compressed_size,
-
(zip64 && zip64.original_size) ? 0xFFFFFFFF : @size,
-
name_size,
-
@extra ? @extra.c_dir_size : 0,
-
comment_size,
-
(zip64 && zip64.disk_start_number) ? 0xFFFF : 0, # disk number start
-
@internal_file_attributes, # file type (binary=0, text=1)
-
@external_file_attributes, # native filesystem attributes
-
(zip64 && zip64.relative_header_offset) ? 0xFFFFFFFF : @local_header_offset,
-
@name,
-
@extra,
-
@comment
-
].pack('VCCvvvvvVVVvvvvvVV')
-
end
-
-
1
def write_c_dir_entry(io) #:nodoc:all
-
prep_zip64_extra(false)
-
case @fstype
-
when ::Zip::FSTYPE_UNIX
-
ft = case @ftype
-
when :file
-
@unix_perms ||= 0644
-
::Zip::FILE_TYPE_FILE
-
when :directory
-
@unix_perms ||= 0755
-
::Zip::FILE_TYPE_DIR
-
when :symlink
-
@unix_perms ||= 0755
-
::Zip::FILE_TYPE_SYMLINK
-
end
-
-
unless ft.nil?
-
@external_file_attributes = (ft << 12 | (@unix_perms & 07777)) << 16
-
end
-
end
-
-
io << pack_c_dir_entry
-
-
io << @name
-
io << (@extra ? @extra.to_c_dir_bin : '')
-
io << @comment
-
end
-
-
1
def ==(other)
-
return false unless other.class == self.class
-
# Compares contents of local entry and exposed fields
-
keys_equal = %w(compression_method crc compressed_size size name extra filepath).all? do |k|
-
other.__send__(k.to_sym) == self.__send__(k.to_sym)
-
end
-
keys_equal && self.time.dos_equals(other.time)
-
end
-
-
1
def <=> (other)
-
self.to_s <=> other.to_s
-
end
-
-
# Returns an IO like object for the given ZipEntry.
-
# Warning: may behave weird with symlinks.
-
1
def get_input_stream(&block)
-
if @ftype == :directory
-
yield ::Zip::NullInputStream if block_given?
-
::Zip::NullInputStream
-
elsif @filepath
-
case @ftype
-
when :file
-
::File.open(@filepath, 'rb', &block)
-
when :symlink
-
linkpath = ::File.readlink(@filepath)
-
stringio = ::StringIO.new(linkpath)
-
yield(stringio) if block_given?
-
stringio
-
else
-
raise "unknown @file_type #{@ftype}"
-
end
-
else
-
zis = ::Zip::InputStream.new(@zipfile, local_header_offset)
-
zis.get_next_entry
-
if block_given?
-
begin
-
yield(zis)
-
ensure
-
zis.close
-
end
-
else
-
zis
-
end
-
end
-
end
-
-
1
def gather_fileinfo_from_srcpath(src_path) # :nodoc:
-
stat = file_stat(src_path)
-
@ftype = case stat.ftype
-
when 'file'
-
if name_is_directory?
-
raise ArgumentError,
-
"entry name '#{newEntry}' indicates directory entry, but "+
-
"'#{src_path}' is not a directory"
-
end
-
:file
-
when 'directory'
-
@name += '/' unless name_is_directory?
-
:directory
-
when 'link'
-
if name_is_directory?
-
raise ArgumentError,
-
"entry name '#{newEntry}' indicates directory entry, but "+
-
"'#{src_path}' is not a directory"
-
end
-
:symlink
-
else
-
raise RuntimeError, "unknown file type: #{src_path.inspect} #{stat.inspect}"
-
end
-
-
@filepath = src_path
-
get_extra_attributes_from_path(@filepath)
-
end
-
-
1
def write_to_zip_output_stream(zip_output_stream) #:nodoc:all
-
if @ftype == :directory
-
zip_output_stream.put_next_entry(self, nil, nil, ::Zip::Entry::STORED)
-
elsif @filepath
-
zip_output_stream.put_next_entry(self, nil, nil, self.compression_method || ::Zip::Entry::DEFLATED)
-
get_input_stream { |is| ::Zip::IOExtras.copy_stream(zip_output_stream, is) }
-
else
-
zip_output_stream.copy_raw_entry(self)
-
end
-
end
-
-
1
def parent_as_string
-
entry_name = name.chomp('/')
-
slash_index = entry_name.rindex('/')
-
slash_index ? entry_name.slice(0, slash_index+1) : nil
-
end
-
-
1
def get_raw_input_stream(&block)
-
if @zipfile.is_a?(::IO) || @zipfile.is_a?(::StringIO)
-
yield @zipfile
-
else
-
::File.open(@zipfile, "rb", &block)
-
end
-
end
-
-
1
def clean_up
-
# By default, do nothing
-
end
-
-
1
private
-
-
1
def set_time(binary_dos_date, binary_dos_time)
-
@time = ::Zip::DOSTime.parse_binary_dos_format(binary_dos_date, binary_dos_time)
-
rescue ArgumentError
-
puts "Invalid date/time in zip entry"
-
end
-
-
1
def create_file(dest_path, continue_on_exists_proc = proc { Zip.continue_on_exists_proc })
-
if ::File.exist?(dest_path) && !yield(self, dest_path)
-
raise ::Zip::DestinationFileExistsError,
-
"Destination '#{dest_path}' already exists"
-
end
-
::File.open(dest_path, "wb") do |os|
-
get_input_stream do |is|
-
set_extra_attributes_on_path(dest_path)
-
-
buf = ''
-
while buf = is.sysread(::Zip::Decompressor::CHUNK_SIZE, buf)
-
os << buf
-
end
-
end
-
end
-
end
-
-
1
def create_directory(dest_path)
-
return if ::File.directory?(dest_path)
-
if ::File.exist?(dest_path)
-
if block_given? && yield(self, dest_path)
-
::FileUtils::rm_f dest_path
-
else
-
raise ::Zip::DestinationFileExistsError,
-
"Cannot create directory '#{dest_path}'. "+
-
"A file already exists with that name"
-
end
-
end
-
::FileUtils.mkdir_p(dest_path)
-
set_extra_attributes_on_path(dest_path)
-
end
-
-
# BUG: create_symlink() does not use &block
-
1
def create_symlink(dest_path)
-
stat = nil
-
begin
-
stat = ::File.lstat(dest_path)
-
rescue Errno::ENOENT
-
end
-
-
io = get_input_stream
-
linkto = io.read
-
-
if stat
-
if stat.symlink?
-
if ::File.readlink(dest_path) == linkto
-
return
-
else
-
raise ::Zip::DestinationFileExistsError,
-
"Cannot create symlink '#{dest_path}'. "+
-
"A symlink already exists with that name"
-
end
-
else
-
raise ::Zip::DestinationFileExistsError,
-
"Cannot create symlink '#{dest_path}'. "+
-
"A file already exists with that name"
-
end
-
end
-
-
::File.symlink(linkto, dest_path)
-
end
-
-
# apply missing data from the zip64 extra information field, if present
-
# (required when file sizes exceed 2**32, but can be used for all files)
-
1
def parse_zip64_extra(for_local_header) #:nodoc:all
-
if zip64 = @extra['Zip64']
-
if for_local_header
-
@size, @compressed_size = zip64.parse(@size, @compressed_size)
-
else
-
@size, @compressed_size, @local_header_offset = zip64.parse(@size, @compressed_size, @local_header_offset)
-
end
-
end
-
end
-
-
# create a zip64 extra information field if we need one
-
1
def prep_zip64_extra(for_local_header) #:nodoc:all
-
return unless ::Zip.write_zip64_support
-
need_zip64 = @size >= 0xFFFFFFFF || @compressed_size >= 0xFFFFFFFF
-
unless for_local_header
-
need_zip64 ||= @local_header_offset >= 0xFFFFFFFF
-
end
-
-
if need_zip64
-
@version_needed_to_extract = VERSION_NEEDED_TO_EXTRACT_ZIP64
-
@extra.delete('Zip64Placeholder')
-
zip64 = @extra.create('Zip64')
-
if for_local_header
-
# local header always includes size and compressed size
-
zip64.original_size = @size
-
zip64.compressed_size = @compressed_size
-
else
-
# central directory entry entries include whichever fields are necessary
-
zip64.original_size = @size if @size >= 0xFFFFFFFF
-
zip64.compressed_size = @compressed_size if @compressed_size >= 0xFFFFFFFF
-
zip64.relative_header_offset = @local_header_offset if @local_header_offset >= 0xFFFFFFFF
-
end
-
else
-
@extra.delete('Zip64')
-
-
# if this is a local header entry, create a placeholder
-
# so we have room to write a zip64 extra field afterward
-
# (we won't know if it's needed until the file data is written)
-
if for_local_header
-
@extra.create('Zip64Placeholder')
-
else
-
@extra.delete('Zip64Placeholder')
-
end
-
end
-
end
-
-
end
-
end
-
-
# Copyright (C) 2002, 2003 Thomas Sondergaard
-
# rubyzip is free software; you can redistribute it and/or
-
# modify it under the terms of the ruby license.
-
1
module Zip
-
1
class EntrySet #:nodoc:all
-
1
include Enumerable
-
1
attr_accessor :entry_set, :entry_order
-
-
1
def initialize(an_enumerable = [])
-
super()
-
@entry_set = {}
-
an_enumerable.each { |o| push(o) }
-
end
-
-
1
def include?(entry)
-
@entry_set.include?(to_key(entry))
-
end
-
-
1
def find_entry(entry)
-
@entry_set[to_key(entry)]
-
end
-
-
1
def <<(entry)
-
@entry_set[to_key(entry)] = entry
-
end
-
-
1
alias :push :<<
-
-
1
def size
-
@entry_set.size
-
end
-
-
1
alias :length :size
-
-
1
def delete(entry)
-
if @entry_set.delete(to_key(entry))
-
entry
-
else
-
nil
-
end
-
end
-
-
1
def each(&block)
-
@entry_set = @entry_set.dup.each do |_, value|
-
block.call(value)
-
end
-
end
-
-
1
def entries
-
if ::Zip.sort_entries == true
-
@entry_set.values.sort_by{|x| x.name}
-
else
-
@entry_set.values
-
end
-
end
-
-
# deep clone
-
1
def dup
-
EntrySet.new(@entry_set.map { |key, value| value.dup })
-
end
-
-
1
def ==(other)
-
return false unless other.kind_of?(EntrySet)
-
@entry_set.values == other.entry_set.values
-
end
-
-
1
def parent(entry)
-
@entry_set[to_key(entry.parent_as_string)]
-
end
-
-
1
def glob(pattern, flags = ::File::FNM_PATHNAME|::File::FNM_DOTMATCH)
-
entries.map do |entry|
-
next nil unless ::File.fnmatch(pattern, entry.name.chomp('/'), flags)
-
yield(entry) if block_given?
-
entry
-
end.compact
-
end
-
-
1
protected
-
-
1
private
-
1
def to_key(entry)
-
entry.to_s.chomp('/')
-
end
-
end
-
end
-
-
# Copyright (C) 2002, 2003 Thomas Sondergaard
-
# rubyzip is free software; you can redistribute it and/or
-
# modify it under the terms of the ruby license.
-
1
module Zip
-
1
class Error < StandardError; end
-
1
class EntryExistsError < Error; end
-
1
class DestinationFileExistsError < Error; end
-
1
class CompressionMethodError < Error; end
-
1
class EntryNameError < Error; end
-
1
class InternalError < Error; end
-
-
# Backwards compatibility with v1 (delete in v2)
-
1
ZipError = Error
-
1
ZipEntryExistsError = EntryExistsError
-
1
ZipDestinationFileExistsError = DestinationFileExistsError
-
1
ZipCompressionMethodError = CompressionMethodError
-
1
ZipEntryNameError = EntryNameError
-
1
ZipInternalError = InternalError
-
end
-
1
module Zip
-
1
class ExtraField < Hash
-
1
ID_MAP = {}
-
-
1
def initialize(binstr = nil)
-
binstr and merge(binstr)
-
end
-
-
1
def extra_field_type_exist(binstr, id, len, i)
-
field_name = ID_MAP[id].name
-
if self.member?(field_name)
-
self[field_name].merge(binstr[i, len + 4])
-
else
-
field_obj = ID_MAP[id].new(binstr[i, len + 4])
-
self[field_name] = field_obj
-
end
-
end
-
-
1
def extra_field_type_unknown(binstr, len, i)
-
create_unknown_item unless self['Unknown']
-
if !len || len + 4 > binstr[i..-1].bytesize
-
self['Unknown'] << binstr[i..-1]
-
return
-
end
-
self['Unknown'] << binstr[i, len + 4]
-
end
-
-
1
def create_unknown_item
-
s = ''
-
class << s
-
alias_method :to_c_dir_bin, :to_s
-
alias_method :to_local_bin, :to_s
-
end
-
self['Unknown'] = s
-
end
-
-
1
def merge(binstr)
-
return if binstr.empty?
-
i = 0
-
while i < binstr.bytesize
-
id = binstr[i, 2]
-
len = binstr[i + 2, 2].to_s.unpack('v').first
-
if id && ID_MAP.member?(id)
-
extra_field_type_exist(binstr, id, len, i)
-
elsif id
-
create_unknown_item unless self['Unknown']
-
break unless extra_field_type_unknown(binstr, len, i)
-
end
-
i += len + 4
-
end
-
end
-
-
1
def create(name)
-
unless field_class = ID_MAP.values.find { |k| k.name == name }
-
raise Error, "Unknown extra field '#{name}'"
-
end
-
self[name] = field_class.new
-
end
-
-
# place Unknown last, so "extra" data that is missing the proper signature/size
-
# does not prevent known fields from being read back in
-
1
def ordered_values
-
result = []
-
self.each { |k,v| k == 'Unknown' ? result.push(v) : result.unshift(v) }
-
result
-
end
-
-
1
def to_local_bin
-
ordered_values.map! { |v| v.to_local_bin.force_encoding('BINARY') }.join
-
end
-
-
1
alias :to_s :to_local_bin
-
-
1
def to_c_dir_bin
-
ordered_values.map! { |v| v.to_c_dir_bin.force_encoding('BINARY') }.join
-
end
-
-
1
def c_dir_size
-
to_c_dir_bin.bytesize
-
end
-
-
1
def local_size
-
to_local_bin.bytesize
-
end
-
-
1
alias :length :local_size
-
1
alias :size :local_size
-
end
-
end
-
-
1
require 'zip/extra_field/generic'
-
1
require 'zip/extra_field/universal_time'
-
1
require 'zip/extra_field/old_unix'
-
1
require 'zip/extra_field/unix'
-
1
require 'zip/extra_field/zip64'
-
1
require 'zip/extra_field/zip64_placeholder'
-
-
# Copyright (C) 2002, 2003 Thomas Sondergaard
-
# rubyzip is free software; you can redistribute it and/or
-
# modify it under the terms of the ruby license.
-
1
module Zip
-
1
class ExtraField::Generic
-
1
def self.register_map
-
5
if self.const_defined?(:HEADER_ID)
-
5
::Zip::ExtraField::ID_MAP[self.const_get(:HEADER_ID)] = self
-
end
-
end
-
-
1
def self.name
-
@name ||= self.to_s.split("::")[-1]
-
end
-
-
# return field [size, content] or false
-
1
def initial_parse(binstr)
-
if !binstr
-
# If nil, start with empty.
-
return false
-
elsif binstr[0, 2] != self.class.const_get(:HEADER_ID)
-
$stderr.puts "Warning: weired extra feild header ID. skip parsing"
-
return false
-
end
-
[binstr[2, 2].unpack("v")[0], binstr[4..-1]]
-
end
-
-
1
def ==(other)
-
return false if self.class != other.class
-
each do |k, v|
-
v != other[k] and return false
-
end
-
true
-
end
-
-
1
def to_local_bin
-
s = pack_for_local
-
self.class.const_get(:HEADER_ID) + [s.bytesize].pack("v") << s
-
end
-
-
1
def to_c_dir_bin
-
s = pack_for_c_dir
-
self.class.const_get(:HEADER_ID) + [s.bytesize].pack("v") << s
-
end
-
end
-
end
-
1
module Zip
-
# Olf Info-ZIP Extra for UNIX uid/gid and file timestampes
-
1
class ExtraField::OldUnix < ExtraField::Generic
-
1
HEADER_ID = "UX"
-
1
register_map
-
-
1
def initialize(binstr = nil)
-
@uid = 0
-
@gid = 0
-
@atime = nil
-
@mtime = nil
-
binstr and merge(binstr)
-
end
-
-
1
attr_accessor :uid, :gid, :atime, :mtime
-
-
1
def merge(binstr)
-
return if binstr.empty?
-
size, content = initial_parse(binstr)
-
# size: 0 for central directory. 4 for local header
-
return if (!size || size == 0)
-
atime, mtime, uid, gid = content.unpack("VVvv")
-
@uid ||= uid
-
@gid ||= gid
-
@atime ||= atime
-
@mtime ||= mtime
-
end
-
-
1
def ==(other)
-
@uid == other.uid &&
-
@gid == other.gid &&
-
@atime == other.atime &&
-
@mtime == other.mtime
-
end
-
-
1
def pack_for_local
-
[@atime, @mtime, @uid, @gid].pack("VVvv")
-
end
-
-
1
def pack_for_c_dir
-
[@atime, @mtime].pack("VV")
-
end
-
end
-
-
end
-
1
module Zip
-
# Info-ZIP Additional timestamp field
-
1
class ExtraField::UniversalTime < ExtraField::Generic
-
1
HEADER_ID = "UT"
-
1
register_map
-
-
1
def initialize(binstr = nil)
-
@ctime = nil
-
@mtime = nil
-
@atime = nil
-
@flag = nil
-
binstr and merge(binstr)
-
end
-
-
1
attr_accessor :atime, :ctime, :mtime, :flag
-
-
1
def merge(binstr)
-
return if binstr.empty?
-
size, content = initial_parse(binstr)
-
size or return
-
@flag, mtime, atime, ctime = content.unpack("CVVV")
-
mtime and @mtime ||= ::Zip::DOSTime.at(mtime)
-
atime and @atime ||= ::Zip::DOSTime.at(atime)
-
ctime and @ctime ||= ::Zip::DOSTime.at(ctime)
-
end
-
-
1
def ==(other)
-
@mtime == other.mtime &&
-
@atime == other.atime &&
-
@ctime == other.ctime
-
end
-
-
1
def pack_for_local
-
s = [@flag].pack("C")
-
@flag & 1 != 0 and s << [@mtime.to_i].pack("V")
-
@flag & 2 != 0 and s << [@atime.to_i].pack("V")
-
@flag & 4 != 0 and s << [@ctime.to_i].pack("V")
-
s
-
end
-
-
1
def pack_for_c_dir
-
s = [@flag].pack("C")
-
@flag & 1 == 1 and s << [@mtime.to_i].pack("V")
-
s
-
end
-
end
-
end
-
1
module Zip
-
# Info-ZIP Extra for UNIX uid/gid
-
1
class ExtraField::IUnix < ExtraField::Generic
-
1
HEADER_ID = "Ux"
-
1
register_map
-
-
1
def initialize(binstr = nil)
-
@uid = 0
-
@gid = 0
-
binstr and merge(binstr)
-
end
-
-
1
attr_accessor :uid, :gid
-
-
1
def merge(binstr)
-
return if binstr.empty?
-
size, content = initial_parse(binstr)
-
# size: 0 for central directory. 4 for local header
-
return if (!size || size == 0)
-
uid, gid = content.unpack("vv")
-
@uid ||= uid
-
@gid ||= gid
-
end
-
-
1
def ==(other)
-
@uid == other.uid && @gid == other.gid
-
end
-
-
1
def pack_for_local
-
[@uid, @gid].pack("vv")
-
end
-
-
1
def pack_for_c_dir
-
''
-
end
-
end
-
-
end
-
1
module Zip
-
# Info-ZIP Extra for Zip64 size
-
1
class ExtraField::Zip64 < ExtraField::Generic
-
1
attr_accessor :original_size, :compressed_size, :relative_header_offset, :disk_start_number
-
1
HEADER_ID = ['0100'].pack('H*')
-
1
register_map
-
-
1
def initialize(binstr = nil)
-
@content = nil # unparsed binary; we don't actually know what this contains
-
# without looking for FFs in the associated file header
-
# call parse after initializing with a binary string
-
@original_size = nil
-
@compressed_size = nil
-
@relative_header_offset = nil
-
@disk_start_number = nil
-
binstr and merge(binstr)
-
end
-
-
1
def ==(other)
-
other.original_size == @original_size &&
-
other.compressed_size == @compressed_size &&
-
other.relative_header_offset == @relative_header_offset &&
-
other.disk_start_number == @disk_start_number
-
end
-
-
1
def merge(binstr)
-
return if binstr.empty?
-
_, @content = initial_parse(binstr)
-
end
-
-
# pass the values from the base entry (if applicable)
-
# wider values are only present in the extra field for base values set to all FFs
-
# returns the final values for the four attributes (from the base or zip64 extra record)
-
1
def parse(original_size, compressed_size, relative_header_offset = nil, disk_start_number = nil)
-
@original_size = extract(8, 'Q<') if original_size == 0xFFFFFFFF
-
@compressed_size = extract(8, 'Q<') if compressed_size == 0xFFFFFFFF
-
@relative_header_offset = extract(8, 'Q<') if relative_header_offset && relative_header_offset == 0xFFFFFFFF
-
@disk_start_number = extract(4, 'V') if disk_start_number && disk_start_number == 0xFFFF
-
@content = nil
-
[@original_size || original_size,
-
@compressed_size || compressed_size,
-
@relative_header_offset || relative_header_offset,
-
@disk_start_number || disk_start_number]
-
end
-
-
1
def extract(size, format)
-
@content.slice!(0, size).unpack(format)[0]
-
end
-
1
private :extract
-
-
1
def pack_for_local
-
# local header entries must contain original size and compressed size; other fields do not apply
-
return '' unless @original_size && @compressed_size
-
[@original_size, @compressed_size].pack("Q<Q<")
-
end
-
-
1
def pack_for_c_dir
-
# central directory entries contain only fields that didn't fit in the main entry part
-
packed = ''.force_encoding('BINARY')
-
packed << [@original_size].pack("Q<") if @original_size
-
packed << [@compressed_size].pack("Q<") if @compressed_size
-
packed << [@relative_header_offset].pack("Q<") if @relative_header_offset
-
packed << [@disk_start_number].pack("V") if @disk_start_number
-
packed
-
end
-
end
-
end
-
1
module Zip
-
# placeholder to reserve space for a Zip64 extra information record, for the
-
# local file header only, that we won't know if we'll need until after
-
# we write the file data
-
1
class ExtraField::Zip64Placeholder < ExtraField::Generic
-
1
HEADER_ID = ['9999'].pack('H*') # this ID is used by other libraries such as .NET's Ionic.zip
-
1
register_map
-
-
1
def initialize(binstr = nil)
-
end
-
-
1
def pack_for_local
-
"\x00" * 16
-
end
-
end
-
end
-
1
module Zip
-
# ZipFile is modeled after java.util.zip.ZipFile from the Java SDK.
-
# The most important methods are those inherited from
-
# ZipCentralDirectory for accessing information about the entries in
-
# the archive and methods such as get_input_stream and
-
# get_output_stream for reading from and writing entries to the
-
# archive. The class includes a few convenience methods such as
-
# #extract for extracting entries to the filesystem, and #remove,
-
# #replace, #rename and #mkdir for making simple modifications to
-
# the archive.
-
#
-
# Modifications to a zip archive are not committed until #commit or
-
# #close is called. The method #open accepts a block following
-
# the pattern from File.open offering a simple way to
-
# automatically close the archive when the block returns.
-
#
-
# The following example opens zip archive <code>my.zip</code>
-
# (creating it if it doesn't exist) and adds an entry
-
# <code>first.txt</code> and a directory entry <code>a_dir</code>
-
# to it.
-
#
-
# require 'zip'
-
#
-
# Zip::File.open("my.zip", Zip::File::CREATE) {
-
# |zipfile|
-
# zipfile.get_output_stream("first.txt") { |f| f.puts "Hello from ZipFile" }
-
# zipfile.mkdir("a_dir")
-
# }
-
#
-
# The next example reopens <code>my.zip</code> writes the contents of
-
# <code>first.txt</code> to standard out and deletes the entry from
-
# the archive.
-
#
-
# require 'zip'
-
#
-
# Zip::File.open("my.zip", Zip::File::CREATE) {
-
# |zipfile|
-
# puts zipfile.read("first.txt")
-
# zipfile.remove("first.txt")
-
# }
-
#
-
# ZipFileSystem offers an alternative API that emulates ruby's
-
# interface for accessing the filesystem, ie. the File and Dir classes.
-
-
1
class File < CentralDirectory
-
-
1
CREATE = 1
-
1
SPLIT_SIGNATURE = 0x08074b50
-
1
ZIP64_EOCD_SIGNATURE = 0x06064b50
-
1
MAX_SEGMENT_SIZE = 3221225472
-
1
MIN_SEGMENT_SIZE = 65536
-
1
DATA_BUFFER_SIZE = 8192
-
-
1
attr_reader :name
-
-
# default -> false
-
1
attr_accessor :restore_ownership
-
# default -> false
-
1
attr_accessor :restore_permissions
-
# default -> true
-
1
attr_accessor :restore_times
-
# Returns the zip files comment, if it has one
-
1
attr_accessor :comment
-
-
# Opens a zip archive. Pass true as the second parameter to create
-
# a new archive if it doesn't exist already.
-
1
def initialize(file_name, create = nil, buffer = false, options = {})
-
super()
-
@name = file_name
-
@comment = ''
-
@create = create
-
case
-
when !buffer && ::File.size?(file_name)
-
@create = nil
-
@exist_file_perms = ::File.stat(file_name).mode
-
::File.open(name, 'rb') do |f|
-
read_from_stream(f)
-
end
-
when create
-
@entry_set = EntrySet.new
-
else
-
raise Error, "File #{file_name} not found"
-
end
-
@stored_entries = @entry_set.dup
-
@stored_comment = @comment
-
@restore_ownership = options[:restore_ownership] || false
-
@restore_permissions = options[:restore_permissions] || true
-
@restore_times = options[:restore_times] || true
-
end
-
-
1
class << self
-
# Same as #new. If a block is passed the ZipFile object is passed
-
# to the block and is automatically closed afterwards just as with
-
# ruby's builtin File.open method.
-
1
def open(file_name, create = nil)
-
zf = ::Zip::File.new(file_name, create)
-
return zf unless block_given?
-
begin
-
yield zf
-
ensure
-
zf.close
-
end
-
end
-
-
# Same as #open. But outputs data to a buffer instead of a file
-
1
def add_buffer
-
io = ::StringIO.new('')
-
zf = ::Zip::File.new(io, true, true)
-
yield zf
-
zf.write_buffer(io)
-
end
-
-
# Like #open, but reads zip archive contents from a String or open IO
-
# stream, and outputs data to a buffer.
-
# (This can be used to extract data from a
-
# downloaded zip archive without first saving it to disk.)
-
1
def open_buffer(io, options = {})
-
unless io.is_a?(IO) || io.is_a?(String) || io.is_a?(Tempfile)
-
raise "Zip::File.open_buffer expects an argument of class String, IO, or Tempfile. Found: #{io.class}"
-
end
-
if io.is_a?(::String)
-
require 'stringio'
-
io = ::StringIO.new(io)
-
end
-
zf = ::Zip::File.new(io, true, true, options)
-
zf.read_from_stream(io)
-
yield zf
-
zf.write_buffer(io)
-
end
-
-
# Iterates over the contents of the ZipFile. This is more efficient
-
# than using a ZipInputStream since this methods simply iterates
-
# through the entries in the central directory structure in the archive
-
# whereas ZipInputStream jumps through the entire archive accessing the
-
# local entry headers (which contain the same information as the
-
# central directory).
-
1
def foreach(aZipFileName, &block)
-
open(aZipFileName) do |zipFile|
-
zipFile.each(&block)
-
end
-
end
-
-
1
def get_segment_size_for_split(segment_size)
-
case
-
when MIN_SEGMENT_SIZE > segment_size
-
MIN_SEGMENT_SIZE
-
when MAX_SEGMENT_SIZE < segment_size
-
MAX_SEGMENT_SIZE
-
else
-
segment_size
-
end
-
end
-
-
1
def get_partial_zip_file_name(zip_file_name, partial_zip_file_name)
-
partial_zip_file_name = zip_file_name.sub(/#{::File.basename(zip_file_name)}\z/,
-
partial_zip_file_name + ::File.extname(zip_file_name)) unless partial_zip_file_name.nil?
-
partial_zip_file_name ||= zip_file_name
-
partial_zip_file_name
-
end
-
-
1
def get_segment_count_for_split(zip_file_size, segment_size)
-
(zip_file_size / segment_size).to_i + (zip_file_size % segment_size == 0 ? 0 : 1)
-
end
-
-
1
def put_split_signature(szip_file, segment_size)
-
signature_packed = [SPLIT_SIGNATURE].pack('V')
-
szip_file << signature_packed
-
segment_size - signature_packed.size
-
end
-
-
#
-
# TODO: Make the code more understandable
-
#
-
1
def save_splited_part(zip_file, partial_zip_file_name, zip_file_size, szip_file_index, segment_size, segment_count)
-
ssegment_size = zip_file_size - zip_file.pos
-
ssegment_size = segment_size if ssegment_size > segment_size
-
szip_file_name = "#{partial_zip_file_name}.#{'%03d'%(szip_file_index)}"
-
::File.open(szip_file_name, 'wb') do |szip_file|
-
if szip_file_index == 1
-
ssegment_size = put_split_signature(szip_file, segment_size)
-
end
-
chunk_bytes = 0
-
until ssegment_size == chunk_bytes || zip_file.eof?
-
segment_bytes_left = ssegment_size - chunk_bytes
-
buffer_size = segment_bytes_left < DATA_BUFFER_SIZE ? segment_bytes_left : DATA_BUFFER_SIZE
-
chunk = zip_file.read(buffer_size)
-
chunk_bytes += buffer_size
-
szip_file << chunk
-
# Info for track splitting
-
yield segment_count, szip_file_index, chunk_bytes, ssegment_size if block_given?
-
end
-
end
-
end
-
-
# Splits an archive into parts with segment size
-
1
def split(zip_file_name, segment_size = MAX_SEGMENT_SIZE, delete_zip_file = true, partial_zip_file_name = nil)
-
raise Error, "File #{zip_file_name} not found" unless ::File.exist?(zip_file_name)
-
raise Errno::ENOENT, zip_file_name unless ::File.readable?(zip_file_name)
-
zip_file_size = ::File.size(zip_file_name)
-
segment_size = get_segment_size_for_split(segment_size)
-
return if zip_file_size <= segment_size
-
segment_count = get_segment_count_for_split(zip_file_size, segment_size)
-
# Checking for correct zip structure
-
self.open(zip_file_name) {}
-
partial_zip_file_name = get_partial_zip_file_name(zip_file_name, partial_zip_file_name)
-
szip_file_index = 0
-
::File.open(zip_file_name, 'rb') do |zip_file|
-
until zip_file.eof?
-
szip_file_index += 1
-
save_splited_part(zip_file, partial_zip_file_name, zip_file_size, szip_file_index, segment_size, segment_count)
-
end
-
end
-
::File.delete(zip_file_name) if delete_zip_file
-
szip_file_index
-
end
-
end
-
-
-
# Returns an input stream to the specified entry. If a block is passed
-
# the stream object is passed to the block and the stream is automatically
-
# closed afterwards just as with ruby's builtin File.open method.
-
1
def get_input_stream(entry, &aProc)
-
get_entry(entry).get_input_stream(&aProc)
-
end
-
-
# Returns an output stream to the specified entry. If entry is not an instance
-
# of Zip::Entry, a new Zip::Entry will be initialized using the arguments
-
# specified. If a block is passed the stream object is passed to the block and
-
# the stream is automatically closed afterwards just as with ruby's builtin
-
# File.open method.
-
1
def get_output_stream(entry, permission_int = nil, comment = nil, extra = nil, compressed_size = nil, crc = nil, compression_method = nil, size = nil, time = nil, &aProc)
-
new_entry =
-
if entry.kind_of?(Entry)
-
entry
-
else
-
Entry.new(@name, entry.to_s, comment, extra, compressed_size, crc, compression_method, size, time)
-
end
-
if new_entry.directory?
-
raise ArgumentError,
-
"cannot open stream to directory entry - '#{new_entry}'"
-
end
-
new_entry.unix_perms = permission_int
-
zip_streamable_entry = StreamableStream.new(new_entry)
-
@entry_set << zip_streamable_entry
-
zip_streamable_entry.get_output_stream(&aProc)
-
end
-
-
# Returns the name of the zip archive
-
1
def to_s
-
@name
-
end
-
-
# Returns a string containing the contents of the specified entry
-
1
def read(entry)
-
get_input_stream(entry) { |is| is.read }
-
end
-
-
# Convenience method for adding the contents of a file to the archive
-
1
def add(entry, src_path, &continue_on_exists_proc)
-
continue_on_exists_proc ||= proc { ::Zip.continue_on_exists_proc }
-
check_entry_exists(entry, continue_on_exists_proc, "add")
-
new_entry = entry.kind_of?(::Zip::Entry) ? entry : ::Zip::Entry.new(@name, entry.to_s)
-
new_entry.gather_fileinfo_from_srcpath(src_path)
-
new_entry.dirty = true
-
@entry_set << new_entry
-
end
-
-
# Removes the specified entry.
-
1
def remove(entry)
-
@entry_set.delete(get_entry(entry))
-
end
-
-
# Renames the specified entry.
-
1
def rename(entry, new_name, &continue_on_exists_proc)
-
foundEntry = get_entry(entry)
-
check_entry_exists(new_name, continue_on_exists_proc, 'rename')
-
@entry_set.delete(foundEntry)
-
foundEntry.name = new_name
-
@entry_set << foundEntry
-
end
-
-
# Replaces the specified entry with the contents of srcPath (from
-
# the file system).
-
1
def replace(entry, srcPath)
-
check_file(srcPath)
-
remove(entry)
-
add(entry, srcPath)
-
end
-
-
# Extracts entry to file dest_path.
-
1
def extract(entry, dest_path, &block)
-
block ||= proc { ::Zip.on_exists_proc }
-
found_entry = get_entry(entry)
-
found_entry.extract(dest_path, &block)
-
end
-
-
# Commits changes that has been made since the previous commit to
-
# the zip archive.
-
1
def commit
-
return unless commit_required?
-
on_success_replace do |tmp_file|
-
::Zip::OutputStream.open(tmp_file) do |zos|
-
@entry_set.each do |e|
-
e.write_to_zip_output_stream(zos)
-
e.dirty = false
-
e.clean_up
-
end
-
zos.comment = comment
-
end
-
true
-
end
-
initialize(name)
-
end
-
-
# Write buffer write changes to buffer and return
-
1
def write_buffer(io = ::StringIO.new(''))
-
::Zip::OutputStream.write_buffer(io) do |zos|
-
@entry_set.each { |e| e.write_to_zip_output_stream(zos) }
-
zos.comment = comment
-
end
-
end
-
-
# Closes the zip file committing any changes that has been made.
-
1
def close
-
commit
-
end
-
-
# Returns true if any changes has been made to this archive since
-
# the previous commit
-
1
def commit_required?
-
@entry_set.each do |e|
-
return true if e.dirty
-
end
-
@comment != @stored_comment || @entry_set != @stored_entries || @create == ::Zip::File::CREATE
-
end
-
-
# Searches for entry with the specified name. Returns nil if
-
# no entry is found. See also get_entry
-
1
def find_entry(entry_name)
-
@entry_set.find_entry(entry_name)
-
end
-
-
# Searches for entries given a glob
-
1
def glob(*args, &block)
-
@entry_set.glob(*args, &block)
-
end
-
-
# Searches for an entry just as find_entry, but throws Errno::ENOENT
-
# if no entry is found.
-
1
def get_entry(entry)
-
selected_entry = find_entry(entry)
-
unless selected_entry
-
raise Errno::ENOENT, entry
-
end
-
selected_entry.restore_ownership = @restore_ownership
-
selected_entry.restore_permissions = @restore_permissions
-
selected_entry.restore_times = @restore_times
-
selected_entry
-
end
-
-
# Creates a directory
-
1
def mkdir(entryName, permissionInt = 0755)
-
if find_entry(entryName)
-
raise Errno::EEXIST, "File exists - #{entryName}"
-
end
-
entryName = entryName.dup.to_s
-
entryName << '/' unless entryName.end_with?('/')
-
@entry_set << ::Zip::StreamableDirectory.new(@name, entryName, nil, permissionInt)
-
end
-
-
1
private
-
-
1
def is_directory(newEntry, srcPath)
-
srcPathIsDirectory = ::File.directory?(srcPath)
-
if newEntry.is_directory && !srcPathIsDirectory
-
raise ArgumentError,
-
"entry name '#{newEntry}' indicates directory entry, but "+
-
"'#{srcPath}' is not a directory"
-
elsif !newEntry.is_directory && srcPathIsDirectory
-
newEntry.name += "/"
-
end
-
newEntry.is_directory && srcPathIsDirectory
-
end
-
-
1
def check_entry_exists(entryName, continue_on_exists_proc, procedureName)
-
continue_on_exists_proc ||= proc { Zip.continue_on_exists_proc }
-
if @entry_set.include?(entryName)
-
if continue_on_exists_proc.call
-
remove get_entry(entryName)
-
else
-
raise ::Zip::EntryExistsError,
-
procedureName + " failed. Entry #{entryName} already exists"
-
end
-
end
-
end
-
-
1
def check_file(path)
-
unless ::File.readable?(path)
-
raise Errno::ENOENT, path
-
end
-
end
-
-
1
def on_success_replace
-
tmpfile = get_tempfile
-
tmp_filename = tmpfile.path
-
tmpfile.close
-
if yield tmp_filename
-
::File.rename(tmp_filename, self.name)
-
if defined?(@exist_file_perms)
-
::File.chmod(@exist_file_perms, self.name)
-
end
-
end
-
end
-
-
1
def get_tempfile
-
temp_file = Tempfile.new(::File.basename(name), ::File.dirname(name))
-
temp_file.binmode
-
temp_file
-
end
-
-
end
-
end
-
-
# Copyright (C) 2002, 2003 Thomas Sondergaard
-
# rubyzip is free software; you can redistribute it and/or
-
# modify it under the terms of the ruby license.
-
1
module Zip
-
1
class Inflater < Decompressor #:nodoc:all
-
1
def initialize(input_stream)
-
super
-
@zlib_inflater = ::Zlib::Inflate.new(-Zlib::MAX_WBITS)
-
@output_buffer = ''
-
@has_returned_empty_string = false
-
end
-
-
1
def sysread(number_of_bytes = nil, buf = '')
-
readEverything = number_of_bytes.nil?
-
while readEverything || @output_buffer.bytesize < number_of_bytes
-
break if internal_input_finished?
-
@output_buffer << internal_produce_input(buf)
-
end
-
return value_when_finished if @output_buffer.bytesize == 0 && input_finished?
-
end_index = number_of_bytes.nil? ? @output_buffer.bytesize : number_of_bytes
-
@output_buffer.slice!(0...end_index)
-
end
-
-
1
def produce_input
-
if (@output_buffer.empty?)
-
internal_produce_input
-
else
-
@output_buffer.slice!(0...(@output_buffer.length))
-
end
-
end
-
-
# to be used with produce_input, not read (as read may still have more data cached)
-
# is data cached anywhere other than @outputBuffer? the comment above may be wrong
-
1
def input_finished?
-
@output_buffer.empty? && internal_input_finished?
-
end
-
-
1
alias :eof :input_finished?
-
1
alias :eof? :input_finished?
-
-
1
private
-
-
1
def internal_produce_input(buf = '')
-
retried = 0
-
begin
-
@zlib_inflater.inflate(@input_stream.read(Decompressor::CHUNK_SIZE, buf))
-
rescue Zlib::BufError
-
raise if retried >= 5 # how many times should we retry?
-
retried += 1
-
retry
-
end
-
end
-
-
1
def internal_input_finished?
-
@zlib_inflater.finished?
-
end
-
-
1
def value_when_finished # mimic behaviour of ruby File object.
-
return if @has_returned_empty_string
-
@has_returned_empty_string = true
-
''
-
end
-
end
-
end
-
-
# Copyright (C) 2002, 2003 Thomas Sondergaard
-
# rubyzip is free software; you can redistribute it and/or
-
# modify it under the terms of the ruby license.
-
1
module Zip
-
# InputStream is the basic class for reading zip entries in a
-
# zip file. It is possible to create a InputStream object directly,
-
# passing the zip file name to the constructor, but more often than not
-
# the InputStream will be obtained from a File (perhaps using the
-
# ZipFileSystem interface) object for a particular entry in the zip
-
# archive.
-
#
-
# A InputStream inherits IOExtras::AbstractInputStream in order
-
# to provide an IO-like interface for reading from a single zip
-
# entry. Beyond methods for mimicking an IO-object it contains
-
# the method get_next_entry for iterating through the entries of
-
# an archive. get_next_entry returns a Entry object that describes
-
# the zip entry the InputStream is currently reading from.
-
#
-
# Example that creates a zip archive with ZipOutputStream and reads it
-
# back again with a InputStream.
-
#
-
# require 'zip'
-
#
-
# Zip::OutputStream.open("my.zip") do |io|
-
#
-
# io.put_next_entry("first_entry.txt")
-
# io.write "Hello world!"
-
#
-
# io.put_next_entry("adir/first_entry.txt")
-
# io.write "Hello again!"
-
# end
-
#
-
#
-
# Zip::InputStream.open("my.zip") do |io|
-
#
-
# while (entry = io.get_next_entry)
-
# puts "Contents of #{entry.name}: '#{io.read}'"
-
# end
-
# end
-
#
-
# java.util.zip.ZipInputStream is the original inspiration for this
-
# class.
-
-
1
class InputStream
-
1
include ::Zip::IOExtras::AbstractInputStream
-
-
# Opens the indicated zip file. An exception is thrown
-
# if the specified offset in the specified filename is
-
# not a local zip entry header.
-
#
-
# @param context [String||IO||StringIO] file path or IO/StringIO object
-
# @param offset [Integer] offset in the IO/StringIO
-
1
def initialize(context, offset = 0)
-
super()
-
@archive_io = get_io(context, offset)
-
@decompressor = ::Zip::NullDecompressor
-
@current_entry = nil
-
end
-
-
1
def close
-
@archive_io.close
-
end
-
-
# Returns a Entry object. It is necessary to call this
-
# method on a newly created InputStream before reading from
-
# the first entry in the archive. Returns nil when there are
-
# no more entries.
-
1
def get_next_entry
-
@archive_io.seek(@current_entry.next_header_offset, IO::SEEK_SET) if @current_entry
-
open_entry
-
end
-
-
# Rewinds the stream to the beginning of the current entry
-
1
def rewind
-
return if @current_entry.nil?
-
@lineno = 0
-
@pos = 0
-
@archive_io.seek(@current_entry.local_header_offset, IO::SEEK_SET)
-
open_entry
-
end
-
-
# Modeled after IO.sysread
-
1
def sysread(number_of_bytes = nil, buf = nil)
-
@decompressor.sysread(number_of_bytes, buf)
-
end
-
-
1
def eof
-
@output_buffer.empty? && @decompressor.eof
-
end
-
-
1
alias :eof? :eof
-
-
1
class << self
-
# Same as #initialize but if a block is passed the opened
-
# stream is passed to the block and closed when the block
-
# returns.
-
1
def open(filename_or_io, offset = 0)
-
zio = self.new(filename_or_io, offset)
-
return zio unless block_given?
-
begin
-
yield zio
-
ensure
-
zio.close if zio
-
end
-
end
-
-
1
def open_buffer(filename_or_io, offset = 0)
-
puts "open_buffer is deprecated!!! Use open instead!"
-
self.open(filename_or_io, offset)
-
end
-
end
-
-
1
protected
-
-
1
def get_io(io_or_file, offset = 0)
-
case io_or_file
-
when IO, StringIO
-
io = io_or_file.dup
-
io.seek(offset, ::IO::SEEK_SET)
-
io
-
else
-
file = ::File.open(io_or_file, 'rb')
-
file.seek(offset, ::IO::SEEK_SET)
-
file
-
end
-
end
-
-
1
def open_entry
-
@current_entry = ::Zip::Entry.read_local_entry(@archive_io)
-
@decompressor = get_decompressor
-
flush
-
@current_entry
-
end
-
-
1
def get_decompressor
-
case
-
when @current_entry.nil?
-
::Zip::NullDecompressor
-
when @current_entry.compression_method == ::Zip::Entry::STORED
-
::Zip::PassThruDecompressor.new(@archive_io, @current_entry.size)
-
when @current_entry.compression_method == ::Zip::Entry::DEFLATED
-
::Zip::Inflater.new(@archive_io)
-
else
-
raise ::Zip::CompressionMethodError,
-
"Unsupported compression method #{@current_entry.compression_method}"
-
end
-
end
-
-
1
def produce_input
-
@decompressor.produce_input
-
end
-
-
1
def input_finished?
-
@decompressor.input_finished?
-
end
-
end
-
end
-
-
# Copyright (C) 2002, 2003 Thomas Sondergaard
-
# rubyzip is free software; you can redistribute it and/or
-
# modify it under the terms of the ruby license.
-
1
module Zip
-
1
module IOExtras #:nodoc:
-
-
1
CHUNK_SIZE = 131072
-
-
1
RANGE_ALL = 0..-1
-
-
1
class << self
-
1
def copy_stream(ostream, istream)
-
ostream.write(istream.read(CHUNK_SIZE, '')) until istream.eof?
-
end
-
-
1
def copy_stream_n(ostream, istream, nbytes)
-
toread = nbytes
-
while toread > 0 && !istream.eof?
-
tr = toread > CHUNK_SIZE ? CHUNK_SIZE : toread
-
ostream.write(istream.read(tr, ''))
-
toread -= tr
-
end
-
end
-
end
-
-
# Implements kind_of? in order to pretend to be an IO object
-
1
module FakeIO
-
1
def kind_of?(object)
-
object == IO || super
-
end
-
end
-
-
end # IOExtras namespace module
-
end
-
-
1
require 'zip/ioextras/abstract_input_stream'
-
1
require 'zip/ioextras/abstract_output_stream'
-
-
# Copyright (C) 2002-2004 Thomas Sondergaard
-
# rubyzip is free software; you can redistribute it and/or
-
# modify it under the terms of the ruby license.
-
1
module Zip
-
1
module IOExtras
-
# Implements many of the convenience methods of IO
-
# such as gets, getc, readline and readlines
-
# depends on: input_finished?, produce_input and read
-
1
module AbstractInputStream
-
1
include Enumerable
-
1
include FakeIO
-
-
1
def initialize
-
super
-
@lineno = 0
-
@pos = 0
-
@output_buffer = ''
-
end
-
-
1
attr_accessor :lineno
-
1
attr_reader :pos
-
-
1
def read(number_of_bytes = nil, buf = '')
-
tbuf = if @output_buffer.bytesize > 0
-
if number_of_bytes <= @output_buffer.bytesize
-
@output_buffer.slice!(0, number_of_bytes)
-
else
-
number_of_bytes -= @output_buffer.bytesize if number_of_bytes
-
rbuf = sysread(number_of_bytes, buf)
-
out = @output_buffer
-
out << rbuf if rbuf
-
@output_buffer = ''
-
out
-
end
-
else
-
sysread(number_of_bytes, buf)
-
end
-
-
if tbuf.nil? || tbuf.length == 0
-
return nil if number_of_bytes
-
return ""
-
end
-
-
@pos += tbuf.length
-
-
if buf
-
buf.replace(tbuf)
-
else
-
buf = tbuf
-
end
-
buf
-
end
-
-
1
def readlines(a_sep_string = $/)
-
ret_val = []
-
each_line(a_sep_string) { |line| ret_val << line }
-
ret_val
-
end
-
-
1
def gets(a_sep_string = $/, number_of_bytes = nil)
-
@lineno = @lineno.next
-
-
if number_of_bytes.respond_to?(:to_int)
-
number_of_bytes = number_of_bytes.to_int
-
a_sep_string = a_sep_string.to_str if a_sep_string
-
elsif a_sep_string.respond_to?(:to_int)
-
number_of_bytes = a_sep_string.to_int
-
a_sep_string = $/
-
else
-
number_of_bytes = nil
-
a_sep_string = a_sep_string.to_str if a_sep_string
-
end
-
-
return read(number_of_bytes) if a_sep_string.nil?
-
a_sep_string = "#{$/}#{$/}" if a_sep_string.empty?
-
-
buffer_index = 0
-
over_limit = (number_of_bytes && @output_buffer.bytesize >= number_of_bytes)
-
while (match_index = @output_buffer.index(a_sep_string, buffer_index)).nil? && !over_limit
-
buffer_index = [buffer_index, @output_buffer.bytesize - a_sep_string.bytesize].max
-
if input_finished?
-
return @output_buffer.empty? ? nil : flush
-
end
-
@output_buffer << produce_input
-
over_limit = (number_of_bytes && @output_buffer.bytesize >= number_of_bytes)
-
end
-
sep_index = [match_index + a_sep_string.bytesize, number_of_bytes || @output_buffer.bytesize].min
-
@pos += sep_index
-
return @output_buffer.slice!(0...sep_index)
-
end
-
-
1
def ungetc(byte)
-
@output_buffer = byte.chr + @output_buffer
-
end
-
-
1
def flush
-
ret_val = @output_buffer
-
@output_buffer = ''
-
ret_val
-
end
-
-
1
def readline(a_sep_string = $/)
-
ret_val = gets(a_sep_string)
-
raise EOFError unless ret_val
-
ret_val
-
end
-
-
1
def each_line(a_sep_string = $/)
-
while true
-
yield readline(a_sep_string)
-
end
-
rescue EOFError
-
end
-
-
1
alias_method :each, :each_line
-
end
-
end
-
end
-
1
module Zip
-
1
module IOExtras
-
# Implements many of the output convenience methods of IO.
-
# relies on <<
-
1
module AbstractOutputStream
-
1
include FakeIO
-
-
1
def write(data)
-
self << data
-
data.to_s.bytesize
-
end
-
-
-
1
def print(*params)
-
self << params.join($,) << $\.to_s
-
end
-
-
1
def printf(a_format_string, *params)
-
self << sprintf(a_format_string, *params)
-
end
-
-
1
def putc(an_object)
-
self << case an_object
-
when Fixnum
-
an_object.chr
-
when String
-
an_object
-
else
-
raise TypeError, 'putc: Only Fixnum and String supported'
-
end
-
an_object
-
end
-
-
1
def puts(*params)
-
params << "\n" if params.empty?
-
params.flatten.each do |element|
-
val = element.to_s
-
self << val
-
self << "\n" unless val[-1, 1] == "\n"
-
end
-
end
-
-
end
-
end
-
end
-
1
module Zip
-
1
class NullCompressor < Compressor #:nodoc:all
-
1
include Singleton
-
-
1
def <<(data)
-
raise IOError, "closed stream"
-
end
-
-
1
attr_reader :size, :compressed_size
-
end
-
end
-
-
# Copyright (C) 2002, 2003 Thomas Sondergaard
-
# rubyzip is free software; you can redistribute it and/or
-
# modify it under the terms of the ruby license.
-
1
module Zip
-
1
module NullDecompressor #:nodoc:all
-
1
extend self
-
-
1
def sysread(numberOfBytes = nil, buf = nil)
-
nil
-
end
-
-
1
def produce_input
-
nil
-
end
-
-
1
def input_finished?
-
true
-
end
-
-
1
def eof
-
true
-
end
-
-
1
alias :eof? :eof
-
end
-
end
-
-
# Copyright (C) 2002, 2003 Thomas Sondergaard
-
# rubyzip is free software; you can redistribute it and/or
-
# modify it under the terms of the ruby license.
-
1
module Zip
-
1
module NullInputStream #:nodoc:all
-
1
include ::Zip::NullDecompressor
-
1
include ::Zip::IOExtras::AbstractInputStream
-
end
-
end
-
-
# Copyright (C) 2002, 2003 Thomas Sondergaard
-
# rubyzip is free software; you can redistribute it and/or
-
# modify it under the terms of the ruby license.
-
1
module Zip
-
# ZipOutputStream is the basic class for writing zip files. It is
-
# possible to create a ZipOutputStream object directly, passing
-
# the zip file name to the constructor, but more often than not
-
# the ZipOutputStream will be obtained from a ZipFile (perhaps using the
-
# ZipFileSystem interface) object for a particular entry in the zip
-
# archive.
-
#
-
# A ZipOutputStream inherits IOExtras::AbstractOutputStream in order
-
# to provide an IO-like interface for writing to a single zip
-
# entry. Beyond methods for mimicking an IO-object it contains
-
# the method put_next_entry that closes the current entry
-
# and creates a new.
-
#
-
# Please refer to ZipInputStream for example code.
-
#
-
# java.util.zip.ZipOutputStream is the original inspiration for this
-
# class.
-
-
1
class OutputStream
-
1
include ::Zip::IOExtras::AbstractOutputStream
-
-
1
attr_accessor :comment
-
-
# Opens the indicated zip file. If a file with that name already
-
# exists it will be overwritten.
-
1
def initialize(file_name, stream=false)
-
super()
-
@file_name = file_name
-
@output_stream = if stream
-
iostream = @file_name.dup
-
iostream.reopen
-
iostream.rewind
-
iostream
-
else
-
::File.new(@file_name, "wb")
-
end
-
@entry_set = ::Zip::EntrySet.new
-
@compressor = ::Zip::NullCompressor.instance
-
@closed = false
-
@current_entry = nil
-
@comment = nil
-
end
-
-
# Same as #initialize but if a block is passed the opened
-
# stream is passed to the block and closed when the block
-
# returns.
-
1
class << self
-
1
def open(file_name)
-
return new(file_name) unless block_given?
-
zos = new(file_name)
-
yield zos
-
ensure
-
zos.close if zos
-
end
-
-
# Same as #open but writes to a filestream instead
-
1
def write_buffer(io = ::StringIO.new(''))
-
zos = new(io, true)
-
yield zos
-
zos.close_buffer
-
end
-
end
-
-
# Closes the stream and writes the central directory to the zip file
-
1
def close
-
return if @closed
-
finalize_current_entry
-
update_local_headers
-
write_central_directory
-
@output_stream.close
-
@closed = true
-
end
-
-
# Closes the stream and writes the central directory to the zip file
-
1
def close_buffer
-
return @output_stream if @closed
-
finalize_current_entry
-
update_local_headers
-
write_central_directory
-
@closed = true
-
@output_stream
-
end
-
-
# Closes the current entry and opens a new for writing.
-
# +entry+ can be a ZipEntry object or a string.
-
1
def put_next_entry(entry_name, comment = nil, extra = nil, compression_method = Entry::DEFLATED, level = Zip.default_compression)
-
raise Error, "zip stream is closed" if @closed
-
if entry_name.kind_of?(Entry)
-
new_entry = entry_name
-
else
-
new_entry = Entry.new(@file_name, entry_name.to_s)
-
end
-
new_entry.comment = comment unless comment.nil?
-
unless extra.nil?
-
new_entry.extra = ExtraField === extra ? extra : ExtraField.new(extra.to_s)
-
end
-
new_entry.compression_method = compression_method unless compression_method.nil?
-
init_next_entry(new_entry, level)
-
@current_entry = new_entry
-
end
-
-
1
def copy_raw_entry(entry)
-
entry = entry.dup
-
raise Error, "zip stream is closed" if @closed
-
raise Error, "entry is not a ZipEntry" unless entry.is_a?(Entry)
-
finalize_current_entry
-
@entry_set << entry
-
src_pos = entry.local_header_offset
-
entry.write_local_entry(@output_stream)
-
@compressor = NullCompressor.instance
-
entry.get_raw_input_stream do |is|
-
is.seek(src_pos, IO::SEEK_SET)
-
::Zip::Entry.read_local_entry(is)
-
IOExtras.copy_stream_n(@output_stream, is, entry.compressed_size)
-
end
-
@compressor = NullCompressor.instance
-
@current_entry = nil
-
end
-
-
1
private
-
-
1
def finalize_current_entry
-
return unless @current_entry
-
finish
-
@current_entry.compressed_size = @output_stream.tell - @current_entry.local_header_offset - @current_entry.calculate_local_header_size
-
@current_entry.size = @compressor.size
-
@current_entry.crc = @compressor.crc
-
@current_entry = nil
-
@compressor = ::Zip::NullCompressor.instance
-
end
-
-
1
def init_next_entry(entry, level = Zip.default_compression)
-
finalize_current_entry
-
@entry_set << entry
-
entry.write_local_entry(@output_stream)
-
@compressor = get_compressor(entry, level)
-
end
-
-
1
def get_compressor(entry, level)
-
case entry.compression_method
-
when Entry::DEFLATED then
-
::Zip::Deflater.new(@output_stream, level)
-
when Entry::STORED then
-
::Zip::PassThruCompressor.new(@output_stream)
-
else
-
raise ::Zip::CompressionMethodError,
-
"Invalid compression method: '#{entry.compression_method}'"
-
end
-
end
-
-
1
def update_local_headers
-
pos = @output_stream.pos
-
@entry_set.each do |entry|
-
@output_stream.pos = entry.local_header_offset
-
entry.write_local_entry(@output_stream, true)
-
end
-
@output_stream.pos = pos
-
end
-
-
1
def write_central_directory
-
cdir = CentralDirectory.new(@entry_set, @comment)
-
cdir.write_to_stream(@output_stream)
-
end
-
-
1
protected
-
-
1
def finish
-
@compressor.finish
-
end
-
-
1
public
-
-
# Modeled after IO.<<
-
1
def << (data)
-
@compressor << data
-
self
-
end
-
-
end
-
end
-
-
# Copyright (C) 2002, 2003 Thomas Sondergaard
-
# rubyzip is free software; you can redistribute it and/or
-
# modify it under the terms of the ruby license.
-
1
module Zip
-
1
class PassThruCompressor < Compressor #:nodoc:all
-
1
def initialize(outputStream)
-
super()
-
@output_stream = outputStream
-
@crc = Zlib::crc32
-
@size = 0
-
end
-
-
1
def << (data)
-
val = data.to_s
-
@crc = Zlib::crc32(val, @crc)
-
@size += val.bytesize
-
@output_stream << val
-
end
-
-
1
attr_reader :size, :crc
-
end
-
end
-
-
# Copyright (C) 2002, 2003 Thomas Sondergaard
-
# rubyzip is free software; you can redistribute it and/or
-
# modify it under the terms of the ruby license.
-
1
module Zip
-
1
class PassThruDecompressor < Decompressor #:nodoc:all
-
-
1
def initialize(input_stream, chars_to_read)
-
super(input_stream)
-
@chars_to_read = chars_to_read
-
@read_so_far = 0
-
@has_returned_empty_string = false
-
end
-
-
1
def sysread(number_of_bytes = nil, buf = '')
-
if input_finished?
-
has_returned_empty_string_val = @has_returned_empty_string
-
@has_returned_empty_string = true
-
return '' unless has_returned_empty_string_val
-
return
-
end
-
-
if number_of_bytes.nil? || @read_so_far + number_of_bytes > @chars_to_read
-
number_of_bytes = @chars_to_read - @read_so_far
-
end
-
@read_so_far += number_of_bytes
-
@input_stream.read(number_of_bytes, buf)
-
end
-
-
1
def produce_input
-
sysread(::Zip::Decompressor::CHUNK_SIZE)
-
end
-
-
1
def input_finished?
-
@read_so_far >= @chars_to_read
-
end
-
-
1
alias :eof :input_finished?
-
1
alias :eof? :input_finished?
-
end
-
end
-
-
# Copyright (C) 2002, 2003 Thomas Sondergaard
-
# rubyzip is free software; you can redistribute it and/or
-
# modify it under the terms of the ruby license.
-
1
module Zip
-
1
class StreamableDirectory < Entry
-
1
def initialize(zipfile, entry, srcPath = nil, permissionInt = nil)
-
super(zipfile, entry)
-
-
@ftype = :directory
-
entry.get_extra_attributes_from_path(srcPath) if (srcPath)
-
@unix_perms = permissionInt if (permissionInt)
-
end
-
end
-
end
-
-
# Copyright (C) 2002, 2003 Thomas Sondergaard
-
# rubyzip is free software; you can redistribute it and/or
-
# modify it under the terms of the ruby license.
-
1
module Zip
-
1
class StreamableStream < DelegateClass(Entry) #nodoc:all
-
1
def initialize(entry)
-
super(entry)
-
dirname = if zipfile.is_a?(::String)
-
::File.dirname(zipfile)
-
else
-
'.'
-
end
-
@temp_file = Tempfile.new(::File.basename(name), dirname)
-
@temp_file.binmode
-
end
-
-
1
def get_output_stream
-
if block_given?
-
begin
-
yield(@temp_file)
-
ensure
-
@temp_file.close
-
end
-
else
-
@temp_file
-
end
-
end
-
-
1
def get_input_stream
-
if !@temp_file.closed?
-
raise StandardError, "cannot open entry for reading while its open for writing - #{name}"
-
end
-
@temp_file.open # reopens tempfile from top
-
@temp_file.binmode
-
if block_given?
-
begin
-
yield(@temp_file)
-
ensure
-
@temp_file.close
-
end
-
else
-
@temp_file
-
end
-
end
-
-
1
def write_to_zip_output_stream(aZipOutputStream)
-
aZipOutputStream.put_next_entry(self)
-
get_input_stream { |is| ::Zip::IOExtras.copy_stream(aZipOutputStream, is) }
-
end
-
-
1
def clean_up
-
@temp_file.unlink
-
end
-
end
-
end
-
-
# Copyright (C) 2002, 2003 Thomas Sondergaard
-
# rubyzip is free software; you can redistribute it and/or
-
# modify it under the terms of the ruby license.
-
1
module SafeYAML
-
1
class Deep
-
1
def self.freeze(object)
-
14
object.each do |*entry|
-
31
value = entry.last
-
31
case value
-
when String, Regexp
-
7
value.freeze
-
when Enumerable
-
11
Deep.freeze(value)
-
end
-
end
-
-
14
return object.freeze
-
end
-
-
1
def self.copy(object)
-
7
duplicate = object.dup rescue object
-
-
7
case object
-
when Array
-
1
(0...duplicate.count).each do |i|
-
duplicate[i] = Deep.copy(duplicate[i])
-
end
-
when Hash
-
2
duplicate.keys.each do |key|
-
6
duplicate[key] = Deep.copy(duplicate[key])
-
end
-
end
-
-
7
duplicate
-
end
-
end
-
end
-
1
require "set"
-
-
1
module SafeYAML
-
1
class LibyamlChecker
-
1
LIBYAML_VERSION = Psych::LIBYAML_VERSION rescue nil
-
-
# Do proper version comparison (e.g. so 0.1.10 is >= 0.1.6)
-
1
SAFE_LIBYAML_VERSION = Gem::Version.new("0.1.6")
-
-
1
KNOWN_PATCHED_LIBYAML_VERSIONS = Set.new([
-
# http://people.canonical.com/~ubuntu-security/cve/2014/CVE-2014-2525.html
-
"0.1.4-2ubuntu0.12.04.3",
-
"0.1.4-2ubuntu0.12.10.3",
-
"0.1.4-2ubuntu0.13.10.3",
-
"0.1.4-3ubuntu3",
-
-
# https://security-tracker.debian.org/tracker/CVE-2014-2525
-
"0.1.3-1+deb6u4",
-
"0.1.4-2+deb7u4",
-
"0.1.4-3.2"
-
]).freeze
-
-
1
def self.libyaml_version_ok?
-
return true if YAML_ENGINE != "psych" || defined?(JRUBY_VERSION)
-
return true if Gem::Version.new(LIBYAML_VERSION || "0") >= SAFE_LIBYAML_VERSION
-
return libyaml_patched?
-
end
-
-
1
def self.libyaml_patched?
-
return false if (`which dpkg` rescue '').empty?
-
libyaml_version = `dpkg -s libyaml-0-2`.match(/^Version: (.*)$/)
-
return false if libyaml_version.nil?
-
KNOWN_PATCHED_LIBYAML_VERSIONS.include?(libyaml_version[1])
-
end
-
end
-
end
-
1
require "set"
-
1
require "yaml"
-
-
# This needs to be defined up front in case any internal classes need to base
-
# their behavior off of this.
-
1
module SafeYAML
-
1
YAML_ENGINE = defined?(YAML::ENGINE) ? YAML::ENGINE.yamler : (defined?(Psych) && YAML == Psych ? "psych" : "syck")
-
end
-
-
1
require "safe_yaml/libyaml_checker"
-
1
require "safe_yaml/deep"
-
1
require "safe_yaml/parse/hexadecimal"
-
1
require "safe_yaml/parse/sexagesimal"
-
1
require "safe_yaml/parse/date"
-
1
require "safe_yaml/transform/transformation_map"
-
1
require "safe_yaml/transform/to_boolean"
-
1
require "safe_yaml/transform/to_date"
-
1
require "safe_yaml/transform/to_float"
-
1
require "safe_yaml/transform/to_integer"
-
1
require "safe_yaml/transform/to_nil"
-
1
require "safe_yaml/transform/to_symbol"
-
1
require "safe_yaml/transform"
-
1
require "safe_yaml/resolver"
-
1
require "safe_yaml/syck_hack" if SafeYAML::YAML_ENGINE == "syck" && defined?(JRUBY_VERSION)
-
-
1
module SafeYAML
-
1
MULTI_ARGUMENT_YAML_LOAD = YAML.method(:load).arity != 1
-
-
1
DEFAULT_OPTIONS = Deep.freeze({
-
:default_mode => nil,
-
:suppress_warnings => false,
-
:deserialize_symbols => false,
-
:whitelisted_tags => [],
-
:custom_initializers => {},
-
:raise_on_unknown_tag => false
-
})
-
-
1
OPTIONS = Deep.copy(DEFAULT_OPTIONS)
-
-
1
PREDEFINED_TAGS = {}
-
-
1
if YAML_ENGINE == "syck"
-
YAML.tagged_classes.each do |tag, klass|
-
PREDEFINED_TAGS[klass] = tag
-
end
-
-
else
-
# Special tags appear to be hard-coded in Psych:
-
# https://github.com/tenderlove/psych/blob/v1.3.4/lib/psych/visitors/to_ruby.rb
-
# Fortunately, there aren't many that SafeYAML doesn't already support.
-
1
PREDEFINED_TAGS.merge!({
-
Exception => "!ruby/exception",
-
Range => "!ruby/range",
-
Regexp => "!ruby/regexp",
-
})
-
end
-
-
1
Deep.freeze(PREDEFINED_TAGS)
-
-
1
module_function
-
-
1
def restore_defaults!
-
OPTIONS.clear.merge!(Deep.copy(DEFAULT_OPTIONS))
-
end
-
-
1
def tag_safety_check!(tag, options)
-
return if tag.nil? || tag == "!"
-
if options[:raise_on_unknown_tag] && !options[:whitelisted_tags].include?(tag) && !tag_is_explicitly_trusted?(tag)
-
raise "Unknown YAML tag '#{tag}'"
-
end
-
end
-
-
1
def whitelist!(*classes)
-
classes.each do |klass|
-
whitelist_class!(klass)
-
end
-
end
-
-
1
def whitelist_class!(klass)
-
raise "#{klass} not a Class" unless klass.is_a?(::Class)
-
-
klass_name = klass.name
-
raise "#{klass} cannot be anonymous" if klass_name.nil? || klass_name.empty?
-
-
# Whitelist any built-in YAML tags supplied by Syck or Psych.
-
predefined_tag = PREDEFINED_TAGS[klass]
-
if predefined_tag
-
OPTIONS[:whitelisted_tags] << predefined_tag
-
return
-
end
-
-
# Exception is exceptional (har har).
-
tag_class = klass < Exception ? "exception" : "object"
-
-
tag_prefix = case YAML_ENGINE
-
when "psych" then "!ruby/#{tag_class}"
-
when "syck" then "tag:ruby.yaml.org,2002:#{tag_class}"
-
else raise "unknown YAML_ENGINE #{YAML_ENGINE}"
-
end
-
OPTIONS[:whitelisted_tags] << "#{tag_prefix}:#{klass_name}"
-
end
-
-
1
if YAML_ENGINE == "psych"
-
1
def tag_is_explicitly_trusted?(tag)
-
false
-
end
-
-
else
-
TRUSTED_TAGS = Set.new([
-
"tag:yaml.org,2002:binary",
-
"tag:yaml.org,2002:bool#no",
-
"tag:yaml.org,2002:bool#yes",
-
"tag:yaml.org,2002:float",
-
"tag:yaml.org,2002:float#fix",
-
"tag:yaml.org,2002:int",
-
"tag:yaml.org,2002:map",
-
"tag:yaml.org,2002:null",
-
"tag:yaml.org,2002:seq",
-
"tag:yaml.org,2002:str",
-
"tag:yaml.org,2002:timestamp",
-
"tag:yaml.org,2002:timestamp#ymd"
-
]).freeze
-
-
def tag_is_explicitly_trusted?(tag)
-
TRUSTED_TAGS.include?(tag)
-
end
-
end
-
-
1
if SafeYAML::YAML_ENGINE == "psych"
-
1
require "safe_yaml/psych_handler"
-
1
require "safe_yaml/psych_resolver"
-
1
require "safe_yaml/safe_to_ruby_visitor"
-
-
1
def self.load(yaml, filename=nil, options={})
-
# If the user hasn't whitelisted any tags, we can go with this implementation which is
-
# significantly faster.
-
if (options && options[:whitelisted_tags] || SafeYAML::OPTIONS[:whitelisted_tags]).empty?
-
safe_handler = SafeYAML::PsychHandler.new(options) do |result|
-
return result
-
end
-
arguments_for_parse = [yaml]
-
arguments_for_parse << filename if SafeYAML::MULTI_ARGUMENT_YAML_LOAD
-
Psych::Parser.new(safe_handler).parse(*arguments_for_parse)
-
return safe_handler.result
-
-
else
-
safe_resolver = SafeYAML::PsychResolver.new(options)
-
tree = SafeYAML::MULTI_ARGUMENT_YAML_LOAD ?
-
Psych.parse(yaml, filename) :
-
Psych.parse(yaml)
-
return safe_resolver.resolve_node(tree)
-
end
-
end
-
-
1
def self.load_file(filename, options={})
-
if SafeYAML::MULTI_ARGUMENT_YAML_LOAD
-
File.open(filename, 'r:bom|utf-8') { |f| self.load(f, filename, options) }
-
-
else
-
# Ruby pukes on 1.9.2 if we try to open an empty file w/ 'r:bom|utf-8';
-
# so we'll not specify those flags here. This mirrors the behavior for
-
# unsafe_load_file so it's probably preferable anyway.
-
self.load File.open(filename), nil, options
-
end
-
end
-
-
else
-
require "safe_yaml/syck_resolver"
-
require "safe_yaml/syck_node_monkeypatch"
-
-
def self.load(yaml, options={})
-
resolver = SafeYAML::SyckResolver.new(SafeYAML::OPTIONS.merge(options || {}))
-
tree = YAML.parse(yaml)
-
return resolver.resolve_node(tree)
-
end
-
-
def self.load_file(filename, options={})
-
File.open(filename) { |f| self.load(f, options) }
-
end
-
end
-
end
-
1
module SafeYAML
-
1
class Parse
-
1
class Date
-
# This one's easy enough :)
-
1
DATE_MATCHER = /\A(\d{4})-(\d{2})-(\d{2})\Z/.freeze
-
-
# This unbelievable little gem is taken basically straight from the YAML spec, but made
-
# slightly more readable (to my poor eyes at least) to me:
-
# http://yaml.org/type/timestamp.html
-
1
TIME_MATCHER = /\A\d{4}-\d{1,2}-\d{1,2}(?:[Tt]|\s+)\d{1,2}:\d{2}:\d{2}(?:\.\d*)?\s*(?:Z|[-+]\d{1,2}(?::?\d{2})?)?\Z/.freeze
-
-
1
SECONDS_PER_DAY = 60 * 60 * 24
-
1
MICROSECONDS_PER_SECOND = 1000000
-
-
# So this is weird. In Ruby 1.8.7, the DateTime#sec_fraction method returned fractional
-
# seconds in units of DAYS for some reason. In 1.9.2, they changed the units -- much more
-
# reasonably -- to seconds.
-
1
SEC_FRACTION_MULTIPLIER = RUBY_VERSION == "1.8.7" ? (SECONDS_PER_DAY * MICROSECONDS_PER_SECOND) : MICROSECONDS_PER_SECOND
-
-
# The DateTime class has a #to_time method in Ruby 1.9+;
-
# Before that we'll just need to convert DateTime to Time ourselves.
-
1
TO_TIME_AVAILABLE = DateTime.instance_methods.include?(:to_time)
-
-
1
def self.value(value)
-
d = DateTime.parse(value)
-
-
return d.to_time if TO_TIME_AVAILABLE
-
-
usec = d.sec_fraction * SEC_FRACTION_MULTIPLIER
-
time = Time.utc(d.year, d.month, d.day, d.hour, d.min, d.sec, usec) - (d.offset * SECONDS_PER_DAY)
-
time.getlocal
-
end
-
end
-
end
-
end
-
1
module SafeYAML
-
1
class Parse
-
1
class Hexadecimal
-
1
MATCHER = /\A[-+]?0x[0-9a-fA-F_]+\Z/.freeze
-
-
1
def self.value(value)
-
# This is safe to do since we already validated the value.
-
return Integer(value.gsub(/_/, ""))
-
end
-
end
-
end
-
end
-
1
module SafeYAML
-
1
class Parse
-
1
class Sexagesimal
-
1
INTEGER_MATCHER = /\A[-+]?[0-9][0-9_]*(:[0-5]?[0-9])+\Z/.freeze
-
1
FLOAT_MATCHER = /\A[-+]?[0-9][0-9_]*(:[0-5]?[0-9])+\.[0-9_]*\Z/.freeze
-
-
1
def self.value(value)
-
before_decimal, after_decimal = value.split(".")
-
-
whole_part = 0
-
multiplier = 1
-
-
before_decimal = before_decimal.split(":")
-
until before_decimal.empty?
-
whole_part += (Float(before_decimal.pop) * multiplier)
-
multiplier *= 60
-
end
-
-
result = whole_part
-
result += Float("." + after_decimal) unless after_decimal.nil?
-
result *= -1 if value[0] == "-"
-
result
-
end
-
end
-
end
-
end
-
1
require "psych"
-
1
require "base64"
-
-
1
module SafeYAML
-
1
class PsychHandler < Psych::Handler
-
1
def initialize(options, &block)
-
@options = SafeYAML::OPTIONS.merge(options || {})
-
@block = block
-
@initializers = @options[:custom_initializers] || {}
-
@anchors = {}
-
@stack = []
-
@current_key = nil
-
@result = nil
-
@begun = false
-
end
-
-
1
def result
-
@begun ? @result : false
-
end
-
-
1
def add_to_current_structure(value, anchor=nil, quoted=nil, tag=nil)
-
value = Transform.to_proper_type(value, quoted, tag, @options)
-
-
@anchors[anchor] = value if anchor
-
-
if !@begun
-
@begun = true
-
@result = value
-
@current_structure = @result
-
return
-
end
-
-
if @current_structure.respond_to?(:<<)
-
@current_structure << value
-
-
elsif @current_structure.respond_to?(:[]=)
-
if @current_key.nil?
-
@current_key = value
-
-
else
-
if @current_key == "<<"
-
@current_structure.merge!(value)
-
else
-
@current_structure[@current_key] = value
-
end
-
-
@current_key = nil
-
end
-
-
else
-
raise "Don't know how to add to a #{@current_structure.class}!"
-
end
-
end
-
-
1
def end_current_structure
-
@stack.pop
-
@current_structure = @stack.last
-
end
-
-
1
def streaming?
-
true
-
end
-
-
# event handlers
-
1
def alias(anchor)
-
add_to_current_structure(@anchors[anchor])
-
end
-
-
1
def scalar(value, anchor, tag, plain, quoted, style)
-
add_to_current_structure(value, anchor, quoted, tag)
-
end
-
-
1
def end_document(implicit)
-
@block.call(@result)
-
end
-
-
1
def start_mapping(anchor, tag, implicit, style)
-
map = @initializers.include?(tag) ? @initializers[tag].call : {}
-
self.add_to_current_structure(map, anchor)
-
@current_structure = map
-
@stack.push(map)
-
end
-
-
1
def end_mapping
-
self.end_current_structure()
-
end
-
-
1
def start_sequence(anchor, tag, implicit, style)
-
seq = @initializers.include?(tag) ? @initializers[tag].call : []
-
self.add_to_current_structure(seq, anchor)
-
@current_structure = seq
-
@stack.push(seq)
-
end
-
-
1
def end_sequence
-
self.end_current_structure()
-
end
-
end
-
end
-
1
module SafeYAML
-
1
class PsychResolver < Resolver
-
1
NODE_TYPES = {
-
Psych::Nodes::Document => :root,
-
Psych::Nodes::Mapping => :map,
-
Psych::Nodes::Sequence => :seq,
-
Psych::Nodes::Scalar => :scalar,
-
Psych::Nodes::Alias => :alias
-
}.freeze
-
-
1
def initialize(options={})
-
super
-
@aliased_nodes = {}
-
end
-
-
1
def resolve_root(root)
-
resolve_seq(root).first
-
end
-
-
1
def resolve_alias(node)
-
resolve_node(@aliased_nodes[node.anchor])
-
end
-
-
1
def native_resolve(node)
-
@visitor ||= SafeYAML::SafeToRubyVisitor.new(self)
-
@visitor.accept(node)
-
end
-
-
1
def get_node_type(node)
-
NODE_TYPES[node.class]
-
end
-
-
1
def get_node_tag(node)
-
node.tag
-
end
-
-
1
def get_node_value(node)
-
@aliased_nodes[node.anchor] = node if node.respond_to?(:anchor) && node.anchor
-
-
case get_node_type(node)
-
when :root, :map, :seq
-
node.children
-
when :scalar
-
node.value
-
end
-
end
-
-
1
def value_is_quoted?(node)
-
node.quoted
-
end
-
end
-
end
-
1
module SafeYAML
-
1
class Resolver
-
1
def initialize(options)
-
@options = SafeYAML::OPTIONS.merge(options || {})
-
@whitelist = @options[:whitelisted_tags] || []
-
@initializers = @options[:custom_initializers] || {}
-
@raise_on_unknown_tag = @options[:raise_on_unknown_tag]
-
end
-
-
1
def resolve_node(node)
-
return node if !node
-
return self.native_resolve(node) if tag_is_whitelisted?(self.get_node_tag(node))
-
-
case self.get_node_type(node)
-
when :root
-
resolve_root(node)
-
when :map
-
resolve_map(node)
-
when :seq
-
resolve_seq(node)
-
when :scalar
-
resolve_scalar(node)
-
when :alias
-
resolve_alias(node)
-
else
-
raise "Don't know how to resolve this node: #{node.inspect}"
-
end
-
end
-
-
1
def resolve_map(node)
-
tag = get_and_check_node_tag(node)
-
hash = @initializers.include?(tag) ? @initializers[tag].call : {}
-
map = normalize_map(self.get_node_value(node))
-
-
# Take the "<<" key nodes first, as these are meant to approximate a form of inheritance.
-
inheritors = map.select { |key_node, value_node| resolve_node(key_node) == "<<" }
-
inheritors.each do |key_node, value_node|
-
merge_into_hash(hash, resolve_node(value_node))
-
end
-
-
# All that's left should be normal (non-"<<") nodes.
-
(map - inheritors).each do |key_node, value_node|
-
hash[resolve_node(key_node)] = resolve_node(value_node)
-
end
-
-
return hash
-
end
-
-
1
def resolve_seq(node)
-
seq = self.get_node_value(node)
-
-
tag = get_and_check_node_tag(node)
-
arr = @initializers.include?(tag) ? @initializers[tag].call : []
-
-
seq.inject(arr) { |array, n| array << resolve_node(n) }
-
end
-
-
1
def resolve_scalar(node)
-
Transform.to_proper_type(self.get_node_value(node), self.value_is_quoted?(node), get_and_check_node_tag(node), @options)
-
end
-
-
1
def get_and_check_node_tag(node)
-
tag = self.get_node_tag(node)
-
SafeYAML.tag_safety_check!(tag, @options)
-
tag
-
end
-
-
1
def tag_is_whitelisted?(tag)
-
@whitelist.include?(tag)
-
end
-
-
1
def options
-
@options
-
end
-
-
1
private
-
1
def normalize_map(map)
-
# Syck creates Hashes from maps.
-
if map.is_a?(Hash)
-
map.inject([]) { |arr, key_and_value| arr << key_and_value }
-
-
# Psych is really weird; it flattens out a Hash completely into: [key, value, key, value, ...]
-
else
-
map.each_slice(2).to_a
-
end
-
end
-
-
1
def merge_into_hash(hash, array)
-
array.each do |key, value|
-
hash[key] = value
-
end
-
end
-
end
-
end
-
1
module SafeYAML
-
1
class SafeToRubyVisitor < Psych::Visitors::ToRuby
-
1
INITIALIZE_ARITY = superclass.instance_method(:initialize).arity
-
-
1
def initialize(resolver)
-
case INITIALIZE_ARITY
-
when 2
-
# https://github.com/tenderlove/psych/blob/v2.0.0/lib/psych/visitors/to_ruby.rb#L14-L28
-
loader = Psych::ClassLoader.new
-
scanner = Psych::ScalarScanner.new(loader)
-
super(scanner, loader)
-
-
else
-
super()
-
end
-
-
@resolver = resolver
-
end
-
-
1
def accept(node)
-
if node.tag
-
SafeYAML.tag_safety_check!(node.tag, @resolver.options)
-
return super
-
end
-
-
@resolver.resolve_node(node)
-
end
-
end
-
end
-
1
require 'base64'
-
-
1
module SafeYAML
-
1
class Transform
-
1
TRANSFORMERS = [
-
Transform::ToSymbol.new,
-
Transform::ToInteger.new,
-
Transform::ToFloat.new,
-
Transform::ToNil.new,
-
Transform::ToBoolean.new,
-
Transform::ToDate.new
-
]
-
-
1
def self.to_guessed_type(value, quoted=false, options=nil)
-
return value if quoted
-
-
if value.is_a?(String)
-
TRANSFORMERS.each do |transformer|
-
success, transformed_value = transformer.method(:transform?).arity == 1 ?
-
transformer.transform?(value) :
-
transformer.transform?(value, options)
-
-
return transformed_value if success
-
end
-
end
-
-
value
-
end
-
-
1
def self.to_proper_type(value, quoted=false, tag=nil, options=nil)
-
case tag
-
when "tag:yaml.org,2002:binary", "x-private:binary", "!binary"
-
decoded = Base64.decode64(value)
-
decoded = decoded.force_encoding(value.encoding) if decoded.respond_to?(:force_encoding)
-
decoded
-
else
-
self.to_guessed_type(value, quoted, options)
-
end
-
end
-
end
-
end
-
1
module SafeYAML
-
1
class Transform
-
1
class ToBoolean
-
1
include TransformationMap
-
-
1
set_predefined_values({
-
"yes" => true,
-
"on" => true,
-
"true" => true,
-
"no" => false,
-
"off" => false,
-
"false" => false
-
})
-
-
1
def transform?(value)
-
return false if value.length > 5
-
return PREDEFINED_VALUES.include?(value), PREDEFINED_VALUES[value]
-
end
-
end
-
end
-
end
-
1
module SafeYAML
-
1
class Transform
-
1
class ToDate
-
1
def transform?(value)
-
return true, Date.parse(value) if Parse::Date::DATE_MATCHER.match(value)
-
return true, Parse::Date.value(value) if Parse::Date::TIME_MATCHER.match(value)
-
false
-
rescue ArgumentError
-
return true, value
-
end
-
end
-
end
-
end
-
1
module SafeYAML
-
1
class Transform
-
1
class ToFloat
-
1
Infinity = 1.0 / 0.0
-
1
NaN = 0.0 / 0.0
-
-
1
PREDEFINED_VALUES = {
-
".inf" => Infinity,
-
".Inf" => Infinity,
-
".INF" => Infinity,
-
"-.inf" => -Infinity,
-
"-.Inf" => -Infinity,
-
"-.INF" => -Infinity,
-
".nan" => NaN,
-
".NaN" => NaN,
-
".NAN" => NaN,
-
}.freeze
-
-
1
MATCHER = /\A[-+]?(?:\d[\d_]*)?\.[\d_]+(?:[eE][-+][\d]+)?\Z/.freeze
-
-
1
def transform?(value)
-
return true, Float(value) if MATCHER.match(value)
-
try_edge_cases?(value)
-
end
-
-
1
def try_edge_cases?(value)
-
return true, PREDEFINED_VALUES[value] if PREDEFINED_VALUES.include?(value)
-
return true, Parse::Sexagesimal.value(value) if Parse::Sexagesimal::FLOAT_MATCHER.match(value)
-
return false
-
end
-
end
-
end
-
end
-
1
module SafeYAML
-
1
class Transform
-
1
class ToInteger
-
1
MATCHERS = Deep.freeze([
-
/\A[-+]?(0|([1-9][0-9_,]*))\Z/, # decimal
-
/\A0[0-7]+\Z/, # octal
-
/\A0x[0-9a-f]+\Z/i, # hexadecimal
-
/\A0b[01_]+\Z/ # binary
-
])
-
-
1
def transform?(value)
-
MATCHERS.each_with_index do |matcher, idx|
-
value = value.gsub(/[_,]/, "") if idx == 0
-
return true, Integer(value) if matcher.match(value)
-
end
-
try_edge_cases?(value)
-
end
-
-
1
def try_edge_cases?(value)
-
return true, Parse::Hexadecimal.value(value) if Parse::Hexadecimal::MATCHER.match(value)
-
return true, Parse::Sexagesimal.value(value) if Parse::Sexagesimal::INTEGER_MATCHER.match(value)
-
return false
-
end
-
end
-
end
-
end
-
1
module SafeYAML
-
1
class Transform
-
1
class ToNil
-
1
include TransformationMap
-
-
1
set_predefined_values({
-
"" => nil,
-
"~" => nil,
-
"null" => nil
-
})
-
-
1
def transform?(value)
-
return false if value.length > 4
-
return PREDEFINED_VALUES.include?(value), PREDEFINED_VALUES[value]
-
end
-
end
-
end
-
end
-
1
module SafeYAML
-
1
class Transform
-
1
class ToSymbol
-
1
def transform?(value, options=SafeYAML::OPTIONS)
-
if options[:deserialize_symbols] && value =~ /\A:./
-
if value =~ /\A:(["'])(.*)\1\Z/
-
return true, $2.sub(/^:/, "").to_sym
-
else
-
return true, value.sub(/^:/, "").to_sym
-
end
-
end
-
-
return false
-
end
-
end
-
end
-
end
-
1
module SafeYAML
-
1
class Transform
-
1
module TransformationMap
-
1
def self.included(base)
-
2
base.extend(ClassMethods)
-
end
-
-
1
class CaseAgnosticMap < Hash
-
1
def initialize(*args)
-
2
super
-
end
-
-
1
def include?(key)
-
super(key.downcase)
-
end
-
-
1
def [](key)
-
super(key.downcase)
-
end
-
-
# OK, I actually don't think it's all that important that this map be
-
# frozen.
-
1
def freeze
-
2
self
-
end
-
end
-
-
1
module ClassMethods
-
1
def set_predefined_values(predefined_values)
-
2
if SafeYAML::YAML_ENGINE == "syck"
-
expanded_map = predefined_values.inject({}) do |hash, (key, value)|
-
hash[key] = value
-
hash[key.capitalize] = value
-
hash[key.upcase] = value
-
hash
-
end
-
else
-
2
expanded_map = CaseAgnosticMap.new
-
2
expanded_map.merge!(predefined_values)
-
end
-
-
2
self.const_set(:PREDEFINED_VALUES, expanded_map.freeze)
-
end
-
end
-
end
-
end
-
end
-
1
dir = File.dirname(__FILE__)
-
1
$LOAD_PATH.unshift dir unless $LOAD_PATH.include?(dir)
-
-
# This is necessary to set so that the Haml code that tries to load Sass
-
# knows that Sass is indeed loading,
-
# even if there's some crazy autoload stuff going on.
-
1
SASS_BEGUN_TO_LOAD = true unless defined?(SASS_BEGUN_TO_LOAD)
-
-
1
require 'sass/version'
-
-
# The module that contains everything Sass-related:
-
#
-
# * {Sass::Engine} is the class used to render Sass/SCSS within Ruby code.
-
# * {Sass::Plugin} is interfaces with web frameworks (Rails and Merb in particular).
-
# * {Sass::SyntaxError} is raised when Sass encounters an error.
-
# * {Sass::CSS} handles conversion of CSS to Sass.
-
#
-
# Also see the {file:SASS_REFERENCE.md full Sass reference}.
-
1
module Sass
-
# The global load paths for Sass files. This is meant for plugins and
-
# libraries to register the paths to their Sass stylesheets to that they may
-
# be `@imported`. This load path is used by every instance of [Sass::Engine].
-
# They are lower-precedence than any load paths passed in via the
-
# {file:SASS_REFERENCE.md#load_paths-option `:load_paths` option}.
-
#
-
# If the `SASS_PATH` environment variable is set,
-
# the initial value of `load_paths` will be initialized based on that.
-
# The variable should be a colon-separated list of path names
-
# (semicolon-separated on Windows).
-
#
-
# Note that files on the global load path are never compiled to CSS
-
# themselves, even if they aren't partials. They exist only to be imported.
-
#
-
# @example
-
# Sass.load_paths << File.dirname(__FILE__ + '/sass')
-
# @return [Array<String, Pathname, Sass::Importers::Base>]
-
1
def self.load_paths
-
@load_paths ||= ENV['SASS_PATH'] ?
-
1
ENV['SASS_PATH'].split(Sass::Util.windows? ? ';' : ':') : []
-
end
-
-
# Compile a Sass or SCSS string to CSS.
-
# Defaults to SCSS.
-
#
-
# @param contents [String] The contents of the Sass file.
-
# @param options [{Symbol => Object}] An options hash;
-
# see {file:SASS_REFERENCE.md#sass_options the Sass options documentation}
-
# @raise [Sass::SyntaxError] if there's an error in the document
-
# @raise [Encoding::UndefinedConversionError] if the source encoding
-
# cannot be converted to UTF-8
-
# @raise [ArgumentError] if the document uses an unknown encoding with `@charset`
-
1
def self.compile(contents, options = {})
-
options[:syntax] ||= :scss
-
Engine.new(contents, options).to_css
-
end
-
-
# Compile a file on disk to CSS.
-
#
-
# @param filename [String] The path to the Sass, SCSS, or CSS file on disk.
-
# @param options [{Symbol => Object}] An options hash;
-
# see {file:SASS_REFERENCE.md#sass_options the Sass options documentation}
-
# @raise [Sass::SyntaxError] if there's an error in the document
-
# @raise [Encoding::UndefinedConversionError] if the source encoding
-
# cannot be converted to UTF-8
-
# @raise [ArgumentError] if the document uses an unknown encoding with `@charset`
-
#
-
# @overload compile_file(filename, options = {})
-
# Return the compiled CSS rather than writing it to a file.
-
#
-
# @return [String] The compiled CSS.
-
#
-
# @overload compile_file(filename, css_filename, options = {})
-
# Write the compiled CSS to a file.
-
#
-
# @param css_filename [String] The location to which to write the compiled CSS.
-
1
def self.compile_file(filename, *args)
-
options = args.last.is_a?(Hash) ? args.pop : {}
-
css_filename = args.shift
-
result = Sass::Engine.for_file(filename, options).render
-
if css_filename
-
options[:css_filename] ||= css_filename
-
open(css_filename,"w") {|css_file| css_file.write(result)}
-
nil
-
else
-
result
-
end
-
end
-
end
-
-
1
require 'sass/logger'
-
1
require 'sass/util'
-
-
1
require 'sass/engine'
-
1
require 'sass/plugin' if defined?(Merb::Plugins)
-
1
require 'sass/railtie'
-
1
require 'stringio'
-
-
1
module Sass
-
# Sass cache stores are in charge of storing cached information,
-
# especially parse trees for Sass documents.
-
#
-
# User-created importers must inherit from {CacheStores::Base}.
-
1
module CacheStores
-
end
-
end
-
-
1
require 'sass/cache_stores/base'
-
1
require 'sass/cache_stores/filesystem'
-
1
require 'sass/cache_stores/memory'
-
1
require 'sass/cache_stores/chain'
-
1
module Sass
-
1
module CacheStores
-
# An abstract base class for backends for the Sass cache.
-
# Any key-value store can act as such a backend;
-
# it just needs to implement the
-
# \{#_store} and \{#_retrieve} methods.
-
#
-
# To use a cache store with Sass,
-
# use the {file:SASS_REFERENCE.md#cache_store-option `:cache_store` option}.
-
#
-
# @abstract
-
1
class Base
-
# Store cached contents for later retrieval
-
# Must be implemented by all CacheStore subclasses
-
#
-
# Note: cache contents contain binary data.
-
#
-
# @param key [String] The key to store the contents under
-
# @param version [String] The current sass version.
-
# Cached contents must not be retrieved across different versions of sass.
-
# @param sha [String] The sha of the sass source.
-
# Cached contents must not be retrieved if the sha has changed.
-
# @param contents [String] The contents to store.
-
1
def _store(key, version, sha, contents)
-
raise "#{self.class} must implement #_store."
-
end
-
-
# Retrieved cached contents.
-
# Must be implemented by all subclasses.
-
#
-
# Note: if the key exists but the sha or version have changed,
-
# then the key may be deleted by the cache store, if it wants to do so.
-
#
-
# @param key [String] The key to retrieve
-
# @param version [String] The current sass version.
-
# Cached contents must not be retrieved across different versions of sass.
-
# @param sha [String] The sha of the sass source.
-
# Cached contents must not be retrieved if the sha has changed.
-
# @return [String] The contents that were previously stored.
-
# @return [NilClass] when the cache key is not found or the version or sha have changed.
-
1
def _retrieve(key, version, sha)
-
raise "#{self.class} must implement #_retrieve."
-
end
-
-
# Store a {Sass::Tree::RootNode}.
-
#
-
# @param key [String] The key to store it under.
-
# @param sha [String] The checksum for the contents that are being stored.
-
# @param obj [Object] The object to cache.
-
1
def store(key, sha, root)
-
_store(key, Sass::VERSION, sha, Marshal.dump(root))
-
rescue TypeError, LoadError => e
-
Sass::Util.sass_warn "Warning. Error encountered while saving cache #{path_to(key)}: #{e}"
-
nil
-
end
-
-
# Retrieve a {Sass::Tree::RootNode}.
-
#
-
# @param key [String] The key the root element was stored under.
-
# @param sha [String] The checksum of the root element's content.
-
# @return [Object] The cached object.
-
1
def retrieve(key, sha)
-
contents = _retrieve(key, Sass::VERSION, sha)
-
Marshal.load(contents) if contents
-
rescue EOFError, TypeError, ArgumentError, LoadError => e
-
Sass::Util.sass_warn "Warning. Error encountered while reading cache #{path_to(key)}: #{e}"
-
nil
-
end
-
-
# Return the key for the sass file.
-
#
-
# The `(sass_dirname, sass_basename)` pair
-
# should uniquely identify the Sass document,
-
# but otherwise there are no restrictions on their content.
-
#
-
# @param sass_dirname [String]
-
# The fully-expanded location of the Sass file.
-
# This corresponds to the directory name on a filesystem.
-
# @param sass_basename [String] The name of the Sass file that is being referenced.
-
# This corresponds to the basename on a filesystem.
-
1
def key(sass_dirname, sass_basename)
-
dir = Digest::SHA1.hexdigest(sass_dirname)
-
filename = "#{sass_basename}c"
-
"#{dir}/#{filename}"
-
end
-
end
-
end
-
end
-
1
module Sass
-
1
module CacheStores
-
# A meta-cache that chains multiple caches together.
-
# Specifically:
-
#
-
# * All `#store`s are passed to all caches.
-
# * `#retrieve`s are passed to each cache until one has a hit.
-
# * When one cache has a hit, the value is `#store`d in all earlier caches.
-
1
class Chain < Base
-
# Create a new cache chaining the given caches.
-
#
-
# @param caches [Array<Sass::CacheStores::Base>] The caches to chain.
-
1
def initialize(*caches)
-
@caches = caches
-
end
-
-
# @see Base#store
-
1
def store(key, sha, obj)
-
@caches.each {|c| c.store(key, sha, obj)}
-
end
-
-
# @see Base#retrieve
-
1
def retrieve(key, sha)
-
@caches.each_with_index do |c, i|
-
next unless obj = c.retrieve(key, sha)
-
@caches[0...i].each {|prev| prev.store(key, sha, obj)}
-
return obj
-
end
-
nil
-
end
-
end
-
end
-
end
-
1
require 'fileutils'
-
-
1
module Sass
-
1
module CacheStores
-
# A backend for the Sass cache using the filesystem.
-
1
class Filesystem < Base
-
# The directory where the cached files will be stored.
-
#
-
# @return [String]
-
1
attr_accessor :cache_location
-
-
# @param cache_location [String] see \{#cache\_location}
-
1
def initialize(cache_location)
-
@cache_location = cache_location
-
end
-
-
# @see Base#\_retrieve
-
1
def _retrieve(key, version, sha)
-
return unless File.readable?(path_to(key))
-
File.open(path_to(key), "rb") do |f|
-
if f.readline("\n").strip == version && f.readline("\n").strip == sha
-
return f.read
-
end
-
end
-
begin
-
File.unlink path_to(key)
-
rescue Errno::ENOENT
-
# Already deleted. Race condition?
-
end
-
nil
-
rescue EOFError, TypeError, ArgumentError => e
-
Sass::Util.sass_warn "Warning. Error encountered while reading cache #{path_to(key)}: #{e}"
-
end
-
-
# @see Base#\_store
-
1
def _store(key, version, sha, contents)
-
# return unless File.writable?(File.dirname(@cache_location))
-
# return if File.exists?(@cache_location) && !File.writable?(@cache_location)
-
compiled_filename = path_to(key)
-
# return if File.exists?(File.dirname(compiled_filename)) && !File.writable?(File.dirname(compiled_filename))
-
# return if File.exists?(compiled_filename) && !File.writable?(compiled_filename)
-
FileUtils.mkdir_p(File.dirname(compiled_filename))
-
Sass::Util.atomic_create_and_write_file(compiled_filename, 0600) do |f|
-
f.puts(version)
-
f.puts(sha)
-
f.write(contents)
-
end
-
rescue Errno::EACCES
-
#pass
-
end
-
-
1
private
-
-
# Returns the path to a file for the given key.
-
#
-
# @param key [String]
-
# @return [String] The path to the cache file.
-
1
def path_to(key)
-
key = key.gsub(/[<>:\\|?*%]/) {|c| "%%%03d" % Sass::Util.ord(c)}
-
File.join(cache_location, key)
-
end
-
end
-
end
-
end
-
1
module Sass
-
1
module CacheStores
-
# A backend for the Sass cache using in-process memory.
-
1
class Memory < Base
-
# Since the {Memory} store is stored in the Sass tree's options hash,
-
# when the options get serialized as part of serializing the tree,
-
# you get crazy exponential growth in the size of the cached objects
-
# unless you don't dump the cache.
-
#
-
# @private
-
1
def _dump(depth)
-
""
-
end
-
-
# If we deserialize this class, just make a new empty one.
-
#
-
# @private
-
1
def self._load(repr)
-
Memory.new
-
end
-
-
# Create a new, empty cache store.
-
1
def initialize
-
@contents = {}
-
end
-
-
# @see Base#retrieve
-
1
def retrieve(key, sha)
-
if @contents.has_key?(key)
-
return unless @contents[key][:sha] == sha
-
obj = @contents[key][:obj]
-
obj.respond_to?(:deep_copy) ? obj.deep_copy : obj.dup
-
end
-
end
-
-
# @see Base#store
-
1
def store(key, sha, obj)
-
@contents[key] = {:sha => sha, :obj => obj}
-
end
-
-
# Destructively clear the cache.
-
1
def reset!
-
@contents = {}
-
end
-
end
-
end
-
end
-
1
require 'set'
-
1
require 'digest/sha1'
-
1
require 'sass/cache_stores'
-
1
require 'sass/tree/node'
-
1
require 'sass/tree/root_node'
-
1
require 'sass/tree/rule_node'
-
1
require 'sass/tree/comment_node'
-
1
require 'sass/tree/prop_node'
-
1
require 'sass/tree/directive_node'
-
1
require 'sass/tree/media_node'
-
1
require 'sass/tree/supports_node'
-
1
require 'sass/tree/css_import_node'
-
1
require 'sass/tree/variable_node'
-
1
require 'sass/tree/mixin_def_node'
-
1
require 'sass/tree/mixin_node'
-
1
require 'sass/tree/trace_node'
-
1
require 'sass/tree/content_node'
-
1
require 'sass/tree/function_node'
-
1
require 'sass/tree/return_node'
-
1
require 'sass/tree/extend_node'
-
1
require 'sass/tree/if_node'
-
1
require 'sass/tree/while_node'
-
1
require 'sass/tree/for_node'
-
1
require 'sass/tree/each_node'
-
1
require 'sass/tree/debug_node'
-
1
require 'sass/tree/warn_node'
-
1
require 'sass/tree/import_node'
-
1
require 'sass/tree/charset_node'
-
1
require 'sass/tree/visitors/base'
-
1
require 'sass/tree/visitors/perform'
-
1
require 'sass/tree/visitors/cssize'
-
1
require 'sass/tree/visitors/extend'
-
1
require 'sass/tree/visitors/convert'
-
1
require 'sass/tree/visitors/to_css'
-
1
require 'sass/tree/visitors/deep_copy'
-
1
require 'sass/tree/visitors/set_options'
-
1
require 'sass/tree/visitors/check_nesting'
-
1
require 'sass/selector'
-
1
require 'sass/environment'
-
1
require 'sass/script'
-
1
require 'sass/scss'
-
1
require 'sass/error'
-
1
require 'sass/importers'
-
1
require 'sass/shared'
-
1
require 'sass/media'
-
1
require 'sass/supports'
-
-
1
module Sass
-
-
# A Sass mixin or function.
-
#
-
# `name`: `String`
-
# : The name of the mixin/function.
-
#
-
# `args`: `Array<(Script::Node, Script::Node)>`
-
# : The arguments for the mixin/function.
-
# Each element is a tuple containing the variable node of the argument
-
# and the parse tree for the default value of the argument.
-
#
-
# `splat`: `Script::Node?`
-
# : The variable node of the splat argument for this callable, or null.
-
#
-
# `environment`: {Sass::Environment}
-
# : The environment in which the mixin/function was defined.
-
# This is captured so that the mixin/function can have access
-
# to local variables defined in its scope.
-
#
-
# `tree`: `Array<Tree::Node>`
-
# : The parse tree for the mixin/function.
-
#
-
# `has_content`: `Boolean`
-
# : Whether the callable accepts a content block.
-
#
-
# `type`: `String`
-
# : The user-friendly name of the type of the callable.
-
1
Callable = Struct.new(:name, :args, :splat, :environment, :tree, :has_content, :type)
-
-
# This class handles the parsing and compilation of the Sass template.
-
# Example usage:
-
#
-
# template = File.load('stylesheets/sassy.sass')
-
# sass_engine = Sass::Engine.new(template)
-
# output = sass_engine.render
-
# puts output
-
1
class Engine
-
1
include Sass::Util
-
-
# A line of Sass code.
-
#
-
# `text`: `String`
-
# : The text in the line, without any whitespace at the beginning or end.
-
#
-
# `tabs`: `Fixnum`
-
# : The level of indentation of the line.
-
#
-
# `index`: `Fixnum`
-
# : The line number in the original document.
-
#
-
# `offset`: `Fixnum`
-
# : The number of bytes in on the line that the text begins.
-
# This ends up being the number of bytes of leading whitespace.
-
#
-
# `filename`: `String`
-
# : The name of the file in which this line appeared.
-
#
-
# `children`: `Array<Line>`
-
# : The lines nested below this one.
-
#
-
# `comment_tab_str`: `String?`
-
# : The prefix indentation for this comment, if it is a comment.
-
1
class Line < Struct.new(:text, :tabs, :index, :offset, :filename, :children, :comment_tab_str)
-
1
def comment?
-
text[0] == COMMENT_CHAR && (text[1] == SASS_COMMENT_CHAR || text[1] == CSS_COMMENT_CHAR)
-
end
-
end
-
-
# The character that begins a CSS property.
-
1
PROPERTY_CHAR = ?:
-
-
# The character that designates the beginning of a comment,
-
# either Sass or CSS.
-
1
COMMENT_CHAR = ?/
-
-
# The character that follows the general COMMENT_CHAR and designates a Sass comment,
-
# which is not output as a CSS comment.
-
1
SASS_COMMENT_CHAR = ?/
-
-
# The character that indicates that a comment allows interpolation
-
# and should be preserved even in `:compressed` mode.
-
1
SASS_LOUD_COMMENT_CHAR = ?!
-
-
# The character that follows the general COMMENT_CHAR and designates a CSS comment,
-
# which is embedded in the CSS document.
-
1
CSS_COMMENT_CHAR = ?*
-
-
# The character used to denote a compiler directive.
-
1
DIRECTIVE_CHAR = ?@
-
-
# Designates a non-parsed rule.
-
1
ESCAPE_CHAR = ?\\
-
-
# Designates block as mixin definition rather than CSS rules to output
-
1
MIXIN_DEFINITION_CHAR = ?=
-
-
# Includes named mixin declared using MIXIN_DEFINITION_CHAR
-
1
MIXIN_INCLUDE_CHAR = ?+
-
-
# The regex that matches and extracts data from
-
# properties of the form `:name prop`.
-
1
PROPERTY_OLD = /^:([^\s=:"]+)\s*(?:\s+|$)(.*)/
-
-
# The default options for Sass::Engine.
-
# @api public
-
1
DEFAULT_OPTIONS = {
-
:style => :nested,
-
:load_paths => ['.'],
-
:cache => true,
-
:cache_location => './.sass-cache',
-
:syntax => :sass,
-
:filesystem_importer => Sass::Importers::Filesystem
-
}.freeze
-
-
# Converts a Sass options hash into a standard form, filling in
-
# default values and resolving aliases.
-
#
-
# @param options [{Symbol => Object}] The options hash;
-
# see {file:SASS_REFERENCE.md#sass_options the Sass options documentation}
-
# @return [{Symbol => Object}] The normalized options hash.
-
# @private
-
1
def self.normalize_options(options)
-
options = DEFAULT_OPTIONS.merge(options.reject {|k, v| v.nil?})
-
-
# If the `:filename` option is passed in without an importer,
-
# assume it's using the default filesystem importer.
-
options[:importer] ||= options[:filesystem_importer].new(".") if options[:filename]
-
-
# Tracks the original filename of the top-level Sass file
-
options[:original_filename] ||= options[:filename]
-
-
options[:cache_store] ||= Sass::CacheStores::Chain.new(
-
Sass::CacheStores::Memory.new, Sass::CacheStores::Filesystem.new(options[:cache_location]))
-
# Support both, because the docs said one and the other actually worked
-
# for quite a long time.
-
options[:line_comments] ||= options[:line_numbers]
-
-
options[:load_paths] = (options[:load_paths] + Sass.load_paths).map do |p|
-
next p unless p.is_a?(String) || (defined?(Pathname) && p.is_a?(Pathname))
-
options[:filesystem_importer].new(p.to_s)
-
end
-
-
# Backwards compatibility
-
options[:property_syntax] ||= options[:attribute_syntax]
-
case options[:property_syntax]
-
when :alternate; options[:property_syntax] = :new
-
when :normal; options[:property_syntax] = :old
-
end
-
-
options
-
end
-
-
# Returns the {Sass::Engine} for the given file.
-
# This is preferable to Sass::Engine.new when reading from a file
-
# because it properly sets up the Engine's metadata,
-
# enables parse-tree caching,
-
# and infers the syntax from the filename.
-
#
-
# @param filename [String] The path to the Sass or SCSS file
-
# @param options [{Symbol => Object}] The options hash;
-
# See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
-
# @return [Sass::Engine] The Engine for the given Sass or SCSS file.
-
# @raise [Sass::SyntaxError] if there's an error in the document.
-
1
def self.for_file(filename, options)
-
had_syntax = options[:syntax]
-
-
if had_syntax
-
# Use what was explicitly specificed
-
elsif filename =~ /\.scss$/
-
options.merge!(:syntax => :scss)
-
elsif filename =~ /\.sass$/
-
options.merge!(:syntax => :sass)
-
end
-
-
Sass::Engine.new(File.read(filename), options.merge(:filename => filename))
-
end
-
-
# The options for the Sass engine.
-
# See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
-
#
-
# @return [{Symbol => Object}]
-
1
attr_reader :options
-
-
# Creates a new Engine. Note that Engine should only be used directly
-
# when compiling in-memory Sass code.
-
# If you're compiling a single Sass file from the filesystem,
-
# use \{Sass::Engine.for\_file}.
-
# If you're compiling multiple files from the filesystem,
-
# use {Sass::Plugin}.
-
#
-
# @param template [String] The Sass template.
-
# This template can be encoded using any encoding
-
# that can be converted to Unicode.
-
# If the template contains an `@charset` declaration,
-
# that overrides the Ruby encoding
-
# (see {file:SASS_REFERENCE.md#encodings the encoding documentation})
-
# @param options [{Symbol => Object}] An options hash.
-
# See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
-
# @see {Sass::Engine.for_file}
-
# @see {Sass::Plugin}
-
1
def initialize(template, options={})
-
@options = self.class.normalize_options(options)
-
@template = template
-
end
-
-
# Render the template to CSS.
-
#
-
# @return [String] The CSS
-
# @raise [Sass::SyntaxError] if there's an error in the document
-
# @raise [Encoding::UndefinedConversionError] if the source encoding
-
# cannot be converted to UTF-8
-
# @raise [ArgumentError] if the document uses an unknown encoding with `@charset`
-
1
def render
-
return _render unless @options[:quiet]
-
Sass::Util.silence_sass_warnings {_render}
-
end
-
1
alias_method :to_css, :render
-
-
# Parses the document into its parse tree. Memoized.
-
#
-
# @return [Sass::Tree::Node] The root of the parse tree.
-
# @raise [Sass::SyntaxError] if there's an error in the document
-
1
def to_tree
-
@tree ||= @options[:quiet] ?
-
Sass::Util.silence_sass_warnings {_to_tree} :
-
_to_tree
-
end
-
-
# Returns the original encoding of the document,
-
# or `nil` under Ruby 1.8.
-
#
-
# @return [Encoding, nil]
-
# @raise [Encoding::UndefinedConversionError] if the source encoding
-
# cannot be converted to UTF-8
-
# @raise [ArgumentError] if the document uses an unknown encoding with `@charset`
-
1
def source_encoding
-
check_encoding!
-
@original_encoding
-
end
-
-
# Gets a set of all the documents
-
# that are (transitive) dependencies of this document,
-
# not including the document itself.
-
#
-
# @return [[Sass::Engine]] The dependency documents.
-
1
def dependencies
-
_dependencies(Set.new, engines = Set.new)
-
Sass::Util.array_minus(engines, [self])
-
end
-
-
# Helper for \{#dependencies}.
-
#
-
# @private
-
1
def _dependencies(seen, engines)
-
return if seen.include?(key = [@options[:filename], @options[:importer]])
-
seen << key
-
engines << self
-
to_tree.grep(Tree::ImportNode) do |n|
-
next if n.css_import?
-
n.imported_file._dependencies(seen, engines)
-
end
-
end
-
-
1
private
-
-
1
def _render
-
rendered = _to_tree.render
-
return rendered if ruby1_8?
-
begin
-
# Try to convert the result to the original encoding,
-
# but if that doesn't work fall back on UTF-8
-
rendered = rendered.encode(source_encoding)
-
rescue EncodingError
-
end
-
rendered.gsub(Regexp.new('\A@charset "(.*?)"'.encode(source_encoding)),
-
"@charset \"#{source_encoding.name}\"".encode(source_encoding))
-
end
-
-
1
def _to_tree
-
if (@options[:cache] || @options[:read_cache]) &&
-
@options[:filename] && @options[:importer]
-
key = sassc_key
-
sha = Digest::SHA1.hexdigest(@template)
-
-
if root = @options[:cache_store].retrieve(key, sha)
-
root.options = @options
-
return root
-
end
-
end
-
-
check_encoding!
-
-
if @options[:syntax] == :scss
-
root = Sass::SCSS::Parser.new(@template, @options[:filename]).parse
-
else
-
root = Tree::RootNode.new(@template)
-
append_children(root, tree(tabulate(@template)).first, true)
-
end
-
-
root.options = @options
-
if @options[:cache] && key && sha
-
begin
-
old_options = root.options
-
root.options = {}
-
@options[:cache_store].store(key, sha, root)
-
ensure
-
root.options = old_options
-
end
-
end
-
root
-
rescue SyntaxError => e
-
e.modify_backtrace(:filename => @options[:filename], :line => @line)
-
e.sass_template = @template
-
raise e
-
end
-
-
1
def sassc_key
-
@options[:cache_store].key(*@options[:importer].key(@options[:filename], @options))
-
end
-
-
1
def check_encoding!
-
return if @checked_encoding
-
@checked_encoding = true
-
@template, @original_encoding = check_sass_encoding(@template) do |msg, line|
-
raise Sass::SyntaxError.new(msg, :line => line)
-
end
-
end
-
-
1
def tabulate(string)
-
tab_str = nil
-
comment_tab_str = nil
-
first = true
-
lines = []
-
string.gsub(/\r\n|\r|\n/, "\n").scan(/^[^\n]*?$/).each_with_index do |line, index|
-
index += (@options[:line] || 1)
-
if line.strip.empty?
-
lines.last.text << "\n" if lines.last && lines.last.comment?
-
next
-
end
-
-
line_tab_str = line[/^\s*/]
-
unless line_tab_str.empty?
-
if tab_str.nil?
-
comment_tab_str ||= line_tab_str
-
next if try_comment(line, lines.last, "", comment_tab_str, index)
-
comment_tab_str = nil
-
end
-
-
tab_str ||= line_tab_str
-
-
raise SyntaxError.new("Indenting at the beginning of the document is illegal.",
-
:line => index) if first
-
-
raise SyntaxError.new("Indentation can't use both tabs and spaces.",
-
:line => index) if tab_str.include?(?\s) && tab_str.include?(?\t)
-
end
-
first &&= !tab_str.nil?
-
if tab_str.nil?
-
lines << Line.new(line.strip, 0, index, 0, @options[:filename], [])
-
next
-
end
-
-
comment_tab_str ||= line_tab_str
-
if try_comment(line, lines.last, tab_str * lines.last.tabs, comment_tab_str, index)
-
next
-
else
-
comment_tab_str = nil
-
end
-
-
line_tabs = line_tab_str.scan(tab_str).size
-
if tab_str * line_tabs != line_tab_str
-
message = <<END.strip.gsub("\n", ' ')
-
Inconsistent indentation: #{Sass::Shared.human_indentation line_tab_str, true} used for indentation,
-
but the rest of the document was indented using #{Sass::Shared.human_indentation tab_str}.
-
END
-
raise SyntaxError.new(message, :line => index)
-
end
-
-
lines << Line.new(line.strip, line_tabs, index, tab_str.size, @options[:filename], [])
-
end
-
lines
-
end
-
-
1
def try_comment(line, last, tab_str, comment_tab_str, index)
-
return unless last && last.comment?
-
# Nested comment stuff must be at least one whitespace char deeper
-
# than the normal indentation
-
return unless line =~ /^#{tab_str}\s/
-
unless line =~ /^(?:#{comment_tab_str})(.*)$/
-
raise SyntaxError.new(<<MSG.strip.gsub("\n", " "), :line => index)
-
Inconsistent indentation:
-
previous line was indented by #{Sass::Shared.human_indentation comment_tab_str},
-
but this line was indented by #{Sass::Shared.human_indentation line[/^\s*/]}.
-
MSG
-
end
-
-
last.comment_tab_str ||= comment_tab_str
-
last.text << "\n" << line
-
true
-
end
-
-
1
def tree(arr, i = 0)
-
return [], i if arr[i].nil?
-
-
base = arr[i].tabs
-
nodes = []
-
while (line = arr[i]) && line.tabs >= base
-
if line.tabs > base
-
raise SyntaxError.new("The line was indented #{line.tabs - base} levels deeper than the previous line.",
-
:line => line.index) if line.tabs > base + 1
-
-
nodes.last.children, i = tree(arr, i)
-
else
-
nodes << line
-
i += 1
-
end
-
end
-
return nodes, i
-
end
-
-
1
def build_tree(parent, line, root = false)
-
@line = line.index
-
node_or_nodes = parse_line(parent, line, root)
-
-
Array(node_or_nodes).each do |node|
-
# Node is a symbol if it's non-outputting, like a variable assignment
-
next unless node.is_a? Tree::Node
-
-
node.line = line.index
-
node.filename = line.filename
-
-
append_children(node, line.children, false)
-
end
-
-
node_or_nodes
-
end
-
-
1
def append_children(parent, children, root)
-
continued_rule = nil
-
continued_comment = nil
-
children.each do |line|
-
child = build_tree(parent, line, root)
-
-
if child.is_a?(Tree::RuleNode)
-
if child.continued? && child.children.empty?
-
if continued_rule
-
continued_rule.add_rules child
-
else
-
continued_rule = child
-
end
-
next
-
elsif continued_rule
-
continued_rule.add_rules child
-
continued_rule.children = child.children
-
continued_rule, child = nil, continued_rule
-
end
-
elsif continued_rule
-
continued_rule = nil
-
end
-
-
if child.is_a?(Tree::CommentNode) && child.type == :silent
-
if continued_comment &&
-
child.line == continued_comment.line +
-
continued_comment.lines + 1
-
continued_comment.value += ["\n"] + child.value
-
next
-
end
-
-
continued_comment = child
-
end
-
-
check_for_no_children(child)
-
validate_and_append_child(parent, child, line, root)
-
end
-
-
parent
-
end
-
-
1
def validate_and_append_child(parent, child, line, root)
-
case child
-
when Array
-
child.each {|c| validate_and_append_child(parent, c, line, root)}
-
when Tree::Node
-
parent << child
-
end
-
end
-
-
1
def check_for_no_children(node)
-
return unless node.is_a?(Tree::RuleNode) && node.children.empty?
-
Sass::Util.sass_warn(<<WARNING.strip)
-
WARNING on line #{node.line}#{" of #{node.filename}" if node.filename}:
-
This selector doesn't have any properties and will not be rendered.
-
WARNING
-
end
-
-
1
def parse_line(parent, line, root)
-
case line.text[0]
-
when PROPERTY_CHAR
-
if line.text[1] == PROPERTY_CHAR ||
-
(@options[:property_syntax] == :new &&
-
line.text =~ PROPERTY_OLD && $2.empty?)
-
# Support CSS3-style pseudo-elements,
-
# which begin with ::,
-
# as well as pseudo-classes
-
# if we're using the new property syntax
-
Tree::RuleNode.new(parse_interp(line.text))
-
else
-
name, value = line.text.scan(PROPERTY_OLD)[0]
-
raise SyntaxError.new("Invalid property: \"#{line.text}\".",
-
:line => @line) if name.nil? || value.nil?
-
parse_property(name, parse_interp(name), value, :old, line)
-
end
-
when ?$
-
parse_variable(line)
-
when COMMENT_CHAR
-
parse_comment(line)
-
when DIRECTIVE_CHAR
-
parse_directive(parent, line, root)
-
when ESCAPE_CHAR
-
Tree::RuleNode.new(parse_interp(line.text[1..-1]))
-
when MIXIN_DEFINITION_CHAR
-
parse_mixin_definition(line)
-
when MIXIN_INCLUDE_CHAR
-
if line.text[1].nil? || line.text[1] == ?\s
-
Tree::RuleNode.new(parse_interp(line.text))
-
else
-
parse_mixin_include(line, root)
-
end
-
else
-
parse_property_or_rule(line)
-
end
-
end
-
-
1
def parse_property_or_rule(line)
-
scanner = Sass::Util::MultibyteStringScanner.new(line.text)
-
hack_char = scanner.scan(/[:\*\.]|\#(?!\{)/)
-
parser = Sass::SCSS::Parser.new(scanner, @options[:filename], @line)
-
-
unless res = parser.parse_interp_ident
-
return Tree::RuleNode.new(parse_interp(line.text))
-
end
-
res.unshift(hack_char) if hack_char
-
if comment = scanner.scan(Sass::SCSS::RX::COMMENT)
-
res << comment
-
end
-
-
name = line.text[0...scanner.pos]
-
if scanner.scan(/\s*:(?:\s|$)/)
-
parse_property(name, res, scanner.rest, :new, line)
-
else
-
res.pop if comment
-
Tree::RuleNode.new(res + parse_interp(scanner.rest))
-
end
-
end
-
-
1
def parse_property(name, parsed_name, value, prop, line)
-
if value.strip.empty?
-
expr = Sass::Script::String.new("")
-
else
-
expr = parse_script(value, :offset => line.offset + line.text.index(value))
-
end
-
node = Tree::PropNode.new(parse_interp(name), expr, prop)
-
if value.strip.empty? && line.children.empty?
-
raise SyntaxError.new(
-
"Invalid property: \"#{node.declaration}\" (no value)." +
-
node.pseudo_class_selector_message)
-
end
-
-
node
-
end
-
-
1
def parse_variable(line)
-
name, value, default = line.text.scan(Script::MATCH)[0]
-
raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath variable declarations.",
-
:line => @line + 1) unless line.children.empty?
-
raise SyntaxError.new("Invalid variable: \"#{line.text}\".",
-
:line => @line) unless name && value
-
-
expr = parse_script(value, :offset => line.offset + line.text.index(value))
-
-
Tree::VariableNode.new(name, expr, default)
-
end
-
-
1
def parse_comment(line)
-
if line.text[1] == CSS_COMMENT_CHAR || line.text[1] == SASS_COMMENT_CHAR
-
silent = line.text[1] == SASS_COMMENT_CHAR
-
loud = !silent && line.text[2] == SASS_LOUD_COMMENT_CHAR
-
if silent
-
value = [line.text]
-
else
-
value = self.class.parse_interp(line.text, line.index, line.offset, :filename => @filename)
-
end
-
value = with_extracted_values(value) do |str|
-
str = str.gsub(/^#{line.comment_tab_str}/m, '')[2..-1] # get rid of // or /*
-
format_comment_text(str, silent)
-
end
-
type = if silent then :silent elsif loud then :loud else :normal end
-
Tree::CommentNode.new(value, type)
-
else
-
Tree::RuleNode.new(parse_interp(line.text))
-
end
-
end
-
-
1
def parse_directive(parent, line, root)
-
directive, whitespace, value = line.text[1..-1].split(/(\s+)/, 2)
-
offset = directive.size + whitespace.size + 1 if whitespace
-
-
# If value begins with url( or ",
-
# it's a CSS @import rule and we don't want to touch it.
-
case directive
-
when 'import'
-
parse_import(line, value, offset)
-
when 'mixin'
-
parse_mixin_definition(line)
-
when 'content'
-
parse_content_directive(line)
-
when 'include'
-
parse_mixin_include(line, root)
-
when 'function'
-
parse_function(line, root)
-
when 'for'
-
parse_for(line, root, value)
-
when 'each'
-
parse_each(line, root, value)
-
when 'else'
-
parse_else(parent, line, value)
-
when 'while'
-
raise SyntaxError.new("Invalid while directive '@while': expected expression.") unless value
-
Tree::WhileNode.new(parse_script(value, :offset => offset))
-
when 'if'
-
raise SyntaxError.new("Invalid if directive '@if': expected expression.") unless value
-
Tree::IfNode.new(parse_script(value, :offset => offset))
-
when 'debug'
-
raise SyntaxError.new("Invalid debug directive '@debug': expected expression.") unless value
-
raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath debug directives.",
-
:line => @line + 1) unless line.children.empty?
-
offset = line.offset + line.text.index(value).to_i
-
Tree::DebugNode.new(parse_script(value, :offset => offset))
-
when 'extend'
-
raise SyntaxError.new("Invalid extend directive '@extend': expected expression.") unless value
-
raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath extend directives.",
-
:line => @line + 1) unless line.children.empty?
-
optional = !!value.gsub!(/\s+#{Sass::SCSS::RX::OPTIONAL}$/, '')
-
offset = line.offset + line.text.index(value).to_i
-
Tree::ExtendNode.new(parse_interp(value, offset), optional)
-
when 'warn'
-
raise SyntaxError.new("Invalid warn directive '@warn': expected expression.") unless value
-
raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath warn directives.",
-
:line => @line + 1) unless line.children.empty?
-
offset = line.offset + line.text.index(value).to_i
-
Tree::WarnNode.new(parse_script(value, :offset => offset))
-
when 'return'
-
raise SyntaxError.new("Invalid @return: expected expression.") unless value
-
raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath return directives.",
-
:line => @line + 1) unless line.children.empty?
-
offset = line.offset + line.text.index(value).to_i
-
Tree::ReturnNode.new(parse_script(value, :offset => offset))
-
when 'charset'
-
name = value && value[/\A(["'])(.*)\1\Z/, 2] #"
-
raise SyntaxError.new("Invalid charset directive '@charset': expected string.") unless name
-
raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath charset directives.",
-
:line => @line + 1) unless line.children.empty?
-
Tree::CharsetNode.new(name)
-
when 'media'
-
parser = Sass::SCSS::Parser.new(value, @options[:filename], @line)
-
Tree::MediaNode.new(parser.parse_media_query_list.to_a)
-
when nil
-
raise SyntaxError.new("Invalid directive: '@'.")
-
else
-
unprefixed_directive = directive.gsub(/^-[a-z0-9]+-/i, '')
-
if unprefixed_directive == 'supports'
-
parser = Sass::SCSS::Parser.new(value, @options[:filename], @line)
-
return Tree::SupportsNode.new(directive, parser.parse_supports_condition)
-
end
-
-
Tree::DirectiveNode.new(
-
value.nil? ? ["@#{directive}"] : ["@#{directive} "] + parse_interp(value, offset))
-
end
-
end
-
-
1
def parse_for(line, root, text)
-
var, from_expr, to_name, to_expr = text.scan(/^([^\s]+)\s+from\s+(.+)\s+(to|through)\s+(.+)$/).first
-
-
if var.nil? # scan failed, try to figure out why for error message
-
if text !~ /^[^\s]+/
-
expected = "variable name"
-
elsif text !~ /^[^\s]+\s+from\s+.+/
-
expected = "'from <expr>'"
-
else
-
expected = "'to <expr>' or 'through <expr>'"
-
end
-
raise SyntaxError.new("Invalid for directive '@for #{text}': expected #{expected}.")
-
end
-
raise SyntaxError.new("Invalid variable \"#{var}\".") unless var =~ Script::VALIDATE
-
-
var = var[1..-1]
-
parsed_from = parse_script(from_expr, :offset => line.offset + line.text.index(from_expr))
-
parsed_to = parse_script(to_expr, :offset => line.offset + line.text.index(to_expr))
-
Tree::ForNode.new(var, parsed_from, parsed_to, to_name == 'to')
-
end
-
-
1
def parse_each(line, root, text)
-
var, list_expr = text.scan(/^([^\s]+)\s+in\s+(.+)$/).first
-
-
if var.nil? # scan failed, try to figure out why for error message
-
if text !~ /^[^\s]+/
-
expected = "variable name"
-
elsif text !~ /^[^\s]+\s+from\s+.+/
-
expected = "'in <expr>'"
-
end
-
raise SyntaxError.new("Invalid for directive '@each #{text}': expected #{expected}.")
-
end
-
raise SyntaxError.new("Invalid variable \"#{var}\".") unless var =~ Script::VALIDATE
-
-
var = var[1..-1]
-
parsed_list = parse_script(list_expr, :offset => line.offset + line.text.index(list_expr))
-
Tree::EachNode.new(var, parsed_list)
-
end
-
-
1
def parse_else(parent, line, text)
-
previous = parent.children.last
-
raise SyntaxError.new("@else must come after @if.") unless previous.is_a?(Tree::IfNode)
-
-
if text
-
if text !~ /^if\s+(.+)/
-
raise SyntaxError.new("Invalid else directive '@else #{text}': expected 'if <expr>'.")
-
end
-
expr = parse_script($1, :offset => line.offset + line.text.index($1))
-
end
-
-
node = Tree::IfNode.new(expr)
-
append_children(node, line.children, false)
-
previous.add_else node
-
nil
-
end
-
-
1
def parse_import(line, value, offset)
-
raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath import directives.",
-
:line => @line + 1) unless line.children.empty?
-
-
scanner = Sass::Util::MultibyteStringScanner.new(value)
-
values = []
-
-
loop do
-
unless node = parse_import_arg(scanner, offset + scanner.pos)
-
raise SyntaxError.new("Invalid @import: expected file to import, was #{scanner.rest.inspect}",
-
:line => @line)
-
end
-
values << node
-
break unless scanner.scan(/,\s*/)
-
end
-
-
if scanner.scan(/;/)
-
raise SyntaxError.new("Invalid @import: expected end of line, was \";\".",
-
:line => @line)
-
end
-
-
return values
-
end
-
-
1
def parse_import_arg(scanner, offset)
-
return if scanner.eos?
-
-
if scanner.match?(/url\(/i)
-
script_parser = Sass::Script::Parser.new(scanner, @line, offset, @options)
-
str = script_parser.parse_string
-
media_parser = Sass::SCSS::Parser.new(scanner, @options[:filename], @line)
-
media = media_parser.parse_media_query_list
-
return Tree::CssImportNode.new(str, media.to_a)
-
end
-
-
unless str = scanner.scan(Sass::SCSS::RX::STRING)
-
return Tree::ImportNode.new(scanner.scan(/[^,;]+/))
-
end
-
-
val = scanner[1] || scanner[2]
-
scanner.scan(/\s*/)
-
if !scanner.match?(/[,;]|$/)
-
media_parser = Sass::SCSS::Parser.new(scanner, @options[:filename], @line)
-
media = media_parser.parse_media_query_list
-
Tree::CssImportNode.new(str || uri, media.to_a)
-
elsif val =~ /^(https?:)?\/\//
-
Tree::CssImportNode.new("url(#{val})")
-
else
-
Tree::ImportNode.new(val)
-
end
-
end
-
-
1
MIXIN_DEF_RE = /^(?:=|@mixin)\s*(#{Sass::SCSS::RX::IDENT})(.*)$/
-
1
def parse_mixin_definition(line)
-
name, arg_string = line.text.scan(MIXIN_DEF_RE).first
-
raise SyntaxError.new("Invalid mixin \"#{line.text[1..-1]}\".") if name.nil?
-
-
offset = line.offset + line.text.size - arg_string.size
-
args, splat = Script::Parser.new(arg_string.strip, @line, offset, @options).
-
parse_mixin_definition_arglist
-
Tree::MixinDefNode.new(name, args, splat)
-
end
-
-
1
CONTENT_RE = /^@content\s*(.+)?$/
-
1
def parse_content_directive(line)
-
trailing = line.text.scan(CONTENT_RE).first.first
-
raise SyntaxError.new("Invalid content directive. Trailing characters found: \"#{trailing}\".") unless trailing.nil?
-
raise SyntaxError.new("Illegal nesting: Nothing may be nested beneath @content directives.",
-
:line => line.index + 1) unless line.children.empty?
-
Tree::ContentNode.new
-
end
-
-
1
MIXIN_INCLUDE_RE = /^(?:\+|@include)\s*(#{Sass::SCSS::RX::IDENT})(.*)$/
-
1
def parse_mixin_include(line, root)
-
name, arg_string = line.text.scan(MIXIN_INCLUDE_RE).first
-
raise SyntaxError.new("Invalid mixin include \"#{line.text}\".") if name.nil?
-
-
offset = line.offset + line.text.size - arg_string.size
-
args, keywords, splat = Script::Parser.new(arg_string.strip, @line, offset, @options).
-
parse_mixin_include_arglist
-
Tree::MixinNode.new(name, args, keywords, splat)
-
end
-
-
1
FUNCTION_RE = /^@function\s*(#{Sass::SCSS::RX::IDENT})(.*)$/
-
1
def parse_function(line, root)
-
name, arg_string = line.text.scan(FUNCTION_RE).first
-
raise SyntaxError.new("Invalid function definition \"#{line.text}\".") if name.nil?
-
-
offset = line.offset + line.text.size - arg_string.size
-
args, splat = Script::Parser.new(arg_string.strip, @line, offset, @options).
-
parse_function_definition_arglist
-
Tree::FunctionNode.new(name, args, splat)
-
end
-
-
1
def parse_script(script, options = {})
-
line = options[:line] || @line
-
offset = options[:offset] || 0
-
Script.parse(script, line, offset, @options)
-
end
-
-
1
def format_comment_text(text, silent)
-
content = text.split("\n")
-
-
if content.first && content.first.strip.empty?
-
removed_first = true
-
content.shift
-
end
-
-
return silent ? "//" : "/* */" if content.empty?
-
content.last.gsub!(%r{ ?\*/ *$}, '')
-
content.map! {|l| l.gsub!(/^\*( ?)/, '\1') || (l.empty? ? "" : " ") + l}
-
content.first.gsub!(/^ /, '') unless removed_first
-
if silent
-
"//" + content.join("\n//")
-
else
-
# The #gsub fixes the case of a trailing */
-
"/*" + content.join("\n *").gsub(/ \*\Z/, '') + " */"
-
end
-
end
-
-
1
def parse_interp(text, offset = 0)
-
self.class.parse_interp(text, @line, offset, :filename => @filename)
-
end
-
-
# It's important that this have strings (at least)
-
# at the beginning, the end, and between each Script::Node.
-
#
-
# @private
-
1
def self.parse_interp(text, line, offset, options)
-
res = []
-
rest = Sass::Shared.handle_interpolation text do |scan|
-
escapes = scan[2].size
-
res << scan.matched[0...-2 - escapes]
-
if escapes % 2 == 1
-
res << "\\" * (escapes - 1) << '#{'
-
else
-
res << "\\" * [0, escapes - 1].max
-
res << Script::Parser.new(
-
scan, line, offset + scan.pos - scan.matched_size, options).
-
parse_interpolated
-
end
-
end
-
res << rest
-
end
-
end
-
end
-
1
require 'set'
-
-
1
module Sass
-
# The lexical environment for SassScript.
-
# This keeps track of variable, mixin, and function definitions.
-
#
-
# A new environment is created for each level of Sass nesting.
-
# This allows variables to be lexically scoped.
-
# The new environment refers to the environment in the upper scope,
-
# so it has access to variables defined in enclosing scopes,
-
# but new variables are defined locally.
-
#
-
# Environment also keeps track of the {Engine} options
-
# so that they can be made available to {Sass::Script::Functions}.
-
1
class Environment
-
# The enclosing environment,
-
# or nil if this is the global environment.
-
#
-
# @return [Environment]
-
1
attr_reader :parent
-
1
attr_reader :options
-
1
attr_writer :caller
-
1
attr_writer :content
-
-
# @param options [{Symbol => Object}] The options hash. See
-
# {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
-
# @param parent [Environment] See \{#parent}
-
1
def initialize(parent = nil, options = nil)
-
@parent = parent
-
@options = options || (parent && parent.options) || {}
-
end
-
-
# The environment of the caller of this environment's mixin or function.
-
# @return {Environment?}
-
1
def caller
-
@caller || (@parent && @parent.caller)
-
end
-
-
# The content passed to this environmnet. This is naturally only set
-
# for mixin body environments with content passed in.
-
# @return {Environment?}
-
1
def content
-
@content || (@parent && @parent.content)
-
end
-
-
1
private
-
-
1
class << self
-
1
private
-
1
UNDERSCORE, DASH = '_', '-'
-
-
# Note: when updating this,
-
# update sass/yard/inherited_hash.rb as well.
-
1
def inherited_hash(name)
-
3
class_eval <<RUBY, __FILE__, __LINE__ + 1
-
def #{name}(name)
-
_#{name}(name.tr(UNDERSCORE, DASH))
-
end
-
-
def _#{name}(name)
-
(@#{name}s && @#{name}s[name]) || @parent && @parent._#{name}(name)
-
end
-
protected :_#{name}
-
-
def set_#{name}(name, value)
-
name = name.tr(UNDERSCORE, DASH)
-
@#{name}s[name] = value unless try_set_#{name}(name, value)
-
end
-
-
def try_set_#{name}(name, value)
-
@#{name}s ||= {}
-
if @#{name}s.include?(name)
-
@#{name}s[name] = value
-
true
-
elsif @parent
-
@parent.try_set_#{name}(name, value)
-
else
-
false
-
end
-
end
-
protected :try_set_#{name}
-
-
def set_local_#{name}(name, value)
-
@#{name}s ||= {}
-
@#{name}s[name.tr(UNDERSCORE, DASH)] = value
-
end
-
RUBY
-
end
-
end
-
-
# variable
-
# Script::Literal
-
1
inherited_hash :var
-
# mixin
-
# Sass::Callable
-
1
inherited_hash :mixin
-
# function
-
# Sass::Callable
-
1
inherited_hash :function
-
end
-
end
-
1
module Sass
-
# An exception class that keeps track of
-
# the line of the Sass template it was raised on
-
# and the Sass file that was being parsed (if applicable).
-
#
-
# All Sass errors are raised as {Sass::SyntaxError}s.
-
#
-
# When dealing with SyntaxErrors,
-
# it's important to provide filename and line number information.
-
# This will be used in various error reports to users, including backtraces;
-
# see \{#sass\_backtrace} for details.
-
#
-
# Some of this information is usually provided as part of the constructor.
-
# New backtrace entries can be added with \{#add\_backtrace},
-
# which is called when an exception is raised between files (e.g. with `@import`).
-
#
-
# Often, a chunk of code will all have similar backtrace information -
-
# the same filename or even line.
-
# It may also be useful to have a default line number set.
-
# In those situations, the default values can be used
-
# by omitting the information on the original exception,
-
# and then calling \{#modify\_backtrace} in a wrapper `rescue`.
-
# When doing this, be sure that all exceptions ultimately end up
-
# with the information filled in.
-
1
class SyntaxError < StandardError
-
# The backtrace of the error within Sass files.
-
# This is an array of hashes containing information for a single entry.
-
# The hashes have the following keys:
-
#
-
# `:filename`
-
# : The name of the file in which the exception was raised,
-
# or `nil` if no filename is available.
-
#
-
# `:mixin`
-
# : The name of the mixin in which the exception was raised,
-
# or `nil` if it wasn't raised in a mixin.
-
#
-
# `:line`
-
# : The line of the file on which the error occurred. Never nil.
-
#
-
# This information is also included in standard backtrace format
-
# in the output of \{#backtrace}.
-
#
-
# @return [Aray<{Symbol => Object>}]
-
1
attr_accessor :sass_backtrace
-
-
# The text of the template where this error was raised.
-
#
-
# @return [String]
-
1
attr_accessor :sass_template
-
-
# @param msg [String] The error message
-
# @param attrs [{Symbol => Object}] The information in the backtrace entry.
-
# See \{#sass\_backtrace}
-
1
def initialize(msg, attrs = {})
-
@message = msg
-
@sass_backtrace = []
-
add_backtrace(attrs)
-
end
-
-
# The name of the file in which the exception was raised.
-
# This could be `nil` if no filename is available.
-
#
-
# @return [String, nil]
-
1
def sass_filename
-
sass_backtrace.first[:filename]
-
end
-
-
# The name of the mixin in which the error occurred.
-
# This could be `nil` if the error occurred outside a mixin.
-
#
-
# @return [Fixnum]
-
1
def sass_mixin
-
sass_backtrace.first[:mixin]
-
end
-
-
# The line of the Sass template on which the error occurred.
-
#
-
# @return [Fixnum]
-
1
def sass_line
-
sass_backtrace.first[:line]
-
end
-
-
# Adds an entry to the exception's Sass backtrace.
-
#
-
# @param attrs [{Symbol => Object}] The information in the backtrace entry.
-
# See \{#sass\_backtrace}
-
1
def add_backtrace(attrs)
-
sass_backtrace << attrs.reject {|k, v| v.nil?}
-
end
-
-
# Modify the top Sass backtrace entries
-
# (that is, the most deeply nested ones)
-
# to have the given attributes.
-
#
-
# Specifically, this goes through the backtrace entries
-
# from most deeply nested to least,
-
# setting the given attributes for each entry.
-
# If an entry already has one of the given attributes set,
-
# the pre-existing attribute takes precedence
-
# and is not used for less deeply-nested entries
-
# (even if they don't have that attribute set).
-
#
-
# @param attrs [{Symbol => Object}] The information to add to the backtrace entry.
-
# See \{#sass\_backtrace}
-
1
def modify_backtrace(attrs)
-
attrs = attrs.reject {|k, v| v.nil?}
-
# Move backwards through the backtrace
-
(0...sass_backtrace.size).to_a.reverse.each do |i|
-
entry = sass_backtrace[i]
-
sass_backtrace[i] = attrs.merge(entry)
-
attrs.reject! {|k, v| entry.include?(k)}
-
break if attrs.empty?
-
end
-
end
-
-
# @return [String] The error message
-
1
def to_s
-
@message
-
end
-
-
# Returns the standard exception backtrace,
-
# including the Sass backtrace.
-
#
-
# @return [Array<String>]
-
1
def backtrace
-
return nil if super.nil?
-
return super if sass_backtrace.all? {|h| h.empty?}
-
sass_backtrace.map do |h|
-
"#{h[:filename] || "(sass)"}:#{h[:line]}" +
-
(h[:mixin] ? ":in `#{h[:mixin]}'" : "")
-
end + super
-
end
-
-
# Returns a string representation of the Sass backtrace.
-
#
-
# @param default_filename [String] The filename to use for unknown files
-
# @see #sass_backtrace
-
# @return [String]
-
1
def sass_backtrace_str(default_filename = "an unknown file")
-
lines = self.message.split("\n")
-
msg = lines[0] + lines[1..-1].
-
map {|l| "\n" + (" " * "Syntax error: ".size) + l}.join
-
"Syntax error: #{msg}" +
-
Sass::Util.enum_with_index(sass_backtrace).map do |entry, i|
-
"\n #{i == 0 ? "on" : "from"} line #{entry[:line]}" +
-
" of #{entry[:filename] || default_filename}" +
-
(entry[:mixin] ? ", in `#{entry[:mixin]}'" : "")
-
end.join
-
end
-
-
1
class << self
-
# Returns an error report for an exception in CSS format.
-
#
-
# @param e [Exception]
-
# @param options [{Symbol => Object}] The options passed to {Sass::Engine#initialize}
-
# @return [String] The error report
-
# @raise [Exception] `e`, if the
-
# {file:SASS_REFERENCE.md#full_exception-option `:full_exception`} option
-
# is set to false.
-
1
def exception_to_css(e, options)
-
raise e unless options[:full_exception]
-
-
header = header_string(e, options)
-
-
<<END
-
/*
-
#{header.gsub("*/", "*\\/")}
-
-
Backtrace:\n#{e.backtrace.join("\n").gsub("*/", "*\\/")}
-
*/
-
body:before {
-
white-space: pre;
-
font-family: monospace;
-
content: "#{header.gsub('"', '\"').gsub("\n", '\\A ')}"; }
-
END
-
end
-
-
1
private
-
-
1
def header_string(e, options)
-
unless e.is_a?(Sass::SyntaxError) && e.sass_line && e.sass_template
-
return "#{e.class}: #{e.message}"
-
end
-
-
line_offset = options[:line] || 1
-
line_num = e.sass_line + 1 - line_offset
-
min = [line_num - 6, 0].max
-
section = e.sass_template.rstrip.split("\n")[min ... line_num + 5]
-
return e.sass_backtrace_str if section.nil? || section.empty?
-
-
e.sass_backtrace_str + "\n\n" + Sass::Util.enum_with_index(section).
-
map {|line, i| "#{line_offset + min + i}: #{line}"}.join("\n")
-
end
-
end
-
end
-
-
# The class for Sass errors that are raised due to invalid unit conversions
-
# in SassScript.
-
1
class UnitConversionError < SyntaxError; end
-
end
-
1
module Sass
-
# Sass importers are in charge of taking paths passed to `@import`
-
# and finding the appropriate Sass code for those paths.
-
# By default, this code is always loaded from the filesystem,
-
# but importers could be added to load from a database or over HTTP.
-
#
-
# Each importer is in charge of a single load path
-
# (or whatever the corresponding notion is for the backend).
-
# Importers can be placed in the {file:SASS_REFERENCE.md#load_paths-option `:load_paths` array}
-
# alongside normal filesystem paths.
-
#
-
# When resolving an `@import`, Sass will go through the load paths
-
# looking for an importer that successfully imports the path.
-
# Once one is found, the imported file is used.
-
#
-
# User-created importers must inherit from {Importers::Base}.
-
1
module Importers
-
end
-
end
-
-
1
require 'sass/importers/base'
-
1
require 'sass/importers/filesystem'
-
1
module Sass
-
1
module Importers
-
# The abstract base class for Sass importers.
-
# All importers should inherit from this.
-
#
-
# At the most basic level, an importer is given a string
-
# and must return a {Sass::Engine} containing some Sass code.
-
# This string can be interpreted however the importer wants;
-
# however, subclasses are encouraged to use the URI format
-
# for pathnames.
-
#
-
# Importers that have some notion of "relative imports"
-
# should take a single load path in their constructor,
-
# and interpret paths as relative to that.
-
# They should also implement the \{#find\_relative} method.
-
#
-
# Importers should be serializable via `Marshal.dump`.
-
# In addition to the standard `_dump` and `_load` methods,
-
# importers can define `_before_dump`, `_after_dump`, `_around_dump`,
-
# and `_after_load` methods as per {Sass::Util#dump} and {Sass::Util#load}.
-
#
-
# @abstract
-
1
class Base
-
-
# Find a Sass file relative to another file.
-
# Importers without a notion of "relative paths"
-
# should just return nil here.
-
#
-
# If the importer does have a notion of "relative paths",
-
# it should ignore its load path during this method.
-
#
-
# See \{#find} for important information on how this method should behave.
-
#
-
# The `:filename` option passed to the returned {Sass::Engine}
-
# should be of a format that could be passed to \{#find}.
-
#
-
# @param uri [String] The URI to import. This is not necessarily relative,
-
# but this method should only return true if it is.
-
# @param base [String] The base filename. If `uri` is relative,
-
# it should be interpreted as relative to `base`.
-
# `base` is guaranteed to be in a format importable by this importer.
-
# @param options [{Symbol => Object}] Options for the Sass file
-
# containing the `@import` that's currently being resolved.
-
# @return [Sass::Engine, nil] An Engine containing the imported file,
-
# or nil if it couldn't be found or was in the wrong format.
-
1
def find_relative(uri, base, options)
-
Sass::Util.abstract(self)
-
end
-
-
# Find a Sass file, if it exists.
-
#
-
# This is the primary entry point of the Importer.
-
# It corresponds directly to an `@import` statement in Sass.
-
# It should do three basic things:
-
#
-
# * Determine if the URI is in this importer's format.
-
# If not, return nil.
-
# * Determine if the file indicated by the URI actually exists and is readable.
-
# If not, return nil.
-
# * Read the file and place the contents in a {Sass::Engine}.
-
# Return that engine.
-
#
-
# If this importer's format allows for file extensions,
-
# it should treat them the same way as the default {Filesystem} importer.
-
# If the URI explicitly has a `.sass` or `.scss` filename,
-
# the importer should look for that exact file
-
# and import it as the syntax indicated.
-
# If it doesn't exist, the importer should return nil.
-
#
-
# If the URI doesn't have either of these extensions,
-
# the importer should look for files with the extensions.
-
# If no such files exist, it should return nil.
-
#
-
# The {Sass::Engine} to be returned should be passed `options`,
-
# with a few modifications. `:syntax` should be set appropriately,
-
# `:filename` should be set to `uri`,
-
# and `:importer` should be set to this importer.
-
#
-
# @param uri [String] The URI to import.
-
# @param options [{Symbol => Object}] Options for the Sass file
-
# containing the `@import` that's currently being resolved.
-
# This is safe for subclasses to modify destructively.
-
# Callers should only pass in a value they don't mind being destructively modified.
-
# @return [Sass::Engine, nil] An Engine containing the imported file,
-
# or nil if it couldn't be found or was in the wrong format.
-
1
def find(uri, options)
-
Sass::Util.abstract(self)
-
end
-
-
# Returns the time the given Sass file was last modified.
-
#
-
# If the given file has been deleted or the time can't be accessed
-
# for some other reason, this should return nil.
-
#
-
# @param uri [String] The URI of the file to check.
-
# Comes from a `:filename` option set on an engine returned by this importer.
-
# @param options [{Symbol => Objet}] Options for the Sass file
-
# containing the `@import` currently being checked.
-
# @return [Time, nil]
-
1
def mtime(uri, options)
-
Sass::Util.abstract(self)
-
end
-
-
# Get the cache key pair for the given Sass URI.
-
# The URI need not be checked for validity.
-
#
-
# The only strict requirement is that the returned pair of strings
-
# uniquely identify the file at the given URI.
-
# However, the first component generally corresponds roughly to the directory,
-
# and the second to the basename, of the URI.
-
#
-
# Note that keys must be unique *across importers*.
-
# Thus it's probably a good idea to include the importer name
-
# at the beginning of the first component.
-
#
-
# @param uri [String] A URI known to be valid for this importer.
-
# @param options [{Symbol => Object}] Options for the Sass file
-
# containing the `@import` currently being checked.
-
# @return [(String, String)] The key pair which uniquely identifies
-
# the file at the given URI.
-
1
def key(uri, options)
-
Sass::Util.abstract(self)
-
end
-
-
# A string representation of the importer.
-
# Should be overridden by subclasses.
-
#
-
# This is used to help debugging,
-
# and should usually just show the load path encapsulated by this importer.
-
#
-
# @return [String]
-
1
def to_s
-
Sass::Util.abstract(self)
-
end
-
end
-
end
-
end
-
-
-
1
require 'pathname'
-
1
require 'set'
-
-
1
module Sass
-
1
module Importers
-
# The default importer, used for any strings found in the load path.
-
# Simply loads Sass files from the filesystem using the default logic.
-
1
class Filesystem < Base
-
-
1
attr_accessor :root
-
-
# Creates a new filesystem importer that imports files relative to a given path.
-
#
-
# @param root [String] The root path.
-
# This importer will import files relative to this path.
-
1
def initialize(root)
-
@root = File.expand_path(root)
-
@same_name_warnings = Set.new
-
end
-
-
# @see Base#find_relative
-
1
def find_relative(name, base, options)
-
_find(File.dirname(base), name, options)
-
end
-
-
# @see Base#find
-
1
def find(name, options)
-
_find(@root, name, options)
-
end
-
-
# @see Base#mtime
-
1
def mtime(name, options)
-
file, _ = Sass::Util.destructure(find_real_file(@root, name, options))
-
File.mtime(file) if file
-
rescue Errno::ENOENT
-
nil
-
end
-
-
# @see Base#key
-
1
def key(name, options)
-
[self.class.name + ":" + File.dirname(File.expand_path(name)),
-
File.basename(name)]
-
end
-
-
# @see Base#to_s
-
1
def to_s
-
@root
-
end
-
-
1
def hash
-
@root.hash
-
end
-
-
1
def eql?(other)
-
root.eql?(other.root)
-
end
-
-
1
protected
-
-
# If a full uri is passed, this removes the root from it
-
# otherwise returns the name unchanged
-
1
def remove_root(name)
-
if name.index(@root + "/") == 0
-
name[(@root.length + 1)..-1]
-
else
-
name
-
end
-
end
-
-
# A hash from file extensions to the syntaxes for those extensions.
-
# The syntaxes must be `:sass` or `:scss`.
-
#
-
# This can be overridden by subclasses that want normal filesystem importing
-
# with unusual extensions.
-
#
-
# @return [{String => Symbol}]
-
1
def extensions
-
{'sass' => :sass, 'scss' => :scss}
-
end
-
-
# Given an `@import`ed path, returns an array of possible
-
# on-disk filenames and their corresponding syntaxes for that path.
-
#
-
# @param name [String] The filename.
-
# @return [Array(String, Symbol)] An array of pairs.
-
# The first element of each pair is a filename to look for;
-
# the second element is the syntax that file would be in (`:sass` or `:scss`).
-
1
def possible_files(name)
-
name = escape_glob_characters(name)
-
dirname, basename, extname = split(name)
-
sorted_exts = extensions.sort
-
syntax = extensions[extname]
-
-
if syntax
-
ret = [["#{dirname}/{_,}#{basename}.#{extensions.invert[syntax]}", syntax]]
-
else
-
ret = sorted_exts.map {|ext, syn| ["#{dirname}/{_,}#{basename}.#{ext}", syn]}
-
end
-
-
# JRuby chokes when trying to import files from JARs when the path starts with './'.
-
ret.map {|f, s| [f.sub(%r{^\./}, ''), s]}
-
end
-
-
1
def escape_glob_characters(name)
-
name.gsub(/[\*\[\]\{\}\?]/) do |char|
-
"\\#{char}"
-
end
-
end
-
-
1
REDUNDANT_DIRECTORY = %r{#{Regexp.escape(File::SEPARATOR)}\.#{Regexp.escape(File::SEPARATOR)}}
-
# Given a base directory and an `@import`ed name,
-
# finds an existant file that matches the name.
-
#
-
# @param dir [String] The directory relative to which to search.
-
# @param name [String] The filename to search for.
-
# @return [(String, Symbol)] A filename-syntax pair.
-
1
def find_real_file(dir, name, options)
-
# on windows 'dir' can be in native File::ALT_SEPARATOR form
-
dir = dir.gsub(File::ALT_SEPARATOR, File::SEPARATOR) unless File::ALT_SEPARATOR.nil?
-
-
found = possible_files(remove_root(name)).map do |f, s|
-
path = (dir == "." || Pathname.new(f).absolute?) ? f : "#{escape_glob_characters(dir)}/#{f}"
-
Dir[path].map do |full_path|
-
full_path.gsub!(REDUNDANT_DIRECTORY, File::SEPARATOR)
-
[Pathname.new(full_path).cleanpath.to_s, s]
-
end
-
end
-
found = Sass::Util.flatten(found, 1)
-
return if found.empty?
-
-
if found.size > 1 && !@same_name_warnings.include?(found.first.first)
-
found.each {|(f, _)| @same_name_warnings << f}
-
relative_to = Pathname.new(dir)
-
if options[:_line]
-
# If _line exists, we're here due to an actual import in an
-
# import_node and we want to print a warning for a user writing an
-
# ambiguous import.
-
candidates = found.map {|(f, _)| " " + Pathname.new(f).relative_path_from(relative_to).to_s}.join("\n")
-
Sass::Util.sass_warn <<WARNING
-
WARNING: On line #{options[:_line]}#{" of #{options[:filename]}" if options[:filename]}:
-
It's not clear which file to import for '@import "#{name}"'.
-
Candidates:
-
#{candidates}
-
For now I'll choose #{File.basename found.first.first}.
-
This will be an error in future versions of Sass.
-
WARNING
-
else
-
# Otherwise, we're here via StalenessChecker, and we want to print a
-
# warning for a user running `sass --watch` with two ambiguous files.
-
candidates = found.map {|(f, _)| " " + File.basename(f)}.join("\n")
-
Sass::Util.sass_warn <<WARNING
-
WARNING: In #{File.dirname(name)}:
-
There are multiple files that match the name "#{File.basename(name)}":
-
#{candidates}
-
WARNING
-
end
-
end
-
found.first
-
end
-
-
# Splits a filename into three parts, a directory part, a basename, and an extension
-
# Only the known extensions returned from the extensions method will be recognized as such.
-
1
def split(name)
-
extension = nil
-
dirname, basename = File.dirname(name), File.basename(name)
-
if basename =~ /^(.*)\.(#{extensions.keys.map{|e| Regexp.escape(e)}.join('|')})$/
-
basename = $1
-
extension = $2
-
end
-
[dirname, basename, extension]
-
end
-
-
1
private
-
-
1
def _find(dir, name, options)
-
full_filename, syntax = Sass::Util.destructure(find_real_file(dir, name, options))
-
return unless full_filename && File.readable?(full_filename)
-
-
options[:syntax] = syntax
-
options[:filename] = full_filename
-
options[:importer] = self
-
Sass::Engine.new(File.read(full_filename), options)
-
end
-
end
-
end
-
end
-
1
module Sass::Logger
-
-
end
-
-
1
require "sass/logger/log_level"
-
1
require "sass/logger/base"
-
-
1
module Sass
-
-
1
class << self
-
1
attr_accessor :logger
-
end
-
-
1
self.logger = Sass::Logger::Base.new
-
end
-
1
require 'sass/logger/log_level'
-
-
1
class Sass::Logger::Base
-
-
1
include Sass::Logger::LogLevel
-
-
1
attr_accessor :log_level
-
1
attr_accessor :disabled
-
-
1
log_level :trace
-
1
log_level :debug
-
1
log_level :info
-
1
log_level :warn
-
1
log_level :error
-
-
1
def initialize(log_level = :debug)
-
2
self.log_level = log_level
-
end
-
-
1
def logging_level?(level)
-
!disabled && self.class.log_level?(level, log_level)
-
end
-
-
1
def log(level, message)
-
self._log(level, message) if logging_level?(level)
-
end
-
-
1
def _log(level, message)
-
Kernel::warn(message)
-
end
-
-
end
-
1
module Sass
-
1
module Logger
-
1
module LogLevel
-
-
1
def self.included(base)
-
1
base.extend(ClassMethods)
-
end
-
-
1
module ClassMethods
-
1
def inherited(subclass)
-
1
subclass.log_levels = subclass.superclass.log_levels.dup
-
end
-
-
1
def log_levels
-
11
@log_levels ||= {}
-
end
-
-
1
def log_levels=(levels)
-
1
@log_levels = levels
-
end
-
-
1
def log_level?(level, min_level)
-
log_levels[level] >= log_levels[min_level]
-
end
-
-
1
def log_level(name, options = {})
-
5
if options[:prepend]
-
level = log_levels.values.min
-
level = level.nil? ? 0 : level - 1
-
else
-
5
level = log_levels.values.max
-
5
level = level.nil? ? 0 : level + 1
-
end
-
5
log_levels.update(name => level)
-
5
define_logger(name)
-
end
-
-
1
def define_logger(name, options = {})
-
5
class_eval <<-RUBY, __FILE__, __LINE__ + 1
-
def #{name}(message)
-
#{options.fetch(:to, :log)}(#{name.inspect}, message)
-
end
-
RUBY
-
end
-
end
-
-
end
-
end
-
end
-
# A namespace for the `@media` query parse tree.
-
1
module Sass::Media
-
# A comma-separated list of queries.
-
#
-
# media_query [ ',' S* media_query ]*
-
1
class QueryList
-
# The queries contained in this list.
-
#
-
# @return [Array<Query>]
-
1
attr_accessor :queries
-
-
# @param queries [Array<Query>] See \{#queries}
-
1
def initialize(queries)
-
@queries = queries
-
end
-
-
# Merges this query list with another. The returned query list
-
# queries for the intersection between the two inputs.
-
#
-
# Both query lists should be resolved.
-
#
-
# @param other [QueryList]
-
# @return [QueryList?] The merged list, or nil if there is no intersection.
-
1
def merge(other)
-
new_queries = queries.map {|q1| other.queries.map {|q2| q1.merge(q2)}}.flatten.compact
-
return if new_queries.empty?
-
QueryList.new(new_queries)
-
end
-
-
# Returns the CSS for the media query list.
-
#
-
# @return [String]
-
1
def to_css
-
queries.map {|q| q.to_css}.join(', ')
-
end
-
-
# Returns the Sass/SCSS code for the media query list.
-
#
-
# @param options [{Symbol => Object}] An options hash (see {Sass::CSS#initialize}).
-
# @return [String]
-
1
def to_src(options)
-
queries.map {|q| q.to_src(options)}.join(', ')
-
end
-
-
# Returns a representation of the query as an array of strings and
-
# potentially {Sass::Script::Node}s (if there's interpolation in it). When
-
# the interpolation is resolved and the strings are joined together, this
-
# will be the string representation of this query.
-
#
-
# @return [Array<String, Sass::Script::Node>]
-
1
def to_a
-
Sass::Util.intersperse(queries.map {|q| q.to_a}, ', ').flatten
-
end
-
-
# Returns a deep copy of this query list and all its children.
-
#
-
# @return [QueryList]
-
1
def deep_copy
-
QueryList.new(queries.map {|q| q.deep_copy})
-
end
-
end
-
-
# A single media query.
-
#
-
# [ [ONLY | NOT]? S* media_type S* | expression ] [ AND S* expression ]*
-
1
class Query
-
# The modifier for the query.
-
#
-
# When parsed as Sass code, this contains strings and SassScript nodes. When
-
# parsed as CSS, it contains a single string (accessible via
-
# \{#resolved_modifier}).
-
#
-
# @return [Array<String, Sass::Script::Node>]
-
1
attr_accessor :modifier
-
-
# The type of the query (e.g. `"screen"` or `"print"`).
-
#
-
# When parsed as Sass code, this contains strings and SassScript nodes. When
-
# parsed as CSS, it contains a single string (accessible via
-
# \{#resolved_type}).
-
#
-
# @return [Array<String, Sass::Script::Node>]
-
1
attr_accessor :type
-
-
# The trailing expressions in the query.
-
#
-
# When parsed as Sass code, each expression contains strings and SassScript
-
# nodes. When parsed as CSS, each one contains a single string.
-
#
-
# @return [Array<Array<String, Sass::Script::Node>>]
-
1
attr_accessor :expressions
-
-
# @param modifier [Array<String, Sass::Script::Node>] See \{#modifier}
-
# @param type [Array<String, Sass::Script::Node>] See \{#type}
-
# @param expressions [Array<Array<String, Sass::Script::Node>>] See \{#expressions}
-
1
def initialize(modifier, type, expressions)
-
@modifier = modifier
-
@type = type
-
@expressions = expressions
-
end
-
-
# See \{#modifier}.
-
# @return [String]
-
1
def resolved_modifier
-
# modifier should contain only a single string
-
modifier.first || ''
-
end
-
-
# See \{#type}.
-
# @return [String]
-
1
def resolved_type
-
# type should contain only a single string
-
type.first || ''
-
end
-
-
# Merges this query with another. The returned query queries for
-
# the intersection between the two inputs.
-
#
-
# Both queries should be resolved.
-
#
-
# @param other [Query]
-
# @return [Query?] The merged query, or nil if there is no intersection.
-
1
def merge(other)
-
m1, t1 = resolved_modifier.downcase, resolved_type.downcase
-
m2, t2 = other.resolved_modifier.downcase, other.resolved_type.downcase
-
t1 = t2 if t1.empty?
-
t2 = t1 if t2.empty?
-
if ((m1 == 'not') ^ (m2 == 'not'))
-
return if t1 == t2
-
type = m1 == 'not' ? t2 : t1
-
mod = m1 == 'not' ? m2 : m1
-
elsif m1 == 'not' && m2 == 'not'
-
# CSS has no way of representing "neither screen nor print"
-
return unless t1 == t2
-
type = t1
-
mod = 'not'
-
elsif t1 != t2
-
return
-
else # t1 == t2, neither m1 nor m2 are "not"
-
type = t1
-
mod = m1.empty? ? m2 : m1
-
end
-
return Query.new([mod], [type], other.expressions + expressions)
-
end
-
-
# Returns the CSS for the media query.
-
#
-
# @return [String]
-
1
def to_css
-
css = ''
-
css << resolved_modifier
-
css << ' ' unless resolved_modifier.empty?
-
css << resolved_type
-
css << ' and ' unless resolved_type.empty? || expressions.empty?
-
css << expressions.map do |e|
-
# It's possible for there to be script nodes in Expressions even when
-
# we're converting to CSS in the case where we parsed the document as
-
# CSS originally (as in css_test.rb).
-
e.map {|c| c.is_a?(Sass::Script::Node) ? c.to_sass : c.to_s}.join
-
end.join(' and ')
-
css
-
end
-
-
# Returns the Sass/SCSS code for the media query.
-
#
-
# @param options [{Symbol => Object}] An options hash (see {Sass::CSS#initialize}).
-
# @return [String]
-
1
def to_src(options)
-
src = ''
-
src << Sass::Media._interp_to_src(modifier, options)
-
src << ' ' unless modifier.empty?
-
src << Sass::Media._interp_to_src(type, options)
-
src << ' and ' unless type.empty? || expressions.empty?
-
src << expressions.map do |e|
-
Sass::Media._interp_to_src(e, options)
-
end.join(' and ')
-
src
-
end
-
-
# @see \{MediaQuery#to\_a}
-
1
def to_a
-
res = []
-
res += modifier
-
res << ' ' unless modifier.empty?
-
res += type
-
res << ' and ' unless type.empty? || expressions.empty?
-
res += Sass::Util.intersperse(expressions, ' and ').flatten
-
res
-
end
-
-
# Returns a deep copy of this query and all its children.
-
#
-
# @return [Query]
-
1
def deep_copy
-
Query.new(
-
modifier.map {|c| c.is_a?(Sass::Script::Node) ? c.deep_copy : c},
-
type.map {|c| c.is_a?(Sass::Script::Node) ? c.deep_copy : c},
-
expressions.map {|e| e.map {|c| c.is_a?(Sass::Script::Node) ? c.deep_copy : c}})
-
end
-
end
-
-
# Converts an interpolation array to source.
-
#
-
# @param [Array<String, Sass::Script::Node>] The interpolation array to convert.
-
# @param options [{Symbol => Object}] An options hash (see {Sass::CSS#initialize}).
-
# @return [String]
-
1
def self._interp_to_src(interp, options)
-
interp.map do |r|
-
next r if r.is_a?(String)
-
"\#{#{r.to_sass(options)}}"
-
end.join
-
end
-
end
-
# Rails 3.0.0.beta.2+, < 3.1
-
1
if defined?(ActiveSupport) && Sass::Util.has?(:public_method, ActiveSupport, :on_load) &&
-
!Sass::Util.ap_geq?('3.1.0.beta')
-
require 'sass/plugin/configuration'
-
ActiveSupport.on_load(:before_configuration) do
-
require 'sass'
-
require 'sass/plugin'
-
require 'sass/plugin/rails'
-
end
-
end
-
1
module Sass
-
# The root directory of the Sass source tree.
-
# This may be overridden by the package manager
-
# if the lib directory is separated from the main source tree.
-
# @api public
-
1
ROOT_DIR = File.expand_path(File.join(__FILE__, "../../.."))
-
end
-
1
require 'sass/script/node'
-
1
require 'sass/script/variable'
-
1
require 'sass/script/funcall'
-
1
require 'sass/script/operation'
-
1
require 'sass/script/literal'
-
1
require 'sass/script/parser'
-
-
1
module Sass
-
# SassScript is code that's embedded in Sass documents
-
# to allow for property values to be computed from variables.
-
#
-
# This module contains code that handles the parsing and evaluation of SassScript.
-
1
module Script
-
# The regular expression used to parse variables.
-
1
MATCH = /^\$(#{Sass::SCSS::RX::IDENT})\s*:\s*(.+?)(!(?i:default))?$/
-
-
# The regular expression used to validate variables without matching.
-
1
VALIDATE = /^\$#{Sass::SCSS::RX::IDENT}$/
-
-
# Parses a string of SassScript
-
#
-
# @param value [String] The SassScript
-
# @param line [Fixnum] The number of the line on which the SassScript appeared.
-
# Used for error reporting
-
# @param offset [Fixnum] The number of characters in on `line` that the SassScript started.
-
# Used for error reporting
-
# @param options [{Symbol => Object}] An options hash;
-
# see {file:SASS_REFERENCE.md#sass_options the Sass options documentation}
-
# @return [Script::Node] The root node of the parse tree
-
1
def self.parse(value, line, offset, options = {})
-
Parser.parse(value, line, offset, options)
-
rescue Sass::SyntaxError => e
-
e.message << ": #{value.inspect}." if e.message == "SassScript error"
-
e.modify_backtrace(:line => line, :filename => options[:filename])
-
raise e
-
end
-
-
end
-
end
-
1
module Sass::Script
-
# A SassScript object representing a variable argument list. This works just
-
# like a normal list, but can also contain keyword arguments.
-
#
-
# The keyword arguments attached to this list are unused except when this is
-
# passed as a glob argument to a function or mixin.
-
1
class ArgList < List
-
# Whether \{#keywords} has been accessed. If so, we assume that all keywords
-
# were valid for the function that created this ArgList.
-
#
-
# @return [Boolean]
-
1
attr_accessor :keywords_accessed
-
-
# Creates a new argument list.
-
#
-
# @param value [Array<Literal>] See \{List#value}.
-
# @param keywords [Hash<String, Literal>] See \{#keywords}
-
# @param separator [String] See \{List#separator}.
-
1
def initialize(value, keywords, separator)
-
super(value, separator)
-
@keywords = keywords
-
end
-
-
# The keyword arguments attached to this list.
-
#
-
# @return [Hash<String, Literal>]
-
1
def keywords
-
@keywords_accessed = true
-
@keywords
-
end
-
-
# @see Node#children
-
1
def children
-
super + @keywords.values
-
end
-
-
# @see Node#deep_copy
-
1
def deep_copy
-
node = super
-
node.instance_variable_set('@keywords',
-
Sass::Util.map_hash(@keywords) {|k, v| [k, v.deep_copy]})
-
node
-
end
-
-
1
protected
-
-
# @see Node#_perform
-
1
def _perform(environment)
-
self
-
end
-
end
-
end
-
1
require 'sass/script/literal'
-
-
1
module Sass::Script
-
# A SassScript object representing a boolean (true or false) value.
-
1
class Bool < Literal
-
# The Ruby value of the boolean.
-
#
-
# @return [Boolean]
-
1
attr_reader :value
-
1
alias_method :to_bool, :value
-
-
# @return [String] "true" or "false"
-
1
def to_s(opts = {})
-
@value.to_s
-
end
-
1
alias_method :to_sass, :to_s
-
end
-
end
-
1
require 'sass/script/literal'
-
-
1
module Sass::Script
-
# A SassScript object representing a CSS color.
-
#
-
# A color may be represented internally as RGBA, HSLA, or both.
-
# It's originally represented as whatever its input is;
-
# if it's created with RGB values, it's represented as RGBA,
-
# and if it's created with HSL values, it's represented as HSLA.
-
# Once a property is accessed that requires the other representation --
-
# for example, \{#red} for an HSL color --
-
# that component is calculated and cached.
-
#
-
# The alpha channel of a color is independent of its RGB or HSL representation.
-
# It's always stored, as 1 if nothing else is specified.
-
# If only the alpha channel is modified using \{#with},
-
# the cached RGB and HSL values are retained.
-
1
class Color < Literal
-
2
class << self; include Sass::Util; end
-
-
# A hash from color names to `[red, green, blue]` value arrays.
-
1
COLOR_NAMES = map_vals({
-
'aliceblue' => 0xf0f8ff,
-
'antiquewhite' => 0xfaebd7,
-
'aqua' => 0x00ffff,
-
'aquamarine' => 0x7fffd4,
-
'azure' => 0xf0ffff,
-
'beige' => 0xf5f5dc,
-
'bisque' => 0xffe4c4,
-
'black' => 0x000000,
-
'blanchedalmond' => 0xffebcd,
-
'blue' => 0x0000ff,
-
'blueviolet' => 0x8a2be2,
-
'brown' => 0xa52a2a,
-
'burlywood' => 0xdeb887,
-
'cadetblue' => 0x5f9ea0,
-
'chartreuse' => 0x7fff00,
-
'chocolate' => 0xd2691e,
-
'coral' => 0xff7f50,
-
'cornflowerblue' => 0x6495ed,
-
'cornsilk' => 0xfff8dc,
-
'crimson' => 0xdc143c,
-
'cyan' => 0x00ffff,
-
'darkblue' => 0x00008b,
-
'darkcyan' => 0x008b8b,
-
'darkgoldenrod' => 0xb8860b,
-
'darkgray' => 0xa9a9a9,
-
'darkgrey' => 0xa9a9a9,
-
'darkgreen' => 0x006400,
-
'darkkhaki' => 0xbdb76b,
-
'darkmagenta' => 0x8b008b,
-
'darkolivegreen' => 0x556b2f,
-
'darkorange' => 0xff8c00,
-
'darkorchid' => 0x9932cc,
-
'darkred' => 0x8b0000,
-
'darksalmon' => 0xe9967a,
-
'darkseagreen' => 0x8fbc8f,
-
'darkslateblue' => 0x483d8b,
-
'darkslategray' => 0x2f4f4f,
-
'darkslategrey' => 0x2f4f4f,
-
'darkturquoise' => 0x00ced1,
-
'darkviolet' => 0x9400d3,
-
'deeppink' => 0xff1493,
-
'deepskyblue' => 0x00bfff,
-
'dimgray' => 0x696969,
-
'dimgrey' => 0x696969,
-
'dodgerblue' => 0x1e90ff,
-
'firebrick' => 0xb22222,
-
'floralwhite' => 0xfffaf0,
-
'forestgreen' => 0x228b22,
-
'fuchsia' => 0xff00ff,
-
'gainsboro' => 0xdcdcdc,
-
'ghostwhite' => 0xf8f8ff,
-
'gold' => 0xffd700,
-
'goldenrod' => 0xdaa520,
-
'gray' => 0x808080,
-
'green' => 0x008000,
-
'greenyellow' => 0xadff2f,
-
'honeydew' => 0xf0fff0,
-
'hotpink' => 0xff69b4,
-
'indianred' => 0xcd5c5c,
-
'indigo' => 0x4b0082,
-
'ivory' => 0xfffff0,
-
'khaki' => 0xf0e68c,
-
'lavender' => 0xe6e6fa,
-
'lavenderblush' => 0xfff0f5,
-
'lawngreen' => 0x7cfc00,
-
'lemonchiffon' => 0xfffacd,
-
'lightblue' => 0xadd8e6,
-
'lightcoral' => 0xf08080,
-
'lightcyan' => 0xe0ffff,
-
'lightgoldenrodyellow' => 0xfafad2,
-
'lightgreen' => 0x90ee90,
-
'lightgray' => 0xd3d3d3,
-
'lightgrey' => 0xd3d3d3,
-
'lightpink' => 0xffb6c1,
-
'lightsalmon' => 0xffa07a,
-
'lightseagreen' => 0x20b2aa,
-
'lightskyblue' => 0x87cefa,
-
'lightslategray' => 0x778899,
-
'lightslategrey' => 0x778899,
-
'lightsteelblue' => 0xb0c4de,
-
'lightyellow' => 0xffffe0,
-
'lime' => 0x00ff00,
-
'limegreen' => 0x32cd32,
-
'linen' => 0xfaf0e6,
-
'magenta' => 0xff00ff,
-
'maroon' => 0x800000,
-
'mediumaquamarine' => 0x66cdaa,
-
'mediumblue' => 0x0000cd,
-
'mediumorchid' => 0xba55d3,
-
'mediumpurple' => 0x9370db,
-
'mediumseagreen' => 0x3cb371,
-
'mediumslateblue' => 0x7b68ee,
-
'mediumspringgreen' => 0x00fa9a,
-
'mediumturquoise' => 0x48d1cc,
-
'mediumvioletred' => 0xc71585,
-
'midnightblue' => 0x191970,
-
'mintcream' => 0xf5fffa,
-
'mistyrose' => 0xffe4e1,
-
'moccasin' => 0xffe4b5,
-
'navajowhite' => 0xffdead,
-
'navy' => 0x000080,
-
'oldlace' => 0xfdf5e6,
-
'olive' => 0x808000,
-
'olivedrab' => 0x6b8e23,
-
'orange' => 0xffa500,
-
'orangered' => 0xff4500,
-
'orchid' => 0xda70d6,
-
'palegoldenrod' => 0xeee8aa,
-
'palegreen' => 0x98fb98,
-
'paleturquoise' => 0xafeeee,
-
'palevioletred' => 0xdb7093,
-
'papayawhip' => 0xffefd5,
-
'peachpuff' => 0xffdab9,
-
'peru' => 0xcd853f,
-
'pink' => 0xffc0cb,
-
'plum' => 0xdda0dd,
-
'powderblue' => 0xb0e0e6,
-
'purple' => 0x800080,
-
'red' => 0xff0000,
-
'rosybrown' => 0xbc8f8f,
-
'royalblue' => 0x4169e1,
-
'saddlebrown' => 0x8b4513,
-
'salmon' => 0xfa8072,
-
'sandybrown' => 0xf4a460,
-
'seagreen' => 0x2e8b57,
-
'seashell' => 0xfff5ee,
-
'sienna' => 0xa0522d,
-
'silver' => 0xc0c0c0,
-
'skyblue' => 0x87ceeb,
-
'slateblue' => 0x6a5acd,
-
'slategray' => 0x708090,
-
'slategrey' => 0x708090,
-
'snow' => 0xfffafa,
-
'springgreen' => 0x00ff7f,
-
'steelblue' => 0x4682b4,
-
'tan' => 0xd2b48c,
-
'teal' => 0x008080,
-
'thistle' => 0xd8bfd8,
-
'tomato' => 0xff6347,
-
'turquoise' => 0x40e0d0,
-
'violet' => 0xee82ee,
-
'wheat' => 0xf5deb3,
-
'white' => 0xffffff,
-
'whitesmoke' => 0xf5f5f5,
-
'yellow' => 0xffff00,
-
'yellowgreen' => 0x9acd32
-
584
}) {|color| (0..2).map {|n| color >> (n << 3) & 0xff}.reverse}
-
-
# A hash from `[red, green, blue]` value arrays to color names.
-
147
COLOR_NAMES_REVERSE = map_hash(hash_to_a(COLOR_NAMES)) {|k, v| [v, k]}
-
-
# Constructs an RGB or HSL color object,
-
# optionally with an alpha channel.
-
#
-
# The RGB values must be between 0 and 255.
-
# The saturation and lightness values must be between 0 and 100.
-
# The alpha value must be between 0 and 1.
-
#
-
# @raise [Sass::SyntaxError] if any color value isn't in the specified range
-
#
-
# @overload initialize(attrs)
-
# The attributes are specified as a hash.
-
# This hash must contain either `:hue`, `:saturation`, and `:value` keys,
-
# or `:red`, `:green`, and `:blue` keys.
-
# It cannot contain both HSL and RGB keys.
-
# It may also optionally contain an `:alpha` key.
-
#
-
# @param attrs [{Symbol => Numeric}] A hash of color attributes to values
-
# @raise [ArgumentError] if not enough attributes are specified,
-
# or both RGB and HSL attributes are specified
-
#
-
# @overload initialize(rgba)
-
# The attributes are specified as an array.
-
# This overload only supports RGB or RGBA colors.
-
#
-
# @param rgba [Array<Numeric>] A three- or four-element array
-
# of the red, green, blue, and optionally alpha values (respectively)
-
# of the color
-
# @raise [ArgumentError] if not enough attributes are specified
-
1
def initialize(attrs, allow_both_rgb_and_hsl = false)
-
super(nil)
-
-
if attrs.is_a?(Array)
-
unless (3..4).include?(attrs.size)
-
raise ArgumentError.new("Color.new(array) expects a three- or four-element array")
-
end
-
-
red, green, blue = attrs[0...3].map {|c| c.to_i}
-
@attrs = {:red => red, :green => green, :blue => blue}
-
@attrs[:alpha] = attrs[3] ? attrs[3].to_f : 1
-
else
-
attrs = attrs.reject {|k, v| v.nil?}
-
hsl = [:hue, :saturation, :lightness] & attrs.keys
-
rgb = [:red, :green, :blue] & attrs.keys
-
if !allow_both_rgb_and_hsl && !hsl.empty? && !rgb.empty?
-
raise ArgumentError.new("Color.new(hash) may not have both HSL and RGB keys specified")
-
elsif hsl.empty? && rgb.empty?
-
raise ArgumentError.new("Color.new(hash) must have either HSL or RGB keys specified")
-
elsif !hsl.empty? && hsl.size != 3
-
raise ArgumentError.new("Color.new(hash) must have all three HSL values specified")
-
elsif !rgb.empty? && rgb.size != 3
-
raise ArgumentError.new("Color.new(hash) must have all three RGB values specified")
-
end
-
-
@attrs = attrs
-
@attrs[:hue] %= 360 if @attrs[:hue]
-
@attrs[:alpha] ||= 1
-
end
-
-
[:red, :green, :blue].each do |k|
-
next if @attrs[k].nil?
-
@attrs[k] = @attrs[k].to_i
-
Sass::Util.check_range("#{k.to_s.capitalize} value", 0..255, @attrs[k])
-
end
-
-
[:saturation, :lightness].each do |k|
-
next if @attrs[k].nil?
-
value = Number.new(@attrs[k], ['%']) # Get correct unit for error messages
-
@attrs[k] = Sass::Util.check_range("#{k.to_s.capitalize}", 0..100, value, '%')
-
end
-
-
@attrs[:alpha] = Sass::Util.check_range("Alpha channel", 0..1, @attrs[:alpha])
-
end
-
-
# The red component of the color.
-
#
-
# @return [Fixnum]
-
1
def red
-
hsl_to_rgb!
-
@attrs[:red]
-
end
-
-
# The green component of the color.
-
#
-
# @return [Fixnum]
-
1
def green
-
hsl_to_rgb!
-
@attrs[:green]
-
end
-
-
# The blue component of the color.
-
#
-
# @return [Fixnum]
-
1
def blue
-
hsl_to_rgb!
-
@attrs[:blue]
-
end
-
-
# The hue component of the color.
-
#
-
# @return [Numeric]
-
1
def hue
-
rgb_to_hsl!
-
@attrs[:hue]
-
end
-
-
# The saturation component of the color.
-
#
-
# @return [Numeric]
-
1
def saturation
-
rgb_to_hsl!
-
@attrs[:saturation]
-
end
-
-
# The lightness component of the color.
-
#
-
# @return [Numeric]
-
1
def lightness
-
rgb_to_hsl!
-
@attrs[:lightness]
-
end
-
-
# The alpha channel (opacity) of the color.
-
# This is 1 unless otherwise defined.
-
#
-
# @return [Fixnum]
-
1
def alpha
-
@attrs[:alpha]
-
end
-
-
# Returns whether this color object is translucent;
-
# that is, whether the alpha channel is non-1.
-
#
-
# @return [Boolean]
-
1
def alpha?
-
alpha < 1
-
end
-
-
# Returns the red, green, and blue components of the color.
-
#
-
# @return [Array<Fixnum>] A frozen three-element array of the red, green, and blue
-
# values (respectively) of the color
-
1
def rgb
-
[red, green, blue].freeze
-
end
-
-
# Returns the hue, saturation, and lightness components of the color.
-
#
-
# @return [Array<Fixnum>] A frozen three-element array of the
-
# hue, saturation, and lightness values (respectively) of the color
-
1
def hsl
-
[hue, saturation, lightness].freeze
-
end
-
-
# The SassScript `==` operation.
-
# **Note that this returns a {Sass::Script::Bool} object,
-
# not a Ruby boolean**.
-
#
-
# @param other [Literal] The right-hand side of the operator
-
# @return [Bool] True if this literal is the same as the other,
-
# false otherwise
-
1
def eq(other)
-
Sass::Script::Bool.new(
-
other.is_a?(Color) && rgb == other.rgb && alpha == other.alpha)
-
end
-
-
# Returns a copy of this color with one or more channels changed.
-
# RGB or HSL colors may be changed, but not both at once.
-
#
-
# For example:
-
#
-
# Color.new([10, 20, 30]).with(:blue => 40)
-
# #=> rgb(10, 40, 30)
-
# Color.new([126, 126, 126]).with(:red => 0, :green => 255)
-
# #=> rgb(0, 255, 126)
-
# Color.new([255, 0, 127]).with(:saturation => 60)
-
# #=> rgb(204, 51, 127)
-
# Color.new([1, 2, 3]).with(:alpha => 0.4)
-
# #=> rgba(1, 2, 3, 0.4)
-
#
-
# @param attrs [{Symbol => Numeric}]
-
# A map of channel names (`:red`, `:green`, `:blue`,
-
# `:hue`, `:saturation`, `:lightness`, or `:alpha`) to values
-
# @return [Color] The new Color object
-
# @raise [ArgumentError] if both RGB and HSL keys are specified
-
1
def with(attrs)
-
attrs = attrs.reject {|k, v| v.nil?}
-
hsl = !([:hue, :saturation, :lightness] & attrs.keys).empty?
-
rgb = !([:red, :green, :blue] & attrs.keys).empty?
-
if hsl && rgb
-
raise ArgumentError.new("Cannot specify HSL and RGB values for a color at the same time")
-
end
-
-
if hsl
-
[:hue, :saturation, :lightness].each {|k| attrs[k] ||= send(k)}
-
elsif rgb
-
[:red, :green, :blue].each {|k| attrs[k] ||= send(k)}
-
else
-
# If we're just changing the alpha channel,
-
# keep all the HSL/RGB stuff we've calculated
-
attrs = @attrs.merge(attrs)
-
end
-
attrs[:alpha] ||= alpha
-
-
Color.new(attrs, :allow_both_rgb_and_hsl)
-
end
-
-
# The SassScript `+` operation.
-
# Its functionality depends on the type of its argument:
-
#
-
# {Number}
-
# : Adds the number to each of the RGB color channels.
-
#
-
# {Color}
-
# : Adds each of the RGB color channels together.
-
#
-
# {Literal}
-
# : See {Literal#plus}.
-
#
-
# @param other [Literal] The right-hand side of the operator
-
# @return [Color] The resulting color
-
# @raise [Sass::SyntaxError] if `other` is a number with units
-
1
def plus(other)
-
if other.is_a?(Sass::Script::Number) || other.is_a?(Sass::Script::Color)
-
piecewise(other, :+)
-
else
-
super
-
end
-
end
-
-
# The SassScript `-` operation.
-
# Its functionality depends on the type of its argument:
-
#
-
# {Number}
-
# : Subtracts the number from each of the RGB color channels.
-
#
-
# {Color}
-
# : Subtracts each of the other color's RGB color channels from this color's.
-
#
-
# {Literal}
-
# : See {Literal#minus}.
-
#
-
# @param other [Literal] The right-hand side of the operator
-
# @return [Color] The resulting color
-
# @raise [Sass::SyntaxError] if `other` is a number with units
-
1
def minus(other)
-
if other.is_a?(Sass::Script::Number) || other.is_a?(Sass::Script::Color)
-
piecewise(other, :-)
-
else
-
super
-
end
-
end
-
-
# The SassScript `*` operation.
-
# Its functionality depends on the type of its argument:
-
#
-
# {Number}
-
# : Multiplies the number by each of the RGB color channels.
-
#
-
# {Color}
-
# : Multiplies each of the RGB color channels together.
-
#
-
# @param other [Number, Color] The right-hand side of the operator
-
# @return [Color] The resulting color
-
# @raise [Sass::SyntaxError] if `other` is a number with units
-
1
def times(other)
-
if other.is_a?(Sass::Script::Number) || other.is_a?(Sass::Script::Color)
-
piecewise(other, :*)
-
else
-
raise NoMethodError.new(nil, :times)
-
end
-
end
-
-
# The SassScript `/` operation.
-
# Its functionality depends on the type of its argument:
-
#
-
# {Number}
-
# : Divides each of the RGB color channels by the number.
-
#
-
# {Color}
-
# : Divides each of this color's RGB color channels by the other color's.
-
#
-
# {Literal}
-
# : See {Literal#div}.
-
#
-
# @param other [Literal] The right-hand side of the operator
-
# @return [Color] The resulting color
-
# @raise [Sass::SyntaxError] if `other` is a number with units
-
1
def div(other)
-
if other.is_a?(Sass::Script::Number) || other.is_a?(Sass::Script::Color)
-
piecewise(other, :/)
-
else
-
super
-
end
-
end
-
-
# The SassScript `%` operation.
-
# Its functionality depends on the type of its argument:
-
#
-
# {Number}
-
# : Takes each of the RGB color channels module the number.
-
#
-
# {Color}
-
# : Takes each of this color's RGB color channels modulo the other color's.
-
#
-
# @param other [Number, Color] The right-hand side of the operator
-
# @return [Color] The resulting color
-
# @raise [Sass::SyntaxError] if `other` is a number with units
-
1
def mod(other)
-
if other.is_a?(Sass::Script::Number) || other.is_a?(Sass::Script::Color)
-
piecewise(other, :%)
-
else
-
raise NoMethodError.new(nil, :mod)
-
end
-
end
-
-
# Returns a string representation of the color.
-
# This is usually the color's hex value,
-
# but if the color has a name that's used instead.
-
#
-
# @return [String] The string representation
-
1
def to_s(opts = {})
-
return rgba_str if alpha?
-
return smallest if options[:style] == :compressed
-
return COLOR_NAMES_REVERSE[rgb] if COLOR_NAMES_REVERSE[rgb]
-
hex_str
-
end
-
1
alias_method :to_sass, :to_s
-
-
# Returns a string representation of the color.
-
#
-
# @return [String] The hex value
-
1
def inspect
-
alpha? ? rgba_str : hex_str
-
end
-
-
1
private
-
-
1
def smallest
-
small_hex_str = hex_str.gsub(/^#(.)\1(.)\2(.)\3$/, '#\1\2\3')
-
return small_hex_str unless (color = COLOR_NAMES_REVERSE[rgb]) &&
-
color.size <= small_hex_str.size
-
return color
-
end
-
-
1
def rgba_str
-
split = options[:style] == :compressed ? ',' : ', '
-
"rgba(#{rgb.join(split)}#{split}#{Number.round(alpha)})"
-
end
-
-
1
def hex_str
-
red, green, blue = rgb.map { |num| num.to_s(16).rjust(2, '0') }
-
"##{red}#{green}#{blue}"
-
end
-
-
1
def piecewise(other, operation)
-
other_num = other.is_a? Number
-
if other_num && !other.unitless?
-
raise Sass::SyntaxError.new("Cannot add a number with units (#{other}) to a color (#{self}).")
-
end
-
-
result = []
-
for i in (0...3)
-
res = rgb[i].send(operation, other_num ? other.value : other.rgb[i])
-
result[i] = [ [res, 255].min, 0 ].max
-
end
-
-
if !other_num && other.alpha != alpha
-
raise Sass::SyntaxError.new("Alpha channels must be equal: #{self} #{operation} #{other}")
-
end
-
-
with(:red => result[0], :green => result[1], :blue => result[2])
-
end
-
-
1
def hsl_to_rgb!
-
return if @attrs[:red] && @attrs[:blue] && @attrs[:green]
-
-
h = @attrs[:hue] / 360.0
-
s = @attrs[:saturation] / 100.0
-
l = @attrs[:lightness] / 100.0
-
-
# Algorithm from the CSS3 spec: http://www.w3.org/TR/css3-color/#hsl-color.
-
m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s
-
m1 = l * 2 - m2
-
@attrs[:red], @attrs[:green], @attrs[:blue] = [
-
hue_to_rgb(m1, m2, h + 1.0/3),
-
hue_to_rgb(m1, m2, h),
-
hue_to_rgb(m1, m2, h - 1.0/3)
-
].map {|c| (c * 0xff).round}
-
end
-
-
1
def hue_to_rgb(m1, m2, h)
-
h += 1 if h < 0
-
h -= 1 if h > 1
-
return m1 + (m2 - m1) * h * 6 if h * 6 < 1
-
return m2 if h * 2 < 1
-
return m1 + (m2 - m1) * (2.0/3 - h) * 6 if h * 3 < 2
-
return m1
-
end
-
-
1
def rgb_to_hsl!
-
return if @attrs[:hue] && @attrs[:saturation] && @attrs[:lightness]
-
r, g, b = [:red, :green, :blue].map {|k| @attrs[k] / 255.0}
-
-
# Algorithm from http://en.wikipedia.org/wiki/HSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV
-
max = [r, g, b].max
-
min = [r, g, b].min
-
d = max - min
-
-
h =
-
case max
-
when min; 0
-
when r; 60 * (g-b)/d
-
when g; 60 * (b-r)/d + 120
-
when b; 60 * (r-g)/d + 240
-
end
-
-
l = (max + min)/2.0
-
-
s =
-
if max == min
-
0
-
elsif l < 0.5
-
d/(2*l)
-
else
-
d/(2 - 2*l)
-
end
-
-
@attrs[:hue] = h % 360
-
@attrs[:saturation] = s * 100
-
@attrs[:lightness] = l * 100
-
end
-
end
-
end
-
1
module Sass
-
1
module Script
-
# This is a subclass of {Lexer} for use in parsing plain CSS properties.
-
#
-
# @see Sass::SCSS::CssParser
-
1
class CssLexer < Lexer
-
1
private
-
-
1
def token
-
important || super
-
end
-
-
1
def string(re, *args)
-
if re == :uri
-
return unless uri = scan(URI)
-
return [:string, Script::String.new(uri)]
-
end
-
-
return unless scan(STRING)
-
[:string, Script::String.new((@scanner[1] || @scanner[2]).gsub(/\\(['"])/, '\1'), :string)]
-
end
-
-
1
def important
-
return unless s = scan(IMPORTANT)
-
[:raw, s]
-
end
-
end
-
end
-
end
-
1
require 'sass/script'
-
1
require 'sass/script/css_lexer'
-
-
1
module Sass
-
1
module Script
-
# This is a subclass of {Parser} for use in parsing plain CSS properties.
-
#
-
# @see Sass::SCSS::CssParser
-
1
class CssParser < Parser
-
1
private
-
-
# @private
-
1
def lexer_class; CssLexer; end
-
-
# We need a production that only does /,
-
# since * and % aren't allowed in plain CSS
-
1
production :div, :unary_plus, :div
-
-
1
def string
-
return number unless tok = try_tok(:string)
-
return tok.value unless @lexer.peek && @lexer.peek.type == :begin_interpolation
-
end
-
-
# Short-circuit all the SassScript-only productions
-
1
alias_method :interpolation, :space
-
1
alias_method :or_expr, :div
-
1
alias_method :unary_div, :ident
-
1
alias_method :paren, :string
-
end
-
end
-
end
-
1
require 'sass/script/functions'
-
-
1
module Sass
-
1
module Script
-
# A SassScript parse node representing a function call.
-
#
-
# A function call either calls one of the functions in {Script::Functions},
-
# or if no function with the given name exists
-
# it returns a string representation of the function call.
-
1
class Funcall < Node
-
# The name of the function.
-
#
-
# @return [String]
-
1
attr_reader :name
-
-
# The arguments to the function.
-
#
-
# @return [Array<Script::Node>]
-
1
attr_reader :args
-
-
# The keyword arguments to the function.
-
#
-
# @return [{String => Script::Node}]
-
1
attr_reader :keywords
-
-
# The splat argument for this function, if one exists.
-
#
-
# @return [Script::Node?]
-
1
attr_accessor :splat
-
-
# @param name [String] See \{#name}
-
# @param args [Array<Script::Node>] See \{#args}
-
# @param splat [Script::Node] See \{#splat}
-
# @param keywords [{String => Script::Node}] See \{#keywords}
-
1
def initialize(name, args, keywords, splat)
-
@name = name
-
@args = args
-
@keywords = keywords
-
@splat = splat
-
super()
-
end
-
-
# @return [String] A string representation of the function call
-
1
def inspect
-
args = @args.map {|a| a.inspect}.join(', ')
-
keywords = Sass::Util.hash_to_a(@keywords).
-
map {|k, v| "$#{k}: #{v.inspect}"}.join(', ')
-
if self.splat
-
splat = (args.empty? && keywords.empty?) ? "" : ", "
-
splat = "#{splat}#{self.splat.inspect}..."
-
end
-
"#{name}(#{args}#{', ' unless args.empty? || keywords.empty?}#{keywords}#{splat})"
-
end
-
-
# @see Node#to_sass
-
1
def to_sass(opts = {})
-
arg_to_sass = lambda do |arg|
-
sass = arg.to_sass(opts)
-
sass = "(#{sass})" if arg.is_a?(Sass::Script::List) && arg.separator == :comma
-
sass
-
end
-
-
args = @args.map(&arg_to_sass).join(', ')
-
keywords = Sass::Util.hash_to_a(@keywords).
-
map {|k, v| "$#{dasherize(k, opts)}: #{arg_to_sass[v]}"}.join(', ')
-
if self.splat
-
splat = (args.empty? && keywords.empty?) ? "" : ", "
-
splat = "#{splat}#{arg_to_sass[self.splat]}..."
-
end
-
"#{dasherize(name, opts)}(#{args}#{', ' unless args.empty? || keywords.empty?}#{keywords}#{splat})"
-
end
-
-
# Returns the arguments to the function.
-
#
-
# @return [Array<Node>]
-
# @see Node#children
-
1
def children
-
res = @args + @keywords.values
-
res << @splat if @splat
-
res
-
end
-
-
# @see Node#deep_copy
-
1
def deep_copy
-
node = dup
-
node.instance_variable_set('@args', args.map {|a| a.deep_copy})
-
node.instance_variable_set('@keywords', Hash[keywords.map {|k, v| [k, v.deep_copy]}])
-
node
-
end
-
-
1
protected
-
-
# Evaluates the function call.
-
#
-
# @param environment [Sass::Environment] The environment in which to evaluate the SassScript
-
# @return [Literal] The SassScript object that is the value of the function call
-
# @raise [Sass::SyntaxError] if the function call raises an ArgumentError
-
1
def _perform(environment)
-
args = @args.map {|a| a.perform(environment)}
-
splat = @splat.perform(environment) if @splat
-
if fn = environment.function(@name)
-
keywords = Sass::Util.map_hash(@keywords) {|k, v| [k, v.perform(environment)]}
-
return without_original(perform_sass_fn(fn, args, keywords, splat))
-
end
-
-
ruby_name = @name.tr('-', '_')
-
args = construct_ruby_args(ruby_name, args, splat, environment)
-
-
unless Functions.callable?(ruby_name)
-
without_original(opts(to_literal(args)))
-
else
-
without_original(opts(Functions::EvaluationContext.new(environment.options).
-
send(ruby_name, *args)))
-
end
-
rescue ArgumentError => e
-
message = e.message
-
-
# If this is a legitimate Ruby-raised argument error, re-raise it.
-
# Otherwise, it's an error in the user's stylesheet, so wrap it.
-
if Sass::Util.rbx?
-
# Rubinius has a different error report string than vanilla Ruby. It
-
# also doesn't put the actual method for which the argument error was
-
# thrown in the backtrace, nor does it include `send`, so we look for
-
# `_perform`.
-
if e.message =~ /^method '([^']+)': given (\d+), expected (\d+)/
-
error_name, given, expected = $1, $2, $3
-
raise e if error_name != ruby_name || e.backtrace[0] !~ /:in `_perform'$/
-
message = "wrong number of arguments (#{given} for #{expected})"
-
end
-
elsif Sass::Util.jruby?
-
if Sass::Util.jruby1_6?
-
should_maybe_raise = e.message =~ /^wrong number of arguments \((\d+) for (\d+)\)/ &&
-
# The one case where JRuby does include the Ruby name of the function
-
# is manually-thrown ArgumentErrors, which are indistinguishable from
-
# legitimate ArgumentErrors. We treat both of these as
-
# Sass::SyntaxErrors even though it can hide Ruby errors.
-
e.backtrace[0] !~ /:in `(block in )?#{ruby_name}'$/
-
else
-
should_maybe_raise = e.message =~ /^wrong number of arguments calling `[^`]+` \((\d+) for (\d+)\)/
-
given, expected = $1, $2
-
end
-
-
if should_maybe_raise
-
# JRuby 1.7 includes __send__ before send and _perform.
-
trace = e.backtrace.dup
-
raise e if !Sass::Util.jruby1_6? && trace.shift !~ /:in `__send__'$/
-
-
# JRuby (as of 1.7.2) doesn't put the actual method
-
# for which the argument error was thrown in the backtrace, so we
-
# detect whether our send threw an argument error.
-
if !(trace[0] =~ /:in `send'$/ && trace[1] =~ /:in `_perform'$/)
-
raise e
-
elsif !Sass::Util.jruby1_6?
-
# JRuby 1.7 doesn't use standard formatting for its ArgumentErrors.
-
message = "wrong number of arguments (#{given} for #{expected})"
-
end
-
end
-
elsif e.message =~ /^wrong number of arguments \(\d+ for \d+\)/ &&
-
e.backtrace[0] !~ /:in `(block in )?#{ruby_name}'$/
-
raise e
-
end
-
raise Sass::SyntaxError.new("#{message} for `#{name}'")
-
end
-
-
# This method is factored out from `_perform` so that compass can override
-
# it with a cross-browser implementation for functions that require vendor prefixes
-
# in the generated css.
-
1
def to_literal(args)
-
Script::String.new("#{name}(#{args.join(', ')})")
-
end
-
-
1
private
-
-
1
def without_original(value)
-
return value unless value.is_a?(Number)
-
value = value.dup
-
value.original = nil
-
return value
-
end
-
-
1
def construct_ruby_args(name, args, splat, environment)
-
args += splat.to_a if splat
-
-
# If variable arguments were passed, there won't be any explicit keywords.
-
if splat.is_a?(Sass::Script::ArgList)
-
kwargs_size = splat.keywords.size
-
splat.keywords_accessed = false
-
else
-
kwargs_size = @keywords.size
-
end
-
-
unless signature = Functions.signature(name.to_sym, args.size, kwargs_size)
-
return args if @keywords.empty?
-
raise Sass::SyntaxError.new("Function #{name} doesn't support keyword arguments")
-
end
-
keywords = splat.is_a?(Sass::Script::ArgList) ? splat.keywords :
-
Sass::Util.map_hash(@keywords) {|k, v| [k, v.perform(environment)]}
-
-
# If the user passes more non-keyword args than the function expects,
-
# but it does expect keyword args, Ruby's arg handling won't raise an error.
-
# Since we don't want to make functions think about this,
-
# we'll handle it for them here.
-
if signature.var_kwargs && !signature.var_args && args.size > signature.args.size
-
raise Sass::SyntaxError.new(
-
"#{args[signature.args.size].inspect} is not a keyword argument for `#{name}'")
-
elsif keywords.empty?
-
return args
-
end
-
-
args = args + signature.args[args.size..-1].map do |argname|
-
if keywords.has_key?(argname)
-
keywords.delete(argname)
-
else
-
raise Sass::SyntaxError.new("Function #{name} requires an argument named $#{argname}")
-
end
-
end
-
-
if keywords.size > 0
-
if signature.var_kwargs
-
args << keywords
-
else
-
argname = keywords.keys.sort.first
-
if signature.args.include?(argname)
-
raise Sass::SyntaxError.new("Function #{name} was passed argument $#{argname} both by position and by name")
-
else
-
raise Sass::SyntaxError.new("Function #{name} doesn't have an argument named $#{argname}")
-
end
-
end
-
end
-
-
args
-
end
-
-
1
def perform_sass_fn(function, args, keywords, splat)
-
Sass::Tree::Visitors::Perform.perform_arguments(function, args, keywords, splat) do |env|
-
val = catch :_sass_return do
-
function.tree.each {|c| Sass::Tree::Visitors::Perform.visit(c, env)}
-
raise Sass::SyntaxError.new("Function #{@name} finished without @return")
-
end
-
val
-
end
-
end
-
end
-
end
-
end
-
1
module Sass::Script
-
# Methods in this module are accessible from the SassScript context.
-
# For example, you can write
-
#
-
# $color: hsl(120deg, 100%, 50%)
-
#
-
# and it will call {Sass::Script::Functions#hsl}.
-
#
-
# The following functions are provided:
-
#
-
# *Note: These functions are described in more detail below.*
-
#
-
# ## RGB Functions
-
#
-
# \{#rgb rgb($red, $green, $blue)}
-
# : Creates a {Color} from red, green, and blue values.
-
#
-
# \{#rgba rgba($red, $green, $blue, $alpha)}
-
# : Creates a {Color} from red, green, blue, and alpha values.
-
#
-
# \{#red red($color)}
-
# : Gets the red component of a color.
-
#
-
# \{#green green($color)}
-
# : Gets the green component of a color.
-
#
-
# \{#blue blue($color)}
-
# : Gets the blue component of a color.
-
#
-
# \{#mix mix($color-1, $color-2, \[$weight\])}
-
# : Mixes two colors together.
-
#
-
# ## HSL Functions
-
#
-
# \{#hsl hsl($hue, $saturation, $lightness)}
-
# : Creates a {Color} from hue, saturation, and lightness values.
-
#
-
# \{#hsla hsla($hue, $saturation, $lightness, $alpha)}
-
# : Creates a {Color} from hue, saturation, lightness, and alpha
-
# values.
-
#
-
# \{#hue hue($color)}
-
# : Gets the hue component of a color.
-
#
-
# \{#saturation saturation($color)}
-
# : Gets the saturation component of a color.
-
#
-
# \{#lightness lightness($color)}
-
# : Gets the lightness component of a color.
-
#
-
# \{#adjust_hue adjust-hue($color, $degrees)}
-
# : Changes the hue of a color.
-
#
-
# \{#lighten lighten($color, $amount)}
-
# : Makes a color lighter.
-
#
-
# \{#darken darken($color, $amount)}
-
# : Makes a color darker.
-
#
-
# \{#saturate saturate($color, $amount)}
-
# : Makes a color more saturated.
-
#
-
# \{#desaturate desaturate($color, $amount)}
-
# : Makes a color less saturated.
-
#
-
# \{#grayscale grayscale($color)}
-
# : Converts a color to grayscale.
-
#
-
# \{#complement complement($color)}
-
# : Returns the complement of a color.
-
#
-
# \{#invert invert($color)}
-
# : Returns the inverse of a color.
-
#
-
# ## Opacity Functions
-
#
-
# \{#alpha alpha($color)} / \{#opacity opacity($color)}
-
# : Gets the alpha component (opacity) of a color.
-
#
-
# \{#rgba rgba($color, $alpha)}
-
# : Changes the alpha component for a color.
-
#
-
# \{#opacify opacify($color, $amount)} / \{#fade_in fade-in($color, $amount)}
-
# : Makes a color more opaque.
-
#
-
# \{#transparentize transparentize($color, $amount)} / \{#fade_out fade-out($color, $amount)}
-
# : Makes a color more transparent.
-
#
-
# ## Other Color Functions
-
#
-
# \{#adjust_color adjust-color($color, \[$red\], \[$green\], \[$blue\], \[$hue\], \[$saturation\], \[$lightness\], \[$alpha\])}
-
# : Increases or decreases one or more components of a color.
-
#
-
# \{#scale_color scale-color($color, \[$red\], \[$green\], \[$blue\], \[$saturation\], \[$lightness\], \[$alpha\])}
-
# : Fluidly scales one or more properties of a color.
-
#
-
# \{#change_color change-color($color, \[$red\], \[$green\], \[$blue\], \[$hue\], \[$saturation\], \[$lightness\], \[$alpha\])}
-
# : Changes one or more properties of a color.
-
#
-
# \{#ie_hex_str ie-hex-str($color)}
-
# : Converts a color into the format understood by IE filters.
-
#
-
# ## String Functions
-
#
-
# \{#unquote unquote($string)}
-
# : Removes quotes from a string.
-
#
-
# \{#quote quote($string)}
-
# : Adds quotes to a string.
-
#
-
# ## Number Functions
-
#
-
# \{#percentage percentage($value)}
-
# : Converts a unitless number to a percentage.
-
#
-
# \{#round round($value)}
-
# : Rounds a number to the nearest whole number.
-
#
-
# \{#ceil ceil($value)}
-
# : Rounds a number up to the next whole number.
-
#
-
# \{#floor floor($value)}
-
# : Rounds a number down to the previous whole number.
-
#
-
# \{#abs abs($value)}
-
# : Returns the absolute value of a number.
-
#
-
# \{#min min($numbers...)\}
-
# : Finds the minimum of several numbers.
-
#
-
# \{#max max($numbers...)\}
-
# : Finds the maximum of several numbers.
-
#
-
# ## List Functions {#list-functions}
-
#
-
# \{#length length($list)}
-
# : Returns the length of a list.
-
#
-
# \{#nth nth($list, $n)}
-
# : Returns a specific item in a list.
-
#
-
# \{#join join($list1, $list2, \[$separator\])}
-
# : Joins together two lists into one.
-
#
-
# \{#append append($list1, $val, \[$separator\])}
-
# : Appends a single value onto the end of a list.
-
#
-
# \{#zip zip($lists...)}
-
# : Combines several lists into a single multidimensional list.
-
#
-
# \{#index index($list, $value)}
-
# : Returns the position of a value within a list.
-
#
-
# ## Introspection Functions
-
#
-
# \{#type_of type-of($value)}
-
# : Returns the type of a value.
-
#
-
# \{#unit unit($number)}
-
# : Returns the unit(s) associated with a number.
-
#
-
# \{#unitless unitless($number)}
-
# : Returns whether a number has units.
-
#
-
# \{#comparable comparable($number-1, $number-2)}
-
# : Returns whether two numbers can be added, subtracted, or compared.
-
#
-
# ## Miscellaneous Functions
-
#
-
# \{#if if($condition, $if-true, $if-false)}
-
# : Returns one of two values, depending on whether or not `$condition` is
-
# true.
-
#
-
# ## Adding Custom Functions
-
#
-
# New Sass functions can be added by adding Ruby methods to this module.
-
# For example:
-
#
-
# module Sass::Script::Functions
-
# def reverse(string)
-
# assert_type string, :String
-
# Sass::Script::String.new(string.value.reverse)
-
# end
-
# declare :reverse, :args => [:string]
-
# end
-
#
-
# Calling {declare} tells Sass the argument names for your function.
-
# If omitted, the function will still work, but will not be able to accept keyword arguments.
-
# {declare} can also allow your function to take arbitrary keyword arguments.
-
#
-
# There are a few things to keep in mind when modifying this module.
-
# First of all, the arguments passed are {Sass::Script::Literal} objects.
-
# Literal objects are also expected to be returned.
-
# This means that Ruby values must be unwrapped and wrapped.
-
#
-
# Most Literal objects support the {Sass::Script::Literal#value value} accessor
-
# for getting their Ruby values.
-
# Color objects, though, must be accessed using {Sass::Script::Color#rgb rgb},
-
# {Sass::Script::Color#red red}, {Sass::Script::Color#blue green}, or {Sass::Script::Color#blue blue}.
-
#
-
# Second, making Ruby functions accessible from Sass introduces the temptation
-
# to do things like database access within stylesheets.
-
# This is generally a bad idea;
-
# since Sass files are by default only compiled once,
-
# dynamic code is not a great fit.
-
#
-
# If you really, really need to compile Sass on each request,
-
# first make sure you have adequate caching set up.
-
# Then you can use {Sass::Engine} to render the code,
-
# using the {file:SASS_REFERENCE.md#custom-option `options` parameter}
-
# to pass in data that {EvaluationContext#options can be accessed}
-
# from your Sass functions.
-
#
-
# Within one of the functions in this module,
-
# methods of {EvaluationContext} can be used.
-
#
-
# ### Caveats
-
#
-
# When creating new {Literal} objects within functions,
-
# be aware that it's not safe to call {Literal#to_s #to_s}
-
# (or other methods that use the string representation)
-
# on those objects without first setting {Node#options= the #options attribute}.
-
1
module Functions
-
1
@signatures = {}
-
-
# A class representing a Sass function signature.
-
#
-
# @attr args [Array<Symbol>] The names of the arguments to the function.
-
# @attr var_args [Boolean] Whether the function takes a variable number of arguments.
-
# @attr var_kwargs [Boolean] Whether the function takes an arbitrary set of keyword arguments.
-
1
Signature = Struct.new(:args, :var_args, :var_kwargs)
-
-
# Declare a Sass signature for a Ruby-defined function.
-
# This includes the names of the arguments,
-
# whether the function takes a variable number of arguments,
-
# and whether the function takes an arbitrary set of keyword arguments.
-
#
-
# It's not necessary to declare a signature for a function.
-
# However, without a signature it won't support keyword arguments.
-
#
-
# A single function can have multiple signatures declared
-
# as long as each one takes a different number of arguments.
-
# It's also possible to declare multiple signatures
-
# that all take the same number of arguments,
-
# but none of them but the first will be used
-
# unless the user uses keyword arguments.
-
#
-
# @example
-
# declare :rgba, [:hex, :alpha]
-
# declare :rgba, [:red, :green, :blue, :alpha]
-
# declare :accepts_anything, [], :var_args => true, :var_kwargs => true
-
# declare :some_func, [:foo, :bar, :baz], :var_kwargs => true
-
#
-
# @param method_name [Symbol] The name of the method
-
# whose signature is being declared.
-
# @param args [Array<Symbol>] The names of the arguments for the function signature.
-
# @option options :var_args [Boolean] (false)
-
# Whether the function accepts a variable number of (unnamed) arguments
-
# in addition to the named arguments.
-
# @option options :var_kwargs [Boolean] (false)
-
# Whether the function accepts other keyword arguments
-
# in addition to those in `:args`.
-
# If this is true, the Ruby function will be passed a hash from strings
-
# to {Sass::Script::Literal}s as the last argument.
-
# In addition, if this is true and `:var_args` is not,
-
# Sass will ensure that the last argument passed is a hash.
-
1
def self.declare(method_name, args, options = {})
-
56
@signatures[method_name] ||= []
-
@signatures[method_name] << Signature.new(
-
85
args.map {|s| s.to_s},
-
options[:var_args],
-
56
options[:var_kwargs])
-
end
-
-
# Determine the correct signature for the number of arguments
-
# passed in for a given function.
-
# If no signatures match, the first signature is returned for error messaging.
-
#
-
# @param method_name [Symbol] The name of the Ruby function to be called.
-
# @param arg_arity [Number] The number of unnamed arguments the function was passed.
-
# @param kwarg_arity [Number] The number of keyword arguments the function was passed.
-
#
-
# @return [{Symbol => Object}, nil]
-
# The signature options for the matching signature,
-
# or nil if no signatures are declared for this function. See {declare}.
-
1
def self.signature(method_name, arg_arity, kwarg_arity)
-
return unless @signatures[method_name]
-
@signatures[method_name].each do |signature|
-
return signature if signature.args.size == arg_arity + kwarg_arity
-
next unless signature.args.size < arg_arity + kwarg_arity
-
-
# We have enough args.
-
# Now we need to figure out which args are varargs
-
# and if the signature allows them.
-
t_arg_arity, t_kwarg_arity = arg_arity, kwarg_arity
-
if signature.args.size > t_arg_arity
-
# we transfer some kwargs arity to args arity
-
# if it does not have enough args -- assuming the names will work out.
-
t_kwarg_arity -= (signature.args.size - t_arg_arity)
-
t_arg_arity = signature.args.size
-
end
-
-
if ( t_arg_arity == signature.args.size || t_arg_arity > signature.args.size && signature.var_args ) &&
-
(t_kwarg_arity == 0 || t_kwarg_arity > 0 && signature.var_kwargs)
-
return signature
-
end
-
end
-
@signatures[method_name].first
-
end
-
-
# The context in which methods in {Script::Functions} are evaluated.
-
# That means that all instance methods of {EvaluationContext}
-
# are available to use in functions.
-
1
class EvaluationContext
-
1
include Functions
-
-
# The options hash for the {Sass::Engine} that is processing the function call
-
#
-
# @return [{Symbol => Object}]
-
1
attr_reader :options
-
-
# @param options [{Symbol => Object}] See \{#options}
-
1
def initialize(options)
-
@options = options
-
end
-
-
# Asserts that the type of a given SassScript value
-
# is the expected type (designated by a symbol).
-
#
-
# Valid types are `:Bool`, `:Color`, `:Number`, and `:String`.
-
# Note that `:String` will match both double-quoted strings
-
# and unquoted identifiers.
-
#
-
# @example
-
# assert_type value, :String
-
# assert_type value, :Number
-
# @param value [Sass::Script::Literal] A SassScript value
-
# @param type [Symbol] The name of the type the value is expected to be
-
# @param name [String, Symbol, nil] The name of the argument.
-
1
def assert_type(value, type, name = nil)
-
return if value.is_a?(Sass::Script.const_get(type))
-
err = "#{value.inspect} is not a #{type.to_s.downcase}"
-
err = "$#{name.to_s.gsub('_', '-')}: " + err if name
-
raise ArgumentError.new(err)
-
end
-
end
-
-
1
class << self
-
# Returns whether user function with a given name exists.
-
#
-
# @param function_name [String]
-
# @return [Boolean]
-
1
alias_method :callable?, :public_method_defined?
-
-
1
private
-
1
def include(*args)
-
r = super
-
# We have to re-include ourselves into EvaluationContext to work around
-
# an icky Ruby restriction.
-
EvaluationContext.send :include, self
-
r
-
end
-
end
-
-
# Creates a {Color} object from red, green, and blue values.
-
#
-
# @see #rgba
-
# @overload rgb($red, $green, $blue)
-
# @param $red [Number] The amount of red in the color. Must be between 0 and
-
# 255 inclusive, or between `0%` and `100%` inclusive
-
# @param $green [Number] The amount of green in the color. Must be between 0
-
# and 255 inclusive, or between `0%` and `100%` inclusive
-
# @param $blue [Number] The amount of blue in the color. Must be between 0
-
# and 255 inclusive, or between `0%` and `100%` inclusive
-
# @return [Color]
-
# @raise [ArgumentError] if any parameter is the wrong type or out of bounds
-
1
def rgb(red, green, blue)
-
assert_type red, :Number, :red
-
assert_type green, :Number, :green
-
assert_type blue, :Number, :blue
-
-
Color.new([[red, :red], [green, :green], [blue, :blue]].map do |(c, name)|
-
v = c.value
-
if c.numerator_units == ["%"] && c.denominator_units.empty?
-
v = Sass::Util.check_range("$#{name}: Color value", 0..100, c, '%')
-
v * 255 / 100.0
-
else
-
Sass::Util.check_range("$#{name}: Color value", 0..255, c)
-
end
-
end)
-
end
-
1
declare :rgb, [:red, :green, :blue]
-
-
# Creates a {Color} from red, green, blue, and alpha values.
-
# @see #rgb
-
#
-
# @overload rgba($red, $green, $blue, $alpha)
-
# @param $red [Number] The amount of red in the color. Must be between 0
-
# and 255 inclusive
-
# @param $green [Number] The amount of green in the color. Must be between
-
# 0 and 255 inclusive
-
# @param $blue [Number] The amount of blue in the color. Must be between 0
-
# and 255 inclusive
-
# @param $alpha [Number] The opacity of the color. Must be between 0 and 1
-
# inclusive
-
# @return [Color]
-
# @raise [ArgumentError] if any parameter is the wrong type or out of
-
# bounds
-
#
-
# @overload rgba($color, $alpha)
-
# Sets the opacity of an existing color.
-
#
-
# @example
-
# rgba(#102030, 0.5) => rgba(16, 32, 48, 0.5)
-
# rgba(blue, 0.2) => rgba(0, 0, 255, 0.2)
-
#
-
# @param $color [Color] The color whose opacity will be changed.
-
# @param $alpha [Number] The new opacity of the color. Must be between 0
-
# and 1 inclusive
-
# @return [Color]
-
# @raise [ArgumentError] if `$alpha` is out of bounds or either parameter
-
# is the wrong type
-
1
def rgba(*args)
-
case args.size
-
when 2
-
color, alpha = args
-
-
assert_type color, :Color, :color
-
assert_type alpha, :Number, :alpha
-
-
Sass::Util.check_range('Alpha channel', 0..1, alpha)
-
color.with(:alpha => alpha.value)
-
when 4
-
red, green, blue, alpha = args
-
rgba(rgb(red, green, blue), alpha)
-
else
-
raise ArgumentError.new("wrong number of arguments (#{args.size} for 4)")
-
end
-
end
-
1
declare :rgba, [:red, :green, :blue, :alpha]
-
1
declare :rgba, [:color, :alpha]
-
-
# Creates a {Color} from hue, saturation, and lightness values. Uses the
-
# algorithm from the [CSS3 spec][].
-
#
-
# [CSS3 spec]: http://www.w3.org/TR/css3-color/#hsl-color
-
#
-
# @see #hsla
-
# @overload hsl($hue, $saturation, $lightness)
-
# @param $hue [Number] The hue of the color. Should be between 0 and 360
-
# degrees, inclusive
-
# @param $saturation [Number] The saturation of the color. Must be between
-
# `0%` and `100%`, inclusive
-
# @param $lightness [Number] The lightness of the color. Must be between
-
# `0%` and `100%`, inclusive
-
# @return [Color]
-
# @raise [ArgumentError] if `$saturation` or `$lightness` are out of bounds
-
# or any parameter is the wrong type
-
1
def hsl(hue, saturation, lightness)
-
hsla(hue, saturation, lightness, Number.new(1))
-
end
-
1
declare :hsl, [:hue, :saturation, :lightness]
-
-
# Creates a {Color} from hue, saturation, lightness, and alpha
-
# values. Uses the algorithm from the [CSS3 spec][].
-
#
-
# [CSS3 spec]: http://www.w3.org/TR/css3-color/#hsl-color
-
#
-
# @see #hsl
-
# @overload hsla($hue, $saturation, $lightness, $alpha)
-
# @param $hue [Number] The hue of the color. Should be between 0 and 360
-
# degrees, inclusive
-
# @param $saturation [Number] The saturation of the color. Must be between
-
# `0%` and `100%`, inclusive
-
# @param $lightness [Number] The lightness of the color. Must be between
-
# `0%` and `100%`, inclusive
-
# @param $alpha [Number] The opacity of the color. Must be between 0 and 1,
-
# inclusive
-
# @return [Color]
-
# @raise [ArgumentError] if `$saturation`, `$lightness`, or `$alpha` are out
-
# of bounds or any parameter is the wrong type
-
1
def hsla(hue, saturation, lightness, alpha)
-
assert_type hue, :Number, :hue
-
assert_type saturation, :Number, :saturation
-
assert_type lightness, :Number, :lightness
-
assert_type alpha, :Number, :alpha
-
-
Sass::Util.check_range('Alpha channel', 0..1, alpha)
-
-
h = hue.value
-
s = Sass::Util.check_range('Saturation', 0..100, saturation, '%')
-
l = Sass::Util.check_range('Lightness', 0..100, lightness, '%')
-
-
Color.new(:hue => h, :saturation => s, :lightness => l, :alpha => alpha.value)
-
end
-
1
declare :hsla, [:hue, :saturation, :lightness, :alpha]
-
-
# Gets the red component of a color. Calculated from HSL where necessary via
-
# [this algorithm][hsl-to-rgb].
-
#
-
# [hsl-to-rgb]: http://www.w3.org/TR/css3-color/#hsl-color
-
#
-
# @overload red($color)
-
# @param $color [Color]
-
# @return [Number] The red component, between 0 and 255 inclusive
-
# @raise [ArgumentError] if `$color` isn't a color
-
1
def red(color)
-
assert_type color, :Color, :color
-
Sass::Script::Number.new(color.red)
-
end
-
1
declare :red, [:color]
-
-
# Gets the green component of a color. Calculated from HSL where necessary
-
# via [this algorithm][hsl-to-rgb].
-
#
-
# [hsl-to-rgb]: http://www.w3.org/TR/css3-color/#hsl-color
-
#
-
# @overload green($color)
-
# @param $color [Color]
-
# @return [Number] The green component, between 0 and 255 inclusive
-
# @raise [ArgumentError] if `$color` isn't a color
-
1
def green(color)
-
assert_type color, :Color, :color
-
Sass::Script::Number.new(color.green)
-
end
-
1
declare :green, [:color]
-
-
# Gets the blue component of a color. Calculated from HSL where necessary
-
# via [this algorithm][hsl-to-rgb].
-
#
-
# [hsl-to-rgb]: http://www.w3.org/TR/css3-color/#hsl-color
-
#
-
# @overload blue($color)
-
# @param $color [Color]
-
# @return [Number] The blue component, between 0 and 255 inclusive
-
# @raise [ArgumentError] if `$color` isn't a color
-
1
def blue(color)
-
assert_type color, :Color, :color
-
Sass::Script::Number.new(color.blue)
-
end
-
1
declare :blue, [:color]
-
-
# Returns the hue component of a color. See [the CSS3 HSL
-
# specification][hsl]. Calculated from RGB where necessary via [this
-
# algorithm][rgb-to-hsl].
-
#
-
# [hsl]: http://en.wikipedia.org/wiki/HSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV
-
# [rgb-to-hsl]: http://en.wikipedia.org/wiki/HSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV
-
#
-
# @overload hue($color)
-
# @param $color [Color]
-
# @return [Number] The hue component, between 0deg and 360deg
-
# @raise [ArgumentError] if `$color` isn't a color
-
1
def hue(color)
-
assert_type color, :Color, :color
-
Sass::Script::Number.new(color.hue, ["deg"])
-
end
-
1
declare :hue, [:color]
-
-
# Returns the saturation component of a color. See [the CSS3 HSL
-
# specification][hsl]. Calculated from RGB where necessary via [this
-
# algorithm][rgb-to-hsl].
-
#
-
# [hsl]: http://en.wikipedia.org/wiki/HSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV
-
# [rgb-to-hsl]: http://en.wikipedia.org/wiki/HSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV
-
#
-
# @overload saturation($color)
-
# @param $color [Color]
-
# @return [Number] The saturation component, between 0% and 100%
-
# @raise [ArgumentError] if `$color` isn't a color
-
1
def saturation(color)
-
assert_type color, :Color, :color
-
Sass::Script::Number.new(color.saturation, ["%"])
-
end
-
1
declare :saturation, [:color]
-
-
# Returns the lightness component of a color. See [the CSS3 HSL
-
# specification][hsl]. Calculated from RGB where necessary via [this
-
# algorithm][rgb-to-hsl].
-
#
-
# [hsl]: http://en.wikipedia.org/wiki/HSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV
-
# [rgb-to-hsl]: http://en.wikipedia.org/wiki/HSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV
-
#
-
# @overload lightness($color)
-
# @param $color [Color]
-
# @return [Number] The lightness component, between 0% and 100%
-
# @raise [ArgumentError] if `$color` isn't a color
-
1
def lightness(color)
-
assert_type color, :Color, :color
-
Sass::Script::Number.new(color.lightness, ["%"])
-
end
-
1
declare :lightness, [:color]
-
-
# Returns the alpha component (opacity) of a color. This is 1 unless
-
# otherwise specified.
-
#
-
# This function also supports the proprietary Microsoft `alpha(opacity=20)`
-
# syntax as a special case.
-
#
-
# @overload alpha($color)
-
# @param $color [Color]
-
# @return [Number] The alpha component, between 0 and 1
-
# @raise [ArgumentError] if `$color` isn't a color
-
1
def alpha(*args)
-
if args.all? do |a|
-
a.is_a?(Sass::Script::String) && a.type == :identifier &&
-
a.value =~ /^[a-zA-Z]+\s*=/
-
end
-
# Support the proprietary MS alpha() function
-
return Sass::Script::String.new("alpha(#{args.map {|a| a.to_s}.join(", ")})")
-
end
-
-
raise ArgumentError.new("wrong number of arguments (#{args.size} for 1)") if args.size != 1
-
-
assert_type args.first, :Color, :color
-
Sass::Script::Number.new(args.first.alpha)
-
end
-
1
declare :alpha, [:color]
-
-
# Returns the alpha component (opacity) of a color. This is 1 unless
-
# otherwise specified.
-
#
-
# @overload opacity($color)
-
# @param $color [Color]
-
# @return [Number] The alpha component, between 0 and 1
-
# @raise [ArgumentError] if `$color` isn't a color
-
1
def opacity(color)
-
return Sass::Script::String.new("opacity(#{color})") if color.is_a?(Sass::Script::Number)
-
assert_type color, :Color, :color
-
Sass::Script::Number.new(color.alpha)
-
end
-
1
declare :opacity, [:color]
-
-
# Makes a color more opaque. Takes a color and a number between 0 and 1, and
-
# returns a color with the opacity increased by that amount.
-
#
-
# @see #transparentize
-
# @example
-
# opacify(rgba(0, 0, 0, 0.5), 0.1) => rgba(0, 0, 0, 0.6)
-
# opacify(rgba(0, 0, 17, 0.8), 0.2) => #001
-
# @overload opacify($color, $amount)
-
# @param $color [Color]
-
# @param $amount [Number] The amount to increase the opacity by, between 0
-
# and 1
-
# @return [Color]
-
# @raise [ArgumentError] if `$amount` is out of bounds, or either parameter
-
# is the wrong type
-
1
def opacify(color, amount)
-
_adjust(color, amount, :alpha, 0..1, :+)
-
end
-
1
declare :opacify, [:color, :amount]
-
-
1
alias_method :fade_in, :opacify
-
1
declare :fade_in, [:color, :amount]
-
-
# Makes a color more transparent. Takes a color and a number between 0 and
-
# 1, and returns a color with the opacity decreased by that amount.
-
#
-
# @see #opacify
-
# @example
-
# transparentize(rgba(0, 0, 0, 0.5), 0.1) => rgba(0, 0, 0, 0.4)
-
# transparentize(rgba(0, 0, 0, 0.8), 0.2) => rgba(0, 0, 0, 0.6)
-
# @overload transparentize($color, $amount)
-
# @param $color [Color]
-
# @param $amount [Number] The amount to decrease the opacity by, between 0
-
# and 1
-
# @return [Color]
-
# @raise [ArgumentError] if `$amount` is out of bounds, or either parameter
-
# is the wrong type
-
1
def transparentize(color, amount)
-
_adjust(color, amount, :alpha, 0..1, :-)
-
end
-
1
declare :transparentize, [:color, :amount]
-
-
1
alias_method :fade_out, :transparentize
-
1
declare :fade_out, [:color, :amount]
-
-
# Makes a color lighter. Takes a color and a number between `0%` and `100%`,
-
# and returns a color with the lightness increased by that amount.
-
#
-
# @see #darken
-
# @example
-
# lighten(hsl(0, 0%, 0%), 30%) => hsl(0, 0, 30)
-
# lighten(#800, 20%) => #e00
-
# @overload lighten($color, $amount)
-
# @param $color [Color]
-
# @param $amount [Number] The amount to increase the lightness by, between
-
# `0%` and `100%`
-
# @return [Color]
-
# @raise [ArgumentError] if `$amount` is out of bounds, or either parameter
-
# is the wrong type
-
1
def lighten(color, amount)
-
_adjust(color, amount, :lightness, 0..100, :+, "%")
-
end
-
1
declare :lighten, [:color, :amount]
-
-
# Makes a color darker. Takes a color and a number between 0% and 100%, and
-
# returns a color with the lightness decreased by that amount.
-
#
-
# @see #lighten
-
# @example
-
# darken(hsl(25, 100%, 80%), 30%) => hsl(25, 100%, 50%)
-
# darken(#800, 20%) => #200
-
# @overload darken($color, $amount)
-
# @param $color [Color]
-
# @param $amount [Number] The amount to dencrease the lightness by, between
-
# `0%` and `100%`
-
# @return [Color]
-
# @raise [ArgumentError] if `$amount` is out of bounds, or either parameter
-
# is the wrong type
-
1
def darken(color, amount)
-
_adjust(color, amount, :lightness, 0..100, :-, "%")
-
end
-
1
declare :darken, [:color, :amount]
-
-
# Makes a color more saturated. Takes a color and a number between 0% and
-
# 100%, and returns a color with the saturation increased by that amount.
-
#
-
# @see #desaturate
-
# @example
-
# saturate(hsl(120, 30%, 90%), 20%) => hsl(120, 50%, 90%)
-
# saturate(#855, 20%) => #9e3f3f
-
# @overload saturate($color, $amount)
-
# @param $color [Color]
-
# @param $amount [Number] The amount to increase the saturation by, between
-
# `0%` and `100%`
-
# @return [Color]
-
# @raise [ArgumentError] if `$amount` is out of bounds, or either parameter
-
# is the wrong type
-
1
def saturate(color, amount = nil)
-
# Support the filter effects definition of saturate.
-
# https://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html
-
return Sass::Script::String.new("saturate(#{color})") if amount.nil?
-
_adjust(color, amount, :saturation, 0..100, :+, "%")
-
end
-
1
declare :saturate, [:color, :amount]
-
1
declare :saturate, [:amount]
-
-
# Makes a color less saturated. Takes a color and a number between 0% and
-
# 100%, and returns a color with the saturation decreased by that value.
-
#
-
# @see #saturate
-
# @example
-
# desaturate(hsl(120, 30%, 90%), 20%) => hsl(120, 10%, 90%)
-
# desaturate(#855, 20%) => #726b6b
-
# @overload desaturate($color, $amount)
-
# @param $color [Color]
-
# @param $amount [Number] The amount to decrease the saturation by, between
-
# `0%` and `100%`
-
# @return [Color]
-
# @raise [ArgumentError] if `$amount` is out of bounds, or either parameter
-
# is the wrong type
-
1
def desaturate(color, amount)
-
_adjust(color, amount, :saturation, 0..100, :-, "%")
-
end
-
1
declare :desaturate, [:color, :amount]
-
-
# Changes the hue of a color. Takes a color and a number of degrees (usually
-
# between `-360deg` and `360deg`), and returns a color with the hue rotated
-
# along the color wheel by that amount.
-
#
-
# @example
-
# adjust-hue(hsl(120, 30%, 90%), 60deg) => hsl(180, 30%, 90%)
-
# adjust-hue(hsl(120, 30%, 90%), 060deg) => hsl(60, 30%, 90%)
-
# adjust-hue(#811, 45deg) => #886a11
-
# @overload adjust_hue($color, $degrees)
-
# @param $color [Color]
-
# @param $degrees [Number] The number of degrees to rotate the hue
-
# @return [Color]
-
# @raise [ArgumentError] if either parameter is the wrong type
-
1
def adjust_hue(color, degrees)
-
assert_type color, :Color, :color
-
assert_type degrees, :Number, :degrees
-
color.with(:hue => color.hue + degrees.value)
-
end
-
1
declare :adjust_hue, [:color, :degrees]
-
-
# Converts a color into the format understood by IE filters.
-
#
-
# @example
-
# ie-hex-str(#abc) => #FFAABBCC
-
# ie-hex-str(#3322BB) => #FF3322BB
-
# ie-hex-str(rgba(0, 255, 0, 0.5)) => #8000FF00
-
# @overload ie_hex_str($color)
-
# @param $color [Color]
-
# @return [String] The IE-formatted string representation of the color
-
# @raise [ArgumentError] if `$color` isn't a color
-
1
def ie_hex_str(color)
-
assert_type color, :Color, :color
-
alpha = (color.alpha * 255).round.to_s(16).rjust(2, '0')
-
Sass::Script::String.new("##{alpha}#{color.send(:hex_str)[1..-1]}".upcase)
-
end
-
1
declare :ie_hex_str, [:color]
-
-
# Increases or decreases one or more properties of a color. This can change
-
# the red, green, blue, hue, saturation, value, and alpha properties. The
-
# properties are specified as keyword arguments, and are added to or
-
# subtracted from the color's current value for that property.
-
#
-
# All properties are optional. You can't specify both RGB properties
-
# (`$red`, `$green`, `$blue`) and HSL properties (`$hue`, `$saturation`,
-
# `$value`) at the same time.
-
#
-
# @example
-
# adjust-color(#102030, $blue: 5) => #102035
-
# adjust-color(#102030, $red: -5, $blue: 5) => #0b2035
-
# adjust-color(hsl(25, 100%, 80%), $lightness: -30%, $alpha: -0.4) => hsla(25, 100%, 50%, 0.6)
-
# @overload adjust_color($color, [$red], [$green], [$blue], [$hue], [$saturation], [$lightness], [$alpha])
-
# @param $color [Color]
-
# @param $red [Number] The adjustment to make on the red component, between
-
# -255 and 255 inclusive
-
# @param $green [Number] The adjustment to make on the green component,
-
# between -255 and 255 inclusive
-
# @param $blue [Number] The adjustment to make on the blue component, between
-
# -255 and 255 inclusive
-
# @param $hue [Number] The adjustment to make on the hue component, in
-
# degrees
-
# @param $saturation [Number] The adjustment to make on the saturation
-
# component, between `-100%` and `100%` inclusive
-
# @param $lightness [Number] The adjustment to make on the lightness
-
# component, between `-100%` and `100%` inclusive
-
# @param $alpha [Number] The adjustment to make on the alpha component,
-
# between -1 and 1 inclusive
-
# @return [Color]
-
# @raise [ArgumentError] if any parameter is the wrong type or out-of
-
# bounds, or if RGB properties and HSL properties are adjusted at the
-
# same time
-
1
def adjust_color(color, kwargs)
-
assert_type color, :Color, :color
-
with = Sass::Util.map_hash({
-
"red" => [-255..255, ""],
-
"green" => [-255..255, ""],
-
"blue" => [-255..255, ""],
-
"hue" => nil,
-
"saturation" => [-100..100, "%"],
-
"lightness" => [-100..100, "%"],
-
"alpha" => [-1..1, ""]
-
}) do |name, (range, units)|
-
-
next unless val = kwargs.delete(name)
-
assert_type val, :Number, name
-
Sass::Util.check_range("$#{name}: Amount", range, val, units) if range
-
adjusted = color.send(name) + val.value
-
adjusted = [0, Sass::Util.restrict(adjusted, range)].max if range
-
[name.to_sym, adjusted]
-
end
-
-
unless kwargs.empty?
-
name, val = kwargs.to_a.first
-
raise ArgumentError.new("Unknown argument $#{name} (#{val})")
-
end
-
-
color.with(with)
-
end
-
1
declare :adjust_color, [:color], :var_kwargs => true
-
-
# Fluidly scales one or more properties of a color. Unlike
-
# \{#adjust_color adjust-color}, which changes a color's properties by fixed
-
# amounts, \{#scale_color scale-color} fluidly changes them based on how
-
# high or low they already are. That means that lightening an already-light
-
# color with \{#scale_color scale-color} won't change the lightness much,
-
# but lightening a dark color by the same amount will change it more
-
# dramatically. This has the benefit of making `scale-color($color, ...)`
-
# have a similar effect regardless of what `$color` is.
-
#
-
# For example, the lightness of a color can be anywhere between `0%` and
-
# `100%`. If `scale-color($color, $lightness: 40%)` is called, the resulting
-
# color's lightness will be 40% of the way between its original lightness
-
# and 100. If `scale-color($color, $lightness: -40%)` is called instead, the
-
# lightness will be 40% of the way between the original and 0.
-
#
-
# This can change the red, green, blue, saturation, value, and alpha
-
# properties. The properties are specified as keyword arguments. All
-
# arguments should be percentages between `0%` and `100%`.
-
#
-
# All properties are optional. You can't specify both RGB properties
-
# (`$red`, `$green`, `$blue`) and HSL properties (`$saturation`, `$value`)
-
# at the same time.
-
#
-
# @example
-
# scale-color(hsl(120, 70%, 80%), $lightness: 50%) => hsl(120, 70%, 90%)
-
# scale-color(rgb(200, 150%, 170%), $green: -40%, $blue: 70%) => rgb(200, 90, 229)
-
# scale-color(hsl(200, 70%, 80%), $saturation: -90%, $alpha: -30%) => hsla(200, 7%, 80%, 0.7)
-
# @overload scale_color($color, [$red], [$green], [$blue], [$saturation], [$lightness], [$alpha])
-
# @param $color [Color]
-
# @param $red [Number]
-
# @param $green [Number]
-
# @param $blue [Number]
-
# @param $saturation [Number]
-
# @param $lightness [Number]
-
# @param $alpha [Number]
-
# @return [Color]
-
# @raise [ArgumentError] if any parameter is the wrong type or out-of
-
# bounds, or if RGB properties and HSL properties are adjusted at the
-
# same time
-
1
def scale_color(color, kwargs)
-
assert_type color, :Color, :color
-
with = Sass::Util.map_hash({
-
"red" => 255,
-
"green" => 255,
-
"blue" => 255,
-
"saturation" => 100,
-
"lightness" => 100,
-
"alpha" => 1
-
}) do |name, max|
-
-
next unless val = kwargs.delete(name)
-
assert_type val, :Number, name
-
if !(val.numerator_units == ['%'] && val.denominator_units.empty?)
-
raise ArgumentError.new("$#{name}: Amount #{val} must be a % (e.g. #{val.value}%)")
-
else
-
Sass::Util.check_range("$#{name}: Amount", -100..100, val, '%')
-
end
-
-
current = color.send(name)
-
scale = val.value/100.0
-
diff = scale > 0 ? max - current : current
-
[name.to_sym, current + diff*scale]
-
end
-
-
unless kwargs.empty?
-
name, val = kwargs.to_a.first
-
raise ArgumentError.new("Unknown argument $#{name} (#{val})")
-
end
-
-
color.with(with)
-
end
-
1
declare :scale_color, [:color], :var_kwargs => true
-
-
# Changes one or more properties of a color. This can change the red, green,
-
# blue, hue, saturation, value, and alpha properties. The properties are
-
# specified as keyword arguments, and replace the color's current value for
-
# that property.
-
#
-
# All properties are optional. You can't specify both RGB properties
-
# (`$red`, `$green`, `$blue`) and HSL properties (`$hue`, `$saturation`,
-
# `$value`) at the same time.
-
#
-
# @example
-
# change-color(#102030, $blue: 5) => #102005
-
# change-color(#102030, $red: 120, $blue: 5) => #782005
-
# change-color(hsl(25, 100%, 80%), $lightness: 40%, $alpha: 0.8) => hsla(25, 100%, 40%, 0.8)
-
# @overload change_color($color, [$red], [$green], [$blue], [$hue], [$saturation], [$lightness], [$alpha])
-
# @param $color [Color]
-
# @param $red [Number] The new red component for the color, within 0 and 255
-
# inclusive
-
# @param $green [Number] The new green component for the color, within 0 and
-
# 255 inclusive
-
# @param $blue [Number] The new blue component for the color, within 0 and
-
# 255 inclusive
-
# @param $hue [Number] The new hue component for the color, in degrees
-
# @param $saturation [Number] The new saturation component for the color,
-
# between `0%` and `100%` inclusive
-
# @param $lightness [Number] The new lightness component for the color,
-
# within `0%` and `100%` inclusive
-
# @param $alpha [Number] The new alpha component for the color, within 0 and
-
# 1 inclusive
-
# @return [Color]
-
# @raise [ArgumentError] if any parameter is the wrong type or out-of
-
# bounds, or if RGB properties and HSL properties are adjusted at the
-
# same time
-
1
def change_color(color, kwargs)
-
assert_type color, :Color, :color
-
with = Sass::Util.map_hash(%w[red green blue hue saturation lightness alpha]) do |name, max|
-
next unless val = kwargs.delete(name)
-
assert_type val, :Number, name
-
[name.to_sym, val.value]
-
end
-
-
unless kwargs.empty?
-
name, val = kwargs.to_a.first
-
raise ArgumentError.new("Unknown argument $#{name} (#{val})")
-
end
-
-
color.with(with)
-
end
-
1
declare :change_color, [:color], :var_kwargs => true
-
-
# Mixes two colors together. Specifically, takes the average of each of the
-
# RGB components, optionally weighted by the given percentage. The opacity
-
# of the colors is also considered when weighting the components.
-
#
-
# The weight specifies the amount of the first color that should be included
-
# in the returned color. The default, `50%`, means that half the first color
-
# and half the second color should be used. `25%` means that a quarter of
-
# the first color and three quarters of the second color should be used.
-
#
-
# @example
-
# mix(#f00, #00f) => #7f007f
-
# mix(#f00, #00f, 25%) => #3f00bf
-
# mix(rgba(255, 0, 0, 0.5), #00f) => rgba(63, 0, 191, 0.75)
-
# @overload mix($color-1, $color-2, $weight: 50%)
-
# @param $color-1 [Color]
-
# @param $color-2 [Color]
-
# @param $weight [Number] The relative weight of each color. Closer to `0%`
-
# gives more weight to `$color`, closer to `100%` gives more weight to
-
# `$color2`
-
# @return [Color]
-
# @raise [ArgumentError] if `$weight` is out of bounds or any parameter is
-
# the wrong type
-
1
def mix(color_1, color_2, weight = Number.new(50))
-
assert_type color_1, :Color, :color_1
-
assert_type color_2, :Color, :color_2
-
assert_type weight, :Number, :weight
-
-
Sass::Util.check_range("Weight", 0..100, weight, '%')
-
-
# This algorithm factors in both the user-provided weight (w) and the
-
# difference between the alpha values of the two colors (a) to decide how
-
# to perform the weighted average of the two RGB values.
-
#
-
# It works by first normalizing both parameters to be within [-1, 1],
-
# where 1 indicates "only use color_1", -1 indicates "only use color_2", and
-
# all values in between indicated a proportionately weighted average.
-
#
-
# Once we have the normalized variables w and a, we apply the formula
-
# (w + a)/(1 + w*a) to get the combined weight (in [-1, 1]) of color_1.
-
# This formula has two especially nice properties:
-
#
-
# * When either w or a are -1 or 1, the combined weight is also that number
-
# (cases where w * a == -1 are undefined, and handled as a special case).
-
#
-
# * When a is 0, the combined weight is w, and vice versa.
-
#
-
# Finally, the weight of color_1 is renormalized to be within [0, 1]
-
# and the weight of color_2 is given by 1 minus the weight of color_1.
-
p = (weight.value/100.0).to_f
-
w = p*2 - 1
-
a = color_1.alpha - color_2.alpha
-
-
w1 = (((w * a == -1) ? w : (w + a)/(1 + w*a)) + 1)/2.0
-
w2 = 1 - w1
-
-
rgb = color_1.rgb.zip(color_2.rgb).map {|v1, v2| v1*w1 + v2*w2}
-
alpha = color_1.alpha*p + color_2.alpha*(1-p)
-
Color.new(rgb + [alpha])
-
end
-
1
declare :mix, [:color_1, :color_2]
-
1
declare :mix, [:color_1, :color_2, :weight]
-
-
# Converts a color to grayscale. This is identical to `desaturate(color,
-
# 100%)`.
-
#
-
# @see #desaturate
-
# @overload grayscale($color)
-
# @param $color [Color]
-
# @return [Color]
-
# @raise [ArgumentError] if `$color` isn't a color
-
1
def grayscale(color)
-
return Sass::Script::String.new("grayscale(#{color})") if color.is_a?(Sass::Script::Number)
-
desaturate color, Number.new(100)
-
end
-
1
declare :grayscale, [:color]
-
-
# Returns the complement of a color. This is identical to `adjust-hue(color,
-
# 180deg)`.
-
#
-
# @see #adjust_hue #adjust-hue
-
# @overload complement($color)
-
# @param $color [Color]
-
# @return [Color]
-
# @raise [ArgumentError] if `$color` isn't a color
-
1
def complement(color)
-
adjust_hue color, Number.new(180)
-
end
-
1
declare :complement, [:color]
-
-
# Returns the inverse (negative) of a color. The red, green, and blue values
-
# are inverted, while the opacity is left alone.
-
#
-
# @overload invert($color)
-
# @param $color [Color]
-
# @return [Color]
-
# @raise [ArgumentError] if `$color` isn't a color
-
1
def invert(color)
-
return Sass::Script::String.new("invert(#{color})") if color.is_a?(Sass::Script::Number)
-
-
assert_type color, :Color, :color
-
color.with(
-
:red => (255 - color.red),
-
:green => (255 - color.green),
-
:blue => (255 - color.blue))
-
end
-
1
declare :invert, [:color]
-
-
# Removes quotes from a string. If the string is already unquoted, this will
-
# return it unmodified.
-
#
-
# @see #quote
-
# @example
-
# unquote("foo") => foo
-
# unquote(foo) => foo
-
# @overload unquote($string)
-
# @param $string [String]
-
# @return [String]
-
# @raise [ArgumentError] if `$string` isn't a string
-
1
def unquote(string)
-
if string.is_a?(Sass::Script::String)
-
Sass::Script::String.new(string.value, :identifier)
-
else
-
string
-
end
-
end
-
1
declare :unquote, [:string]
-
-
# Add quotes to a string if the string isn't quoted,
-
# or returns the same string if it is.
-
#
-
# @see #unquote
-
# @example
-
# quote("foo") => "foo"
-
# quote(foo) => "foo"
-
# @overload quote($string)
-
# @param $string [String]
-
# @return [String]
-
# @raise [ArgumentError] if `$string` isn't a string
-
1
def quote(string)
-
assert_type string, :String, :string
-
Sass::Script::String.new(string.value, :string)
-
end
-
1
declare :quote, [:string]
-
-
# Returns the type of a value.
-
#
-
# @example
-
# type-of(100px) => number
-
# type-of(asdf) => string
-
# type-of("asdf") => string
-
# type-of(true) => bool
-
# type-of(#fff) => color
-
# type-of(blue) => color
-
# @overload type_of($value)
-
# @param $value [Literal] The value to inspect
-
# @return [String] The unquoted string name of the value's type
-
1
def type_of(value)
-
Sass::Script::String.new(value.class.name.gsub(/Sass::Script::/,'').downcase)
-
end
-
1
declare :type_of, [:value]
-
-
# Returns the unit(s) associated with a number. Complex units are sorted in
-
# alphabetical order by numerator and denominator.
-
#
-
# @example
-
# unit(100) => ""
-
# unit(100px) => "px"
-
# unit(3em) => "em"
-
# unit(10px * 5em) => "em*px"
-
# unit(10px * 5em / 30cm / 1rem) => "em*px/cm*rem"
-
# @overload unit($number)
-
# @param $number [Number]
-
# @return [String] The unit(s) of the number, as a quoted string
-
# @raise [ArgumentError] if `$number` isn't a number
-
1
def unit(number)
-
assert_type number, :Number, :number
-
Sass::Script::String.new(number.unit_str, :string)
-
end
-
1
declare :unit, [:number]
-
-
# Returns whether a number has units.
-
#
-
# @example
-
# unitless(100) => true
-
# unitless(100px) => false
-
# @overload unitless($number)
-
# @param $number [Number]
-
# @return [Bool]
-
# @raise [ArgumentError] if `$number` isn't a number
-
1
def unitless(number)
-
assert_type number, :Number, :number
-
Sass::Script::Bool.new(number.unitless?)
-
end
-
1
declare :unitless, [:number]
-
-
# Returns whether two numbers can added, subtracted, or compared.
-
#
-
# @example
-
# comparable(2px, 1px) => true
-
# comparable(100px, 3em) => false
-
# comparable(10cm, 3mm) => true
-
# @overload comparable($number-1, $number-2)
-
# @param $number-1 [Number]
-
# @param $number-2 [Number]
-
# @return [Bool]
-
# @raise [ArgumentError] if either parameter is the wrong type
-
1
def comparable(number_1, number_2)
-
assert_type number_1, :Number, :number_1
-
assert_type number_2, :Number, :number_2
-
Sass::Script::Bool.new(number_1.comparable_to?(number_2))
-
end
-
1
declare :comparable, [:number_1, :number_2]
-
-
# Converts a unitless number to a percentage.
-
#
-
# @example
-
# percentage(0.2) => 20%
-
# percentage(100px / 50px) => 200%
-
# @overload percentage($value)
-
# @param $value [Number]
-
# @return [Number]
-
# @raise [ArgumentError] if `$value` isn't a unitless number
-
1
def percentage(value)
-
unless value.is_a?(Sass::Script::Number) && value.unitless?
-
raise ArgumentError.new("$value: #{value.inspect} is not a unitless number")
-
end
-
Sass::Script::Number.new(value.value * 100, ['%'])
-
end
-
1
declare :percentage, [:value]
-
-
# Rounds a number to the nearest whole number.
-
#
-
# @example
-
# round(10.4px) => 10px
-
# round(10.6px) => 11px
-
# @overload round($value)
-
# @param $value [Number]
-
# @return [Number]
-
# @raise [ArgumentError] if `$value` isn't a number
-
1
def round(value)
-
numeric_transformation(value) {|n| n.round}
-
end
-
1
declare :round, [:value]
-
-
# Rounds a number up to the next whole number.
-
#
-
# @example
-
# ceil(10.4px) => 11px
-
# ceil(10.6px) => 11px
-
# @overload ceil($value)
-
# @param $value [Number]
-
# @return [Number]
-
# @raise [ArgumentError] if `$value` isn't a number
-
1
def ceil(value)
-
numeric_transformation(value) {|n| n.ceil}
-
end
-
1
declare :ceil, [:value]
-
-
# Rounds a number down to the previous whole number.
-
#
-
# @example
-
# floor(10.4px) => 10px
-
# floor(10.6px) => 10px
-
# @overload floor($value)
-
# @param $value [Number]
-
# @return [Number]
-
# @raise [ArgumentError] if `$value` isn't a number
-
1
def floor(value)
-
numeric_transformation(value) {|n| n.floor}
-
end
-
1
declare :floor, [:value]
-
-
# Returns the absolute value of a number.
-
#
-
# @example
-
# abs(10px) => 10px
-
# abs(-10px) => 10px
-
# @overload abs($value)
-
# @param $value [Number]
-
# @return [Number]
-
# @raise [ArgumentError] if `$value` isn't a number
-
1
def abs(value)
-
numeric_transformation(value) {|n| n.abs}
-
end
-
1
declare :abs, [:value]
-
-
# Finds the minimum of several numbers. This function takes any number of
-
# arguments.
-
#
-
# @example
-
# min(1px, 4px) => 1px
-
# min(5em, 3em, 4em) => 3em
-
# @overload min($numbers...)
-
# @param $numbers [[Number]]
-
# @return [Number]
-
# @raise [ArgumentError] if any argument isn't a number, or if not all of
-
# the arguments have comparable units
-
1
def min(*numbers)
-
numbers.each {|n| assert_type n, :Number}
-
numbers.inject {|min, num| min.lt(num).to_bool ? min : num}
-
end
-
1
declare :min, [], :var_args => :true
-
-
# Finds the maximum of several numbers. This function takes any number of
-
# arguments.
-
#
-
# @example
-
# max(1px, 4px) => 4px
-
# max(5em, 3em, 4em) => 5em
-
# @overload max($numbers...)
-
# @param $numbers [[Number]]
-
# @return [Number]
-
# @raise [ArgumentError] if any argument isn't a number, or if not all of
-
# the arguments have comparable units
-
1
def max(*values)
-
values.each {|v| assert_type v, :Number}
-
values.inject {|max, val| max.gt(val).to_bool ? max : val}
-
end
-
1
declare :max, [], :var_args => :true
-
-
# Return the length of a list.
-
#
-
# @example
-
# length(10px) => 1
-
# length(10px 20px 30px) => 3
-
# @overload length($list)
-
# @param $list [Literal]
-
# @return [Number]
-
1
def length(list)
-
Sass::Script::Number.new(list.to_a.size)
-
end
-
1
declare :length, [:list]
-
-
# Gets the nth item in a list.
-
#
-
# Note that unlike some languages, the first item in a Sass list is number
-
# 1, the second number 2, and so forth.
-
#
-
# @example
-
# nth(10px 20px 30px, 1) => 10px
-
# nth((Helvetica, Arial, sans-serif), 3) => sans-serif
-
# @overload nth($list, $n)
-
# @param $list [Literal]
-
# @param $n [Number] The index of the item to get
-
# @return [Literal]
-
# @raise [ArgumentError] if `$n` isn't an integer between 1 and the length
-
# of `$list`
-
1
def nth(list, n)
-
assert_type n, :Number, :n
-
if !n.int?
-
raise ArgumentError.new("List index #{n} must be an integer")
-
elsif n.to_i < 1
-
raise ArgumentError.new("List index #{n} must be greater than or equal to 1")
-
elsif list.to_a.size == 0
-
raise ArgumentError.new("List index is #{n} but list has no items")
-
elsif n.to_i > (size = list.to_a.size)
-
raise ArgumentError.new("List index is #{n} but list is only #{size} item#{'s' if size != 1} long")
-
end
-
-
list.to_a[n.to_i - 1]
-
end
-
1
declare :nth, [:list, :n]
-
-
# Joins together two lists into one.
-
#
-
# Unless `$separator` is passed, if one list is comma-separated and one is
-
# space-separated, the first parameter's separator is used for the resulting
-
# list. If both lists have fewer than two items, spaces are used for the
-
# resulting list.
-
#
-
# @example
-
# join(10px 20px, 30px 40px) => 10px 20px 30px 40px
-
# join((blue, red), (#abc, #def)) => blue, red, #abc, #def
-
# join(10px, 20px) => 10px 20px
-
# join(10px, 20px, comma) => 10px, 20px
-
# join((blue, red), (#abc, #def), space) => blue red #abc #def
-
# @overload join($list1, $list2, $separator: auto)
-
# @param $list1 [Literal]
-
# @param $list2 [Literal]
-
# @param $separator [String] The list separator to use. If this is `comma`
-
# or `space`, that separator will be used. If this is `auto` (the
-
# default), the separator is determined as explained above.
-
# @return [List]
-
1
def join(list1, list2, separator = Sass::Script::String.new("auto"))
-
assert_type separator, :String, :separator
-
unless %w[auto space comma].include?(separator.value)
-
raise ArgumentError.new("Separator name must be space, comma, or auto")
-
end
-
sep1 = list1.separator if list1.is_a?(Sass::Script::List) && !list1.value.empty?
-
sep2 = list2.separator if list2.is_a?(Sass::Script::List) && !list2.value.empty?
-
Sass::Script::List.new(
-
list1.to_a + list2.to_a,
-
if separator.value == 'auto'
-
sep1 || sep2 || :space
-
else
-
separator.value.to_sym
-
end)
-
end
-
1
declare :join, [:list1, :list2]
-
1
declare :join, [:list1, :list2, :separator]
-
-
# Appends a single value onto the end of a list.
-
#
-
# Unless the `$separator` argument is passed, if the list had only one item,
-
# the resulting list will be space-separated.
-
#
-
# @example
-
# append(10px 20px, 30px) => 10px 20px 30px
-
# append((blue, red), green) => blue, red, green
-
# append(10px 20px, 30px 40px) => 10px 20px (30px 40px)
-
# append(10px, 20px, comma) => 10px, 20px
-
# append((blue, red), green, space) => blue red green
-
# @overload append($list, $val, $separator: auto)
-
# @param $list [Literal]
-
# @param $val [Literal]
-
# @param $separator [String] The list separator to use. If this is `comma`
-
# or `space`, that separator will be used. If this is `auto` (the
-
# default), the separator is determined as explained above.
-
# @return [List]
-
1
def append(list, val, separator = Sass::Script::String.new("auto"))
-
assert_type separator, :String, :separator
-
unless %w[auto space comma].include?(separator.value)
-
raise ArgumentError.new("Separator name must be space, comma, or auto")
-
end
-
sep = list.separator if list.is_a?(Sass::Script::List)
-
Sass::Script::List.new(
-
list.to_a + [val],
-
if separator.value == 'auto'
-
sep || :space
-
else
-
separator.value.to_sym
-
end)
-
end
-
1
declare :append, [:list, :val]
-
1
declare :append, [:list, :val, :separator]
-
-
# Combines several lists into a single multidimensional list. The nth value
-
# of the resulting list is a space separated list of the source lists' nth
-
# values.
-
#
-
# The length of the resulting list is the length of the
-
# shortest list.
-
#
-
# @example
-
# zip(1px 1px 3px, solid dashed solid, red green blue)
-
# => 1px solid red, 1px dashed green, 3px solid blue
-
# @overload zip($lists...)
-
# @param $lists [[Literal]]
-
# @return [List]
-
1
def zip(*lists)
-
length = nil
-
values = []
-
lists.each do |list|
-
array = list.to_a
-
values << array.dup
-
length = length.nil? ? array.length : [length, array.length].min
-
end
-
values.each do |value|
-
value.slice!(length)
-
end
-
new_list_value = values.first.zip(*values[1..-1])
-
List.new(new_list_value.map{|list| List.new(list, :space)}, :comma)
-
end
-
1
declare :zip, [], :var_args => true
-
-
-
# Returns the position of a value within a list. If the value isn't found,
-
# returns false instead.
-
#
-
# Note that unlike some languages, the first item in a Sass list is number
-
# 1, the second number 2, and so forth.
-
#
-
# @example
-
# index(1px solid red, solid) => 2
-
# index(1px solid red, dashed) => false
-
# @overload index($list, $value)
-
# @param $list [Literal]
-
# @param $value [Literal]
-
# @return [Number, Bool] The 1-based index of `$value` in `$list`, or
-
# `false`
-
1
def index(list, value)
-
index = list.to_a.index {|e| e.eq(value).to_bool }
-
if index
-
Number.new(index + 1)
-
else
-
Bool.new(false)
-
end
-
end
-
1
declare :index, [:list, :value]
-
-
# Returns one of two values, depending on whether or not `$condition` is
-
# true. Just like in `@if`, all values other than `false` and `null` are
-
# considered to be true.
-
#
-
# @example
-
# if(true, 1px, 2px) => 1px
-
# if(false, 1px, 2px) => 2px
-
# @overload if($condition, $if-true, $if-false)
-
# @param $condition [Literal] Whether the `$if-true` or `$if-false` will be
-
# returned
-
# @param $if-true [Literal]
-
# @param $if-false [Literal]
-
# @return [Literal] `$if-true` or `$if-false`
-
1
def if(condition, if_true, if_false)
-
if condition.to_bool
-
if_true
-
else
-
if_false
-
end
-
end
-
1
declare :if, [:condition, :if_true, :if_false]
-
-
# This function only exists as a workaround for IE7's [`content: counter`
-
# bug][bug]. It works identically to any other plain-CSS function, except it
-
# avoids adding spaces between the argument commas.
-
#
-
# [bug]: http://jes.st/2013/ie7s-css-breaking-content-counter-bug/
-
#
-
# @example
-
# counter(item, ".") => counter(item,".")
-
# @overload counter($args...)
-
# @return [String]
-
1
def counter(*args)
-
Sass::Script::String.new("counter(#{args.map {|a| a.to_s(options)}.join(',')})")
-
end
-
1
declare :counter, [], :var_args => true
-
-
# This function only exists as a workaround for IE7's [`content: counters`
-
# bug][bug]. It works identically to any other plain-CSS function, except it
-
# avoids adding spaces between the argument commas.
-
#
-
# [bug]: http://jes.st/2013/ie7s-css-breaking-content-counter-bug/
-
#
-
# @example
-
# counters(item, ".") => counters(item,".")
-
# @overload counters($args...)
-
# @return [String]
-
1
def counters(*args)
-
Sass::Script::String.new("counters(#{args.map {|a| a.to_s(options)}.join(',')})")
-
end
-
1
declare :counters, [], :var_args => true
-
-
1
private
-
-
# This method implements the pattern of transforming a numeric value into
-
# another numeric value with the same units.
-
# It yields a number to a block to perform the operation and return a number
-
1
def numeric_transformation(value)
-
assert_type value, :Number, :value
-
Sass::Script::Number.new(yield(value.value), value.numerator_units, value.denominator_units)
-
end
-
-
1
def _adjust(color, amount, attr, range, op, units = "")
-
assert_type color, :Color, :color
-
assert_type amount, :Number, :amount
-
Sass::Util.check_range('Amount', range, amount, units)
-
-
# TODO: is it worth restricting here,
-
# or should we do so in the Color constructor itself,
-
# and allow clipping in rgb() et al?
-
color.with(attr => Sass::Util.restrict(
-
color.send(attr).send(op, amount.value), range))
-
end
-
end
-
end
-
1
module Sass::Script
-
# A SassScript object representing `#{}` interpolation outside a string.
-
#
-
# @see StringInterpolation
-
1
class Interpolation < Node
-
# Interpolation in a property is of the form `before #{mid} after`.
-
#
-
# @param before [Node] The SassScript before the interpolation
-
# @param mid [Node] The SassScript within the interpolation
-
# @param after [Node] The SassScript after the interpolation
-
# @param wb [Boolean] Whether there was whitespace between `before` and `#{`
-
# @param wa [Boolean] Whether there was whitespace between `}` and `after`
-
# @param originally_text [Boolean]
-
# Whether the original format of the interpolation was plain text,
-
# not an interpolation.
-
# This is used when converting back to SassScript.
-
1
def initialize(before, mid, after, wb, wa, originally_text = false)
-
@before = before
-
@mid = mid
-
@after = after
-
@whitespace_before = wb
-
@whitespace_after = wa
-
@originally_text = originally_text
-
end
-
-
# @return [String] A human-readable s-expression representation of the interpolation
-
1
def inspect
-
"(interpolation #{@before.inspect} #{@mid.inspect} #{@after.inspect})"
-
end
-
-
# @see Node#to_sass
-
1
def to_sass(opts = {})
-
res = ""
-
res << @before.to_sass(opts) if @before
-
res << ' ' if @before && @whitespace_before
-
res << '#{' unless @originally_text
-
res << @mid.to_sass(opts)
-
res << '}' unless @originally_text
-
res << ' ' if @after && @whitespace_after
-
res << @after.to_sass(opts) if @after
-
res
-
end
-
-
# Returns the three components of the interpolation, `before`, `mid`, and `after`.
-
#
-
# @return [Array<Node>]
-
# @see #initialize
-
# @see Node#children
-
1
def children
-
[@before, @mid, @after].compact
-
end
-
-
# @see Node#deep_copy
-
1
def deep_copy
-
node = dup
-
node.instance_variable_set('@before', @before.deep_copy) if @before
-
node.instance_variable_set('@mid', @mid.deep_copy)
-
node.instance_variable_set('@after', @after.deep_copy) if @after
-
node
-
end
-
-
1
protected
-
-
# Evaluates the interpolation.
-
#
-
# @param environment [Sass::Environment] The environment in which to evaluate the SassScript
-
# @return [Sass::Script::String] The SassScript string that is the value of the interpolation
-
1
def _perform(environment)
-
res = ""
-
res << @before.perform(environment).to_s if @before
-
res << " " if @before && @whitespace_before
-
val = @mid.perform(environment)
-
res << (val.is_a?(Sass::Script::String) ? val.value : val.to_s)
-
res << " " if @after && @whitespace_after
-
res << @after.perform(environment).to_s if @after
-
opts(Sass::Script::String.new(res))
-
end
-
end
-
end
-
1
require 'sass/scss/rx'
-
-
1
module Sass
-
1
module Script
-
# The lexical analyzer for SassScript.
-
# It takes a raw string and converts it to individual tokens
-
# that are easier to parse.
-
1
class Lexer
-
1
include Sass::SCSS::RX
-
-
# A struct containing information about an individual token.
-
#
-
# `type`: \[`Symbol`\]
-
# : The type of token.
-
#
-
# `value`: \[`Object`\]
-
# : The Ruby object corresponding to the value of the token.
-
#
-
# `line`: \[`Fixnum`\]
-
# : The line of the source file on which the token appears.
-
#
-
# `offset`: \[`Fixnum`\]
-
# : The number of bytes into the line the SassScript token appeared.
-
#
-
# `pos`: \[`Fixnum`\]
-
# : The scanner position at which the SassScript token appeared.
-
1
Token = Struct.new(:type, :value, :line, :offset, :pos)
-
-
# The line number of the lexer's current position.
-
#
-
# @return [Fixnum]
-
1
attr_reader :line
-
-
# The number of bytes into the current line
-
# of the lexer's current position.
-
#
-
# @return [Fixnum]
-
1
attr_reader :offset
-
-
# A hash from operator strings to the corresponding token types.
-
1
OPERATORS = {
-
'+' => :plus,
-
'-' => :minus,
-
'*' => :times,
-
'/' => :div,
-
'%' => :mod,
-
'=' => :single_eq,
-
':' => :colon,
-
'(' => :lparen,
-
')' => :rparen,
-
',' => :comma,
-
'and' => :and,
-
'or' => :or,
-
'not' => :not,
-
'==' => :eq,
-
'!=' => :neq,
-
'>=' => :gte,
-
'<=' => :lte,
-
'>' => :gt,
-
'<' => :lt,
-
'#{' => :begin_interpolation,
-
'}' => :end_interpolation,
-
';' => :semicolon,
-
'{' => :lcurly,
-
'...' => :splat,
-
}
-
-
25
OPERATORS_REVERSE = Sass::Util.map_hash(OPERATORS) {|k, v| [v, k]}
-
-
25
TOKEN_NAMES = Sass::Util.map_hash(OPERATORS_REVERSE) {|k, v| [k, v.inspect]}.merge({
-
:const => "variable (e.g. $foo)",
-
:ident => "identifier (e.g. middle)"
-
})
-
-
# A list of operator strings ordered with longer names first
-
# so that `>` and `<` don't clobber `>=` and `<=`.
-
25
OP_NAMES = OPERATORS.keys.sort_by {|o| -o.size}
-
-
# A sub-list of {OP_NAMES} that only includes operators
-
# with identifier names.
-
25
IDENT_OP_NAMES = OP_NAMES.select {|k, v| k =~ /^\w+/}
-
-
# A hash of regular expressions that are used for tokenizing.
-
1
REGULAR_EXPRESSIONS = {
-
:whitespace => /\s+/,
-
:comment => COMMENT,
-
:single_line_comment => SINGLE_LINE_COMMENT,
-
:variable => /(\$)(#{IDENT})/,
-
:ident => /(#{IDENT})(\()?/,
-
:number => /(-)?(?:(\d*\.\d+)|(\d+))([a-zA-Z%]+)?/,
-
:color => HEXCOLOR,
-
3
:ident_op => %r{(#{Regexp.union(*IDENT_OP_NAMES.map{|s| Regexp.new(Regexp.escape(s) + "(?!#{NMCHAR}|\Z)")})})},
-
:op => %r{(#{Regexp.union(*OP_NAMES)})},
-
}
-
-
1
class << self
-
1
private
-
1
def string_re(open, close)
-
4
/#{open}((?:\\.|\#(?!\{)|[^#{close}\\#])*)(#{close}|#\{)/
-
end
-
end
-
-
# A hash of regular expressions that are used for tokenizing strings.
-
#
-
# The key is a `[Symbol, Boolean]` pair.
-
# The symbol represents which style of quotation to use,
-
# while the boolean represents whether or not the string
-
# is following an interpolated segment.
-
1
STRING_REGULAR_EXPRESSIONS = {
-
:double => {
-
false => string_re('"', '"'),
-
true => string_re('', '"')
-
},
-
:single => {
-
false => string_re("'", "'"),
-
true => string_re('', "'")
-
},
-
:uri => {
-
false => /url\(#{W}(#{URLCHAR}*?)(#{W}\)|#\{)/,
-
true => /(#{URLCHAR}*?)(#{W}\)|#\{)/
-
},
-
# Defined in https://developer.mozilla.org/en/CSS/@-moz-document as a
-
# non-standard version of http://www.w3.org/TR/css3-conditional/
-
:url_prefix => {
-
false => /url-prefix\(#{W}(#{URLCHAR}*?)(#{W}\)|#\{)/,
-
true => /(#{URLCHAR}*?)(#{W}\)|#\{)/
-
},
-
:domain => {
-
false => /domain\(#{W}(#{URLCHAR}*?)(#{W}\)|#\{)/,
-
true => /(#{URLCHAR}*?)(#{W}\)|#\{)/
-
}
-
}
-
-
# @param str [String, StringScanner] The source text to lex
-
# @param line [Fixnum] The line on which the SassScript appears.
-
# Used for error reporting
-
# @param offset [Fixnum] The number of characters in on which the SassScript appears.
-
# Used for error reporting
-
# @param options [{Symbol => Object}] An options hash;
-
# see {file:SASS_REFERENCE.md#sass_options the Sass options documentation}
-
1
def initialize(str, line, offset, options)
-
@scanner = str.is_a?(StringScanner) ? str : Sass::Util::MultibyteStringScanner.new(str)
-
@line = line
-
@offset = offset
-
@options = options
-
@interpolation_stack = []
-
@prev = nil
-
end
-
-
# Moves the lexer forward one token.
-
#
-
# @return [Token] The token that was moved past
-
1
def next
-
@tok ||= read_token
-
@tok, tok = nil, @tok
-
@prev = tok
-
return tok
-
end
-
-
# Returns whether or not there's whitespace before the next token.
-
#
-
# @return [Boolean]
-
1
def whitespace?(tok = @tok)
-
if tok
-
@scanner.string[0...tok.pos] =~ /\s\Z/
-
else
-
@scanner.string[@scanner.pos, 1] =~ /^\s/ ||
-
@scanner.string[@scanner.pos - 1, 1] =~ /\s\Z/
-
end
-
end
-
-
# Returns the next token without moving the lexer forward.
-
#
-
# @return [Token] The next token
-
1
def peek
-
@tok ||= read_token
-
end
-
-
# Rewinds the underlying StringScanner
-
# to before the token returned by \{#peek}.
-
1
def unpeek!
-
@scanner.pos = @tok.pos if @tok
-
end
-
-
# @return [Boolean] Whether or not there's more source text to lex.
-
1
def done?
-
whitespace unless after_interpolation? && @interpolation_stack.last
-
@scanner.eos? && @tok.nil?
-
end
-
-
# @return [Boolean] Whether or not the last token lexed was `:end_interpolation`.
-
1
def after_interpolation?
-
@prev && @prev.type == :end_interpolation
-
end
-
-
# Raise an error to the effect that `name` was expected in the input stream
-
# and wasn't found.
-
#
-
# This calls \{#unpeek!} to rewind the scanner to immediately after
-
# the last returned token.
-
#
-
# @param name [String] The name of the entity that was expected but not found
-
# @raise [Sass::SyntaxError]
-
1
def expected!(name)
-
unpeek!
-
Sass::SCSS::Parser.expected(@scanner, name, @line)
-
end
-
-
# Records all non-comment text the lexer consumes within the block
-
# and returns it as a string.
-
#
-
# @yield A block in which text is recorded
-
# @return [String]
-
1
def str
-
old_pos = @tok ? @tok.pos : @scanner.pos
-
yield
-
new_pos = @tok ? @tok.pos : @scanner.pos
-
@scanner.string[old_pos...new_pos]
-
end
-
-
1
private
-
-
1
def read_token
-
return if done?
-
return unless value = token
-
type, val, size = value
-
size ||= @scanner.matched_size
-
-
val.line = @line if val.is_a?(Script::Node)
-
Token.new(type, val, @line,
-
current_position - size, @scanner.pos - size)
-
end
-
-
1
def whitespace
-
nil while scan(REGULAR_EXPRESSIONS[:whitespace]) ||
-
scan(REGULAR_EXPRESSIONS[:comment]) ||
-
scan(REGULAR_EXPRESSIONS[:single_line_comment])
-
end
-
-
1
def token
-
if after_interpolation? && (interp_type = @interpolation_stack.pop)
-
return string(interp_type, true)
-
end
-
-
variable || string(:double, false) || string(:single, false) || number || color ||
-
string(:uri, false) || raw(UNICODERANGE) || special_fun || special_val || ident_op ||
-
ident || op
-
end
-
-
1
def variable
-
_variable(REGULAR_EXPRESSIONS[:variable])
-
end
-
-
1
def _variable(rx)
-
return unless scan(rx)
-
-
[:const, @scanner[2]]
-
end
-
-
1
def ident
-
return unless scan(REGULAR_EXPRESSIONS[:ident])
-
[@scanner[2] ? :funcall : :ident, @scanner[1]]
-
end
-
-
1
def string(re, open)
-
return unless scan(STRING_REGULAR_EXPRESSIONS[re][open])
-
if @scanner[2] == '#{' #'
-
@scanner.pos -= 2 # Don't actually consume the #{
-
@interpolation_stack << re
-
end
-
str =
-
if re == :uri
-
Script::String.new("#{'url(' unless open}#{@scanner[1]}#{')' unless @scanner[2] == '#{'}")
-
else
-
Script::String.new(@scanner[1].gsub(/\\(['"]|\#\{)/, '\1'), :string)
-
end
-
[:string, str]
-
end
-
-
1
def number
-
return unless scan(REGULAR_EXPRESSIONS[:number])
-
value = @scanner[2] ? @scanner[2].to_f : @scanner[3].to_i
-
value = -value if @scanner[1]
-
[:number, Script::Number.new(value, Array(@scanner[4]))]
-
end
-
-
1
def color
-
return unless s = scan(REGULAR_EXPRESSIONS[:color])
-
raise Sass::SyntaxError.new(<<MESSAGE.rstrip) unless s.size == 4 || s.size == 7
-
Colors must have either three or six digits: '#{s}'
-
MESSAGE
-
value = s.scan(/^#(..?)(..?)(..?)$/).first.
-
map {|num| num.ljust(2, num).to_i(16)}
-
[:color, Script::Color.new(value)]
-
end
-
-
1
def special_fun
-
return unless str1 = scan(/((-[\w-]+-)?(calc|element)|expression|progid:[a-z\.]*)\(/i)
-
str2, _ = Sass::Shared.balance(@scanner, ?(, ?), 1)
-
c = str2.count("\n")
-
old_line = @line
-
old_offset = @offset
-
@line += c
-
@offset = (c == 0 ? @offset + str2.size : str2[/\n(.*)/, 1].size)
-
[:special_fun,
-
Sass::Util.merge_adjacent_strings(
-
[str1] + Sass::Engine.parse_interp(str2, old_line, old_offset, @options)),
-
str1.size + str2.size]
-
end
-
-
1
def special_val
-
return unless scan(/!important/i)
-
[:string, Script::String.new("!important")]
-
end
-
-
1
def ident_op
-
return unless op = scan(REGULAR_EXPRESSIONS[:ident_op])
-
[OPERATORS[op]]
-
end
-
-
1
def op
-
return unless op = scan(REGULAR_EXPRESSIONS[:op])
-
@interpolation_stack << nil if op == :begin_interpolation
-
[OPERATORS[op]]
-
end
-
-
1
def raw(rx)
-
return unless val = scan(rx)
-
[:raw, val]
-
end
-
-
1
def scan(re)
-
return unless str = @scanner.scan(re)
-
c = str.count("\n")
-
@line += c
-
@offset = (c == 0 ? @offset + str.size : str[/\n(.*)/, 1].size)
-
str
-
end
-
-
1
def current_position
-
@offset + 1
-
end
-
end
-
end
-
end
-
1
module Sass::Script
-
# A SassScript object representing a CSS list.
-
# This includes both comma-separated lists and space-separated lists.
-
1
class List < Literal
-
# The Ruby array containing the contents of the list.
-
#
-
# @return [Array<Literal>]
-
1
attr_reader :value
-
1
alias_method :children, :value
-
1
alias_method :to_a, :value
-
-
# The operator separating the values of the list.
-
# Either `:comma` or `:space`.
-
#
-
# @return [Symbol]
-
1
attr_reader :separator
-
-
# Creates a new list.
-
#
-
# @param value [Array<Literal>] See \{#value}
-
# @param separator [String] See \{#separator}
-
1
def initialize(value, separator)
-
super(value)
-
@separator = separator
-
end
-
-
# @see Node#deep_copy
-
1
def deep_copy
-
node = dup
-
node.instance_variable_set('@value', value.map {|c| c.deep_copy})
-
node
-
end
-
-
# @see Node#eq
-
1
def eq(other)
-
Sass::Script::Bool.new(
-
other.is_a?(List) && self.value == other.value &&
-
self.separator == other.separator)
-
end
-
-
# @see Node#to_s
-
1
def to_s(opts = {})
-
raise Sass::SyntaxError.new("() isn't a valid CSS value.") if value.empty?
-
return value.reject {|e| e.is_a?(Null) || e.is_a?(List) && e.value.empty?}.map {|e| e.to_s(opts)}.join(sep_str)
-
end
-
-
# @see Node#to_sass
-
1
def to_sass(opts = {})
-
return "()" if value.empty?
-
precedence = Sass::Script::Parser.precedence_of(separator)
-
value.reject {|e| e.is_a?(Null)}.map do |v|
-
if v.is_a?(List) && Sass::Script::Parser.precedence_of(v.separator) <= precedence ||
-
separator == :space && v.is_a?(UnaryOperation) && (v.operator == :minus || v.operator == :plus)
-
"(#{v.to_sass(opts)})"
-
else
-
v.to_sass(opts)
-
end
-
end.join(sep_str(nil))
-
end
-
-
# @see Node#inspect
-
1
def inspect
-
"(#{to_sass})"
-
end
-
-
1
protected
-
-
# @see Node#_perform
-
1
def _perform(environment)
-
list = Sass::Script::List.new(
-
value.map {|e| e.perform(environment)},
-
separator)
-
list.options = self.options
-
list
-
end
-
-
1
private
-
-
1
def sep_str(opts = self.options)
-
return ' ' if separator == :space
-
return ',' if opts && opts[:style] == :compressed
-
return ', '
-
end
-
end
-
end
-
1
module Sass::Script
-
# The abstract superclass for SassScript objects.
-
#
-
# Many of these methods, especially the ones that correspond to SassScript operations,
-
# are designed to be overridden by subclasses which may change the semantics somewhat.
-
# The operations listed here are just the defaults.
-
1
class Literal < Node
-
1
require 'sass/script/string'
-
1
require 'sass/script/number'
-
1
require 'sass/script/color'
-
1
require 'sass/script/bool'
-
1
require 'sass/script/null'
-
1
require 'sass/script/list'
-
1
require 'sass/script/arg_list'
-
-
# Returns the Ruby value of the literal.
-
# The type of this value varies based on the subclass.
-
#
-
# @return [Object]
-
1
attr_reader :value
-
-
# Creates a new literal.
-
#
-
# @param value [Object] The object for \{#value}
-
1
def initialize(value = nil)
-
@value = value
-
super()
-
end
-
-
# Returns an empty array.
-
#
-
# @return [Array<Node>] empty
-
# @see Node#children
-
1
def children
-
[]
-
end
-
-
# @see Node#deep_copy
-
1
def deep_copy
-
dup
-
end
-
-
# Returns the options hash for this node.
-
#
-
# @return [{Symbol => Object}]
-
# @raise [Sass::SyntaxError] if the options hash hasn't been set.
-
# This should only happen when the literal was created
-
# outside of the parser and \{#to\_s} was called on it
-
1
def options
-
opts = super
-
return opts if opts
-
raise Sass::SyntaxError.new(<<MSG)
-
The #options attribute is not set on this #{self.class}.
-
This error is probably occurring because #to_s was called
-
on this literal within a custom Sass function without first
-
setting the #option attribute.
-
MSG
-
end
-
-
# The SassScript `==` operation.
-
# **Note that this returns a {Sass::Script::Bool} object,
-
# not a Ruby boolean**.
-
#
-
# @param other [Literal] The right-hand side of the operator
-
# @return [Bool] True if this literal is the same as the other,
-
# false otherwise
-
1
def eq(other)
-
Sass::Script::Bool.new(self.class == other.class && self.value == other.value)
-
end
-
-
# The SassScript `!=` operation.
-
# **Note that this returns a {Sass::Script::Bool} object,
-
# not a Ruby boolean**.
-
#
-
# @param other [Literal] The right-hand side of the operator
-
# @return [Bool] False if this literal is the same as the other,
-
# true otherwise
-
1
def neq(other)
-
Sass::Script::Bool.new(!eq(other).to_bool)
-
end
-
-
# The SassScript `==` operation.
-
# **Note that this returns a {Sass::Script::Bool} object,
-
# not a Ruby boolean**.
-
#
-
# @param other [Literal] The right-hand side of the operator
-
# @return [Bool] True if this literal is the same as the other,
-
# false otherwise
-
1
def unary_not
-
Sass::Script::Bool.new(!to_bool)
-
end
-
-
# The SassScript `=` operation
-
# (used for proprietary MS syntax like `alpha(opacity=20)`).
-
#
-
# @param other [Literal] The right-hand side of the operator
-
# @return [Script::String] A string containing both literals
-
# separated by `"="`
-
1
def single_eq(other)
-
Sass::Script::String.new("#{self.to_s}=#{other.to_s}")
-
end
-
-
# The SassScript `+` operation.
-
#
-
# @param other [Literal] The right-hand side of the operator
-
# @return [Script::String] A string containing both literals
-
# without any separation
-
1
def plus(other)
-
if other.is_a?(Sass::Script::String)
-
return Sass::Script::String.new(self.to_s + other.value, other.type)
-
end
-
Sass::Script::String.new(self.to_s + other.to_s)
-
end
-
-
# The SassScript `-` operation.
-
#
-
# @param other [Literal] The right-hand side of the operator
-
# @return [Script::String] A string containing both literals
-
# separated by `"-"`
-
1
def minus(other)
-
Sass::Script::String.new("#{self.to_s}-#{other.to_s}")
-
end
-
-
# The SassScript `/` operation.
-
#
-
# @param other [Literal] The right-hand side of the operator
-
# @return [Script::String] A string containing both literals
-
# separated by `"/"`
-
1
def div(other)
-
Sass::Script::String.new("#{self.to_s}/#{other.to_s}")
-
end
-
-
# The SassScript unary `+` operation (e.g. `+$a`).
-
#
-
# @param other [Literal] The right-hand side of the operator
-
# @return [Script::String] A string containing the literal
-
# preceded by `"+"`
-
1
def unary_plus
-
Sass::Script::String.new("+#{self.to_s}")
-
end
-
-
# The SassScript unary `-` operation (e.g. `-$a`).
-
#
-
# @param other [Literal] The right-hand side of the operator
-
# @return [Script::String] A string containing the literal
-
# preceded by `"-"`
-
1
def unary_minus
-
Sass::Script::String.new("-#{self.to_s}")
-
end
-
-
# The SassScript unary `/` operation (e.g. `/$a`).
-
#
-
# @param other [Literal] The right-hand side of the operator
-
# @return [Script::String] A string containing the literal
-
# preceded by `"/"`
-
1
def unary_div
-
Sass::Script::String.new("/#{self.to_s}")
-
end
-
-
# @return [String] A readable representation of the literal
-
1
def inspect
-
value.inspect
-
end
-
-
# @return [Boolean] `true` (the Ruby boolean value)
-
1
def to_bool
-
true
-
end
-
-
# Compares this object with another.
-
#
-
# @param other [Object] The object to compare with
-
# @return [Boolean] Whether or not this literal is equivalent to `other`
-
1
def ==(other)
-
eq(other).to_bool
-
end
-
-
# @return [Fixnum] The integer value of this literal
-
# @raise [Sass::SyntaxError] if this literal isn't an integer
-
1
def to_i
-
raise Sass::SyntaxError.new("#{self.inspect} is not an integer.")
-
end
-
-
# @raise [Sass::SyntaxError] if this literal isn't an integer
-
1
def assert_int!; to_i; end
-
-
# Returns the value of this literal as a list.
-
# Single literals are considered the same as single-element lists.
-
#
-
# @return [Array<Literal>] The of this literal as a list
-
1
def to_a
-
[self]
-
end
-
-
# Returns the string representation of this literal
-
# as it would be output to the CSS document.
-
#
-
# @return [String]
-
1
def to_s(opts = {})
-
raise Sass::SyntaxError.new("[BUG] All subclasses of Sass::Literal must implement #to_s.")
-
end
-
1
alias_method :to_sass, :to_s
-
-
# Returns whether or not this object is null.
-
#
-
# @return [Boolean] `false`
-
1
def null?
-
false
-
end
-
-
1
protected
-
-
# Evaluates the literal.
-
#
-
# @param environment [Sass::Environment] The environment in which to evaluate the SassScript
-
# @return [Literal] This literal
-
1
def _perform(environment)
-
self
-
end
-
end
-
end
-
1
module Sass::Script
-
# The abstract superclass for SassScript parse tree nodes.
-
#
-
# Use \{#perform} to evaluate a parse tree.
-
1
class Node
-
# The options hash for this node.
-
#
-
# @return [{Symbol => Object}]
-
1
attr_reader :options
-
-
# The line of the document on which this node appeared.
-
#
-
# @return [Fixnum]
-
1
attr_accessor :line
-
-
# Sets the options hash for this node,
-
# as well as for all child nodes.
-
# See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
-
#
-
# @param options [{Symbol => Object}] The options
-
1
def options=(options)
-
@options = options
-
children.each do |c|
-
if c.is_a? Hash
-
c.values.each {|v| v.options = options }
-
else
-
c.options = options
-
end
-
end
-
end
-
-
# Evaluates the node.
-
#
-
# \{#perform} shouldn't be overridden directly;
-
# instead, override \{#\_perform}.
-
#
-
# @param environment [Sass::Environment] The environment in which to evaluate the SassScript
-
# @return [Literal] The SassScript object that is the value of the SassScript
-
1
def perform(environment)
-
_perform(environment)
-
rescue Sass::SyntaxError => e
-
e.modify_backtrace(:line => line)
-
raise e
-
end
-
-
# Returns all child nodes of this node.
-
#
-
# @return [Array<Node>]
-
1
def children
-
Sass::Util.abstract(self)
-
end
-
-
# Returns the text of this SassScript expression.
-
#
-
# @return [String]
-
1
def to_sass(opts = {})
-
Sass::Util.abstract(self)
-
end
-
-
# Returns a deep clone of this node.
-
# The child nodes are cloned, but options are not.
-
#
-
# @return [Node]
-
1
def deep_copy
-
Sass::Util.abstract(self)
-
end
-
-
1
protected
-
-
# Converts underscores to dashes if the :dasherize option is set.
-
1
def dasherize(s, opts)
-
if opts[:dasherize]
-
s.gsub(/_/,'-')
-
else
-
s
-
end
-
end
-
-
# Evaluates this node.
-
# Note that all {Literal} objects created within this method
-
# should have their \{#options} attribute set, probably via \{#opts}.
-
#
-
# @param environment [Sass::Environment] The environment in which to evaluate the SassScript
-
# @return [Literal] The SassScript object that is the value of the SassScript
-
# @see #perform
-
1
def _perform(environment)
-
Sass::Util.abstract(self)
-
end
-
-
# Sets the \{#options} field on the given literal and returns it
-
#
-
# @param literal [Literal]
-
# @return [Literal]
-
1
def opts(literal)
-
literal.options = options
-
literal
-
end
-
end
-
end
-
1
require 'sass/script/literal'
-
-
1
module Sass::Script
-
# A SassScript object representing a null value.
-
1
class Null < Literal
-
# Creates a new null literal.
-
1
def initialize
-
super nil
-
end
-
-
# @return [Boolean] `false` (the Ruby boolean value)
-
1
def to_bool
-
false
-
end
-
-
# @return [Boolean] `true`
-
1
def null?
-
true
-
end
-
-
# @return [String] '' (An empty string)
-
1
def to_s(opts = {})
-
''
-
end
-
-
1
def to_sass(opts = {})
-
'null'
-
end
-
-
# Returns a string representing a null value.
-
#
-
# @return [String]
-
1
def inspect
-
'null'
-
end
-
end
-
end
-
1
require 'sass/script/literal'
-
-
1
module Sass::Script
-
# A SassScript object representing a number.
-
# SassScript numbers can have decimal values,
-
# and can also have units.
-
# For example, `12`, `1px`, and `10.45em`
-
# are all valid values.
-
#
-
# Numbers can also have more complex units, such as `1px*em/in`.
-
# These cannot be inputted directly in Sass code at the moment.
-
1
class Number < Literal
-
# The Ruby value of the number.
-
#
-
# @return [Numeric]
-
1
attr_reader :value
-
-
# A list of units in the numerator of the number.
-
# For example, `1px*em/in*cm` would return `["px", "em"]`
-
# @return [Array<String>]
-
1
attr_reader :numerator_units
-
-
# A list of units in the denominator of the number.
-
# For example, `1px*em/in*cm` would return `["in", "cm"]`
-
# @return [Array<String>]
-
1
attr_reader :denominator_units
-
-
# The original representation of this number.
-
# For example, although the result of `1px/2px` is `0.5`,
-
# the value of `#original` is `"1px/2px"`.
-
#
-
# This is only non-nil when the original value should be used as the CSS value,
-
# as in `font: 1px/2px`.
-
#
-
# @return [Boolean, nil]
-
1
attr_accessor :original
-
-
1
def self.precision
-
1
@precision ||= 5
-
end
-
-
# Sets the number of digits of precision
-
# For example, if this is `3`,
-
# `3.1415926` will be printed as `3.142`.
-
1
def self.precision=(digits)
-
1
@precision = digits.round
-
1
@precision_factor = 10.0**@precision
-
end
-
-
# the precision factor used in numeric output
-
# it is derived from the `precision` method.
-
1
def self.precision_factor
-
@precision_factor ||= 10.0**precision
-
end
-
-
# Handles the deprecation warning for the PRECISION constant
-
# This can be removed in 3.2.
-
1
def self.const_missing(const)
-
if const == :PRECISION
-
Sass::Util.sass_warn("Sass::Script::Number::PRECISION is deprecated and will be removed in a future release. Use Sass::Script::Number.precision_factor instead.")
-
const_set(:PRECISION, self.precision_factor)
-
else
-
super
-
end
-
end
-
-
# Used so we don't allocate two new arrays for each new number.
-
1
NO_UNITS = []
-
-
# @param value [Numeric] The value of the number
-
# @param numerator_units [Array<String>] See \{#numerator\_units}
-
# @param denominator_units [Array<String>] See \{#denominator\_units}
-
1
def initialize(value, numerator_units = NO_UNITS, denominator_units = NO_UNITS)
-
super(value)
-
@numerator_units = numerator_units
-
@denominator_units = denominator_units
-
normalize!
-
end
-
-
# The SassScript `+` operation.
-
# Its functionality depends on the type of its argument:
-
#
-
# {Number}
-
# : Adds the two numbers together, converting units if possible.
-
#
-
# {Color}
-
# : Adds this number to each of the RGB color channels.
-
#
-
# {Literal}
-
# : See {Literal#plus}.
-
#
-
# @param other [Literal] The right-hand side of the operator
-
# @return [Literal] The result of the operation
-
# @raise [Sass::UnitConversionError] if `other` is a number with incompatible units
-
1
def plus(other)
-
if other.is_a? Number
-
operate(other, :+)
-
elsif other.is_a?(Color)
-
other.plus(self)
-
else
-
super
-
end
-
end
-
-
# The SassScript binary `-` operation (e.g. `$a - $b`).
-
# Its functionality depends on the type of its argument:
-
#
-
# {Number}
-
# : Subtracts this number from the other, converting units if possible.
-
#
-
# {Literal}
-
# : See {Literal#minus}.
-
#
-
# @param other [Literal] The right-hand side of the operator
-
# @return [Literal] The result of the operation
-
# @raise [Sass::UnitConversionError] if `other` is a number with incompatible units
-
1
def minus(other)
-
if other.is_a? Number
-
operate(other, :-)
-
else
-
super
-
end
-
end
-
-
# The SassScript unary `+` operation (e.g. `+$a`).
-
#
-
# @return [Number] The value of this number
-
1
def unary_plus
-
self
-
end
-
-
# The SassScript unary `-` operation (e.g. `-$a`).
-
#
-
# @return [Number] The negative value of this number
-
1
def unary_minus
-
Number.new(-value, @numerator_units, @denominator_units)
-
end
-
-
# The SassScript `*` operation.
-
# Its functionality depends on the type of its argument:
-
#
-
# {Number}
-
# : Multiplies the two numbers together, converting units appropriately.
-
#
-
# {Color}
-
# : Multiplies each of the RGB color channels by this number.
-
#
-
# @param other [Number, Color] The right-hand side of the operator
-
# @return [Number, Color] The result of the operation
-
# @raise [NoMethodError] if `other` is an invalid type
-
1
def times(other)
-
if other.is_a? Number
-
operate(other, :*)
-
elsif other.is_a? Color
-
other.times(self)
-
else
-
raise NoMethodError.new(nil, :times)
-
end
-
end
-
-
# The SassScript `/` operation.
-
# Its functionality depends on the type of its argument:
-
#
-
# {Number}
-
# : Divides this number by the other, converting units appropriately.
-
#
-
# {Literal}
-
# : See {Literal#div}.
-
#
-
# @param other [Literal] The right-hand side of the operator
-
# @return [Literal] The result of the operation
-
1
def div(other)
-
if other.is_a? Number
-
res = operate(other, :/)
-
if self.original && other.original
-
res.original = "#{self.original}/#{other.original}"
-
end
-
res
-
else
-
super
-
end
-
end
-
-
# The SassScript `%` operation.
-
#
-
# @param other [Number] The right-hand side of the operator
-
# @return [Number] This number modulo the other
-
# @raise [NoMethodError] if `other` is an invalid type
-
# @raise [Sass::UnitConversionError] if `other` has any units
-
1
def mod(other)
-
if other.is_a?(Number)
-
unless other.unitless?
-
raise Sass::UnitConversionError.new("Cannot modulo by a number with units: #{other.inspect}.")
-
end
-
operate(other, :%)
-
else
-
raise NoMethodError.new(nil, :mod)
-
end
-
end
-
-
# The SassScript `==` operation.
-
#
-
# @param other [Literal] The right-hand side of the operator
-
# @return [Boolean] Whether this number is equal to the other object
-
1
def eq(other)
-
return Sass::Script::Bool.new(false) unless other.is_a?(Sass::Script::Number)
-
this = self
-
begin
-
if unitless?
-
this = this.coerce(other.numerator_units, other.denominator_units)
-
else
-
other = other.coerce(@numerator_units, @denominator_units)
-
end
-
rescue Sass::UnitConversionError
-
return Sass::Script::Bool.new(false)
-
end
-
-
Sass::Script::Bool.new(this.value == other.value)
-
end
-
-
# The SassScript `>` operation.
-
#
-
# @param other [Number] The right-hand side of the operator
-
# @return [Boolean] Whether this number is greater than the other
-
# @raise [NoMethodError] if `other` is an invalid type
-
1
def gt(other)
-
raise NoMethodError.new(nil, :gt) unless other.is_a?(Number)
-
operate(other, :>)
-
end
-
-
# The SassScript `>=` operation.
-
#
-
# @param other [Number] The right-hand side of the operator
-
# @return [Boolean] Whether this number is greater than or equal to the other
-
# @raise [NoMethodError] if `other` is an invalid type
-
1
def gte(other)
-
raise NoMethodError.new(nil, :gte) unless other.is_a?(Number)
-
operate(other, :>=)
-
end
-
-
# The SassScript `<` operation.
-
#
-
# @param other [Number] The right-hand side of the operator
-
# @return [Boolean] Whether this number is less than the other
-
# @raise [NoMethodError] if `other` is an invalid type
-
1
def lt(other)
-
raise NoMethodError.new(nil, :lt) unless other.is_a?(Number)
-
operate(other, :<)
-
end
-
-
# The SassScript `<=` operation.
-
#
-
# @param other [Number] The right-hand side of the operator
-
# @return [Boolean] Whether this number is less than or equal to the other
-
# @raise [NoMethodError] if `other` is an invalid type
-
1
def lte(other)
-
raise NoMethodError.new(nil, :lte) unless other.is_a?(Number)
-
operate(other, :<=)
-
end
-
-
# @return [String] The CSS representation of this number
-
# @raise [Sass::SyntaxError] if this number has units that can't be used in CSS
-
# (e.g. `px*in`)
-
1
def to_s(opts = {})
-
return original if original
-
raise Sass::SyntaxError.new("#{inspect} isn't a valid CSS value.") unless legal_units?
-
inspect
-
end
-
-
# Returns a readable representation of this number.
-
#
-
# This representation is valid CSS (and valid SassScript)
-
# as long as there is only one unit.
-
#
-
# @return [String] The representation
-
1
def inspect(opts = {})
-
value = self.class.round(self.value)
-
unitless? ? value.to_s : "#{value}#{unit_str}"
-
end
-
1
alias_method :to_sass, :inspect
-
-
# @return [Fixnum] The integer value of the number
-
# @raise [Sass::SyntaxError] if the number isn't an integer
-
1
def to_i
-
super unless int?
-
return value
-
end
-
-
# @return [Boolean] Whether or not this number is an integer.
-
1
def int?
-
value % 1 == 0.0
-
end
-
-
# @return [Boolean] Whether or not this number has no units.
-
1
def unitless?
-
@numerator_units.empty? && @denominator_units.empty?
-
end
-
-
# @return [Boolean] Whether or not this number has units that can be represented in CSS
-
# (that is, zero or one \{#numerator\_units}).
-
1
def legal_units?
-
(@numerator_units.empty? || @numerator_units.size == 1) && @denominator_units.empty?
-
end
-
-
# Returns this number converted to other units.
-
# The conversion takes into account the relationship between e.g. mm and cm,
-
# as well as between e.g. in and cm.
-
#
-
# If this number has no units, it will simply return itself
-
# with the given units.
-
#
-
# An incompatible coercion, e.g. between px and cm, will raise an error.
-
#
-
# @param num_units [Array<String>] The numerator units to coerce this number into.
-
# See {\#numerator\_units}
-
# @param den_units [Array<String>] The denominator units to coerce this number into.
-
# See {\#denominator\_units}
-
# @return [Number] The number with the new units
-
# @raise [Sass::UnitConversionError] if the given units are incompatible with the number's
-
# current units
-
1
def coerce(num_units, den_units)
-
Number.new(if unitless?
-
self.value
-
else
-
self.value * coercion_factor(@numerator_units, num_units) /
-
coercion_factor(@denominator_units, den_units)
-
end, num_units, den_units)
-
end
-
-
# @param other [Number] A number to decide if it can be compared with this number.
-
# @return [Boolean] Whether or not this number can be compared with the other.
-
1
def comparable_to?(other)
-
begin
-
operate(other, :+)
-
true
-
rescue Sass::UnitConversionError
-
false
-
end
-
end
-
-
# Returns a human readable representation of the units in this number.
-
# For complex units this takes the form of:
-
# numerator_unit1 * numerator_unit2 / denominator_unit1 * denominator_unit2
-
# @return [String] a string that represents the units in this number
-
1
def unit_str
-
rv = @numerator_units.sort.join("*")
-
if @denominator_units.any?
-
rv << "/"
-
rv << @denominator_units.sort.join("*")
-
end
-
rv
-
end
-
-
1
private
-
-
# @private
-
1
def self.round(num)
-
if num.is_a?(Float) && (num.infinite? || num.nan?)
-
num
-
elsif num % 1 == 0.0
-
num.to_i
-
else
-
((num * self.precision_factor).round / self.precision_factor).to_f
-
end
-
end
-
-
1
OPERATIONS = [:+, :-, :<=, :<, :>, :>=]
-
-
1
def operate(other, operation)
-
this = self
-
if OPERATIONS.include?(operation)
-
if unitless?
-
this = this.coerce(other.numerator_units, other.denominator_units)
-
else
-
other = other.coerce(@numerator_units, @denominator_units)
-
end
-
end
-
# avoid integer division
-
value = (:/ == operation) ? this.value.to_f : this.value
-
result = value.send(operation, other.value)
-
-
if result.is_a?(Numeric)
-
Number.new(result, *compute_units(this, other, operation))
-
else # Boolean op
-
Bool.new(result)
-
end
-
end
-
-
1
def coercion_factor(from_units, to_units)
-
# get a list of unmatched units
-
from_units, to_units = sans_common_units(from_units, to_units)
-
-
if from_units.size != to_units.size || !convertable?(from_units | to_units)
-
raise Sass::UnitConversionError.new("Incompatible units: '#{from_units.join('*')}' and '#{to_units.join('*')}'.")
-
end
-
-
from_units.zip(to_units).inject(1) {|m,p| m * conversion_factor(p[0], p[1]) }
-
end
-
-
1
def compute_units(this, other, operation)
-
case operation
-
when :*
-
[this.numerator_units + other.numerator_units, this.denominator_units + other.denominator_units]
-
when :/
-
[this.numerator_units + other.denominator_units, this.denominator_units + other.numerator_units]
-
else
-
[this.numerator_units, this.denominator_units]
-
end
-
end
-
-
1
def normalize!
-
return if unitless?
-
@numerator_units, @denominator_units = sans_common_units(@numerator_units, @denominator_units)
-
-
@denominator_units.each_with_index do |d, i|
-
if convertable?(d) && (u = @numerator_units.detect(&method(:convertable?)))
-
@value /= conversion_factor(d, u)
-
@denominator_units.delete_at(i)
-
@numerator_units.delete_at(@numerator_units.index(u))
-
end
-
end
-
end
-
-
# A hash of unit names to their index in the conversion table
-
1
CONVERTABLE_UNITS = {"in" => 0, "cm" => 1, "pc" => 2, "mm" => 3, "pt" => 4, "px" => 5 }
-
1
CONVERSION_TABLE = [[ 1, 2.54, 6, 25.4, 72 , 96 ], # in
-
[ nil, 1, 2.36220473, 10, 28.3464567, 37.795275591], # cm
-
[ nil, nil, 1, 4.23333333, 12 , 16 ], # pc
-
[ nil, nil, nil, 1, 2.83464567, 3.7795275591], # mm
-
[ nil, nil, nil, nil, 1 , 1.3333333333], # pt
-
[ nil, nil, nil, nil, nil , 1 ]] # px
-
-
1
def conversion_factor(from_unit, to_unit)
-
res = CONVERSION_TABLE[CONVERTABLE_UNITS[from_unit]][CONVERTABLE_UNITS[to_unit]]
-
return 1.0 / conversion_factor(to_unit, from_unit) if res.nil?
-
res
-
end
-
-
1
def convertable?(units)
-
Array(units).all? {|u| CONVERTABLE_UNITS.include?(u)}
-
end
-
-
1
def sans_common_units(units1, units2)
-
units2 = units2.dup
-
# Can't just use -, because we want px*px to coerce properly to px*mm
-
return units1.map do |u|
-
next u unless j = units2.index(u)
-
units2.delete_at(j)
-
nil
-
end.compact, units2
-
end
-
end
-
end
-
1
require 'set'
-
1
require 'sass/script/string'
-
1
require 'sass/script/number'
-
1
require 'sass/script/color'
-
1
require 'sass/script/functions'
-
1
require 'sass/script/unary_operation'
-
1
require 'sass/script/interpolation'
-
1
require 'sass/script/string_interpolation'
-
-
1
module Sass::Script
-
# A SassScript parse node representing a binary operation,
-
# such as `$a + $b` or `"foo" + 1`.
-
1
class Operation < Node
-
1
attr_reader :operand1
-
1
attr_reader :operand2
-
1
attr_reader :operator
-
-
# @param operand1 [Script::Node] The parse-tree node
-
# for the right-hand side of the operator
-
# @param operand2 [Script::Node] The parse-tree node
-
# for the left-hand side of the operator
-
# @param operator [Symbol] The operator to perform.
-
# This should be one of the binary operator names in {Lexer::OPERATORS}
-
1
def initialize(operand1, operand2, operator)
-
@operand1 = operand1
-
@operand2 = operand2
-
@operator = operator
-
super()
-
end
-
-
# @return [String] A human-readable s-expression representation of the operation
-
1
def inspect
-
"(#{@operator.inspect} #{@operand1.inspect} #{@operand2.inspect})"
-
end
-
-
# @see Node#to_sass
-
1
def to_sass(opts = {})
-
o1 = operand_to_sass @operand1, :left, opts
-
o2 = operand_to_sass @operand2, :right, opts
-
sep =
-
case @operator
-
when :comma; ", "
-
when :space; " "
-
else; " #{Lexer::OPERATORS_REVERSE[@operator]} "
-
end
-
"#{o1}#{sep}#{o2}"
-
end
-
-
# Returns the operands for this operation.
-
#
-
# @return [Array<Node>]
-
# @see Node#children
-
1
def children
-
[@operand1, @operand2]
-
end
-
-
# @see Node#deep_copy
-
1
def deep_copy
-
node = dup
-
node.instance_variable_set('@operand1', @operand1.deep_copy)
-
node.instance_variable_set('@operand2', @operand2.deep_copy)
-
node
-
end
-
-
1
protected
-
-
# Evaluates the operation.
-
#
-
# @param environment [Sass::Environment] The environment in which to evaluate the SassScript
-
# @return [Literal] The SassScript object that is the value of the operation
-
# @raise [Sass::SyntaxError] if the operation is undefined for the operands
-
1
def _perform(environment)
-
literal1 = @operand1.perform(environment)
-
-
# Special-case :and and :or to support short-circuiting.
-
if @operator == :and
-
return literal1.to_bool ? @operand2.perform(environment) : literal1
-
elsif @operator == :or
-
return literal1.to_bool ? literal1 : @operand2.perform(environment)
-
end
-
-
literal2 = @operand2.perform(environment)
-
-
if (literal1.is_a?(Null) || literal2.is_a?(Null)) && @operator != :eq && @operator != :neq
-
raise Sass::SyntaxError.new("Invalid null operation: \"#{literal1.inspect} #{@operator} #{literal2.inspect}\".")
-
end
-
-
begin
-
opts(literal1.send(@operator, literal2))
-
rescue NoMethodError => e
-
raise e unless e.name.to_s == @operator.to_s
-
raise Sass::SyntaxError.new("Undefined operation: \"#{literal1} #{@operator} #{literal2}\".")
-
end
-
end
-
-
1
private
-
-
1
def operand_to_sass(op, side, opts)
-
return "(#{op.to_sass(opts)})" if op.is_a?(List)
-
return op.to_sass(opts) unless op.is_a?(Operation)
-
-
pred = Sass::Script::Parser.precedence_of(@operator)
-
sub_pred = Sass::Script::Parser.precedence_of(op.operator)
-
assoc = Sass::Script::Parser.associative?(@operator)
-
return "(#{op.to_sass(opts)})" if sub_pred < pred ||
-
(side == :right && sub_pred == pred && !assoc)
-
op.to_sass(opts)
-
end
-
end
-
end
-
1
require 'sass/script/lexer'
-
-
1
module Sass
-
1
module Script
-
# The parser for SassScript.
-
# It parses a string of code into a tree of {Script::Node}s.
-
1
class Parser
-
# The line number of the parser's current position.
-
#
-
# @return [Fixnum]
-
1
def line
-
@lexer.line
-
end
-
-
# @param str [String, StringScanner] The source text to parse
-
# @param line [Fixnum] The line on which the SassScript appears.
-
# Used for error reporting
-
# @param offset [Fixnum] The number of characters in on which the SassScript appears.
-
# Used for error reporting
-
# @param options [{Symbol => Object}] An options hash;
-
# see {file:SASS_REFERENCE.md#sass_options the Sass options documentation}
-
1
def initialize(str, line, offset, options = {})
-
@options = options
-
@lexer = lexer_class.new(str, line, offset, options)
-
end
-
-
# Parses a SassScript expression within an interpolated segment (`#{}`).
-
# This means that it stops when it comes across an unmatched `}`,
-
# which signals the end of an interpolated segment,
-
# it returns rather than throwing an error.
-
#
-
# @return [Script::Node] The root node of the parse tree
-
# @raise [Sass::SyntaxError] if the expression isn't valid SassScript
-
1
def parse_interpolated
-
expr = assert_expr :expr
-
assert_tok :end_interpolation
-
expr.options = @options
-
expr
-
rescue Sass::SyntaxError => e
-
e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
-
raise e
-
end
-
-
# Parses a SassScript expression.
-
#
-
# @return [Script::Node] The root node of the parse tree
-
# @raise [Sass::SyntaxError] if the expression isn't valid SassScript
-
1
def parse
-
expr = assert_expr :expr
-
assert_done
-
expr.options = @options
-
expr
-
rescue Sass::SyntaxError => e
-
e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
-
raise e
-
end
-
-
# Parses a SassScript expression,
-
# ending it when it encounters one of the given identifier tokens.
-
#
-
# @param [#include?(String)] A set of strings that delimit the expression.
-
# @return [Script::Node] The root node of the parse tree
-
# @raise [Sass::SyntaxError] if the expression isn't valid SassScript
-
1
def parse_until(tokens)
-
@stop_at = tokens
-
expr = assert_expr :expr
-
assert_done
-
expr.options = @options
-
expr
-
rescue Sass::SyntaxError => e
-
e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
-
raise e
-
end
-
-
# Parses the argument list for a mixin include.
-
#
-
# @return [(Array<Script::Node>, {String => Script::Node}, Script::Node)]
-
# The root nodes of the positional arguments, keyword arguments, and
-
# splat argument. Keyword arguments are in a hash from names to values.
-
# @raise [Sass::SyntaxError] if the argument list isn't valid SassScript
-
1
def parse_mixin_include_arglist
-
args, keywords = [], {}
-
if try_tok(:lparen)
-
args, keywords, splat = mixin_arglist || [[], {}]
-
assert_tok(:rparen)
-
end
-
assert_done
-
-
args.each {|a| a.options = @options}
-
keywords.each {|k, v| v.options = @options}
-
splat.options = @options if splat
-
return args, keywords, splat
-
rescue Sass::SyntaxError => e
-
e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
-
raise e
-
end
-
-
# Parses the argument list for a mixin definition.
-
#
-
# @return [(Array<Script::Node>, Script::Node)]
-
# The root nodes of the arguments, and the splat argument.
-
# @raise [Sass::SyntaxError] if the argument list isn't valid SassScript
-
1
def parse_mixin_definition_arglist
-
args, splat = defn_arglist!(false)
-
assert_done
-
-
args.each do |k, v|
-
k.options = @options
-
v.options = @options if v
-
end
-
splat.options = @options if splat
-
return args, splat
-
rescue Sass::SyntaxError => e
-
e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
-
raise e
-
end
-
-
# Parses the argument list for a function definition.
-
#
-
# @return [(Array<Script::Node>, Script::Node)]
-
# The root nodes of the arguments, and the splat argument.
-
# @raise [Sass::SyntaxError] if the argument list isn't valid SassScript
-
1
def parse_function_definition_arglist
-
args, splat = defn_arglist!(true)
-
assert_done
-
-
args.each do |k, v|
-
k.options = @options
-
v.options = @options if v
-
end
-
splat.options = @options if splat
-
return args, splat
-
rescue Sass::SyntaxError => e
-
e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
-
raise e
-
end
-
-
# Parse a single string value, possibly containing interpolation.
-
# Doesn't assert that the scanner is finished after parsing.
-
#
-
# @return [Script::Node] The root node of the parse tree.
-
# @raise [Sass::SyntaxError] if the string isn't valid SassScript
-
1
def parse_string
-
unless (peek = @lexer.peek) &&
-
(peek.type == :string ||
-
(peek.type == :funcall && peek.value.downcase == 'url'))
-
lexer.expected!("string")
-
end
-
-
expr = assert_expr :funcall
-
expr.options = @options
-
@lexer.unpeek!
-
expr
-
rescue Sass::SyntaxError => e
-
e.modify_backtrace :line => @lexer.line, :filename => @options[:filename]
-
raise e
-
end
-
-
# Parses a SassScript expression.
-
#
-
# @overload parse(str, line, offset, filename = nil)
-
# @return [Script::Node] The root node of the parse tree
-
# @see Parser#initialize
-
# @see Parser#parse
-
1
def self.parse(*args)
-
new(*args).parse
-
end
-
-
1
PRECEDENCE = [
-
:comma, :single_eq, :space, :or, :and,
-
[:eq, :neq],
-
[:gt, :gte, :lt, :lte],
-
[:plus, :minus],
-
[:times, :div, :mod],
-
]
-
-
1
ASSOCIATIVE = [:plus, :times]
-
-
1
class << self
-
# Returns an integer representing the precedence
-
# of the given operator.
-
# A lower integer indicates a looser binding.
-
#
-
# @private
-
1
def precedence_of(op)
-
PRECEDENCE.each_with_index do |e, i|
-
return i if Array(e).include?(op)
-
end
-
raise "[BUG] Unknown operator #{op}"
-
end
-
-
# Returns whether or not the given operation is associative.
-
#
-
# @private
-
1
def associative?(op)
-
ASSOCIATIVE.include?(op)
-
end
-
-
1
private
-
-
# Defines a simple left-associative production.
-
# name is the name of the production,
-
# sub is the name of the production beneath it,
-
# and ops is a list of operators for this precedence level
-
1
def production(name, sub, *ops)
-
8
class_eval <<RUBY, __FILE__, __LINE__ + 1
-
def #{name}
-
interp = try_ops_after_interp(#{ops.inspect}, #{name.inspect}) and return interp
-
return unless e = #{sub}
-
15
while tok = try_tok(#{ops.map {|o| o.inspect}.join(', ')})
-
if interp = try_op_before_interp(tok, e)
-
return interp unless other_interp = try_ops_after_interp(#{ops.inspect}, #{name.inspect}, interp)
-
return other_interp
-
end
-
-
line = @lexer.line
-
e = Operation.new(e, assert_expr(#{sub.inspect}), tok.type)
-
e.line = line
-
end
-
e
-
end
-
RUBY
-
end
-
-
1
def unary(op, sub)
-
4
class_eval <<RUBY, __FILE__, __LINE__ + 1
-
def unary_#{op}
-
return #{sub} unless tok = try_tok(:#{op})
-
interp = try_op_before_interp(tok) and return interp
-
line = @lexer.line
-
op = UnaryOperation.new(assert_expr(:unary_#{op}), :#{op})
-
op.line = line
-
op
-
end
-
RUBY
-
end
-
end
-
-
1
private
-
-
# @private
-
1
def lexer_class; Lexer; end
-
-
1
def expr
-
line = @lexer.line
-
return unless e = interpolation
-
list = node(List.new([e], :comma), line)
-
while tok = try_tok(:comma)
-
if interp = try_op_before_interp(tok, list)
-
return interp unless other_interp = try_ops_after_interp([:comma], :expr, interp)
-
return other_interp
-
end
-
list.value << assert_expr(:interpolation)
-
end
-
list.value.size == 1 ? list.value.first : list
-
end
-
-
1
production :equals, :interpolation, :single_eq
-
-
1
def try_op_before_interp(op, prev = nil)
-
return unless @lexer.peek && @lexer.peek.type == :begin_interpolation
-
wb = @lexer.whitespace?(op)
-
str = Script::String.new(Lexer::OPERATORS_REVERSE[op.type])
-
str.line = @lexer.line
-
interp = Script::Interpolation.new(prev, str, nil, wb, !:wa, :originally_text)
-
interp.line = @lexer.line
-
interpolation(interp)
-
end
-
-
1
def try_ops_after_interp(ops, name, prev = nil)
-
return unless @lexer.after_interpolation?
-
return unless op = try_tok(*ops)
-
interp = try_op_before_interp(op, prev) and return interp
-
-
wa = @lexer.whitespace?
-
str = Script::String.new(Lexer::OPERATORS_REVERSE[op.type])
-
str.line = @lexer.line
-
interp = Script::Interpolation.new(prev, str, assert_expr(name), !:wb, wa, :originally_text)
-
interp.line = @lexer.line
-
return interp
-
end
-
-
1
def interpolation(first = space)
-
e = first
-
while interp = try_tok(:begin_interpolation)
-
wb = @lexer.whitespace?(interp)
-
line = @lexer.line
-
mid = parse_interpolated
-
wa = @lexer.whitespace?
-
e = Script::Interpolation.new(e, mid, space, wb, wa)
-
e.line = line
-
end
-
e
-
end
-
-
1
def space
-
line = @lexer.line
-
return unless e = or_expr
-
arr = [e]
-
while e = or_expr
-
arr << e
-
end
-
arr.size == 1 ? arr.first : node(List.new(arr, :space), line)
-
end
-
-
1
production :or_expr, :and_expr, :or
-
1
production :and_expr, :eq_or_neq, :and
-
1
production :eq_or_neq, :relational, :eq, :neq
-
1
production :relational, :plus_or_minus, :gt, :gte, :lt, :lte
-
1
production :plus_or_minus, :times_div_or_mod, :plus, :minus
-
1
production :times_div_or_mod, :unary_plus, :times, :div, :mod
-
-
1
unary :plus, :unary_minus
-
1
unary :minus, :unary_div
-
1
unary :div, :unary_not # For strings, so /foo/bar works
-
1
unary :not, :ident
-
-
1
def ident
-
return funcall unless @lexer.peek && @lexer.peek.type == :ident
-
return if @stop_at && @stop_at.include?(@lexer.peek.value)
-
-
name = @lexer.next
-
if color = Color::COLOR_NAMES[name.value.downcase]
-
node(Color.new(color))
-
elsif name.value == "true"
-
node(Script::Bool.new(true))
-
elsif name.value == "false"
-
node(Script::Bool.new(false))
-
elsif name.value == "null"
-
node(Script::Null.new)
-
else
-
node(Script::String.new(name.value, :identifier))
-
end
-
end
-
-
1
def funcall
-
return raw unless tok = try_tok(:funcall)
-
args, keywords, splat = fn_arglist || [[], {}]
-
assert_tok(:rparen)
-
node(Script::Funcall.new(tok.value, args, keywords, splat))
-
end
-
-
1
def defn_arglist!(must_have_parens)
-
if must_have_parens
-
assert_tok(:lparen)
-
else
-
return [], nil unless try_tok(:lparen)
-
end
-
return [], nil if try_tok(:rparen)
-
-
res = []
-
splat = nil
-
must_have_default = false
-
loop do
-
c = assert_tok(:const)
-
var = Script::Variable.new(c.value)
-
if try_tok(:colon)
-
val = assert_expr(:space)
-
must_have_default = true
-
elsif must_have_default
-
raise SyntaxError.new("Required argument #{var.inspect} must come before any optional arguments.")
-
elsif try_tok(:splat)
-
splat = var
-
break
-
end
-
res << [var, val]
-
break unless try_tok(:comma)
-
end
-
assert_tok(:rparen)
-
return res, splat
-
end
-
-
1
def fn_arglist
-
arglist(:equals, "function argument")
-
end
-
-
1
def mixin_arglist
-
arglist(:interpolation, "mixin argument")
-
end
-
-
1
def arglist(subexpr, description)
-
return unless e = send(subexpr)
-
-
args = []
-
keywords = {}
-
loop do
-
if @lexer.peek && @lexer.peek.type == :colon
-
name = e
-
@lexer.expected!("comma") unless name.is_a?(Variable)
-
assert_tok(:colon)
-
value = assert_expr(subexpr, description)
-
-
if keywords[name.underscored_name]
-
raise SyntaxError.new("Keyword argument \"#{name.to_sass}\" passed more than once")
-
end
-
-
keywords[name.underscored_name] = value
-
else
-
if !keywords.empty?
-
raise SyntaxError.new("Positional arguments must come before keyword arguments.")
-
end
-
-
return args, keywords, e if try_tok(:splat)
-
args << e
-
end
-
-
return args, keywords unless try_tok(:comma)
-
e = assert_expr(subexpr, description)
-
end
-
end
-
-
1
def raw
-
return special_fun unless tok = try_tok(:raw)
-
node(Script::String.new(tok.value))
-
end
-
-
1
def special_fun
-
return paren unless tok = try_tok(:special_fun)
-
first = node(Script::String.new(tok.value.first))
-
Sass::Util.enum_slice(tok.value[1..-1], 2).inject(first) do |l, (i, r)|
-
Script::Interpolation.new(
-
l, i, r && node(Script::String.new(r)),
-
false, false)
-
end
-
end
-
-
1
def paren
-
return variable unless try_tok(:lparen)
-
was_in_parens = @in_parens
-
@in_parens = true
-
line = @lexer.line
-
e = expr
-
assert_tok(:rparen)
-
return e || node(List.new([], :space), line)
-
ensure
-
@in_parens = was_in_parens
-
end
-
-
1
def variable
-
return string unless c = try_tok(:const)
-
node(Variable.new(*c.value))
-
end
-
-
1
def string
-
return number unless first = try_tok(:string)
-
return first.value unless try_tok(:begin_interpolation)
-
line = @lexer.line
-
mid = parse_interpolated
-
last = assert_expr(:string)
-
interp = StringInterpolation.new(first.value, mid, last)
-
interp.line = line
-
interp
-
end
-
-
1
def number
-
return literal unless tok = try_tok(:number)
-
num = tok.value
-
num.original = num.to_s unless @in_parens
-
num
-
end
-
-
1
def literal
-
(t = try_tok(:color)) && (return t.value)
-
end
-
-
# It would be possible to have unified #assert and #try methods,
-
# but detecting the method/token difference turns out to be quite expensive.
-
-
1
EXPR_NAMES = {
-
:string => "string",
-
:default => "expression (e.g. 1px, bold)",
-
:mixin_arglist => "mixin argument",
-
:fn_arglist => "function argument",
-
}
-
-
1
def assert_expr(name, expected = nil)
-
(e = send(name)) && (return e)
-
@lexer.expected!(expected || EXPR_NAMES[name] || EXPR_NAMES[:default])
-
end
-
-
1
def assert_tok(*names)
-
(t = try_tok(*names)) && (return t)
-
@lexer.expected!(names.map {|tok| Lexer::TOKEN_NAMES[tok] || tok}.join(" or "))
-
end
-
-
1
def try_tok(*names)
-
peeked = @lexer.peek
-
peeked && names.include?(peeked.type) && @lexer.next
-
end
-
-
1
def assert_done
-
return if @lexer.done?
-
@lexer.expected!(EXPR_NAMES[:default])
-
end
-
-
1
def node(node, line = @lexer.line)
-
node.line = line
-
node
-
end
-
end
-
end
-
end
-
1
require 'sass/script/literal'
-
-
1
module Sass::Script
-
# A SassScript object representing a CSS string *or* a CSS identifier.
-
1
class String < Literal
-
# The Ruby value of the string.
-
#
-
# @return [String]
-
1
attr_reader :value
-
-
# Whether this is a CSS string or a CSS identifier.
-
# The difference is that strings are written with double-quotes,
-
# while identifiers aren't.
-
#
-
# @return [Symbol] `:string` or `:identifier`
-
1
attr_reader :type
-
-
# Creates a new string.
-
#
-
# @param value [String] See \{#value}
-
# @param type [Symbol] See \{#type}
-
1
def initialize(value, type = :identifier)
-
super(value)
-
@type = type
-
end
-
-
# @see Literal#plus
-
1
def plus(other)
-
other_str = other.is_a?(Sass::Script::String) ? other.value : other.to_s
-
Sass::Script::String.new(self.value + other_str, self.type)
-
end
-
-
# @see Node#to_s
-
1
def to_s(opts = {})
-
if @type == :identifier
-
return @value.gsub(/\n\s*/, " ")
-
end
-
-
return "\"#{value.gsub('"', "\\\"")}\"" if opts[:quote] == %q{"}
-
return "'#{value.gsub("'", "\\'")}'" if opts[:quote] == %q{'}
-
return "\"#{value}\"" unless value.include?('"')
-
return "'#{value}'" unless value.include?("'")
-
"\"#{value.gsub('"', "\\\"")}\"" #'
-
end
-
-
# @see Node#to_sass
-
1
def to_sass(opts = {})
-
to_s
-
end
-
end
-
end
-
1
module Sass::Script
-
# A SassScript object representing `#{}` interpolation within a string.
-
#
-
# @see Interpolation
-
1
class StringInterpolation < Node
-
# Interpolation in a string is of the form `"before #{mid} after"`,
-
# where `before` and `after` may include more interpolation.
-
#
-
# @param before [Node] The string before the interpolation
-
# @param mid [Node] The SassScript within the interpolation
-
# @param after [Node] The string after the interpolation
-
1
def initialize(before, mid, after)
-
@before = before
-
@mid = mid
-
@after = after
-
end
-
-
# @return [String] A human-readable s-expression representation of the interpolation
-
1
def inspect
-
"(string_interpolation #{@before.inspect} #{@mid.inspect} #{@after.inspect})"
-
end
-
-
# @see Node#to_sass
-
1
def to_sass(opts = {})
-
# We can get rid of all of this when we remove the deprecated :equals context
-
# XXX CE: It's gone now but I'm not sure what can be removed now.
-
before_unquote, before_quote_char, before_str = parse_str(@before.to_sass(opts))
-
after_unquote, after_quote_char, after_str = parse_str(@after.to_sass(opts))
-
unquote = before_unquote || after_unquote ||
-
(before_quote_char && !after_quote_char && !after_str.empty?) ||
-
(!before_quote_char && after_quote_char && !before_str.empty?)
-
quote_char =
-
if before_quote_char && after_quote_char && before_quote_char != after_quote_char
-
before_str.gsub!("\\'", "'")
-
before_str.gsub!('"', "\\\"")
-
after_str.gsub!("\\'", "'")
-
after_str.gsub!('"', "\\\"")
-
'"'
-
else
-
before_quote_char || after_quote_char
-
end
-
-
res = ""
-
res << 'unquote(' if unquote
-
res << quote_char if quote_char
-
res << before_str
-
res << '#{' << @mid.to_sass(opts) << '}'
-
res << after_str
-
res << quote_char if quote_char
-
res << ')' if unquote
-
res
-
end
-
-
# Returns the three components of the interpolation, `before`, `mid`, and `after`.
-
#
-
# @return [Array<Node>]
-
# @see #initialize
-
# @see Node#children
-
1
def children
-
[@before, @mid, @after].compact
-
end
-
-
# @see Node#deep_copy
-
1
def deep_copy
-
node = dup
-
node.instance_variable_set('@before', @before.deep_copy) if @before
-
node.instance_variable_set('@mid', @mid.deep_copy)
-
node.instance_variable_set('@after', @after.deep_copy) if @after
-
node
-
end
-
-
1
protected
-
-
# Evaluates the interpolation.
-
#
-
# @param environment [Sass::Environment] The environment in which to evaluate the SassScript
-
# @return [Sass::Script::String] The SassScript string that is the value of the interpolation
-
1
def _perform(environment)
-
res = ""
-
before = @before.perform(environment)
-
res << before.value
-
mid = @mid.perform(environment)
-
res << (mid.is_a?(Sass::Script::String) ? mid.value : mid.to_s)
-
res << @after.perform(environment).value
-
opts(Sass::Script::String.new(res, before.type))
-
end
-
-
1
private
-
-
1
def parse_str(str)
-
case str
-
when /^unquote\((["'])(.*)\1\)$/
-
return true, $1, $2
-
when '""'
-
return false, nil, ""
-
when /^(["'])(.*)\1$/
-
return false, $1, $2
-
else
-
return false, nil, str
-
end
-
end
-
end
-
end
-
1
module Sass::Script
-
# A SassScript parse node representing a unary operation,
-
# such as `-$b` or `not true`.
-
#
-
# Currently only `-`, `/`, and `not` are unary operators.
-
1
class UnaryOperation < Node
-
# @return [Symbol] The operation to perform
-
1
attr_reader :operator
-
-
# @return [Script::Node] The parse-tree node for the object of the operator
-
1
attr_reader :operand
-
-
# @param operand [Script::Node] See \{#operand}
-
# @param operator [Symbol] See \{#operator}
-
1
def initialize(operand, operator)
-
@operand = operand
-
@operator = operator
-
super()
-
end
-
-
# @return [String] A human-readable s-expression representation of the operation
-
1
def inspect
-
"(#{@operator.inspect} #{@operand.inspect})"
-
end
-
-
# @see Node#to_sass
-
1
def to_sass(opts = {})
-
operand = @operand.to_sass(opts)
-
if @operand.is_a?(Operation) ||
-
(@operator == :minus &&
-
(operand =~ Sass::SCSS::RX::IDENT) == 0)
-
operand = "(#{@operand.to_sass(opts)})"
-
end
-
op = Lexer::OPERATORS_REVERSE[@operator]
-
op + (op =~ /[a-z]/ ? " " : "") + operand
-
end
-
-
# Returns the operand of the operation.
-
#
-
# @return [Array<Node>]
-
# @see Node#children
-
1
def children
-
[@operand]
-
end
-
-
# @see Node#deep_copy
-
1
def deep_copy
-
node = dup
-
node.instance_variable_set('@operand', @operand.deep_copy)
-
node
-
end
-
-
1
protected
-
-
# Evaluates the operation.
-
#
-
# @param environment [Sass::Environment] The environment in which to evaluate the SassScript
-
# @return [Literal] The SassScript object that is the value of the operation
-
# @raise [Sass::SyntaxError] if the operation is undefined for the operand
-
1
def _perform(environment)
-
operator = "unary_#{@operator}"
-
literal = @operand.perform(environment)
-
literal.send(operator)
-
rescue NoMethodError => e
-
raise e unless e.name.to_s == operator.to_s
-
raise Sass::SyntaxError.new("Undefined unary operation: \"#{@operator} #{literal}\".")
-
end
-
end
-
end
-
1
module Sass
-
1
module Script
-
# A SassScript parse node representing a variable.
-
1
class Variable < Node
-
# The name of the variable.
-
#
-
# @return [String]
-
1
attr_reader :name
-
-
# The underscored name of the variable.
-
#
-
# @return [String]
-
1
attr_reader :underscored_name
-
-
# @param name [String] See \{#name}
-
1
def initialize(name)
-
@name = name
-
@underscored_name = name.gsub(/-/,"_")
-
super()
-
end
-
-
# @return [String] A string representation of the variable
-
1
def inspect(opts = {})
-
"$#{dasherize(name, opts)}"
-
end
-
1
alias_method :to_sass, :inspect
-
-
# Returns an empty array.
-
#
-
# @return [Array<Node>] empty
-
# @see Node#children
-
1
def children
-
[]
-
end
-
-
# @see Node#deep_copy
-
1
def deep_copy
-
dup
-
end
-
-
1
protected
-
-
# Evaluates the variable.
-
#
-
# @param environment [Sass::Environment] The environment in which to evaluate the SassScript
-
# @return [Literal] The SassScript object that is the value of the variable
-
# @raise [Sass::SyntaxError] if the variable is undefined
-
1
def _perform(environment)
-
raise SyntaxError.new("Undefined variable: \"$#{name}\".") unless val = environment.var(name)
-
if val.is_a?(Number)
-
val = val.dup
-
val.original = nil
-
end
-
return val
-
end
-
end
-
end
-
end
-
1
require 'sass/scss/rx'
-
1
require 'sass/scss/script_lexer'
-
1
require 'sass/scss/script_parser'
-
1
require 'sass/scss/parser'
-
1
require 'sass/scss/static_parser'
-
1
require 'sass/scss/css_parser'
-
-
1
module Sass
-
# SCSS is the CSS syntax for Sass.
-
# It parses into the same syntax tree as Sass,
-
# and generates the same sort of output CSS.
-
#
-
# This module contains code for the parsing of SCSS.
-
# The evaluation is handled by the broader {Sass} module.
-
1
module SCSS; end
-
end
-
1
require 'sass/script/css_parser'
-
-
1
module Sass
-
1
module SCSS
-
# This is a subclass of {Parser} which only parses plain CSS.
-
# It doesn't support any Sass extensions, such as interpolation,
-
# parent references, nested selectors, and so forth.
-
# It does support all the same CSS hacks as the SCSS parser, though.
-
1
class CssParser < StaticParser
-
1
private
-
-
1
def placeholder_selector; nil; end
-
1
def parent_selector; nil; end
-
1
def interpolation; nil; end
-
1
def use_css_import?; true; end
-
-
1
def block_child(context)
-
case context
-
when :ruleset
-
declaration
-
when :stylesheet
-
directive || ruleset
-
when :directive
-
directive || declaration_or_ruleset
-
end
-
end
-
-
1
def nested_properties!(node, space)
-
expected('expression (e.g. 1px, bold)');
-
end
-
-
1
@sass_script_parser = Class.new(Sass::Script::CssParser)
-
1
@sass_script_parser.send(:include, ScriptParser)
-
end
-
end
-
end
-
1
require 'set'
-
-
1
module Sass
-
1
module SCSS
-
# The parser for SCSS.
-
# It parses a string of code into a tree of {Sass::Tree::Node}s.
-
1
class Parser
-
# @param str [String, StringScanner] The source document to parse.
-
# Note that `Parser` *won't* raise a nice error message if this isn't properly parsed;
-
# for that, you should use the higher-level {Sass::Engine} or {Sass::CSS}.
-
# @param filename [String] The name of the file being parsed. Used for warnings.
-
# @param line [Fixnum] The line on which the source string appeared,
-
# if it's part of another document.
-
1
def initialize(str, filename, line = 1)
-
@template = str
-
@filename = filename
-
@line = line
-
@strs = []
-
end
-
-
# Parses an SCSS document.
-
#
-
# @return [Sass::Tree::RootNode] The root node of the document tree
-
# @raise [Sass::SyntaxError] if there's a syntax error in the document
-
1
def parse
-
init_scanner!
-
root = stylesheet
-
expected("selector or at-rule") unless @scanner.eos?
-
root
-
end
-
-
# Parses an identifier with interpolation.
-
# Note that this won't assert that the identifier takes up the entire input string;
-
# it's meant to be used with `StringScanner`s as part of other parsers.
-
#
-
# @return [Array<String, Sass::Script::Node>, nil]
-
# The interpolated identifier, or nil if none could be parsed
-
1
def parse_interp_ident
-
init_scanner!
-
interp_ident
-
end
-
-
# Parses a media query list.
-
#
-
# @return [Sass::Media::QueryList] The parsed query list
-
# @raise [Sass::SyntaxError] if there's a syntax error in the query list,
-
# or if it doesn't take up the entire input string.
-
1
def parse_media_query_list
-
init_scanner!
-
ql = media_query_list
-
expected("media query list") unless @scanner.eos?
-
ql
-
end
-
-
# Parses a supports query condition.
-
#
-
# @return [Sass::Supports::Condition] The parsed condition
-
# @raise [Sass::SyntaxError] if there's a syntax error in the condition,
-
# or if it doesn't take up the entire input string.
-
1
def parse_supports_condition
-
init_scanner!
-
condition = supports_condition
-
expected("supports condition") unless @scanner.eos?
-
condition
-
end
-
-
1
private
-
-
1
include Sass::SCSS::RX
-
-
1
def init_scanner!
-
@scanner =
-
if @template.is_a?(StringScanner)
-
@template
-
else
-
Sass::Util::MultibyteStringScanner.new(@template.gsub("\r", ""))
-
end
-
end
-
-
1
def stylesheet
-
node = node(Sass::Tree::RootNode.new(@scanner.string))
-
block_contents(node, :stylesheet) {s(node)}
-
end
-
-
1
def s(node)
-
while tok(S) || tok(CDC) || tok(CDO) || (c = tok(SINGLE_LINE_COMMENT)) || (c = tok(COMMENT))
-
next unless c
-
process_comment c, node
-
c = nil
-
end
-
true
-
end
-
-
1
def ss
-
nil while tok(S) || tok(SINGLE_LINE_COMMENT) || tok(COMMENT)
-
true
-
end
-
-
1
def ss_comments(node)
-
while tok(S) || (c = tok(SINGLE_LINE_COMMENT)) || (c = tok(COMMENT))
-
next unless c
-
process_comment c, node
-
c = nil
-
end
-
-
true
-
end
-
-
1
def whitespace
-
return unless tok(S) || tok(SINGLE_LINE_COMMENT) || tok(COMMENT)
-
ss
-
end
-
-
1
def process_comment(text, node)
-
silent = text =~ /^\/\//
-
loud = !silent && text =~ %r{^/[/*]!}
-
line = @line - text.count("\n")
-
-
if silent
-
value = [text.sub(/^\s*\/\//, '/*').gsub(/^\s*\/\//, ' *') + ' */']
-
else
-
value = Sass::Engine.parse_interp(text, line, @scanner.pos - text.size, :filename => @filename)
-
value.unshift(@scanner.
-
string[0...@scanner.pos].
-
reverse[/.*?\*\/(.*?)($|\Z)/, 1].
-
reverse.gsub(/[^\s]/, ' '))
-
end
-
-
type = if silent then :silent elsif loud then :loud else :normal end
-
comment = Sass::Tree::CommentNode.new(value, type)
-
comment.line = line
-
node << comment
-
end
-
-
1
DIRECTIVES = Set[:mixin, :include, :function, :return, :debug, :warn, :for,
-
:each, :while, :if, :else, :extend, :import, :media, :charset, :content,
-
:_moz_document]
-
-
1
PREFIXED_DIRECTIVES = Set[:supports]
-
-
1
def directive
-
return unless tok(/@/)
-
name = tok!(IDENT)
-
ss
-
-
if dir = special_directive(name)
-
return dir
-
elsif dir = prefixed_directive(name)
-
return dir
-
end
-
-
# Most at-rules take expressions (e.g. @import),
-
# but some (e.g. @page) take selector-like arguments.
-
# Some take no arguments at all.
-
val = expr || selector
-
val = val ? ["@#{name} "] + Sass::Util.strip_string_array(val) : ["@#{name}"]
-
directive_body(val)
-
end
-
-
1
def directive_body(value)
-
node = node(Sass::Tree::DirectiveNode.new(value))
-
-
if tok(/\{/)
-
node.has_children = true
-
block_contents(node, :directive)
-
tok!(/\}/)
-
end
-
-
node
-
end
-
-
1
def special_directive(name)
-
sym = name.gsub('-', '_').to_sym
-
DIRECTIVES.include?(sym) && send("#{sym}_directive")
-
end
-
-
1
def prefixed_directive(name)
-
sym = name.gsub(/^-[a-z0-9]+-/i, '').gsub('-', '_').to_sym
-
PREFIXED_DIRECTIVES.include?(sym) && send("#{sym}_directive", name)
-
end
-
-
1
def mixin_directive
-
name = tok! IDENT
-
args, splat = sass_script(:parse_mixin_definition_arglist)
-
ss
-
block(node(Sass::Tree::MixinDefNode.new(name, args, splat)), :directive)
-
end
-
-
1
def include_directive
-
name = tok! IDENT
-
args, keywords, splat = sass_script(:parse_mixin_include_arglist)
-
ss
-
include_node = node(Sass::Tree::MixinNode.new(name, args, keywords, splat))
-
if tok?(/\{/)
-
include_node.has_children = true
-
block(include_node, :directive)
-
else
-
include_node
-
end
-
end
-
-
1
def content_directive
-
ss
-
node(Sass::Tree::ContentNode.new)
-
end
-
-
1
def function_directive
-
name = tok! IDENT
-
args, splat = sass_script(:parse_function_definition_arglist)
-
ss
-
block(node(Sass::Tree::FunctionNode.new(name, args, splat)), :function)
-
end
-
-
1
def return_directive
-
node(Sass::Tree::ReturnNode.new(sass_script(:parse)))
-
end
-
-
1
def debug_directive
-
node(Sass::Tree::DebugNode.new(sass_script(:parse)))
-
end
-
-
1
def warn_directive
-
node(Sass::Tree::WarnNode.new(sass_script(:parse)))
-
end
-
-
1
def for_directive
-
tok!(/\$/)
-
var = tok! IDENT
-
ss
-
-
tok!(/from/)
-
from = sass_script(:parse_until, Set["to", "through"])
-
ss
-
-
@expected = '"to" or "through"'
-
exclusive = (tok(/to/) || tok!(/through/)) == 'to'
-
to = sass_script(:parse)
-
ss
-
-
block(node(Sass::Tree::ForNode.new(var, from, to, exclusive)), :directive)
-
end
-
-
1
def each_directive
-
tok!(/\$/)
-
var = tok! IDENT
-
ss
-
-
tok!(/in/)
-
list = sass_script(:parse)
-
ss
-
-
block(node(Sass::Tree::EachNode.new(var, list)), :directive)
-
end
-
-
1
def while_directive
-
expr = sass_script(:parse)
-
ss
-
block(node(Sass::Tree::WhileNode.new(expr)), :directive)
-
end
-
-
1
def if_directive
-
expr = sass_script(:parse)
-
ss
-
node = block(node(Sass::Tree::IfNode.new(expr)), :directive)
-
pos = @scanner.pos
-
line = @line
-
ss
-
-
else_block(node) ||
-
begin
-
# Backtrack in case there are any comments we want to parse
-
@scanner.pos = pos
-
@line = line
-
node
-
end
-
end
-
-
1
def else_block(node)
-
return unless tok(/@else/)
-
ss
-
else_node = block(
-
Sass::Tree::IfNode.new((sass_script(:parse) if tok(/if/))),
-
:directive)
-
node.add_else(else_node)
-
pos = @scanner.pos
-
line = @line
-
ss
-
-
else_block(node) ||
-
begin
-
# Backtrack in case there are any comments we want to parse
-
@scanner.pos = pos
-
@line = line
-
node
-
end
-
end
-
-
1
def else_directive
-
err("Invalid CSS: @else must come after @if")
-
end
-
-
1
def extend_directive
-
selector = expr!(:selector_sequence)
-
optional = tok(OPTIONAL)
-
ss
-
node(Sass::Tree::ExtendNode.new(selector, !!optional))
-
end
-
-
1
def import_directive
-
values = []
-
-
loop do
-
values << expr!(:import_arg)
-
break if use_css_import?
-
break unless tok(/,/)
-
ss
-
end
-
-
return values
-
end
-
-
1
def import_arg
-
line = @line
-
return unless (str = tok(STRING)) || (uri = tok?(/url\(/i))
-
if uri
-
str = sass_script(:parse_string)
-
ss
-
media = media_query_list
-
ss
-
return node(Tree::CssImportNode.new(str, media.to_a))
-
end
-
-
path = @scanner[1] || @scanner[2]
-
ss
-
-
media = media_query_list
-
if path =~ /^(https?:)?\/\// || media || use_css_import?
-
node = Sass::Tree::CssImportNode.new(str, media.to_a)
-
else
-
node = Sass::Tree::ImportNode.new(path.strip)
-
end
-
node.line = line
-
node
-
end
-
-
1
def use_css_import?; false; end
-
-
1
def media_directive
-
block(node(Sass::Tree::MediaNode.new(expr!(:media_query_list).to_a)), :directive)
-
end
-
-
# http://www.w3.org/TR/css3-mediaqueries/#syntax
-
1
def media_query_list
-
return unless query = media_query
-
queries = [query]
-
-
ss
-
while tok(/,/)
-
ss; queries << expr!(:media_query)
-
end
-
ss
-
-
Sass::Media::QueryList.new(queries)
-
end
-
-
1
def media_query
-
if ident1 = interp_ident
-
ss
-
ident2 = interp_ident
-
ss
-
if ident2 && ident2.length == 1 && ident2[0].is_a?(String) && ident2[0].downcase == 'and'
-
query = Sass::Media::Query.new([], ident1, [])
-
else
-
if ident2
-
query = Sass::Media::Query.new(ident1, ident2, [])
-
else
-
query = Sass::Media::Query.new([], ident1, [])
-
end
-
return query unless tok(/and/i)
-
ss
-
end
-
end
-
-
if query
-
expr = expr!(:media_expr)
-
else
-
return unless expr = media_expr
-
end
-
query ||= Sass::Media::Query.new([], [], [])
-
query.expressions << expr
-
-
ss
-
while tok(/and/i)
-
ss; query.expressions << expr!(:media_expr)
-
end
-
-
query
-
end
-
-
1
def media_expr
-
interp = interpolation and return interp
-
return unless tok(/\(/)
-
res = ['(']
-
ss
-
res << sass_script(:parse)
-
-
if tok(/:/)
-
res << ': '
-
ss
-
res << sass_script(:parse)
-
end
-
res << tok!(/\)/)
-
ss
-
res
-
end
-
-
1
def charset_directive
-
tok! STRING
-
name = @scanner[1] || @scanner[2]
-
ss
-
node(Sass::Tree::CharsetNode.new(name))
-
end
-
-
# The document directive is specified in
-
# http://www.w3.org/TR/css3-conditional/, but Gecko allows the
-
# `url-prefix` and `domain` functions to omit quotation marks, contrary to
-
# the standard.
-
#
-
# We could parse all document directives according to Mozilla's syntax,
-
# but if someone's using e.g. @-webkit-document we don't want them to
-
# think WebKit works sans quotes.
-
1
def _moz_document_directive
-
res = ["@-moz-document "]
-
loop do
-
res << str{ss} << expr!(:moz_document_function)
-
break unless c = tok(/,/)
-
res << c
-
end
-
directive_body(res.flatten)
-
end
-
-
1
def moz_document_function
-
return unless val = interp_uri || _interp_string(:url_prefix) ||
-
_interp_string(:domain) || function(!:allow_var) || interpolation
-
ss
-
val
-
end
-
-
# http://www.w3.org/TR/css3-conditional/
-
1
def supports_directive(name)
-
condition = expr!(:supports_condition)
-
node = node(Sass::Tree::SupportsNode.new(name, condition))
-
-
tok!(/\{/)
-
node.has_children = true
-
block_contents(node, :directive)
-
tok!(/\}/)
-
-
node
-
end
-
-
1
def supports_condition
-
supports_negation || supports_operator || supports_interpolation
-
end
-
-
1
def supports_negation
-
return unless tok(/not/i)
-
ss
-
Sass::Supports::Negation.new(expr!(:supports_condition_in_parens))
-
end
-
-
1
def supports_operator
-
return unless cond = supports_condition_in_parens
-
return cond unless op = tok(/and|or/i)
-
begin
-
ss
-
cond = Sass::Supports::Operator.new(
-
cond, expr!(:supports_condition_in_parens), op)
-
end while op = tok(/and|or/i)
-
cond
-
end
-
-
1
def supports_condition_in_parens
-
interp = supports_interpolation and return interp
-
return unless tok(/\(/); ss
-
if cond = supports_condition
-
tok!(/\)/); ss
-
cond
-
else
-
name = sass_script(:parse)
-
tok!(/:/); ss
-
value = sass_script(:parse)
-
tok!(/\)/); ss
-
Sass::Supports::Declaration.new(name, value)
-
end
-
end
-
-
1
def supports_declaration_condition
-
return unless tok(/\(/); ss
-
supports_declaration_body
-
end
-
-
1
def supports_interpolation
-
return unless interp = interpolation
-
ss
-
Sass::Supports::Interpolation.new(interp)
-
end
-
-
1
def variable
-
return unless tok(/\$/)
-
name = tok!(IDENT)
-
ss; tok!(/:/); ss
-
-
expr = sass_script(:parse)
-
guarded = tok(DEFAULT)
-
node(Sass::Tree::VariableNode.new(name, expr, guarded))
-
end
-
-
1
def operator
-
# Many of these operators (all except / and ,)
-
# are disallowed by the CSS spec,
-
# but they're included here for compatibility
-
# with some proprietary MS properties
-
str {ss if tok(/[\/,:.=]/)}
-
end
-
-
1
def ruleset
-
return unless rules = selector_sequence
-
block(node(Sass::Tree::RuleNode.new(rules.flatten.compact)), :ruleset)
-
end
-
-
1
def block(node, context)
-
node.has_children = true
-
tok!(/\{/)
-
block_contents(node, context)
-
tok!(/\}/)
-
node
-
end
-
-
# A block may contain declarations and/or rulesets
-
1
def block_contents(node, context)
-
block_given? ? yield : ss_comments(node)
-
node << (child = block_child(context))
-
while tok(/;/) || has_children?(child)
-
block_given? ? yield : ss_comments(node)
-
node << (child = block_child(context))
-
end
-
node
-
end
-
-
1
def block_child(context)
-
return variable || directive if context == :function
-
return variable || directive || ruleset if context == :stylesheet
-
variable || directive || declaration_or_ruleset
-
end
-
-
1
def has_children?(child_or_array)
-
return false unless child_or_array
-
return child_or_array.last.has_children if child_or_array.is_a?(Array)
-
return child_or_array.has_children
-
end
-
-
# This is a nasty hack, and the only place in the parser
-
# that requires a large amount of backtracking.
-
# The reason is that we can't figure out if certain strings
-
# are declarations or rulesets with fixed finite lookahead.
-
# For example, "foo:bar baz baz baz..." could be either a property
-
# or a selector.
-
#
-
# To handle this, we simply check if it works as a property
-
# (which is the most common case)
-
# and, if it doesn't, try it as a ruleset.
-
#
-
# We could eke some more efficiency out of this
-
# by handling some easy cases (first token isn't an identifier,
-
# no colon after the identifier, whitespace after the colon),
-
# but I'm not sure the gains would be worth the added complexity.
-
1
def declaration_or_ruleset
-
old_use_property_exception, @use_property_exception =
-
@use_property_exception, false
-
decl_err = catch_error do
-
decl = declaration
-
unless decl && decl.has_children
-
# We want an exception if it's not there,
-
# but we don't want to consume if it is
-
tok!(/[;}]/) unless tok?(/[;}]/)
-
end
-
return decl
-
end
-
-
ruleset_err = catch_error {return ruleset}
-
rethrow(@use_property_exception ? decl_err : ruleset_err)
-
ensure
-
@use_property_exception = old_use_property_exception
-
end
-
-
1
def selector_sequence
-
if sel = tok(STATIC_SELECTOR, true)
-
return [sel]
-
end
-
-
rules = []
-
return unless v = selector
-
rules.concat v
-
-
ws = ''
-
while tok(/,/)
-
ws << str {ss}
-
if v = selector
-
rules << ',' << ws
-
rules.concat v
-
ws = ''
-
end
-
end
-
rules
-
end
-
-
1
def selector
-
return unless sel = _selector
-
sel.to_a
-
end
-
-
1
def selector_comma_sequence
-
return unless sel = _selector
-
selectors = [sel]
-
ws = ''
-
while tok(/,/)
-
ws << str{ss}
-
if sel = _selector
-
selectors << sel
-
selectors[-1] = Selector::Sequence.new(["\n"] + selectors.last.members) if ws.include?("\n")
-
ws = ''
-
end
-
end
-
Selector::CommaSequence.new(selectors)
-
end
-
-
1
def _selector
-
# The combinator here allows the "> E" hack
-
return unless val = combinator || simple_selector_sequence
-
nl = str{ss}.include?("\n")
-
res = []
-
res << val
-
res << "\n" if nl
-
-
while val = combinator || simple_selector_sequence
-
res << val
-
res << "\n" if str{ss}.include?("\n")
-
end
-
Selector::Sequence.new(res.compact)
-
end
-
-
1
def combinator
-
tok(PLUS) || tok(GREATER) || tok(TILDE) || reference_combinator
-
end
-
-
1
def reference_combinator
-
return unless tok(/\//)
-
res = ['/']
-
ns, name = expr!(:qualified_name)
-
res << ns << '|' if ns
-
res << name << tok!(/\//)
-
res = res.flatten
-
res = res.join '' if res.all? {|e| e.is_a?(String)}
-
res
-
end
-
-
1
def simple_selector_sequence
-
# Returning expr by default allows for stuff like
-
# http://www.w3.org/TR/css3-animations/#keyframes-
-
return expr(!:allow_var) unless e = element_name || id_selector ||
-
class_selector || placeholder_selector || attrib || pseudo ||
-
parent_selector || interpolation_selector
-
res = [e]
-
-
# The tok(/\*/) allows the "E*" hack
-
while v = id_selector || class_selector || placeholder_selector || attrib ||
-
pseudo || interpolation_selector ||
-
(tok(/\*/) && Selector::Universal.new(nil))
-
res << v
-
end
-
-
pos = @scanner.pos
-
line = @line
-
if sel = str? {simple_selector_sequence}
-
@scanner.pos = pos
-
@line = line
-
begin
-
# If we see "*E", don't force a throw because this could be the
-
# "*prop: val" hack.
-
expected('"{"') if res.length == 1 && res[0].is_a?(Selector::Universal)
-
throw_error {expected('"{"')}
-
rescue Sass::SyntaxError => e
-
e.message << "\n\n\"#{sel}\" may only be used at the beginning of a compound selector."
-
raise e
-
end
-
end
-
-
Selector::SimpleSequence.new(res, tok(/!/))
-
end
-
-
1
def parent_selector
-
return unless tok(/&/)
-
Selector::Parent.new
-
end
-
-
1
def class_selector
-
return unless tok(/\./)
-
@expected = "class name"
-
Selector::Class.new(merge(expr!(:interp_ident)))
-
end
-
-
1
def id_selector
-
return unless tok(/#(?!\{)/)
-
@expected = "id name"
-
Selector::Id.new(merge(expr!(:interp_name)))
-
end
-
-
1
def placeholder_selector
-
return unless tok(/%/)
-
@expected = "placeholder name"
-
Selector::Placeholder.new(merge(expr!(:interp_ident)))
-
end
-
-
1
def element_name
-
ns, name = Sass::Util.destructure(qualified_name(:allow_star_name))
-
return unless ns || name
-
-
if name == '*'
-
Selector::Universal.new(merge(ns))
-
else
-
Selector::Element.new(merge(name), merge(ns))
-
end
-
end
-
-
1
def qualified_name(allow_star_name=false)
-
return unless name = interp_ident || tok(/\*/) || (tok?(/\|/) && "")
-
return nil, name unless tok(/\|/)
-
-
return name, expr!(:interp_ident) unless allow_star_name
-
@expected = "identifier or *"
-
return name, interp_ident || tok!(/\*/)
-
end
-
-
1
def interpolation_selector
-
return unless script = interpolation
-
Selector::Interpolation.new(script)
-
end
-
-
1
def attrib
-
return unless tok(/\[/)
-
ss
-
ns, name = attrib_name!
-
ss
-
-
if op = tok(/=/) ||
-
tok(INCLUDES) ||
-
tok(DASHMATCH) ||
-
tok(PREFIXMATCH) ||
-
tok(SUFFIXMATCH) ||
-
tok(SUBSTRINGMATCH)
-
@expected = "identifier or string"
-
ss
-
val = interp_ident || expr!(:interp_string)
-
ss
-
end
-
flags = interp_ident || interp_string
-
tok!(/\]/)
-
-
Selector::Attribute.new(merge(name), merge(ns), op, merge(val), merge(flags))
-
end
-
-
1
def attrib_name!
-
if name_or_ns = interp_ident
-
# E, E|E
-
if tok(/\|(?!=)/)
-
ns = name_or_ns
-
name = interp_ident
-
else
-
name = name_or_ns
-
end
-
else
-
# *|E or |E
-
ns = [tok(/\*/) || ""]
-
tok!(/\|/)
-
name = expr!(:interp_ident)
-
end
-
return ns, name
-
end
-
-
1
def pseudo
-
return unless s = tok(/::?/)
-
@expected = "pseudoclass or pseudoelement"
-
name = expr!(:interp_ident)
-
if tok(/\(/)
-
ss
-
arg = expr!(:pseudo_arg)
-
while tok(/,/)
-
arg << ',' << str{ss}
-
arg.concat expr!(:pseudo_arg)
-
end
-
tok!(/\)/)
-
end
-
Selector::Pseudo.new(s == ':' ? :class : :element, merge(name), merge(arg))
-
end
-
-
1
def pseudo_arg
-
# In the CSS spec, every pseudo-class/element either takes a pseudo
-
# expression or a selector comma sequence as an argument. However, we
-
# don't want to have to know which takes which, so we handle both at
-
# once.
-
#
-
# However, there are some ambiguities between the two. For instance, "n"
-
# could start a pseudo expression like "n+1", or it could start a
-
# selector like "n|m". In order to handle this, we must regrettably
-
# backtrack.
-
expr, sel = nil, nil
-
pseudo_err = catch_error do
-
expr = pseudo_expr
-
next if tok?(/[,)]/)
-
expr = nil
-
expected '")"'
-
end
-
-
return expr if expr
-
sel_err = catch_error {sel = selector}
-
return sel if sel
-
rethrow pseudo_err if pseudo_err
-
rethrow sel_err if sel_err
-
return
-
end
-
-
1
def pseudo_expr
-
return unless e = tok(PLUS) || tok(/[-*]/) || tok(NUMBER) ||
-
interp_string || tok(IDENT) || interpolation
-
res = [e, str{ss}]
-
while e = tok(PLUS) || tok(/[-*]/) || tok(NUMBER) ||
-
interp_string || tok(IDENT) || interpolation
-
res << e << str{ss}
-
end
-
res
-
end
-
-
1
def declaration
-
# This allows the "*prop: val", ":prop: val", and ".prop: val" hacks
-
if s = tok(/[:\*\.]|\#(?!\{)/)
-
@use_property_exception = s !~ /[\.\#]/
-
name = [s, str{ss}, *expr!(:interp_ident)]
-
else
-
return unless name = interp_ident
-
name = [name] if name.is_a?(String)
-
end
-
if comment = tok(COMMENT)
-
name << comment
-
end
-
ss
-
-
tok!(/:/)
-
space, value = value!
-
ss
-
require_block = tok?(/\{/)
-
-
node = node(Sass::Tree::PropNode.new(name.flatten.compact, value, :new))
-
-
return node unless require_block
-
nested_properties! node, space
-
end
-
-
1
def value!
-
space = !str {ss}.empty?
-
@use_property_exception ||= space || !tok?(IDENT)
-
-
return true, Sass::Script::String.new("") if tok?(/\{/)
-
# This is a bit of a dirty trick:
-
# if the value is completely static,
-
# we don't parse it at all, and instead return a plain old string
-
# containing the value.
-
# This results in a dramatic speed increase.
-
if val = tok(STATIC_VALUE, true)
-
return space, Sass::Script::String.new(val.strip)
-
end
-
return space, sass_script(:parse)
-
end
-
-
1
def nested_properties!(node, space)
-
err(<<MESSAGE) unless space
-
Invalid CSS: a space is required between a property and its definition
-
when it has other properties nested beneath it.
-
MESSAGE
-
-
@use_property_exception = true
-
@expected = 'expression (e.g. 1px, bold) or "{"'
-
block(node, :property)
-
end
-
-
1
def expr(allow_var = true)
-
return unless t = term(allow_var)
-
res = [t, str{ss}]
-
-
while (o = operator) && (t = term(allow_var))
-
res << o << t << str{ss}
-
end
-
-
res.flatten
-
end
-
-
1
def term(allow_var)
-
if e = tok(NUMBER) ||
-
interp_uri ||
-
function(allow_var) ||
-
interp_string ||
-
tok(UNICODERANGE) ||
-
interp_ident ||
-
tok(HEXCOLOR) ||
-
(allow_var && var_expr)
-
return e
-
end
-
-
return unless op = tok(/[+-]/)
-
@expected = "number or function"
-
return [op, tok(NUMBER) || function(allow_var) ||
-
(allow_var && var_expr) || expr!(:interpolation)]
-
end
-
-
1
def function(allow_var)
-
return unless name = tok(FUNCTION)
-
if name == "expression(" || name == "calc("
-
str, _ = Sass::Shared.balance(@scanner, ?(, ?), 1)
-
[name, str]
-
else
-
[name, str{ss}, expr(allow_var), tok!(/\)/)]
-
end
-
end
-
-
1
def var_expr
-
return unless tok(/\$/)
-
line = @line
-
var = Sass::Script::Variable.new(tok!(IDENT))
-
var.line = line
-
var
-
end
-
-
1
def interpolation
-
return unless tok(INTERP_START)
-
sass_script(:parse_interpolated)
-
end
-
-
1
def interp_string
-
_interp_string(:double) || _interp_string(:single)
-
end
-
-
1
def interp_uri
-
_interp_string(:uri)
-
end
-
-
1
def _interp_string(type)
-
return unless start = tok(Sass::Script::Lexer::STRING_REGULAR_EXPRESSIONS[type][false])
-
res = [start]
-
-
mid_re = Sass::Script::Lexer::STRING_REGULAR_EXPRESSIONS[type][true]
-
# @scanner[2].empty? means we've started an interpolated section
-
while @scanner[2] == '#{'
-
@scanner.pos -= 2 # Don't consume the #{
-
res.last.slice!(-2..-1)
-
res << expr!(:interpolation) << tok(mid_re)
-
end
-
res
-
end
-
-
1
def interp_ident(start = IDENT)
-
return unless val = tok(start) || interpolation || tok(IDENT_HYPHEN_INTERP, true)
-
res = [val]
-
while val = tok(NAME) || interpolation
-
res << val
-
end
-
res
-
end
-
-
1
def interp_ident_or_var
-
(id = interp_ident) and return id
-
(var = var_expr) and return [var]
-
end
-
-
1
def interp_name
-
interp_ident NAME
-
end
-
-
1
def str
-
@strs.push ""
-
yield
-
@strs.last
-
ensure
-
@strs.pop
-
end
-
-
1
def str?
-
pos = @scanner.pos
-
line = @line
-
@strs.push ""
-
throw_error {yield} && @strs.last
-
rescue Sass::SyntaxError
-
@scanner.pos = pos
-
@line = line
-
nil
-
ensure
-
@strs.pop
-
end
-
-
1
def node(node)
-
node.line = @line
-
node
-
end
-
-
1
@sass_script_parser = Class.new(Sass::Script::Parser)
-
1
@sass_script_parser.send(:include, ScriptParser)
-
# @private
-
1
def self.sass_script_parser; @sass_script_parser; end
-
-
1
def sass_script(*args)
-
parser = self.class.sass_script_parser.new(@scanner, @line,
-
@scanner.pos - (@scanner.string[0...@scanner.pos].rindex("\n") || 0))
-
result = parser.send(*args)
-
unless @strs.empty?
-
# Convert to CSS manually so that comments are ignored.
-
src = result.to_sass
-
@strs.each {|s| s << src}
-
end
-
@line = parser.line
-
result
-
rescue Sass::SyntaxError => e
-
throw(:_sass_parser_error, true) if @throw_error
-
raise e
-
end
-
-
1
def merge(arr)
-
arr && Sass::Util.merge_adjacent_strings([arr].flatten)
-
end
-
-
1
EXPR_NAMES = {
-
:media_query => "media query (e.g. print, screen, print and screen)",
-
:media_query_list => "media query (e.g. print, screen, print and screen)",
-
:media_expr => "media expression (e.g. (min-device-width: 800px))",
-
:pseudo_arg => "expression (e.g. fr, 2n+1)",
-
:interp_ident => "identifier",
-
:interp_name => "identifier",
-
:qualified_name => "identifier",
-
:expr => "expression (e.g. 1px, bold)",
-
:_selector => "selector",
-
:selector_comma_sequence => "selector",
-
:simple_selector_sequence => "selector",
-
:import_arg => "file to import (string or url())",
-
:moz_document_function => "matching function (e.g. url-prefix(), domain())",
-
:supports_condition => "@supports condition (e.g. (display: flexbox))",
-
:supports_condition_in_parens => "@supports condition (e.g. (display: flexbox))",
-
}
-
-
1
TOK_NAMES = Sass::Util.to_hash(
-
52
Sass::SCSS::RX.constants.map {|c| [Sass::SCSS::RX.const_get(c), c.downcase]}).
-
merge(IDENT => "identifier", /[;}]/ => '";"')
-
-
1
def tok?(rx)
-
@scanner.match?(rx)
-
end
-
-
1
def expr!(name)
-
(e = send(name)) && (return e)
-
expected(EXPR_NAMES[name] || name.to_s)
-
end
-
-
1
def tok!(rx)
-
(t = tok(rx)) && (return t)
-
name = TOK_NAMES[rx]
-
-
unless name
-
# Display basic regexps as plain old strings
-
string = rx.source.gsub(/\\(.)/, '\1')
-
name = rx.source == Regexp.escape(string) ? string.inspect : rx.inspect
-
end
-
-
expected(name)
-
end
-
-
1
def expected(name)
-
throw(:_sass_parser_error, true) if @throw_error
-
self.class.expected(@scanner, @expected || name, @line)
-
end
-
-
1
def err(msg)
-
throw(:_sass_parser_error, true) if @throw_error
-
raise Sass::SyntaxError.new(msg, :line => @line)
-
end
-
-
1
def throw_error
-
old_throw_error, @throw_error = @throw_error, false
-
yield
-
ensure
-
@throw_error = old_throw_error
-
end
-
-
1
def catch_error(&block)
-
old_throw_error, @throw_error = @throw_error, true
-
pos = @scanner.pos
-
line = @line
-
expected = @expected
-
if catch(:_sass_parser_error) {yield; false}
-
@scanner.pos = pos
-
@line = line
-
@expected = expected
-
{:pos => pos, :line => line, :expected => @expected, :block => block}
-
end
-
ensure
-
@throw_error = old_throw_error
-
end
-
-
1
def rethrow(err)
-
if @throw_error
-
throw :_sass_parser_error, err
-
else
-
@scanner = Sass::Util::MultibyteStringScanner.new(@scanner.string)
-
@scanner.pos = err[:pos]
-
@line = err[:line]
-
@expected = err[:expected]
-
err[:block].call
-
end
-
end
-
-
# @private
-
1
def self.expected(scanner, expected, line)
-
pos = scanner.pos
-
-
after = scanner.string[0...pos]
-
# Get rid of whitespace between pos and the last token,
-
# but only if there's a newline in there
-
after.gsub!(/\s*\n\s*$/, '')
-
# Also get rid of stuff before the last newline
-
after.gsub!(/.*\n/, '')
-
after = "..." + after[-15..-1] if after.size > 18
-
-
was = scanner.rest.dup
-
# Get rid of whitespace between pos and the next token,
-
# but only if there's a newline in there
-
was.gsub!(/^\s*\n\s*/, '')
-
# Also get rid of stuff after the next newline
-
was.gsub!(/\n.*/, '')
-
was = was[0...15] + "..." if was.size > 18
-
-
raise Sass::SyntaxError.new(
-
"Invalid CSS after \"#{after}\": expected #{expected}, was \"#{was}\"",
-
:line => line)
-
end
-
-
# Avoid allocating lots of new strings for `#tok`.
-
# This is important because `#tok` is called all the time.
-
1
NEWLINE = "\n"
-
-
1
def tok(rx, last_group_lookahead = false)
-
res = @scanner.scan(rx)
-
if res
-
# This fixes https://github.com/nex3/sass/issues/104, which affects
-
# Ruby 1.8.7 and REE. This fix is to replace the ?= zero-width
-
# positive lookahead operator in the Regexp (which matches without
-
# consuming the matched group), with a match that does consume the
-
# group, but then rewinds the scanner and removes the group from the
-
# end of the matched string. This fix makes the assumption that the
-
# matched group will always occur at the end of the match.
-
if last_group_lookahead && @scanner[-1]
-
@scanner.pos -= @scanner[-1].length
-
res.slice!(-@scanner[-1].length..-1)
-
end
-
@line += res.count(NEWLINE)
-
@expected = nil
-
if !@strs.empty? && rx != COMMENT && rx != SINGLE_LINE_COMMENT
-
@strs.each {|s| s << res}
-
end
-
res
-
end
-
end
-
end
-
end
-
end
-
1
module Sass
-
1
module SCSS
-
# A module containing regular expressions used
-
# for lexing tokens in an SCSS document.
-
# Most of these are taken from [the CSS3 spec](http://www.w3.org/TR/css3-syntax/#lexical),
-
# although some have been modified for various reasons.
-
1
module RX
-
# Takes a string and returns a CSS identifier
-
# that will have the value of the given string.
-
#
-
# @param str [String] The string to escape
-
# @return [String] The escaped string
-
1
def self.escape_ident(str)
-
return "" if str.empty?
-
return "\\#{str}" if str == '-' || str == '_'
-
out = ""
-
value = str.dup
-
out << value.slice!(0...1) if value =~ /^[-_]/
-
if value[0...1] =~ NMSTART
-
out << value.slice!(0...1)
-
else
-
out << escape_char(value.slice!(0...1))
-
end
-
out << value.gsub(/[^a-zA-Z0-9_-]/) {|c| escape_char c}
-
return out
-
end
-
-
# Escapes a single character for a CSS identifier.
-
#
-
# @param c [String] The character to escape. Should have length 1
-
# @return [String] The escaped character
-
# @private
-
1
def self.escape_char(c)
-
return "\\%06x" % Sass::Util.ord(c) unless c =~ /[ -\/:-~]/
-
return "\\#{c}"
-
end
-
-
# Creates a Regexp from a plain text string,
-
# escaping all significant characters.
-
#
-
# @param str [String] The text of the regexp
-
# @param flags [Fixnum] Flags for the created regular expression
-
# @return [Regexp]
-
# @private
-
1
def self.quote(str, flags = 0)
-
8
Regexp.new(Regexp.quote(str), flags)
-
end
-
-
1
H = /[0-9a-fA-F]/
-
1
NL = /\n|\r\n|\r|\f/
-
1
UNICODE = /\\#{H}{1,6}[ \t\r\n\f]?/
-
1
s = if Sass::Util.ruby1_8?
-
'\200-\377'
-
elsif Sass::Util.macruby?
-
'\u0080-\uD7FF\uE000-\uFFFD\U00010000-\U0010FFFF'
-
else
-
1
'\u{80}-\u{D7FF}\u{E000}-\u{FFFD}\u{10000}-\u{10FFFF}'
-
end
-
1
NONASCII = /[#{s}]/
-
1
ESCAPE = /#{UNICODE}|\\[ -~#{s}]/
-
1
NMSTART = /[_a-zA-Z]|#{NONASCII}|#{ESCAPE}/
-
1
NMCHAR = /[a-zA-Z0-9_-]|#{NONASCII}|#{ESCAPE}/
-
1
STRING1 = /\"((?:[^\n\r\f\\"]|\\#{NL}|#{ESCAPE})*)\"/
-
1
STRING2 = /\'((?:[^\n\r\f\\']|\\#{NL}|#{ESCAPE})*)\'/
-
-
1
IDENT = /-?#{NMSTART}#{NMCHAR}*/
-
1
NAME = /#{NMCHAR}+/
-
1
NUM = /[0-9]+|[0-9]*\.[0-9]+/
-
1
STRING = /#{STRING1}|#{STRING2}/
-
1
URLCHAR = /[#%&*-~]|#{NONASCII}|#{ESCAPE}/
-
1
URL = /(#{URLCHAR}*)/
-
1
W = /[ \t\r\n\f]*/
-
1
VARIABLE = /(\$)(#{Sass::SCSS::RX::IDENT})/
-
-
# This is more liberal than the spec's definition,
-
# but that definition didn't work well with the greediness rules
-
1
RANGE = /(?:#{H}|\?){1,6}/
-
-
##
-
-
1
S = /[ \t\r\n\f]+/
-
-
1
COMMENT = /\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\//
-
1
SINGLE_LINE_COMMENT = /\/\/.*(\n[ \t]*\/\/.*)*/
-
-
1
CDO = quote("<!--")
-
1
CDC = quote("-->")
-
1
INCLUDES = quote("~=")
-
1
DASHMATCH = quote("|=")
-
1
PREFIXMATCH = quote("^=")
-
1
SUFFIXMATCH = quote("$=")
-
1
SUBSTRINGMATCH = quote("*=")
-
-
1
HASH = /##{NAME}/
-
-
1
IMPORTANT = /!#{W}important/i
-
1
DEFAULT = /!#{W}default/i
-
-
1
NUMBER = /#{NUM}(?:#{IDENT}|%)?/
-
-
1
URI = /url\(#{W}(?:#{STRING}|#{URL})#{W}\)/i
-
1
FUNCTION = /#{IDENT}\(/
-
-
1
UNICODERANGE = /u\+(?:#{H}{1,6}-#{H}{1,6}|#{RANGE})/i
-
-
# Defined in http://www.w3.org/TR/css3-selectors/#lex
-
1
PLUS = /#{W}\+/
-
1
GREATER = /#{W}>/
-
1
TILDE = /#{W}~/
-
1
NOT = quote(":not(", Regexp::IGNORECASE)
-
-
# Defined in https://developer.mozilla.org/en/CSS/@-moz-document as a
-
# non-standard version of http://www.w3.org/TR/css3-conditional/
-
1
URL_PREFIX = /url-prefix\(#{W}(?:#{STRING}|#{URL})#{W}\)/i
-
1
DOMAIN = /domain\(#{W}(?:#{STRING}|#{URL})#{W}\)/i
-
-
# Custom
-
1
HEXCOLOR = /\#[0-9a-fA-F]+/
-
1
INTERP_START = /#\{/
-
1
ANY = /:(-[-\w]+-)?any\(/i
-
1
OPTIONAL = /!#{W}optional/i
-
-
1
IDENT_HYPHEN_INTERP = /-(#\{)/
-
1
STRING1_NOINTERP = /\"((?:[^\n\r\f\\"#]|#(?!\{)|\\#{NL}|#{ESCAPE})*)\"/
-
1
STRING2_NOINTERP = /\'((?:[^\n\r\f\\'#]|#(?!\{)|\\#{NL}|#{ESCAPE})*)\'/
-
1
STRING_NOINTERP = /#{STRING1_NOINTERP}|#{STRING2_NOINTERP}/
-
-
1
STATIC_COMPONENT = /#{IDENT}|#{STRING_NOINTERP}|#{HEXCOLOR}|[+-]?#{NUMBER}|\!important/i
-
1
STATIC_VALUE = /#{STATIC_COMPONENT}(\s*[\s,\/]\s*#{STATIC_COMPONENT})*([;}])/i
-
1
STATIC_SELECTOR = /(#{NMCHAR}|[ \t]|[,>+*]|[:#.]#{NMSTART}){0,50}([{])/i
-
end
-
end
-
end
-
1
module Sass
-
1
module SCSS
-
# A mixin for subclasses of {Sass::Script::Lexer}
-
# that makes them usable by {SCSS::Parser} to parse SassScript.
-
# In particular, the lexer doesn't support `!` for a variable prefix.
-
1
module ScriptLexer
-
1
private
-
-
1
def variable
-
return [:raw, "!important"] if scan(Sass::SCSS::RX::IMPORTANT)
-
_variable(Sass::SCSS::RX::VARIABLE)
-
end
-
end
-
end
-
end
-
1
module Sass
-
1
module SCSS
-
# A mixin for subclasses of {Sass::Script::Parser}
-
# that makes them usable by {SCSS::Parser} to parse SassScript.
-
# In particular, the parser won't raise an error
-
# when there's more content in the lexer once lexing is done.
-
# In addition, the parser doesn't support `!` for a variable prefix.
-
1
module ScriptParser
-
1
private
-
-
# @private
-
1
def lexer_class
-
klass = Class.new(super)
-
klass.send(:include, ScriptLexer)
-
klass
-
end
-
-
# Instead of raising an error when the parser is done,
-
# rewind the StringScanner so that it hasn't consumed the final token.
-
1
def assert_done
-
@lexer.unpeek!
-
end
-
end
-
end
-
end
-
1
require 'sass/script/css_parser'
-
-
1
module Sass
-
1
module SCSS
-
# A parser for a static SCSS tree.
-
# Parses with SCSS extensions, like nested rules and parent selectors,
-
# but without dynamic SassScript.
-
# This is useful for e.g. \{#parse\_selector parsing selectors}
-
# after resolving the interpolation.
-
1
class StaticParser < Parser
-
# Parses the text as a selector.
-
#
-
# @param filename [String, nil] The file in which the selector appears,
-
# or nil if there is no such file.
-
# Used for error reporting.
-
# @return [Selector::CommaSequence] The parsed selector
-
# @raise [Sass::SyntaxError] if there's a syntax error in the selector
-
1
def parse_selector
-
init_scanner!
-
seq = expr!(:selector_comma_sequence)
-
expected("selector") unless @scanner.eos?
-
seq.line = @line
-
seq.filename = @filename
-
seq
-
end
-
-
1
private
-
-
1
def moz_document_function
-
return unless val = tok(URI) || tok(URL_PREFIX) || tok(DOMAIN) ||
-
function(!:allow_var)
-
ss
-
[val]
-
end
-
-
1
def variable; nil; end
-
1
def script_value; nil; end
-
1
def interpolation; nil; end
-
1
def var_expr; nil; end
-
1
def interp_string; s = tok(STRING) and [s]; end
-
1
def interp_uri; s = tok(URI) and [s]; end
-
1
def interp_ident(ident = IDENT); s = tok(ident) and [s]; end
-
1
def use_css_import?; true; end
-
-
1
def special_directive(name)
-
return unless %w[media import charset -moz-document].include?(name)
-
super
-
end
-
-
1
@sass_script_parser = Class.new(Sass::Script::CssParser)
-
1
@sass_script_parser.send(:include, ScriptParser)
-
end
-
end
-
end
-
1
require 'sass/selector/simple'
-
1
require 'sass/selector/abstract_sequence'
-
1
require 'sass/selector/comma_sequence'
-
1
require 'sass/selector/sequence'
-
1
require 'sass/selector/simple_sequence'
-
-
1
module Sass
-
# A namespace for nodes in the parse tree for selectors.
-
#
-
# {CommaSequence} is the toplevel seelctor,
-
# representing a comma-separated sequence of {Sequence}s,
-
# such as `foo bar, baz bang`.
-
# {Sequence} is the next level,
-
# representing {SimpleSequence}s separated by combinators (e.g. descendant or child),
-
# such as `foo bar` or `foo > bar baz`.
-
# {SimpleSequence} is a sequence of selectors that all apply to a single element,
-
# such as `foo.bar[attr=val]`.
-
# Finally, {Simple} is the superclass of the simplest selectors,
-
# such as `.foo` or `#bar`.
-
1
module Selector
-
# The base used for calculating selector specificity. The spec says this
-
# should be "sufficiently high"; it's extremely unlikely that any single
-
# selector sequence will contain 1,000 simple selectors.
-
#
-
# @type [Fixnum]
-
1
SPECIFICITY_BASE = 1_000
-
-
# A parent-referencing selector (`&` in Sass).
-
# The function of this is to be replaced by the parent selector
-
# in the nested hierarchy.
-
1
class Parent < Simple
-
# @see Selector#to_a
-
1
def to_a
-
["&"]
-
end
-
-
# Always raises an exception.
-
#
-
# @raise [Sass::SyntaxError] Parent selectors should be resolved before unification
-
# @see Selector#unify
-
1
def unify(sels)
-
raise Sass::SyntaxError.new("[BUG] Cannot unify parent selectors.")
-
end
-
end
-
-
# A class selector (e.g. `.foo`).
-
1
class Class < Simple
-
# The class name.
-
#
-
# @return [Array<String, Sass::Script::Node>]
-
1
attr_reader :name
-
-
# @param name [Array<String, Sass::Script::Node>] The class name
-
1
def initialize(name)
-
@name = name
-
end
-
-
# @see Selector#to_a
-
1
def to_a
-
[".", *@name]
-
end
-
-
# @see AbstractSequence#specificity
-
1
def specificity
-
SPECIFICITY_BASE
-
end
-
end
-
-
# An id selector (e.g. `#foo`).
-
1
class Id < Simple
-
# The id name.
-
#
-
# @return [Array<String, Sass::Script::Node>]
-
1
attr_reader :name
-
-
# @param name [Array<String, Sass::Script::Node>] The id name
-
1
def initialize(name)
-
@name = name
-
end
-
-
# @see Selector#to_a
-
1
def to_a
-
["#", *@name]
-
end
-
-
# Returns `nil` if `sels` contains an {Id} selector
-
# with a different name than this one.
-
#
-
# @see Selector#unify
-
1
def unify(sels)
-
return if sels.any? {|sel2| sel2.is_a?(Id) && self.name != sel2.name}
-
super
-
end
-
-
# @see AbstractSequence#specificity
-
1
def specificity
-
SPECIFICITY_BASE**2
-
end
-
end
-
-
# A placeholder selector (e.g. `%foo`).
-
# This exists to be replaced via `@extend`.
-
# Rulesets using this selector will not be printed, but can be extended.
-
# Otherwise, this acts just like a class selector.
-
1
class Placeholder < Simple
-
# The placeholder name.
-
#
-
# @return [Array<String, Sass::Script::Node>]
-
1
attr_reader :name
-
-
# @param name [Array<String, Sass::Script::Node>] The placeholder name
-
1
def initialize(name)
-
@name = name
-
end
-
-
# @see Selector#to_a
-
1
def to_a
-
["%", *@name]
-
end
-
-
# @see AbstractSequence#specificity
-
1
def specificity
-
SPECIFICITY_BASE
-
end
-
end
-
-
# A universal selector (`*` in CSS).
-
1
class Universal < Simple
-
# The selector namespace.
-
# `nil` means the default namespace,
-
# `[""]` means no namespace,
-
# `["*"]` means any namespace.
-
#
-
# @return [Array<String, Sass::Script::Node>, nil]
-
1
attr_reader :namespace
-
-
# @param namespace [Array<String, Sass::Script::Node>, nil] See \{#namespace}
-
1
def initialize(namespace)
-
@namespace = namespace
-
end
-
-
# @see Selector#to_a
-
1
def to_a
-
@namespace ? @namespace + ["|*"] : ["*"]
-
end
-
-
# Unification of a universal selector is somewhat complicated,
-
# especially when a namespace is specified.
-
# If there is no namespace specified
-
# or any namespace is specified (namespace `"*"`),
-
# then `sel` is returned without change
-
# (unless it's empty, in which case `"*"` is required).
-
#
-
# If a namespace is specified
-
# but `sel` does not specify a namespace,
-
# then the given namespace is applied to `sel`,
-
# either by adding this {Universal} selector
-
# or applying this namespace to an existing {Element} selector.
-
#
-
# If both this selector *and* `sel` specify namespaces,
-
# those namespaces are unified via {Simple#unify_namespaces}
-
# and the unified namespace is used, if possible.
-
#
-
# @todo There are lots of cases that this documentation specifies;
-
# make sure we thoroughly test **all of them**.
-
# @todo Keep track of whether a default namespace has been declared
-
# and handle namespace-unspecified selectors accordingly.
-
# @todo If any branch of a CommaSequence ends up being just `"*"`,
-
# then all other branches should be eliminated
-
#
-
# @see Selector#unify
-
1
def unify(sels)
-
name =
-
case sels.first
-
when Universal; :universal
-
when Element; sels.first.name
-
else
-
return [self] + sels unless namespace.nil? || namespace == ['*']
-
return sels unless sels.empty?
-
return [self]
-
end
-
-
ns, accept = unify_namespaces(namespace, sels.first.namespace)
-
return unless accept
-
[name == :universal ? Universal.new(ns) : Element.new(name, ns)] + sels[1..-1]
-
end
-
-
# @see AbstractSequence#specificity
-
1
def specificity
-
0
-
end
-
end
-
-
# An element selector (e.g. `h1`).
-
1
class Element < Simple
-
# The element name.
-
#
-
# @return [Array<String, Sass::Script::Node>]
-
1
attr_reader :name
-
-
# The selector namespace.
-
# `nil` means the default namespace,
-
# `[""]` means no namespace,
-
# `["*"]` means any namespace.
-
#
-
# @return [Array<String, Sass::Script::Node>, nil]
-
1
attr_reader :namespace
-
-
# @param name [Array<String, Sass::Script::Node>] The element name
-
# @param namespace [Array<String, Sass::Script::Node>, nil] See \{#namespace}
-
1
def initialize(name, namespace)
-
@name = name
-
@namespace = namespace
-
end
-
-
# @see Selector#to_a
-
1
def to_a
-
@namespace ? @namespace + ["|"] + @name : @name
-
end
-
-
# Unification of an element selector is somewhat complicated,
-
# especially when a namespace is specified.
-
# First, if `sel` contains another {Element} with a different \{#name},
-
# then the selectors can't be unified and `nil` is returned.
-
#
-
# Otherwise, if `sel` doesn't specify a namespace,
-
# or it specifies any namespace (via `"*"`),
-
# then it's returned with this element selector
-
# (e.g. `.foo` becomes `a.foo` or `svg|a.foo`).
-
# Similarly, if this selector doesn't specify a namespace,
-
# the namespace from `sel` is used.
-
#
-
# If both this selector *and* `sel` specify namespaces,
-
# those namespaces are unified via {Simple#unify_namespaces}
-
# and the unified namespace is used, if possible.
-
#
-
# @todo There are lots of cases that this documentation specifies;
-
# make sure we thoroughly test **all of them**.
-
# @todo Keep track of whether a default namespace has been declared
-
# and handle namespace-unspecified selectors accordingly.
-
#
-
# @see Selector#unify
-
1
def unify(sels)
-
case sels.first
-
when Universal;
-
when Element; return unless name == sels.first.name
-
else return [self] + sels
-
end
-
-
ns, accept = unify_namespaces(namespace, sels.first.namespace)
-
return unless accept
-
[Element.new(name, ns)] + sels[1..-1]
-
end
-
-
# @see AbstractSequence#specificity
-
1
def specificity
-
1
-
end
-
end
-
-
# Selector interpolation (`#{}` in Sass).
-
1
class Interpolation < Simple
-
# The script to run.
-
#
-
# @return [Sass::Script::Node]
-
1
attr_reader :script
-
-
# @param script [Sass::Script::Node] The script to run
-
1
def initialize(script)
-
@script = script
-
end
-
-
# @see Selector#to_a
-
1
def to_a
-
[@script]
-
end
-
-
# Always raises an exception.
-
#
-
# @raise [Sass::SyntaxError] Interpolation selectors should be resolved before unification
-
# @see Selector#unify
-
1
def unify(sels)
-
raise Sass::SyntaxError.new("[BUG] Cannot unify interpolation selectors.")
-
end
-
end
-
-
# An attribute selector (e.g. `[href^="http://"]`).
-
1
class Attribute < Simple
-
# The attribute name.
-
#
-
# @return [Array<String, Sass::Script::Node>]
-
1
attr_reader :name
-
-
# The attribute namespace.
-
# `nil` means the default namespace,
-
# `[""]` means no namespace,
-
# `["*"]` means any namespace.
-
#
-
# @return [Array<String, Sass::Script::Node>, nil]
-
1
attr_reader :namespace
-
-
# The matching operator, e.g. `"="` or `"^="`.
-
#
-
# @return [String]
-
1
attr_reader :operator
-
-
# The right-hand side of the operator.
-
#
-
# @return [Array<String, Sass::Script::Node>]
-
1
attr_reader :value
-
-
# Flags for the attribute selector (e.g. `i`).
-
#
-
# @return [Array<String, Sass::Script::Node>]
-
1
attr_reader :flags
-
-
# @param name [Array<String, Sass::Script::Node>] The attribute name
-
# @param namespace [Array<String, Sass::Script::Node>, nil] See \{#namespace}
-
# @param operator [String] The matching operator, e.g. `"="` or `"^="`
-
# @param value [Array<String, Sass::Script::Node>] See \{#value}
-
# @param value [Array<String, Sass::Script::Node>] See \{#flags}
-
1
def initialize(name, namespace, operator, value, flags)
-
@name = name
-
@namespace = namespace
-
@operator = operator
-
@value = value
-
@flags = flags
-
end
-
-
# @see Selector#to_a
-
1
def to_a
-
res = ["["]
-
res.concat(@namespace) << "|" if @namespace
-
res.concat @name
-
(res << @operator).concat @value if @value
-
(res << " ").concat @flags if @flags
-
res << "]"
-
end
-
-
# @see AbstractSequence#specificity
-
1
def specificity
-
SPECIFICITY_BASE
-
end
-
end
-
-
# A pseudoclass (e.g. `:visited`) or pseudoelement (e.g. `::first-line`) selector.
-
# It can have arguments (e.g. `:nth-child(2n+1)`).
-
1
class Pseudo < Simple
-
# Some psuedo-class-syntax selectors are actually considered
-
# pseudo-elements and must be treated differently. This is a list of such
-
# selectors
-
#
-
# @return [Array<String>]
-
1
ACTUALLY_ELEMENTS = %w[after before first-line first-letter]
-
-
# Like \{#type}, but returns the type of selector this looks like, rather
-
# than the type it is semantically. This only differs from type for
-
# selectors in \{ACTUALLY\_ELEMENTS}.
-
#
-
# @return [Symbol]
-
1
attr_reader :syntactic_type
-
-
# The name of the selector.
-
#
-
# @return [Array<String, Sass::Script::Node>]
-
1
attr_reader :name
-
-
# The argument to the selector,
-
# or `nil` if no argument was given.
-
#
-
# This may include SassScript nodes that will be run during resolution.
-
# Note that this should not include SassScript nodes
-
# after resolution has taken place.
-
#
-
# @return [Array<String, Sass::Script::Node>, nil]
-
1
attr_reader :arg
-
-
# @param type [Symbol] See \{#type}
-
# @param name [Array<String, Sass::Script::Node>] The name of the selector
-
# @param arg [nil, Array<String, Sass::Script::Node>] The argument to the selector,
-
# or nil if no argument was given
-
1
def initialize(type, name, arg)
-
@syntactic_type = type
-
@name = name
-
@arg = arg
-
end
-
-
# The type of the selector. `:class` if this is a pseudoclass selector,
-
# `:element` if it's a pseudoelement.
-
#
-
# @return [Symbol]
-
1
def type
-
ACTUALLY_ELEMENTS.include?(name.first) ? :element : syntactic_type
-
end
-
-
# @see Selector#to_a
-
1
def to_a
-
res = [syntactic_type == :class ? ":" : "::"] + @name
-
(res << "(").concat(Sass::Util.strip_string_array(@arg)) << ")" if @arg
-
res
-
end
-
-
# Returns `nil` if this is a pseudoelement selector
-
# and `sels` contains a pseudoelement selector different than this one.
-
#
-
# @see Selector#unify
-
1
def unify(sels)
-
return if type == :element && sels.any? do |sel|
-
sel.is_a?(Pseudo) && sel.type == :element &&
-
(sel.name != self.name || sel.arg != self.arg)
-
end
-
super
-
end
-
-
# @see AbstractSequence#specificity
-
1
def specificity
-
type == :class ? SPECIFICITY_BASE : 1
-
end
-
end
-
-
# A pseudoclass selector whose argument is itself a selector
-
# (e.g. `:not(.foo)` or `:-moz-all(.foo, .bar)`).
-
1
class SelectorPseudoClass < Simple
-
# The name of the pseudoclass.
-
#
-
# @return [String]
-
1
attr_reader :name
-
-
# The selector argument.
-
#
-
# @return [Selector::Sequence]
-
1
attr_reader :selector
-
-
# @param [String] The name of the pseudoclass
-
# @param [Selector::CommaSequence] The selector argument
-
1
def initialize(name, selector)
-
@name = name
-
@selector = selector
-
end
-
-
# @see Selector#to_a
-
1
def to_a
-
[":", @name, "("] + @selector.to_a + [")"]
-
end
-
-
# @see AbstractSequence#specificity
-
1
def specificity
-
SPECIFICITY_BASE
-
end
-
end
-
end
-
end
-
1
module Sass
-
1
module Selector
-
# The abstract parent class of the various selector sequence classes.
-
#
-
# All subclasses should implement a `members` method that returns an array
-
# of object that respond to `#line=` and `#filename=`, as well as a `to_a`
-
# method that returns an array of strings and script nodes.
-
1
class AbstractSequence
-
# The line of the Sass template on which this selector was declared.
-
#
-
# @return [Fixnum]
-
1
attr_reader :line
-
-
# The name of the file in which this selector was declared.
-
#
-
# @return [String, nil]
-
1
attr_reader :filename
-
-
# Sets the line of the Sass template on which this selector was declared.
-
# This also sets the line for all child selectors.
-
#
-
# @param line [Fixnum]
-
# @return [Fixnum]
-
1
def line=(line)
-
members.each {|m| m.line = line}
-
@line = line
-
end
-
-
# Sets the name of the file in which this selector was declared,
-
# or `nil` if it was not declared in a file (e.g. on stdin).
-
# This also sets the filename for all child selectors.
-
#
-
# @param filename [String, nil]
-
# @return [String, nil]
-
1
def filename=(filename)
-
members.each {|m| m.filename = filename}
-
@filename = filename
-
end
-
-
# Returns a hash code for this sequence.
-
#
-
# Subclasses should define `#_hash` rather than overriding this method,
-
# which automatically handles memoizing the result.
-
#
-
# @return [Fixnum]
-
1
def hash
-
@_hash ||= _hash
-
end
-
-
# Checks equality between this and another object.
-
#
-
# Subclasses should define `#_eql?` rather than overriding this method,
-
# which handles checking class equality and hash equality.
-
#
-
# @param other [Object] The object to test equality against
-
# @return [Boolean] Whether or not this is equal to `other`
-
1
def eql?(other)
-
other.class == self.class && other.hash == self.hash && _eql?(other)
-
end
-
1
alias_method :==, :eql?
-
-
# Whether or not this selector sequence contains a placeholder selector.
-
# Checks recursively.
-
1
def has_placeholder?
-
@has_placeholder ||=
-
members.any? {|m| m.is_a?(AbstractSequence) ? m.has_placeholder? : m.is_a?(Placeholder)}
-
end
-
-
# Converts the selector into a string. This is the standard selector
-
# string, along with any SassScript interpolation that may exist.
-
#
-
# @return [String]
-
1
def to_s
-
to_a.map {|e| e.is_a?(Sass::Script::Node) ? "\#{#{e.to_sass}}" : e}.join
-
end
-
-
# Returns the specificity of the selector as an integer. The base is given
-
# by {Sass::Selector::SPECIFICITY_BASE}.
-
#
-
# @return [Fixnum]
-
1
def specificity
-
_specificity(members)
-
end
-
-
1
protected
-
-
1
def _specificity(arr)
-
spec = 0
-
arr.map {|m| spec += m.is_a?(String) ? 0 : m.specificity}
-
spec
-
end
-
end
-
end
-
end
-
1
module Sass
-
1
module Selector
-
# A comma-separated sequence of selectors.
-
1
class CommaSequence < AbstractSequence
-
# The comma-separated selector sequences
-
# represented by this class.
-
#
-
# @return [Array<Sequence>]
-
1
attr_reader :members
-
-
# @param seqs [Array<Sequence>] See \{#members}
-
1
def initialize(seqs)
-
@members = seqs
-
end
-
-
# Resolves the {Parent} selectors within this selector
-
# by replacing them with the given parent selector,
-
# handling commas appropriately.
-
#
-
# @param super_cseq [CommaSequence] The parent selector
-
# @return [CommaSequence] This selector, with parent references resolved
-
# @raise [Sass::SyntaxError] If a parent selector is invalid
-
1
def resolve_parent_refs(super_cseq)
-
if super_cseq.nil?
-
if @members.any? do |sel|
-
sel.members.any? do |sel_or_op|
-
sel_or_op.is_a?(SimpleSequence) && sel_or_op.members.any? {|ssel| ssel.is_a?(Parent)}
-
end
-
end
-
raise Sass::SyntaxError.new("Base-level rules cannot contain the parent-selector-referencing character '&'.")
-
end
-
return self
-
end
-
-
CommaSequence.new(
-
super_cseq.members.map do |super_seq|
-
@members.map {|seq| seq.resolve_parent_refs(super_seq)}
-
end.flatten)
-
end
-
-
# Non-destrucively extends this selector with the extensions specified in a hash
-
# (which should come from {Sass::Tree::Visitors::Cssize}).
-
#
-
# @todo Link this to the reference documentation on `@extend`
-
# when such a thing exists.
-
#
-
# @param extends [Sass::Util::SubsetMap{Selector::Simple =>
-
# Sass::Tree::Visitors::Cssize::Extend}]
-
# The extensions to perform on this selector
-
# @param parent_directives [Array<Sass::Tree::DirectiveNode>]
-
# The directives containing this selector.
-
# @return [CommaSequence] A copy of this selector,
-
# with extensions made according to `extends`
-
1
def do_extend(extends, parent_directives)
-
CommaSequence.new(members.map do |seq|
-
extended = seq.do_extend(extends, parent_directives)
-
# First Law of Extend: the result of extending a selector should
-
# always contain the base selector.
-
#
-
# See https://github.com/nex3/sass/issues/324.
-
extended.unshift seq unless seq.has_placeholder? || extended.include?(seq)
-
extended
-
end.flatten)
-
end
-
-
# Returns a string representation of the sequence.
-
# This is basically the selector string.
-
#
-
# @return [String]
-
1
def inspect
-
members.map {|m| m.inspect}.join(", ")
-
end
-
-
# @see Simple#to_a
-
1
def to_a
-
arr = Sass::Util.intersperse(@members.map {|m| m.to_a}, ", ").flatten
-
arr.delete("\n")
-
arr
-
end
-
-
1
private
-
-
1
def _hash
-
members.hash
-
end
-
-
1
def _eql?(other)
-
other.class == self.class && other.members.eql?(self.members)
-
end
-
end
-
end
-
end
-
1
module Sass
-
1
module Selector
-
# An operator-separated sequence of
-
# {SimpleSequence simple selector sequences}.
-
1
class Sequence < AbstractSequence
-
# Sets the line of the Sass template on which this selector was declared.
-
# This also sets the line for all child selectors.
-
#
-
# @param line [Fixnum]
-
# @return [Fixnum]
-
1
def line=(line)
-
members.each {|m| m.line = line if m.is_a?(SimpleSequence)}
-
line
-
end
-
-
# Sets the name of the file in which this selector was declared,
-
# or `nil` if it was not declared in a file (e.g. on stdin).
-
# This also sets the filename for all child selectors.
-
#
-
# @param filename [String, nil]
-
# @return [String, nil]
-
1
def filename=(filename)
-
members.each {|m| m.filename = filename if m.is_a?(SimpleSequence)}
-
filename
-
end
-
-
# The array of {SimpleSequence simple selector sequences}, operators, and
-
# newlines. The operators are strings such as `"+"` and `">"` representing
-
# the corresponding CSS operators, or interpolated SassScript. Newlines
-
# are also newline strings; these aren't semantically relevant, but they
-
# do affect formatting.
-
#
-
# @return [Array<SimpleSequence, String|Array<Sass::Tree::Node, String>>]
-
1
attr_reader :members
-
-
# @param seqs_and_ops [Array<SimpleSequence, String|Array<Sass::Tree::Node, String>>] See \{#members}
-
1
def initialize(seqs_and_ops)
-
@members = seqs_and_ops
-
end
-
-
# Resolves the {Parent} selectors within this selector
-
# by replacing them with the given parent selector,
-
# handling commas appropriately.
-
#
-
# @param super_seq [Sequence] The parent selector sequence
-
# @return [Sequence] This selector, with parent references resolved
-
# @raise [Sass::SyntaxError] If a parent selector is invalid
-
1
def resolve_parent_refs(super_seq)
-
members = @members.dup
-
nl = (members.first == "\n" && members.shift)
-
unless members.any? do |seq_or_op|
-
seq_or_op.is_a?(SimpleSequence) && seq_or_op.members.first.is_a?(Parent)
-
end
-
old_members, members = members, []
-
members << nl if nl
-
members << SimpleSequence.new([Parent.new], false)
-
members += old_members
-
end
-
-
Sequence.new(
-
members.map do |seq_or_op|
-
next seq_or_op unless seq_or_op.is_a?(SimpleSequence)
-
seq_or_op.resolve_parent_refs(super_seq)
-
end.flatten)
-
end
-
-
# Non-destructively extends this selector with the extensions specified in a hash
-
# (which should come from {Sass::Tree::Visitors::Cssize}).
-
#
-
# @overload def do_extend(extends, parent_directives)
-
# @param extends [Sass::Util::SubsetMap{Selector::Simple =>
-
# Sass::Tree::Visitors::Cssize::Extend}]
-
# The extensions to perform on this selector
-
# @param parent_directives [Array<Sass::Tree::DirectiveNode>]
-
# The directives containing this selector.
-
# @return [Array<Sequence>] A list of selectors generated
-
# by extending this selector with `extends`.
-
# These correspond to a {CommaSequence}'s {CommaSequence#members members array}.
-
# @see CommaSequence#do_extend
-
1
def do_extend(extends, parent_directives, seen = Set.new)
-
extended_not_expanded = members.map do |sseq_or_op|
-
next [[sseq_or_op]] unless sseq_or_op.is_a?(SimpleSequence)
-
extended = sseq_or_op.do_extend(extends, parent_directives, seen)
-
choices = extended.map {|seq| seq.members}
-
choices.unshift([sseq_or_op]) unless extended.any? {|seq| seq.superselector?(sseq_or_op)}
-
choices
-
end
-
weaves = Sass::Util.paths(extended_not_expanded).map {|path| weave(path)}
-
Sass::Util.flatten(trim(weaves), 1).map {|p| Sequence.new(p)}
-
end
-
-
# Returns whether or not this selector matches all elements
-
# that the given selector matches (as well as possibly more).
-
#
-
# @example
-
# (.foo).superselector?(.foo.bar) #=> true
-
# (.foo).superselector?(.bar) #=> false
-
# (.bar .foo).superselector?(.foo) #=> false
-
# @param sseq [SimpleSequence]
-
# @return [Boolean]
-
1
def superselector?(sseq)
-
return false unless members.size == 1
-
members.last.superselector?(sseq)
-
end
-
-
# @see Simple#to_a
-
1
def to_a
-
ary = @members.map {|seq_or_op| seq_or_op.is_a?(SimpleSequence) ? seq_or_op.to_a : seq_or_op}
-
Sass::Util.intersperse(ary, " ").flatten.compact
-
end
-
-
# Returns a string representation of the sequence.
-
# This is basically the selector string.
-
#
-
# @return [String]
-
1
def inspect
-
members.map {|m| m.inspect}.join(" ")
-
end
-
-
# Add to the {SimpleSequence#sources} sets of the child simple sequences.
-
# This destructively modifies this sequence's members array, but not the
-
# child simple sequences.
-
#
-
# @param sources [Set<Sequence>]
-
1
def add_sources!(sources)
-
members.map! {|m| m.is_a?(SimpleSequence) ? m.with_more_sources(sources) : m}
-
end
-
-
1
private
-
-
# Conceptually, this expands "parenthesized selectors".
-
# That is, if we have `.A .B {@extend .C}` and `.D .C {...}`,
-
# this conceptually expands into `.D .C, .D (.A .B)`,
-
# and this function translates `.D (.A .B)` into `.D .A .B, .A.D .B, .D .A .B`.
-
#
-
# @param path [Array<Array<SimpleSequence or String>>] A list of parenthesized selector groups.
-
# @return [Array<Array<SimpleSequence or String>>] A list of fully-expanded selectors.
-
1
def weave(path)
-
# This function works by moving through the selector path left-to-right,
-
# building all possible prefixes simultaneously. These prefixes are
-
# `befores`, while the remaining parenthesized suffixes is `afters`.
-
befores = [[]]
-
afters = path.dup
-
-
until afters.empty?
-
current = afters.shift.dup
-
last_current = [current.pop]
-
befores = Sass::Util.flatten(befores.map do |before|
-
next [] unless sub = subweave(before, current)
-
sub.map {|seqs| seqs + last_current}
-
end, 1)
-
end
-
return befores
-
end
-
-
# This interweaves two lists of selectors,
-
# returning all possible orderings of them (including using unification)
-
# that maintain the relative ordering of the input arrays.
-
#
-
# For example, given `.foo .bar` and `.baz .bang`,
-
# this would return `.foo .bar .baz .bang`, `.foo .bar.baz .bang`,
-
# `.foo .baz .bar .bang`, `.foo .baz .bar.bang`, `.foo .baz .bang .bar`,
-
# and so on until `.baz .bang .foo .bar`.
-
#
-
# Semantically, for selectors A and B, this returns all selectors `AB_i`
-
# such that the union over all i of elements matched by `AB_i X` is
-
# identical to the intersection of all elements matched by `A X` and all
-
# elements matched by `B X`. Some `AB_i` are elided to reduce the size of
-
# the output.
-
#
-
# @param seq1 [Array<SimpleSequence or String>]
-
# @param seq2 [Array<SimpleSequence or String>]
-
# @return [Array<Array<SimpleSequence or String>>]
-
1
def subweave(seq1, seq2)
-
return [seq2] if seq1.empty?
-
return [seq1] if seq2.empty?
-
-
seq1, seq2 = seq1.dup, seq2.dup
-
return unless init = merge_initial_ops(seq1, seq2)
-
return unless fin = merge_final_ops(seq1, seq2)
-
seq1 = group_selectors(seq1)
-
seq2 = group_selectors(seq2)
-
lcs = Sass::Util.lcs(seq2, seq1) do |s1, s2|
-
next s1 if s1 == s2
-
next unless s1.first.is_a?(SimpleSequence) && s2.first.is_a?(SimpleSequence)
-
next s2 if parent_superselector?(s1, s2)
-
next s1 if parent_superselector?(s2, s1)
-
end
-
-
diff = [[init]]
-
until lcs.empty?
-
diff << chunks(seq1, seq2) {|s| parent_superselector?(s.first, lcs.first)} << [lcs.shift]
-
seq1.shift
-
seq2.shift
-
end
-
diff << chunks(seq1, seq2) {|s| s.empty?}
-
diff += fin.map {|sel| sel.is_a?(Array) ? sel : [sel]}
-
diff.reject! {|c| c.empty?}
-
-
Sass::Util.paths(diff).map {|p| p.flatten}.reject {|p| path_has_two_subjects?(p)}
-
end
-
-
# Extracts initial selector combinators (`"+"`, `">"`, `"~"`, and `"\n"`)
-
# from two sequences and merges them together into a single array of
-
# selector combinators.
-
#
-
# @param seq1 [Array<SimpleSequence or String>]
-
# @param seq2 [Array<SimpleSequence or String>]
-
# @return [Array<String>, nil] If there are no operators in the merged
-
# sequence, this will be the empty array. If the operators cannot be
-
# merged, this will be nil.
-
1
def merge_initial_ops(seq1, seq2)
-
ops1, ops2 = [], []
-
ops1 << seq1.shift while seq1.first.is_a?(String)
-
ops2 << seq2.shift while seq2.first.is_a?(String)
-
-
newline = false
-
newline ||= !!ops1.shift if ops1.first == "\n"
-
newline ||= !!ops2.shift if ops2.first == "\n"
-
-
# If neither sequence is a subsequence of the other, they cannot be
-
# merged successfully
-
lcs = Sass::Util.lcs(ops1, ops2)
-
return unless lcs == ops1 || lcs == ops2
-
return (newline ? ["\n"] : []) + (ops1.size > ops2.size ? ops1 : ops2)
-
end
-
-
# Extracts final selector combinators (`"+"`, `">"`, `"~"`) and the
-
# selectors to which they apply from two sequences and merges them
-
# together into a single array.
-
#
-
# @param seq1 [Array<SimpleSequence or String>]
-
# @param seq2 [Array<SimpleSequence or String>]
-
# @return [Array<SimpleSequence or String or
-
# Array<Array<SimpleSequence or String>>]
-
# If there are no trailing combinators to be merged, this will be the
-
# empty array. If the trailing combinators cannot be merged, this will
-
# be nil. Otherwise, this will contained the merged selector. Array
-
# elements are [Sass::Util#paths]-style options; conceptually, an "or"
-
# of multiple selectors.
-
1
def merge_final_ops(seq1, seq2, res = [])
-
ops1, ops2 = [], []
-
ops1 << seq1.pop while seq1.last.is_a?(String)
-
ops2 << seq2.pop while seq2.last.is_a?(String)
-
-
# Not worth the headache of trying to preserve newlines here. The most
-
# important use of newlines is at the beginning of the selector to wrap
-
# across lines anyway.
-
ops1.reject! {|o| o == "\n"}
-
ops2.reject! {|o| o == "\n"}
-
-
return res if ops1.empty? && ops2.empty?
-
if ops1.size > 1 || ops2.size > 1
-
# If there are multiple operators, something hacky's going on. If one
-
# is a supersequence of the other, use that, otherwise give up.
-
lcs = Sass::Util.lcs(ops1, ops2)
-
return unless lcs == ops1 || lcs == ops2
-
res.unshift(*(ops1.size > ops2.size ? ops1 : ops2).reverse)
-
return res
-
end
-
-
# This code looks complicated, but it's actually just a bunch of special
-
# cases for interactions between different combinators.
-
op1, op2 = ops1.first, ops2.first
-
if op1 && op2
-
sel1 = seq1.pop
-
sel2 = seq2.pop
-
if op1 == '~' && op2 == '~'
-
if sel1.superselector?(sel2)
-
res.unshift sel2, '~'
-
elsif sel2.superselector?(sel1)
-
res.unshift sel1, '~'
-
else
-
merged = sel1.unify(sel2.members, sel2.subject?)
-
res.unshift [
-
[sel1, '~', sel2, '~'],
-
[sel2, '~', sel1, '~'],
-
([merged, '~'] if merged)
-
].compact
-
end
-
elsif (op1 == '~' && op2 == '+') || (op1 == '+' && op2 == '~')
-
if op1 == '~'
-
tilde_sel, plus_sel = sel1, sel2
-
else
-
tilde_sel, plus_sel = sel2, sel1
-
end
-
-
if tilde_sel.superselector?(plus_sel)
-
res.unshift plus_sel, '+'
-
else
-
merged = plus_sel.unify(tilde_sel.members, tilde_sel.subject?)
-
res.unshift [
-
[tilde_sel, '~', plus_sel, '+'],
-
([merged, '+'] if merged)
-
].compact
-
end
-
elsif op1 == '>' && %w[~ +].include?(op2)
-
res.unshift sel2, op2
-
seq1.push sel1, op1
-
elsif op2 == '>' && %w[~ +].include?(op1)
-
res.unshift sel1, op1
-
seq2.push sel2, op2
-
elsif op1 == op2
-
return unless merged = sel1.unify(sel2.members, sel2.subject?)
-
res.unshift merged, op1
-
else
-
# Unknown selector combinators can't be unified
-
return
-
end
-
return merge_final_ops(seq1, seq2, res)
-
elsif op1
-
seq2.pop if op1 == '>' && seq2.last && seq2.last.superselector?(seq1.last)
-
res.unshift seq1.pop, op1
-
return merge_final_ops(seq1, seq2, res)
-
else # op2
-
seq1.pop if op2 == '>' && seq1.last && seq1.last.superselector?(seq2.last)
-
res.unshift seq2.pop, op2
-
return merge_final_ops(seq1, seq2, res)
-
end
-
end
-
-
# Takes initial subsequences of `seq1` and `seq2` and returns all
-
# orderings of those subsequences. The initial subsequences are determined
-
# by a block.
-
#
-
# Destructively removes the initial subsequences of `seq1` and `seq2`.
-
#
-
# For example, given `(A B C | D E)` and `(1 2 | 3 4 5)` (with `|`
-
# denoting the boundary of the initial subsequence), this would return
-
# `[(A B C 1 2), (1 2 A B C)]`. The sequences would then be `(D E)` and
-
# `(3 4 5)`.
-
#
-
# @param seq1 [Array]
-
# @param seq2 [Array]
-
# @yield [a] Used to determine when to cut off the initial subsequences.
-
# Called repeatedly for each sequence until it returns true.
-
# @yieldparam a [Array] A final subsequence of one input sequence after
-
# cutting off some initial subsequence.
-
# @yieldreturn [Boolean] Whether or not to cut off the initial subsequence
-
# here.
-
# @return [Array<Array>] All possible orderings of the initial subsequences.
-
1
def chunks(seq1, seq2)
-
chunk1 = []
-
chunk1 << seq1.shift until yield seq1
-
chunk2 = []
-
chunk2 << seq2.shift until yield seq2
-
return [] if chunk1.empty? && chunk2.empty?
-
return [chunk2] if chunk1.empty?
-
return [chunk1] if chunk2.empty?
-
[chunk1 + chunk2, chunk2 + chunk1]
-
end
-
-
# Groups a sequence into subsequences. The subsequences are determined by
-
# strings; adjacent non-string elements will be put into separate groups,
-
# but any element adjacent to a string will be grouped with that string.
-
#
-
# For example, `(A B "C" D E "F" G "H" "I" J)` will become `[(A) (B "C" D)
-
# (E "F" G "H" "I" J)]`.
-
#
-
# @param seq [Array]
-
# @return [Array<Array>]
-
1
def group_selectors(seq)
-
newseq = []
-
tail = seq.dup
-
until tail.empty?
-
head = []
-
begin
-
head << tail.shift
-
end while !tail.empty? && head.last.is_a?(String) || tail.first.is_a?(String)
-
newseq << head
-
end
-
return newseq
-
end
-
-
# Given two selector sequences, returns whether `seq1` is a
-
# superselector of `seq2`; that is, whether `seq1` matches every
-
# element `seq2` matches.
-
#
-
# @param seq1 [Array<SimpleSequence or String>]
-
# @param seq2 [Array<SimpleSequence or String>]
-
# @return [Boolean]
-
1
def _superselector?(seq1, seq2)
-
seq1 = seq1.reject {|e| e == "\n"}
-
seq2 = seq2.reject {|e| e == "\n"}
-
# Selectors with leading or trailing operators are neither
-
# superselectors nor subselectors.
-
return if seq1.last.is_a?(String) || seq2.last.is_a?(String) ||
-
seq1.first.is_a?(String) || seq2.first.is_a?(String)
-
# More complex selectors are never superselectors of less complex ones
-
return if seq1.size > seq2.size
-
return seq1.first.superselector?(seq2.last) if seq1.size == 1
-
-
_, si = Sass::Util.enum_with_index(seq2).find do |e, i|
-
return if i == seq2.size - 1
-
next if e.is_a?(String)
-
seq1.first.superselector?(e)
-
end
-
return unless si
-
-
if seq1[1].is_a?(String)
-
return unless seq2[si+1].is_a?(String)
-
# .foo ~ .bar is a superselector of .foo + .bar
-
return unless seq1[1] == "~" ? seq2[si+1] != ">" : seq1[1] == seq2[si+1]
-
return _superselector?(seq1[2..-1], seq2[si+2..-1])
-
elsif seq2[si+1].is_a?(String)
-
return unless seq2[si+1] == ">"
-
return _superselector?(seq1[1..-1], seq2[si+2..-1])
-
else
-
return _superselector?(seq1[1..-1], seq2[si+1..-1])
-
end
-
end
-
-
# Like \{#_superselector?}, but compares the selectors in the
-
# context of parent selectors, as though they shared an implicit
-
# base simple selector. For example, `B` is not normally a
-
# superselector of `B A`, since it doesn't match `A` elements.
-
# However, it is a parent superselector, since `B X` is a
-
# superselector of `B A X`.
-
#
-
# @param seq1 [Array<SimpleSequence or String>]
-
# @param seq2 [Array<SimpleSequence or String>]
-
# @return [Boolean]
-
1
def parent_superselector?(seq1, seq2)
-
base = Sass::Selector::SimpleSequence.new([Sass::Selector::Placeholder.new('<temp>')], false)
-
_superselector?(seq1 + [base], seq2 + [base])
-
end
-
-
# Removes redundant selectors from between multiple lists of
-
# selectors. This takes a list of lists of selector sequences;
-
# each individual list is assumed to have no redundancy within
-
# itself. A selector is only removed if it's redundant with a
-
# selector in another list.
-
#
-
# "Redundant" here means that one selector is a superselector of
-
# the other. The more specific selector is removed.
-
#
-
# @param seqses [Array<Array<Array<SimpleSequence or String>>>]
-
# @return [Array<Array<Array<SimpleSequence or String>>>]
-
1
def trim(seqses)
-
# Avoid truly horrific quadratic behavior. TODO: I think there
-
# may be a way to get perfect trimming without going quadratic.
-
return seqses if seqses.size > 100
-
-
# Keep the results in a separate array so we can be sure we aren't
-
# comparing against an already-trimmed selector. This ensures that two
-
# identical selectors don't mutually trim one another.
-
result = seqses.dup
-
-
# This is n^2 on the sequences, but only comparing between
-
# separate sequences should limit the quadratic behavior.
-
seqses.each_with_index do |seqs1, i|
-
result[i] = seqs1.reject do |seq1|
-
max_spec = _sources(seq1).map {|seq| seq.specificity}.max || 0
-
result.any? do |seqs2|
-
next if seqs1.equal?(seqs2)
-
# Second Law of Extend: the specificity of a generated selector
-
# should never be less than the specificity of the extending
-
# selector.
-
#
-
# See https://github.com/nex3/sass/issues/324.
-
seqs2.any? {|seq2| _specificity(seq2) >= max_spec && _superselector?(seq2, seq1)}
-
end
-
end
-
end
-
result
-
end
-
-
1
def _hash
-
members.reject {|m| m == "\n"}.hash
-
end
-
-
1
def _eql?(other)
-
other.members.reject {|m| m == "\n"}.eql?(self.members.reject {|m| m == "\n"})
-
end
-
-
1
private
-
-
1
def path_has_two_subjects?(path)
-
subject = false
-
path.each do |sseq_or_op|
-
next unless sseq_or_op.is_a?(SimpleSequence)
-
next unless sseq_or_op.subject?
-
return true if subject
-
subject = true
-
end
-
false
-
end
-
-
1
def _sources(seq)
-
s = Set.new
-
seq.map {|sseq_or_op| s.merge sseq_or_op.sources if sseq_or_op.is_a?(SimpleSequence)}
-
s
-
end
-
-
1
def extended_not_expanded_to_s(extended_not_expanded)
-
extended_not_expanded.map do |choices|
-
choices = choices.map do |sel|
-
next sel.first.to_s if sel.size == 1
-
"#{sel.join ' '}"
-
end
-
next choices.first if choices.size == 1 && !choices.include?(' ')
-
"(#{choices.join ', '})"
-
end.join ' '
-
end
-
end
-
end
-
end
-
1
module Sass
-
1
module Selector
-
# The abstract superclass for simple selectors
-
# (that is, those that don't compose multiple selectors).
-
1
class Simple
-
# The line of the Sass template on which this selector was declared.
-
#
-
# @return [Fixnum]
-
1
attr_accessor :line
-
-
# The name of the file in which this selector was declared,
-
# or `nil` if it was not declared in a file (e.g. on stdin).
-
#
-
# @return [String, nil]
-
1
attr_accessor :filename
-
-
# Returns a representation of the node
-
# as an array of strings and potentially {Sass::Script::Node}s
-
# (if there's interpolation in the selector).
-
# When the interpolation is resolved and the strings are joined together,
-
# this will be the string representation of this node.
-
#
-
# @return [Array<String, Sass::Script::Node>]
-
1
def to_a
-
Sass::Util.abstract(self)
-
end
-
-
# Returns a string representation of the node.
-
# This is basically the selector string.
-
#
-
# @return [String]
-
1
def inspect
-
to_a.map {|e| e.is_a?(Sass::Script::Node) ? "\#{#{e.to_sass}}" : e}.join
-
end
-
-
# @see \{#inspect}
-
# @return [String]
-
1
def to_s
-
inspect
-
end
-
-
# Returns a hash code for this selector object.
-
#
-
# By default, this is based on the value of \{#to\_a},
-
# so if that contains information irrelevant to the identity of the selector,
-
# this should be overridden.
-
#
-
# @return [Fixnum]
-
1
def hash
-
@_hash ||= to_a.hash
-
end
-
-
# Checks equality between this and another object.
-
#
-
# By default, this is based on the value of \{#to\_a},
-
# so if that contains information irrelevant to the identity of the selector,
-
# this should be overridden.
-
#
-
# @param other [Object] The object to test equality against
-
# @return [Boolean] Whether or not this is equal to `other`
-
1
def eql?(other)
-
other.class == self.class && other.hash == self.hash && other.to_a.eql?(to_a)
-
end
-
1
alias_method :==, :eql?
-
-
# Unifies this selector with a {SimpleSequence}'s {SimpleSequence#members members array},
-
# returning another `SimpleSequence` members array
-
# that matches both this selector and the input selector.
-
#
-
# By default, this just appends this selector to the end of the array
-
# (or returns the original array if this selector already exists in it).
-
#
-
# @param sels [Array<Simple>] A {SimpleSequence}'s {SimpleSequence#members members array}
-
# @return [Array<Simple>, nil] A {SimpleSequence} {SimpleSequence#members members array}
-
# matching both `sels` and this selector,
-
# or `nil` if this is impossible (e.g. unifying `#foo` and `#bar`)
-
# @raise [Sass::SyntaxError] If this selector cannot be unified.
-
# This will only ever occur when a dynamic selector,
-
# such as {Parent} or {Interpolation}, is used in unification.
-
# Since these selectors should be resolved
-
# by the time extension and unification happen,
-
# this exception will only ever be raised as a result of programmer error
-
1
def unify(sels)
-
return sels if sels.any? {|sel2| eql?(sel2)}
-
sels_with_ix = Sass::Util.enum_with_index(sels)
-
_, i =
-
if self.is_a?(Pseudo) || self.is_a?(SelectorPseudoClass)
-
sels_with_ix.find {|sel, _| sel.is_a?(Pseudo) && (sels.last.type == :element)}
-
else
-
sels_with_ix.find {|sel, _| sel.is_a?(Pseudo) || sel.is_a?(SelectorPseudoClass)}
-
end
-
return sels + [self] unless i
-
return sels[0...i] + [self] + sels[i..-1]
-
end
-
-
1
protected
-
-
# Unifies two namespaces,
-
# returning a namespace that works for both of them if possible.
-
#
-
# @param ns1 [String, nil] The first namespace.
-
# `nil` means none specified, e.g. `foo`.
-
# The empty string means no namespace specified, e.g. `|foo`.
-
# `"*"` means any namespace is allowed, e.g. `*|foo`.
-
# @param ns2 [String, nil] The second namespace. See `ns1`.
-
# @return [Array(String or nil, Boolean)]
-
# The first value is the unified namespace, or `nil` for no namespace.
-
# The second value is whether or not a namespace that works for both inputs
-
# could be found at all.
-
# If the second value is `false`, the first should be ignored.
-
1
def unify_namespaces(ns1, ns2)
-
return nil, false unless ns1 == ns2 || ns1.nil? || ns1 == ['*'] || ns2.nil? || ns2 == ['*']
-
return ns2, true if ns1 == ['*']
-
return ns1, true if ns2 == ['*']
-
return ns1 || ns2, true
-
end
-
end
-
end
-
end
-
1
module Sass
-
1
module Selector
-
# A unseparated sequence of selectors
-
# that all apply to a single element.
-
# For example, `.foo#bar[attr=baz]` is a simple sequence
-
# of the selectors `.foo`, `#bar`, and `[attr=baz]`.
-
1
class SimpleSequence < AbstractSequence
-
# The array of individual selectors.
-
#
-
# @return [Array<Simple>]
-
1
attr_accessor :members
-
-
# The extending selectors that caused this selector sequence to be
-
# generated. For example:
-
#
-
# a.foo { ... }
-
# b.bar {@extend a}
-
# c.baz {@extend b}
-
#
-
# The generated selector `b.foo.bar` has `{b.bar}` as its `sources` set,
-
# and the generated selector `c.foo.bar.baz` has `{b.bar, c.baz}` as its
-
# `sources` set.
-
#
-
# This is populated during the {#do_extend} process.
-
#
-
# @return {Set<Sequence>}
-
1
attr_accessor :sources
-
-
# @see \{#subject?}
-
1
attr_writer :subject
-
-
# Returns the element or universal selector in this sequence,
-
# if it exists.
-
#
-
# @return [Element, Universal, nil]
-
1
def base
-
@base ||= (members.first if members.first.is_a?(Element) || members.first.is_a?(Universal))
-
end
-
-
1
def pseudo_elements
-
@pseudo_elements ||= (members - [base]).
-
select {|sel| sel.is_a?(Pseudo) && sel.type == :element}
-
end
-
-
# Returns the non-base, non-pseudo-class selectors in this sequence.
-
#
-
# @return [Set<Simple>]
-
1
def rest
-
@rest ||= Set.new(members - [base] - pseudo_elements)
-
end
-
-
# Whether or not this compound selector is the subject of the parent
-
# selector; that is, whether it is prepended with `$` and represents the
-
# actual element that will be selected.
-
#
-
# @return [Boolean]
-
1
def subject?
-
@subject
-
end
-
-
# @param selectors [Array<Simple>] See \{#members}
-
# @param subject [Boolean] See \{#subject?}
-
# @param sources [Set<Sequence>]
-
1
def initialize(selectors, subject, sources = Set.new)
-
@members = selectors
-
@subject = subject
-
@sources = sources
-
end
-
-
# Resolves the {Parent} selectors within this selector
-
# by replacing them with the given parent selector,
-
# handling commas appropriately.
-
#
-
# @param super_seq [Sequence] The parent selector sequence
-
# @return [Array<SimpleSequence>] This selector, with parent references resolved.
-
# This is an array because the parent selector is itself a {Sequence}
-
# @raise [Sass::SyntaxError] If a parent selector is invalid
-
1
def resolve_parent_refs(super_seq)
-
# Parent selector only appears as the first selector in the sequence
-
return [self] unless @members.first.is_a?(Parent)
-
-
members = super_seq.members.dup
-
newline = members.pop if members.last == "\n"
-
return members if @members.size == 1
-
unless members.last.is_a?(SimpleSequence)
-
raise Sass::SyntaxError.new("Invalid parent selector: " + super_seq.to_a.join)
-
end
-
-
members[0...-1] +
-
[SimpleSequence.new(members.last.members + @members[1..-1], subject?)] +
-
[newline].compact
-
end
-
-
# Non-destrucively extends this selector with the extensions specified in a hash
-
# (which should come from {Sass::Tree::Visitors::Cssize}).
-
#
-
# @overload def do_extend(extends, parent_directives)
-
# @param extends [{Selector::Simple =>
-
# Sass::Tree::Visitors::Cssize::Extend}]
-
# The extensions to perform on this selector
-
# @param parent_directives [Array<Sass::Tree::DirectiveNode>]
-
# The directives containing this selector.
-
# @return [Array<Sequence>] A list of selectors generated
-
# by extending this selector with `extends`.
-
# @see CommaSequence#do_extend
-
1
def do_extend(extends, parent_directives, seen = Set.new)
-
Sass::Util.group_by_to_a(extends.get(members.to_set)) {|ex, _| ex.extender}.map do |seq, group|
-
sels = group.map {|_, s| s}.flatten
-
# If A {@extend B} and C {...},
-
# seq is A, sels is B, and self is C
-
-
self_without_sel = Sass::Util.array_minus(self.members, sels)
-
group.each {|e, _| e.result = :failed_to_unify unless e.result == :succeeded}
-
next unless unified = seq.members.last.unify(self_without_sel, subject?)
-
group.each {|e, _| e.result = :succeeded}
-
next if group.map {|e, _| check_directives_match!(e, parent_directives)}.none?
-
new_seq = Sequence.new(seq.members[0...-1] + [unified])
-
new_seq.add_sources!(sources + [seq])
-
[sels, new_seq]
-
end.compact.map do |sels, seq|
-
seen.include?(sels) ? [] : seq.do_extend(extends, parent_directives, seen + [sels])
-
end.flatten.uniq
-
end
-
-
# Unifies this selector with another {SimpleSequence}'s {SimpleSequence#members members array},
-
# returning another `SimpleSequence`
-
# that matches both this selector and the input selector.
-
#
-
# @param sels [Array<Simple>] A {SimpleSequence}'s {SimpleSequence#members members array}
-
# @param subject [Boolean] Whether the {SimpleSequence} being merged is a subject.
-
# @return [SimpleSequence, nil] A {SimpleSequence} matching both `sels` and this selector,
-
# or `nil` if this is impossible (e.g. unifying `#foo` and `#bar`)
-
# @raise [Sass::SyntaxError] If this selector cannot be unified.
-
# This will only ever occur when a dynamic selector,
-
# such as {Parent} or {Interpolation}, is used in unification.
-
# Since these selectors should be resolved
-
# by the time extension and unification happen,
-
# this exception will only ever be raised as a result of programmer error
-
1
def unify(sels, other_subject)
-
return unless sseq = members.inject(sels) do |member, sel|
-
return unless member
-
sel.unify(member)
-
end
-
SimpleSequence.new(sseq, other_subject || subject?)
-
end
-
-
# Returns whether or not this selector matches all elements
-
# that the given selector matches (as well as possibly more).
-
#
-
# @example
-
# (.foo).superselector?(.foo.bar) #=> true
-
# (.foo).superselector?(.bar) #=> false
-
# @param sseq [SimpleSequence]
-
# @return [Boolean]
-
1
def superselector?(sseq)
-
(base.nil? || base.eql?(sseq.base)) &&
-
pseudo_elements.eql?(sseq.pseudo_elements) &&
-
rest.subset?(sseq.rest)
-
end
-
-
# @see Simple#to_a
-
1
def to_a
-
res = @members.map {|sel| sel.to_a}.flatten
-
res << '!' if subject?
-
res
-
end
-
-
# Returns a string representation of the sequence.
-
# This is basically the selector string.
-
#
-
# @return [String]
-
1
def inspect
-
members.map {|m| m.inspect}.join
-
end
-
-
# Return a copy of this simple sequence with `sources` merged into the
-
# {#sources} set.
-
#
-
# @param sources [Set<Sequence>]
-
# @return [SimpleSequence]
-
1
def with_more_sources(sources)
-
sseq = dup
-
sseq.members = members.dup
-
sseq.sources = self.sources | sources
-
sseq
-
end
-
-
1
private
-
-
1
def check_directives_match!(extend, parent_directives)
-
dirs1 = extend.directives.map {|d| d.resolved_value}
-
dirs2 = parent_directives.map {|d| d.resolved_value}
-
return true if Sass::Util.subsequence?(dirs1, dirs2)
-
-
Sass::Util.sass_warn <<WARNING
-
DEPRECATION WARNING on line #{extend.node.line}#{" of #{extend.node.filename}" if extend.node.filename}:
-
@extending an outer selector from within #{extend.directives.last.name} is deprecated.
-
You may only @extend selectors within the same directive.
-
This will be an error in Sass 3.3.
-
It can only work once @extend is supported natively in the browser.
-
WARNING
-
return false
-
end
-
-
1
def _hash
-
[base, Sass::Util.set_hash(rest)].hash
-
end
-
-
1
def _eql?(other)
-
other.base.eql?(self.base) && other.pseudo_elements == pseudo_elements &&
-
Sass::Util.set_eql?(other.rest, self.rest) && other.subject? == self.subject?
-
end
-
end
-
end
-
end
-
1
module Sass
-
# This module contains functionality that's shared between Haml and Sass.
-
1
module Shared
-
1
extend self
-
-
# Scans through a string looking for the interoplation-opening `#{`
-
# and, when it's found, yields the scanner to the calling code
-
# so it can handle it properly.
-
#
-
# The scanner will have any backslashes immediately in front of the `#{`
-
# as the second capture group (`scan[2]`),
-
# and the text prior to that as the first (`scan[1]`).
-
#
-
# @yieldparam scan [StringScanner] The scanner scanning through the string
-
# @return [String] The text remaining in the scanner after all `#{`s have been processed
-
1
def handle_interpolation(str)
-
scan = Sass::Util::MultibyteStringScanner.new(str)
-
yield scan while scan.scan(/(.*?)(\\*)\#\{/m)
-
scan.rest
-
end
-
-
# Moves a scanner through a balanced pair of characters.
-
# For example:
-
#
-
# Foo (Bar (Baz bang) bop) (Bang (bop bip))
-
# ^ ^
-
# from to
-
#
-
# @param scanner [StringScanner] The string scanner to move
-
# @param start [Character] The character opening the balanced pair.
-
# A `Fixnum` in 1.8, a `String` in 1.9
-
# @param finish [Character] The character closing the balanced pair.
-
# A `Fixnum` in 1.8, a `String` in 1.9
-
# @param count [Fixnum] The number of opening characters matched
-
# before calling this method
-
# @return [(String, String)] The string matched within the balanced pair
-
# and the rest of the string.
-
# `["Foo (Bar (Baz bang) bop)", " (Bang (bop bip))"]` in the example above.
-
1
def balance(scanner, start, finish, count = 0)
-
str = ''
-
scanner = Sass::Util::MultibyteStringScanner.new(scanner) unless scanner.is_a? StringScanner
-
regexp = Regexp.new("(.*?)[\\#{start.chr}\\#{finish.chr}]", Regexp::MULTILINE)
-
while scanner.scan(regexp)
-
str << scanner.matched
-
count += 1 if scanner.matched[-1] == start
-
count -= 1 if scanner.matched[-1] == finish
-
return [str.strip, scanner.rest] if count == 0
-
end
-
end
-
-
# Formats a string for use in error messages about indentation.
-
#
-
# @param indentation [String] The string used for indentation
-
# @param was [Boolean] Whether or not to add `"was"` or `"were"`
-
# (depending on how many characters were in `indentation`)
-
# @return [String] The name of the indentation (e.g. `"12 spaces"`, `"1 tab"`)
-
1
def human_indentation(indentation, was = false)
-
if !indentation.include?(?\t)
-
noun = 'space'
-
elsif !indentation.include?(?\s)
-
noun = 'tab'
-
else
-
return indentation.inspect + (was ? ' was' : '')
-
end
-
-
singular = indentation.length == 1
-
if was
-
was = singular ? ' was' : ' were'
-
else
-
was = ''
-
end
-
-
"#{indentation.length} #{noun}#{'s' unless singular}#{was}"
-
end
-
end
-
end
-
1
module Sass::Tree
-
# A static node representing an unproccessed Sass `@charset` directive.
-
#
-
# @see Sass::Tree
-
1
class CharsetNode < Node
-
# The name of the charset.
-
#
-
# @return [String]
-
1
attr_accessor :name
-
-
# @param name [String] see \{#name}
-
1
def initialize(name)
-
@name = name
-
super()
-
end
-
-
# @see Node#invisible?
-
1
def invisible?
-
!Sass::Util.ruby1_8?
-
end
-
end
-
end
-
1
require 'sass/tree/node'
-
-
1
module Sass::Tree
-
# A static node representing a Sass comment (silent or loud).
-
#
-
# @see Sass::Tree
-
1
class CommentNode < Node
-
# The text of the comment, not including `/*` and `*/`.
-
# Interspersed with {Sass::Script::Node}s representing `#{}`-interpolation
-
# if this is a loud comment.
-
#
-
# @return [Array<String, Sass::Script::Node>]
-
1
attr_accessor :value
-
-
# The text of the comment
-
# after any interpolated SassScript has been resolved.
-
# Only set once \{Tree::Visitors::Perform} has been run.
-
#
-
# @return [String]
-
1
attr_accessor :resolved_value
-
-
# The type of the comment. `:silent` means it's never output to CSS,
-
# `:normal` means it's output in every compile mode except `:compressed`,
-
# and `:loud` means it's output even in `:compressed`.
-
#
-
# @return [Symbol]
-
1
attr_accessor :type
-
-
# @param value [Array<String, Sass::Script::Node>] See \{#value}
-
# @param type [Symbol] See \{#type}
-
1
def initialize(value, type)
-
@value = Sass::Util.with_extracted_values(value) {|str| normalize_indentation str}
-
@type = type
-
super()
-
end
-
-
# Compares the contents of two comments.
-
#
-
# @param other [Object] The object to compare with
-
# @return [Boolean] Whether or not this node and the other object
-
# are the same
-
1
def ==(other)
-
self.class == other.class && value == other.value && type == other.type
-
end
-
-
# Returns `true` if this is a silent comment
-
# or the current style doesn't render comments.
-
#
-
# Comments starting with ! are never invisible (and the ! is removed from the output.)
-
#
-
# @return [Boolean]
-
1
def invisible?
-
case @type
-
when :loud; false
-
when :silent; true
-
else; style == :compressed
-
end
-
end
-
-
# Returns the number of lines in the comment.
-
#
-
# @return [Fixnum]
-
1
def lines
-
@value.inject(0) do |s, e|
-
next s + e.count("\n") if e.is_a?(String)
-
next s
-
end
-
end
-
-
1
private
-
-
1
def normalize_indentation(str)
-
ind = str.split("\n").inject(str[/^[ \t]*/].split("")) do |pre, line|
-
line[/^[ \t]*/].split("").zip(pre).inject([]) do |arr, (a, b)|
-
break arr if a != b
-
arr << a
-
end
-
end.join
-
str.gsub(/^#{ind}/, '')
-
end
-
end
-
end
-
1
module Sass
-
1
module Tree
-
# A node representing the placement within a mixin of the include statement's content.
-
#
-
# @see Sass::Tree
-
1
class ContentNode < Node
-
end
-
end
-
end
-
1
module Sass::Tree
-
# A node representing an `@import` rule that's importing plain CSS.
-
#
-
# @see Sass::Tree
-
1
class CssImportNode < DirectiveNode
-
# The URI being imported, either as a plain string or an interpolated
-
# script string.
-
#
-
# @return [String, Sass::Script::Node]
-
1
attr_accessor :uri
-
-
# The text of the URI being imported after any interpolated SassScript has
-
# been resolved. Only set once \{Tree::Visitors::Perform} has been run.
-
#
-
# @return [String]
-
1
attr_accessor :resolved_uri
-
-
# The media query for this rule, interspersed with {Sass::Script::Node}s
-
# representing `#{}`-interpolation. Any adjacent strings will be merged
-
# together.
-
#
-
# @return [Array<String, Sass::Script::Node>]
-
1
attr_accessor :query
-
-
# The media query for this rule, without any unresolved interpolation. It's
-
# only set once {Tree::Node#perform} has been called.
-
#
-
# @return [Sass::Media::QueryList]
-
1
attr_accessor :resolved_query
-
-
# @param uri [String, Sass::Script::Node] See \{#uri}
-
# @param query [Array<String, Sass::Script::Node>] See \{#query}
-
1
def initialize(uri, query = nil)
-
@uri = uri
-
@query = query
-
super('')
-
end
-
-
# @param uri [String] See \{#resolved_uri}
-
# @return [CssImportNode]
-
1
def self.resolved(uri)
-
node = new(uri)
-
node.resolved_uri = uri
-
node
-
end
-
-
# @see DirectiveNode#value
-
1
def value; raise NotImplementedError; end
-
-
# @see DirectiveNode#resolved_value
-
1
def resolved_value
-
@resolved_value ||=
-
begin
-
str = "@import #{resolved_uri}"
-
str << " #{resolved_query.to_css}" if resolved_query
-
str
-
end
-
end
-
end
-
end
-
1
module Sass
-
1
module Tree
-
# A dynamic node representing a Sass `@debug` statement.
-
#
-
# @see Sass::Tree
-
1
class DebugNode < Node
-
# The expression to print.
-
# @return [Script::Node]
-
1
attr_accessor :expr
-
-
# @param expr [Script::Node] The expression to print
-
1
def initialize(expr)
-
@expr = expr
-
super()
-
end
-
end
-
end
-
end
-
1
module Sass::Tree
-
# A static node representing an unproccessed Sass `@`-directive.
-
# Directives known to Sass, like `@for` and `@debug`,
-
# are handled by their own nodes;
-
# only CSS directives like `@media` and `@font-face` become {DirectiveNode}s.
-
#
-
# `@import` and `@charset` are special cases;
-
# they become {ImportNode}s and {CharsetNode}s, respectively.
-
#
-
# @see Sass::Tree
-
1
class DirectiveNode < Node
-
# The text of the directive, `@` and all, with interpolation included.
-
#
-
# @return [Array<String, Sass::Script::Node>]
-
1
attr_accessor :value
-
-
# The text of the directive after any interpolated SassScript has been resolved.
-
# Only set once \{Tree::Visitors::Perform} has been run.
-
#
-
# @return [String]
-
1
attr_accessor :resolved_value
-
-
# @param value [Array<String, Sass::Script::Node>] See \{#value}
-
1
def initialize(value)
-
@value = value
-
super()
-
end
-
-
# @param value [String] See \{#resolved_value}
-
# @return [DirectiveNode]
-
1
def self.resolved(value)
-
node = new([value])
-
node.resolved_value = value
-
node
-
end
-
-
# @return [String] The name of the directive, including `@`.
-
1
def name
-
value.first.gsub(/ .*$/, '')
-
end
-
end
-
end
-
1
require 'sass/tree/node'
-
-
1
module Sass::Tree
-
# A dynamic node representing a Sass `@each` loop.
-
#
-
# @see Sass::Tree
-
1
class EachNode < Node
-
# The name of the loop variable.
-
# @return [String]
-
1
attr_reader :var
-
-
# The parse tree for the list.
-
# @param [Script::Node]
-
1
attr_accessor :list
-
-
# @param var [String] The name of the loop variable
-
# @param list [Script::Node] The parse tree for the list
-
1
def initialize(var, list)
-
@var = var
-
@list = list
-
super()
-
end
-
end
-
end
-
1
require 'sass/tree/node'
-
-
1
module Sass::Tree
-
# A static node reprenting an `@extend` directive.
-
#
-
# @see Sass::Tree
-
1
class ExtendNode < Node
-
# The parsed selector after interpolation has been resolved.
-
# Only set once {Tree::Visitors::Perform} has been run.
-
#
-
# @return [Selector::CommaSequence]
-
1
attr_accessor :resolved_selector
-
-
# The CSS selector to extend, interspersed with {Sass::Script::Node}s
-
# representing `#{}`-interpolation.
-
#
-
# @return [Array<String, Sass::Script::Node>]
-
1
attr_accessor :selector
-
-
# Whether the `@extend` is allowed to match no selectors or not.
-
#
-
# @return [Boolean]
-
1
def optional?; @optional; end
-
-
# @param selector [Array<String, Sass::Script::Node>]
-
# The CSS selector to extend,
-
# interspersed with {Sass::Script::Node}s
-
# representing `#{}`-interpolation.
-
# @param optional [Boolean] See \{#optional}
-
1
def initialize(selector, optional)
-
@selector = selector
-
@optional = optional
-
super()
-
end
-
end
-
end
-
1
require 'sass/tree/node'
-
-
1
module Sass::Tree
-
# A dynamic node representing a Sass `@for` loop.
-
#
-
# @see Sass::Tree
-
1
class ForNode < Node
-
# The name of the loop variable.
-
# @return [String]
-
1
attr_reader :var
-
-
# The parse tree for the initial expression.
-
# @return [Script::Node]
-
1
attr_accessor :from
-
-
# The parse tree for the final expression.
-
# @return [Script::Node]
-
1
attr_accessor :to
-
-
# Whether to include `to` in the loop or stop just before.
-
# @return [Boolean]
-
1
attr_reader :exclusive
-
-
# @param var [String] See \{#var}
-
# @param from [Script::Node] See \{#from}
-
# @param to [Script::Node] See \{#to}
-
# @param exclusive [Boolean] See \{#exclusive}
-
1
def initialize(var, from, to, exclusive)
-
@var = var
-
@from = from
-
@to = to
-
@exclusive = exclusive
-
super()
-
end
-
end
-
end
-
1
module Sass
-
1
module Tree
-
# A dynamic node representing a function definition.
-
#
-
# @see Sass::Tree
-
1
class FunctionNode < Node
-
# The name of the function.
-
# @return [String]
-
1
attr_reader :name
-
-
# The arguments to the function. Each element is a tuple
-
# containing the variable for argument and the parse tree for
-
# the default value of the argument
-
#
-
# @return [Array<Script::Node>]
-
1
attr_accessor :args
-
-
# The splat argument for this function, if one exists.
-
#
-
# @return [Script::Node?]
-
1
attr_accessor :splat
-
-
# @param name [String] The function name
-
# @param args [Array<(Script::Node, Script::Node)>] The arguments for the function.
-
# @param splat [Script::Node] See \{#splat}
-
1
def initialize(name, args, splat)
-
@name = name
-
@args = args
-
@splat = splat
-
super()
-
end
-
end
-
end
-
end
-
1
require 'sass/tree/node'
-
-
1
module Sass::Tree
-
# A dynamic node representing a Sass `@if` statement.
-
#
-
# {IfNode}s are a little odd, in that they also represent `@else` and `@else if`s.
-
# This is done as a linked list:
-
# each {IfNode} has a link (\{#else}) to the next {IfNode}.
-
#
-
# @see Sass::Tree
-
1
class IfNode < Node
-
# The conditional expression.
-
# If this is nil, this is an `@else` node, not an `@else if`.
-
#
-
# @return [Script::Expr]
-
1
attr_accessor :expr
-
-
# The next {IfNode} in the if-else list, or `nil`.
-
#
-
# @return [IfNode]
-
1
attr_accessor :else
-
-
# @param expr [Script::Expr] See \{#expr}
-
1
def initialize(expr)
-
@expr = expr
-
@last_else = self
-
super()
-
end
-
-
# Append an `@else` node to the end of the list.
-
#
-
# @param node [IfNode] The `@else` node to append
-
1
def add_else(node)
-
@last_else.else = node
-
@last_else = node
-
end
-
-
1
def _dump(f)
-
Marshal.dump([self.expr, self.else, self.children])
-
end
-
-
1
def self._load(data)
-
expr, else_, children = Marshal.load(data)
-
node = IfNode.new(expr)
-
node.else = else_
-
node.children = children
-
node.instance_variable_set('@last_else',
-
node.else ? node.else.instance_variable_get('@last_else') : node)
-
node
-
end
-
end
-
end
-
1
module Sass
-
1
module Tree
-
# A static node that wraps the {Sass::Tree} for an `@import`ed file.
-
# It doesn't have a functional purpose other than to add the `@import`ed file
-
# to the backtrace if an error occurs.
-
1
class ImportNode < RootNode
-
# The name of the imported file as it appears in the Sass document.
-
#
-
# @return [String]
-
1
attr_reader :imported_filename
-
-
# Sets the imported file.
-
1
attr_writer :imported_file
-
-
# @param imported_filename [String] The name of the imported file
-
1
def initialize(imported_filename)
-
@imported_filename = imported_filename
-
super(nil)
-
end
-
-
1
def invisible?; to_s.empty?; end
-
-
# Returns the imported file.
-
#
-
# @return [Sass::Engine]
-
# @raise [Sass::SyntaxError] If no file could be found to import.
-
1
def imported_file
-
@imported_file ||= import
-
end
-
-
# Returns whether or not this import should emit a CSS @import declaration
-
#
-
# @return [Boolean] Whether or not this is a simple CSS @import declaration.
-
1
def css_import?
-
if @imported_filename =~ /\.css$/
-
@imported_filename
-
elsif imported_file.is_a?(String) && imported_file =~ /\.css$/
-
imported_file
-
end
-
end
-
-
1
private
-
-
1
def import
-
paths = @options[:load_paths]
-
-
if @options[:importer]
-
f = @options[:importer].find_relative(
-
@imported_filename, @options[:filename], options_for_importer)
-
return f if f
-
end
-
-
paths.each do |p|
-
if f = p.find(@imported_filename, options_for_importer)
-
return f
-
end
-
end
-
-
message = "File to import not found or unreadable: #{@imported_filename}.\n"
-
if paths.size == 1
-
message << "Load path: #{paths.first}"
-
else
-
message << "Load paths:\n " << paths.join("\n ")
-
end
-
raise SyntaxError.new(message)
-
rescue SyntaxError => e
-
raise SyntaxError.new(e.message, :line => self.line, :filename => @filename)
-
end
-
-
1
def options_for_importer
-
@options.merge(:_line => line)
-
end
-
end
-
end
-
end
-
1
module Sass::Tree
-
# A static node representing a `@media` rule.
-
# `@media` rules behave differently from other directives
-
# in that when they're nested within rules,
-
# they bubble up to top-level.
-
#
-
# @see Sass::Tree
-
1
class MediaNode < DirectiveNode
-
# TODO: parse and cache the query immediately if it has no dynamic elements
-
-
# The media query for this rule, interspersed with {Sass::Script::Node}s
-
# representing `#{}`-interpolation. Any adjacent strings will be merged
-
# together.
-
#
-
# @return [Array<String, Sass::Script::Node>]
-
1
attr_accessor :query
-
-
# The media query for this rule, without any unresolved interpolation. It's
-
# only set once {Tree::Node#perform} has been called.
-
#
-
# @return [Sass::Media::QueryList]
-
1
attr_accessor :resolved_query
-
-
# @see RuleNode#tabs
-
1
attr_accessor :tabs
-
-
# @see RuleNode#group_end
-
1
attr_accessor :group_end
-
-
# @param query [Array<String, Sass::Script::Node>] See \{#query}
-
1
def initialize(query)
-
@query = query
-
@tabs = 0
-
super('')
-
end
-
-
# @see DirectiveNode#value
-
1
def value; raise NotImplementedError; end
-
-
# @see DirectiveNode#name
-
1
def name; '@media'; end
-
-
# @see DirectiveNode#resolved_value
-
1
def resolved_value
-
@resolved_value ||= "@media #{resolved_query.to_css}"
-
end
-
-
# True when the directive has no visible children.
-
#
-
# @return [Boolean]
-
1
def invisible?
-
children.all? {|c| c.invisible?}
-
end
-
-
# @see Node#bubbles?
-
1
def bubbles?; true; end
-
end
-
end
-
1
module Sass
-
1
module Tree
-
# A dynamic node representing a mixin definition.
-
#
-
# @see Sass::Tree
-
1
class MixinDefNode < Node
-
# The mixin name.
-
# @return [String]
-
1
attr_reader :name
-
-
# The arguments for the mixin.
-
# Each element is a tuple containing the variable for argument
-
# and the parse tree for the default value of the argument.
-
#
-
# @return [Array<(Script::Node, Script::Node)>]
-
1
attr_accessor :args
-
-
# The splat argument for this mixin, if one exists.
-
#
-
# @return [Script::Node?]
-
1
attr_accessor :splat
-
-
# Whether the mixin uses `@content`. Set during the nesting check phase.
-
# @return [Boolean]
-
1
attr_accessor :has_content
-
-
# @param name [String] The mixin name
-
# @param args [Array<(Script::Node, Script::Node)>] See \{#args}
-
# @param splat [Script::Node] See \{#splat}
-
1
def initialize(name, args, splat)
-
@name = name
-
@args = args
-
@splat = splat
-
super()
-
end
-
end
-
end
-
end
-
1
require 'sass/tree/node'
-
-
1
module Sass::Tree
-
# A static node representing a mixin include.
-
# When in a static tree, the sole purpose is to wrap exceptions
-
# to add the mixin to the backtrace.
-
#
-
# @see Sass::Tree
-
1
class MixinNode < Node
-
# The name of the mixin.
-
# @return [String]
-
1
attr_reader :name
-
-
# The arguments to the mixin.
-
# @return [Array<Script::Node>]
-
1
attr_accessor :args
-
-
# A hash from keyword argument names to values.
-
# @return [{String => Script::Node}]
-
1
attr_accessor :keywords
-
-
# The splat argument for this mixin, if one exists.
-
#
-
# @return [Script::Node?]
-
1
attr_accessor :splat
-
-
# @param name [String] The name of the mixin
-
# @param args [Array<Script::Node>] See \{#args}
-
# @param splat [Script::Node] See \{#splat}
-
# @param keywords [{String => Script::Node}] See \{#keywords}
-
1
def initialize(name, args, keywords, splat)
-
@name = name
-
@args = args
-
@keywords = keywords
-
@splat = splat
-
super()
-
end
-
end
-
end
-
1
module Sass
-
# A namespace for nodes in the Sass parse tree.
-
#
-
# The Sass parse tree has three states: dynamic, static Sass, and static CSS.
-
#
-
# When it's first parsed, a Sass document is in the dynamic state.
-
# It has nodes for mixin definitions and `@for` loops and so forth,
-
# in addition to nodes for CSS rules and properties.
-
# Nodes that only appear in this state are called **dynamic nodes**.
-
#
-
# {Tree::Visitors::Perform} creates a static Sass tree, which is different.
-
# It still has nodes for CSS rules and properties
-
# but it doesn't have any dynamic-generation-related nodes.
-
# The nodes in this state are in the same structure as the Sass document:
-
# rules and properties are nested beneath one another.
-
# Nodes that can be in this state or in the dynamic state
-
# are called **static nodes**; nodes that can only be in this state
-
# are called **solely static nodes**.
-
#
-
# {Tree::Visitors::Cssize} is then used to create a static CSS tree.
-
# This is like a static Sass tree,
-
# but the structure exactly mirrors that of the generated CSS.
-
# Rules and properties can't be nested beneath one another in this state.
-
#
-
# Finally, {Tree::Visitors::ToCss} can be called on a static CSS tree
-
# to get the actual CSS code as a string.
-
1
module Tree
-
# The abstract superclass of all parse-tree nodes.
-
1
class Node
-
1
include Enumerable
-
-
# The child nodes of this node.
-
#
-
# @return [Array<Tree::Node>]
-
1
attr_accessor :children
-
-
# Whether or not this node has child nodes.
-
# This may be true even when \{#children} is empty,
-
# in which case this node has an empty block (e.g. `{}`).
-
#
-
# @return [Boolean]
-
1
attr_accessor :has_children
-
-
# The line of the document on which this node appeared.
-
#
-
# @return [Fixnum]
-
1
attr_accessor :line
-
-
# The name of the document on which this node appeared.
-
#
-
# @return [String]
-
1
attr_writer :filename
-
-
# The options hash for the node.
-
# See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
-
#
-
# @return [{Symbol => Object}]
-
1
attr_reader :options
-
-
1
def initialize
-
@children = []
-
end
-
-
# Sets the options hash for the node and all its children.
-
#
-
# @param options [{Symbol => Object}] The options
-
# @see #options
-
1
def options=(options)
-
Sass::Tree::Visitors::SetOptions.visit(self, options)
-
end
-
-
# @private
-
1
def children=(children)
-
self.has_children ||= !children.empty?
-
@children = children
-
end
-
-
# The name of the document on which this node appeared.
-
#
-
# @return [String]
-
1
def filename
-
@filename || (@options && @options[:filename])
-
end
-
-
# Appends a child to the node.
-
#
-
# @param child [Tree::Node, Array<Tree::Node>] The child node or nodes
-
# @raise [Sass::SyntaxError] if `child` is invalid
-
1
def <<(child)
-
return if child.nil?
-
if child.is_a?(Array)
-
child.each {|c| self << c}
-
else
-
self.has_children = true
-
@children << child
-
end
-
end
-
-
# Compares this node and another object (only other {Tree::Node}s will be equal).
-
# This does a structural comparison;
-
# if the contents of the nodes and all the child nodes are equivalent,
-
# then the nodes are as well.
-
#
-
# Only static nodes need to override this.
-
#
-
# @param other [Object] The object to compare with
-
# @return [Boolean] Whether or not this node and the other object
-
# are the same
-
# @see Sass::Tree
-
1
def ==(other)
-
self.class == other.class && other.children == children
-
end
-
-
# True if \{#to\_s} will return `nil`;
-
# that is, if the node shouldn't be rendered.
-
# Should only be called in a static tree.
-
#
-
# @return [Boolean]
-
1
def invisible?; false; end
-
-
# The output style. See {file:SASS_REFERENCE.md#sass_options the Sass options documentation}.
-
#
-
# @return [Symbol]
-
1
def style
-
@options[:style]
-
end
-
-
# Computes the CSS corresponding to this static CSS tree.
-
#
-
# @return [String, nil] The resulting CSS
-
# @see Sass::Tree
-
1
def to_s
-
Sass::Tree::Visitors::ToCss.visit(self)
-
end
-
-
# Returns a representation of the node for debugging purposes.
-
#
-
# @return [String]
-
1
def inspect
-
return self.class.to_s unless has_children
-
"(#{self.class} #{children.map {|c| c.inspect}.join(' ')})"
-
end
-
-
# Iterates through each node in the tree rooted at this node
-
# in a pre-order walk.
-
#
-
# @yield node
-
# @yieldparam node [Node] a node in the tree
-
1
def each
-
yield self
-
children.each {|c| c.each {|n| yield n}}
-
end
-
-
# Converts a node to Sass code that will generate it.
-
#
-
# @param options [{Symbol => Object}] An options hash (see {Sass::CSS#initialize})
-
# @return [String] The Sass code corresponding to the node
-
1
def to_sass(options = {})
-
Sass::Tree::Visitors::Convert.visit(self, options, :sass)
-
end
-
-
# Converts a node to SCSS code that will generate it.
-
#
-
# @param options [{Symbol => Object}] An options hash (see {Sass::CSS#initialize})
-
# @return [String] The Sass code corresponding to the node
-
1
def to_scss(options = {})
-
Sass::Tree::Visitors::Convert.visit(self, options, :scss)
-
end
-
-
# Return a deep clone of this node.
-
# The child nodes are cloned, but options are not.
-
#
-
# @return [Node]
-
1
def deep_copy
-
Sass::Tree::Visitors::DeepCopy.visit(self)
-
end
-
-
# Whether or not this node bubbles up through RuleNodes.
-
#
-
# @return [Boolean]
-
1
def bubbles?
-
false
-
end
-
-
1
protected
-
-
# @see Sass::Shared.balance
-
# @raise [Sass::SyntaxError] if the brackets aren't balanced
-
1
def balance(*args)
-
res = Sass::Shared.balance(*args)
-
return res if res
-
raise Sass::SyntaxError.new("Unbalanced brackets.", :line => line)
-
end
-
end
-
end
-
end
-
1
module Sass::Tree
-
# A static node reprenting a CSS property.
-
#
-
# @see Sass::Tree
-
1
class PropNode < Node
-
# The name of the property,
-
# interspersed with {Sass::Script::Node}s
-
# representing `#{}`-interpolation.
-
# Any adjacent strings will be merged together.
-
#
-
# @return [Array<String, Sass::Script::Node>]
-
1
attr_accessor :name
-
-
# The name of the property
-
# after any interpolated SassScript has been resolved.
-
# Only set once \{Tree::Visitors::Perform} has been run.
-
#
-
# @return [String]
-
1
attr_accessor :resolved_name
-
-
# The value of the property.
-
#
-
# @return [Sass::Script::Node]
-
1
attr_accessor :value
-
-
# The value of the property
-
# after any interpolated SassScript has been resolved.
-
# Only set once \{Tree::Visitors::Perform} has been run.
-
#
-
# @return [String]
-
1
attr_accessor :resolved_value
-
-
# How deep this property is indented
-
# relative to a normal property.
-
# This is only greater than 0 in the case that:
-
#
-
# * This node is in a CSS tree
-
# * The style is :nested
-
# * This is a child property of another property
-
# * The parent property has a value, and thus will be rendered
-
#
-
# @return [Fixnum]
-
1
attr_accessor :tabs
-
-
# @param name [Array<String, Sass::Script::Node>] See \{#name}
-
# @param value [Sass::Script::Node] See \{#value}
-
# @param prop_syntax [Symbol] `:new` if this property uses `a: b`-style syntax,
-
# `:old` if it uses `:a b`-style syntax
-
1
def initialize(name, value, prop_syntax)
-
@name = Sass::Util.strip_string_array(
-
Sass::Util.merge_adjacent_strings(name))
-
@value = value
-
@tabs = 0
-
@prop_syntax = prop_syntax
-
super()
-
end
-
-
# Compares the names and values of two properties.
-
#
-
# @param other [Object] The object to compare with
-
# @return [Boolean] Whether or not this node and the other object
-
# are the same
-
1
def ==(other)
-
self.class == other.class && name == other.name && value == other.value && super
-
end
-
-
# Returns a appropriate message indicating how to escape pseudo-class selectors.
-
# This only applies for old-style properties with no value,
-
# so returns the empty string if this is new-style.
-
#
-
# @return [String] The message
-
1
def pseudo_class_selector_message
-
return "" if @prop_syntax == :new || !value.is_a?(Sass::Script::String) || !value.value.empty?
-
"\nIf #{declaration.dump} should be a selector, use \"\\#{declaration}\" instead."
-
end
-
-
# Computes the Sass or SCSS code for the variable declaration.
-
# This is like \{#to\_scss} or \{#to\_sass},
-
# except it doesn't print any child properties or a trailing semicolon.
-
#
-
# @param opts [{Symbol => Object}] The options hash for the tree.
-
# @param fmt [Symbol] `:scss` or `:sass`.
-
1
def declaration(opts = {:old => @prop_syntax == :old}, fmt = :sass)
-
name = self.name.map {|n| n.is_a?(String) ? n : "\#{#{n.to_sass(opts)}}"}.join
-
if name[0] == ?:
-
raise Sass::SyntaxError.new("The \"#{name}: #{self.class.val_to_sass(value, opts)}\" hack is not allowed in the Sass indented syntax")
-
end
-
-
old = opts[:old] && fmt == :sass
-
initial = old ? ':' : ''
-
mid = old ? '' : ':'
-
"#{initial}#{name}#{mid} #{self.class.val_to_sass(value, opts)}".rstrip
-
end
-
-
# A property node is invisible if its value is empty.
-
#
-
# @return [Boolean]
-
1
def invisible?
-
resolved_value.empty?
-
end
-
-
1
private
-
-
1
def check!
-
if @options[:property_syntax] && @options[:property_syntax] != @prop_syntax
-
raise Sass::SyntaxError.new(
-
"Illegal property syntax: can't use #{@prop_syntax} syntax when :property_syntax => #{@options[:property_syntax].inspect} is set.")
-
end
-
end
-
-
1
class << self
-
# @private
-
1
def val_to_sass(value, opts)
-
val_to_sass_comma(value, opts).to_sass(opts)
-
end
-
-
1
private
-
-
1
def val_to_sass_comma(node, opts)
-
return node unless node.is_a?(Sass::Script::Operation)
-
return val_to_sass_concat(node, opts) unless node.operator == :comma
-
-
Sass::Script::Operation.new(
-
val_to_sass_concat(node.operand1, opts),
-
val_to_sass_comma(node.operand2, opts),
-
node.operator)
-
end
-
-
1
def val_to_sass_concat(node, opts)
-
return node unless node.is_a?(Sass::Script::Operation)
-
return val_to_sass_div(node, opts) unless node.operator == :space
-
-
Sass::Script::Operation.new(
-
val_to_sass_div(node.operand1, opts),
-
val_to_sass_concat(node.operand2, opts),
-
node.operator)
-
end
-
-
1
def val_to_sass_div(node, opts)
-
unless node.is_a?(Sass::Script::Operation) && node.operator == :div &&
-
node.operand1.is_a?(Sass::Script::Number) &&
-
node.operand2.is_a?(Sass::Script::Number) &&
-
(!node.operand1.original || !node.operand2.original)
-
return node
-
end
-
-
Sass::Script::String.new("(#{node.to_sass(opts)})")
-
end
-
-
end
-
end
-
end
-
1
module Sass
-
1
module Tree
-
# A dynamic node representing returning from a function.
-
#
-
# @see Sass::Tree
-
1
class ReturnNode < Node
-
# The expression to return.
-
# @type [Script::Node]
-
1
attr_accessor :expr
-
-
# @param expr [Script::Node] The expression to return
-
1
def initialize(expr)
-
@expr = expr
-
super()
-
end
-
end
-
end
-
end
-
1
module Sass
-
1
module Tree
-
# A static node that is the root node of the Sass document.
-
1
class RootNode < Node
-
# The Sass template from which this node was created
-
#
-
# @param template [String]
-
1
attr_reader :template
-
-
# @param template [String] The Sass template from which this node was created
-
1
def initialize(template)
-
super()
-
@template = template
-
end
-
-
# Runs the dynamic Sass code *and* computes the CSS for the tree.
-
# @see #to_s
-
1
def render
-
Visitors::CheckNesting.visit(self)
-
result = Visitors::Perform.visit(self)
-
Visitors::CheckNesting.visit(result) # Check again to validate mixins
-
result, extends = Visitors::Cssize.visit(result)
-
Visitors::Extend.visit(result, extends)
-
result.to_s
-
end
-
end
-
end
-
end
-
1
require 'pathname'
-
1
require 'uri'
-
-
1
module Sass::Tree
-
# A static node reprenting a CSS rule.
-
#
-
# @see Sass::Tree
-
1
class RuleNode < Node
-
# The character used to include the parent selector
-
1
PARENT = '&'
-
-
# The CSS selector for this rule,
-
# interspersed with {Sass::Script::Node}s
-
# representing `#{}`-interpolation.
-
# Any adjacent strings will be merged together.
-
#
-
# @return [Array<String, Sass::Script::Node>]
-
1
attr_accessor :rule
-
-
# The CSS selector for this rule,
-
# without any unresolved interpolation
-
# but with parent references still intact.
-
# It's only set once {Tree::Node#perform} has been called.
-
#
-
# @return [Selector::CommaSequence]
-
1
attr_accessor :parsed_rules
-
-
# The CSS selector for this rule,
-
# without any unresolved interpolation or parent references.
-
# It's only set once {Tree::Visitors::Cssize} has been run.
-
#
-
# @return [Selector::CommaSequence]
-
1
attr_accessor :resolved_rules
-
-
# How deep this rule is indented
-
# relative to a base-level rule.
-
# This is only greater than 0 in the case that:
-
#
-
# * This node is in a CSS tree
-
# * The style is :nested
-
# * This is a child rule of another rule
-
# * The parent rule has properties, and thus will be rendered
-
#
-
# @return [Fixnum]
-
1
attr_accessor :tabs
-
-
# Whether or not this rule is the last rule in a nested group.
-
# This is only set in a CSS tree.
-
#
-
# @return [Boolean]
-
1
attr_accessor :group_end
-
-
# The stack trace.
-
# This is only readable in a CSS tree as it is written during the perform step
-
# and only when the :trace_selectors option is set.
-
#
-
# @return [Array<String>]
-
1
attr_accessor :stack_trace
-
-
# @param rule [Array<String, Sass::Script::Node>]
-
# The CSS rule. See \{#rule}
-
1
def initialize(rule)
-
merged = Sass::Util.merge_adjacent_strings(rule)
-
@rule = Sass::Util.strip_string_array(merged)
-
@tabs = 0
-
try_to_parse_non_interpolated_rules
-
super()
-
end
-
-
# If we've precached the parsed selector, set the line on it, too.
-
1
def line=(line)
-
@parsed_rules.line = line if @parsed_rules
-
super
-
end
-
-
# If we've precached the parsed selector, set the filename on it, too.
-
1
def filename=(filename)
-
@parsed_rules.filename = filename if @parsed_rules
-
super
-
end
-
-
# Compares the contents of two rules.
-
#
-
# @param other [Object] The object to compare with
-
# @return [Boolean] Whether or not this node and the other object
-
# are the same
-
1
def ==(other)
-
self.class == other.class && rule == other.rule && super
-
end
-
-
# Adds another {RuleNode}'s rules to this one's.
-
#
-
# @param node [RuleNode] The other node
-
1
def add_rules(node)
-
@rule = Sass::Util.strip_string_array(
-
Sass::Util.merge_adjacent_strings(@rule + ["\n"] + node.rule))
-
try_to_parse_non_interpolated_rules
-
end
-
-
# @return [Boolean] Whether or not this rule is continued on the next line
-
1
def continued?
-
last = @rule.last
-
last.is_a?(String) && last[-1] == ?,
-
end
-
-
# A hash that will be associated with this rule in the CSS document
-
# if the {file:SASS_REFERENCE.md#debug_info-option `:debug_info` option} is enabled.
-
# This data is used by e.g. [the FireSass Firebug extension](https://addons.mozilla.org/en-US/firefox/addon/103988).
-
#
-
# @return [{#to_s => #to_s}]
-
1
def debug_info
-
{:filename => filename && ("file://" + URI.escape(File.expand_path(filename))),
-
:line => self.line}
-
end
-
-
# A rule node is invisible if it has only placeholder selectors.
-
1
def invisible?
-
resolved_rules.members.all? {|seq| seq.has_placeholder?}
-
end
-
-
1
private
-
-
1
def try_to_parse_non_interpolated_rules
-
if @rule.all? {|t| t.kind_of?(String)}
-
# We don't use real filename/line info because we don't have it yet.
-
# When we get it, we'll set it on the parsed rules if possible.
-
parser = Sass::SCSS::StaticParser.new(@rule.join.strip, '', 1)
-
@parsed_rules = parser.parse_selector rescue nil
-
end
-
end
-
end
-
end
-
1
module Sass::Tree
-
# A static node representing a `@supports` rule.
-
# `@supports` rules behave differently from other directives
-
# in that when they're nested within rules,
-
# they bubble up to top-level.
-
#
-
# @see Sass::Tree
-
1
class SupportsNode < DirectiveNode
-
# The name, which may include a browser prefix.
-
#
-
# @return [String]
-
1
attr_accessor :name
-
-
# The supports condition.
-
#
-
# @return [Sass::Supports::Condition]
-
1
attr_accessor :condition
-
-
# @see RuleNode#tabs
-
1
attr_accessor :tabs
-
-
# @see RuleNode#group_end
-
1
attr_accessor :group_end
-
-
# @param condition [Sass::Supports::Condition] See \{#condition}
-
1
def initialize(name, condition)
-
@name = name
-
@condition = condition
-
@tabs = 0
-
super('')
-
end
-
-
# @see DirectiveNode#value
-
1
def value; raise NotImplementedError; end
-
-
# @see DirectiveNode#resolved_value
-
1
def resolved_value
-
@resolved_value ||= "@#{name} #{condition.to_css}"
-
end
-
-
# True when the directive has no visible children.
-
#
-
# @return [Boolean]
-
1
def invisible?
-
children.all? {|c| c.invisible?}
-
end
-
-
# @see Node#bubbles?
-
1
def bubbles?; true; end
-
end
-
end
-
1
require 'sass/tree/node'
-
-
1
module Sass::Tree
-
# A solely static node left over after a mixin include or @content has been performed.
-
# Its sole purpose is to wrap exceptions to add to the backtrace.
-
#
-
# @see Sass::Tree
-
1
class TraceNode < Node
-
# The name of the trace entry to add.
-
# @return [String]
-
1
attr_reader :name
-
-
# @param name [String] The name of the trace entry to add.
-
1
def initialize(name)
-
@name = name
-
self.has_children = true
-
super()
-
end
-
-
# Initializes this node from an existing node.
-
# @param name [String] The name of the trace entry to add.
-
# @param mixin [Node] The node to copy information from.
-
# @return [TraceNode]
-
1
def self.from_node(name, node)
-
trace = new(name)
-
trace.line = node.line
-
trace.filename = node.filename
-
trace.options = node.options
-
trace
-
end
-
end
-
end
-
1
module Sass
-
1
module Tree
-
# A dynamic node representing a variable definition.
-
#
-
# @see Sass::Tree
-
1
class VariableNode < Node
-
# The name of the variable.
-
# @return [String]
-
1
attr_reader :name
-
-
# The parse tree for the variable value.
-
# @return [Script::Node]
-
1
attr_accessor :expr
-
-
# Whether this is a guarded variable assignment (`!default`).
-
# @return [Boolean]
-
1
attr_reader :guarded
-
-
# @param name [String] The name of the variable
-
# @param expr [Script::Node] See \{#expr}
-
# @param guarded [Boolean] See \{#guarded}
-
1
def initialize(name, expr, guarded)
-
@name = name
-
@expr = expr
-
@guarded = guarded
-
super()
-
end
-
end
-
end
-
end
-
# Visitors are used to traverse the Sass parse tree.
-
# Visitors should extend {Visitors::Base},
-
# which provides a small amount of scaffolding for traversal.
-
1
module Sass::Tree::Visitors
-
# The abstract base class for Sass visitors.
-
# Visitors should extend this class,
-
# then implement `visit_*` methods for each node they care about
-
# (e.g. `visit_rule` for {RuleNode} or `visit_for` for {ForNode}).
-
# These methods take the node in question as argument.
-
# They may `yield` to visit the child nodes of the current node.
-
#
-
# *Note*: due to the unusual nature of {Sass::Tree::IfNode},
-
# special care must be taken to ensure that it is properly handled.
-
# In particular, there is no built-in scaffolding
-
# for dealing with the return value of `@else` nodes.
-
#
-
# @abstract
-
1
class Base
-
# Runs the visitor on a tree.
-
#
-
# @param root [Tree::Node] The root node of the Sass tree.
-
# @return [Object] The return value of \{#visit} for the root node.
-
1
def self.visit(root)
-
new.send(:visit, root)
-
end
-
-
1
protected
-
-
# Runs the visitor on the given node.
-
# This can be overridden by subclasses that need to do something for each node.
-
#
-
# @param node [Tree::Node] The node to visit.
-
# @return [Object] The return value of the `visit_*` method for this node.
-
1
def visit(node)
-
method = "visit_#{node_name node}"
-
if self.respond_to?(method, true)
-
self.send(method, node) {visit_children(node)}
-
else
-
visit_children(node)
-
end
-
end
-
-
# Visit the child nodes for a given node.
-
# This can be overridden by subclasses that need to do something
-
# with the child nodes' return values.
-
#
-
# This method is run when `visit_*` methods `yield`,
-
# and its return value is returned from the `yield`.
-
#
-
# @param parent [Tree::Node] The parent node of the children to visit.
-
# @return [Array<Object>] The return values of the `visit_*` methods for the children.
-
1
def visit_children(parent)
-
parent.children.map {|c| visit(c)}
-
end
-
-
1
NODE_NAME_RE = /.*::(.*?)Node$/
-
-
# Returns the name of a node as used in the `visit_*` method.
-
#
-
# @param [Tree::Node] node The node.
-
# @return [String] The name.
-
1
def node_name(node)
-
@@node_names ||= {}
-
@@node_names[node.class.name] ||= node.class.name.gsub(NODE_NAME_RE, '\\1').downcase
-
end
-
-
# `yield`s, then runs the visitor on the `@else` clause if the node has one.
-
# This exists to ensure that the contents of the `@else` clause get visited.
-
1
def visit_if(node)
-
yield
-
visit(node.else) if node.else
-
node
-
end
-
end
-
end
-
# A visitor for checking that all nodes are properly nested.
-
1
class Sass::Tree::Visitors::CheckNesting < Sass::Tree::Visitors::Base
-
1
protected
-
-
1
def initialize
-
@parents = []
-
end
-
-
1
def visit(node)
-
if error = @parent && (
-
try_send("invalid_#{node_name @parent}_child?", @parent, node) ||
-
try_send("invalid_#{node_name node}_parent?", @parent, node))
-
raise Sass::SyntaxError.new(error)
-
end
-
super
-
rescue Sass::SyntaxError => e
-
e.modify_backtrace(:filename => node.filename, :line => node.line)
-
raise e
-
end
-
-
1
CONTROL_NODES = [Sass::Tree::EachNode, Sass::Tree::ForNode, Sass::Tree::IfNode,
-
Sass::Tree::WhileNode, Sass::Tree::TraceNode]
-
1
SCRIPT_NODES = [Sass::Tree::ImportNode] + CONTROL_NODES
-
1
def visit_children(parent)
-
old_parent = @parent
-
@parent = parent unless is_any_of?(parent, SCRIPT_NODES) ||
-
(parent.bubbles? && !old_parent.is_a?(Sass::Tree::RootNode))
-
@parents.push parent
-
super
-
ensure
-
@parent = old_parent
-
@parents.pop
-
end
-
-
1
def visit_root(node)
-
yield
-
rescue Sass::SyntaxError => e
-
e.sass_template ||= node.template
-
raise e
-
end
-
-
1
def visit_import(node)
-
yield
-
rescue Sass::SyntaxError => e
-
e.modify_backtrace(:filename => node.children.first.filename)
-
e.add_backtrace(:filename => node.filename, :line => node.line)
-
raise e
-
end
-
-
1
def visit_mixindef(node)
-
@current_mixin_def, old_mixin_def = node, @current_mixin_def
-
yield
-
ensure
-
@current_mixin_def = old_mixin_def
-
end
-
-
1
def invalid_content_parent?(parent, child)
-
if @current_mixin_def
-
@current_mixin_def.has_content = true
-
nil
-
else
-
"@content may only be used within a mixin."
-
end
-
end
-
-
1
def invalid_charset_parent?(parent, child)
-
"@charset may only be used at the root of a document." unless parent.is_a?(Sass::Tree::RootNode)
-
end
-
-
1
VALID_EXTEND_PARENTS = [Sass::Tree::RuleNode, Sass::Tree::MixinDefNode, Sass::Tree::MixinNode]
-
1
def invalid_extend_parent?(parent, child)
-
unless is_any_of?(parent, VALID_EXTEND_PARENTS)
-
return "Extend directives may only be used within rules."
-
end
-
end
-
-
1
INVALID_IMPORT_PARENTS = CONTROL_NODES +
-
[Sass::Tree::MixinDefNode, Sass::Tree::MixinNode]
-
1
def invalid_import_parent?(parent, child)
-
unless (@parents.map {|p| p.class} & INVALID_IMPORT_PARENTS).empty?
-
return "Import directives may not be used within control directives or mixins."
-
end
-
return if parent.is_a?(Sass::Tree::RootNode)
-
return "CSS import directives may only be used at the root of a document." if child.css_import?
-
rescue Sass::SyntaxError => e
-
e.modify_backtrace(:filename => child.imported_file.options[:filename])
-
e.add_backtrace(:filename => child.filename, :line => child.line)
-
raise e
-
end
-
-
1
def invalid_mixindef_parent?(parent, child)
-
unless (@parents.map {|p| p.class} & INVALID_IMPORT_PARENTS).empty?
-
return "Mixins may not be defined within control directives or other mixins."
-
end
-
end
-
-
1
def invalid_function_parent?(parent, child)
-
unless (@parents.map {|p| p.class} & INVALID_IMPORT_PARENTS).empty?
-
return "Functions may not be defined within control directives or other mixins."
-
end
-
end
-
-
1
VALID_FUNCTION_CHILDREN = [
-
Sass::Tree::CommentNode, Sass::Tree::DebugNode, Sass::Tree::ReturnNode,
-
Sass::Tree::VariableNode, Sass::Tree::WarnNode
-
] + CONTROL_NODES
-
1
def invalid_function_child?(parent, child)
-
unless is_any_of?(child, VALID_FUNCTION_CHILDREN)
-
"Functions can only contain variable declarations and control directives."
-
end
-
end
-
-
1
VALID_PROP_CHILDREN = [Sass::Tree::CommentNode, Sass::Tree::PropNode, Sass::Tree::MixinNode] + CONTROL_NODES
-
1
def invalid_prop_child?(parent, child)
-
unless is_any_of?(child, VALID_PROP_CHILDREN)
-
"Illegal nesting: Only properties may be nested beneath properties."
-
end
-
end
-
-
1
VALID_PROP_PARENTS = [Sass::Tree::RuleNode, Sass::Tree::PropNode,
-
Sass::Tree::MixinDefNode, Sass::Tree::DirectiveNode,
-
Sass::Tree::MixinNode]
-
1
def invalid_prop_parent?(parent, child)
-
unless is_any_of?(parent, VALID_PROP_PARENTS)
-
"Properties are only allowed within rules, directives, mixin includes, or other properties." + child.pseudo_class_selector_message
-
end
-
end
-
-
1
def invalid_return_parent?(parent, child)
-
"@return may only be used within a function." unless parent.is_a?(Sass::Tree::FunctionNode)
-
end
-
-
1
private
-
-
1
def is_any_of?(val, classes)
-
for c in classes
-
return true if val.is_a?(c)
-
end
-
return false
-
end
-
-
1
def try_send(method, *args)
-
return unless respond_to?(method, true)
-
send(method, *args)
-
end
-
end
-
-
# A visitor for converting a Sass tree into a source string.
-
1
class Sass::Tree::Visitors::Convert < Sass::Tree::Visitors::Base
-
# Runs the visitor on a tree.
-
#
-
# @param root [Tree::Node] The root node of the Sass tree.
-
# @param options [{Symbol => Object}] An options hash (see {Sass::CSS#initialize}).
-
# @param format [Symbol] `:sass` or `:scss`.
-
# @return [String] The Sass or SCSS source for the tree.
-
1
def self.visit(root, options, format)
-
new(options, format).send(:visit, root)
-
end
-
-
1
protected
-
-
1
def initialize(options, format)
-
@options = options
-
@format = format
-
@tabs = 0
-
# 2 spaces by default
-
@tab_chars = @options[:indent] || " "
-
end
-
-
1
def visit_children(parent)
-
@tabs += 1
-
return @format == :sass ? "\n" : " {}\n" if parent.children.empty?
-
(@format == :sass ? "\n" : " {\n") + super.join.rstrip + (@format == :sass ? "\n" : "\n#{ @tab_chars * (@tabs-1)}}\n")
-
ensure
-
@tabs -= 1
-
end
-
-
# Ensures proper spacing between top-level nodes.
-
1
def visit_root(node)
-
Sass::Util.enum_cons(node.children + [nil], 2).map do |child, nxt|
-
visit(child) +
-
if nxt &&
-
(child.is_a?(Sass::Tree::CommentNode) &&
-
child.line + child.lines + 1 == nxt.line) ||
-
(child.is_a?(Sass::Tree::ImportNode) && nxt.is_a?(Sass::Tree::ImportNode) &&
-
child.line + 1 == nxt.line) ||
-
(child.is_a?(Sass::Tree::VariableNode) && nxt.is_a?(Sass::Tree::VariableNode) &&
-
child.line + 1 == nxt.line)
-
""
-
else
-
"\n"
-
end
-
end.join.rstrip + "\n"
-
end
-
-
1
def visit_charset(node)
-
"#{tab_str}@charset \"#{node.name}\"#{semi}\n"
-
end
-
-
1
def visit_comment(node)
-
value = interp_to_src(node.value)
-
content = if @format == :sass
-
content = value.gsub(/\*\/$/, '').rstrip
-
if content =~ /\A[ \t]/
-
# Re-indent SCSS comments like this:
-
# /* foo
-
# bar
-
# baz */
-
content.gsub!(/^/, ' ')
-
content.sub!(/\A([ \t]*)\/\*/, '/*\1')
-
end
-
-
content =
-
unless content.include?("\n")
-
content
-
else
-
content.gsub!(/\n( \*|\/\/)/, "\n ")
-
spaces = content.scan(/\n( *)/).map {|s| s.first.size}.min
-
sep = node.type == :silent ? "\n//" : "\n *"
-
if spaces >= 2
-
content.gsub(/\n /, sep)
-
else
-
content.gsub(/\n#{' ' * spaces}/, sep)
-
end
-
end
-
-
content.gsub!(/\A\/\*/, '//') if node.type == :silent
-
content.gsub!(/^/, tab_str)
-
content.rstrip + "\n"
-
else
-
spaces = (@tab_chars * [@tabs - value[/^ */].size, 0].max)
-
content = if node.type == :silent
-
value.gsub(/^[\/ ]\*/, '//').gsub(/ *\*\/$/, '')
-
else
-
value
-
end.gsub(/^/, spaces) + "\n"
-
content
-
end
-
content
-
end
-
-
1
def visit_debug(node)
-
"#{tab_str}@debug #{node.expr.to_sass(@options)}#{semi}\n"
-
end
-
-
1
def visit_directive(node)
-
res = "#{tab_str}#{interp_to_src(node.value)}"
-
res.gsub!(/^@import \#\{(.*)\}([^}]*)$/, '@import \1\2');
-
return res + "#{semi}\n" unless node.has_children
-
res + yield + "\n"
-
end
-
-
1
def visit_each(node)
-
"#{tab_str}@each $#{dasherize(node.var)} in #{node.list.to_sass(@options)}#{yield}"
-
end
-
-
1
def visit_extend(node)
-
"#{tab_str}@extend #{selector_to_src(node.selector).lstrip}#{semi}#{" !optional" if node.optional?}\n"
-
end
-
-
1
def visit_for(node)
-
"#{tab_str}@for $#{dasherize(node.var)} from #{node.from.to_sass(@options)} " +
-
"#{node.exclusive ? "to" : "through"} #{node.to.to_sass(@options)}#{yield}"
-
end
-
-
1
def visit_function(node)
-
args = node.args.map do |v, d|
-
d ? "#{v.to_sass(@options)}: #{d.to_sass(@options)}" : v.to_sass(@options)
-
end.join(", ")
-
if node.splat
-
args << ", " unless node.args.empty?
-
args << node.splat.to_sass(@options) << "..."
-
end
-
-
"#{tab_str}@function #{dasherize(node.name)}(#{args})#{yield}"
-
end
-
-
1
def visit_if(node)
-
name =
-
if !@is_else; "if"
-
elsif node.expr; "else if"
-
else; "else"
-
end
-
@is_else = false
-
str = "#{tab_str}@#{name}"
-
str << " #{node.expr.to_sass(@options)}" if node.expr
-
str << yield
-
@is_else = true
-
str << visit(node.else) if node.else
-
str
-
ensure
-
@is_else = false
-
end
-
-
1
def visit_import(node)
-
quote = @format == :scss ? '"' : ''
-
"#{tab_str}@import #{quote}#{node.imported_filename}#{quote}#{semi}\n"
-
end
-
-
1
def visit_media(node)
-
"#{tab_str}@media #{media_interp_to_src(node.query)}#{yield}"
-
end
-
-
1
def visit_supports(node)
-
"#{tab_str}@#{node.name} #{node.condition.to_src(@options)}#{yield}"
-
end
-
-
1
def visit_cssimport(node)
-
if node.uri.is_a?(Sass::Script::Node)
-
str = "#{tab_str}@import #{node.uri.to_sass(@options)}"
-
else
-
str = "#{tab_str}@import #{node.uri}"
-
end
-
str << " #{interp_to_src(node.query)}" unless node.query.empty?
-
"#{str}#{semi}\n"
-
end
-
-
1
def visit_mixindef(node)
-
args =
-
if node.args.empty? && node.splat.nil?
-
""
-
else
-
str = '('
-
str << node.args.map do |v, d|
-
if d
-
"#{v.to_sass(@options)}: #{d.to_sass(@options)}"
-
else
-
v.to_sass(@options)
-
end
-
end.join(", ")
-
-
if node.splat
-
str << ", " unless node.args.empty?
-
str << node.splat.to_sass(@options) << '...'
-
end
-
-
str << ')'
-
end
-
-
"#{tab_str}#{@format == :sass ? '=' : '@mixin '}#{dasherize(node.name)}#{args}#{yield}"
-
end
-
-
1
def visit_mixin(node)
-
arg_to_sass = lambda do |arg|
-
sass = arg.to_sass(@options)
-
sass = "(#{sass})" if arg.is_a?(Sass::Script::List) && arg.separator == :comma
-
sass
-
end
-
-
unless node.args.empty? && node.keywords.empty? && node.splat.nil?
-
args = node.args.map(&arg_to_sass).join(", ")
-
keywords = Sass::Util.hash_to_a(node.keywords).
-
map {|k, v| "$#{dasherize(k)}: #{arg_to_sass[v]}"}.join(', ')
-
if node.splat
-
splat = (args.empty? && keywords.empty?) ? "" : ", "
-
splat = "#{splat}#{arg_to_sass[node.splat]}..."
-
end
-
arglist = "(#{args}#{', ' unless args.empty? || keywords.empty?}#{keywords}#{splat})"
-
end
-
"#{tab_str}#{@format == :sass ? '+' : '@include '}#{dasherize(node.name)}#{arglist}#{node.has_children ? yield : semi}\n"
-
end
-
-
1
def visit_content(node)
-
"#{tab_str}@content#{semi}\n"
-
end
-
-
1
def visit_prop(node)
-
res = tab_str + node.declaration(@options, @format)
-
return res + semi + "\n" if node.children.empty?
-
res + yield.rstrip + semi + "\n"
-
end
-
-
1
def visit_return(node)
-
"#{tab_str}@return #{node.expr.to_sass(@options)}#{semi}\n"
-
end
-
-
1
def visit_rule(node)
-
if @format == :sass
-
name = selector_to_sass(node.rule)
-
name = "\\" + name if name[0] == ?:
-
name.gsub(/^/, tab_str) + yield
-
elsif @format == :scss
-
name = selector_to_scss(node.rule)
-
res = name + yield
-
if node.children.last.is_a?(Sass::Tree::CommentNode) && node.children.last.type == :silent
-
res.slice!(-3..-1)
-
res << "\n" << tab_str << "}\n"
-
end
-
res
-
end
-
end
-
-
1
def visit_variable(node)
-
"#{tab_str}$#{dasherize(node.name)}: #{node.expr.to_sass(@options)}#{' !default' if node.guarded}#{semi}\n"
-
end
-
-
1
def visit_warn(node)
-
"#{tab_str}@warn #{node.expr.to_sass(@options)}#{semi}\n"
-
end
-
-
1
def visit_while(node)
-
"#{tab_str}@while #{node.expr.to_sass(@options)}#{yield}"
-
end
-
-
1
private
-
-
1
def interp_to_src(interp)
-
interp.map do |r|
-
next r if r.is_a?(String)
-
"\#{#{r.to_sass(@options)}}"
-
end.join
-
end
-
-
# Like interp_to_src, but removes the unnecessary `#{}` around the keys and
-
# values in media expressions.
-
1
def media_interp_to_src(interp)
-
Sass::Util.enum_with_index(interp).map do |r, i|
-
next r if r.is_a?(String)
-
before, after = interp[i-1], interp[i+1]
-
if before.is_a?(String) && after.is_a?(String) &&
-
((before[-1] == ?( && after[0] == ?:) ||
-
(before =~ /:\s*/ && after[0] == ?)))
-
r.to_sass(@options)
-
else
-
"\#{#{r.to_sass(@options)}}"
-
end
-
end.join
-
end
-
-
1
def selector_to_src(sel)
-
@format == :sass ? selector_to_sass(sel) : selector_to_scss(sel)
-
end
-
-
1
def selector_to_sass(sel)
-
sel.map do |r|
-
if r.is_a?(String)
-
r.gsub(/(,)?([ \t]*)\n\s*/) {$1 ? "#{$1}#{$2}\n" : " "}
-
else
-
"\#{#{r.to_sass(@options)}}"
-
end
-
end.join
-
end
-
-
1
def selector_to_scss(sel)
-
interp_to_src(sel).gsub(/^[ \t]*/, tab_str).gsub(/[ \t]*$/, '')
-
end
-
-
1
def semi
-
@format == :sass ? "" : ";"
-
end
-
-
1
def tab_str
-
@tab_chars * @tabs
-
end
-
-
1
def dasherize(s)
-
if @options[:dasherize]
-
s.gsub('_', '-')
-
else
-
s
-
end
-
end
-
end
-
# A visitor for converting a static Sass tree into a static CSS tree.
-
1
class Sass::Tree::Visitors::Cssize < Sass::Tree::Visitors::Base
-
# @param root [Tree::Node] The root node of the tree to visit.
-
# @return [(Tree::Node, Sass::Util::SubsetMap)] The resulting tree of static nodes
-
# *and* the extensions defined for this tree
-
1
def self.visit(root); super; end
-
-
1
protected
-
-
# Returns the immediate parent of the current node.
-
# @return [Tree::Node]
-
1
attr_reader :parent
-
-
1
def initialize
-
@parent_directives = []
-
@extends = Sass::Util::SubsetMap.new
-
end
-
-
# If an exception is raised, this adds proper metadata to the backtrace.
-
1
def visit(node)
-
super(node)
-
rescue Sass::SyntaxError => e
-
e.modify_backtrace(:filename => node.filename, :line => node.line)
-
raise e
-
end
-
-
# Keeps track of the current parent node.
-
1
def visit_children(parent)
-
with_parent parent do
-
parent.children = super.flatten
-
parent
-
end
-
end
-
-
1
MERGEABLE_DIRECTIVES = [Sass::Tree::MediaNode]
-
-
# Runs a block of code with the current parent node
-
# replaced with the given node.
-
#
-
# @param parent [Tree::Node] The new parent for the duration of the block.
-
# @yield A block in which the parent is set to `parent`.
-
# @return [Object] The return value of the block.
-
1
def with_parent(parent)
-
if parent.is_a?(Sass::Tree::DirectiveNode)
-
if MERGEABLE_DIRECTIVES.any? {|klass| parent.is_a?(klass)}
-
old_parent_directive = @parent_directives.pop
-
end
-
@parent_directives.push parent
-
end
-
-
old_parent, @parent = @parent, parent
-
yield
-
ensure
-
@parent_directives.pop if parent.is_a?(Sass::Tree::DirectiveNode)
-
@parent_directives.push old_parent_directive if old_parent_directive
-
@parent = old_parent
-
end
-
-
# In Ruby 1.8, ensures that there's only one `@charset` directive
-
# and that it's at the top of the document.
-
#
-
# @return [(Tree::Node, Sass::Util::SubsetMap)] The resulting tree of static nodes
-
# *and* the extensions defined for this tree
-
1
def visit_root(node)
-
yield
-
-
if parent.nil?
-
# In Ruby 1.9 we can make all @charset nodes invisible
-
# and infer the final @charset from the encoding of the final string.
-
if Sass::Util.ruby1_8?
-
charset = node.children.find {|c| c.is_a?(Sass::Tree::CharsetNode)}
-
node.children.reject! {|c| c.is_a?(Sass::Tree::CharsetNode)}
-
node.children.unshift charset if charset
-
end
-
-
imports_to_move = []
-
import_limit = nil
-
i = -1
-
node.children.reject! do |n|
-
i += 1
-
if import_limit
-
next false unless n.is_a?(Sass::Tree::CssImportNode)
-
imports_to_move << n
-
next true
-
end
-
-
if !n.is_a?(Sass::Tree::CommentNode) &&
-
!n.is_a?(Sass::Tree::CharsetNode) &&
-
!n.is_a?(Sass::Tree::CssImportNode)
-
import_limit = i
-
end
-
-
false
-
end
-
-
if import_limit
-
node.children = node.children[0...import_limit] + imports_to_move +
-
node.children[import_limit..-1]
-
end
-
end
-
-
return node, @extends
-
rescue Sass::SyntaxError => e
-
e.sass_template ||= node.template
-
raise e
-
end
-
-
# A simple struct wrapping up information about a single `@extend` instance. A
-
# single [ExtendNode] can have multiple Extends if either the parent node or
-
# the extended selector is a comma sequence.
-
#
-
# @attr extender [Sass::Selector::Sequence]
-
# The selector of the CSS rule containing the `@extend`.
-
# @attr target [Array<Sass::Selector::Simple>] The selector being `@extend`ed.
-
# @attr node [Sass::Tree::ExtendNode] The node that produced this extend.
-
# @attr directives [Array<Sass::Tree::DirectiveNode>]
-
# The directives containing the `@extend`.
-
# @attr result [Symbol]
-
# The result of this extend. One of `:not_found` (the target doesn't exist
-
# in the document), `:failed_to_unify` (the target exists but cannot be
-
# unified with the extender), or `:succeeded`.
-
1
Extend = Struct.new(:extender, :target, :node, :directives, :result)
-
-
# Registers an extension in the `@extends` subset map.
-
1
def visit_extend(node)
-
node.resolved_selector.members.each do |seq|
-
if seq.members.size > 1
-
raise Sass::SyntaxError.new("Can't extend #{seq.to_a.join}: can't extend nested selectors")
-
end
-
-
sseq = seq.members.first
-
if !sseq.is_a?(Sass::Selector::SimpleSequence)
-
raise Sass::SyntaxError.new("Can't extend #{seq.to_a.join}: invalid selector")
-
elsif sseq.members.any? {|ss| ss.is_a?(Sass::Selector::Parent)}
-
raise Sass::SyntaxError.new("Can't extend #{seq.to_a.join}: can't extend parent selectors")
-
end
-
-
sel = sseq.members
-
parent.resolved_rules.members.each do |member|
-
if !member.members.last.is_a?(Sass::Selector::SimpleSequence)
-
raise Sass::SyntaxError.new("#{member} can't extend: invalid selector")
-
end
-
-
@extends[sel] = Extend.new(member, sel, node, @parent_directives.dup, :not_found)
-
end
-
end
-
-
[]
-
end
-
-
# Modifies exception backtraces to include the imported file.
-
1
def visit_import(node)
-
# Don't use #visit_children to avoid adding the import node to the list of parents.
-
node.children.map {|c| visit(c)}.flatten
-
rescue Sass::SyntaxError => e
-
e.modify_backtrace(:filename => node.children.first.filename)
-
e.add_backtrace(:filename => node.filename, :line => node.line)
-
raise e
-
end
-
-
# Bubbles the `@media` directive up through RuleNodes
-
# and merges it with other `@media` directives.
-
1
def visit_media(node)
-
yield unless bubble(node)
-
media = node.children.select {|c| c.is_a?(Sass::Tree::MediaNode)}
-
node.children.reject! {|c| c.is_a?(Sass::Tree::MediaNode)}
-
media = media.select {|n| n.resolved_query = n.resolved_query.merge(node.resolved_query)}
-
(node.children.empty? ? [] : [node]) + media
-
end
-
-
# Bubbles the `@supports` directive up through RuleNodes.
-
1
def visit_supports(node)
-
yield unless bubble(node)
-
node
-
end
-
-
# Asserts that all the traced children are valid in their new location.
-
1
def visit_trace(node)
-
# Don't use #visit_children to avoid adding the trace node to the list of parents.
-
node.children.map {|c| visit(c)}.flatten
-
rescue Sass::SyntaxError => e
-
e.modify_backtrace(:mixin => node.name, :filename => node.filename, :line => node.line)
-
e.add_backtrace(:filename => node.filename, :line => node.line)
-
raise e
-
end
-
-
# Converts nested properties into flat properties
-
# and updates the indentation of the prop node based on the nesting level.
-
1
def visit_prop(node)
-
if parent.is_a?(Sass::Tree::PropNode)
-
node.resolved_name = "#{parent.resolved_name}-#{node.resolved_name}"
-
node.tabs = parent.tabs + (parent.resolved_value.empty? ? 0 : 1) if node.style == :nested
-
end
-
-
yield
-
-
result = node.children.dup
-
if !node.resolved_value.empty? || node.children.empty?
-
node.send(:check!)
-
result.unshift(node)
-
end
-
-
result
-
end
-
-
# Resolves parent references and nested selectors,
-
# and updates the indentation of the rule node based on the nesting level.
-
1
def visit_rule(node)
-
parent_resolved_rules = parent.is_a?(Sass::Tree::RuleNode) ? parent.resolved_rules : nil
-
# It's possible for resolved_rules to be set if we've duplicated this node during @media bubbling
-
node.resolved_rules ||= node.parsed_rules.resolve_parent_refs(parent_resolved_rules)
-
-
yield
-
-
rules = node.children.select {|c| c.is_a?(Sass::Tree::RuleNode) || c.bubbles?}
-
props = node.children.reject {|c| c.is_a?(Sass::Tree::RuleNode) || c.bubbles? || c.invisible?}
-
-
unless props.empty?
-
node.children = props
-
rules.each {|r| r.tabs += 1} if node.style == :nested
-
rules.unshift(node)
-
end
-
-
rules.last.group_end = true unless parent.is_a?(Sass::Tree::RuleNode) || rules.empty?
-
-
rules
-
end
-
-
1
private
-
-
1
def bubble(node)
-
return unless parent.is_a?(Sass::Tree::RuleNode)
-
new_rule = parent.dup
-
new_rule.children = node.children
-
node.children = with_parent(node) {Array(visit(new_rule))}
-
# If the last child is actually the end of the group,
-
# the parent's cssize will set it properly
-
node.children.last.group_end = false unless node.children.empty?
-
true
-
end
-
end
-
# A visitor for performing selector inheritance on a static CSS tree.
-
#
-
# Destructively modifies the tree.
-
1
class Sass::Tree::Visitors::Extend < Sass::Tree::Visitors::Base
-
# Performs the given extensions on the static CSS tree based in `root`, then
-
# validates that all extends matched some selector.
-
#
-
# @param root [Tree::Node] The root node of the tree to visit.
-
# @param extends [Sass::Util::SubsetMap{Selector::Simple =>
-
# Sass::Tree::Visitors::Cssize::Extend}]
-
# The extensions to perform on this tree.
-
# @return [Object] The return value of \{#visit} for the root node.
-
1
def self.visit(root, extends)
-
return if extends.empty?
-
new(extends).send(:visit, root)
-
check_extends_fired! extends
-
end
-
-
1
protected
-
-
1
def initialize(extends)
-
@parent_directives = []
-
@extends = extends
-
end
-
-
# If an exception is raised, this adds proper metadata to the backtrace.
-
1
def visit(node)
-
super(node)
-
rescue Sass::SyntaxError => e
-
e.modify_backtrace(:filename => node.filename, :line => node.line)
-
raise e
-
end
-
-
# Keeps track of the current parent directives.
-
1
def visit_children(parent)
-
@parent_directives.push parent if parent.is_a?(Sass::Tree::DirectiveNode)
-
super
-
ensure
-
@parent_directives.pop if parent.is_a?(Sass::Tree::DirectiveNode)
-
end
-
-
# Applies the extend to a single rule's selector.
-
1
def visit_rule(node)
-
node.resolved_rules = node.resolved_rules.do_extend(@extends, @parent_directives)
-
end
-
-
1
private
-
-
1
def self.check_extends_fired!(extends)
-
extends.each_value do |ex|
-
next if ex.result == :succeeded || ex.node.optional?
-
warn = "\"#{ex.extender}\" failed to @extend \"#{ex.target.join}\"."
-
reason =
-
if ex.result == :not_found
-
"The selector \"#{ex.target.join}\" was not found."
-
else
-
"No selectors matching \"#{ex.target.join}\" could be unified with \"#{ex.extender}\"."
-
end
-
-
Sass::Util.sass_warn <<WARN
-
WARNING on line #{ex.node.line}#{" of #{ex.node.filename}" if ex.node.filename}: #{warn}
-
#{reason}
-
This will be an error in future releases of Sass.
-
Use "@extend #{ex.target.join} !optional" if the extend should be able to fail.
-
WARN
-
end
-
end
-
end
-
# A visitor for converting a dynamic Sass tree into a static Sass tree.
-
1
class Sass::Tree::Visitors::Perform < Sass::Tree::Visitors::Base
-
# @param root [Tree::Node] The root node of the tree to visit.
-
# @param environment [Sass::Environment] The lexical environment.
-
# @return [Tree::Node] The resulting tree of static nodes.
-
1
def self.visit(root, environment = Sass::Environment.new)
-
new(environment).send(:visit, root)
-
end
-
-
# @api private
-
1
def self.perform_arguments(callable, args, keywords, splat)
-
desc = "#{callable.type.capitalize} #{callable.name}"
-
downcase_desc = "#{callable.type} #{callable.name}"
-
-
begin
-
unless keywords.empty?
-
unknown_args = Sass::Util.array_minus(keywords.keys,
-
callable.args.map {|var| var.first.underscored_name})
-
if callable.splat && unknown_args.include?(callable.splat.underscored_name)
-
raise Sass::SyntaxError.new("Argument $#{callable.splat.name} of #{downcase_desc} cannot be used as a named argument.")
-
elsif unknown_args.any?
-
description = unknown_args.length > 1 ? 'the following arguments:' : 'an argument named'
-
raise Sass::SyntaxError.new("#{desc} doesn't have #{description} #{unknown_args.map {|name| "$#{name}"}.join ', '}.")
-
end
-
end
-
rescue Sass::SyntaxError => keyword_exception
-
end
-
-
# If there's no splat, raise the keyword exception immediately. The actual
-
# raising happens in the ensure clause at the end of this function.
-
return if keyword_exception && !callable.splat
-
-
if args.size > callable.args.size && !callable.splat
-
takes = callable.args.size
-
passed = args.size
-
raise Sass::SyntaxError.new(
-
"#{desc} takes #{takes} argument#{'s' unless takes == 1} " +
-
"but #{passed} #{passed == 1 ? 'was' : 'were'} passed.")
-
end
-
-
splat_sep = :comma
-
if splat
-
args += splat.to_a
-
splat_sep = splat.separator if splat.is_a?(Sass::Script::List)
-
# If the splat argument exists, there won't be any keywords passed in
-
# manually, so we can safely overwrite rather than merge here.
-
keywords = splat.keywords if splat.is_a?(Sass::Script::ArgList)
-
end
-
-
keywords = keywords.dup
-
env = Sass::Environment.new(callable.environment)
-
callable.args.zip(args[0...callable.args.length]) do |(var, default), value|
-
if value && keywords.include?(var.underscored_name)
-
raise Sass::SyntaxError.new("#{desc} was passed argument $#{var.name} both by position and by name.")
-
end
-
-
value ||= keywords.delete(var.underscored_name)
-
value ||= default && default.perform(env)
-
raise Sass::SyntaxError.new("#{desc} is missing argument #{var.inspect}.") unless value
-
env.set_local_var(var.name, value)
-
end
-
-
if callable.splat
-
rest = args[callable.args.length..-1]
-
arg_list = Sass::Script::ArgList.new(rest, keywords.dup, splat_sep)
-
arg_list.options = env.options
-
env.set_local_var(callable.splat.name, arg_list)
-
end
-
-
yield env
-
rescue Exception => e
-
ensure
-
# If there's a keyword exception, we don't want to throw it immediately,
-
# because the invalid keywords may be part of a glob argument that should be
-
# passed on to another function. So we only raise it if we reach the end of
-
# this function *and* the keywords attached to the argument list glob object
-
# haven't been accessed.
-
#
-
# The keyword exception takes precedence over any Sass errors, but not over
-
# non-Sass exceptions.
-
if keyword_exception &&
-
!(arg_list && arg_list.keywords_accessed) &&
-
(e.nil? || e.is_a?(Sass::SyntaxError))
-
raise keyword_exception
-
elsif e
-
raise e
-
end
-
end
-
-
1
protected
-
-
1
def initialize(env)
-
@environment = env
-
# Stack trace information, including mixin includes and imports.
-
@stack = []
-
end
-
-
# If an exception is raised, this adds proper metadata to the backtrace.
-
1
def visit(node)
-
super(node.dup)
-
rescue Sass::SyntaxError => e
-
e.modify_backtrace(:filename => node.filename, :line => node.line)
-
raise e
-
end
-
-
# Keeps track of the current environment.
-
1
def visit_children(parent)
-
with_environment Sass::Environment.new(@environment, parent.options) do
-
parent.children = super.flatten
-
parent
-
end
-
end
-
-
# Runs a block of code with the current environment replaced with the given one.
-
#
-
# @param env [Sass::Environment] The new environment for the duration of the block.
-
# @yield A block in which the environment is set to `env`.
-
# @return [Object] The return value of the block.
-
1
def with_environment(env)
-
old_env, @environment = @environment, env
-
yield
-
ensure
-
@environment = old_env
-
end
-
-
# Sets the options on the environment if this is the top-level root.
-
1
def visit_root(node)
-
yield
-
rescue Sass::SyntaxError => e
-
e.sass_template ||= node.template
-
raise e
-
end
-
-
# Removes this node from the tree if it's a silent comment.
-
1
def visit_comment(node)
-
return [] if node.invisible?
-
node.resolved_value = run_interp_no_strip(node.value)
-
node.resolved_value.gsub!(/\\([\\#])/, '\1')
-
node
-
end
-
-
# Prints the expression to STDERR.
-
1
def visit_debug(node)
-
res = node.expr.perform(@environment)
-
res = res.value if res.is_a?(Sass::Script::String)
-
if node.filename
-
Sass::Util.sass_warn "#{node.filename}:#{node.line} DEBUG: #{res}"
-
else
-
Sass::Util.sass_warn "Line #{node.line} DEBUG: #{res}"
-
end
-
[]
-
end
-
-
# Runs the child nodes once for each value in the list.
-
1
def visit_each(node)
-
list = node.list.perform(@environment)
-
-
with_environment Sass::Environment.new(@environment) do
-
list.to_a.map do |v|
-
@environment.set_local_var(node.var, v)
-
node.children.map {|c| visit(c)}
-
end.flatten
-
end
-
end
-
-
# Runs SassScript interpolation in the selector,
-
# and then parses the result into a {Sass::Selector::CommaSequence}.
-
1
def visit_extend(node)
-
parser = Sass::SCSS::StaticParser.new(run_interp(node.selector), node.filename, node.line)
-
node.resolved_selector = parser.parse_selector
-
node
-
end
-
-
# Runs the child nodes once for each time through the loop, varying the variable each time.
-
1
def visit_for(node)
-
from = node.from.perform(@environment)
-
to = node.to.perform(@environment)
-
from.assert_int!
-
to.assert_int!
-
-
to = to.coerce(from.numerator_units, from.denominator_units)
-
range = Range.new(from.to_i, to.to_i, node.exclusive)
-
-
with_environment Sass::Environment.new(@environment) do
-
range.map do |i|
-
@environment.set_local_var(node.var,
-
Sass::Script::Number.new(i, from.numerator_units, from.denominator_units))
-
node.children.map {|c| visit(c)}
-
end.flatten
-
end
-
end
-
-
# Loads the function into the environment.
-
1
def visit_function(node)
-
env = Sass::Environment.new(@environment, node.options)
-
@environment.set_local_function(node.name,
-
Sass::Callable.new(node.name, node.args, node.splat, env, node.children, !:has_content, "function"))
-
[]
-
end
-
-
# Runs the child nodes if the conditional expression is true;
-
# otherwise, tries the else nodes.
-
1
def visit_if(node)
-
if node.expr.nil? || node.expr.perform(@environment).to_bool
-
yield
-
node.children
-
elsif node.else
-
visit(node.else)
-
else
-
[]
-
end
-
end
-
-
# Returns a static DirectiveNode if this is importing a CSS file,
-
# or parses and includes the imported Sass file.
-
1
def visit_import(node)
-
if path = node.css_import?
-
return Sass::Tree::CssImportNode.resolved("url(#{path})")
-
end
-
file = node.imported_file
-
handle_import_loop!(node) if @stack.any? {|e| e[:filename] == file.options[:filename]}
-
-
begin
-
@stack.push(:filename => node.filename, :line => node.line)
-
root = file.to_tree
-
Sass::Tree::Visitors::CheckNesting.visit(root)
-
node.children = root.children.map {|c| visit(c)}.flatten
-
node
-
rescue Sass::SyntaxError => e
-
e.modify_backtrace(:filename => node.imported_file.options[:filename])
-
e.add_backtrace(:filename => node.filename, :line => node.line)
-
raise e
-
end
-
ensure
-
@stack.pop unless path
-
end
-
-
# Loads a mixin into the environment.
-
1
def visit_mixindef(node)
-
env = Sass::Environment.new(@environment, node.options)
-
@environment.set_local_mixin(node.name,
-
Sass::Callable.new(node.name, node.args, node.splat, env, node.children, node.has_content, "mixin"))
-
[]
-
end
-
-
# Runs a mixin.
-
1
def visit_mixin(node)
-
include_loop = true
-
handle_include_loop!(node) if @stack.any? {|e| e[:name] == node.name}
-
include_loop = false
-
-
@stack.push(:filename => node.filename, :line => node.line, :name => node.name)
-
raise Sass::SyntaxError.new("Undefined mixin '#{node.name}'.") unless mixin = @environment.mixin(node.name)
-
-
if node.children.any? && !mixin.has_content
-
raise Sass::SyntaxError.new(%Q{Mixin "#{node.name}" does not accept a content block.})
-
end
-
-
args = node.args.map {|a| a.perform(@environment)}
-
keywords = Sass::Util.map_hash(node.keywords) {|k, v| [k, v.perform(@environment)]}
-
splat = node.splat.perform(@environment) if node.splat
-
-
self.class.perform_arguments(mixin, args, keywords, splat) do |env|
-
env.caller = Sass::Environment.new(@environment)
-
env.content = node.children if node.has_children
-
-
trace_node = Sass::Tree::TraceNode.from_node(node.name, node)
-
with_environment(env) {trace_node.children = mixin.tree.map {|c| visit(c)}.flatten}
-
trace_node
-
end
-
rescue Sass::SyntaxError => e
-
unless include_loop
-
e.modify_backtrace(:mixin => node.name, :line => node.line)
-
e.add_backtrace(:line => node.line)
-
end
-
raise e
-
ensure
-
@stack.pop unless include_loop
-
end
-
-
1
def visit_content(node)
-
return [] unless content = @environment.content
-
@stack.push(:filename => node.filename, :line => node.line, :name => '@content')
-
trace_node = Sass::Tree::TraceNode.from_node('@content', node)
-
with_environment(@environment.caller) {trace_node.children = content.map {|c| visit(c.dup)}.flatten}
-
trace_node
-
rescue Sass::SyntaxError => e
-
e.modify_backtrace(:mixin => '@content', :line => node.line)
-
e.add_backtrace(:line => node.line)
-
raise e
-
ensure
-
@stack.pop if content
-
end
-
-
# Runs any SassScript that may be embedded in a property.
-
1
def visit_prop(node)
-
node.resolved_name = run_interp(node.name)
-
val = node.value.perform(@environment)
-
node.resolved_value = val.to_s
-
yield
-
end
-
-
# Returns the value of the expression.
-
1
def visit_return(node)
-
throw :_sass_return, node.expr.perform(@environment)
-
end
-
-
# Runs SassScript interpolation in the selector,
-
# and then parses the result into a {Sass::Selector::CommaSequence}.
-
1
def visit_rule(node)
-
rule = node.rule
-
rule = rule.map {|e| e.is_a?(String) && e != ' ' ? e.strip : e} if node.style == :compressed
-
parser = Sass::SCSS::StaticParser.new(run_interp(node.rule), node.filename, node.line)
-
node.parsed_rules ||= parser.parse_selector
-
if node.options[:trace_selectors]
-
@stack.push(:filename => node.filename, :line => node.line)
-
node.stack_trace = stack_trace
-
@stack.pop
-
end
-
yield
-
end
-
-
# Loads the new variable value into the environment.
-
1
def visit_variable(node)
-
var = @environment.var(node.name)
-
return [] if node.guarded && var && !var.null?
-
val = node.expr.perform(@environment)
-
@environment.set_var(node.name, val)
-
[]
-
end
-
-
# Prints the expression to STDERR with a stylesheet trace.
-
1
def visit_warn(node)
-
@stack.push(:filename => node.filename, :line => node.line)
-
res = node.expr.perform(@environment)
-
res = res.value if res.is_a?(Sass::Script::String)
-
msg = "WARNING: #{res}\n "
-
msg << stack_trace.join("\n ") << "\n"
-
Sass::Util.sass_warn msg
-
[]
-
ensure
-
@stack.pop
-
end
-
-
# Runs the child nodes until the continuation expression becomes false.
-
1
def visit_while(node)
-
children = []
-
with_environment Sass::Environment.new(@environment) do
-
children += node.children.map {|c| visit(c)} while node.expr.perform(@environment).to_bool
-
end
-
children.flatten
-
end
-
-
1
def visit_directive(node)
-
node.resolved_value = run_interp(node.value)
-
yield
-
end
-
-
1
def visit_media(node)
-
parser = Sass::SCSS::StaticParser.new(run_interp(node.query), node.filename, node.line)
-
node.resolved_query ||= parser.parse_media_query_list
-
yield
-
end
-
-
1
def visit_supports(node)
-
node.condition = node.condition.deep_copy
-
node.condition.perform(@environment)
-
yield
-
end
-
-
1
def visit_cssimport(node)
-
node.resolved_uri = run_interp([node.uri])
-
if node.query
-
parser = Sass::SCSS::StaticParser.new(run_interp(node.query), node.filename, node.line)
-
node.resolved_query ||= parser.parse_media_query_list
-
end
-
yield
-
end
-
-
1
private
-
-
1
def stack_trace
-
trace = []
-
stack = @stack.map {|e| e.dup}.reverse
-
stack.each_cons(2) {|(e1, e2)| e1[:caller] = e2[:name]; [e1, e2]}
-
stack.each_with_index do |entry, i|
-
msg = "#{i == 0 ? "on" : "from"} line #{entry[:line]}"
-
msg << " of #{entry[:filename] || "an unknown file"}"
-
msg << ", in `#{entry[:caller]}'" if entry[:caller]
-
trace << msg
-
end
-
trace
-
end
-
-
1
def run_interp_no_strip(text)
-
text.map do |r|
-
next r if r.is_a?(String)
-
val = r.perform(@environment)
-
# Interpolated strings should never render with quotes
-
next val.value if val.is_a?(Sass::Script::String)
-
val.to_s
-
end.join
-
end
-
-
1
def run_interp(text)
-
run_interp_no_strip(text).strip
-
end
-
-
1
def handle_include_loop!(node)
-
msg = "An @include loop has been found:"
-
content_count = 0
-
mixins = @stack.reverse.map {|s| s[:name]}.compact.select do |s|
-
if s == '@content'
-
content_count += 1
-
false
-
elsif content_count > 0
-
content_count -= 1
-
false
-
else
-
true
-
end
-
end
-
-
return unless mixins.include?(node.name)
-
raise Sass::SyntaxError.new("#{msg} #{node.name} includes itself") if mixins.size == 1
-
-
msg << "\n" << Sass::Util.enum_cons(mixins.reverse + [node.name], 2).map do |m1, m2|
-
" #{m1} includes #{m2}"
-
end.join("\n")
-
raise Sass::SyntaxError.new(msg)
-
end
-
-
1
def handle_import_loop!(node)
-
msg = "An @import loop has been found:"
-
files = @stack.map {|s| s[:filename]}.compact
-
if node.filename == node.imported_file.options[:filename]
-
raise Sass::SyntaxError.new("#{msg} #{node.filename} imports itself")
-
end
-
-
files << node.filename << node.imported_file.options[:filename]
-
msg << "\n" << Sass::Util.enum_cons(files, 2).map do |m1, m2|
-
" #{m1} imports #{m2}"
-
end.join("\n")
-
raise Sass::SyntaxError.new(msg)
-
end
-
end
-
# A visitor for converting a Sass tree into CSS.
-
1
class Sass::Tree::Visitors::ToCss < Sass::Tree::Visitors::Base
-
1
protected
-
-
1
def initialize
-
@tabs = 0
-
end
-
-
1
def visit(node)
-
super
-
rescue Sass::SyntaxError => e
-
e.modify_backtrace(:filename => node.filename, :line => node.line)
-
raise e
-
end
-
-
1
def with_tabs(tabs)
-
old_tabs, @tabs = @tabs, tabs
-
yield
-
ensure
-
@tabs = old_tabs
-
end
-
-
1
def visit_root(node)
-
result = String.new
-
node.children.each do |child|
-
next if child.invisible?
-
child_str = visit(child)
-
result << child_str + (node.style == :compressed ? '' : "\n")
-
end
-
result.rstrip!
-
return "" if result.empty?
-
result << "\n"
-
unless Sass::Util.ruby1_8? || result.ascii_only?
-
if node.children.first.is_a?(Sass::Tree::CharsetNode)
-
begin
-
encoding = node.children.first.name
-
# Default to big-endian encoding, because we have to decide somehow
-
encoding << 'BE' if encoding =~ /\Autf-(16|32)\Z/i
-
result = result.encode(Encoding.find(encoding))
-
rescue EncodingError
-
end
-
end
-
-
result = "@charset \"#{result.encoding.name}\";#{
-
node.style == :compressed ? '' : "\n"
-
}".encode(result.encoding) + result
-
end
-
result
-
rescue Sass::SyntaxError => e
-
e.sass_template ||= node.template
-
raise e
-
end
-
-
1
def visit_charset(node)
-
"@charset \"#{node.name}\";"
-
end
-
-
1
def visit_comment(node)
-
return if node.invisible?
-
spaces = (' ' * [@tabs - node.resolved_value[/^ */].size, 0].max)
-
-
content = node.resolved_value.gsub(/^/, spaces)
-
content.gsub!(%r{^(\s*)//(.*)$}) {|md| "#{$1}/*#{$2} */"} if node.type == :silent
-
content.gsub!(/\n +(\* *(?!\/))?/, ' ') if (node.style == :compact || node.style == :compressed) && node.type != :loud
-
content
-
end
-
-
1
def visit_directive(node)
-
was_in_directive = @in_directive
-
tab_str = ' ' * @tabs
-
return tab_str + node.resolved_value + ";" unless node.has_children
-
return tab_str + node.resolved_value + " {}" if node.children.empty?
-
@in_directive = @in_directive || !node.is_a?(Sass::Tree::MediaNode)
-
result = if node.style == :compressed
-
"#{node.resolved_value}{"
-
else
-
"#{tab_str}#{node.resolved_value} {" + (node.style == :compact ? ' ' : "\n")
-
end
-
was_prop = false
-
first = true
-
node.children.each do |child|
-
next if child.invisible?
-
if node.style == :compact
-
if child.is_a?(Sass::Tree::PropNode)
-
with_tabs(first || was_prop ? 0 : @tabs + 1) {result << visit(child) << ' '}
-
else
-
result[-1] = "\n" if was_prop
-
rendered = with_tabs(@tabs + 1) {visit(child).dup}
-
rendered = rendered.lstrip if first
-
result << rendered.rstrip + "\n"
-
end
-
was_prop = child.is_a?(Sass::Tree::PropNode)
-
first = false
-
elsif node.style == :compressed
-
result << (was_prop ? ";" : "") << with_tabs(0) {visit(child)}
-
was_prop = child.is_a?(Sass::Tree::PropNode)
-
else
-
result << with_tabs(@tabs + 1) {visit(child)} + "\n"
-
end
-
end
-
result.rstrip + if node.style == :compressed
-
"}"
-
else
-
(node.style == :expanded ? "\n" : " ") + "}\n"
-
end
-
ensure
-
@in_directive = was_in_directive
-
end
-
-
1
def visit_media(node)
-
str = with_tabs(@tabs + node.tabs) {visit_directive(node)}
-
str.gsub!(/\n\Z/, '') unless node.style == :compressed || node.group_end
-
str
-
end
-
-
1
def visit_supports(node)
-
visit_media(node)
-
end
-
-
1
def visit_cssimport(node)
-
visit_directive(node)
-
end
-
-
1
def visit_prop(node)
-
return if node.resolved_value.empty?
-
tab_str = ' ' * (@tabs + node.tabs)
-
if node.style == :compressed
-
"#{tab_str}#{node.resolved_name}:#{node.resolved_value}"
-
else
-
"#{tab_str}#{node.resolved_name}: #{node.resolved_value};"
-
end
-
end
-
-
1
def visit_rule(node)
-
with_tabs(@tabs + node.tabs) do
-
rule_separator = node.style == :compressed ? ',' : ', '
-
line_separator =
-
case node.style
-
when :nested, :expanded; "\n"
-
when :compressed; ""
-
else; " "
-
end
-
rule_indent = ' ' * @tabs
-
per_rule_indent, total_indent = [:nested, :expanded].include?(node.style) ? [rule_indent, ''] : ['', rule_indent]
-
-
joined_rules = node.resolved_rules.members.map do |seq|
-
next if seq.has_placeholder?
-
rule_part = seq.to_a.join
-
if node.style == :compressed
-
rule_part.gsub!(/([^,])\s*\n\s*/m, '\1 ')
-
rule_part.gsub!(/\s*([,+>])\s*/m, '\1')
-
rule_part.strip!
-
end
-
rule_part
-
end.compact.join(rule_separator)
-
-
joined_rules.sub!(/\A\s*/, per_rule_indent)
-
joined_rules.gsub!(/\s*\n\s*/, "#{line_separator}#{per_rule_indent}")
-
total_rule = total_indent << joined_rules
-
-
to_return = ''
-
old_spaces = ' ' * @tabs
-
if node.style != :compressed
-
if node.options[:debug_info] && !@in_directive
-
to_return << visit(debug_info_rule(node.debug_info, node.options)) << "\n"
-
elsif node.options[:trace_selectors]
-
to_return << "#{old_spaces}/* "
-
to_return << node.stack_trace.join("\n #{old_spaces}")
-
to_return << " */\n"
-
elsif node.options[:line_comments]
-
to_return << "#{old_spaces}/* line #{node.line}"
-
-
if node.filename
-
relative_filename = if node.options[:css_filename]
-
begin
-
Pathname.new(node.filename).relative_path_from(
-
Pathname.new(File.dirname(node.options[:css_filename]))).to_s
-
rescue ArgumentError
-
nil
-
end
-
end
-
relative_filename ||= node.filename
-
to_return << ", #{relative_filename}"
-
end
-
-
to_return << " */\n"
-
end
-
end
-
-
if node.style == :compact
-
properties = with_tabs(0) {node.children.map {|a| visit(a)}.join(' ')}
-
to_return << "#{total_rule} { #{properties} }#{"\n" if node.group_end}"
-
elsif node.style == :compressed
-
properties = with_tabs(0) {node.children.map {|a| visit(a)}.join(';')}
-
to_return << "#{total_rule}{#{properties}}"
-
else
-
properties = with_tabs(@tabs + 1) {node.children.map {|a| visit(a)}.join("\n")}
-
end_props = (node.style == :expanded ? "\n" + old_spaces : ' ')
-
to_return << "#{total_rule} {\n#{properties}#{end_props}}#{"\n" if node.group_end}"
-
end
-
-
to_return
-
end
-
end
-
-
1
private
-
-
1
def debug_info_rule(debug_info, options)
-
node = Sass::Tree::DirectiveNode.resolved("@media -sass-debug-info")
-
Sass::Util.hash_to_a(debug_info.map {|k, v| [k.to_s, v.to_s]}).each do |k, v|
-
rule = Sass::Tree::RuleNode.new([""])
-
rule.resolved_rules = Sass::Selector::CommaSequence.new(
-
[Sass::Selector::Sequence.new(
-
[Sass::Selector::SimpleSequence.new(
-
[Sass::Selector::Element.new(k.to_s.gsub(/[^\w-]/, "\\\\\\0"), nil)],
-
false)
-
])
-
])
-
prop = Sass::Tree::PropNode.new([""], Sass::Script::String.new(''), :new)
-
prop.resolved_name = "font-family"
-
prop.resolved_value = Sass::SCSS::RX.escape_ident(v.to_s)
-
rule << prop
-
node << rule
-
end
-
node.options = options.merge(:debug_info => false, :line_comments => false, :style => :compressed)
-
node
-
end
-
end
-
1
module Sass
-
1
module Tree
-
# A dynamic node representing a Sass `@warn` statement.
-
#
-
# @see Sass::Tree
-
1
class WarnNode < Node
-
# The expression to print.
-
# @return [Script::Node]
-
1
attr_accessor :expr
-
-
# @param expr [Script::Node] The expression to print
-
1
def initialize(expr)
-
@expr = expr
-
super()
-
end
-
end
-
end
-
end
-
1
require 'sass/tree/node'
-
-
1
module Sass::Tree
-
# A dynamic node representing a Sass `@while` loop.
-
#
-
# @see Sass::Tree
-
1
class WhileNode < Node
-
# The parse tree for the continuation expression.
-
# @return [Script::Node]
-
1
attr_accessor :expr
-
-
# @param expr [Script::Node] See \{#expr}
-
1
def initialize(expr)
-
@expr = expr
-
super()
-
end
-
end
-
end
-
1
require 'erb'
-
1
require 'set'
-
1
require 'enumerator'
-
1
require 'stringio'
-
1
require 'rbconfig'
-
1
require 'thread'
-
-
1
require 'sass/root'
-
1
require 'sass/util/subset_map'
-
-
1
module Sass
-
# A module containing various useful functions.
-
1
module Util
-
1
extend self
-
-
# An array of ints representing the Ruby version number.
-
# @api public
-
4
RUBY_VERSION = ::RUBY_VERSION.split(".").map {|s| s.to_i}
-
-
# The Ruby engine we're running under. Defaults to `"ruby"`
-
# if the top-level constant is undefined.
-
# @api public
-
1
RUBY_ENGINE = defined?(::RUBY_ENGINE) ? ::RUBY_ENGINE : "ruby"
-
-
# Returns the path of a file relative to the Sass root directory.
-
#
-
# @param file [String] The filename relative to the Sass root
-
# @return [String] The filename relative to the the working directory
-
1
def scope(file)
-
7
File.join(Sass::ROOT_DIR, file)
-
end
-
-
# Converts an array of `[key, value]` pairs to a hash.
-
#
-
# @example
-
# to_hash([[:foo, "bar"], [:baz, "bang"]])
-
# #=> {:foo => "bar", :baz => "bang"}
-
# @param arr [Array<(Object, Object)>] An array of pairs
-
# @return [Hash] A hash
-
1
def to_hash(arr)
-
5
Hash[arr.compact]
-
end
-
-
# Maps the keys in a hash according to a block.
-
#
-
# @example
-
# map_keys({:foo => "bar", :baz => "bang"}) {|k| k.to_s}
-
# #=> {"foo" => "bar", "baz" => "bang"}
-
# @param hash [Hash] The hash to map
-
# @yield [key] A block in which the keys are transformed
-
# @yieldparam key [Object] The key that should be mapped
-
# @yieldreturn [Object] The new value for the key
-
# @return [Hash] The mapped hash
-
# @see #map_vals
-
# @see #map_hash
-
1
def map_keys(hash)
-
to_hash(hash.map {|k, v| [yield(k), v]})
-
end
-
-
# Maps the values in a hash according to a block.
-
#
-
# @example
-
# map_values({:foo => "bar", :baz => "bang"}) {|v| v.to_sym}
-
# #=> {:foo => :bar, :baz => :bang}
-
# @param hash [Hash] The hash to map
-
# @yield [value] A block in which the values are transformed
-
# @yieldparam value [Object] The value that should be mapped
-
# @yieldreturn [Object] The new value for the value
-
# @return [Hash] The mapped hash
-
# @see #map_keys
-
# @see #map_hash
-
1
def map_vals(hash)
-
147
to_hash(hash.map {|k, v| [k, yield(v)]})
-
end
-
-
# Maps the key-value pairs of a hash according to a block.
-
#
-
# @example
-
# map_hash({:foo => "bar", :baz => "bang"}) {|k, v| [k.to_s, v.to_sym]}
-
# #=> {"foo" => :bar, "baz" => :bang}
-
# @param hash [Hash] The hash to map
-
# @yield [key, value] A block in which the key-value pairs are transformed
-
# @yieldparam [key] The hash key
-
# @yieldparam [value] The hash value
-
# @yieldreturn [(Object, Object)] The new value for the `[key, value]` pair
-
# @return [Hash] The mapped hash
-
# @see #map_keys
-
# @see #map_vals
-
1
def map_hash(hash)
-
# Using &block here completely hoses performance on 1.8.
-
197
to_hash(hash.map {|k, v| yield k, v})
-
end
-
-
# Computes the powerset of the given array.
-
# This is the set of all subsets of the array.
-
#
-
# @example
-
# powerset([1, 2, 3]) #=>
-
# Set[Set[], Set[1], Set[2], Set[3], Set[1, 2], Set[2, 3], Set[1, 3], Set[1, 2, 3]]
-
# @param arr [Enumerable]
-
# @return [Set<Set>] The subsets of `arr`
-
1
def powerset(arr)
-
arr.inject([Set.new].to_set) do |powerset, el|
-
new_powerset = Set.new
-
powerset.each do |subset|
-
new_powerset << subset
-
new_powerset << subset + [el]
-
end
-
new_powerset
-
end
-
end
-
-
# Restricts a number to falling within a given range.
-
# Returns the number if it falls within the range,
-
# or the closest value in the range if it doesn't.
-
#
-
# @param value [Numeric]
-
# @param range [Range<Numeric>]
-
# @return [Numeric]
-
1
def restrict(value, range)
-
[[value, range.first].max, range.last].min
-
end
-
-
# Concatenates all strings that are adjacent in an array,
-
# while leaving other elements as they are.
-
#
-
# @example
-
# merge_adjacent_strings([1, "foo", "bar", 2, "baz"])
-
# #=> [1, "foobar", 2, "baz"]
-
# @param arr [Array]
-
# @return [Array] The enumerable with strings merged
-
1
def merge_adjacent_strings(arr)
-
# Optimize for the common case of one element
-
return arr if arr.size < 2
-
arr.inject([]) do |a, e|
-
if e.is_a?(String)
-
if a.last.is_a?(String)
-
a.last << e
-
else
-
a << e.dup
-
end
-
else
-
a << e
-
end
-
a
-
end
-
end
-
-
# Intersperses a value in an enumerable, as would be done with `Array#join`
-
# but without concatenating the array together afterwards.
-
#
-
# @param enum [Enumerable]
-
# @param val
-
# @return [Array]
-
1
def intersperse(enum, val)
-
enum.inject([]) {|a, e| a << e << val}[0...-1]
-
end
-
-
# Substitutes a sub-array of one array with another sub-array.
-
#
-
# @param ary [Array] The array in which to make the substitution
-
# @param from [Array] The sequence of elements to replace with `to`
-
# @param to [Array] The sequence of elements to replace `from` with
-
1
def substitute(ary, from, to)
-
res = ary.dup
-
i = 0
-
while i < res.size
-
if res[i...i+from.size] == from
-
res[i...i+from.size] = to
-
end
-
i += 1
-
end
-
res
-
end
-
-
# Destructively strips whitespace from the beginning and end
-
# of the first and last elements, respectively,
-
# in the array (if those elements are strings).
-
#
-
# @param arr [Array]
-
# @return [Array] `arr`
-
1
def strip_string_array(arr)
-
arr.first.lstrip! if arr.first.is_a?(String)
-
arr.last.rstrip! if arr.last.is_a?(String)
-
arr
-
end
-
-
# Return an array of all possible paths through the given arrays.
-
#
-
# @param arrs [Array<Array>]
-
# @return [Array<Arrays>]
-
#
-
# @example
-
# paths([[1, 2], [3, 4], [5]]) #=>
-
# # [[1, 3, 5],
-
# # [2, 3, 5],
-
# # [1, 4, 5],
-
# # [2, 4, 5]]
-
1
def paths(arrs)
-
arrs.inject([[]]) do |paths, arr|
-
flatten(arr.map {|e| paths.map {|path| path + [e]}}, 1)
-
end
-
end
-
-
# Computes a single longest common subsequence for `x` and `y`.
-
# If there are more than one longest common subsequences,
-
# the one returned is that which starts first in `x`.
-
#
-
# @param x [Array]
-
# @param y [Array]
-
# @yield [a, b] An optional block to use in place of a check for equality
-
# between elements of `x` and `y`.
-
# @yieldreturn [Object, nil] If the two values register as equal,
-
# this will return the value to use in the LCS array.
-
# @return [Array] The LCS
-
1
def lcs(x, y, &block)
-
x = [nil, *x]
-
y = [nil, *y]
-
block ||= proc {|a, b| a == b && a}
-
lcs_backtrace(lcs_table(x, y, &block), x, y, x.size-1, y.size-1, &block)
-
end
-
-
# Converts a Hash to an Array. This is usually identical to `Hash#to_a`,
-
# with the following exceptions:
-
#
-
# * In Ruby 1.8, `Hash#to_a` is not deterministically ordered, but this is.
-
# * In Ruby 1.9 when running tests, this is ordered in the same way it would
-
# be under Ruby 1.8 (sorted key order rather than insertion order).
-
#
-
# @param hash [Hash]
-
# @return [Array]
-
1
def hash_to_a(hash)
-
1
return hash.to_a unless ruby1_8? || defined?(Test::Unit)
-
return hash.sort_by {|k, v| k}
-
end
-
-
# Performs the equivalent of `enum.group_by.to_a`, but with a guaranteed
-
# order. Unlike [#hash_to_a], the resulting order isn't sorted key order;
-
# instead, it's the same order as `#group_by` has under Ruby 1.9 (key
-
# appearance order).
-
#
-
# @param enum [Enumerable]
-
# @return [Array<[Object, Array]>] An array of pairs.
-
1
def group_by_to_a(enum, &block)
-
return enum.group_by(&block).to_a unless ruby1_8?
-
order = {}
-
arr = []
-
enum.group_by do |e|
-
res = block[e]
-
unless order.include?(res)
-
order[res] = order.size
-
end
-
res
-
end.each do |key, vals|
-
arr[order[key]] = [key, vals]
-
end
-
arr
-
end
-
-
# Returns a sub-array of `minuend` containing only elements that are also in
-
# `subtrahend`. Ensures that the return value has the same order as
-
# `minuend`, even on Rubinius where that's not guaranteed by {Array#-}.
-
#
-
# @param minuend [Array]
-
# @param subtrahend [Array]
-
# @return [Array]
-
1
def array_minus(minuend, subtrahend)
-
return minuend - subtrahend unless rbx?
-
set = Set.new(minuend) - subtrahend
-
minuend.select {|e| set.include?(e)}
-
end
-
-
# Returns a string description of the character that caused an
-
# `Encoding::UndefinedConversionError`.
-
#
-
# @param [Encoding::UndefinedConversionError]
-
# @return [String]
-
1
def undefined_conversion_error_char(e)
-
# Rubinius (as of 2.0.0.rc1) pre-quotes the error character.
-
return e.error_char if rbx?
-
# JRuby (as of 1.7.2) doesn't have an error_char field on
-
# Encoding::UndefinedConversionError.
-
return e.error_char.dump unless jruby?
-
e.message[/^"[^"]+"/] #"
-
end
-
-
# Asserts that `value` falls within `range` (inclusive), leaving
-
# room for slight floating-point errors.
-
#
-
# @param name [String] The name of the value. Used in the error message.
-
# @param range [Range] The allowed range of values.
-
# @param value [Numeric, Sass::Script::Number] The value to check.
-
# @param unit [String] The unit of the value. Used in error reporting.
-
# @return [Numeric] `value` adjusted to fall within range, if it
-
# was outside by a floating-point margin.
-
1
def check_range(name, range, value, unit='')
-
grace = (-0.00001..0.00001)
-
str = value.to_s
-
value = value.value if value.is_a?(Sass::Script::Number)
-
return value if range.include?(value)
-
return range.first if grace.include?(value - range.first)
-
return range.last if grace.include?(value - range.last)
-
raise ArgumentError.new(
-
"#{name} #{str} must be between #{range.first}#{unit} and #{range.last}#{unit}")
-
end
-
-
# Returns whether or not `seq1` is a subsequence of `seq2`. That is, whether
-
# or not `seq2` contains every element in `seq1` in the same order (and
-
# possibly more elements besides).
-
#
-
# @param seq1 [Array]
-
# @param seq2 [Array]
-
# @return [Boolean]
-
1
def subsequence?(seq1, seq2)
-
i = j = 0
-
loop do
-
return true if i == seq1.size
-
return false if j == seq2.size
-
i += 1 if seq1[i] == seq2[j]
-
j += 1
-
end
-
end
-
-
# Returns information about the caller of the previous method.
-
#
-
# @param entry [String] An entry in the `#caller` list, or a similarly formatted string
-
# @return [[String, Fixnum, (String, nil)]] An array containing the filename, line, and method name of the caller.
-
# The method name may be nil
-
1
def caller_info(entry = nil)
-
# JRuby evaluates `caller` incorrectly when it's in an actual default argument.
-
entry ||= caller[1]
-
info = entry.scan(/^(.*?):(-?.*?)(?::.*`(.+)')?$/).first
-
info[1] = info[1].to_i
-
# This is added by Rubinius to designate a block, but we don't care about it.
-
info[2].sub!(/ \{\}\Z/, '') if info[2]
-
info
-
end
-
-
# Returns whether one version string represents a more recent version than another.
-
#
-
# @param v1 [String] A version string.
-
# @param v2 [String] Another version string.
-
# @return [Boolean]
-
1
def version_gt(v1, v2)
-
# Construct an array to make sure the shorter version is padded with nil
-
1
Array.new([v1.length, v2.length].max).zip(v1.split("."), v2.split(".")) do |_, p1, p2|
-
1
p1 ||= "0"
-
1
p2 ||= "0"
-
1
release1 = p1 =~ /^[0-9]+$/
-
1
release2 = p2 =~ /^[0-9]+$/
-
1
if release1 && release2
-
# Integer comparison if both are full releases
-
1
p1, p2 = p1.to_i, p2.to_i
-
1
next if p1 == p2
-
1
return p1 > p2
-
elsif !release1 && !release2
-
# String comparison if both are prereleases
-
next if p1 == p2
-
return p1 > p2
-
else
-
# If only one is a release, that one is newer
-
return release1
-
end
-
end
-
end
-
-
# Returns whether one version string represents the same or a more
-
# recent version than another.
-
#
-
# @param v1 [String] A version string.
-
# @param v2 [String] Another version string.
-
# @return [Boolean]
-
1
def version_geq(v1, v2)
-
1
version_gt(v1, v2) || !version_gt(v2, v1)
-
end
-
-
# Throws a NotImplementedError for an abstract method.
-
#
-
# @param obj [Object] `self`
-
# @raise [NotImplementedError]
-
1
def abstract(obj)
-
raise NotImplementedError.new("#{obj.class} must implement ##{caller_info[2]}")
-
end
-
-
# Silence all output to STDERR within a block.
-
#
-
# @yield A block in which no output will be printed to STDERR
-
1
def silence_warnings
-
the_real_stderr, $stderr = $stderr, StringIO.new
-
yield
-
ensure
-
$stderr = the_real_stderr
-
end
-
-
1
@@silence_warnings = false
-
# Silences all Sass warnings within a block.
-
#
-
# @yield A block in which no Sass warnings will be printed
-
1
def silence_sass_warnings
-
old_level, Sass.logger.log_level = Sass.logger.log_level, :error
-
yield
-
ensure
-
Sass.logger.log_level = old_level
-
end
-
-
# The same as `Kernel#warn`, but is silenced by \{#silence\_sass\_warnings}.
-
#
-
# @param msg [String]
-
1
def sass_warn(msg)
-
msg = msg + "\n" unless ruby1?
-
Sass.logger.warn(msg)
-
end
-
-
## Cross Rails Version Compatibility
-
-
# Returns the root of the Rails application,
-
# if this is running in a Rails context.
-
# Returns `nil` if no such root is defined.
-
#
-
# @return [String, nil]
-
1
def rails_root
-
if defined?(::Rails.root)
-
return ::Rails.root.to_s if ::Rails.root
-
raise "ERROR: Rails.root is nil!"
-
end
-
return RAILS_ROOT.to_s if defined?(RAILS_ROOT)
-
return nil
-
end
-
-
# Returns the environment of the Rails application,
-
# if this is running in a Rails context.
-
# Returns `nil` if no such environment is defined.
-
#
-
# @return [String, nil]
-
1
def rails_env
-
return ::Rails.env.to_s if defined?(::Rails.env)
-
return RAILS_ENV.to_s if defined?(RAILS_ENV)
-
return nil
-
end
-
-
# Returns whether this environment is using ActionPack
-
# version 3.0.0 or greater.
-
#
-
# @return [Boolean]
-
1
def ap_geq_3?
-
ap_geq?("3.0.0.beta1")
-
end
-
-
# Returns whether this environment is using ActionPack
-
# of a version greater than or equal to that specified.
-
#
-
# @param version [String] The string version number to check against.
-
# Should be greater than or equal to Rails 3,
-
# because otherwise ActionPack::VERSION isn't autoloaded
-
# @return [Boolean]
-
1
def ap_geq?(version)
-
# The ActionPack module is always loaded automatically in Rails >= 3
-
1
return false unless defined?(ActionPack) && defined?(ActionPack::VERSION) &&
-
defined?(ActionPack::VERSION::STRING)
-
-
1
version_geq(ActionPack::VERSION::STRING, version)
-
end
-
-
# Returns an ActionView::Template* class.
-
# In pre-3.0 versions of Rails, most of these classes
-
# were of the form `ActionView::TemplateFoo`,
-
# while afterwards they were of the form `ActionView;:Template::Foo`.
-
#
-
# @param name [#to_s] The name of the class to get.
-
# For example, `:Error` will return `ActionView::TemplateError`
-
# or `ActionView::Template::Error`.
-
1
def av_template_class(name)
-
return ActionView.const_get("Template#{name}") if ActionView.const_defined?("Template#{name}")
-
return ActionView::Template.const_get(name.to_s)
-
end
-
-
## Cross-OS Compatibility
-
-
# Whether or not this is running on Windows.
-
#
-
# @return [Boolean]
-
1
def windows?
-
RbConfig::CONFIG['host_os'] =~ /mswin|windows|mingw/i
-
end
-
-
# Whether or not this is running on IronRuby.
-
#
-
# @return [Boolean]
-
1
def ironruby?
-
5
RUBY_ENGINE == "ironruby"
-
end
-
-
# Whether or not this is running on Rubinius.
-
#
-
# @return [Boolean]
-
1
def rbx?
-
1
RUBY_ENGINE == "rbx"
-
end
-
-
# Whether or not this is running on JRuby.
-
#
-
# @return [Boolean]
-
1
def jruby?
-
RUBY_PLATFORM =~ /java/
-
end
-
-
# Returns an array of ints representing the JRuby version number.
-
#
-
# @return [Array<Fixnum>]
-
1
def jruby_version
-
$jruby_version ||= ::JRUBY_VERSION.split(".").map {|s| s.to_i}
-
end
-
-
# Like `Dir.glob`, but works with backslash-separated paths on Windows.
-
#
-
# @param path [String]
-
1
def glob(path, &block)
-
path = path.gsub('\\', '/') if windows?
-
Dir.glob(path, &block)
-
end
-
-
# Prepare a value for a destructuring assignment (e.g. `a, b =
-
# val`). This works around a performance bug when using
-
# ActiveSupport, and only needs to be called when `val` is likely
-
# to be `nil` reasonably often.
-
#
-
# See [this bug report](http://redmine.ruby-lang.org/issues/4917).
-
#
-
# @param val [Object]
-
# @return [Object]
-
1
def destructure(val)
-
val || []
-
end
-
-
## Cross-Ruby-Version Compatibility
-
-
# Whether or not this is running under a Ruby version under 2.0.
-
#
-
# @return [Boolean]
-
1
def ruby1?
-
Sass::Util::RUBY_VERSION[0] <= 1
-
end
-
-
# Whether or not this is running under Ruby 1.8 or lower.
-
#
-
# Note that IronRuby counts as Ruby 1.8,
-
# because it doesn't support the Ruby 1.9 encoding API.
-
#
-
# @return [Boolean]
-
1
def ruby1_8?
-
# IronRuby says its version is 1.9, but doesn't support any of the encoding APIs.
-
# We have to fall back to 1.8 behavior.
-
5
ironruby? || (Sass::Util::RUBY_VERSION[0] == 1 && Sass::Util::RUBY_VERSION[1] < 9)
-
end
-
-
# Whether or not this is running under Ruby 1.8.6 or lower.
-
# Note that lower versions are not officially supported.
-
#
-
# @return [Boolean]
-
1
def ruby1_8_6?
-
ruby1_8? && Sass::Util::RUBY_VERSION[2] < 7
-
end
-
-
# Wehter or not this is running under JRuby 1.6 or lower.
-
1
def jruby1_6?
-
jruby? && jruby_version[0] == 1 && jruby_version[1] < 7
-
end
-
-
# Whether or not this is running under MacRuby.
-
#
-
# @return [Boolean]
-
1
def macruby?
-
1
RUBY_ENGINE == 'macruby'
-
end
-
-
# Checks that the encoding of a string is valid in Ruby 1.9
-
# and cleans up potential encoding gotchas like the UTF-8 BOM.
-
# If it's not, yields an error string describing the invalid character
-
# and the line on which it occurrs.
-
#
-
# @param str [String] The string of which to check the encoding
-
# @yield [msg] A block in which an encoding error can be raised.
-
# Only yields if there is an encoding error
-
# @yieldparam msg [String] The error message to be raised
-
# @return [String] `str`, potentially with encoding gotchas like BOMs removed
-
1
def check_encoding(str)
-
if ruby1_8?
-
return str.gsub(/\A\xEF\xBB\xBF/, '') # Get rid of the UTF-8 BOM
-
elsif str.valid_encoding?
-
# Get rid of the Unicode BOM if possible
-
if str.encoding.name =~ /^UTF-(8|16|32)(BE|LE)?$/
-
return str.gsub(Regexp.new("\\A\uFEFF".encode(str.encoding.name)), '')
-
else
-
return str
-
end
-
end
-
-
encoding = str.encoding
-
newlines = Regexp.new("\r\n|\r|\n".encode(encoding).force_encoding("binary"))
-
str.force_encoding("binary").split(newlines).each_with_index do |line, i|
-
begin
-
line.encode(encoding)
-
rescue Encoding::UndefinedConversionError => e
-
yield <<MSG.rstrip, i + 1
-
Invalid #{encoding.name} character #{undefined_conversion_error_char(e)}
-
MSG
-
end
-
end
-
return str
-
end
-
-
# Like {\#check\_encoding}, but also checks for a `@charset` declaration
-
# at the beginning of the file and uses that encoding if it exists.
-
#
-
# The Sass encoding rules are simple.
-
# If a `@charset` declaration exists,
-
# we assume that that's the original encoding of the document.
-
# Otherwise, we use whatever encoding Ruby has.
-
# Then we convert that to UTF-8 to process internally.
-
# The UTF-8 end result is what's returned by this method.
-
#
-
# @param str [String] The string of which to check the encoding
-
# @yield [msg] A block in which an encoding error can be raised.
-
# Only yields if there is an encoding error
-
# @yieldparam msg [String] The error message to be raised
-
# @return [(String, Encoding)] The original string encoded as UTF-8,
-
# and the source encoding of the string (or `nil` under Ruby 1.8)
-
# @raise [Encoding::UndefinedConversionError] if the source encoding
-
# cannot be converted to UTF-8
-
# @raise [ArgumentError] if the document uses an unknown encoding with `@charset`
-
1
def check_sass_encoding(str, &block)
-
return check_encoding(str, &block), nil if ruby1_8?
-
# We allow any printable ASCII characters but double quotes in the charset decl
-
bin = str.dup.force_encoding("BINARY")
-
encoding = Sass::Util::ENCODINGS_TO_CHECK.find do |enc|
-
re = Sass::Util::CHARSET_REGEXPS[enc]
-
re && bin =~ re
-
end
-
charset, bom = $1, $2
-
if charset
-
charset = charset.force_encoding(encoding).encode("UTF-8")
-
if endianness = encoding[/[BL]E$/]
-
begin
-
Encoding.find(charset + endianness)
-
charset << endianness
-
rescue ArgumentError # Encoding charset + endianness doesn't exist
-
end
-
end
-
str.force_encoding(charset)
-
elsif bom
-
str.force_encoding(encoding)
-
end
-
-
str = check_encoding(str, &block)
-
return str.encode("UTF-8"), str.encoding
-
end
-
-
1
unless ruby1_8?
-
# @private
-
1
def _enc(string, encoding)
-
string.encode(encoding).force_encoding("BINARY")
-
end
-
-
# We could automatically add in any non-ASCII-compatible encodings here,
-
# but there's not really a good way to do that
-
# without manually checking that each encoding
-
# encodes all ASCII characters properly,
-
# which takes long enough to affect the startup time of the CLI.
-
1
ENCODINGS_TO_CHECK = %w[UTF-8 UTF-16BE UTF-16LE UTF-32BE UTF-32LE]
-
-
1
CHARSET_REGEXPS = Hash.new do |h, e|
-
h[e] =
-
begin
-
# /\A(?:\uFEFF)?@charset "(.*?)"|\A(\uFEFF)/
-
Regexp.new(/\A(?:#{_enc("\uFEFF", e)})?#{
-
_enc('@charset "', e)}(.*?)#{_enc('"', e)}|\A(#{
-
_enc("\uFEFF", e)})/)
-
rescue Encoding::ConverterNotFoundError => _
-
nil # JRuby on Java 5 doesn't support UTF-32
-
rescue
-
# /\A@charset "(.*?)"/
-
Regexp.new(/\A#{_enc('@charset "', e)}(.*?)#{_enc('"', e)}/)
-
end
-
end
-
end
-
-
# Checks to see if a class has a given method.
-
# For example:
-
#
-
# Sass::Util.has?(:public_instance_method, String, :gsub) #=> true
-
#
-
# Method collections like `Class#instance_methods`
-
# return strings in Ruby 1.8 and symbols in Ruby 1.9 and on,
-
# so this handles checking for them in a compatible way.
-
#
-
# @param attr [#to_s] The (singular) name of the method-collection method
-
# (e.g. `:instance_methods`, `:private_methods`)
-
# @param klass [Module] The class to check the methods of which to check
-
# @param method [String, Symbol] The name of the method do check for
-
# @return [Boolean] Whether or not the given collection has the given method
-
1
def has?(attr, klass, method)
-
1
klass.send("#{attr}s").include?(ruby1_8? ? method.to_s : method.to_sym)
-
end
-
-
# A version of `Enumerable#enum_with_index` that works in Ruby 1.8 and 1.9.
-
#
-
# @param enum [Enumerable] The enumerable to get the enumerator for
-
# @return [Enumerator] The with-index enumerator
-
1
def enum_with_index(enum)
-
ruby1_8? ? enum.enum_with_index : enum.each_with_index
-
end
-
-
# A version of `Enumerable#enum_cons` that works in Ruby 1.8 and 1.9.
-
#
-
# @param enum [Enumerable] The enumerable to get the enumerator for
-
# @param n [Fixnum] The size of each cons
-
# @return [Enumerator] The consed enumerator
-
1
def enum_cons(enum, n)
-
ruby1_8? ? enum.enum_cons(n) : enum.each_cons(n)
-
end
-
-
# A version of `Enumerable#enum_slice` that works in Ruby 1.8 and 1.9.
-
#
-
# @param enum [Enumerable] The enumerable to get the enumerator for
-
# @param n [Fixnum] The size of each slice
-
# @return [Enumerator] The consed enumerator
-
1
def enum_slice(enum, n)
-
ruby1_8? ? enum.enum_slice(n) : enum.each_slice(n)
-
end
-
-
# Destructively removes all elements from an array that match a block, and
-
# returns the removed elements.
-
#
-
# @param array [Array] The array from which to remove elements.
-
# @yield [el] Called for each element.
-
# @yieldparam el [*] The element to test.
-
# @yieldreturn [Boolean] Whether or not to extract the element.
-
# @return [Array] The extracted elements.
-
1
def extract!(array)
-
out = []
-
array.reject! do |e|
-
next false unless yield e
-
out << e
-
true
-
end
-
out
-
end
-
-
# Returns the ASCII code of the given character.
-
#
-
# @param c [String] All characters but the first are ignored.
-
# @return [Fixnum] The ASCII code of `c`.
-
1
def ord(c)
-
ruby1_8? ? c[0] : c.ord
-
end
-
-
# Flattens the first `n` nested arrays in a cross-version manner.
-
#
-
# @param arr [Array] The array to flatten
-
# @param n [Fixnum] The number of levels to flatten
-
# @return [Array] The flattened array
-
1
def flatten(arr, n)
-
return arr.flatten(n) unless ruby1_8_6?
-
return arr if n == 0
-
arr.inject([]) {|res, e| e.is_a?(Array) ? res.concat(flatten(e, n - 1)) : res << e}
-
end
-
-
# Returns the hash code for a set in a cross-version manner.
-
# Aggravatingly, this is order-dependent in Ruby 1.8.6.
-
#
-
# @param set [Set]
-
# @return [Fixnum] The order-independent hashcode of `set`
-
1
def set_hash(set)
-
return set.hash unless ruby1_8_6?
-
set.map {|e| e.hash}.uniq.sort.hash
-
end
-
-
# Tests the hash-equality of two sets in a cross-version manner.
-
# Aggravatingly, this is order-dependent in Ruby 1.8.6.
-
#
-
# @param set1 [Set]
-
# @param set2 [Set]
-
# @return [Boolean] Whether or not the sets are hashcode equal
-
1
def set_eql?(set1, set2)
-
return set1.eql?(set2) unless ruby1_8_6?
-
set1.to_a.uniq.sort_by {|e| e.hash}.eql?(set2.to_a.uniq.sort_by {|e| e.hash})
-
end
-
-
# Like `Object#inspect`, but preserves non-ASCII characters rather than escaping them under Ruby 1.9.2.
-
# This is necessary so that the precompiled Haml template can be `#encode`d into `@options[:encoding]`
-
# before being evaluated.
-
#
-
# @param obj {Object}
-
# @return {String}
-
1
def inspect_obj(obj)
-
return obj.inspect unless version_geq(::RUBY_VERSION, "1.9.2")
-
return ':' + inspect_obj(obj.to_s) if obj.is_a?(Symbol)
-
return obj.inspect unless obj.is_a?(String)
-
'"' + obj.gsub(/[\x00-\x7F]+/) {|s| s.inspect[1...-1]} + '"'
-
end
-
-
# Extracts the non-string vlaues from an array containing both strings and non-strings.
-
# These values are replaced with escape sequences.
-
# This can be undone using \{#inject\_values}.
-
#
-
# This is useful e.g. when we want to do string manipulation
-
# on an interpolated string.
-
#
-
# The precise format of the resulting string is not guaranteed.
-
# However, it is guaranteed that newlines and whitespace won't be affected.
-
#
-
# @param arr [Array] The array from which values are extracted.
-
# @return [(String, Array)] The resulting string, and an array of extracted values.
-
1
def extract_values(arr)
-
values = []
-
return arr.map do |e|
-
next e.gsub('{', '{{') if e.is_a?(String)
-
values << e
-
next "{#{values.count - 1}}"
-
end.join, values
-
end
-
-
# Undoes \{#extract\_values} by transforming a string with escape sequences
-
# into an array of strings and non-string values.
-
#
-
# @param str [String] The string with escape sequences.
-
# @param values [Array] The array of values to inject.
-
# @return [Array] The array of strings and values.
-
1
def inject_values(str, values)
-
return [str.gsub('{{', '{')] if values.empty?
-
# Add an extra { so that we process the tail end of the string
-
result = (str + '{{').scan(/(.*?)(?:(\{\{)|\{(\d+)\})/m).map do |(pre, esc, n)|
-
[pre, esc ? '{' : '', n ? values[n.to_i] : '']
-
end.flatten(1)
-
result[-2] = '' # Get rid of the extra {
-
merge_adjacent_strings(result).reject {|s| s == ''}
-
end
-
-
# Allows modifications to be performed on the string form
-
# of an array containing both strings and non-strings.
-
#
-
# @param arr [Array] The array from which values are extracted.
-
# @yield [str] A block in which string manipulation can be done to the array.
-
# @yieldparam str [String] The string form of `arr`.
-
# @yieldreturn [String] The modified string.
-
# @return [Array] The modified, interpolated array.
-
1
def with_extracted_values(arr)
-
str, vals = extract_values(arr)
-
str = yield str
-
inject_values(str, vals)
-
end
-
-
## Static Method Stuff
-
-
# The context in which the ERB for \{#def\_static\_method} will be run.
-
1
class StaticConditionalContext
-
# @param set [#include?] The set of variables that are defined for this context.
-
1
def initialize(set)
-
@set = set
-
end
-
-
# Checks whether or not a variable is defined for this context.
-
#
-
# @param name [Symbol] The name of the variable
-
# @return [Boolean]
-
1
def method_missing(name, *args, &block)
-
super unless args.empty? && block.nil?
-
@set.include?(name)
-
end
-
end
-
-
# @private
-
1
ATOMIC_WRITE_MUTEX = Mutex.new
-
-
-
# This creates a temp file and yields it for writing. When the
-
# write is complete, the file is moved into the desired location.
-
# The atomicity of this operation is provided by the filesystem's
-
# rename operation.
-
#
-
# @param filename [String] The file to write to.
-
# @param perms [Integer] The permissions used for creating this file.
-
# Will be masked by the process umask. Defaults to readable/writeable
-
# by all users however the umask usually changes this to only be writable
-
# by the process's user.
-
# @yieldparam tmpfile [Tempfile] The temp file that can be written to.
-
# @return The value returned by the block.
-
1
def atomic_create_and_write_file(filename, perms = 0666)
-
require 'tempfile'
-
tmpfile = Tempfile.new(File.basename(filename), File.dirname(filename))
-
tmpfile.binmode if tmpfile.respond_to?(:binmode)
-
result = yield tmpfile
-
tmpfile.flush # ensure all writes are flushed to the OS
-
begin
-
tmpfile.fsync # ensure all buffered data in the OS is sync'd to disk.
-
rescue NotImplementedError
-
# Not all OSes support fsync
-
end
-
tmpfile.close # Windows cannot rename an open file.
-
# Make file readable and writeable to all but respect umask (usually 022).
-
File.chmod(perms & ~File.umask, tmpfile.path)
-
ATOMIC_WRITE_MUTEX.synchronize do
-
File.rename tmpfile.path, filename
-
end
-
result
-
ensure
-
# close and remove the tempfile if it still exists,
-
# presumably due to an error during write
-
tmpfile.close if tmpfile
-
tmpfile.unlink if tmpfile
-
end
-
-
1
private
-
-
# Calculates the memoization table for the Least Common Subsequence algorithm.
-
# Algorithm from [Wikipedia](http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Computing_the_length_of_the_LCS)
-
1
def lcs_table(x, y)
-
c = Array.new(x.size) {[]}
-
x.size.times {|i| c[i][0] = 0}
-
y.size.times {|j| c[0][j] = 0}
-
(1...x.size).each do |i|
-
(1...y.size).each do |j|
-
c[i][j] =
-
if yield x[i], y[j]
-
c[i-1][j-1] + 1
-
else
-
[c[i][j-1], c[i-1][j]].max
-
end
-
end
-
end
-
return c
-
end
-
-
# Computes a single longest common subsequence for arrays x and y.
-
# Algorithm from [Wikipedia](http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Reading_out_an_LCS)
-
1
def lcs_backtrace(c, x, y, i, j, &block)
-
return [] if i == 0 || j == 0
-
if v = yield(x[i], y[j])
-
return lcs_backtrace(c, x, y, i-1, j-1, &block) << v
-
end
-
-
return lcs_backtrace(c, x, y, i, j-1, &block) if c[i][j-1] > c[i-1][j]
-
return lcs_backtrace(c, x, y, i-1, j, &block)
-
end
-
end
-
end
-
-
1
require 'sass/util/multibyte_string_scanner'
-
1
require 'strscan'
-
-
1
if Sass::Util.ruby1_8?
-
Sass::Util::MultibyteStringScanner = StringScanner
-
else
-
1
if Sass::Util.rbx?
-
# Rubinius's StringScanner class implements some of its methods in terms of
-
# others, which causes us to double-count bytes in some cases if we do
-
# straightforward inheritance. To work around this, we use a delegate class.
-
require 'delegate'
-
class Sass::Util::MultibyteStringScanner < DelegateClass(StringScanner)
-
def initialize(str)
-
super(StringScanner.new(str))
-
@mb_pos = 0
-
@mb_matched_size = nil
-
@mb_last_pos = nil
-
end
-
-
def is_a?(klass)
-
__getobj__.is_a?(klass) || super
-
end
-
end
-
else
-
1
class Sass::Util::MultibyteStringScanner < StringScanner
-
1
def initialize(str)
-
super
-
@mb_pos = 0
-
@mb_matched_size = nil
-
@mb_last_pos = nil
-
end
-
end
-
end
-
-
# A wrapper of the native StringScanner class that works correctly with
-
# multibyte character encodings. The native class deals only in bytes, not
-
# characters, for methods like [#pos] and [#matched_size]. This class deals
-
# only in characters, instead.
-
1
class Sass::Util::MultibyteStringScanner
-
1
def self.new(str)
-
return StringScanner.new(str) if str.ascii_only?
-
super
-
end
-
-
1
alias_method :byte_pos, :pos
-
1
alias_method :byte_matched_size, :matched_size
-
-
1
def check(pattern); _match super; end
-
1
def check_until(pattern); _matched super; end
-
1
def getch; _forward _match super; end
-
1
def match?(pattern); _size check(pattern); end
-
1
def matched_size; @mb_matched_size; end
-
1
def peek(len); string[@mb_pos, len]; end
-
1
alias_method :peep, :peek
-
1
def pos; @mb_pos; end
-
1
alias_method :pointer, :pos
-
1
def rest_size; rest.size; end
-
1
def scan(pattern); _forward _match super; end
-
1
def scan_until(pattern); _forward _matched super; end
-
1
def skip(pattern); _size scan(pattern); end
-
1
def skip_until(pattern); _matched _size scan_until(pattern); end
-
-
1
def get_byte
-
raise "MultibyteStringScanner doesn't support #get_byte."
-
end
-
-
1
def getbyte
-
raise "MultibyteStringScanner doesn't support #getbyte."
-
end
-
-
1
def pos=(n)
-
@mb_last_pos = nil
-
-
# We set position kind of a lot during parsing, so we want it to be as
-
# efficient as possible. This is complicated by the fact that UTF-8 is a
-
# variable-length encoding, so it's difficult to find the byte length that
-
# corresponds to a given character length.
-
#
-
# Our heuristic here is to try to count the fewest possible characters. So
-
# if the new position is close to the current one, just count the
-
# characters between the two; if the new position is closer to the
-
# beginning of the string, just count the characters from there.
-
if @mb_pos - n < @mb_pos / 2
-
# New position is close to old position
-
byte_delta = @mb_pos > n ? -string[n...@mb_pos].bytesize : string[@mb_pos...n].bytesize
-
super(byte_pos + byte_delta)
-
else
-
# New position is close to BOS
-
super(string[0...n].bytesize)
-
end
-
@mb_pos = n
-
end
-
-
1
def reset
-
@mb_pos = 0
-
@mb_matched_size = nil
-
@mb_last_pos = nil
-
super
-
end
-
-
1
def scan_full(pattern, advance_pointer_p, return_string_p)
-
res = _match super(pattern, advance_pointer_p, true)
-
_forward res if advance_pointer_p
-
return res if return_string_p
-
end
-
-
1
def search_full(pattern, advance_pointer_p, return_string_p)
-
res = super(pattern, advance_pointer_p, true)
-
_forward res if advance_pointer_p
-
_matched((res if return_string_p))
-
end
-
-
1
def string=(str)
-
@mb_pos = 0
-
@mb_matched_size = nil
-
@mb_last_pos = nil
-
super
-
end
-
-
1
def terminate
-
@mb_pos = string.size
-
@mb_matched_size = nil
-
@mb_last_pos = nil
-
super
-
end
-
1
alias_method :clear, :terminate
-
-
1
def unscan
-
super
-
@mb_pos = @mb_last_pos
-
@mb_last_pos = @mb_matched_size = nil
-
end
-
-
1
private
-
-
1
def _size(str)
-
str && str.size
-
end
-
-
1
def _match(str)
-
@mb_matched_size = str && str.size
-
str
-
end
-
-
1
def _matched(res)
-
_match matched
-
res
-
end
-
-
1
def _forward(str)
-
@mb_last_pos = @mb_pos
-
@mb_pos += str.size if str
-
str
-
end
-
end
-
end
-
1
require 'set'
-
-
1
module Sass
-
1
module Util
-
# A map from sets to values.
-
# A value is \{#\[]= set} by providing a set (the "set-set") and a value,
-
# which is then recorded as corresponding to that set.
-
# Values are \{#\[] accessed} by providing a set (the "get-set")
-
# and returning all values that correspond to set-sets
-
# that are subsets of the get-set.
-
#
-
# SubsetMap preserves the order of values as they're inserted.
-
#
-
# @example
-
# ssm = SubsetMap.new
-
# ssm[Set[1, 2]] = "Foo"
-
# ssm[Set[2, 3]] = "Bar"
-
# ssm[Set[1, 2, 3]] = "Baz"
-
#
-
# ssm[Set[1, 2, 3]] #=> ["Foo", "Bar", "Baz"]
-
1
class SubsetMap
-
# Creates a new, empty SubsetMap.
-
1
def initialize
-
@hash = {}
-
@vals = []
-
end
-
-
# Whether or not this SubsetMap has any key-value pairs.
-
#
-
# @return [Boolean]
-
1
def empty?
-
@hash.empty?
-
end
-
-
# Associates a value with a set.
-
# When `set` or any of its supersets is accessed,
-
# `value` will be among the values returned.
-
#
-
# Note that if the same `set` is passed to this method multiple times,
-
# all given `value`s will be associated with that `set`.
-
#
-
# This runs in `O(n)` time, where `n` is the size of `set`.
-
#
-
# @param set [#to_set] The set to use as the map key. May not be empty.
-
# @param value [Object] The value to associate with `set`.
-
# @raise [ArgumentError] If `set` is empty.
-
1
def []=(set, value)
-
raise ArgumentError.new("SubsetMap keys may not be empty.") if set.empty?
-
-
index = @vals.size
-
@vals << value
-
set.each do |k|
-
@hash[k] ||= []
-
@hash[k] << [set, set.to_set, index]
-
end
-
end
-
-
# Returns all values associated with subsets of `set`.
-
#
-
# In the worst case, this runs in `O(m*max(n, log m))` time,
-
# where `n` is the size of `set`
-
# and `m` is the number of assocations in the map.
-
# However, unless many keys in the map overlap with `set`,
-
# `m` will typically be much smaller.
-
#
-
# @param set [Set] The set to use as the map key.
-
# @return [Array<(Object, #to_set)>] An array of pairs,
-
# where the first value is the value associated with a subset of `set`,
-
# and the second value is that subset of `set`
-
# (or whatever `#to_set` object was used to set the value)
-
# This array is in insertion order.
-
# @see #[]
-
1
def get(set)
-
res = set.map do |k|
-
next unless subsets = @hash[k]
-
subsets.map do |subenum, subset, index|
-
next unless subset.subset?(set)
-
[index, subenum]
-
end
-
end
-
res = Sass::Util.flatten(res, 1)
-
res.compact!
-
res.uniq!
-
res.sort!
-
res.map! {|i, s| [@vals[i], s]}
-
return res
-
end
-
-
# Same as \{#get}, but doesn't return the subsets of the argument
-
# for which values were found.
-
#
-
# @param set [Set] The set to use as the map key.
-
# @return [Array] The array of all values
-
# associated with subsets of `set`, in insertion order.
-
# @see #get
-
1
def [](set)
-
get(set).map {|v, _| v}
-
end
-
-
# Iterates over each value in the subset map. Ignores keys completely. If
-
# multiple keys have the same value, this will return them multiple times.
-
#
-
# @yield [Object] Each value in the map.
-
1
def each_value
-
@vals.each {|v| yield v}
-
end
-
end
-
end
-
end
-
1
require 'date'
-
-
# This is necessary for loading Sass when Haml is required in Rails 3.
-
# Once the split is complete, we can remove it.
-
1
require File.dirname(__FILE__) + '/../sass'
-
1
require 'sass/util'
-
-
1
module Sass
-
# Handles Sass version-reporting.
-
# Sass not only reports the standard three version numbers,
-
# but its Git revision hash as well,
-
# if it was installed from Git.
-
1
module Version
-
1
include Sass::Util
-
-
# Returns a hash representing the version of Sass.
-
# The `:major`, `:minor`, and `:teeny` keys have their respective numbers as Fixnums.
-
# The `:name` key has the name of the version.
-
# The `:string` key contains a human-readable string representation of the version.
-
# The `:number` key is the major, minor, and teeny keys separated by periods.
-
# The `:date` key, which is not guaranteed to be defined, is the [DateTime] at which this release was cut.
-
# If Sass is checked out from Git, the `:rev` key will have the revision hash.
-
# For example:
-
#
-
# {
-
# :string => "2.1.0.9616393",
-
# :rev => "9616393b8924ef36639c7e82aa88a51a24d16949",
-
# :number => "2.1.0",
-
# :date => DateTime.parse("Apr 30 13:52:01 2009 -0700"),
-
# :major => 2, :minor => 1, :teeny => 0
-
# }
-
#
-
# If a prerelease version of Sass is being used,
-
# the `:string` and `:number` fields will reflect the full version
-
# (e.g. `"2.2.beta.1"`), and the `:teeny` field will be `-1`.
-
# A `:prerelease` key will contain the name of the prerelease (e.g. `"beta"`),
-
# and a `:prerelease_number` key will contain the rerelease number.
-
# For example:
-
#
-
# {
-
# :string => "3.0.beta.1",
-
# :number => "3.0.beta.1",
-
# :date => DateTime.parse("Mar 31 00:38:04 2010 -0700"),
-
# :major => 3, :minor => 0, :teeny => -1,
-
# :prerelease => "beta",
-
# :prerelease_number => 1
-
# }
-
#
-
# @return [{Symbol => String/Fixnum}] The version hash
-
1
def version
-
1
return @@version if defined?(@@version)
-
-
1
numbers = File.read(scope('VERSION')).strip.split('.').
-
3
map {|n| n =~ /^[0-9]+$/ ? n.to_i : n}
-
1
name = File.read(scope('VERSION_NAME')).strip
-
1
@@version = {
-
:major => numbers[0],
-
:minor => numbers[1],
-
:teeny => numbers[2],
-
:name => name
-
}
-
-
1
if date = version_date
-
1
@@version[:date] = date
-
end
-
-
1
if numbers[3].is_a?(String)
-
@@version[:teeny] = -1
-
@@version[:prerelease] = numbers[3]
-
@@version[:prerelease_number] = numbers[4]
-
end
-
-
1
@@version[:number] = numbers.join('.')
-
1
@@version[:string] = @@version[:number].dup
-
-
1
if rev = revision_number
-
@@version[:rev] = rev
-
unless rev[0] == ?(
-
@@version[:string] << "." << rev[0...7]
-
end
-
end
-
-
1
@@version[:string] << " (#{name})"
-
1
@@version
-
end
-
-
1
private
-
-
1
def revision_number
-
1
if File.exists?(scope('REVISION'))
-
1
rev = File.read(scope('REVISION')).strip
-
1
return rev unless rev =~ /^([a-f0-9]+|\(.*\))$/ || rev == '(unknown)'
-
end
-
-
1
return unless File.exists?(scope('.git/HEAD'))
-
rev = File.read(scope('.git/HEAD')).strip
-
return rev unless rev =~ /^ref: (.*)$/
-
-
ref_name = $1
-
ref_file = scope(".git/#{ref_name}")
-
info_file = scope(".git/info/refs")
-
return File.read(ref_file).strip if File.exists?(ref_file)
-
return unless File.exists?(info_file)
-
File.open(info_file) do |f|
-
f.each do |l|
-
sha, ref = l.strip.split("\t", 2)
-
next unless ref == ref_name
-
return sha
-
end
-
end
-
return nil
-
end
-
-
1
def version_date
-
1
return unless File.exists?(scope('VERSION_DATE'))
-
1
return DateTime.parse(File.read(scope('VERSION_DATE')).strip)
-
end
-
end
-
-
1
extend Sass::Version
-
-
# A string representing the version of Sass.
-
# A more fine-grained representation is available from Sass.version.
-
# @api public
-
1
VERSION = version[:string] unless defined?(Sass::VERSION)
-
end
-
1
module Sass
-
1
module Rails
-
1
autoload :Logger, 'sass/rails/logger'
-
end
-
end
-
-
1
require 'sass/rails/version'
-
1
require 'sass/rails/helpers'
-
1
require 'sass/rails/importer'
-
1
require 'sass/rails/template'
-
1
require 'sass/rails/railtie'
-
1
require 'sprockets/sass_functions'
-
1
require 'active_support/deprecation'
-
-
1
module Sprockets
-
1
module SassFunctions
-
1
if instance_methods.map(&:to_sym).include?(:asset_path)
-
1
undef_method :asset_path
-
end
-
-
1
def asset_path(path, kind = nil)
-
ActiveSupport::Deprecation.warn "asset_path with two arguments is deprecated. Use asset_path(#{path}) instead." if kind
-
-
Sass::Script::String.new(sprockets_context.asset_path(path.value), :string)
-
end
-
-
1
if instance_methods.map(&:to_sym).include?(:asset_url)
-
1
undef_method :asset_url
-
end
-
-
1
def asset_url(path, kind = nil)
-
ActiveSupport::Deprecation.warn "asset_url with two arguments is deprecated. Use asset_url(#{path}) instead." if kind
-
-
Sass::Script::String.new("url(" + sprockets_context.asset_path(path.value) + ")")
-
end
-
-
1
def asset_data_url(path)
-
Sass::Script::String.new("url(" + sprockets_context.asset_data_uri(path.value) + ")")
-
end
-
end
-
end
-
1
require 'sprockets/sass_importer'
-
-
1
module Sprockets
-
1
class SassImporter < Sass::Importers::Filesystem
-
1
GLOB = /\*|\[.+\]/
-
-
1
attr_reader :context
-
1
private :context
-
-
1
def extensions
-
{
-
'css' => :scss,
-
'css.scss' => :scss,
-
'css.sass' => :sass,
-
'css.erb' => :scss,
-
'scss.erb' => :scss,
-
'sass.erb' => :sass,
-
'css.scss.erb' => :scss,
-
'css.sass.erb' => :sass
-
}.merge!(super)
-
end
-
-
1
def find_relative(name, base, options)
-
if name =~ GLOB
-
glob_imports(name, Pathname.new(base), options)
-
else
-
engine_from_path(name, File.dirname(base), options)
-
end
-
end
-
-
1
def find(name, options)
-
if name =~ GLOB
-
nil # globs must be relative
-
else
-
engine_from_path(name, root, options)
-
end
-
end
-
-
1
def each_globbed_file(glob, base_pathname, options)
-
Dir["#{base_pathname}/#{glob}"].sort.each do |filename|
-
next if filename == options[:filename]
-
yield filename if File.directory?(filename) || context.asset_requirable?(filename)
-
end
-
end
-
-
1
def glob_imports(glob, base_pathname, options)
-
contents = ""
-
each_globbed_file(glob, base_pathname.dirname, options) do |filename|
-
if File.directory?(filename)
-
depend_on(filename)
-
elsif context.asset_requirable?(filename)
-
depend_on(filename)
-
contents << "@import #{Pathname.new(filename).relative_path_from(base_pathname.dirname).to_s.inspect};\n"
-
end
-
end
-
return nil if contents.empty?
-
Sass::Engine.new(contents, options.merge(
-
:filename => base_pathname.to_s,
-
:importer => self,
-
:syntax => :scss
-
))
-
end
-
-
1
private
-
-
1
def depend_on(filename)
-
context.depend_on(filename)
-
context.depend_on(globbed_file_parent(filename))
-
end
-
-
1
def globbed_file_parent(filename)
-
if File.directory?(filename)
-
File.expand_path('..', filename)
-
else
-
File.dirname(filename)
-
end
-
end
-
-
1
def engine_from_path(name, dir, options)
-
full_filename, syntax = Sass::Util.destructure(find_real_file(dir, name, options))
-
return unless full_filename && File.readable?(full_filename)
-
-
engine = Sass::Engine.new(evaluate(full_filename), options.merge(
-
syntax: syntax,
-
filename: full_filename,
-
importer: self
-
))
-
-
if engine && (filename = engine.options[:filename])
-
@context.depend_on(filename)
-
end
-
-
engine
-
end
-
-
1
def evaluate(filename)
-
attributes = context.environment.attributes_for(filename)
-
processors = context.environment.preprocessors(attributes.content_type) +
-
attributes.engines.reverse - [Sprockets::ScssTemplate, Sprockets::SassTemplate]
-
-
context.evaluate(filename, processors: processors)
-
end
-
end
-
end
-
1
require 'sass/logger'
-
-
1
module Sass
-
1
module Rails
-
1
class Logger < Sass::Logger::Base
-
1
def _log(level, message)
-
-
case level
-
when :trace, :debug
-
::Rails.logger.debug message
-
when :warn
-
::Rails.logger.warn message
-
when :error
-
::Rails.logger.error message
-
when :info
-
::Rails.logger.info message
-
end
-
end
-
end
-
end
-
end
-
1
require 'sprockets/railtie'
-
-
1
module Sass::Rails
-
1
class Railtie < ::Rails::Railtie
-
1
module SassContext
-
1
attr_accessor :sass_config
-
end
-
-
1
config.sass = ActiveSupport::OrderedOptions.new
-
-
# Establish static configuration defaults
-
# Emit scss files during stylesheet generation of scaffold
-
1
config.sass.preferred_syntax = :scss
-
# Write sass cache files for performance
-
1
config.sass.cache = true
-
# Read sass cache files for performance
-
1
config.sass.read_cache = true
-
# Display line comments above each selector as a debugging aid
-
1
config.sass.line_comments = true
-
# Initialize the load paths to an empty array
-
1
config.sass.load_paths = []
-
# Send Sass logs to Rails.logger
-
1
config.sass.logger = Sass::Rails::Logger.new
-
-
# Set the default stylesheet engine
-
# It can be overridedden by passing:
-
# --stylesheet_engine=sass
-
# to the rails generate command
-
1
config.app_generators.stylesheet_engine config.sass.preferred_syntax
-
-
# Remove the sass middleware if it gets inadvertently enabled by applications.
-
1
config.after_initialize do |app|
-
1
app.config.middleware.delete(Sass::Plugin::Rack) if defined?(Sass::Plugin::Rack)
-
end
-
-
1
initializer :setup_sass, group: :all do |app|
-
# Only emit one kind of syntax because though we have registered two kinds of generators
-
1
syntax = app.config.sass.preferred_syntax.to_sym
-
1
alt_syntax = syntax == :sass ? "scss" : "sass"
-
1
app.config.generators.hide_namespace alt_syntax
-
-
# Override stylesheet engine to the preferred syntax
-
1
config.app_generators.stylesheet_engine syntax
-
-
# Set the sass cache location
-
1
config.sass.cache_location = File.join(Rails.root, "tmp/cache/sass")
-
-
# Establish configuration defaults that are evironmental in nature
-
1
if config.sass.full_exception.nil?
-
# Display a stack trace in the css output when in development-like environments.
-
1
config.sass.full_exception = app.config.consider_all_requests_local
-
end
-
-
1
if app.assets
-
1
app.assets.context_class.extend(SassContext)
-
1
app.assets.context_class.sass_config = app.config.sass
-
end
-
-
1
Sass.logger = app.config.sass.logger
-
end
-
-
1
initializer :setup_compression, group: :all do |app|
-
1
unless Rails.env.development?
-
1
app.config.assets.css_compressor ||= :sass
-
else
-
# Use expanded output instead of the sass default of :nested unless specified
-
app.config.sass.style ||= :expanded
-
end
-
end
-
end
-
end
-
1
require "sprockets/sass_template"
-
-
1
module Sprockets
-
1
class SassTemplate
-
1
def evaluate(context, locals, &block)
-
cache_store = SassCacheStore.new(context.environment)
-
-
options = {
-
:filename => eval_file,
-
:line => line,
-
:syntax => syntax,
-
:cache_store => cache_store,
-
:importer => SassImporter.new(context, context.pathname),
-
:load_paths => context.environment.paths.map { |path| SassImporter.new(context, path) },
-
:sprockets => {
-
:context => context,
-
:environment => context.environment
-
}
-
}
-
-
sass_config = context.environment.context_class.sass_config.merge(options)
-
::Sass::Engine.new(data, sass_config).render
-
rescue ::Sass::SyntaxError => e
-
context.__LINE__ = e.sass_backtrace.first[:line]
-
raise e
-
end
-
end
-
end
-
1
module Sass
-
1
module Rails
-
1
VERSION = "4.0.3"
-
end
-
end
-
1
require "rails"
-
1
require "selectize-rails/version"
-
-
1
module Selectize
-
1
module Rails
-
1
if ::Rails.version < "3.1"
-
require "selectize-rails/railtie"
-
else
-
1
require "selectize-rails/engine"
-
end
-
end
-
end
-
1
module Selectize
-
1
module Rails
-
1
class Engine < ::Rails::Engine; end
-
end
-
end
-
1
module Selectize
-
1
module Rails
-
1
VERSION = "0.11.2"
-
end
-
end
-
1
require 'selenium/webdriver'
-
1
require 'childprocess'
-
1
require 'tmpdir'
-
1
require 'fileutils'
-
1
require 'date'
-
-
1
require 'multi_json'
-
1
require 'selenium/webdriver/common'
-
-
1
module Selenium
-
1
module WebDriver
-
1
extend JsonHelper
-
-
1
Point = Struct.new(:x, :y)
-
1
Dimension = Struct.new(:width, :height)
-
1
Location = Struct.new(:latitude, :longitude, :altitude)
-
-
1
autoload :Android, 'selenium/webdriver/android'
-
1
autoload :Chrome, 'selenium/webdriver/chrome'
-
1
autoload :Firefox, 'selenium/webdriver/firefox'
-
1
autoload :IE, 'selenium/webdriver/ie'
-
1
autoload :IPhone, 'selenium/webdriver/iphone'
-
1
autoload :Opera, 'selenium/webdriver/opera'
-
1
autoload :PhantomJS, 'selenium/webdriver/phantomjs'
-
1
autoload :Remote, 'selenium/webdriver/remote'
-
1
autoload :Safari, 'selenium/webdriver/safari'
-
1
autoload :Support, 'selenium/webdriver/support'
-
-
# @api private
-
-
1
def self.root
-
@root ||= File.expand_path("../..", __FILE__)
-
end
-
-
#
-
# Create a new Driver instance with the correct bridge for the given browser
-
#
-
# @param browser [:ie, :internet_explorer, :remote, :chrome, :firefox, :ff, :android, :iphone, :opera, :phantomjs, :safari]
-
# the driver type to use
-
# @param *rest
-
# arguments passed to Bridge.new
-
#
-
# @return [Driver]
-
#
-
# @see Selenium::WebDriver::Remote::Bridge
-
# @see Selenium::WebDriver::Firefox::Bridge
-
# @see Selenium::WebDriver::IE::Bridge
-
# @see Selenium::WebDriver::Chrome::Bridge
-
# @see Selenium::WebDriver::Android::Bridge
-
# @see Selenium::WebDriver::IPhone::Bridge
-
# @see Selenium::WebDriver::Opera::Bridge
-
# @see Selenium::WebDriver::PhantomJS::Bridge
-
# @see Selenium::WebDriver::Safari::Bridge
-
#
-
# @example
-
#
-
# WebDriver.for :firefox, :profile => "some-profile"
-
# WebDriver.for :firefox, :profile => Profile.new
-
# WebDriver.for :remote, :url => "http://localhost:4444/wd/hub", :desired_capabilities => caps
-
#
-
# One special argument is not passed on to the bridges, :listener. You can pass a listener for this option
-
# to get notified of WebDriver events. The passed object must respond to #call or implement the methods from AbstractEventListener.
-
#
-
# @see Selenium::WebDriver::Support::AbstractEventListener
-
#
-
-
1
def self.for(*args)
-
WebDriver::Driver.for(*args)
-
end
-
-
end # WebDriver
-
end # Selenium
-
1
require 'selenium/webdriver/common/core_ext/dir'
-
1
require 'selenium/webdriver/common/core_ext/string'
-
1
require 'selenium/webdriver/common/core_ext/base64'
-
1
require 'selenium/webdriver/common/error'
-
1
require 'selenium/webdriver/common/platform'
-
1
require 'selenium/webdriver/common/proxy'
-
1
require 'selenium/webdriver/common/log_entry'
-
1
require 'selenium/webdriver/common/file_reaper'
-
1
require 'selenium/webdriver/common/socket_poller'
-
1
require 'selenium/webdriver/common/port_prober'
-
1
require 'selenium/webdriver/common/zipper'
-
1
require 'selenium/webdriver/common/wait'
-
1
require 'selenium/webdriver/common/alert'
-
1
require 'selenium/webdriver/common/mouse'
-
1
require 'selenium/webdriver/common/keyboard'
-
1
require 'selenium/webdriver/common/touch_screen'
-
1
require 'selenium/webdriver/common/target_locator'
-
1
require 'selenium/webdriver/common/navigation'
-
1
require 'selenium/webdriver/common/timeouts'
-
1
require 'selenium/webdriver/common/window'
-
1
require 'selenium/webdriver/common/logs'
-
1
require 'selenium/webdriver/common/options'
-
1
require 'selenium/webdriver/common/search_context'
-
1
require 'selenium/webdriver/common/action_builder'
-
1
require 'selenium/webdriver/common/touch_action_builder'
-
1
require 'selenium/webdriver/common/html5/shared_web_storage'
-
1
require 'selenium/webdriver/common/html5/local_storage'
-
1
require 'selenium/webdriver/common/html5/session_storage'
-
1
require 'selenium/webdriver/common/driver_extensions/takes_screenshot'
-
1
require 'selenium/webdriver/common/driver_extensions/rotatable'
-
1
require 'selenium/webdriver/common/driver_extensions/has_browser_connection'
-
1
require 'selenium/webdriver/common/driver_extensions/has_input_devices'
-
1
require 'selenium/webdriver/common/driver_extensions/has_web_storage'
-
1
require 'selenium/webdriver/common/driver_extensions/has_location'
-
1
require 'selenium/webdriver/common/driver_extensions/has_session_id'
-
1
require 'selenium/webdriver/common/driver_extensions/has_touch_screen'
-
1
require 'selenium/webdriver/common/driver_extensions/has_remote_status'
-
1
require 'selenium/webdriver/common/driver_extensions/uploads_files'
-
1
require 'selenium/webdriver/common/keys'
-
1
require 'selenium/webdriver/common/bridge_helper'
-
1
require 'selenium/webdriver/common/profile_helper'
-
1
require 'selenium/webdriver/common/json_helper'
-
1
require 'selenium/webdriver/common/driver'
-
1
require 'selenium/webdriver/common/element'
-
1
module Selenium
-
1
module WebDriver
-
-
#
-
# The ActionBuilder provides the user a way to set up and perform
-
# complex user interactions.
-
#
-
# This class should not be instantiated directly, but is created by
-
# Selenium::WebDriver::DriverExtensions::HasInputDevices#action, which
-
# is available on Driver instances that support the user interaction API.
-
#
-
# @example
-
#
-
# driver.action.key_down(:shift).
-
# click(element).
-
# click(second_element).
-
# key_up(:shift).
-
# drag_and_drop(element, third_element).
-
# perform
-
#
-
-
1
class ActionBuilder
-
-
#
-
# @api private
-
#
-
-
1
def initialize(mouse, keyboard)
-
@devices = {
-
:mouse => mouse,
-
:keyboard => keyboard
-
}
-
-
@actions = []
-
end
-
-
#
-
# Performs a modifier key press. Does not release
-
# the modifier key - subsequent interactions may assume it's kept pressed.
-
# Note that the modifier key is never released implicitly - either
-
# #key_up(key) or #send_keys(:null) must be called to release the modifier.
-
#
-
# Equivalent to:
-
# driver.action.click(element).send_keys(key)
-
# # or
-
# driver.action.click.send_keys(key)
-
#
-
# @example Press a key
-
#
-
# driver.action.key_down(:control).perform
-
#
-
# @example Press a key on an element
-
#
-
# el = driver.find_element(:id, "some_id")
-
# driver.action.key_down(el, :shift).perform
-
#
-
# @param [:shift, :alt, :control, :command, :meta] The key to press.
-
# @param [Selenium::WebDriver::Element] element An optional element
-
# @raise [ArgumentError] if the given key is not a modifier
-
# @return [ActionBuilder] A self reference.
-
#
-
-
1
def key_down(*args)
-
if args.first.kind_of? Element
-
@actions << [:mouse, :click, [args.shift]]
-
end
-
-
@actions << [:keyboard, :press, args]
-
self
-
end
-
-
#
-
# Performs a modifier key release.
-
# Releasing a non-depressed modifier key will yield undefined behaviour.
-
#
-
# @example Release a key
-
#
-
# driver.action.key_up(:shift).perform
-
#
-
# @example Release a key from an element
-
#
-
# el = driver.find_element(:id, "some_id")
-
# driver.action.key_up(el, :alt).perform
-
#
-
# @param [:shift, :alt, :control, :command, :meta] The modifier key to release.
-
# @param [Selenium::WebDriver::Element] element An optional element
-
# @raise [ArgumentError] if the given key is not a modifier key
-
# @return [ActionBuilder] A self reference.
-
#
-
-
1
def key_up(*args)
-
if args.first.kind_of? Element
-
@actions << [:mouse, :click, [args.shift]]
-
end
-
-
@actions << [:keyboard, :release, args]
-
self
-
end
-
-
#
-
# Sends keys to the active element. This differs from calling
-
# Element#send_keys(keys) on the active element in two ways:
-
#
-
# * The modifier keys included in this call are not released.
-
# * There is no attempt to re-focus the element - so send_keys(:tab) for switching elements should work.
-
#
-
# @example Send the text "help" to an element
-
#
-
# el = driver.find_element(:id, "some_id")
-
# driver.action.send_keys(el, "help").perform
-
#
-
# @example Send the text "help" to the currently focused element
-
#
-
# driver.action.send_keys("help").perform
-
#
-
# @param [Selenium::WebDriver::Element] element An optional element
-
# @param [String] keys The keys to be sent.
-
# @return [ActionBuilder] A self reference.
-
#
-
-
1
def send_keys(*args)
-
if args.first.kind_of? Element
-
@actions << [:mouse, :click, [args.shift]]
-
end
-
-
@actions << [:keyboard, :send_keys, args]
-
self
-
end
-
-
#
-
# Clicks (without releasing) in the middle of the given element. This is
-
# equivalent to:
-
#
-
# driver.action.move_to(element).click_and_hold
-
#
-
# @example Clicking and holding on some element
-
#
-
# el = driver.find_element(:id, "some_id")
-
# driver.action.click_and_hold(el).perform
-
#
-
# @param [Selenium::WebDriver::Element] element the element to move to and click.
-
# @return [ActionBuilder] A self reference.
-
#
-
-
1
def click_and_hold(element = nil)
-
@actions << [:mouse, :down, [element]]
-
self
-
end
-
-
#
-
# Releases the depressed left mouse button at the current mouse location.
-
#
-
# @example Releasing an element after clicking and holding it
-
#
-
# el = driver.find_element(:id, "some_id")
-
# driver.action.click_and_hold(el).release.perform
-
#
-
# @return [ActionBuilder] A self reference.
-
#
-
-
1
def release(element = nil)
-
@actions << [:mouse, :up, [element]]
-
self
-
end
-
-
#
-
# Clicks in the middle of the given element. Equivalent to:
-
#
-
# driver.action.move_to(element).click
-
#
-
# When no element is passed, the current mouse position will be clicked.
-
#
-
# @example Clicking on an element
-
#
-
# el = driver.find_element(:id, "some_id")
-
# driver.action.click(el).perform
-
#
-
# @example Clicking at the current mouse position
-
#
-
# driver.action.click.perform
-
#
-
# @param [Selenium::WebDriver::Element] element An optional element to click.
-
# @return [ActionBuilder] A self reference.
-
#
-
-
1
def click(element = nil)
-
@actions << [:mouse, :click, [element]]
-
self
-
end
-
-
#
-
# Performs a double-click at middle of the given element. Equivalent to:
-
#
-
# driver.action.move_to(element).double_click
-
#
-
# @example Double click an element
-
#
-
# el = driver.find_element(:id, "some_id")
-
# driver.action.double_click(el).perform
-
#
-
# @param [Selenium::WebDriver::Element] element An optional element to move to.
-
# @return [ActionBuilder] A self reference.
-
#
-
-
1
def double_click(element = nil)
-
@actions << [:mouse, :double_click, [element]]
-
self
-
end
-
-
#
-
# Moves the mouse to the middle of the given element. The element is scrolled into
-
# view and its location is calculated using getBoundingClientRect. Then the
-
# mouse is moved to optional offset coordinates from the element.
-
#
-
# Note that when using offsets, both coordinates need to be passed.
-
#
-
# @example Scroll element into view and move the mouse to it
-
#
-
# el = driver.find_element(:id, "some_id")
-
# driver.action.move_to(el).perform
-
#
-
# @example
-
#
-
# el = driver.find_element(:id, "some_id")
-
# driver.action.move_to(el, 100, 100).perform
-
#
-
# @param [Selenium::WebDriver::Element] element to move to.
-
# @param [Integer] right_by Optional offset from the top-left corner. A negative value means
-
# coordinates right from the element.
-
# @param [Integer] down_by Optional offset from the top-left corner. A negative value means
-
# coordinates above the element.
-
# @return [ActionBuilder] A self reference.
-
#
-
-
1
def move_to(element, right_by = nil, down_by = nil)
-
if right_by && down_by
-
@actions << [:mouse, :move_to, [element, right_by, down_by]]
-
else
-
@actions << [:mouse, :move_to, [element]]
-
end
-
-
self
-
end
-
-
#
-
# Moves the mouse from its current position (or 0,0) by the given offset.
-
# If the coordinates provided are outside the viewport (the mouse will
-
# end up outside the browser window) then the viewport is scrolled to
-
# match.
-
#
-
# @example Move the mouse to a certain offset from its current position
-
#
-
# driver.action.move_by(100, 100).perform
-
#
-
# @param [Integer] right_by horizontal offset. A negative value means moving the
-
# mouse left.
-
# @param [Integer] down_by vertical offset. A negative value means moving the mouse
-
# up.
-
# @return [ActionBuilder] A self reference.
-
# @raise [MoveTargetOutOfBoundsError] if the provided offset is outside
-
# the document's boundaries.
-
#
-
-
1
def move_by(right_by, down_by)
-
@actions << [:mouse, :move_by, [right_by, down_by]]
-
self
-
end
-
-
#
-
# Performs a context-click at middle of the given element. First performs
-
# a move_to to the location of the element.
-
#
-
# @example Context-click at middle of given element
-
#
-
# el = driver.find_element(:id, "some_id")
-
# driver.action.context_click(el).perform
-
#
-
# @param [Selenium::WebDriver::Element] element An element to context click.
-
# @return [ActionBuilder] A self reference.
-
#
-
-
1
def context_click(element = nil)
-
@actions << [:mouse, :context_click, [element]]
-
self
-
end
-
-
#
-
# A convenience method that performs click-and-hold at the location of the
-
# source element, moves to the location of the target element, then
-
# releases the mouse.
-
#
-
# @example Drag and drop one element onto another
-
#
-
# el1 = driver.find_element(:id, "some_id1")
-
# el2 = driver.find_element(:id, "some_id2")
-
# driver.action.drag_and_drop(el1, el2).perform
-
#
-
# @param [Selenium::WebDriver::Element] source element to emulate button down at.
-
# @param [Selenium::WebDriver::Element] target element to move to and release the
-
# mouse at.
-
# @return [ActionBuilder] A self reference.
-
#
-
-
1
def drag_and_drop(source, target)
-
click_and_hold source
-
move_to target
-
release target
-
-
self
-
end
-
-
#
-
# A convenience method that performs click-and-hold at the location of
-
# the source element, moves by a given offset, then releases the mouse.
-
#
-
# @example Drag and drop an element by offset
-
#
-
# el = driver.find_element(:id, "some_id1")
-
# driver.action.drag_and_drop_by(el, 100, 100).perform
-
#
-
# @param [Selenium::WebDriver::Element] source Element to emulate button down at.
-
# @param [Integer] right_by horizontal move offset.
-
# @param [Integer] down_by vertical move offset.
-
# @param [Selenium::WebDriver::Element] target Element to move to and release the
-
# mouse at.
-
# @return [ActionBuilder] A self reference.
-
#
-
-
1
def drag_and_drop_by(source, right_by, down_by)
-
click_and_hold source
-
move_by right_by, down_by
-
release
-
-
self
-
end
-
-
-
#
-
# Executes the actions added to the builder.
-
#
-
-
1
def perform
-
@actions.each { |receiver, method, args|
-
@devices.fetch(receiver).__send__(method, *args)
-
}
-
-
nil
-
end
-
-
end # ActionBuilder
-
end # WebDriver
-
end # Selenium
-
1
module Selenium
-
1
module WebDriver
-
1
class Alert
-
-
1
def initialize(bridge)
-
@bridge = bridge
-
-
# fail fast if the alert doesn't exist
-
bridge.getAlertText
-
end
-
-
1
def accept
-
@bridge.acceptAlert
-
end
-
-
1
def dismiss
-
@bridge.dismissAlert
-
end
-
-
1
def send_keys(keys)
-
@bridge.setAlertValue keys
-
end
-
-
1
def text
-
@bridge.getAlertText
-
end
-
-
end # Alert
-
end # WebDriver
-
end # Selenium
-
1
module Selenium
-
1
module WebDriver
-
-
#
-
# Shared across bridges
-
#
-
# @api private
-
#
-
-
1
module BridgeHelper
-
-
1
def unwrap_script_result(arg)
-
case arg
-
when Array
-
arg.map { |e| unwrap_script_result(e) }
-
when Hash
-
if id = element_id_from(arg)
-
Element.new self, id
-
else
-
arg.each { |k, v| arg[k] = unwrap_script_result(v) }
-
end
-
else
-
arg
-
end
-
end
-
-
1
def element_id_from(id)
-
id['ELEMENT']
-
end
-
-
1
def parse_cookie_string(str)
-
result = {
-
'name' => '',
-
'value' => '',
-
'domain' => '',
-
'path' => '',
-
'expires' => '',
-
'secure' => false
-
}
-
-
str.split(";").each do |attribute|
-
if attribute.include? "="
-
key, value = attribute.strip.split("=", 2)
-
if result['name'].empty?
-
result['name'] = key
-
result['value'] = value
-
elsif key == 'domain' && value.strip =~ /^\.(.+)/
-
result['domain'] = $1
-
elsif key && value
-
result[key] = value
-
end
-
elsif attribute == "secure"
-
result['secure'] = true
-
end
-
-
unless [nil, "", "0"].include?(result['expires'])
-
# firefox stores expiry as number of seconds
-
result['expires'] = Time.at(result['expires'].to_i)
-
end
-
end
-
-
result
-
end
-
-
end # BridgeHelper
-
end # WebDriver
-
end # Selenium
-
1
require 'base64'
-
-
1
module Base64
-
-
def self.strict_encode64(str)
-
encode64(str).gsub(/\n/, '')
-
1
end unless respond_to?(:strict_encode64) # added in 1.9
-
-
end
-
1
class Dir
-
# @api private
-
def self.mktmpdir(prefix_suffix=nil, tmpdir=nil)
-
case prefix_suffix
-
when nil
-
prefix = "d"
-
suffix = ""
-
when String
-
prefix = prefix_suffix
-
suffix = ""
-
when Array
-
prefix = prefix_suffix[0]
-
suffix = prefix_suffix[1]
-
else
-
raise ArgumentError, "unexpected prefix_suffix: #{prefix_suffix.inspect}"
-
end
-
tmpdir ||= Dir.tmpdir
-
t = Time.now.strftime("%Y%m%d")
-
n = nil
-
begin
-
path = "#{tmpdir}/#{prefix}#{t}-#{$$}-#{rand(0x100000000).to_s(36)}"
-
path << "-#{n}" if n
-
path << suffix
-
Dir.mkdir(path, 0700)
-
rescue Errno::EEXIST
-
n ||= 0
-
n += 1
-
retry
-
end
-
-
if block_given?
-
begin
-
yield path
-
ensure
-
FileUtils.remove_entry_secure path
-
end
-
else
-
path
-
end
-
1
end unless respond_to? :mktmpdir # > 1.8.7
-
end
-
-
1
unless '1.9'.respond_to?(:bytesize)
-
class String
-
alias :bytesize :size
-
end
-
end
-
1
module Selenium
-
1
module WebDriver
-
-
#
-
# The main class through which you control the browser.
-
#
-
# @see SearchContext
-
# @see Navigation
-
# @see TargetLocator
-
# @see Options
-
#
-
-
1
class Driver
-
1
include SearchContext
-
-
1
class << self
-
-
#
-
# @api private
-
#
-
# @see Selenium::WebDriver.for
-
#
-
# @return [Driver]
-
#
-
-
1
def for(browser, opts = {})
-
listener = opts.delete(:listener)
-
-
bridge = case browser
-
when :firefox, :ff
-
Firefox::Bridge.new(opts)
-
when :remote
-
Remote::Bridge.new(opts)
-
when :ie, :internet_explorer
-
IE::Bridge.new(opts)
-
when :chrome
-
Chrome::Bridge.new(opts)
-
when :android
-
Android::Bridge.new(opts)
-
when :iphone
-
IPhone::Bridge.new(opts)
-
when :opera
-
Opera::Bridge.new(opts)
-
when :phantomjs
-
PhantomJS::Bridge.new(opts)
-
when :safari
-
Safari::Bridge.new(opts)
-
else
-
raise ArgumentError, "unknown driver: #{browser.inspect}"
-
end
-
-
bridge = Support::EventFiringBridge.new(bridge, listener) if listener
-
-
new(bridge)
-
end
-
end
-
-
#
-
# A new Driver instance with the given bridge.
-
# End users should use Selenium::WebDriver.for instead of using this directly.
-
#
-
# @api private
-
#
-
-
1
def initialize(bridge)
-
@bridge = bridge
-
-
# TODO: refactor this away
-
unless bridge.driver_extensions.empty?
-
extend(*bridge.driver_extensions)
-
end
-
end
-
-
1
def inspect
-
'#<%s:0x%x browser=%s>' % [self.class, hash*2, bridge.browser.inspect]
-
end
-
-
#
-
# @return [Navigation]
-
# @see Navigation
-
#
-
-
1
def navigate
-
@navigate ||= WebDriver::Navigation.new(bridge)
-
end
-
-
#
-
# @return [TargetLocator]
-
# @see TargetLocator
-
#
-
-
1
def switch_to
-
@switch_to ||= WebDriver::TargetLocator.new(bridge)
-
end
-
-
#
-
# @return [Options]
-
# @see Options
-
#
-
-
1
def manage
-
@manage ||= WebDriver::Options.new(bridge)
-
end
-
-
#
-
# Opens the specified URL in the browser.
-
#
-
-
1
def get(url)
-
navigate.to(url)
-
end
-
-
#
-
# Get the URL of the current page
-
#
-
# @return [String]
-
#
-
-
1
def current_url
-
bridge.getCurrentUrl
-
end
-
-
#
-
# Get the title of the current page
-
#
-
# @return [String]
-
#
-
-
1
def title
-
bridge.getTitle
-
end
-
-
#
-
# Get the source of the current page
-
#
-
# @return [String]
-
#
-
-
1
def page_source
-
bridge.getPageSource
-
end
-
-
#
-
# Quit the browser
-
#
-
-
1
def quit
-
bridge.quit
-
end
-
-
#
-
# Close the current window, or the browser if no windows are left.
-
#
-
-
1
def close
-
bridge.close
-
end
-
-
#
-
# Get the window handles of open browser windows.
-
#
-
# @return [Array]
-
# @see TargetLocator#window
-
#
-
-
1
def window_handles
-
bridge.getWindowHandles
-
end
-
-
#
-
# Get the current window handle
-
#
-
# @return [String]
-
#
-
-
1
def window_handle
-
bridge.getCurrentWindowHandle
-
end
-
-
#
-
# Execute the given JavaScript
-
#
-
# @param [String] script
-
# JavaScript source to execute
-
# @param [WebDriver::Element,Integer, Float, Boolean, NilClass, String, Array] *args
-
# Arguments will be available in the given script in the 'arguments' pseudo-array.
-
#
-
# @return [WebDriver::Element,Integer,Float,Boolean,NilClass,String,Array]
-
# The value returned from the script.
-
#
-
-
1
def execute_script(script, *args)
-
bridge.executeScript(script, *args)
-
end
-
-
# Execute an asynchronous piece of JavaScript in the context of the
-
# currently selected frame or window. Unlike executing
-
# execute_script (synchronous JavaScript), scripts
-
# executed with this method must explicitly signal they are finished by
-
# invoking the provided callback. This callback is always injected into the
-
# executed function as the last argument.
-
#
-
# @param [String] script
-
# JavaSCript source to execute
-
# @param [WebDriver::Element,Integer, Float, Boolean, NilClass, String, Array] *args
-
# Arguments to the script. May be empty.
-
#
-
# @return [WebDriver::Element,Integer,Float,Boolean,NilClass,String,Array]
-
#
-
-
1
def execute_async_script(script, *args)
-
bridge.executeAsyncScript(script, *args)
-
end
-
-
-
#-------------------------------- sugar --------------------------------
-
-
#
-
# driver.first(:id, 'foo')
-
#
-
-
1
alias_method :first, :find_element
-
-
#
-
# driver.all(:class, 'bar') #=> [#<WebDriver::Element:0x1011c3b88, ...]
-
#
-
-
1
alias_method :all, :find_elements
-
-
#
-
# driver.script('function() { ... };')
-
#
-
-
1
alias_method :script, :execute_script
-
-
# Get the first element matching the given selector. If given a
-
# String or Symbol, it will be used as the id of the element.
-
#
-
# @param [String,Hash] id or selector
-
# @return [WebDriver::Element]
-
#
-
# Examples:
-
#
-
# driver['someElementId'] #=> #<WebDriver::Element:0x1011c3b88>
-
# driver[:tag_name => 'div'] #=> #<WebDriver::Element:0x1011c3b88>
-
#
-
-
1
def [](sel)
-
if sel.kind_of?(String) || sel.kind_of?(Symbol)
-
sel = { :id => sel }
-
end
-
-
find_element sel
-
end
-
-
1
def browser
-
bridge.browser
-
end
-
-
1
def capabilities
-
bridge.capabilities
-
end
-
-
#
-
# @api private
-
# @see SearchContext
-
#
-
-
1
def ref
-
nil
-
end
-
-
1
private
-
-
1
def bridge
-
@bridge
-
end
-
-
end # Driver
-
end # WebDriver
-
end # Selenium
-
1
module Selenium
-
1
module WebDriver
-
1
module DriverExtensions
-
-
1
module HasBrowserConnection
-
1
def online?
-
@bridge.isBrowserOnline
-
end
-
-
1
def online=(bool)
-
@bridge.setBrowserOnline bool
-
end
-
end
-
-
end
-
end
-
end
-
1
module Selenium
-
1
module WebDriver
-
-
#
-
# @api private
-
#
-
-
1
module DriverExtensions
-
1
module HasInputDevices
-
-
#
-
# @return [ActionBuilder]
-
# @api public
-
#
-
-
1
def action
-
ActionBuilder.new mouse, keyboard
-
end
-
-
#
-
# @api private
-
#
-
-
1
def mouse
-
Mouse.new @bridge
-
end
-
-
#
-
# @api private
-
#
-
-
1
def keyboard
-
Keyboard.new @bridge
-
end
-
-
end # HasInputDevices
-
end # DriverExtensions
-
end # WebDriver
-
end # Selenium
-
1
module Selenium
-
1
module WebDriver
-
1
module DriverExtensions
-
-
1
module HasLocation
-
1
def location
-
@bridge.getLocation
-
end
-
-
1
def location=(loc)
-
unless loc.kind_of?(Location)
-
raise TypeError, "expected #{Location}, got #{loc.inspect}:#{loc.class}"
-
end
-
-
@bridge.setLocation loc.latitude, loc.longitude, loc.altitude
-
end
-
-
1
def set_location(lat, lon, alt)
-
self.location = Location.new(Float(lat), Float(lon), Float(alt))
-
end
-
end
-
-
end
-
end
-
end
-
1
module Selenium
-
1
module WebDriver
-
1
module DriverExtensions
-
1
module HasRemoteStatus
-
-
1
def remote_status
-
@bridge.status
-
end
-
-
end
-
end
-
end
-
end
-
1
module Selenium
-
1
module WebDriver
-
-
#
-
# @api private
-
#
-
-
1
module DriverExtensions
-
1
module HasSessionId
-
-
#
-
# @return [String] the session id
-
# @api public
-
#
-
-
1
def session_id
-
@bridge.session_id
-
end
-
end # HasSessionId
-
end # DriverExtensions
-
end # WebDriver
-
end # Selenium
-
1
module Selenium
-
1
module WebDriver
-
1
module DriverExtensions
-
-
1
module HasTouchScreen
-
1
def touch
-
TouchActionBuilder.new mouse, keyboard, touch_screen
-
end
-
-
1
private
-
-
1
def touch_screen
-
TouchScreen.new @bridge
-
end
-
end
-
-
end
-
end
-
end
-
1
module Selenium
-
1
module WebDriver
-
-
#
-
# @api private
-
#
-
-
1
module DriverExtensions
-
1
module HasWebStorage
-
-
1
def local_storage
-
HTML5::LocalStorage.new @bridge
-
end
-
-
1
def session_storage
-
HTML5::SessionStorage.new @bridge
-
end
-
-
end # HasWebStorage
-
end # DriverExtensions
-
end # WebDriver
-
end # Selenium
-
1
module Selenium
-
1
module WebDriver
-
-
#
-
# @api private
-
#
-
-
1
module DriverExtensions
-
1
module Rotatable
-
-
1
ORIENTATIONS = [:landscape, :portrait]
-
-
#
-
# Change the screen orientation
-
#
-
# @param [:landscape, :portrait] Orientation
-
#
-
# @api public
-
#
-
-
1
def rotation=(orientation)
-
unless ORIENTATIONS.include?(orientation)
-
raise ArgumentError, "expected #{ORIENTATIONS.inspect}, got #{orientation.inspect}"
-
end
-
-
bridge.setScreenOrientation(orientation.to_s.upcase)
-
end
-
1
alias_method :rotate, :rotation=
-
-
#
-
# Get the current screen orientation
-
#
-
# @return [:landscape, :portrait] orientation
-
#
-
# @api public
-
#
-
-
1
def orientation
-
bridge.getScreenOrientation.to_sym.downcase
-
end
-
-
end # Rotatable
-
end # DriverExtensions
-
end # WebDriver
-
end # Selenium
-
1
module Selenium
-
1
module WebDriver
-
-
#
-
# @api private
-
#
-
-
1
module DriverExtensions
-
1
module TakesScreenshot
-
-
#
-
# Save a PNG screenshot to the given path
-
#
-
# @api public
-
#
-
-
1
def save_screenshot(png_path)
-
File.open(png_path, 'wb') { |f| f << screenshot_as(:png) }
-
end
-
-
#
-
# Return a PNG screenshot in the given format as a string
-
#
-
# @param [:base64, :png] format
-
# @return String screenshot
-
#
-
# @api public
-
-
1
def screenshot_as(format)
-
case format
-
when :base64
-
bridge.getScreenshot
-
when :png
-
bridge.getScreenshot.unpack("m")[0]
-
else
-
raise Error::UnsupportedOperationError, "unsupported format: #{format.inspect}"
-
end
-
end
-
-
end # TakesScreenshot
-
end # DriverExtensions
-
end # WebDriver
-
end # Selenium
-
1
module Selenium
-
1
module WebDriver
-
-
#
-
# @api private
-
#
-
-
1
module DriverExtensions
-
1
module UploadsFiles
-
-
#
-
# Set the file detector to pass local files to a remote WebDriver.
-
#
-
# The detector is an object that responds to #call, and when called
-
# will determine if the given string represents a file. If it does,
-
# the path to the file on the local file system should be returned,
-
# otherwise nil or false.
-
#
-
# Example:
-
#
-
# driver = Selenium::WebDriver.for :remote
-
# driver.file_detector = lambda do |args|
-
# # args => ["/path/to/file"]
-
# str = args.first.to_s
-
# str if File.exist?(str)
-
# end
-
#
-
# driver.find_element(:id => "upload").send_keys "/path/to/file"
-
#
-
# By default, no file detection is performed.
-
#
-
# @api public
-
#
-
-
1
def file_detector=(detector)
-
unless detector.nil? or detector.respond_to? :call
-
raise ArgumentError, "detector must respond to #call"
-
end
-
-
bridge.file_detector = detector
-
end
-
-
end # UploadsFiles
-
end # DriverExtensions
-
end # WebDriver
-
end # Selenium
-
1
module Selenium
-
1
module WebDriver
-
1
class Element
-
1
include SearchContext
-
-
#
-
# Creates a new Element
-
#
-
# @api private
-
#
-
-
1
def initialize(bridge, id)
-
@bridge, @id = bridge, id
-
end
-
-
1
def inspect
-
'#<%s:0x%x id=%s>' % [self.class, hash*2, @id.inspect]
-
end
-
-
1
def ==(other)
-
other.kind_of?(self.class) && bridge.elementEquals(self, other)
-
end
-
1
alias_method :eql?, :==
-
-
1
def hash
-
@id.hash ^ @bridge.hash
-
end
-
-
#
-
# Click this element. If this causes a new page to load, this method will
-
# attempt to block until the page has loaded. At this point, you should
-
# discard all references to this element and any further operations
-
# performed on this element will raise a StaleElementReferenceError
-
# unless you know that the element and the page will still be present. If
-
# click() causes a new page to be loaded via an event or is done by
-
# sending a native event then the method will *not* wait for it to be
-
# loaded and the caller should verify that a new page has been loaded.
-
#
-
# There are some preconditions for an element to be clicked. The element
-
# must be visible and it must have a height and width greater then 0.
-
#
-
# Equivalent to:
-
# driver.action.click(element)
-
#
-
# @example Click on a button
-
#
-
# driver.find_element(:tag_name, "button").click
-
#
-
# @raise [StaleElementReferenceError] if the element no longer exists as
-
# defined
-
#
-
-
1
def click
-
bridge.clickElement @id
-
end
-
-
#
-
# Get the tag name of the element.
-
#
-
# @example Get the tagname of an INPUT element(returns "input")
-
#
-
# driver.find_element(:xpath, "//input").tag_name
-
#
-
# @return [String] The tag name of this element.
-
#
-
-
1
def tag_name
-
bridge.getElementTagName @id
-
end
-
-
#
-
# Get the value of a the given attribute of the element. Will return the current value, even if
-
# this has been modified after the page has been loaded. More exactly, this method will return
-
# the value of the given attribute, unless that attribute is not present, in which case the
-
# value of the property with the same name is returned. If neither value is set, nil is
-
# returned. The "style" attribute is converted as best can be to a text representation with a
-
# trailing semi-colon. The following are deemed to be "boolean" attributes, and will
-
# return either "true" or "false":
-
#
-
# async, autofocus, autoplay, checked, compact, complete, controls, declare, defaultchecked,
-
# defaultselected, defer, disabled, draggable, ended, formnovalidate, hidden, indeterminate,
-
# iscontenteditable, ismap, itemscope, loop, multiple, muted, nohref, noresize, noshade, novalidate,
-
# nowrap, open, paused, pubdate, readonly, required, reversed, scoped, seamless, seeking,
-
# selected, spellcheck, truespeed, willvalidate
-
#
-
# Finally, the following commonly mis-capitalized attribute/property names are evaluated as
-
# expected:
-
#
-
# class, readonly
-
#
-
# @param [String]
-
# attribute name
-
# @return [String,nil]
-
# attribute value
-
#
-
-
1
def attribute(name)
-
bridge.getElementAttribute @id, name
-
end
-
-
#
-
# Get the text content of this element
-
#
-
# @return [String]
-
#
-
-
1
def text
-
bridge.getElementText @id
-
end
-
-
#
-
# Send keystrokes to this element
-
#
-
# @param [String, Symbol, Array]
-
#
-
# Examples:
-
#
-
# element.send_keys "foo" #=> value: 'foo'
-
# element.send_keys "tet", :arrow_left, "s" #=> value: 'test'
-
# element.send_keys [:control, 'a'], :space #=> value: ' '
-
#
-
# @see Keys::KEYS
-
#
-
-
1
def send_keys(*args)
-
bridge.sendKeysToElement @id, Keys.encode(args)
-
end
-
1
alias_method :send_key, :send_keys
-
-
#
-
# If this element is a text entry element, this will clear the value. Has no effect on other
-
# elements. Text entry elements are INPUT and TEXTAREA elements.
-
#
-
# Note that the events fired by this event may not be as you'd expect. In particular, we don't
-
# fire any keyboard or mouse events. If you want to ensure keyboard events are
-
# fired, consider using #send_keys with the backspace key. To ensure you get a change event,
-
# consider following with a call to #send_keys with the tab key.
-
#
-
-
1
def clear
-
bridge.clearElement @id
-
end
-
-
#
-
# Is the element enabled?
-
#
-
# @return [Boolean]
-
#
-
-
1
def enabled?
-
bridge.isElementEnabled @id
-
end
-
-
#
-
# Is the element selected?
-
#
-
# @return [Boolean]
-
#
-
-
1
def selected?
-
bridge.isElementSelected @id
-
end
-
-
#
-
# Is the element displayed?
-
#
-
# @return [Boolean]
-
#
-
-
1
def displayed?
-
bridge.isElementDisplayed @id
-
end
-
-
#
-
# Submit this element
-
#
-
-
1
def submit
-
bridge.submitElement @id
-
end
-
-
#
-
# Get the value of the given CSS property
-
#
-
# Note that shorthand CSS properties (e.g. background, font, border, border-top, margin,
-
# margin-top, padding, padding-top, list-style, outline, pause, cue) are not returned,
-
# in accordance with the DOM CSS2 specification - you should directly access the longhand
-
# properties (e.g. background-color) to access the desired values.
-
#
-
# @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration
-
#
-
-
1
def css_value(prop)
-
bridge.getElementValueOfCssProperty @id, prop
-
end
-
1
alias_method :style, :css_value
-
-
#
-
# Get the location of this element.
-
#
-
# @return [WebDriver::Point]
-
#
-
-
1
def location
-
bridge.getElementLocation @id
-
end
-
-
#
-
# Determine an element's location on the screen once it has been scrolled into view.
-
#
-
# @return [WebDriver::Point]
-
#
-
-
1
def location_once_scrolled_into_view
-
bridge.getElementLocationOnceScrolledIntoView @id
-
end
-
-
#
-
# Get the size of this element
-
#
-
# @return [WebDriver::Dimension]
-
#
-
-
1
def size
-
bridge.getElementSize @id
-
end
-
-
#-------------------------------- sugar --------------------------------
-
-
#
-
# element.first(:id, 'foo')
-
#
-
-
1
alias_method :first, :find_element
-
-
#
-
# element.all(:class, 'bar')
-
#
-
-
1
alias_method :all, :find_elements
-
-
#
-
# element['class'] or element[:class] #=> "someclass"
-
#
-
1
alias_method :[], :attribute
-
-
#
-
# for SearchContext and execute_script
-
#
-
# @api private
-
#
-
-
1
def ref
-
@id
-
end
-
-
#
-
# Convert to a WebElement JSON Object for transmission over the wire.
-
# @see http://code.google.com/p/selenium/wiki/JsonWireProtocol#Basic_Concepts_And_Terms
-
#
-
# @api private
-
#
-
-
1
def to_json(*args)
-
WebDriver.json_dump as_json
-
end
-
-
#
-
# For Rails 3 - http://jonathanjulian.com/2010/04/rails-to_json-or-as_json/
-
#
-
# @api private
-
#
-
-
1
def as_json(opts = nil)
-
{ :ELEMENT => @id }
-
end
-
-
1
private
-
-
1
def bridge
-
@bridge
-
end
-
-
1
def selectable?
-
tn = tag_name.downcase
-
type = attribute(:type).to_s.downcase
-
-
tn == "option" || (tn == "input" && %w[radio checkbox].include?(type))
-
end
-
-
end # Element
-
end # WebDriver
-
end # Selenium
-
1
module Selenium
-
1
module WebDriver
-
1
module Error
-
-
1
class WebDriverError < StandardError; end
-
1
class UnsupportedOperationError < WebDriverError; end
-
-
1
class IndexOutOfBoundsError < WebDriverError; end # 1
-
1
class NoCollectionError < WebDriverError; end # 2
-
1
class NoStringError < WebDriverError; end # 3
-
1
class NoStringLengthError < WebDriverError; end # 4
-
1
class NoStringWrapperError < WebDriverError; end # 5
-
1
class NoSuchDriverError < WebDriverError; end # 6
-
-
#
-
# An element could not be located on the page using the given search
-
# parameters.
-
#
-
-
1
class NoSuchElementError < WebDriverError; end # 7
-
-
#
-
# A request to switch to a frame could not be satisfied because the
-
# frame could not be found.
-
#
-
-
1
class NoSuchFrameError < WebDriverError; end # 8
-
1
class UnknownCommandError < WebDriverError; end # 9
-
-
-
#
-
# Indicates that a reference to an element is now "stale" - the element
-
# no longer appears in the DOM of the page.
-
#
-
-
1
class StaleElementReferenceError < WebDriverError; end # 10
-
-
#
-
# Raised to indicate that although an element is present on the DOM,
-
# it is not visible, and so is not able to be interacted with.
-
#
-
-
1
class ElementNotVisibleError < WebDriverError; end # 11
-
-
#
-
# Raised when an interaction could not be performed because the element
-
# is in an invalid state (e.g. attempting to click a disabled element).
-
#
-
-
1
class InvalidElementStateError < WebDriverError; end # 12
-
-
#
-
# An unknown server-side error occurred while processing the command.
-
#
-
-
1
class UnknownError < WebDriverError; end # 13
-
1
class ExpectedError < WebDriverError; end # 14
-
-
#
-
# An attempt was made to select an element that cannot be selected.
-
#
-
-
1
class ElementNotSelectableError < WebDriverError; end # 15
-
1
class NoSuchDocumentError < WebDriverError; end # 16
-
-
#
-
# An error occurred while executing user supplied JavaScript.
-
#
-
-
1
class JavascriptError < WebDriverError; end # 17
-
1
class NoScriptResultError < WebDriverError; end # 18
-
-
#
-
# An error occurred while searching for an element by XPath.
-
#
-
-
1
class XPathLookupError < WebDriverError; end # 19
-
1
class NoSuchCollectionError < WebDriverError; end # 20
-
-
#
-
# Raised when a command does not complete in enough time.
-
#
-
-
1
class TimeOutError < WebDriverError; end # 21
-
1
class NullPointerError < WebDriverError; end # 22
-
1
class NoSuchWindowError < WebDriverError; end # 23
-
-
#
-
# Raised when attempting to add a cookie under a different domain than
-
# the current URL.
-
#
-
-
1
class InvalidCookieDomainError < WebDriverError; end # 24
-
-
#
-
# Raised when a driver fails to set a cookie.
-
#
-
-
1
class UnableToSetCookieError < WebDriverError; end # 25
-
-
#
-
# Raised when an alert dialog is present that has not been dealt with.
-
#
-
1
class UnhandledAlertError < WebDriverError; end # 26
-
-
#
-
# Indicates that a user has tried to access an alert when one is not present.
-
#
-
-
1
class NoAlertPresentError < WebDriverError; end # 27
-
-
#
-
# A script did not complete before its timeout expired.
-
#
-
-
1
class ScriptTimeOutError < WebDriverError; end # 28
-
-
#
-
# The coordinates provided to an interactions operation are invalid.
-
#
-
-
1
class InvalidElementCoordinatesError < WebDriverError; end # 29
-
-
#
-
# Indicates that IME support is not available. This exception is rasied
-
# for every IME-related method call if IME support is not available on
-
# the machine.
-
#
-
-
1
class IMENotAvailableError < WebDriverError; end # 30
-
-
#
-
# Indicates that activating an IME engine has failed.
-
#
-
-
1
class IMEEngineActivationFailedError < WebDriverError; end # 31
-
-
#
-
# Argument was an invalid selector (e.g. XPath/CSS).
-
#
-
-
1
class InvalidSelectorError < WebDriverError; end # 32
-
# 33
-
-
#
-
# Indicates that the target provided to the actions #move method is
-
# invalid, e.g. outside of the bounds of the window.
-
#
-
-
1
class MoveTargetOutOfBoundsError < WebDriverError; end # 34
-
-
# @api private
-
1
Errors = [
-
IndexOutOfBoundsError, # 1
-
NoCollectionError, # 2
-
NoStringError, # 3
-
NoStringLengthError, # 4
-
NoStringWrapperError, # 5
-
NoSuchDriverError, # 6
-
NoSuchElementError, # 7
-
NoSuchFrameError, # 8
-
UnknownCommandError, # 9
-
StaleElementReferenceError, # 10
-
ElementNotVisibleError, # 11
-
InvalidElementStateError, # 12
-
UnknownError, # 13
-
ExpectedError, # 14
-
ElementNotSelectableError, # 15
-
NoSuchDocumentError, # 16
-
JavascriptError, # 17
-
NoScriptResultError, # 18
-
XPathLookupError, # 19
-
NoSuchCollectionError, # 20
-
TimeOutError, # 21
-
NullPointerError, # 22
-
NoSuchWindowError, # 23
-
InvalidCookieDomainError, # 24
-
UnableToSetCookieError, # 25
-
UnhandledAlertError, # 26
-
NoAlertPresentError, # 27
-
ScriptTimeOutError, # 28
-
InvalidElementCoordinatesError, # 29
-
IMENotAvailableError, # 30
-
IMEEngineActivationFailedError, # 31
-
InvalidSelectorError, # 32
-
nil, # 33
-
MoveTargetOutOfBoundsError # 34
-
]
-
-
# aliased for backwards compatibility
-
1
ObsoleteElementError = StaleElementReferenceError
-
1
UnhandledError = UnknownError
-
1
UnexpectedJavascriptError = JavascriptError
-
1
NoAlertOpenError = NoAlertPresentError
-
1
ElementNotDisplayedError = ElementNotVisibleError
-
-
1
class << self
-
1
def for_code(code)
-
return if [nil, 0].include? code
-
-
Errors[code - 1] || WebDriverError
-
end
-
end
-
-
end # Error
-
end # WebDriver
-
end # Selenium
-
1
module Selenium
-
1
module WebDriver
-
-
#
-
# @api private
-
#
-
-
1
module FileReaper
-
-
1
class << self
-
1
def reap=(bool)
-
@reap = bool
-
end
-
-
1
def reap?
-
1
@reap = true unless defined?(@reap)
-
1
!!@reap
-
end
-
-
1
def tmp_files
-
2
@tmp_files ||= Hash.new { |hash, pid| hash[pid] = [] }
-
1
@tmp_files[Process.pid]
-
end
-
-
1
def <<(file)
-
tmp_files << file
-
end
-
-
1
def reap(file)
-
return unless reap?
-
-
unless tmp_files.include?(file)
-
raise Error::WebDriverError, "file not added for reaping: #{file.inspect}"
-
end
-
-
FileUtils.rm_rf tmp_files.delete(file)
-
end
-
-
1
def reap!
-
1
if reap?
-
1
tmp_files.each { |file| FileUtils.rm_rf(file) }
-
1
true
-
else
-
false
-
end
-
end
-
end
-
-
# we *do* want child process reaping, so not using Platform.exit_hook here.
-
2
at_exit { reap! }
-
-
end # FileReaper
-
end # WebDriver
-
end # Selenium
-
1
module Selenium
-
1
module WebDriver
-
1
module HTML5
-
-
1
class LocalStorage
-
1
include SharedWebStorage
-
-
#
-
# @api private
-
#
-
1
def initialize(bridge)
-
@bridge = bridge
-
end
-
-
1
def [](key)
-
@bridge.getLocalStorageItem key
-
end
-
-
1
def []=(key, value)
-
@bridge.setLocalStorageItem key, value
-
end
-
-
1
def delete(key)
-
@bridge.removeLocalStorageItem key
-
end
-
-
1
def clear
-
@bridge.clearLocalStorage
-
end
-
-
1
def size
-
@bridge.getLocalStorageSize
-
end
-
-
1
def keys
-
@bridge.getLocalStorageKeys.reverse
-
end
-
end
-
-
end
-
end
-
end
-
1
module Selenium
-
1
module WebDriver
-
1
module HTML5
-
-
1
class SessionStorage
-
1
include Enumerable
-
1
include SharedWebStorage
-
-
1
def [](key)
-
@bridge.getSessionStorageItem key
-
end
-
-
1
def []=(key, value)
-
@bridge.setSessionStorageItem key, value
-
end
-
-
1
def delete(key)
-
@bridge.removeSessionStorageItem key
-
end
-
-
1
def clear
-
@bridge.clearSessionStorage
-
end
-
-
1
def size
-
@bridge.getSessionStorageSize
-
end
-
-
1
def keys
-
@bridge.getSessionStorageKeys.reverse
-
end
-
-
#
-
# @api private
-
#
-
-
1
def initialize(bridge)
-
@bridge = bridge
-
end
-
end
-
-
end
-
end
-
end
-
1
module Selenium
-
1
module WebDriver
-
1
module HTML5
-
-
1
module SharedWebStorage
-
1
include Enumerable
-
-
1
def key?(key)
-
keys.include? key
-
end
-
1
alias_method :member?, :key?
-
1
alias_method :has_key?, :key?
-
-
1
def fetch(key, &blk)
-
if self.key? key
-
return self[key]
-
end
-
-
if block_given?
-
yield key
-
else
-
# should be KeyError, but it's 1.9-specific
-
raise IndexError, "missing key #{key.inspect}"
-
end
-
end
-
-
1
def empty?
-
size == 0
-
end
-
-
1
def each(&blk)
-
return enum_for(:each) unless block_given?
-
-
keys.each do |k|
-
yield k, self[k]
-
end
-
end
-
end
-
-
end
-
end
-
end
-
1
module Selenium
-
1
module WebDriver
-
1
module JsonHelper
-
-
# ActiveSupport may define Object#load, so we can't use MultiJson.respond_to? here
-
26
sm = MultiJson.singleton_methods.map { |e| e.to_sym }
-
-
1
if sm.include? :load
-
# @api private
-
1
def json_load(obj)
-
MultiJson.load(obj)
-
end
-
else
-
# @api private
-
def json_load(obj)
-
MultiJson.decode(obj)
-
end
-
end
-
-
1
if sm.include? :dump
-
# @api private
-
1
def json_dump(obj)
-
MultiJson.dump(obj)
-
end
-
else
-
# @api private
-
def json_dump(obj)
-
MultiJson.encode(obj)
-
end
-
end
-
-
end
-
end
-
end
-
1
module Selenium
-
1
module WebDriver
-
-
#
-
# @api private
-
# @see ActionBuilder
-
-
1
class Keyboard
-
-
1
def initialize(bridge)
-
@bridge = bridge
-
end
-
-
1
def send_keys(*keys)
-
@bridge.sendKeysToActiveElement Keys.encode(keys)
-
end
-
-
#
-
# Press a modifier key
-
#
-
# @see Selenium::WebDriver::Keys
-
#
-
-
1
def press(key)
-
assert_modifier key
-
-
@bridge.sendKeysToActiveElement Keys.encode([key])
-
end
-
-
#
-
# Release a modifier key
-
#
-
# @see Selenium::WebDriver::Keys
-
#
-
-
1
def release(key)
-
assert_modifier key
-
-
@bridge.sendKeysToActiveElement Keys.encode([key])
-
end
-
-
1
private
-
-
1
MODIFIERS = [:control, :shift, :alt, :command, :meta]
-
-
1
def assert_modifier(key)
-
unless MODIFIERS.include? key
-
raise ArgumentError,
-
"#{key.inspect} is not a modifier key, expected one of #{MODIFIERS.inspect}"
-
end
-
end
-
-
end # Keyboard
-
end # WebDriver
-
end # Selenium
-
# encoding: utf-8
-
-
1
module Selenium
-
1
module WebDriver
-
1
module Keys
-
-
#
-
# @see Element#send_keys
-
# @see http://www.google.com.au/search?&q=unicode+pua&btnG=Search
-
#
-
-
1
KEYS = {
-
# \x works on both 1.8.6/1.9
-
:null => "\xEE\x80\x80",
-
:cancel => "\xEE\x80\x81",
-
:help => "\xEE\x80\x82",
-
:backspace => "\xEE\x80\x83",
-
:tab => "\xEE\x80\x84",
-
:clear => "\xEE\x80\x85",
-
:return => "\xEE\x80\x86",
-
:enter => "\xEE\x80\x87",
-
:shift => "\xEE\x80\x88",
-
:left_shift => "\xEE\x80\x88",
-
:control => "\xEE\x80\x89",
-
:left_control => "\xEE\x80\x89",
-
:alt => "\xEE\x80\x8A",
-
:left_alt => "\xEE\x80\x8A",
-
:pause => "\xEE\x80\x8B",
-
:escape => "\xEE\x80\x8C",
-
:space => "\xEE\x80\x8D",
-
:page_up => "\xEE\x80\x8E",
-
:page_down => "\xEE\x80\x8F",
-
:end => "\xEE\x80\x90",
-
:home => "\xEE\x80\x91",
-
:left => "\xEE\x80\x92",
-
:arrow_left => "\xEE\x80\x92",
-
:up => "\xEE\x80\x93",
-
:arrow_up => "\xEE\x80\x93",
-
:right => "\xEE\x80\x94",
-
:arrow_right => "\xEE\x80\x94",
-
:down => "\xEE\x80\x95",
-
:arrow_down => "\xEE\x80\x95",
-
:insert => "\xEE\x80\x96",
-
:delete => "\xEE\x80\x97",
-
:semicolon => "\xEE\x80\x98",
-
:equals => "\xEE\x80\x99",
-
:numpad0 => "\xEE\x80\x9A",
-
:numpad1 => "\xEE\x80\x9B",
-
:numpad2 => "\xEE\x80\x9C",
-
:numpad3 => "\xEE\x80\x9D",
-
:numpad4 => "\xEE\x80\x9E",
-
:numpad5 => "\xEE\x80\x9F",
-
:numpad6 => "\xEE\x80\xA0",
-
:numpad7 => "\xEE\x80\xA1",
-
:numpad8 => "\xEE\x80\xA2",
-
:numpad9 => "\xEE\x80\xA3",
-
:multiply => "\xEE\x80\xA4",
-
:add => "\xEE\x80\xA5",
-
:separator => "\xEE\x80\xA6",
-
:subtract => "\xEE\x80\xA7",
-
:decimal => "\xEE\x80\xA8",
-
:divide => "\xEE\x80\xA9",
-
:f1 => "\xEE\x80\xB1",
-
:f2 => "\xEE\x80\xB2",
-
:f3 => "\xEE\x80\xB3",
-
:f4 => "\xEE\x80\xB4",
-
:f5 => "\xEE\x80\xB5",
-
:f6 => "\xEE\x80\xB6",
-
:f7 => "\xEE\x80\xB7",
-
:f8 => "\xEE\x80\xB8",
-
:f9 => "\xEE\x80\xB9",
-
:f10 => "\xEE\x80\xBA",
-
:f11 => "\xEE\x80\xBB",
-
:f12 => "\xEE\x80\xBC",
-
:meta => "\xEE\x80\xBD",
-
:command => "\xEE\x80\xBD" # alias
-
}
-
-
#
-
# @api private
-
#
-
-
1
def self.[](key)
-
KEYS[key] or raise Error::UnsupportedOperationError, "no such key #{key.inspect}"
-
end
-
-
#
-
# @api private
-
#
-
-
1
def self.encode(keys)
-
keys.map do |arg|
-
case arg
-
when Symbol
-
Keys[arg]
-
when Array
-
arg = arg.map { |e| e.kind_of?(Symbol) ? Keys[e] : e }.join
-
arg << Keys[:null]
-
-
arg
-
else
-
arg.to_s
-
end
-
end
-
end
-
-
end # Keys
-
end # WebDriver
-
end # Selenium
-
1
module Selenium
-
1
module WebDriver
-
1
class LogEntry
-
1
attr_reader :level, :timestamp, :message
-
-
1
def initialize(level, timestamp, message)
-
@level = level
-
@timestamp = timestamp
-
@message = message
-
end
-
-
1
def as_json(opts = nil)
-
{
-
'level' => level,
-
'timestamp' => timestamp,
-
'message' => message
-
}
-
end
-
-
1
def to_s
-
"#{level} #{time}: #{message}"
-
end
-
-
1
def time
-
Time.at timestamp / 1000
-
end
-
end
-
end
-
end
-
1
module Selenium
-
1
module WebDriver
-
1
class Logs
-
-
#
-
# @api private
-
#
-
-
1
def initialize(bridge)
-
@bridge = bridge
-
end
-
-
1
def get(type)
-
@bridge.getLog type
-
end
-
-
1
def available_types
-
@bridge.getAvailableLogTypes
-
end
-
-
end
-
end
-
end
-
1
module Selenium
-
1
module WebDriver
-
-
#
-
# @api private
-
# @see ActionBuilder
-
#
-
-
1
class Mouse
-
-
1
def initialize(bridge)
-
@bridge = bridge
-
end
-
-
1
def click(element = nil)
-
move_if_needed element
-
@bridge.click
-
end
-
-
1
def double_click(element = nil)
-
move_if_needed element
-
@bridge.doubleClick
-
end
-
-
1
def context_click(element = nil)
-
move_if_needed element
-
@bridge.contextClick
-
end
-
-
1
def down(element = nil)
-
move_if_needed element
-
@bridge.mouseDown
-
end
-
-
1
def up(element = nil)
-
move_if_needed element
-
@bridge.mouseUp
-
end
-
-
#
-
# Move the mouse.
-
#
-
# Examples:
-
#
-
# driver.mouse.move_to(element)
-
# driver.mouse.move_to(element, 5, 5)
-
#
-
-
1
def move_to(element, right_by = nil, down_by = nil)
-
assert_element element
-
-
@bridge.mouseMoveTo element.ref, right_by, down_by
-
end
-
-
1
def move_by(right_by, down_by)
-
@bridge.mouseMoveTo nil, Integer(right_by), Integer(down_by)
-
end
-
-
1
private
-
-
1
def move_if_needed(element)
-
move_to element if element
-
end
-
-
1
def assert_element(element)
-
unless element.kind_of? Element
-
raise TypeError, "expected #{Element}, got #{element.inspect}:#{element.class}"
-
end
-
end
-
end # Mouse
-
end # WebDriver
-
end # Selenium
-
1
module Selenium
-
1
module WebDriver
-
1
class Navigation
-
-
1
def initialize(bridge)
-
@bridge = bridge
-
end
-
-
#
-
# Navigate to the given URL
-
#
-
-
1
def to(url)
-
@bridge.get url
-
end
-
-
#
-
# Move back a single entry in the browser's history.
-
#
-
-
1
def back
-
@bridge.goBack
-
end
-
-
#
-
# Move forward a single entry in the browser's history.
-
#
-
-
1
def forward
-
@bridge.goForward
-
end
-
-
#
-
# Refresh the current page.
-
#
-
-
1
def refresh
-
@bridge.refresh
-
end
-
-
end # Navigation
-
end # WebDriver
-
end # Selenium
-
1
module Selenium
-
1
module WebDriver
-
1
class Options
-
-
#
-
# @api private
-
#
-
-
1
def initialize(bridge)
-
@bridge = bridge
-
end
-
-
#
-
# Add a cookie to the browser
-
#
-
# @param [Hash] opts the options to create a cookie with.
-
# @option opts [String] :name A name
-
# @option opts [String] :value A value
-
# @option opts [String] :path ('/') A path
-
# @option opts [String] :secure (false) A boolean
-
# @option opts [Time,DateTime,Numeric,nil] :expires (nil) Expiry date, either as a Time, DateTime, or seconds since epoch.
-
#
-
# @raise [ArgumentError] if :name or :value is not specified
-
#
-
-
1
def add_cookie(opts = {})
-
raise ArgumentError, "name is required" unless opts[:name]
-
raise ArgumentError, "value is required" unless opts[:value]
-
-
opts[:path] ||= "/"
-
opts[:secure] ||= false
-
-
if obj = opts.delete(:expires)
-
opts[:expiry] = seconds_from(obj)
-
end
-
-
-
@bridge.addCookie opts
-
end
-
-
#
-
# Get the cookie with the given name
-
#
-
# @param [String] name the name of the cookie
-
# @return [Hash, nil] the cookie, or nil if it wasn't found.
-
#
-
-
1
def cookie_named(name)
-
all_cookies.find { |c| c[:name] == name }
-
end
-
-
#
-
# Delete the cookie with the given name
-
#
-
# @param [String] name the name of the cookie to delete
-
#
-
-
1
def delete_cookie(name)
-
@bridge.deleteCookie name
-
end
-
-
#
-
# Delete all cookies
-
#
-
-
1
def delete_all_cookies
-
@bridge.deleteAllCookies
-
end
-
-
#
-
# Get all cookies
-
#
-
# @return [Array<Hash>] list of cookies
-
#
-
-
1
def all_cookies
-
@bridge.getAllCookies.map do |cookie|
-
{
-
:name => cookie["name"],
-
:value => cookie["value"],
-
:path => cookie["path"],
-
:domain => cookie["domain"] && strip_port(cookie["domain"]),
-
:expires => cookie["expiry"] && datetime_at(cookie['expiry']),
-
:secure => cookie["secure"]
-
}
-
end
-
end
-
-
1
def timeouts
-
@timeouts ||= Timeouts.new(@bridge)
-
end
-
-
#
-
# @api beta This API may be changed or removed in a future release.
-
#
-
-
1
def logs
-
@logs ||= Logs.new(@bridge)
-
end
-
-
#
-
# @api beta This API may be changed or removed in a future release.
-
#
-
-
1
def window
-
@window ||= Window.new(@bridge)
-
end
-
-
1
private
-
-
1
SECONDS_PER_DAY = 86_400.0
-
-
1
def datetime_at(int)
-
DateTime.civil(1970) + (int / SECONDS_PER_DAY)
-
end
-
-
1
def seconds_from(obj)
-
case obj
-
when Time
-
obj.to_f
-
when DateTime
-
(obj - DateTime.civil(1970)) * SECONDS_PER_DAY
-
when Numeric
-
obj
-
else
-
raise ArgumentError, "invalid value for expiration date: #{obj.inspect}"
-
end
-
end
-
-
1
def strip_port(str)
-
str.split(":", 2).first
-
end
-
-
end # Options
-
end # WebDriver
-
end # Selenium
-
1
require 'rbconfig'
-
1
require 'socket'
-
-
1
module Selenium
-
1
module WebDriver
-
-
# @api private
-
1
module Platform
-
-
1
module_function
-
-
1
def home
-
# jruby has an issue with ENV['HOME'] on Windows
-
@home ||= jruby? ? ENV_JAVA['user.home'] : ENV['HOME']
-
end
-
-
1
def engine
-
@engine ||= (
-
1
if defined? RUBY_ENGINE
-
1
RUBY_ENGINE.to_sym
-
else
-
:ruby
-
end
-
1
)
-
end
-
-
1
def os
-
@os ||= (
-
1
host_os = RbConfig::CONFIG['host_os']
-
1
case host_os
-
when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
-
:windows
-
when /darwin|mac os/
-
1
:macosx
-
when /linux/
-
:linux
-
when /solaris|bsd/
-
:unix
-
else
-
raise Error::WebDriverError, "unknown os: #{host_os.inspect}"
-
end
-
1
)
-
end
-
-
1
def bitsize
-
@bitsize ||= (
-
if defined?(FFI::Platform::ADDRESS_SIZE)
-
FFI::Platform::ADDRESS_SIZE
-
elsif defined?(FFI)
-
FFI.type_size(:pointer) == 4 ? 32 : 64
-
elsif jruby?
-
Integer(ENV_JAVA['sun.arch.data.model'])
-
else
-
1.size == 4 ? 32 : 64
-
end
-
)
-
end
-
-
1
def jruby?
-
1
engine == :jruby
-
end
-
-
1
def ironruby?
-
engine == :ironruby
-
end
-
-
1
def ruby187?
-
!!(RUBY_VERSION =~ /^1\.8\.7/)
-
end
-
-
1
def ruby19?
-
!!(RUBY_VERSION =~ /^1\.9/)
-
end
-
-
1
def windows?
-
1
os == :windows
-
end
-
-
1
def mac?
-
os == :macosx
-
end
-
-
1
def linux?
-
os == :linux
-
end
-
-
1
def cygwin?
-
2
!!(RUBY_PLATFORM =~ /cygwin/)
-
end
-
-
1
def null_device
-
@null_device ||= (
-
if defined?(File::NULL)
-
File::NULL
-
else
-
Platform.windows? ? 'NUL' : '/dev/null'
-
end
-
)
-
end
-
-
1
def wrap_in_quotes_if_necessary(str)
-
windows? && !cygwin? ? %{"#{str}"} : str
-
end
-
-
1
def cygwin_path(path, opts = {})
-
flags = []
-
opts.each { |k,v| flags << "--#{k}" if v }
-
-
`cygpath #{flags.join ' '} "#{path}"`.strip
-
end
-
-
1
def make_writable(file)
-
File.chmod 0766, file
-
end
-
-
1
def assert_file(path)
-
unless File.file? path
-
raise Error::WebDriverError, "not a file: #{path.inspect}"
-
end
-
end
-
-
1
def assert_executable(path)
-
assert_file(path)
-
-
unless File.executable? path
-
raise Error::WebDriverError, "not executable: #{path.inspect}"
-
end
-
end
-
-
1
def exit_hook(&blk)
-
pid = Process.pid
-
-
at_exit do
-
yield if Process.pid == pid
-
end
-
end
-
-
1
def find_binary(*binary_names)
-
paths = ENV['PATH'].split(File::PATH_SEPARATOR)
-
binary_names.map! { |n| "#{n}.exe" } if windows?
-
-
binary_names.each do |binary_name|
-
paths.each do |path|
-
exe = File.join(path, binary_name)
-
return exe if File.executable?(exe)
-
end
-
end
-
-
nil
-
end
-
-
1
def find_in_program_files(*binary_names)
-
paths = [
-
ENV['PROGRAMFILES'] || "\\Program Files",
-
ENV['ProgramFiles(x86)'] || "\\Program Files (x86)"
-
]
-
-
paths.each do |root|
-
binary_names.each do |name|
-
exe = File.join(root, name)
-
return exe if File.executable?(exe)
-
end
-
end
-
-
nil
-
end
-
-
1
def localhost
-
info = Socket.getaddrinfo "localhost", 80, Socket::AF_INET, Socket::SOCK_STREAM
-
-
if info.empty?
-
raise Error::WebDriverError, "unable to translate 'localhost' for TCP + IPv4"
-
end
-
-
info[0][3]
-
end
-
-
1
def ip
-
orig = Socket.do_not_reverse_lookup
-
Socket.do_not_reverse_lookup = true
-
-
begin
-
UDPSocket.open do |s|
-
s.connect '8.8.8.8', 53
-
return s.addr.last
-
end
-
ensure
-
Socket.do_not_reverse_lookup = orig
-
end
-
rescue Errno::ENETUNREACH, Errno::EHOSTUNREACH
-
# no external ip
-
end
-
-
1
def interfaces
-
interfaces = Socket.getaddrinfo("localhost", 8080).map { |e| e[3] }
-
interfaces += ["0.0.0.0", Platform.ip]
-
-
interfaces.compact.uniq
-
end
-
-
end # Platform
-
end # WebDriver
-
end # Selenium
-
-
1
if __FILE__ == $0
-
p :engine => Selenium::WebDriver::Platform.engine,
-
:os => Selenium::WebDriver::Platform.os,
-
:ruby187? => Selenium::WebDriver::Platform.ruby187?,
-
:ruby19? => Selenium::WebDriver::Platform.ruby19?,
-
:jruby? => Selenium::WebDriver::Platform.jruby?,
-
:windows? => Selenium::WebDriver::Platform.windows?,
-
:home => Selenium::WebDriver::Platform.home,
-
:bitsize => Selenium::WebDriver::Platform.bitsize,
-
:localhost => Selenium::WebDriver::Platform.localhost,
-
:ip => Selenium::WebDriver::Platform.ip,
-
:interfaces => Selenium::WebDriver::Platform.interfaces,
-
:null_device => Selenium::WebDriver::Platform.null_device
-
end
-
1
module Selenium
-
1
module WebDriver
-
1
class PortProber
-
1
def self.above(port)
-
port += 1 until free? port
-
port
-
end
-
-
1
def self.random
-
# TODO: Avoid this
-
#
-
# (a) should pick a port that's guaranteed to be free on all interfaces
-
# (b) should pick a random port outside the ephemeral port range
-
#
-
server = TCPServer.new(Platform.localhost, 0)
-
port = server.addr[1]
-
server.close
-
-
port
-
end
-
-
1
IGNORED_ERRORS = [Errno::EADDRNOTAVAIL]
-
1
IGNORED_ERRORS << Errno::EBADF if Platform.cygwin?
-
-
1
def self.free?(port)
-
Platform.interfaces.each do |host|
-
begin
-
TCPServer.new(host, port).close
-
rescue *IGNORED_ERRORS => ex
-
$stderr.puts "port prober could not bind to #{host}:#{port} (#{ex.message})" if $DEBUG
-
# ignored - some machines appear unable to bind to some of their interfaces
-
end
-
end
-
-
true
-
rescue SocketError, Errno::EADDRINUSE
-
false
-
end
-
-
end # PortProber
-
end # WebDriver
-
end # Selenium
-
1
module Selenium
-
1
module WebDriver
-
-
#
-
# @api private
-
#
-
# Common methods for Chrome::Profile and Firefox::Profile
-
# Includers must implement #layout_on_disk
-
#
-
-
1
module ProfileHelper
-
-
1
def self.included(base)
-
base.extend ClassMethods
-
end
-
-
1
def as_json(opts = nil)
-
{'zip' => Zipper.zip(layout_on_disk)}
-
end
-
-
1
def to_json(*args)
-
WebDriver.json_dump as_json
-
end
-
-
1
private
-
-
1
def create_tmp_copy(directory)
-
tmp_directory = Dir.mktmpdir("webdriver-rb-profilecopy")
-
-
# TODO: must be a better way..
-
FileUtils.rm_rf tmp_directory
-
FileUtils.mkdir_p File.dirname(tmp_directory), :mode => 0700
-
FileUtils.cp_r directory, tmp_directory
-
-
tmp_directory
-
end
-
-
1
def verify_model(model)
-
return unless model
-
-
raise Errno::ENOENT, model unless File.exist?(model)
-
raise Errno::ENOTDIR, model unless File.directory?(model)
-
-
model
-
end
-
-
1
module ClassMethods
-
1
def from_json(json)
-
data = WebDriver.json_load(json).fetch('zip')
-
-
# can't use Tempfile here since it doesn't support File::BINARY mode on 1.8
-
# can't use Dir.mktmpdir(&blk) because of http://jira.codehaus.org/browse/JRUBY-4082
-
tmp_dir = Dir.mktmpdir
-
begin
-
zip_path = File.join(tmp_dir, "webdriver-profile-duplicate-#{json.hash}.zip")
-
File.open(zip_path, "wb") { |zip_file| zip_file << Base64.decode64(data) }
-
-
new Zipper.unzip(zip_path)
-
ensure
-
FileUtils.rm_rf tmp_dir
-
end
-
end
-
end # ClassMethods
-
-
end # ProfileHelper
-
end # WebDriver
-
end # Selenium
-
1
module Selenium
-
1
module WebDriver
-
1
class Proxy
-
1
TYPES = {
-
:direct => "DIRECT", # Direct connection, no proxy (default on Windows).
-
:manual => "MANUAL", # Manual proxy settings (e.g., for httpProxy).
-
:pac => "PAC", # Proxy autoconfiguration from URL.
-
:auto_detect => "AUTODETECT", # Proxy autodetection (presumably with WPAD).
-
:system => "SYSTEM" # Use system settings (default on Linux).
-
}
-
-
1
attr_reader :type,
-
:ftp,
-
:http,
-
:socks,
-
:socks_username,
-
:socks_password,
-
:no_proxy,
-
:pac,
-
:ssl,
-
:auto_detect
-
-
1
def initialize(opts = {})
-
opts = opts.dup
-
-
self.type = opts.delete(:type) if opts.has_key? :type
-
self.ftp = opts.delete(:ftp) if opts.has_key? :ftp
-
self.http = opts.delete(:http) if opts.has_key? :http
-
self.no_proxy = opts.delete(:no_proxy) if opts.has_key? :no_proxy
-
self.ssl = opts.delete(:ssl) if opts.has_key? :ssl
-
self.pac = opts.delete(:pac) if opts.has_key? :pac
-
self.auto_detect = opts.delete(:auto_detect) if opts.has_key? :auto_detect
-
self.socks = opts.delete(:socks) if opts.has_key? :socks
-
self.socks_username = opts.delete(:socks_username) if opts.has_key? :socks_username
-
self.socks_password = opts.delete(:socks_password) if opts.has_key? :socks_password
-
-
unless opts.empty?
-
raise ArgumentError, "unknown option#{'s' if opts.size != 1}: #{opts.inspect}"
-
end
-
end
-
-
1
def ==(other)
-
other.kind_of?(self.class) && as_json == other.as_json
-
end
-
1
alias_method :eql?, :==
-
-
1
def ftp=(value)
-
self.type = :manual
-
@ftp = value
-
end
-
-
1
def http=(value)
-
self.type = :manual
-
@http = value
-
end
-
-
1
def no_proxy=(value)
-
self.type = :manual
-
@no_proxy = value
-
end
-
-
1
def ssl=(value)
-
self.type = :manual
-
@ssl = value
-
end
-
-
1
def pac=(url)
-
self.type = :pac
-
@pac = url
-
end
-
-
1
def auto_detect=(bool)
-
self.type = :auto_detect
-
@auto_detect = bool
-
end
-
-
1
def socks=(value)
-
self.type = :manual
-
@socks = value
-
end
-
-
1
def socks_username=(value)
-
self.type = :manual
-
@socks_username = value
-
end
-
-
1
def socks_password=(value)
-
self.type = :manual
-
@socks_password = value
-
end
-
-
1
def type=(type)
-
unless TYPES.has_key? type
-
raise ArgumentError, "invalid proxy type: #{type.inspect}, expected one of #{TYPES.keys.inspect}"
-
end
-
-
if defined?(@type) && type != @type
-
raise ArgumentError, "incompatible proxy type #{type.inspect} (already set to #{@type.inspect})"
-
end
-
-
@type = type
-
end
-
-
1
def as_json(opts = nil)
-
json_result = {
-
"proxyType" => TYPES[type]
-
}
-
-
json_result["ftpProxy"] = ftp if ftp
-
json_result["httpProxy"] = http if http
-
json_result["noProxy"] = no_proxy if no_proxy
-
json_result["proxyAutoconfigUrl"] = pac if pac
-
json_result["sslProxy"] = ssl if ssl
-
json_result["autodetect"] = auto_detect if auto_detect
-
json_result["socksProxy"] = socks if socks
-
json_result["socksUsername"] = socks_username if socks_username
-
json_result["socksPassword"] = socks_password if socks_password
-
-
json_result if json_result.length > 1
-
end
-
-
1
def to_json(*args)
-
WebDriver.json_dump as_json
-
end
-
-
1
class << self
-
1
def json_create(data)
-
return if data['proxyType'] == 'UNSPECIFIED'
-
-
proxy = new
-
-
proxy.type = data['proxyType'].downcase.to_sym if data.has_key? 'proxyType'
-
proxy.ftp = data['ftpProxy'] if data.has_key? 'ftpProxy'
-
proxy.http = data['httpProxy'] if data.has_key? 'httpProxy'
-
proxy.no_proxy = data['noProxy'] if data.has_key? 'noProxy'
-
proxy.pac = data['proxyAutoconfigUrl'] if data.has_key? 'proxyAutoconfigUrl'
-
proxy.ssl = data['sslProxy'] if data.has_key? 'sslProxy'
-
proxy.auto_detect = data['autodetect'] if data.has_key? 'autodetect'
-
proxy.socks = data['socksProxy'] if data.has_key? 'socksProxy'
-
proxy.socks_username = data['socksUsername'] if data.has_key? 'socksUsername'
-
proxy.socks_password = data['socksPassword'] if data.has_key? 'socksPassword'
-
-
proxy
-
end
-
end # class << self
-
-
end # Proxy
-
end # WebDriver
-
end # Selenium
-
1
module Selenium
-
1
module WebDriver
-
1
module SearchContext
-
-
# @api private
-
1
FINDERS = {
-
:class => 'class name',
-
:class_name => 'class name',
-
:css => 'css selector',
-
:id => 'id',
-
:link => 'link text',
-
:link_text => 'link text',
-
:name => 'name',
-
:partial_link_text => 'partial link text',
-
:tag_name => 'tag name',
-
:xpath => 'xpath',
-
}
-
-
#
-
# Find the first element matching the given arguments.
-
#
-
# When using Element#find_element with :xpath, be aware that webdriver
-
# follows standard conventions: a search prefixed with "//" will search
-
# the entire document, not just the children of this current node. Use
-
# ".//" to limit your search to the children of the receiving Element.
-
#
-
# @param [:class, :class_name, :css, :id, :link_text, :link, :partial_link_text, :name, :tag_name, :xpath] how
-
# @param [String] what
-
# @return [Element]
-
#
-
# @raise [Error::NoSuchElementError] if the element doesn't exist
-
#
-
#
-
-
1
def find_element(*args)
-
how, what = extract_args(args)
-
-
unless by = FINDERS[how.to_sym]
-
raise ArgumentError, "cannot find element by #{how.inspect}"
-
end
-
-
bridge.find_element_by by, what.to_s, ref
-
end
-
-
#
-
# Find all elements matching the given arguments
-
#
-
# @see SearchContext#find_element
-
#
-
# @param [:class, :class_name, :css, :id, :link_text, :link, :partial_link_text, :name, :tag_name, :xpath] how
-
# @param [String] what
-
# @return [Array<Element>]
-
#
-
-
1
def find_elements(*args)
-
how, what = extract_args(args)
-
-
unless by = FINDERS[how.to_sym]
-
raise ArgumentError, "cannot find elements by #{how.inspect}"
-
end
-
-
bridge.find_elements_by by, what.to_s, ref
-
end
-
-
1
private
-
-
1
def extract_args(args)
-
case args.size
-
when 2
-
args
-
when 1
-
arg = args.first
-
-
unless arg.respond_to?(:shift)
-
raise ArgumentError, "expected #{arg.inspect}:#{arg.class} to respond to #shift"
-
end
-
-
# this will be a single-entry hash, so use #shift over #first or #[]
-
arr = arg.dup.shift
-
unless arr.size == 2
-
raise ArgumentError, "expected #{arr.inspect} to have 2 elements"
-
end
-
-
arr
-
else
-
raise ArgumentError, "wrong number of arguments (#{args.size} for 2)"
-
end
-
end
-
-
end # SearchContext
-
end # WebDriver
-
end # Selenium
-
1
require 'selenium/webdriver/common/platform'
-
1
require 'socket'
-
-
1
module Selenium
-
1
module WebDriver
-
1
class SocketPoller
-
-
1
def initialize(host, port, timeout = 0, interval = 0.25)
-
@host = host
-
@port = Integer(port)
-
@timeout = Float(timeout)
-
@interval = interval
-
end
-
-
#
-
# Returns true if the server is listening within the given timeout,
-
# false otherwise.
-
#
-
# @return [Boolean]
-
#
-
-
1
def connected?
-
with_timeout { listening? }
-
end
-
-
#
-
# Returns true if the server has stopped listening within the given timeout,
-
# false otherwise.
-
#
-
# @return [Boolean]
-
#
-
-
1
def closed?
-
with_timeout { not listening? }
-
end
-
-
1
private
-
-
1
CONNECT_TIMEOUT = 5
-
-
1
NOT_CONNECTED_ERRORS = [Errno::ECONNREFUSED, Errno::ENOTCONN, SocketError]
-
1
NOT_CONNECTED_ERRORS << Errno::EPERM if Platform.cygwin?
-
-
1
CONNECTED_ERRORS = [Errno::EISCONN]
-
1
CONNECTED_ERRORS << Errno::EINVAL if Platform.windows?
-
-
1
if Platform.jruby?
-
# we use a plain TCPSocket here since JRuby has issues select()ing on a connecting socket
-
# see http://jira.codehaus.org/browse/JRUBY-5165
-
def listening?
-
TCPSocket.new(@host, @port).close
-
true
-
rescue *NOT_CONNECTED_ERRORS
-
false
-
end
-
else
-
1
def listening?
-
addr = Socket.getaddrinfo(@host, @port, Socket::AF_INET, Socket::SOCK_STREAM)
-
sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
-
sockaddr = Socket.pack_sockaddr_in(@port, addr[0][3])
-
-
begin
-
sock.connect_nonblock sockaddr
-
rescue Errno::EINPROGRESS
-
if IO.select(nil, [sock], nil, CONNECT_TIMEOUT)
-
retry
-
else
-
raise Errno::ECONNREFUSED
-
end
-
rescue *CONNECTED_ERRORS
-
# yay!
-
end
-
-
sock.close
-
true
-
rescue *NOT_CONNECTED_ERRORS
-
sock.close if sock
-
$stderr.puts [@host, @port].inspect if $DEBUG
-
false
-
end
-
end
-
-
1
def with_timeout(&blk)
-
max_time = time_now + @timeout
-
-
(
-
return true if yield
-
wait
-
) until time_now > max_time
-
-
false
-
end
-
-
1
def wait
-
sleep @interval
-
end
-
-
# for testability
-
1
def time_now
-
Time.now
-
end
-
-
end # SocketPoller
-
end # WebDriver
-
end # Selenium
-
1
module Selenium
-
1
module WebDriver
-
1
class TargetLocator
-
-
#
-
# @api private
-
#
-
-
1
def initialize(bridge)
-
@bridge = bridge
-
end
-
-
#
-
# switch to the frame with the given id
-
#
-
-
1
def frame(id)
-
@bridge.switchToFrame id
-
end
-
-
#
-
# switch to the given window handle
-
#
-
# If given a block, this method will switch back to the original window after
-
# block execution.
-
#
-
# @param id
-
# A window handle, obtained through Driver#window_handles
-
#
-
-
1
def window(id)
-
if block_given?
-
original = @bridge.getCurrentWindowHandle
-
@bridge.switchToWindow id
-
-
begin
-
returned = yield
-
ensure
-
current_handles = @bridge.getWindowHandles
-
-
if current_handles.size == 1
-
original = current_handles.shift
-
end
-
-
@bridge.switchToWindow original
-
returned
-
end
-
else
-
@bridge.switchToWindow id
-
end
-
end
-
-
#
-
# get the active element
-
#
-
# @return [WebDriver::Element]
-
#
-
-
1
def active_element
-
@bridge.switchToActiveElement
-
end
-
-
#
-
# selects either the first frame on the page, or the main document when a page contains iframes.
-
#
-
-
1
def default_content
-
@bridge.switchToDefaultContent
-
end
-
-
#
-
# switches to the currently active modal dialog for this particular driver instance
-
#
-
-
1
def alert
-
Alert.new(@bridge)
-
end
-
-
end # TargetLocator
-
end # WebDriver
-
end # Selenium
-
1
module Selenium
-
1
module WebDriver
-
1
class Timeouts
-
-
1
def initialize(bridge)
-
@bridge = bridge
-
end
-
-
#
-
# Set the amount of time the driver should wait when searching for elements.
-
#
-
-
1
def implicit_wait=(seconds)
-
@bridge.setImplicitWaitTimeout Integer(seconds * 1000)
-
end
-
-
#
-
# Sets the amount of time to wait for an asynchronous script to finish
-
# execution before throwing an error. If the timeout is negative, then the
-
# script will be allowed to run indefinitely.
-
#
-
-
1
def script_timeout=(seconds)
-
@bridge.setScriptTimeout Integer(seconds * 1000)
-
end
-
-
#
-
# Sets the amount of time to wait for a page load to complete before throwing an error.
-
# If the timeout is negative, page loads can be indefinite.
-
#
-
-
1
def page_load=(seconds)
-
@bridge.setTimeout 'page load', Integer(seconds * 1000)
-
end
-
-
end # Timeouts
-
end # WebDriver
-
end # Selenium
-
1
module Selenium
-
1
module WebDriver
-
1
class TouchActionBuilder < ActionBuilder
-
-
#
-
# @api private
-
#
-
-
1
def initialize(mouse, keyboard, touch_screen)
-
super(mouse, keyboard)
-
@devices[:touch_screen] = touch_screen
-
end
-
-
1
def scroll(*args)
-
unless [2,3].include? args.size
-
raise ArgumentError, "wrong number of arguments, expected 2..3, got #{args.size}"
-
end
-
-
@actions << [:touch_screen, :scroll, args]
-
self
-
end
-
-
1
def flick(*args)
-
unless [2,4].include? args.size
-
raise ArgumentError, "wrong number of arguments, expected 2 or 4, got #{args.size}"
-
end
-
-
@actions << [:touch_screen, :flick, args]
-
self
-
end
-
-
1
def single_tap(element)
-
@actions << [:touch_screen, :single_tap, [element]]
-
self
-
end
-
-
1
def double_tap(element)
-
@actions << [:touch_screen, :double_tap, [element]]
-
self
-
end
-
-
1
def long_press(element)
-
@actions << [:touch_screen, :long_press, [element]]
-
self
-
end
-
-
1
def down(x, y = nil)
-
@actions << [:touch_screen, :down, [x, y]]
-
self
-
end
-
-
1
def up(x, y = nil)
-
@actions << [:touch_screen, :up, [x, y]]
-
self
-
end
-
-
1
def move(x, y = nil)
-
@actions << [:touch_screen, :move, [x, y]]
-
self
-
end
-
-
end
-
end
-
end
-
1
module Selenium
-
1
module WebDriver
-
1
class TouchScreen
-
1
FLICK_SPEED = { :normal => 0, :fast => 1}
-
-
-
#
-
# @api private
-
#
-
-
1
def initialize(bridge)
-
@bridge = bridge
-
end
-
-
1
def single_tap(element)
-
assert_element element
-
@bridge.touchSingleTap element.ref
-
end
-
-
1
def double_tap(element)
-
assert_element element
-
@bridge.touchDoubleTap element.ref
-
end
-
-
1
def long_press(element)
-
assert_element element
-
@bridge.touchLongPress element.ref
-
end
-
-
1
def down(x, y = nil)
-
x, y = coords_from x, y
-
@bridge.touchDown x, y
-
end
-
-
1
def up(x, y = nil)
-
x, y = coords_from x, y
-
@bridge.touchUp x, y
-
end
-
-
1
def move(x, y = nil)
-
x, y = coords_from x, y
-
@bridge.touchMove x, y
-
end
-
-
1
def scroll(*args)
-
case args.size
-
when 2
-
x_offset, y_offset = args
-
@bridge.touchScroll nil, Integer(x_offset), Integer(y_offset)
-
when 3
-
element, x_offset, y_offset = args
-
assert_element element
-
@bridge.touchScroll element.ref, Integer(x_offset), Integer(y_offset)
-
else
-
raise ArgumentError, "wrong number of arguments, expected 2..3, got #{args.size}"
-
end
-
end
-
-
1
def flick(*args)
-
case args.size
-
when 2
-
x_speed, y_speed = args
-
@bridge.touchFlick Integer(x_speed), Integer(y_speed)
-
when 4
-
element, xoffset, yoffset, speed = args
-
-
assert_element element
-
flick_speed = FLICK_SPEED[speed.to_sym]
-
-
unless flick_speed
-
raise ArgumentError, "expected one of #{FLICK_SPEED.keys.inspect}, got #{speed.inspect}"
-
end
-
-
@bridge.touchElementFlick element.ref, Integer(xoffset), Integer(yoffset), flick_speed
-
else
-
raise ArgumentError, "wrong number of arguments, expected 2 or 4, got #{args.size}"
-
end
-
-
end
-
-
1
private
-
-
1
def coords_from(x, y)
-
if y.nil?
-
point = x
-
-
unless point.respond_to?(:x) && point.respond_to?(:y)
-
raise ArgumentError, "expected #{point.inspect} to respond to :x and :y"
-
end
-
-
x, y = point.x, point.y
-
end
-
-
[Integer(x), Integer(y)]
-
end
-
-
1
def assert_element(element)
-
unless element.kind_of? Element
-
raise TypeError, "expected #{Element}, got #{element.inspect}:#{element.class}"
-
end
-
end
-
-
end
-
end
-
end
-
1
module Selenium
-
1
module WebDriver
-
1
class Wait
-
-
1
DEFAULT_TIMEOUT = 5
-
1
DEFAULT_INTERVAL = 0.2
-
-
#
-
# Create a new Wait instance
-
#
-
# @param [Hash] opts Options for this instance
-
# @option opts [Numeric] :timeout (5) Seconds to wait before timing out.
-
# @option opts [Numeric] :interval (0.2) Seconds to sleep between polls.
-
# @option opts [String] :message Exception mesage if timed out.
-
# @option opts [Array, Exception] :ignore Exceptions to ignore while polling (default: Error::NoSuchElementError)
-
#
-
-
1
def initialize(opts = {})
-
@timeout = opts.fetch(:timeout, DEFAULT_TIMEOUT)
-
@interval = opts.fetch(:interval, DEFAULT_INTERVAL)
-
@message = opts[:message]
-
@ignored = Array(opts[:ignore] || Error::NoSuchElementError)
-
end
-
-
-
#
-
# Wait until the given block returns a true value.
-
#
-
# @raise [Error::TimeOutError]
-
# @return [Object] the result of the block
-
#
-
-
1
def until(&blk)
-
end_time = Time.now + @timeout
-
last_error = nil
-
-
until Time.now > end_time
-
begin
-
result = yield
-
return result if result
-
rescue *@ignored => last_error
-
# swallowed
-
end
-
-
sleep @interval
-
end
-
-
-
if @message
-
msg = @message.dup
-
else
-
msg = "timed out after #{@timeout} seconds"
-
end
-
-
msg << " (#{last_error.message})" if last_error
-
-
raise Error::TimeOutError, msg
-
end
-
-
end # Wait
-
end # WebDriver
-
end # Selenium
-
-
1
module Selenium
-
1
module WebDriver
-
-
#
-
# @api beta This API may be changed or removed in a future release.
-
#
-
-
1
class Window
-
-
#
-
# @api private
-
#
-
-
1
def initialize(bridge)
-
@bridge = bridge
-
end
-
-
#
-
# Resize the current window to the given dimension.
-
#
-
# @param [Selenium::WebDriver::Dimension, #width and #height] dimension The new size.
-
#
-
-
1
def size=(dimension)
-
unless dimension.respond_to?(:width) && dimension.respond_to?(:height)
-
raise ArgumentError, "expected #{dimension.inspect}:#{dimension.class}" +
-
" to respond to #width and #height"
-
end
-
-
@bridge.setWindowSize dimension.width, dimension.height
-
end
-
-
#
-
# Get the size of the current window.
-
#
-
# @return [Selenium::WebDriver::Dimension] The size.
-
#
-
-
1
def size
-
@bridge.getWindowSize
-
end
-
-
#
-
# Move the current window to the given position.
-
#
-
# @param [Selenium::WebDriver::Point, #x and #y] point The new position.
-
#
-
-
1
def position=(point)
-
unless point.respond_to?(:x) && point.respond_to?(:y)
-
raise ArgumentError, "expected #{point.inspect}:#{point.class}" +
-
" to respond to #x and #y"
-
end
-
-
@bridge.setWindowPosition point.x, point.y
-
end
-
-
#
-
# Get the position of the current window.
-
#
-
# @return [Selenium::WebDriver::Point] The position.
-
#
-
-
1
def position
-
@bridge.getWindowPosition
-
end
-
-
#
-
# Equivalent to #size=, but accepts width and height arguments.
-
#
-
# @example Maximize the window.
-
#
-
# max_width, max_height = driver.execute_script("return [window.screen.availWidth, window.screen.availHeight];")
-
# driver.manage.window.resize_to(max_width, max_height)
-
#
-
-
1
def resize_to(width, height)
-
@bridge.setWindowSize Integer(width), Integer(height)
-
end
-
-
#
-
# Equivalent to #position=, but accepts x and y arguments.
-
#
-
# @example
-
#
-
# driver.manage.window.move_to(300, 400)
-
#
-
-
1
def move_to(x, y)
-
@bridge.setWindowPosition Integer(x), Integer(y)
-
end
-
-
#
-
# Maximize the current window
-
#
-
-
1
def maximize
-
@bridge.maximizeWindow
-
end
-
-
-
end
-
end
-
end
-
1
require 'zip'
-
1
require 'tempfile'
-
1
require 'find'
-
-
1
module Selenium
-
1
module WebDriver
-
#
-
# @api private
-
#
-
-
1
module Zipper
-
-
1
EXTENSIONS = %w[.zip .xpi]
-
-
1
class << self
-
-
1
def unzip(path)
-
destination = Dir.mktmpdir("webdriver-unzip")
-
FileReaper << destination
-
-
Zip::File.open(path) do |zip|
-
zip.each do |entry|
-
to = File.join(destination, entry.name)
-
dirname = File.dirname(to)
-
-
FileUtils.mkdir_p dirname unless File.exist? dirname
-
zip.extract(entry, to)
-
end
-
end
-
-
destination
-
end
-
-
1
def zip(path)
-
with_tmp_zip do |zip|
-
::Find.find(path) do |file|
-
unless File.directory?(file)
-
add_zip_entry zip, file, file.sub("#{path}/", '')
-
end
-
end
-
-
zip.commit
-
File.open(zip.name, "rb") { |io| Base64.strict_encode64 io.read }
-
end
-
end
-
-
1
def zip_file(path)
-
with_tmp_zip do |zip|
-
add_zip_entry zip, path, File.basename(path)
-
-
zip.commit
-
File.open(zip.name, "rb") { |io| Base64.strict_encode64 io.read }
-
end
-
end
-
-
1
private
-
-
1
def with_tmp_zip(&blk)
-
# can't use Tempfile here since it doesn't support File::BINARY mode on 1.8
-
# can't use Dir.mktmpdir(&blk) because of http://jira.codehaus.org/browse/JRUBY-4082
-
tmp_dir = Dir.mktmpdir
-
zip_path = File.join(tmp_dir, "webdriver-zip")
-
-
begin
-
Zip::File.open(zip_path, Zip::File::CREATE, &blk)
-
ensure
-
FileUtils.rm_rf tmp_dir
-
FileUtils.rm_rf zip_path
-
end
-
end
-
-
1
def add_zip_entry(zip, file, entry_name)
-
entry = Zip::Entry.new(zip.name, entry_name)
-
entry.follow_symlinks = true
-
-
zip.add entry, file
-
end
-
-
end
-
end # Zipper
-
end # WebDriver
-
end # Selenium
-
# encoding: utf-8
-
1
require 'sidekiq/version'
-
1
fail "Sidekiq #{Sidekiq::VERSION} does not support Ruby 1.9." if RUBY_PLATFORM != 'java' && RUBY_VERSION < '2.0.0'
-
-
1
require 'sidekiq/logging'
-
1
require 'sidekiq/client'
-
1
require 'sidekiq/worker'
-
1
require 'sidekiq/redis_connection'
-
-
1
require 'json'
-
-
1
module Sidekiq
-
1
NAME = "Sidekiq"
-
1
LICENSE = 'See LICENSE and the LGPL-3.0 for licensing details.'
-
-
1
DEFAULTS = {
-
:queues => [],
-
:labels => [],
-
:concurrency => 25,
-
:require => '.',
-
:environment => nil,
-
:timeout => 8,
-
:error_handlers => [],
-
:lifecycle_events => {
-
:startup => [],
-
:quiet => [],
-
:shutdown => [],
-
}
-
}
-
-
1
def self.❨╯°□°❩╯︵┻━┻
-
puts "Calm down, bro"
-
end
-
-
1
def self.options
-
@options ||= DEFAULTS.dup
-
end
-
-
1
def self.options=(opts)
-
@options = opts
-
end
-
-
##
-
# Configuration for Sidekiq server, use like:
-
#
-
# Sidekiq.configure_server do |config|
-
# config.redis = { :namespace => 'myapp', :size => 25, :url => 'redis://myhost:8877/0' }
-
# config.server_middleware do |chain|
-
# chain.add MyServerHook
-
# end
-
# end
-
1
def self.configure_server
-
yield self if server?
-
end
-
-
##
-
# Configuration for Sidekiq client, use like:
-
#
-
# Sidekiq.configure_client do |config|
-
# config.redis = { :namespace => 'myapp', :size => 1, :url => 'redis://myhost:8877/0' }
-
# end
-
1
def self.configure_client
-
yield self unless server?
-
end
-
-
1
def self.server?
-
defined?(Sidekiq::CLI)
-
end
-
-
1
def self.redis(&block)
-
raise ArgumentError, "requires a block" unless block
-
redis_pool.with(&block)
-
end
-
-
1
def self.redis_pool
-
@redis ||= Sidekiq::RedisConnection.create
-
end
-
-
1
def self.redis=(hash)
-
@redis = if hash.is_a?(ConnectionPool)
-
hash
-
else
-
Sidekiq::RedisConnection.create(hash)
-
end
-
end
-
-
1
def self.client_middleware
-
@client_chain ||= Middleware::Chain.new
-
yield @client_chain if block_given?
-
@client_chain
-
end
-
-
1
def self.server_middleware
-
@server_chain ||= Processor.default_middleware
-
yield @server_chain if block_given?
-
@server_chain
-
end
-
-
1
def self.default_worker_options=(hash)
-
@default_worker_options = default_worker_options.merge(hash)
-
end
-
-
1
def self.default_worker_options
-
defined?(@default_worker_options) ? @default_worker_options : { 'retry' => true, 'queue' => 'default' }
-
end
-
-
1
def self.load_json(string)
-
JSON.parse(string)
-
end
-
-
1
def self.dump_json(object)
-
JSON.generate(object)
-
end
-
-
1
def self.logger
-
Sidekiq::Logging.logger
-
end
-
-
1
def self.logger=(log)
-
Sidekiq::Logging.logger = log
-
end
-
-
# See sidekiq/scheduled.rb for an in-depth explanation of this value
-
1
def self.poll_interval=(interval)
-
self.options[:poll_interval] = interval
-
end
-
-
# Register a proc to handle any error which occurs within the Sidekiq process.
-
#
-
# Sidekiq.configure_server do |config|
-
# config.error_handlers << Proc.new {|ex,ctx_hash| MyErrorService.notify(ex, ctx_hash) }
-
# end
-
#
-
# The default error handler logs errors to Sidekiq.logger.
-
1
def self.error_handlers
-
self.options[:error_handlers]
-
end
-
-
# Register a block to run at a point in the Sidekiq lifecycle.
-
# :startup, :quiet or :shutdown are valid events.
-
#
-
# Sidekiq.configure_server do |config|
-
# config.on(:shutdown) do
-
# puts "Goodbye cruel world!"
-
# end
-
# end
-
1
def self.on(event, &block)
-
raise ArgumentError, "Symbols only please: #{event}" unless event.is_a?(Symbol)
-
raise ArgumentError, "Invalid event name: #{event}" unless options[:lifecycle_events].keys.include?(event)
-
options[:lifecycle_events][event] << block
-
end
-
end
-
-
1
require 'sidekiq/extensions/class_methods'
-
1
require 'sidekiq/extensions/action_mailer'
-
1
require 'sidekiq/extensions/active_record'
-
1
require 'sidekiq/rails' if defined?(::Rails::Engine)
-
1
require 'securerandom'
-
1
require 'sidekiq/middleware/chain'
-
-
1
module Sidekiq
-
1
class Client
-
-
##
-
# Define client-side middleware:
-
#
-
# client = Sidekiq::Client.new
-
# client.middleware do |chain|
-
# chain.use MyClientMiddleware
-
# end
-
# client.push('class' => 'SomeWorker', 'args' => [1,2,3])
-
#
-
# All client instances default to the globally-defined
-
# Sidekiq.client_middleware but you can change as necessary.
-
#
-
1
def middleware(&block)
-
@chain ||= Sidekiq.client_middleware
-
if block_given?
-
@chain = @chain.dup
-
yield @chain
-
end
-
@chain
-
end
-
-
1
attr_accessor :redis_pool
-
-
# Sidekiq::Client normally uses the default Redis pool but you may
-
# pass a custom ConnectionPool if you want to shard your
-
# Sidekiq jobs across several Redis instances (for scalability
-
# reasons, e.g.)
-
#
-
# Sidekiq::Client.new(ConnectionPool.new { Redis.new })
-
#
-
# Generally this is only needed for very large Sidekiq installs processing
-
# more than thousands jobs per second. I do not recommend sharding unless
-
# you truly cannot scale any other way (e.g. splitting your app into smaller apps).
-
# Some features, like the API, do not support sharding: they are designed to work
-
# against a single Redis instance only.
-
1
def initialize(redis_pool=nil)
-
@redis_pool = redis_pool || Thread.current[:sidekiq_via_pool] || Sidekiq.redis_pool
-
end
-
-
##
-
# The main method used to push a job to Redis. Accepts a number of options:
-
#
-
# queue - the named queue to use, default 'default'
-
# class - the worker class to call, required
-
# args - an array of simple arguments to the perform method, must be JSON-serializable
-
# retry - whether to retry this job if it fails, true or false, default true
-
# backtrace - whether to save any error backtrace, default false
-
#
-
# All options must be strings, not symbols. NB: because we are serializing to JSON, all
-
# symbols in 'args' will be converted to strings.
-
#
-
# Returns nil if not pushed to Redis or a unique Job ID if pushed.
-
#
-
# Example:
-
# push('queue' => 'my_queue', 'class' => MyWorker, 'args' => ['foo', 1, :bat => 'bar'])
-
#
-
1
def push(item)
-
normed = normalize_item(item)
-
payload = process_single(item['class'], normed)
-
-
pushed = false
-
pushed = raw_push([payload]) if payload
-
pushed ? payload['jid'] : nil
-
end
-
-
##
-
# Push a large number of jobs to Redis. In practice this method is only
-
# useful if you are pushing tens of thousands of jobs or more, or if you need
-
# to ensure that a batch doesn't complete prematurely. This method
-
# basically cuts down on the redis round trip latency.
-
#
-
# Takes the same arguments as #push except that args is expected to be
-
# an Array of Arrays. All other keys are duplicated for each job. Each job
-
# is run through the client middleware pipeline and each job gets its own Job ID
-
# as normal.
-
#
-
# Returns an array of the of pushed jobs' jids or nil if the pushed failed. The number of jobs
-
# pushed can be less than the number given if the middleware stopped processing for one
-
# or more jobs.
-
1
def push_bulk(items)
-
normed = normalize_item(items)
-
payloads = items['args'].map do |args|
-
raise ArgumentError, "Bulk arguments must be an Array of Arrays: [[1], [2]]" if !args.is_a?(Array)
-
process_single(items['class'], normed.merge('args' => args, 'jid' => SecureRandom.hex(12), 'enqueued_at' => Time.now.to_f))
-
end.compact
-
-
pushed = false
-
pushed = raw_push(payloads) if !payloads.empty?
-
pushed ? payloads.collect { |payload| payload['jid'] } : nil
-
end
-
-
# Allows sharding of jobs across any number of Redis instances. All jobs
-
# defined within the block will use the given Redis connection pool.
-
#
-
# pool = ConnectionPool.new { Redis.new }
-
# Sidekiq::Client.via(pool) do
-
# SomeWorker.perform_async(1,2,3)
-
# SomeOtherWorker.perform_async(1,2,3)
-
# end
-
#
-
# Generally this is only needed for very large Sidekiq installs processing
-
# more than thousands jobs per second. I do not recommend sharding unless
-
# you truly cannot scale any other way (e.g. splitting your app into smaller apps).
-
# Some features, like the API, do not support sharding: they are designed to work
-
# against a single Redis instance.
-
1
def self.via(pool)
-
raise ArgumentError, "No pool given" if pool.nil?
-
raise RuntimeError, "Sidekiq::Client.via is not re-entrant" if x = Thread.current[:sidekiq_via_pool] && x != pool
-
Thread.current[:sidekiq_via_pool] = pool
-
yield
-
ensure
-
Thread.current[:sidekiq_via_pool] = nil
-
end
-
-
1
class << self
-
-
1
def default
-
@default ||= new
-
end
-
-
1
def push(item)
-
default.push(item)
-
end
-
-
1
def push_bulk(items)
-
default.push_bulk(items)
-
end
-
-
# Resque compatibility helpers. Note all helpers
-
# should go through Worker#client_push.
-
#
-
# Example usage:
-
# Sidekiq::Client.enqueue(MyWorker, 'foo', 1, :bat => 'bar')
-
#
-
# Messages are enqueued to the 'default' queue.
-
#
-
1
def enqueue(klass, *args)
-
klass.client_push('class' => klass, 'args' => args)
-
end
-
-
# Example usage:
-
# Sidekiq::Client.enqueue_to(:queue_name, MyWorker, 'foo', 1, :bat => 'bar')
-
#
-
1
def enqueue_to(queue, klass, *args)
-
klass.client_push('queue' => queue, 'class' => klass, 'args' => args)
-
end
-
-
# Example usage:
-
# Sidekiq::Client.enqueue_to_in(:queue_name, 3.minutes, MyWorker, 'foo', 1, :bat => 'bar')
-
#
-
1
def enqueue_to_in(queue, interval, klass, *args)
-
int = interval.to_f
-
now = Time.now.to_f
-
ts = (int < 1_000_000_000 ? now + int : int)
-
-
item = { 'class' => klass, 'args' => args, 'at' => ts, 'queue' => queue }
-
item.delete('at') if ts <= now
-
-
klass.client_push(item)
-
end
-
-
# Example usage:
-
# Sidekiq::Client.enqueue_in(3.minutes, MyWorker, 'foo', 1, :bat => 'bar')
-
#
-
1
def enqueue_in(interval, klass, *args)
-
klass.perform_in(interval, *args)
-
end
-
end
-
-
1
private
-
-
1
def raw_push(payloads)
-
@redis_pool.with do |conn|
-
conn.multi do
-
atomic_push(conn, payloads)
-
end
-
end
-
true
-
end
-
-
1
def atomic_push(conn, payloads)
-
if payloads.first['at']
-
conn.zadd('schedule', payloads.map do |hash|
-
at = hash.delete('at').to_s
-
[at, Sidekiq.dump_json(hash)]
-
end)
-
else
-
q = payloads.first['queue']
-
to_push = payloads.map { |entry| Sidekiq.dump_json(entry) }
-
conn.sadd('queues', q)
-
conn.lpush("queue:#{q}", to_push)
-
end
-
end
-
-
1
def process_single(worker_class, item)
-
queue = item['queue']
-
-
middleware.invoke(worker_class, item, queue, @redis_pool) do
-
item
-
end
-
end
-
-
1
def normalize_item(item)
-
raise(ArgumentError, "Message must be a Hash of the form: { 'class' => SomeWorker, 'args' => ['bob', 1, :foo => 'bar'] }") unless item.is_a?(Hash)
-
raise(ArgumentError, "Message must include a class and set of arguments: #{item.inspect}") if !item['class'] || !item['args']
-
raise(ArgumentError, "Message args must be an Array") unless item['args'].is_a?(Array)
-
raise(ArgumentError, "Message class must be either a Class or String representation of the class name") unless item['class'].is_a?(Class) || item['class'].is_a?(String)
-
-
if item['class'].is_a?(Class)
-
raise(ArgumentError, "Message must include a Sidekiq::Worker class, not class name: #{item['class'].ancestors.inspect}") if !item['class'].respond_to?('get_sidekiq_options')
-
normalized_item = item['class'].get_sidekiq_options.merge(item)
-
normalized_item['class'] = normalized_item['class'].to_s
-
else
-
normalized_item = Sidekiq.default_worker_options.merge(item)
-
end
-
-
normalized_item['queue'] = normalized_item['queue'].to_s
-
normalized_item['jid'] ||= SecureRandom.hex(12)
-
normalized_item['enqueued_at'] ||= Time.now.to_f
-
normalized_item
-
end
-
-
end
-
end
-
1
require 'sidekiq/extensions/generic_proxy'
-
-
1
module Sidekiq
-
1
module Extensions
-
##
-
# Adds 'delay', 'delay_for' and `delay_until` methods to ActionMailer to offload arbitrary email
-
# delivery to Sidekiq. Example:
-
#
-
# UserMailer.delay.send_welcome_email(new_user)
-
# UserMailer.delay_for(5.days).send_welcome_email(new_user)
-
# UserMailer.delay_until(5.days.from_now).send_welcome_email(new_user)
-
1
class DelayedMailer
-
1
include Sidekiq::Worker
-
-
1
def perform(yml)
-
(target, method_name, args) = YAML.load(yml)
-
msg = target.public_send(method_name, *args)
-
# The email method can return nil, which causes ActionMailer to return
-
# an undeliverable empty message.
-
deliver(msg) if msg && (msg.to || msg.cc || msg.bcc) && msg.from
-
end
-
-
1
private
-
-
1
def deliver(msg)
-
if msg.respond_to?(:deliver_now)
-
ActiveSupport::Deprecation.warn('`ActionMailer.delay.method` is deprecated. Use `ActionMailer.method.deliver_later` instead and configure ActiveJob to use Sidekiq.')
-
# Rails 4.2/5.0
-
msg.deliver_now
-
else
-
# Rails 3.2/4.0/4.1
-
msg.deliver
-
end
-
end
-
end
-
-
1
module ActionMailer
-
1
def sidekiq_delay(options={})
-
Proxy.new(DelayedMailer, self, options)
-
end
-
1
def sidekiq_delay_for(interval, options={})
-
Proxy.new(DelayedMailer, self, options.merge('at' => Time.now.to_f + interval.to_f))
-
end
-
1
def sidekiq_delay_until(timestamp, options={})
-
Proxy.new(DelayedMailer, self, options.merge('at' => timestamp.to_f))
-
end
-
1
alias_method :delay, :sidekiq_delay
-
1
alias_method :delay_for, :sidekiq_delay_for
-
1
alias_method :delay_until, :sidekiq_delay_until
-
end
-
-
end
-
end
-
1
require 'sidekiq/extensions/generic_proxy'
-
-
1
module Sidekiq
-
1
module Extensions
-
##
-
# Adds 'delay', 'delay_for' and `delay_until` methods to ActiveRecord to offload instance method
-
# execution to Sidekiq. Examples:
-
#
-
# User.recent_signups.each { |user| user.delay.mark_as_awesome }
-
#
-
# Please note, this is not recommended as this will serialize the entire
-
# object to Redis. Your Sidekiq jobs should pass IDs, not entire instances.
-
# This is here for backwards compatibility with Delayed::Job only.
-
1
class DelayedModel
-
1
include Sidekiq::Worker
-
-
1
def perform(yml)
-
(target, method_name, args) = YAML.load(yml)
-
target.__send__(method_name, *args)
-
end
-
end
-
-
1
module ActiveRecord
-
1
def sidekiq_delay(options={})
-
Proxy.new(DelayedModel, self, options)
-
end
-
1
def sidekiq_delay_for(interval, options={})
-
Proxy.new(DelayedModel, self, options.merge('at' => Time.now.to_f + interval.to_f))
-
end
-
1
def sidekiq_delay_until(timestamp, options={})
-
Proxy.new(DelayedModel, self, options.merge('at' => timestamp.to_f))
-
end
-
1
alias_method :delay, :sidekiq_delay
-
1
alias_method :delay_for, :sidekiq_delay_for
-
1
alias_method :delay_until, :sidekiq_delay_until
-
end
-
-
end
-
end
-
1
require 'sidekiq/extensions/generic_proxy'
-
-
1
module Sidekiq
-
1
module Extensions
-
##
-
# Adds 'delay', 'delay_for' and `delay_until` methods to all Classes to offload class method
-
# execution to Sidekiq. Examples:
-
#
-
# User.delay.delete_inactive
-
# Wikipedia.delay.download_changes_for(Date.today)
-
#
-
1
class DelayedClass
-
1
include Sidekiq::Worker
-
-
1
def perform(yml)
-
(target, method_name, args) = YAML.load(yml)
-
target.__send__(method_name, *args)
-
end
-
end
-
-
1
module Klass
-
1
def sidekiq_delay(options={})
-
Proxy.new(DelayedClass, self, options)
-
end
-
1
def sidekiq_delay_for(interval, options={})
-
Proxy.new(DelayedClass, self, options.merge('at' => Time.now.to_f + interval.to_f))
-
end
-
1
def sidekiq_delay_until(timestamp, options={})
-
Proxy.new(DelayedClass, self, options.merge('at' => timestamp.to_f))
-
end
-
1
alias_method :delay, :sidekiq_delay
-
1
alias_method :delay_for, :sidekiq_delay_for
-
1
alias_method :delay_until, :sidekiq_delay_until
-
end
-
-
end
-
end
-
-
1
Module.__send__(:include, Sidekiq::Extensions::Klass) unless defined?(::Rails)
-
1
require 'yaml'
-
-
1
module Sidekiq
-
1
module Extensions
-
1
class Proxy < BasicObject
-
1
def initialize(performable, target, options={})
-
@performable = performable
-
@target = target
-
@opts = options
-
end
-
-
1
def method_missing(name, *args)
-
# Sidekiq has a limitation in that its message must be JSON.
-
# JSON can't round trip real Ruby objects so we use YAML to
-
# serialize the objects to a String. The YAML will be converted
-
# to JSON and then deserialized on the other side back into a
-
# Ruby object.
-
obj = [@target, name, args]
-
@performable.client_push({ 'class' => @performable, 'args' => [::YAML.dump(obj)] }.merge(@opts))
-
end
-
end
-
-
end
-
end
-
1
require 'time'
-
1
require 'logger'
-
-
1
module Sidekiq
-
1
module Logging
-
-
1
class Pretty < Logger::Formatter
-
# Provide a call() method that returns the formatted message.
-
1
def call(severity, time, program_name, message)
-
"#{time.utc.iso8601(3)} #{::Process.pid} TID-#{Thread.current.object_id.to_s(36)}#{context} #{severity}: #{message}\n"
-
end
-
-
1
def context
-
c = Thread.current[:sidekiq_context]
-
c ? " #{c}" : ''
-
end
-
end
-
-
1
def self.with_context(msg)
-
begin
-
Thread.current[:sidekiq_context] = msg
-
yield
-
ensure
-
Thread.current[:sidekiq_context] = nil
-
end
-
end
-
-
1
def self.initialize_logger(log_target = STDOUT)
-
oldlogger = defined?(@logger) ? @logger : nil
-
@logger = Logger.new(log_target)
-
@logger.level = Logger::INFO
-
@logger.formatter = Pretty.new
-
oldlogger.close if oldlogger && !$TESTING # don't want to close testing's STDOUT logging
-
@logger
-
end
-
-
1
def self.logger
-
defined?(@logger) ? @logger : initialize_logger
-
end
-
-
1
def self.logger=(log)
-
@logger = (log ? log : Logger.new('/dev/null'))
-
end
-
-
# This reopens ALL logfiles in the process that have been rotated
-
# using logrotate(8) (without copytruncate) or similar tools.
-
# A +File+ object is considered for reopening if it is:
-
# 1) opened with the O_APPEND and O_WRONLY flags
-
# 2) the current open file handle does not match its original open path
-
# 3) unbuffered (as far as userspace buffering goes, not O_SYNC)
-
# Returns the number of files reopened
-
1
def self.reopen_logs
-
to_reopen = []
-
append_flags = File::WRONLY | File::APPEND
-
-
ObjectSpace.each_object(File) do |fp|
-
begin
-
if !fp.closed? && fp.stat.file? && fp.sync && (fp.fcntl(Fcntl::F_GETFL) & append_flags) == append_flags
-
to_reopen << fp
-
end
-
rescue IOError, Errno::EBADF
-
end
-
end
-
-
nr = 0
-
to_reopen.each do |fp|
-
orig_st = begin
-
fp.stat
-
rescue IOError, Errno::EBADF
-
next
-
end
-
-
begin
-
b = File.stat(fp.path)
-
next if orig_st.ino == b.ino && orig_st.dev == b.dev
-
rescue Errno::ENOENT
-
end
-
-
begin
-
File.open(fp.path, 'a') { |tmpfp| fp.reopen(tmpfp) }
-
fp.sync = true
-
nr += 1
-
rescue IOError, Errno::EBADF
-
# not much we can do...
-
end
-
end
-
nr
-
rescue RuntimeError => ex
-
# RuntimeError: ObjectSpace is disabled; each_object will only work with Class, pass -X+O to enable
-
puts "Unable to reopen logs: #{ex.message}"
-
end
-
-
1
def logger
-
Sidekiq::Logging.logger
-
end
-
end
-
end
-
1
module Sidekiq
-
# Middleware is code configured to run before/after
-
# a message is processed. It is patterned after Rack
-
# middleware. Middleware exists for the client side
-
# (pushing jobs onto the queue) as well as the server
-
# side (when jobs are actually processed).
-
#
-
# To add middleware for the client:
-
#
-
# Sidekiq.configure_client do |config|
-
# config.client_middleware do |chain|
-
# chain.add MyClientHook
-
# end
-
# end
-
#
-
# To modify middleware for the server, just call
-
# with another block:
-
#
-
# Sidekiq.configure_server do |config|
-
# config.server_middleware do |chain|
-
# chain.add MyServerHook
-
# chain.remove ActiveRecord
-
# end
-
# end
-
#
-
# To insert immediately preceding another entry:
-
#
-
# Sidekiq.configure_client do |config|
-
# config.client_middleware do |chain|
-
# chain.insert_before ActiveRecord, MyClientHook
-
# end
-
# end
-
#
-
# To insert immediately after another entry:
-
#
-
# Sidekiq.configure_client do |config|
-
# config.client_middleware do |chain|
-
# chain.insert_after ActiveRecord, MyClientHook
-
# end
-
# end
-
#
-
# This is an example of a minimal server middleware:
-
#
-
# class MyServerHook
-
# def call(worker_instance, msg, queue)
-
# puts "Before work"
-
# yield
-
# puts "After work"
-
# end
-
# end
-
#
-
# This is an example of a minimal client middleware, note
-
# the method must return the result or the job will not push
-
# to Redis:
-
#
-
# class MyClientHook
-
# def call(worker_class, msg, queue, redis_pool)
-
# puts "Before push"
-
# result = yield
-
# puts "After push"
-
# result
-
# end
-
# end
-
#
-
1
module Middleware
-
1
class Chain
-
1
include Enumerable
-
1
attr_reader :entries
-
-
1
def initialize_copy(copy)
-
copy.instance_variable_set(:@entries, entries.dup)
-
end
-
-
1
def each(&block)
-
entries.each(&block)
-
end
-
-
1
def initialize
-
@entries = []
-
yield self if block_given?
-
end
-
-
1
def remove(klass)
-
entries.delete_if { |entry| entry.klass == klass }
-
end
-
-
1
def add(klass, *args)
-
remove(klass) if exists?(klass)
-
entries << Entry.new(klass, *args)
-
end
-
-
1
def insert_before(oldklass, newklass, *args)
-
i = entries.index { |entry| entry.klass == newklass }
-
new_entry = i.nil? ? Entry.new(newklass, *args) : entries.delete_at(i)
-
i = entries.index { |entry| entry.klass == oldklass } || 0
-
entries.insert(i, new_entry)
-
end
-
-
1
def insert_after(oldklass, newklass, *args)
-
i = entries.index { |entry| entry.klass == newklass }
-
new_entry = i.nil? ? Entry.new(newklass, *args) : entries.delete_at(i)
-
i = entries.index { |entry| entry.klass == oldklass } || entries.count - 1
-
entries.insert(i+1, new_entry)
-
end
-
-
1
def exists?(klass)
-
any? { |entry| entry.klass == klass }
-
end
-
-
1
def retrieve
-
map(&:make_new)
-
end
-
-
1
def clear
-
entries.clear
-
end
-
-
1
def invoke(*args, &final_action)
-
chain = retrieve.dup
-
traverse_chain = lambda do
-
if chain.empty?
-
final_action.call
-
else
-
chain.shift.call(*args, &traverse_chain)
-
end
-
end
-
traverse_chain.call
-
end
-
end
-
-
1
class Entry
-
1
attr_reader :klass
-
-
1
def initialize(klass, *args)
-
@klass = klass
-
@args = args
-
end
-
-
1
def make_new
-
@klass.new(*@args)
-
end
-
end
-
end
-
end
-
1
module Sidekiq
-
1
def self.hook_rails!
-
1
return if defined?(@delay_removed)
-
-
1
ActiveSupport.on_load(:active_record) do
-
1
include Sidekiq::Extensions::ActiveRecord
-
end
-
-
1
ActiveSupport.on_load(:action_mailer) do
-
1
extend Sidekiq::Extensions::ActionMailer
-
end
-
-
1
Module.__send__(:include, Sidekiq::Extensions::Klass)
-
end
-
-
# Removes the generic aliases which MAY clash with names of already
-
# created methods by other applications. The methods `sidekiq_delay`,
-
# `sidekiq_delay_for` and `sidekiq_delay_until` can be used instead.
-
1
def self.remove_delay!
-
@delay_removed = true
-
-
[Extensions::ActiveRecord,
-
Extensions::ActionMailer,
-
Extensions::Klass].each do |mod|
-
mod.module_eval do
-
remove_method :delay if respond_to?(:delay)
-
remove_method :delay_for if respond_to?(:delay_for)
-
remove_method :delay_until if respond_to?(:delay_until)
-
end
-
end
-
end
-
-
class Rails < ::Rails::Engine
-
1
initializer 'sidekiq' do
-
1
Sidekiq.hook_rails!
-
end
-
1
end if defined?(::Rails)
-
end
-
1
require 'connection_pool'
-
1
require 'redis'
-
1
require 'uri'
-
-
1
module Sidekiq
-
1
class RedisConnection
-
1
class << self
-
-
1
def create(options={})
-
url = options[:url] || determine_redis_provider
-
if url
-
options[:url] = url
-
end
-
-
# need a connection for Fetcher and Retry
-
size = options[:size] || (Sidekiq.server? ? (Sidekiq.options[:concurrency] + 2) : 5)
-
pool_timeout = options[:pool_timeout] || 1
-
-
log_info(options)
-
-
ConnectionPool.new(:timeout => pool_timeout, :size => size) do
-
build_client(options)
-
end
-
end
-
-
1
private
-
-
1
def build_client(options)
-
namespace = options[:namespace]
-
-
client = Redis.new client_opts(options)
-
if namespace
-
require 'redis/namespace'
-
Redis::Namespace.new(namespace, :redis => client)
-
else
-
client
-
end
-
end
-
-
1
def client_opts(options)
-
opts = options.dup
-
if opts[:namespace]
-
opts.delete(:namespace)
-
end
-
-
if opts[:network_timeout]
-
opts[:timeout] = opts[:network_timeout]
-
opts.delete(:network_timeout)
-
end
-
-
opts[:driver] = opts[:driver] || 'ruby'
-
-
opts
-
end
-
-
1
def log_info(options)
-
# Don't log Redis AUTH password
-
redacted = "REDACTED"
-
scrubbed_options = options.dup
-
if scrubbed_options[:url] && (uri = URI.parse(scrubbed_options[:url])) && uri.password
-
uri.password = redacted
-
scrubbed_options[:url] = uri.to_s
-
end
-
if scrubbed_options[:password]
-
scrubbed_options[:password] = redacted
-
end
-
if Sidekiq.server?
-
Sidekiq.logger.info("Booting Sidekiq #{Sidekiq::VERSION} with redis options #{scrubbed_options}")
-
else
-
Sidekiq.logger.debug("#{Sidekiq::NAME} client with redis options #{scrubbed_options}")
-
end
-
end
-
-
1
def determine_redis_provider
-
ENV[ENV['REDIS_PROVIDER'] || 'REDIS_URL']
-
end
-
-
end
-
end
-
end
-
1
module Sidekiq
-
1
VERSION = "3.2.6"
-
end
-
1
require 'sidekiq/client'
-
1
require 'sidekiq/core_ext'
-
-
1
module Sidekiq
-
-
##
-
# Include this module in your worker class and you can easily create
-
# asynchronous jobs:
-
#
-
# class HardWorker
-
# include Sidekiq::Worker
-
#
-
# def perform(*args)
-
# # do some work
-
# end
-
# end
-
#
-
# Then in your Rails app, you can do this:
-
#
-
# HardWorker.perform_async(1, 2, 3)
-
#
-
# Note that perform_async is a class method, perform is an instance method.
-
1
module Worker
-
1
attr_accessor :jid
-
-
1
def self.included(base)
-
3
base.extend(ClassMethods)
-
3
base.class_attribute :sidekiq_options_hash
-
3
base.class_attribute :sidekiq_retry_in_block
-
3
base.class_attribute :sidekiq_retries_exhausted_block
-
end
-
-
1
def logger
-
Sidekiq.logger
-
end
-
-
1
module ClassMethods
-
-
1
def perform_async(*args)
-
client_push('class' => self, 'args' => args)
-
end
-
-
1
def perform_in(interval, *args)
-
int = interval.to_f
-
now = Time.now.to_f
-
ts = (int < 1_000_000_000 ? now + int : int)
-
-
item = { 'class' => self, 'args' => args, 'at' => ts }
-
-
# Optimization to enqueue something now that is scheduled to go out now or in the past
-
item.delete('at') if ts <= now
-
-
client_push(item)
-
end
-
1
alias_method :perform_at, :perform_in
-
-
##
-
# Allows customization for this type of Worker.
-
# Legal options:
-
#
-
# :queue - use a named queue for this Worker, default 'default'
-
# :retry - enable the RetryJobs middleware for this Worker, default *true*
-
# :backtrace - whether to save any error backtrace in the retry payload to display in web UI,
-
# can be true, false or an integer number of lines to save, default *false*
-
# :pool - use the given Redis connection pool to push this type of job to a given shard.
-
1
def sidekiq_options(opts={})
-
self.sidekiq_options_hash = get_sidekiq_options.merge((opts || {}).stringify_keys)
-
::Sidekiq.logger.warn("#{self.name} - :timeout is unsafe and support has been removed from Sidekiq, see http://bit.ly/OtYpK for details") if opts.include? :timeout
-
end
-
-
1
def sidekiq_retry_in(&block)
-
self.sidekiq_retry_in_block = block
-
end
-
-
1
def sidekiq_retries_exhausted(&block)
-
self.sidekiq_retries_exhausted_block = block
-
end
-
-
1
def get_sidekiq_options # :nodoc:
-
self.sidekiq_options_hash ||= Sidekiq.default_worker_options
-
end
-
-
1
def client_push(item) # :nodoc:
-
pool = Thread.current[:sidekiq_via_pool] || get_sidekiq_options['pool'] || Sidekiq.redis_pool
-
Sidekiq::Client.new(pool).push(item.stringify_keys)
-
end
-
-
end
-
end
-
end
-
1
require 'slop/option'
-
1
require 'slop/commands'
-
-
1
class Slop
-
1
include Enumerable
-
-
1
VERSION = '3.6.0'
-
-
# The main Error class, all Exception classes inherit from this class.
-
1
class Error < StandardError; end
-
-
# Raised when an option argument is expected but none are given.
-
1
class MissingArgumentError < Error; end
-
-
# Raised when an option is expected/required but not present.
-
1
class MissingOptionError < Error; end
-
-
# Raised when an argument does not match its intended match constraint.
-
1
class InvalidArgumentError < Error; end
-
-
# Raised when an invalid option is found and the strict flag is enabled.
-
1
class InvalidOptionError < Error; end
-
-
# Raised when an invalid command is found and the strict flag is enabled.
-
1
class InvalidCommandError < Error; end
-
-
# Returns a default Hash of configuration options this Slop instance uses.
-
1
DEFAULT_OPTIONS = {
-
:strict => false,
-
:help => false,
-
:banner => nil,
-
:ignore_case => false,
-
:autocreate => false,
-
:arguments => false,
-
:optional_arguments => false,
-
:multiple_switches => true,
-
:longest_flag => 0
-
}
-
-
1
class << self
-
-
# items - The Array of items to extract options from (default: ARGV).
-
# config - The Hash of configuration options to send to Slop.new().
-
# block - An optional block used to add options.
-
#
-
# Examples:
-
#
-
# Slop.parse(ARGV, :help => true) do
-
# on '-n', '--name', 'Your username', :argument => true
-
# end
-
#
-
# Returns a new instance of Slop.
-
1
def parse(items = ARGV, config = {}, &block)
-
parse! items.dup, config, &block
-
end
-
-
# items - The Array of items to extract options from (default: ARGV).
-
# config - The Hash of configuration options to send to Slop.new().
-
# block - An optional block used to add options.
-
#
-
# Returns a new instance of Slop.
-
1
def parse!(items = ARGV, config = {}, &block)
-
config, items = items, ARGV if items.is_a?(Hash) && config.empty?
-
slop = new config, &block
-
slop.parse! items
-
slop
-
end
-
-
# Build a Slop object from a option specification.
-
#
-
# This allows you to design your options via a simple String rather
-
# than programatically. Do note though that with this method, you're
-
# unable to pass any advanced options to the on() method when creating
-
# options.
-
#
-
# string - The optspec String
-
# config - A Hash of configuration options to pass to Slop.new
-
#
-
# Examples:
-
#
-
# opts = Slop.optspec(<<-SPEC)
-
# ruby foo.rb [options]
-
# ---
-
# n,name= Your name
-
# a,age= Your age
-
# A,auth Sign in with auth
-
# p,passcode= Your secret pass code
-
# SPEC
-
#
-
# opts.fetch_option(:name).description #=> "Your name"
-
#
-
# Returns a new instance of Slop.
-
1
def optspec(string, config = {})
-
warn "[DEPRECATED] `Slop.optspec` is deprecated and will be removed in version 4"
-
config[:banner], optspec = string.split(/^--+$/, 2) if string[/^--+$/]
-
lines = optspec.split("\n").reject(&:empty?)
-
opts = Slop.new(config)
-
-
lines.each do |line|
-
opt, description = line.split(' ', 2)
-
short, long = opt.split(',').map { |s| s.sub(/\A--?/, '') }
-
opt = opts.on(short, long, description)
-
-
if long && long.end_with?('=')
-
long.sub!(/\=$/, '')
-
opt.config[:argument] = true
-
end
-
end
-
-
opts
-
end
-
-
end
-
-
# The Hash of configuration options for this Slop instance.
-
1
attr_reader :config
-
-
# The Array of Slop::Option objects tied to this Slop instance.
-
1
attr_reader :options
-
-
# The Hash of sub-commands for this Slop instance.
-
1
attr_reader :commands
-
-
# Create a new instance of Slop and optionally build options via a block.
-
#
-
# config - A Hash of configuration options.
-
# block - An optional block used to specify options.
-
1
def initialize(config = {}, &block)
-
1
@config = DEFAULT_OPTIONS.merge(config)
-
1
@options = []
-
1
@commands = {}
-
1
@trash = []
-
1
@triggered_options = []
-
1
@unknown_options = []
-
1
@callbacks = {}
-
1
@separators = {}
-
1
@runner = nil
-
1
@command = config.delete(:command)
-
-
1
if block_given?
-
1
block.arity == 1 ? yield(self) : instance_eval(&block)
-
end
-
-
1
if config[:help]
-
on('-h', '--help', 'Display this help message.', :tail => true) do
-
puts help
-
exit
-
end
-
end
-
end
-
-
# Is strict mode enabled?
-
#
-
# Returns true if strict mode is enabled, false otherwise.
-
1
def strict?
-
1
config[:strict]
-
end
-
-
# Set the banner.
-
#
-
# banner - The String to set the banner.
-
1
def banner=(banner)
-
config[:banner] = banner
-
end
-
-
# Get or set the banner.
-
#
-
# banner - The String to set the banner.
-
#
-
# Returns the banner String.
-
1
def banner(banner = nil)
-
config[:banner] = banner if banner
-
config[:banner]
-
end
-
-
# Set the description (used for commands).
-
#
-
# desc - The String to set the description.
-
1
def description=(desc)
-
config[:description] = desc
-
end
-
-
# Get or set the description (used for commands).
-
#
-
# desc - The String to set the description.
-
#
-
# Returns the description String.
-
1
def description(desc = nil)
-
config[:description] = desc if desc
-
config[:description]
-
end
-
-
# Add a new command.
-
#
-
# command - The Symbol or String used to identify this command.
-
# options - A Hash of configuration options (see Slop::new)
-
#
-
# Returns a new instance of Slop mapped to this command.
-
1
def command(command, options = {}, &block)
-
options = @config.merge(options)
-
@commands[command.to_s] = Slop.new(options.merge(:command => command.to_s), &block)
-
end
-
-
# Parse a list of items, executing and gathering options along the way.
-
#
-
# items - The Array of items to extract options from (default: ARGV).
-
# block - An optional block which when used will yield non options.
-
#
-
# Returns an Array of original items.
-
1
def parse(items = ARGV, &block)
-
1
parse! items.dup, &block
-
1
items
-
end
-
-
# Parse a list of items, executing and gathering options along the way.
-
# unlike parse() this method will remove any options and option arguments
-
# from the original Array.
-
#
-
# items - The Array of items to extract options from (default: ARGV).
-
# block - An optional block which when used will yield non options.
-
#
-
# Returns an Array of original items with options removed.
-
1
def parse!(items = ARGV, &block)
-
1
if items.empty? && @callbacks[:empty]
-
@callbacks[:empty].each { |cb| cb.call(self) }
-
return items
-
end
-
-
# reset the trash so it doesn't carry over if you parse multiple
-
# times with the same instance
-
1
@trash.clear
-
-
1
if cmd = @commands[items[0]]
-
items.shift
-
return cmd.parse! items
-
end
-
-
1
items.each_with_index do |item, index|
-
1
@trash << index && break if item == '--'
-
1
autocreate(items, index) if config[:autocreate]
-
1
process_item(items, index, &block) unless @trash.include?(index)
-
end
-
2
items.reject!.with_index { |item, index| @trash.include?(index) }
-
-
2
missing_options = options.select { |opt| opt.required? && opt.count < 1 }
-
1
if missing_options.any?
-
raise MissingOptionError,
-
"Missing required option(s): #{missing_options.map(&:key).join(', ')}"
-
end
-
-
1
if @unknown_options.any?
-
raise InvalidOptionError, "Unknown options #{@unknown_options.join(', ')}"
-
end
-
-
1
if @triggered_options.empty? && @callbacks[:no_options]
-
@callbacks[:no_options].each { |cb| cb.call(self) }
-
end
-
-
1
if @runner.respond_to?(:call)
-
@runner.call(self, items) unless config[:help] and present?(:help)
-
end
-
-
1
items
-
end
-
-
# Add an Option.
-
#
-
# objects - An Array with an optional Hash as the last element.
-
#
-
# Examples:
-
#
-
# on '-u', '--username=', 'Your username'
-
# on :v, :verbose, 'Enable verbose mode'
-
#
-
# Returns the created instance of Slop::Option.
-
1
def on(*objects, &block)
-
1
option = build_option(objects, &block)
-
1
original = options.find do |o|
-
o.long and o.long == option.long or o.short and o.short == option.short
-
end
-
1
options.delete(original) if original
-
1
options << option
-
1
option
-
end
-
1
alias option on
-
1
alias opt on
-
-
# Fetch an options argument value.
-
#
-
# key - The Symbol or String option short or long flag.
-
#
-
# Returns the Object value for this option, or nil.
-
1
def [](key)
-
option = fetch_option(key)
-
option.value if option
-
end
-
1
alias get []
-
-
# Returns a new Hash with option flags as keys and option values as values.
-
#
-
# include_commands - If true, merge options from all sub-commands.
-
1
def to_hash(include_commands = false)
-
hash = Hash[options.map { |opt| [opt.key.to_sym, opt.value] }]
-
if include_commands
-
@commands.each { |cmd, opts| hash.merge!(cmd.to_sym => opts.to_hash) }
-
end
-
hash
-
end
-
1
alias to_h to_hash
-
-
# Enumerable interface. Yields each Slop::Option.
-
1
def each(&block)
-
options.each(&block)
-
end
-
-
# Specify code to be executed when these options are parsed.
-
#
-
# callable - An object responding to a call method.
-
#
-
# yields - The instance of Slop parsing these options
-
# An Array of unparsed arguments
-
#
-
# Example:
-
#
-
# Slop.parse do
-
# on :v, :verbose
-
#
-
# run do |opts, args|
-
# puts "Arguments: #{args.inspect}" if opts.verbose?
-
# end
-
# end
-
1
def run(callable = nil, &block)
-
@runner = callable || block
-
unless @runner.respond_to?(:call)
-
raise ArgumentError, "You must specify a callable object or a block to #run"
-
end
-
end
-
-
# Check for an options presence.
-
#
-
# Examples:
-
#
-
# opts.parse %w( --foo )
-
# opts.present?(:foo) #=> true
-
# opts.present?(:bar) #=> false
-
#
-
# Returns true if all of the keys are present in the parsed arguments.
-
1
def present?(*keys)
-
keys.all? { |key| (opt = fetch_option(key)) && opt.count > 0 }
-
end
-
-
# Override this method so we can check if an option? method exists.
-
#
-
# Returns true if this option key exists in our list of options.
-
1
def respond_to_missing?(method_name, include_private = false)
-
options.any? { |o| o.key == method_name.to_s.chop } || super
-
end
-
-
# Fetch a list of options which were missing from the parsed list.
-
#
-
# Examples:
-
#
-
# opts = Slop.new do
-
# on :n, :name=
-
# on :p, :password=
-
# end
-
#
-
# opts.parse %w[ --name Lee ]
-
# opts.missing #=> ['password']
-
#
-
# Returns an Array of Strings representing missing options.
-
1
def missing
-
(options - @triggered_options).map(&:key)
-
end
-
-
# Fetch a Slop::Option object.
-
#
-
# key - The Symbol or String option key.
-
#
-
# Examples:
-
#
-
# opts.on(:foo, 'Something fooey', :argument => :optional)
-
# opt = opts.fetch_option(:foo)
-
# opt.class #=> Slop::Option
-
# opt.accepts_optional_argument? #=> true
-
#
-
# Returns an Option or nil if none were found.
-
1
def fetch_option(key)
-
options.find { |option| [option.long, option.short].include?(clean(key)) }
-
end
-
-
# Fetch a Slop object associated with this command.
-
#
-
# command - The String or Symbol name of the command.
-
#
-
# Examples:
-
#
-
# opts.command :foo do
-
# on :v, :verbose, 'Enable verbose mode'
-
# end
-
#
-
# # ruby run.rb foo -v
-
# opts.fetch_command(:foo).verbose? #=> true
-
1
def fetch_command(command)
-
@commands[command.to_s]
-
end
-
-
# Add a callback.
-
#
-
# label - The Symbol identifier to attach this callback.
-
#
-
# Returns nothing.
-
1
def add_callback(label, &block)
-
(@callbacks[label] ||= []) << block
-
end
-
-
# Add string separators between options.
-
#
-
# text - The String text to print.
-
1
def separator(text)
-
if @separators[options.size]
-
@separators[options.size] << "\n#{text}"
-
else
-
@separators[options.size] = text
-
end
-
end
-
-
# Print a handy Slop help string.
-
#
-
# Returns the banner followed by available option help strings.
-
1
def to_s
-
heads = options.reject(&:tail?)
-
tails = (options - heads)
-
opts = (heads + tails).select(&:help).map(&:to_s)
-
optstr = opts.each_with_index.map { |o, i|
-
(str = @separators[i + 1]) ? [o, str].join("\n") : o
-
}.join("\n")
-
-
if @commands.any?
-
optstr << "\n" if !optstr.empty?
-
optstr << "\nAvailable commands:\n\n"
-
optstr << commands_to_help
-
optstr << "\n\nSee `<command> --help` for more information on a specific command."
-
end
-
-
banner = config[:banner]
-
if banner.nil?
-
banner = "Usage: #{File.basename($0, '.*')}"
-
banner << " #{@command}" if @command
-
banner << " [command]" if @commands.any?
-
banner << " [options]"
-
end
-
if banner
-
"#{banner}\n#{@separators[0] ? "#{@separators[0]}\n" : ''}#{optstr}"
-
else
-
optstr
-
end
-
end
-
1
alias help to_s
-
-
1
private
-
-
# Convenience method for present?(:option).
-
#
-
# Examples:
-
#
-
# opts.parse %( --verbose )
-
# opts.verbose? #=> true
-
# opts.other? #=> false
-
#
-
# Returns true if this option is present. If this method does not end
-
# with a ? character it will instead call super().
-
1
def method_missing(method, *args, &block)
-
meth = method.to_s
-
if meth.end_with?('?')
-
meth.chop!
-
present?(meth) || present?(meth.gsub('_', '-'))
-
else
-
super
-
end
-
end
-
-
# Process a list item, figure out if it's an option, execute any
-
# callbacks, assign any option arguments, and do some sanity checks.
-
#
-
# items - The Array of items to process.
-
# index - The current Integer index of the item we want to process.
-
# block - An optional block which when passed will yield non options.
-
#
-
# Returns nothing.
-
1
def process_item(items, index, &block)
-
1
return unless item = items[index]
-
1
option, argument = extract_option(item) if item.start_with?('-')
-
-
1
if option
-
option.count += 1 unless item.start_with?('--no-')
-
option.count += 1 if option.key[0, 3] == "no-"
-
@trash << index
-
@triggered_options << option
-
-
if option.expects_argument?
-
argument ||= items.at(index + 1)
-
-
if !argument || argument =~ /\A--?[a-zA-Z][a-zA-Z0-9_-]*\z/
-
raise MissingArgumentError, "#{option.key} expects an argument"
-
end
-
-
execute_option(option, argument, index, item)
-
elsif option.accepts_optional_argument?
-
argument ||= items.at(index + 1)
-
-
if argument && argument =~ /\A([^\-?]|-\d)+/
-
execute_option(option, argument, index, item)
-
else
-
option.call(nil)
-
end
-
elsif config[:multiple_switches] && argument
-
execute_multiple_switches(option, argument, items, index)
-
else
-
option.value = option.count > 0
-
option.call(nil)
-
end
-
else
-
1
@unknown_options << item if strict? && item =~ /\A--?/
-
1
block.call(item) if block && !@trash.include?(index)
-
end
-
end
-
-
# Execute an option, firing off callbacks and assigning arguments.
-
#
-
# option - The Slop::Option object found by #process_item.
-
# argument - The argument Object to assign to this option.
-
# index - The current Integer index of the object we're processing.
-
# item - The optional String item we're processing.
-
#
-
# Returns nothing.
-
1
def execute_option(option, argument, index, item = nil)
-
if !option
-
if config[:multiple_switches] && strict?
-
raise InvalidOptionError, "Unknown option -#{item}"
-
end
-
return
-
end
-
-
if argument
-
unless item && item.end_with?("=#{argument}")
-
@trash << index + 1 unless option.argument_in_value
-
end
-
option.value = argument
-
else
-
option.value = option.count > 0
-
end
-
-
if option.match? && !argument.match(option.config[:match])
-
raise InvalidArgumentError, "#{argument} is an invalid argument"
-
end
-
-
option.call(option.value)
-
end
-
-
# Execute a `-abc` type option where a, b and c are all options. This
-
# method is only executed if the multiple_switches argument is true.
-
#
-
# option - The first Option object.
-
# argument - The argument to this option. (Split into multiple Options).
-
# items - The Array of items currently being parsed.
-
# index - The index of the current item being processed.
-
#
-
# Returns nothing.
-
1
def execute_multiple_switches(option, argument, items, index)
-
execute_option(option, nil, index)
-
flags = argument.split('')
-
flags.each do |key|
-
if opt = fetch_option(key)
-
opt.count += 1
-
if (opt.expects_argument? || opt.accepts_optional_argument?) &&
-
(flags[-1] == opt.key) && (val = items[index+1])
-
execute_option(opt, val, index, key)
-
else
-
execute_option(opt, nil, index, key)
-
end
-
else
-
raise InvalidOptionError, "Unknown option -#{key}" if strict?
-
end
-
end
-
end
-
-
# Extract an option from a flag.
-
#
-
# flag - The flag key used to extract an option.
-
#
-
# Returns an Array of [option, argument].
-
1
def extract_option(flag)
-
option = fetch_option(flag)
-
option ||= fetch_option(flag.downcase) if config[:ignore_case]
-
option ||= fetch_option(flag.gsub(/([^-])-/, '\1_'))
-
-
unless option
-
case flag
-
when /\A--?([^=]+)=(.+)\z/, /\A-([a-zA-Z])(.+)\z/, /\A--no-(.+)\z/
-
option, argument = fetch_option($1), ($2 || false)
-
option.argument_in_value = true if option
-
end
-
end
-
-
[option, argument]
-
end
-
-
# Autocreate an option on the fly. See the :autocreate Slop config option.
-
#
-
# items - The Array of items we're parsing.
-
# index - The current Integer index for the item we're processing.
-
#
-
# Returns nothing.
-
1
def autocreate(items, index)
-
flag = items[index]
-
if !fetch_option(flag) && !@trash.include?(index)
-
option = build_option(Array(flag))
-
argument = items[index + 1]
-
option.config[:argument] = (argument && argument !~ /\A--?/)
-
option.config[:autocreated] = true
-
options << option
-
end
-
end
-
-
# Build an option from a list of objects.
-
#
-
# objects - An Array of objects used to build this option.
-
#
-
# Returns a new instance of Slop::Option.
-
1
def build_option(objects, &block)
-
1
config = {}
-
1
config[:argument] = true if @config[:arguments]
-
1
config[:optional_argument] = true if @config[:optional_arguments]
-
-
1
if objects.last.is_a?(Hash)
-
config.merge!(objects.pop)
-
end
-
-
1
short = extract_short_flag(objects, config)
-
1
long = extract_long_flag(objects, config)
-
1
desc = objects.shift if objects[0].respond_to?(:to_str)
-
-
1
Option.new(self, short, long, desc, config, &block)
-
end
-
-
1
def extract_short_flag(objects, config)
-
1
flag = objects[0].to_s
-
1
if flag =~ /\A-?\w=?\z/
-
config[:argument] ||= flag.end_with?('=')
-
objects.shift
-
flag.delete('-=')
-
end
-
end
-
-
# Extract the long flag from an item.
-
#
-
# objects - The Array of objects passed from #build_option.
-
# config - The Hash of configuration options built in #build_option.
-
1
def extract_long_flag(objects, config)
-
1
flag = objects.first.to_s
-
1
if flag =~ /\A(?:--?)?[a-zA-Z0-9][a-zA-Z0-9_.-]+\=?\??\z/
-
1
config[:argument] ||= true if flag.end_with?('=')
-
1
config[:optional_argument] = true if flag.end_with?('=?')
-
1
objects.shift
-
1
clean(flag).sub(/\=\??\z/, '')
-
end
-
end
-
-
# Remove any leading -- characters from a string.
-
#
-
# object - The Object we want to cast to a String and clean.
-
#
-
# Returns the newly cleaned String with leading -- characters removed.
-
1
def clean(object)
-
1
object.to_s.sub(/\A--?/, '')
-
end
-
-
1
def commands_to_help
-
padding = 0
-
@commands.each { |c, _| padding = c.size if c.size > padding }
-
@commands.map do |cmd, opts|
-
" #{cmd}#{' ' * (padding - cmd.size)} #{opts.description}"
-
end.join("\n")
-
end
-
-
end
-
1
class Slop
-
1
class Commands
-
1
include Enumerable
-
-
1
attr_reader :config, :commands, :arguments
-
1
attr_writer :banner
-
-
# Create a new instance of Slop::Commands and optionally build
-
# Slop instances via a block. Any configuration options used in
-
# this method will be the default configuration options sent to
-
# each Slop object created.
-
#
-
# config - An optional configuration Hash.
-
# block - Optional block used to define commands.
-
#
-
# Examples:
-
#
-
# commands = Slop::Commands.new do
-
# on :new do
-
# on '-o', '--outdir=', 'The output directory'
-
# on '-v', '--verbose', 'Enable verbose mode'
-
# end
-
#
-
# on :generate do
-
# on '--assets', 'Generate assets', :default => true
-
# end
-
#
-
# global do
-
# on '-D', '--debug', 'Enable debug mode', :default => false
-
# end
-
# end
-
#
-
# commands[:new].class #=> Slop
-
# commands.parse
-
#
-
1
def initialize(config = {}, &block)
-
@config = config
-
@commands = {}
-
@banner = nil
-
@triggered_command = nil
-
-
warn "[DEPRECATED] Slop::Commands is deprecated and will be removed in "\
-
"Slop version 4. Check out http://leejarvis.github.io/slop/#commands for "\
-
"a new implementation of commands."
-
-
if block_given?
-
block.arity == 1 ? yield(self) : instance_eval(&block)
-
end
-
end
-
-
# Optionally set the banner for this command help output.
-
#
-
# banner - The String text to set the banner.
-
#
-
# Returns the String banner if one is set.
-
1
def banner(banner = nil)
-
@banner = banner if banner
-
@banner
-
end
-
-
# Add a Slop instance for a specific command.
-
#
-
# command - A String or Symbol key used to identify this command.
-
# config - A Hash of configuration options to pass to Slop.
-
# block - An optional block used to pass options to Slop.
-
#
-
# Returns the newly created Slop instance mapped to command.
-
1
def on(command, config = {}, &block)
-
commands[command.to_s] = Slop.new(@config.merge(config), &block)
-
end
-
-
# Add a Slop instance used when no other commands exist.
-
#
-
# config - A Hash of configuration options to pass to Slop.
-
# block - An optional block used to pass options to Slop.
-
#
-
# Returns the newly created Slop instance mapped to default.
-
1
def default(config = {}, &block)
-
on('default', config, &block)
-
end
-
-
# Add a global Slop instance.
-
#
-
# config - A Hash of configuration options to pass to Slop.
-
# block - An optional block used to pass options to Slop.
-
#
-
# Returns the newly created Slop instance mapped to global.
-
1
def global(config = {}, &block)
-
on('global', config, &block)
-
end
-
-
# Fetch the instance of Slop tied to a command.
-
#
-
# key - The String or Symbol key used to locate this command.
-
#
-
# Returns the Slop instance if this key is found, nil otherwise.
-
1
def [](key)
-
commands[key.to_s]
-
end
-
1
alias get []
-
-
# Check for a command presence.
-
#
-
# Examples:
-
#
-
# cmds.parse %w( foo )
-
# cmds.present?(:foo) #=> true
-
# cmds.present?(:bar) #=> false
-
#
-
# Returns true if the given key is present in the parsed arguments.
-
1
def present?(key)
-
key.to_s == @triggered_command
-
end
-
-
# Enumerable interface.
-
1
def each(&block)
-
@commands.each(&block)
-
end
-
-
# Parse a list of items.
-
#
-
# items - The Array of items to parse.
-
#
-
# Returns the original Array of items.
-
1
def parse(items = ARGV)
-
parse! items.dup
-
items
-
end
-
-
# Parse a list of items, removing any options or option arguments found.
-
#
-
# items - The Array of items to parse.
-
#
-
# Returns the original Array of items with options removed.
-
1
def parse!(items = ARGV)
-
if opts = commands[items[0].to_s]
-
@triggered_command = items.shift
-
execute_arguments! items
-
opts.parse! items
-
execute_global_opts! items
-
else
-
if opts = commands['default']
-
opts.parse! items
-
else
-
if config[:strict] && items[0]
-
raise InvalidCommandError, "Unknown command `#{items[0]}`"
-
end
-
end
-
execute_global_opts! items
-
end
-
items
-
end
-
-
# Returns a nested Hash with Slop options and values. See Slop#to_hash.
-
1
def to_hash
-
Hash[commands.map { |k, v| [k.to_sym, v.to_hash] }]
-
end
-
-
# Returns the help String.
-
1
def to_s
-
defaults = commands.delete('default')
-
globals = commands.delete('global')
-
helps = commands.reject { |_, v| v.options.none? }
-
if globals && globals.options.any?
-
helps.merge!('Global options' => globals.to_s)
-
end
-
if defaults && defaults.options.any?
-
helps.merge!('Other options' => defaults.to_s)
-
end
-
banner = @banner ? "#{@banner}\n" : ""
-
banner + helps.map { |key, opts| " #{key}\n#{opts}" }.join("\n\n")
-
end
-
1
alias help to_s
-
-
# Returns the inspection String.
-
1
def inspect
-
"#<Slop::Commands #{config.inspect} #{commands.values.map(&:inspect)}>"
-
end
-
-
1
private
-
-
# Returns nothing.
-
1
def execute_arguments!(items)
-
@arguments = items.take_while { |arg| !arg.start_with?('-') }
-
items.shift @arguments.size
-
end
-
-
# Returns nothing.
-
1
def execute_global_opts!(items)
-
if global_opts = commands['global']
-
global_opts.parse! items
-
end
-
end
-
-
end
-
end
-
1
class Slop
-
1
class Option
-
-
# The default Hash of configuration options this class uses.
-
1
DEFAULT_OPTIONS = {
-
:argument => false,
-
:optional_argument => false,
-
:tail => false,
-
:default => nil,
-
:callback => nil,
-
:delimiter => ',',
-
:limit => 0,
-
:match => nil,
-
:optional => true,
-
:required => false,
-
:as => String,
-
:autocreated => false
-
}
-
-
1
attr_reader :short, :long, :description, :config, :types
-
1
attr_accessor :count, :argument_in_value
-
-
# Incapsulate internal option information, mainly used to store
-
# option specific configuration data, most of the meat of this
-
# class is found in the #value method.
-
#
-
# slop - The instance of Slop tied to this Option.
-
# short - The String or Symbol short flag.
-
# long - The String or Symbol long flag.
-
# description - The String description text.
-
# config - A Hash of configuration options.
-
# block - An optional block used as a callback.
-
1
def initialize(slop, short, long, description, config = {}, &block)
-
1
@slop = slop
-
1
@short = short
-
1
@long = long
-
1
@description = description
-
1
@config = DEFAULT_OPTIONS.merge(config)
-
1
@count = 0
-
1
@callback = block_given? ? block : config[:callback]
-
1
@value = nil
-
-
1
@types = {
-
:string => proc { |v| v.to_s },
-
:symbol => proc { |v| v.to_sym },
-
:integer => proc { |v| value_to_integer(v) },
-
:float => proc { |v| value_to_float(v) },
-
:range => proc { |v| value_to_range(v) },
-
:regexp => proc { |v| Regexp.new(v) },
-
:count => proc { |v| @count }
-
}
-
-
1
if long && long.size > @slop.config[:longest_flag]
-
1
@slop.config[:longest_flag] = long.size
-
end
-
-
1
@config.each_key do |key|
-
12
predicate = :"#{key}?"
-
12
unless self.class.method_defined? predicate
-
13
self.class.__send__(:define_method, predicate) { !!@config[key] }
-
end
-
end
-
end
-
-
# Returns true if this option expects an argument.
-
1
def expects_argument?
-
config[:argument] && config[:argument] != :optional
-
end
-
-
# Returns true if this option accepts an optional argument.
-
1
def accepts_optional_argument?
-
config[:optional_argument] || config[:argument] == :optional
-
end
-
-
# Returns the String flag of this option. Preferring the long flag.
-
1
def key
-
long || short
-
end
-
-
# Call this options callback if one exists, and it responds to call().
-
#
-
# Returns nothing.
-
1
def call(*objects)
-
@callback.call(*objects) if @callback.respond_to?(:call)
-
end
-
-
# Set the new argument value for this option.
-
#
-
# We use this setter method to handle concatenating lists. That is,
-
# when an array type is specified and used more than once, values from
-
# both options will be grouped together and flattened into a single array.
-
1
def value=(new_value)
-
if config[:as].to_s.downcase == 'array'
-
@value ||= []
-
-
if new_value.respond_to?(:split)
-
@value.concat new_value.split(config[:delimiter], config[:limit])
-
end
-
else
-
@value = new_value
-
end
-
end
-
-
# Fetch the argument value for this option.
-
#
-
# Returns the Object once any type conversions have taken place.
-
1
def value
-
value = @value.nil? ? config[:default] : @value
-
-
if [true, false, nil].include?(value) && config[:as].to_s != 'count'
-
return value
-
end
-
-
type = config[:as]
-
if type.respond_to?(:call)
-
type.call(value)
-
else
-
if callable = types[type.to_s.downcase.to_sym]
-
callable.call(value)
-
else
-
value
-
end
-
end
-
end
-
-
# Returns the help String for this option.
-
1
def to_s
-
return config[:help] if config[:help].respond_to?(:to_str)
-
-
out = " #{short ? "-#{short}, " : ' ' * 4}"
-
-
if long
-
out << "--#{long}"
-
size = long.size
-
diff = @slop.config[:longest_flag] - size
-
out << (' ' * (diff + 6))
-
else
-
out << (' ' * (@slop.config[:longest_flag] + 8))
-
end
-
-
if config[:default]
-
default = config[:default]
-
"#{out}#{description} (default: #{default})"
-
else
-
"#{out}#{description}"
-
end
-
end
-
1
alias help to_s
-
-
# Returns the String inspection text.
-
1
def inspect
-
"#<Slop::Option [-#{short} | --#{long}" +
-
"#{'=' if expects_argument?}#{'=?' if accepts_optional_argument?}]" +
-
" (#{description}) #{config.inspect}"
-
end
-
-
1
private
-
-
# Convert an object to an Integer if possible.
-
#
-
# value - The Object we want to convert to an integer.
-
#
-
# Returns the Integer value if possible to convert, else a zero.
-
1
def value_to_integer(value)
-
if @slop.strict?
-
begin
-
Integer(value.to_s, 10)
-
rescue ArgumentError
-
raise InvalidArgumentError, "#{value} could not be coerced into Integer"
-
end
-
else
-
value.to_s.to_i
-
end
-
end
-
-
# Convert an object to a Float if possible.
-
#
-
# value - The Object we want to convert to a float.
-
#
-
# Returns the Float value if possible to convert, else a zero.
-
1
def value_to_float(value)
-
if @slop.strict?
-
begin
-
Float(value.to_s)
-
rescue ArgumentError
-
raise InvalidArgumentError, "#{value} could not be coerced into Float"
-
end
-
else
-
value.to_s.to_f
-
end
-
end
-
-
# Convert an object to a Range if possible.
-
#
-
# value - The Object we want to convert to a range.
-
#
-
# Returns the Range value if one could be found, else the original object.
-
1
def value_to_range(value)
-
case value.to_s
-
when /\A(\-?\d+)\z/
-
Range.new($1.to_i, $1.to_i)
-
when /\A(-?\d+?)(\.\.\.?|-|,)(-?\d+)\z/
-
Range.new($1.to_i, $3.to_i, $2 == '...')
-
else
-
if @slop.strict?
-
raise InvalidArgumentError, "#{value} could not be coerced into Range"
-
else
-
value
-
end
-
end
-
end
-
-
end
-
end
-
1
require 'sprockets/version'
-
-
1
module Sprockets
-
# Environment
-
1
autoload :Base, "sprockets/base"
-
1
autoload :Environment, "sprockets/environment"
-
1
autoload :Index, "sprockets/index"
-
1
autoload :Manifest, "sprockets/manifest"
-
-
# Assets
-
1
autoload :Asset, "sprockets/asset"
-
1
autoload :BundledAsset, "sprockets/bundled_asset"
-
1
autoload :ProcessedAsset, "sprockets/processed_asset"
-
1
autoload :StaticAsset, "sprockets/static_asset"
-
-
# Processing
-
1
autoload :Context, "sprockets/context"
-
1
autoload :EcoTemplate, "sprockets/eco_template"
-
1
autoload :EjsTemplate, "sprockets/ejs_template"
-
1
autoload :JstProcessor, "sprockets/jst_processor"
-
1
autoload :Processor, "sprockets/processor"
-
1
autoload :SassCacheStore, "sprockets/sass_cache_store"
-
1
autoload :SassFunctions, "sprockets/sass_functions"
-
1
autoload :SassImporter, "sprockets/sass_importer"
-
1
autoload :SassTemplate, "sprockets/sass_template"
-
1
autoload :ScssTemplate, "sprockets/scss_template"
-
-
# Internal utilities
-
1
autoload :ArgumentError, "sprockets/errors"
-
1
autoload :AssetAttributes, "sprockets/asset_attributes"
-
1
autoload :CircularDependencyError, "sprockets/errors"
-
1
autoload :ContentTypeMismatch, "sprockets/errors"
-
1
autoload :EngineError, "sprockets/errors"
-
1
autoload :Error, "sprockets/errors"
-
1
autoload :FileNotFound, "sprockets/errors"
-
1
autoload :Utils, "sprockets/utils"
-
-
1
module Cache
-
1
autoload :FileStore, "sprockets/cache/file_store"
-
end
-
-
# Extend Sprockets module to provide global registry
-
1
require 'hike'
-
1
require 'sprockets/engines'
-
1
require 'sprockets/mime'
-
1
require 'sprockets/processing'
-
1
require 'sprockets/compressing'
-
1
require 'sprockets/paths'
-
1
extend Engines, Mime, Processing, Compressing, Paths
-
-
1
@trail = Hike::Trail.new(File.expand_path('..', __FILE__))
-
1
@mime_types = {}
-
1
@engines = {}
-
3
@preprocessors = Hash.new { |h, k| h[k] = [] }
-
2
@postprocessors = Hash.new { |h, k| h[k] = [] }
-
2
@bundle_processors = Hash.new { |h, k| h[k] = [] }
-
3
@compressors = Hash.new { |h, k| h[k] = {} }
-
-
1
register_mime_type 'text/css', '.css'
-
1
register_mime_type 'application/javascript', '.js'
-
-
1
require 'sprockets/directive_processor'
-
1
register_preprocessor 'text/css', DirectiveProcessor
-
1
register_preprocessor 'application/javascript', DirectiveProcessor
-
-
1
require 'sprockets/safety_colons'
-
1
register_postprocessor 'application/javascript', SafetyColons
-
-
1
require 'sprockets/charset_normalizer'
-
1
register_bundle_processor 'text/css', CharsetNormalizer
-
-
1
require 'sprockets/sass_compressor'
-
1
register_compressor 'text/css', :sass, SassCompressor
-
1
register_compressor 'text/css', :scss, SassCompressor
-
-
1
require 'sprockets/yui_compressor'
-
1
register_compressor 'text/css', :yui, YUICompressor
-
-
1
require 'sprockets/closure_compressor'
-
1
register_compressor 'application/javascript', :closure, ClosureCompressor
-
-
1
require 'sprockets/uglifier_compressor'
-
1
register_compressor 'application/javascript', :uglifier, UglifierCompressor
-
1
register_compressor 'application/javascript', :uglify, UglifierCompressor
-
-
1
require 'sprockets/yui_compressor'
-
1
register_compressor 'application/javascript', :yui, YUICompressor
-
-
# Cherry pick the default Tilt engines that make sense for
-
# Sprockets. We don't need ones that only generate html like HAML.
-
-
# Mmm, CoffeeScript
-
1
register_engine '.coffee', Tilt::CoffeeScriptTemplate
-
-
# JST engines
-
1
register_engine '.jst', JstProcessor
-
1
register_engine '.eco', EcoTemplate
-
1
register_engine '.ejs', EjsTemplate
-
-
# CSS engines
-
1
register_engine '.less', Tilt::LessTemplate
-
1
register_engine '.sass', SassTemplate
-
1
register_engine '.scss', ScssTemplate
-
-
# Other
-
1
register_engine '.erb', Tilt::ERBTemplate
-
1
register_engine '.str', Tilt::StringTemplate
-
end
-
1
require 'time'
-
1
require 'set'
-
-
1
module Sprockets
-
# `Asset` is the base class for `BundledAsset` and `StaticAsset`.
-
1
class Asset
-
# Internal initializer to load `Asset` from serialized `Hash`.
-
1
def self.from_hash(environment, hash)
-
return unless hash.is_a?(Hash)
-
-
klass = case hash['class']
-
when 'BundledAsset'
-
BundledAsset
-
when 'ProcessedAsset'
-
ProcessedAsset
-
when 'StaticAsset'
-
StaticAsset
-
else
-
nil
-
end
-
-
if klass
-
asset = klass.allocate
-
asset.init_with(environment, hash)
-
asset
-
end
-
rescue UnserializeError
-
nil
-
end
-
-
1
attr_reader :logical_path, :pathname
-
1
attr_reader :content_type, :mtime, :length, :digest
-
1
alias_method :bytesize, :length
-
-
1
def initialize(environment, logical_path, pathname)
-
raise ArgumentError, "Asset logical path has no extension: #{logical_path}" if File.extname(logical_path) == ""
-
-
@root = environment.root
-
@logical_path = logical_path.to_s
-
@pathname = Pathname.new(pathname)
-
@content_type = environment.content_type_of(pathname)
-
# drop precision to 1 second, same pattern followed elsewhere
-
@mtime = Time.at(environment.stat(pathname).mtime.to_i)
-
@length = environment.stat(pathname).size
-
@digest = environment.file_digest(pathname).hexdigest
-
end
-
-
# Initialize `Asset` from serialized `Hash`.
-
1
def init_with(environment, coder)
-
@root = environment.root
-
-
@logical_path = coder['logical_path']
-
@content_type = coder['content_type']
-
@digest = coder['digest']
-
-
if pathname = coder['pathname']
-
# Expand `$root` placeholder and wrapper string in a `Pathname`
-
@pathname = Pathname.new(expand_root_path(pathname))
-
end
-
-
if mtime = coder['mtime']
-
@mtime = Time.at(mtime)
-
end
-
-
if length = coder['length']
-
# Convert length to an `Integer`
-
@length = Integer(length)
-
end
-
end
-
-
# Copy serialized attributes to the coder object
-
1
def encode_with(coder)
-
coder['class'] = self.class.name.sub(/Sprockets::/, '')
-
coder['logical_path'] = logical_path
-
coder['pathname'] = relativize_root_path(pathname).to_s
-
coder['content_type'] = content_type
-
coder['mtime'] = mtime.to_i
-
coder['length'] = length
-
coder['digest'] = digest
-
end
-
-
# Return logical path with digest spliced in.
-
#
-
# "foo/bar-37b51d194a7513e45b56f6524f2d51f2.js"
-
#
-
1
def digest_path
-
logical_path.sub(/\.(\w+)$/) { |ext| "-#{digest}#{ext}" }
-
end
-
-
# Return an `Array` of `Asset` files that are declared dependencies.
-
1
def dependencies
-
[]
-
end
-
-
# Expand asset into an `Array` of parts.
-
#
-
# Appending all of an assets body parts together should give you
-
# the asset's contents as a whole.
-
#
-
# This allows you to link to individual files for debugging
-
# purposes.
-
1
def to_a
-
[self]
-
end
-
-
# `body` is aliased to source by default if it can't have any dependencies.
-
1
def body
-
source
-
end
-
-
# Return `String` of concatenated source.
-
1
def to_s
-
source
-
end
-
-
# Add enumerator to allow `Asset` instances to be used as Rack
-
# compatible body objects.
-
1
def each
-
yield to_s
-
end
-
-
# Checks if Asset is fresh by comparing the actual mtime and
-
# digest to the inmemory model.
-
#
-
# Used to test if cached models need to be rebuilt.
-
1
def fresh?(environment)
-
# Check current mtime and digest
-
dependency_fresh?(environment, self)
-
end
-
-
# Checks if Asset is stale by comparing the actual mtime and
-
# digest to the inmemory model.
-
#
-
# Subclass must override `fresh?` or `stale?`.
-
1
def stale?(environment)
-
!fresh?(environment)
-
end
-
-
# Save asset to disk.
-
1
def write_to(filename, options = {})
-
# Gzip contents if filename has '.gz'
-
options[:compress] ||= File.extname(filename) == '.gz'
-
-
FileUtils.mkdir_p File.dirname(filename)
-
-
File.open("#{filename}+", 'wb') do |f|
-
if options[:compress]
-
# Run contents through `Zlib`
-
gz = Zlib::GzipWriter.new(f, Zlib::BEST_COMPRESSION)
-
gz.mtime = mtime.to_i
-
gz.write to_s
-
gz.close
-
else
-
# Write out as is
-
f.write to_s
-
end
-
end
-
-
# Atomic write
-
FileUtils.mv("#{filename}+", filename)
-
-
# Set mtime correctly
-
File.utime(mtime, mtime, filename)
-
-
nil
-
ensure
-
# Ensure tmp file gets cleaned up
-
FileUtils.rm("#{filename}+") if File.exist?("#{filename}+")
-
end
-
-
# Pretty inspect
-
1
def inspect
-
"#<#{self.class}:0x#{object_id.to_s(16)} " +
-
"pathname=#{pathname.to_s.inspect}, " +
-
"mtime=#{mtime.inspect}, " +
-
"digest=#{digest.inspect}" +
-
">"
-
end
-
-
1
def hash
-
digest.hash
-
end
-
-
# Assets are equal if they share the same path, mtime and digest.
-
1
def eql?(other)
-
other.class == self.class &&
-
other.logical_path == self.logical_path &&
-
other.mtime.to_i == self.mtime.to_i &&
-
other.digest == self.digest
-
end
-
1
alias_method :==, :eql?
-
-
1
protected
-
# Internal: String paths that are marked as dependencies after processing.
-
#
-
# Default to an empty `Array`.
-
1
def dependency_paths
-
@dependency_paths ||= []
-
end
-
-
# Internal: `ProccessedAsset`s that are required after processing.
-
#
-
# Default to an empty `Array`.
-
1
def required_assets
-
@required_assets ||= []
-
end
-
-
# Get pathname with its root stripped.
-
1
def relative_pathname
-
@relative_pathname ||= Pathname.new(relativize_root_path(pathname))
-
end
-
-
# Replace `$root` placeholder with actual environment root.
-
1
def expand_root_path(path)
-
path.to_s.sub(/^\$root/, @root)
-
end
-
-
# Replace actual environment root with `$root` placeholder.
-
1
def relativize_root_path(path)
-
path.to_s.sub(/^#{Regexp.escape(@root)}/, '$root')
-
end
-
-
# Check if dependency is fresh.
-
#
-
# `dep` is a `Hash` with `path`, `mtime` and `hexdigest` keys.
-
#
-
# A `Hash` is used rather than other `Asset` object because we
-
# want to test non-asset files and directories.
-
1
def dependency_fresh?(environment, dep)
-
path, mtime, hexdigest = dep.pathname.to_s, dep.mtime, dep.digest
-
-
stat = environment.stat(path)
-
-
# If path no longer exists, its definitely stale.
-
if stat.nil?
-
return false
-
end
-
-
# Compare dependency mtime to the actual mtime. If the
-
# dependency mtime is newer than the actual mtime, the file
-
# hasn't changed since we created this `Asset` instance.
-
#
-
# However, if the mtime is newer it doesn't mean the asset is
-
# stale. Many deployment environments may recopy or recheckout
-
# assets on each deploy. In this case the mtime would be the
-
# time of deploy rather than modified time.
-
#
-
# Note: to_i is used in eql? and write_to we assume fidelity of 1 second
-
# if people save files more frequently than 1 second sprockets may
-
# not pick it up, by design
-
if mtime.to_i >= stat.mtime.to_i
-
return true
-
end
-
-
digest = environment.file_digest(path)
-
-
# If the mtime is newer, do a full digest comparsion. Return
-
# fresh if the digests match.
-
if hexdigest == digest.hexdigest
-
return true
-
end
-
-
# Otherwise, its stale.
-
false
-
end
-
end
-
end
-
1
require 'pathname'
-
-
1
module Sprockets
-
# `AssetAttributes` is a wrapper similar to `Pathname` that provides
-
# some helper accessors.
-
#
-
# These methods should be considered internalish.
-
1
class AssetAttributes
-
1
attr_reader :environment, :pathname
-
-
1
def initialize(environment, path)
-
@environment = environment
-
@pathname = path.is_a?(Pathname) ? path : Pathname.new(path.to_s)
-
end
-
-
# Returns paths search the load path for.
-
1
def search_paths
-
paths = [pathname.to_s]
-
-
extension = format_extension
-
path_without_extension = extension ?
-
pathname.sub(extension, '') :
-
pathname
-
-
# optimization: bower.json can only be nested one level deep
-
if !path_without_extension.to_s.index('/')
-
paths << path_without_extension.join(".bower.json").to_s
-
paths << path_without_extension.join("bower.json").to_s
-
# DEPRECATED bower configuration file
-
paths << path_without_extension.join("component.json").to_s
-
end
-
-
if pathname.basename(extension.to_s).to_s != 'index'
-
paths << path_without_extension.join("index#{extension}").to_s
-
end
-
-
paths
-
end
-
-
# Reverse guess logical path for fully expanded path.
-
#
-
# This has some known issues. For an example if a file is
-
# shaddowed in the path, but is required relatively, its logical
-
# path will be incorrect.
-
1
def logical_path
-
if root_path = environment.paths.detect { |path| pathname.to_s[path] }
-
path = pathname.to_s.sub("#{root_path}/", '')
-
path = pathname.relative_path_from(Pathname.new(root_path)).to_s
-
path = engine_extensions.inject(path) { |p, ext| p.sub(ext, '') }
-
path = "#{path}#{engine_format_extension}" unless format_extension
-
path
-
else
-
raise FileOutsidePaths, "#{pathname} isn't in paths: #{environment.paths.join(', ')}"
-
end
-
end
-
-
# Returns `Array` of extension `String`s.
-
#
-
# "foo.js.coffee"
-
# # => [".js", ".coffee"]
-
#
-
1
def extensions
-
@extensions ||= @pathname.basename.to_s.scan(/\.[^.]+/)
-
end
-
-
# Returns the format extension.
-
#
-
# "foo.js.coffee"
-
# # => ".js"
-
#
-
1
def format_extension
-
extensions.reverse.detect { |ext|
-
@environment.mime_types(ext) && !@environment.engines(ext)
-
}
-
end
-
-
# Returns an `Array` of engine extensions.
-
#
-
# "foo.js.coffee.erb"
-
# # => [".coffee", ".erb"]
-
#
-
1
def engine_extensions
-
exts = extensions
-
-
if offset = extensions.index(format_extension)
-
exts = extensions[offset+1..-1]
-
end
-
-
exts.select { |ext| @environment.engines(ext) }
-
end
-
-
# Returns engine classes.
-
1
def engines
-
engine_extensions.map { |ext| @environment.engines(ext) }
-
end
-
-
# Returns all processors to run on the path.
-
1
def processors
-
environment.preprocessors(content_type) +
-
engines.reverse +
-
environment.postprocessors(content_type)
-
end
-
-
# Returns the content type for the pathname. Falls back to `application/octet-stream`.
-
1
def content_type
-
@content_type ||= begin
-
if format_extension.nil?
-
engine_content_type || 'application/octet-stream'
-
else
-
@environment.mime_types(format_extension) ||
-
engine_content_type ||
-
'application/octet-stream'
-
end
-
end
-
end
-
-
1
private
-
# Returns implicit engine content type.
-
#
-
# `.coffee` files carry an implicit `application/javascript`
-
# content type.
-
1
def engine_content_type
-
engines.reverse.each do |engine|
-
if engine.respond_to?(:default_mime_type) && engine.default_mime_type
-
return engine.default_mime_type
-
end
-
end
-
nil
-
end
-
-
1
def engine_format_extension
-
if content_type = engine_content_type
-
environment.extension_for_mime_type(content_type)
-
end
-
end
-
end
-
end
-
1
require 'sprockets/asset_attributes'
-
1
require 'sprockets/bundled_asset'
-
1
require 'sprockets/caching'
-
1
require 'sprockets/errors'
-
1
require 'sprockets/processed_asset'
-
1
require 'sprockets/server'
-
1
require 'sprockets/static_asset'
-
1
require 'multi_json'
-
1
require 'pathname'
-
-
1
module Sprockets
-
# `Base` class for `Environment` and `Index`.
-
1
class Base
-
1
include Caching, Paths, Mime, Processing, Compressing, Engines, Server
-
-
# Returns a `Digest` implementation class.
-
#
-
# Defaults to `Digest::MD5`.
-
1
attr_reader :digest_class
-
-
# Assign a `Digest` implementation class. This maybe any Ruby
-
# `Digest::` implementation such as `Digest::MD5` or
-
# `Digest::SHA1`.
-
#
-
# environment.digest_class = Digest::SHA1
-
#
-
1
def digest_class=(klass)
-
expire_index!
-
@digest_class = klass
-
end
-
-
# The `Environment#version` is a custom value used for manually
-
# expiring all asset caches.
-
#
-
# Sprockets is able to track most file and directory changes and
-
# will take care of expiring the cache for you. However, its
-
# impossible to know when any custom helpers change that you mix
-
# into the `Context`.
-
#
-
# It would be wise to increment this value anytime you make a
-
# configuration change to the `Environment` object.
-
1
attr_reader :version
-
-
# Assign an environment version.
-
#
-
# environment.version = '2.0'
-
#
-
1
def version=(version)
-
2
expire_index!
-
2
@version = version
-
end
-
-
# Returns a `Digest` instance for the `Environment`.
-
#
-
# This value serves two purposes. If two `Environment`s have the
-
# same digest value they can be treated as equal. This is more
-
# useful for comparing environment states between processes rather
-
# than in the same. Two equal `Environment`s can share the same
-
# cached assets.
-
#
-
# The value also provides a seed digest for all `Asset`
-
# digests. Any change in the environment digest will affect all of
-
# its assets.
-
1
def digest
-
# Compute the initial digest using the implementation class. The
-
# Sprockets release version and custom environment version are
-
# mixed in. So any new releases will affect all your assets.
-
1
@digest ||= digest_class.new.update(VERSION).update(version.to_s)
-
-
# Returned a dupped copy so the caller can safely mutate it with `.update`
-
1
@digest.dup
-
end
-
-
# Get and set `Logger` instance.
-
1
attr_accessor :logger
-
-
# Get `Context` class.
-
#
-
# This class maybe mutated and mixed in with custom helpers.
-
#
-
# environment.context_class.instance_eval do
-
# include MyHelpers
-
# def asset_url; end
-
# end
-
#
-
1
attr_reader :context_class
-
-
# Get persistent cache store
-
1
attr_reader :cache
-
-
# Set persistent cache store
-
#
-
# The cache store must implement a pair of getters and
-
# setters. Either `get(key)`/`set(key, value)`,
-
# `[key]`/`[key]=value`, `read(key)`/`write(key, value)`.
-
1
def cache=(cache)
-
1
expire_index!
-
1
@cache = cache
-
end
-
-
1
def prepend_path(path)
-
# Overrides the global behavior to expire the index
-
expire_index!
-
super
-
end
-
-
1
def append_path(path)
-
# Overrides the global behavior to expire the index
-
13
expire_index!
-
13
super
-
end
-
-
1
def clear_paths
-
# Overrides the global behavior to expire the index
-
expire_index!
-
super
-
end
-
-
# Finds the expanded real path for a given logical path by
-
# searching the environment's paths.
-
#
-
# resolve("application.js")
-
# # => "/path/to/app/javascripts/application.js.coffee"
-
#
-
# A `FileNotFound` exception is raised if the file does not exist.
-
1
def resolve(logical_path, options = {})
-
# If a block is given, preform an iterable search
-
if block_given?
-
args = attributes_for(logical_path).search_paths + [options]
-
@trail.find(*args) do |path|
-
pathname = Pathname.new(path)
-
if %w( .bower.json bower.json component.json ).include?(pathname.basename.to_s)
-
bower = json_decode(pathname.read)
-
case bower['main']
-
when String
-
yield pathname.dirname.join(bower['main'])
-
when Array
-
extname = File.extname(logical_path)
-
bower['main'].each do |fn|
-
if extname == "" || extname == File.extname(fn)
-
yield pathname.dirname.join(fn)
-
end
-
end
-
end
-
else
-
yield pathname
-
end
-
end
-
else
-
resolve(logical_path, options) do |pathname|
-
return pathname
-
end
-
raise FileNotFound, "couldn't find file '#{logical_path}'"
-
end
-
end
-
-
# Register a new mime type.
-
1
def register_mime_type(mime_type, ext)
-
# Overrides the global behavior to expire the index
-
expire_index!
-
@trail.append_extension(ext)
-
super
-
end
-
-
# Registers a new Engine `klass` for `ext`.
-
1
def register_engine(ext, klass)
-
# Overrides the global behavior to expire the index
-
expire_index!
-
add_engine_to_trail(ext, klass)
-
super
-
end
-
-
1
def register_preprocessor(mime_type, klass, &block)
-
# Overrides the global behavior to expire the index
-
expire_index!
-
super
-
end
-
-
1
def unregister_preprocessor(mime_type, klass)
-
# Overrides the global behavior to expire the index
-
expire_index!
-
super
-
end
-
-
1
def register_postprocessor(mime_type, klass, &block)
-
# Overrides the global behavior to expire the index
-
expire_index!
-
super
-
end
-
-
1
def unregister_postprocessor(mime_type, klass)
-
# Overrides the global behavior to expire the index
-
expire_index!
-
super
-
end
-
-
1
def register_bundle_processor(mime_type, klass, &block)
-
# Overrides the global behavior to expire the index
-
1
expire_index!
-
1
super
-
end
-
-
1
def unregister_bundle_processor(mime_type, klass)
-
# Overrides the global behavior to expire the index
-
expire_index!
-
super
-
end
-
-
# Return an `Index`. Must be implemented by the subclass.
-
1
def index
-
raise NotImplementedError
-
end
-
-
1
if defined? Encoding.default_external
-
# Define `default_external_encoding` accessor on 1.9.
-
# Defaults to UTF-8.
-
1
attr_accessor :default_external_encoding
-
end
-
-
# Works like `Dir.entries`.
-
#
-
# Subclasses may cache this method.
-
1
def entries(pathname)
-
@trail.entries(pathname)
-
end
-
-
# Works like `File.stat`.
-
#
-
# Subclasses may cache this method.
-
1
def stat(path)
-
@trail.stat(path)
-
end
-
-
# Read and compute digest of filename.
-
#
-
# Subclasses may cache this method.
-
1
def file_digest(path)
-
if stat = self.stat(path)
-
# If its a file, digest the contents
-
if stat.file?
-
digest.file(path.to_s)
-
-
# If its a directive, digest the list of filenames
-
elsif stat.directory?
-
contents = self.entries(path).join(',')
-
digest.update(contents)
-
end
-
end
-
end
-
-
# Internal. Return a `AssetAttributes` for `path`.
-
1
def attributes_for(path)
-
AssetAttributes.new(self, path)
-
end
-
-
# Internal. Return content type of `path`.
-
1
def content_type_of(path)
-
attributes_for(path).content_type
-
end
-
-
# Find asset by logical path or expanded path.
-
1
def find_asset(path, options = {})
-
logical_path = path
-
pathname = Pathname.new(path)
-
-
if pathname.absolute?
-
return unless stat(pathname)
-
logical_path = attributes_for(pathname).logical_path
-
else
-
begin
-
pathname = resolve(logical_path)
-
-
# If logical path is missing a mime type extension, append
-
# the absolute path extname so it has one.
-
#
-
# Ensures some consistency between finding "foo/bar" vs
-
# "foo/bar.js".
-
if File.extname(logical_path) == ""
-
expanded_logical_path = attributes_for(pathname).logical_path
-
logical_path += File.extname(expanded_logical_path)
-
end
-
rescue FileNotFound
-
return nil
-
end
-
end
-
-
build_asset(logical_path, pathname, options)
-
end
-
-
# Preferred `find_asset` shorthand.
-
#
-
# environment['application.js']
-
#
-
1
def [](*args)
-
find_asset(*args)
-
end
-
-
1
def each_entry(root, &block)
-
return to_enum(__method__, root) unless block_given?
-
root = Pathname.new(root) unless root.is_a?(Pathname)
-
-
paths = []
-
entries(root).sort.each do |filename|
-
path = root.join(filename)
-
paths << path
-
-
if stat(path).directory?
-
each_entry(path) do |subpath|
-
paths << subpath
-
end
-
end
-
end
-
-
paths.sort_by(&:to_s).each(&block)
-
-
nil
-
end
-
-
1
def each_file
-
return to_enum(__method__) unless block_given?
-
paths.each do |root|
-
each_entry(root) do |path|
-
if !stat(path).directory?
-
yield path
-
end
-
end
-
end
-
nil
-
end
-
-
1
def each_logical_path(*args, &block)
-
return to_enum(__method__, *args) unless block_given?
-
filters = args.flatten
-
files = {}
-
each_file do |filename|
-
if logical_path = logical_path_for_filename(filename, filters)
-
unless files[logical_path]
-
if block.arity == 2
-
yield logical_path, filename.to_s
-
else
-
yield logical_path
-
end
-
end
-
-
files[logical_path] = true
-
end
-
end
-
nil
-
end
-
-
# Pretty inspect
-
1
def inspect
-
"#<#{self.class}:0x#{object_id.to_s(16)} " +
-
"root=#{root.to_s.inspect}, " +
-
"paths=#{paths.inspect}, " +
-
"digest=#{digest.to_s.inspect}" +
-
">"
-
end
-
-
1
protected
-
# Clear index after mutating state. Must be implemented by the subclass.
-
1
def expire_index!
-
raise NotImplementedError
-
end
-
-
1
def build_asset(logical_path, pathname, options)
-
pathname = Pathname.new(pathname)
-
-
# If there are any processors to run on the pathname, use
-
# `BundledAsset`. Otherwise use `StaticAsset` and treat is as binary.
-
if attributes_for(pathname).processors.any?
-
if options[:bundle] == false
-
circular_call_protection(pathname.to_s) do
-
ProcessedAsset.new(index, logical_path, pathname)
-
end
-
else
-
BundledAsset.new(index, logical_path, pathname)
-
end
-
else
-
StaticAsset.new(index, logical_path, pathname)
-
end
-
end
-
-
1
def cache_key_for(path, options)
-
"#{path}:#{options[:bundle] ? '1' : '0'}"
-
end
-
-
1
def circular_call_protection(path)
-
reset = Thread.current[:sprockets_circular_calls].nil?
-
calls = Thread.current[:sprockets_circular_calls] ||= Set.new
-
if calls.include?(path)
-
raise CircularDependencyError, "#{path} has already been required"
-
end
-
calls << path
-
yield
-
ensure
-
Thread.current[:sprockets_circular_calls] = nil if reset
-
end
-
-
1
def logical_path_for_filename(filename, filters)
-
logical_path = attributes_for(filename).logical_path.to_s
-
-
if matches_filter(filters, logical_path, filename)
-
return logical_path
-
end
-
-
# If filename is an index file, retest with alias
-
if File.basename(logical_path)[/[^\.]+/, 0] == 'index'
-
path = logical_path.sub(/\/index\./, '.')
-
if matches_filter(filters, path, filename)
-
return path
-
end
-
end
-
-
nil
-
end
-
-
1
def matches_filter(filters, logical_path, filename)
-
return true if filters.empty?
-
-
filters.any? do |filter|
-
if filter.is_a?(Regexp)
-
filter.match(logical_path)
-
elsif filter.respond_to?(:call)
-
if filter.arity == 1
-
filter.call(logical_path)
-
else
-
filter.call(logical_path, filename.to_s)
-
end
-
else
-
File.fnmatch(filter.to_s, logical_path)
-
end
-
end
-
end
-
-
# Feature detect newer MultiJson API
-
1
if MultiJson.respond_to?(:dump)
-
1
def json_decode(obj)
-
MultiJson.load(obj)
-
end
-
else
-
def json_decode(obj)
-
MultiJson.decode(obj)
-
end
-
end
-
end
-
end
-
1
require 'sprockets/asset'
-
1
require 'sprockets/errors'
-
1
require 'fileutils'
-
1
require 'set'
-
1
require 'zlib'
-
-
1
module Sprockets
-
# `BundledAsset`s are used for files that need to be processed and
-
# concatenated with other assets. Use for `.js` and `.css` files.
-
1
class BundledAsset < Asset
-
1
attr_reader :source
-
-
1
def initialize(environment, logical_path, pathname)
-
super(environment, logical_path, pathname)
-
-
@processed_asset = environment.find_asset(pathname, :bundle => false)
-
@required_assets = @processed_asset.required_assets
-
@dependency_paths = @processed_asset.dependency_paths
-
-
# Explode Asset into parts and gather the dependency bodies
-
@source = to_a.map { |dependency| dependency.to_s }.join
-
-
# Run bundle processors on concatenated source
-
context = environment.context_class.new(environment, logical_path, pathname)
-
@source = context.evaluate(pathname, :data => @source,
-
:processors => environment.bundle_processors(content_type))
-
-
@mtime = (to_a + @dependency_paths).map(&:mtime).max
-
@length = Rack::Utils.bytesize(source)
-
@digest = environment.digest.update(source).hexdigest
-
end
-
-
# Initialize `BundledAsset` from serialized `Hash`.
-
1
def init_with(environment, coder)
-
super
-
-
@processed_asset = environment.find_asset(pathname, :bundle => false)
-
@required_assets = @processed_asset.required_assets
-
-
if @processed_asset.dependency_digest != coder['required_assets_digest']
-
raise UnserializeError, "processed asset belongs to a stale environment"
-
end
-
-
@source = coder['source']
-
end
-
-
# Serialize custom attributes in `BundledAsset`.
-
1
def encode_with(coder)
-
super
-
-
coder['source'] = source
-
coder['required_assets_digest'] = @processed_asset.dependency_digest
-
end
-
-
# Get asset's own processed contents. Excludes any of its required
-
# dependencies but does run any processors or engines on the
-
# original file.
-
1
def body
-
@processed_asset.source
-
end
-
-
# Return an `Array` of `Asset` files that are declared dependencies.
-
1
def dependencies
-
to_a.reject { |a| a.eql?(@processed_asset) }
-
end
-
-
# Expand asset into an `Array` of parts.
-
1
def to_a
-
required_assets
-
end
-
-
# Checks if Asset is stale by comparing the actual mtime and
-
# digest to the inmemory model.
-
1
def fresh?(environment)
-
@processed_asset.fresh?(environment)
-
end
-
end
-
end
-
1
require 'digest/md5'
-
1
require 'fileutils'
-
1
require 'pathname'
-
-
1
module Sprockets
-
1
module Cache
-
# A simple file system cache store.
-
#
-
# environment.cache = Sprockets::Cache::FileStore.new("/tmp")
-
#
-
1
class FileStore
-
1
def initialize(root)
-
1
@root = Pathname.new(root)
-
end
-
-
# Lookup value in cache
-
1
def [](key)
-
pathname = @root.join(key)
-
pathname.exist? ? pathname.open('rb') { |f| Marshal.load(f) } : nil
-
end
-
-
# Save value to cache
-
1
def []=(key, value)
-
# Ensure directory exists
-
FileUtils.mkdir_p @root.join(key).dirname
-
-
@root.join(key).open('w') { |f| Marshal.dump(value, f)}
-
value
-
end
-
end
-
end
-
end
-
1
module Sprockets
-
# `Caching` is an internal mixin whose public methods are exposed on
-
# the `Environment` and `Index` classes.
-
1
module Caching
-
# Low level cache getter for `key`. Checks a number of supported
-
# cache interfaces.
-
1
def cache_get(key)
-
# `Cache#get(key)` for Memcache
-
if cache.respond_to?(:get)
-
cache.get(key)
-
-
# `Cache#[key]` so `Hash` can be used
-
elsif cache.respond_to?(:[])
-
cache[key]
-
-
# `Cache#read(key)` for `ActiveSupport::Cache` support
-
elsif cache.respond_to?(:read)
-
cache.read(key)
-
-
else
-
nil
-
end
-
end
-
-
# Low level cache setter for `key`. Checks a number of supported
-
# cache interfaces.
-
1
def cache_set(key, value)
-
# `Cache#set(key, value)` for Memcache
-
if cache.respond_to?(:set)
-
cache.set(key, value)
-
-
# `Cache#[key]=value` so `Hash` can be used
-
elsif cache.respond_to?(:[]=)
-
cache[key] = value
-
-
# `Cache#write(key, value)` for `ActiveSupport::Cache` support
-
elsif cache.respond_to?(:write)
-
cache.write(key, value)
-
end
-
-
value
-
end
-
-
1
protected
-
# Cache helper method. Takes a `path` argument which maybe a
-
# logical path or fully expanded path. The `&block` is passed
-
# for finding and building the asset if its not in cache.
-
1
def cache_asset(path)
-
# If `cache` is not set, return fast
-
if cache.nil?
-
yield
-
-
# Check cache for `path`
-
elsif (asset = Asset.from_hash(self, cache_get_hash(path.to_s))) && asset.fresh?(self)
-
asset
-
-
# Otherwise yield block that slowly finds and builds the asset
-
elsif asset = yield
-
hash = {}
-
asset.encode_with(hash)
-
-
# Save the asset to its path
-
cache_set_hash(path.to_s, hash)
-
-
# Since path maybe a logical or full pathname, save the
-
# asset its its full path too
-
if path.to_s != asset.pathname.to_s
-
cache_set_hash(asset.pathname.to_s, hash)
-
end
-
-
asset
-
end
-
end
-
-
1
private
-
# Strips `Environment#root` from key to make the key work
-
# consisently across different servers. The key is also hashed
-
# so it does not exceed 250 characters.
-
1
def expand_cache_key(key)
-
File.join('sprockets', digest_class.hexdigest(key.sub(root, '')))
-
end
-
-
1
def cache_get_hash(key)
-
hash = cache_get(expand_cache_key(key))
-
if hash.is_a?(Hash) && digest.hexdigest == hash['_version']
-
hash
-
end
-
end
-
-
1
def cache_set_hash(key, hash)
-
hash['_version'] = digest.hexdigest
-
cache_set(expand_cache_key(key), hash)
-
hash
-
end
-
end
-
end
-
1
require 'tilt'
-
-
1
module Sprockets
-
# Some browsers have issues with stylesheets that contain multiple
-
# `@charset` definitions. The issue surfaces while using Sass since
-
# it inserts a `@charset` at the top of each file. Then Sprockets
-
# concatenates them together.
-
#
-
# The `CharsetNormalizer` processor strips out multiple `@charset`
-
# definitions.
-
#
-
# The current implementation is naive. It picks the first `@charset`
-
# it sees and strips the others. This works for most people because
-
# the other definitions are usually `UTF-8`. A more sophisticated
-
# approach would be to re-encode stylesheets with mixed encodings.
-
#
-
# This behavior can be disabled with:
-
#
-
# environment.unregister_bundle_processor 'text/css', Sprockets::CharsetNormalizer
-
#
-
1
class CharsetNormalizer < Tilt::Template
-
1
def prepare
-
end
-
-
1
def evaluate(context, locals, &block)
-
charset = nil
-
-
# Find and strip out any `@charset` definitions
-
filtered_data = data.gsub(/^@charset "([^"]+)";$/) {
-
charset ||= $1; ""
-
}
-
-
if charset
-
# If there was a charset, move it to the top
-
"@charset \"#{charset}\";#{filtered_data}"
-
else
-
data
-
end
-
end
-
end
-
end
-
1
require 'tilt'
-
-
1
module Sprockets
-
1
class ClosureCompressor < Tilt::Template
-
1
self.default_mime_type = 'application/javascript'
-
-
1
def self.engine_initialized?
-
defined?(::Closure::Compiler)
-
end
-
-
1
def initialize_engine
-
require_template_library 'closure-compiler'
-
end
-
-
1
def prepare
-
end
-
-
1
def evaluate(context, locals, &block)
-
Closure::Compiler.new.compile(data)
-
end
-
end
-
end
-
1
module Sprockets
-
# `Compressing` is an internal mixin whose public methods are exposed on
-
# the `Environment` and `Index` classes.
-
1
module Compressing
-
1
def compressors
-
3
deep_copy_hash(@compressors)
-
end
-
-
1
def register_compressor(mime_type, sym, klass)
-
7
@compressors[mime_type][sym] = klass
-
end
-
-
# Return CSS compressor or nil if none is set
-
1
def css_compressor
-
1
@css_compressor if defined? @css_compressor
-
end
-
-
# Assign a compressor to run on `text/css` assets.
-
#
-
# The compressor object must respond to `compress`.
-
1
def css_compressor=(compressor)
-
1
unregister_bundle_processor 'text/css', css_compressor if css_compressor
-
1
@css_compressor = nil
-
1
return unless compressor
-
-
1
if compressor.is_a?(Symbol)
-
1
compressor = compressors['text/css'][compressor] || raise(Error, "unknown compressor: #{compressor}")
-
end
-
-
1
if compressor.respond_to?(:compress)
-
klass = Class.new(Processor) do
-
@name = "css_compressor"
-
@processor = proc { |context, data| compressor.compress(data) }
-
end
-
@css_compressor = :css_compressor
-
else
-
1
@css_compressor = klass = compressor
-
end
-
-
1
register_bundle_processor 'text/css', klass
-
end
-
-
# Return JS compressor or nil if none is set
-
1
def js_compressor
-
1
@js_compressor if defined? @js_compressor
-
end
-
-
# Assign a compressor to run on `application/javascript` assets.
-
#
-
# The compressor object must respond to `compress`.
-
1
def js_compressor=(compressor)
-
1
unregister_bundle_processor 'application/javascript', js_compressor if js_compressor
-
1
@js_compressor = nil
-
1
return unless compressor
-
-
if compressor.is_a?(Symbol)
-
compressor = compressors['application/javascript'][compressor] || raise(Error, "unknown compressor: #{compressor}")
-
end
-
-
if compressor.respond_to?(:compress)
-
klass = Class.new(Processor) do
-
@name = "js_compressor"
-
@processor = proc { |context, data| compressor.compress(data) }
-
end
-
@js_compressor = :js_compressor
-
else
-
@js_compressor = klass = compressor
-
end
-
-
register_bundle_processor 'application/javascript', klass
-
end
-
end
-
end
-
1
require 'base64'
-
1
require 'rack/utils'
-
1
require 'sprockets/errors'
-
1
require 'sprockets/utils'
-
1
require 'pathname'
-
1
require 'set'
-
-
1
module Sprockets
-
# `Context` provides helper methods to all `Tilt` processors. They
-
# are typically accessed by ERB templates. You can mix in custom
-
# helpers by injecting them into `Environment#context_class`. Do not
-
# mix them into `Context` directly.
-
#
-
# environment.context_class.class_eval do
-
# include MyHelper
-
# def asset_url; end
-
# end
-
#
-
# <%= asset_url "foo.png" %>
-
#
-
# The `Context` also collects dependencies declared by
-
# assets. See `DirectiveProcessor` for an example of this.
-
1
class Context
-
1
attr_reader :environment, :pathname
-
1
attr_reader :_required_paths, :_stubbed_assets
-
1
attr_reader :_dependency_paths, :_dependency_assets
-
1
attr_writer :__LINE__
-
-
1
def initialize(environment, logical_path, pathname)
-
@environment = environment
-
@logical_path = logical_path
-
@pathname = pathname
-
@__LINE__ = nil
-
-
@_required_paths = []
-
@_stubbed_assets = Set.new
-
@_dependency_paths = Set.new
-
@_dependency_assets = Set.new([pathname.to_s])
-
end
-
-
# Returns the environment path that contains the file.
-
#
-
# If `app/javascripts` and `app/stylesheets` are in your path, and
-
# current file is `app/javascripts/foo/bar.js`, `root_path` would
-
# return `app/javascripts`.
-
1
def root_path
-
environment.paths.detect { |path| pathname.to_s[path] }
-
end
-
-
# Returns logical path without any file extensions.
-
#
-
# 'app/javascripts/application.js'
-
# # => 'application'
-
#
-
1
def logical_path
-
@logical_path.chomp(File.extname(@logical_path))
-
end
-
-
# Returns content type of file
-
#
-
# 'application/javascript'
-
# 'text/css'
-
#
-
1
def content_type
-
environment.content_type_of(pathname)
-
end
-
-
# Given a logical path, `resolve` will find and return the fully
-
# expanded path. Relative paths will also be resolved. An optional
-
# `:content_type` restriction can be supplied to restrict the
-
# search.
-
#
-
# resolve("foo.js")
-
# # => "/path/to/app/javascripts/foo.js"
-
#
-
# resolve("./bar.js")
-
# # => "/path/to/app/javascripts/bar.js"
-
#
-
1
def resolve(path, options = {}, &block)
-
pathname = Pathname.new(path)
-
attributes = environment.attributes_for(pathname)
-
-
if pathname.absolute?
-
if environment.stat(pathname)
-
pathname
-
else
-
raise FileNotFound, "couldn't find file '#{pathname}'"
-
end
-
-
elsif content_type = options[:content_type]
-
content_type = self.content_type if content_type == :self
-
-
if attributes.format_extension
-
if content_type != attributes.content_type
-
raise ContentTypeMismatch, "#{path} is " +
-
"'#{attributes.content_type}', not '#{content_type}'"
-
end
-
end
-
-
resolve(path) do |candidate|
-
if self.content_type == environment.content_type_of(candidate)
-
return candidate
-
end
-
end
-
-
raise FileNotFound, "couldn't find file '#{path}'"
-
else
-
environment.resolve(path, {:base_path => self.pathname.dirname}.merge(options), &block)
-
end
-
end
-
-
# `depend_on` allows you to state a dependency on a file without
-
# including it.
-
#
-
# This is used for caching purposes. Any changes made to
-
# the dependency file with invalidate the cache of the
-
# source file.
-
1
def depend_on(path)
-
@_dependency_paths << resolve(path).to_s
-
nil
-
end
-
-
# `depend_on_asset` allows you to state an asset dependency
-
# without including it.
-
#
-
# This is used for caching purposes. Any changes that would
-
# invalidate the dependency asset will invalidate the source
-
# file. Unlike `depend_on`, this will include recursively include
-
# the target asset's dependencies.
-
1
def depend_on_asset(path)
-
filename = resolve(path).to_s
-
@_dependency_assets << filename
-
nil
-
end
-
-
# `require_asset` declares `path` as a dependency of the file. The
-
# dependency will be inserted before the file and will only be
-
# included once.
-
#
-
# If ERB processing is enabled, you can use it to dynamically
-
# require assets.
-
#
-
# <%= require_asset "#{framework}.js" %>
-
#
-
1
def require_asset(path)
-
pathname = resolve(path, :content_type => :self)
-
depend_on_asset(pathname)
-
@_required_paths << pathname.to_s
-
nil
-
end
-
-
# `stub_asset` blacklists `path` from being included in the bundle.
-
# `path` must be an asset which may or may not already be included
-
# in the bundle.
-
1
def stub_asset(path)
-
@_stubbed_assets << resolve(path, :content_type => :self).to_s
-
nil
-
end
-
-
# Tests if target path is able to be safely required into the
-
# current concatenation.
-
1
def asset_requirable?(path)
-
pathname = resolve(path)
-
content_type = environment.content_type_of(pathname)
-
stat = environment.stat(path)
-
return false unless stat && stat.file?
-
self.content_type.nil? || self.content_type == content_type
-
end
-
-
# Reads `path` and runs processors on the file.
-
#
-
# This allows you to capture the result of an asset and include it
-
# directly in another.
-
#
-
# <%= evaluate "bar.js" %>
-
#
-
1
def evaluate(path, options = {})
-
pathname = resolve(path)
-
attributes = environment.attributes_for(pathname)
-
processors = options[:processors] || attributes.processors
-
-
if options[:data]
-
result = options[:data]
-
else
-
if environment.respond_to?(:default_external_encoding)
-
mime_type = environment.mime_types(pathname.extname)
-
encoding = environment.encoding_for_mime_type(mime_type)
-
result = Sprockets::Utils.read_unicode(pathname, encoding)
-
else
-
result = Sprockets::Utils.read_unicode(pathname)
-
end
-
end
-
-
processors.each do |processor|
-
begin
-
template = processor.new(pathname.to_s) { result }
-
result = template.render(self, {})
-
rescue Exception => e
-
annotate_exception! e
-
raise
-
end
-
end
-
-
result
-
end
-
-
# Returns a Base64-encoded `data:` URI with the contents of the
-
# asset at the specified path, and marks that path as a dependency
-
# of the current file.
-
#
-
# Use `asset_data_uri` from ERB with CSS or JavaScript assets:
-
#
-
# #logo { background: url(<%= asset_data_uri 'logo.png' %>) }
-
#
-
# $('<img>').attr('src', '<%= asset_data_uri 'avatar.jpg' %>')
-
#
-
1
def asset_data_uri(path)
-
depend_on_asset(path)
-
asset = environment.find_asset(path)
-
base64 = Base64.encode64(asset.to_s).gsub(/\s+/, "")
-
"data:#{asset.content_type};base64,#{Rack::Utils.escape(base64)}"
-
end
-
-
# Expands logical path to full url to asset.
-
#
-
# NOTE: This helper is currently not implemented and should be
-
# customized by the application. Though, in the future, some
-
# basics implemention may be provided with different methods that
-
# are required to be overridden.
-
1
def asset_path(path, options = {})
-
message = <<-EOS
-
Custom asset_path helper is not implemented
-
-
Extend your environment context with a custom method.
-
-
environment.context_class.class_eval do
-
def asset_path(path, options = {})
-
end
-
end
-
EOS
-
raise NotImplementedError, message
-
end
-
-
# Expand logical image asset path.
-
1
def image_path(path)
-
asset_path(path, :type => :image)
-
end
-
-
# Expand logical video asset path.
-
1
def video_path(path)
-
asset_path(path, :type => :video)
-
end
-
-
# Expand logical audio asset path.
-
1
def audio_path(path)
-
asset_path(path, :type => :audio)
-
end
-
-
# Expand logical font asset path.
-
1
def font_path(path)
-
asset_path(path, :type => :font)
-
end
-
-
# Expand logical javascript asset path.
-
1
def javascript_path(path)
-
asset_path(path, :type => :javascript)
-
end
-
-
# Expand logical stylesheet asset path.
-
1
def stylesheet_path(path)
-
asset_path(path, :type => :stylesheet)
-
end
-
-
1
private
-
# Annotates exception backtrace with the original template that
-
# the exception was raised in.
-
1
def annotate_exception!(exception)
-
location = pathname.to_s
-
location << ":#{@__LINE__}" if @__LINE__
-
-
exception.extend(Sprockets::EngineError)
-
exception.sprockets_annotation = " (in #{location})"
-
end
-
-
1
def logger
-
environment.logger
-
end
-
end
-
end
-
1
require 'pathname'
-
1
require 'shellwords'
-
1
require 'tilt'
-
1
require 'yaml'
-
-
1
module Sprockets
-
# The `DirectiveProcessor` is responsible for parsing and evaluating
-
# directive comments in a source file.
-
#
-
# A directive comment starts with a comment prefix, followed by an "=",
-
# then the directive name, then any arguments.
-
#
-
# // JavaScript
-
# //= require "foo"
-
#
-
# # CoffeeScript
-
# #= require "bar"
-
#
-
# /* CSS
-
# *= require "baz"
-
# */
-
#
-
# The Processor is implemented as a `Tilt::Template` and is loosely
-
# coupled to Sprockets. This makes it possible to disable or modify
-
# the processor to do whatever you'd like. You could add your own
-
# custom directives or invent your own directive syntax.
-
#
-
# `Environment#processors` includes `DirectiveProcessor` by default.
-
#
-
# To remove the processor entirely:
-
#
-
# env.unregister_processor('text/css', Sprockets::DirectiveProcessor)
-
# env.unregister_processor('application/javascript', Sprockets::DirectiveProcessor)
-
#
-
# Then inject your own preprocessor:
-
#
-
# env.register_processor('text/css', MyProcessor)
-
#
-
1
class DirectiveProcessor < Tilt::Template
-
# Directives will only be picked up if they are in the header
-
# of the source file. C style (/* */), JavaScript (//), and
-
# Ruby (#) comments are supported.
-
#
-
# Directives in comments after the first non-whitespace line
-
# of code will not be processed.
-
#
-
1
HEADER_PATTERN = /
-
\A (
-
(?m:\s*) (
-
(\/\* (?m:.*?) \*\/) |
-
(\#\#\# (?m:.*?) \#\#\#) |
-
(\/\/ .* \n?)+ |
-
(\# .* \n?)+
-
)
-
)+
-
/x
-
-
# Directives are denoted by a `=` followed by the name, then
-
# argument list.
-
#
-
# A few different styles are allowed:
-
#
-
# // =require foo
-
# //= require foo
-
# //= require "foo"
-
#
-
1
DIRECTIVE_PATTERN = /
-
^ \W* = \s* (\w+.*?) (\*\/)? $
-
/x
-
-
1
attr_reader :pathname
-
1
attr_reader :header, :body
-
-
1
def prepare
-
@pathname = Pathname.new(file)
-
-
@header = data[HEADER_PATTERN, 0] || ""
-
@body = $' || data
-
# Ensure body ends in a new line
-
@body += "\n" if @body != "" && @body !~ /\n\Z/m
-
-
@included_pathnames = []
-
@compat = false
-
end
-
-
# Implemented for Tilt#render.
-
#
-
# `context` is a `Context` instance with methods that allow you to
-
# access the environment and append to the bundle. See `Context`
-
# for the complete API.
-
1
def evaluate(context, locals, &block)
-
@context = context
-
-
@result = ""
-
@result.force_encoding(body.encoding) if body.respond_to?(:encoding)
-
-
@has_written_body = false
-
-
process_directives
-
process_source
-
-
@result
-
end
-
-
# Returns the header String with any directives stripped.
-
1
def processed_header
-
lineno = 0
-
@processed_header ||= header.lines.map { |line|
-
lineno += 1
-
# Replace directive line with a clean break
-
directives.assoc(lineno) ? "\n" : line
-
}.join.chomp
-
end
-
-
# Returns the source String with any directives stripped.
-
1
def processed_source
-
@processed_source ||= processed_header + body
-
end
-
-
# Returns an Array of directive structures. Each structure
-
# is an Array with the line number as the first element, the
-
# directive name as the second element, followed by any
-
# arguments.
-
#
-
# [[1, "require", "foo"], [2, "require", "bar"]]
-
#
-
1
def directives
-
@directives ||= header.lines.each_with_index.map { |line, index|
-
if directive = line[DIRECTIVE_PATTERN, 1]
-
name, *args = Shellwords.shellwords(directive)
-
if respond_to?("process_#{name}_directive", true)
-
[index + 1, name, *args]
-
end
-
end
-
}.compact
-
end
-
-
1
protected
-
1
attr_reader :included_pathnames
-
1
attr_reader :context
-
-
# Gathers comment directives in the source and processes them.
-
# Any directive method matching `process_*_directive` will
-
# automatically be available. This makes it easy to extend the
-
# processor.
-
#
-
# To implement a custom directive called `require_glob`, subclass
-
# `Sprockets::DirectiveProcessor`, then add a method called
-
# `process_require_glob_directive`.
-
#
-
# class DirectiveProcessor < Sprockets::DirectiveProcessor
-
# def process_require_glob_directive
-
# Dir["#{pathname.dirname}/#{glob}"].sort.each do |filename|
-
# require(filename)
-
# end
-
# end
-
# end
-
#
-
# Replace the current processor on the environment with your own:
-
#
-
# env.unregister_processor('text/css', Sprockets::DirectiveProcessor)
-
# env.register_processor('text/css', DirectiveProcessor)
-
#
-
1
def process_directives
-
directives.each do |line_number, name, *args|
-
context.__LINE__ = line_number
-
send("process_#{name}_directive", *args)
-
context.__LINE__ = nil
-
end
-
end
-
-
1
def process_source
-
unless @has_written_body || processed_header.empty?
-
@result << processed_header << "\n"
-
end
-
-
included_pathnames.each do |pathname|
-
@result << context.evaluate(pathname)
-
end
-
-
unless @has_written_body
-
@result << body
-
end
-
-
if compat? && constants.any?
-
@result.gsub!(/<%=(.*?)%>/) { constants[$1.strip] }
-
end
-
end
-
-
# The `require` directive functions similar to Ruby's own `require`.
-
# It provides a way to declare a dependency on a file in your path
-
# and ensures its only loaded once before the source file.
-
#
-
# `require` works with files in the environment path:
-
#
-
# //= require "foo.js"
-
#
-
# Extensions are optional. If your source file is ".js", it
-
# assumes you are requiring another ".js".
-
#
-
# //= require "foo"
-
#
-
# Relative paths work too. Use a leading `./` to denote a relative
-
# path:
-
#
-
# //= require "./bar"
-
#
-
1
def process_require_directive(path)
-
if @compat
-
if path =~ /<([^>]+)>/
-
path = $1
-
else
-
path = "./#{path}" unless relative?(path)
-
end
-
end
-
-
context.require_asset(path)
-
end
-
-
# `require_self` causes the body of the current file to be
-
# inserted before any subsequent `require` or `include`
-
# directives. Useful in CSS files, where it's common for the
-
# index file to contain global styles that need to be defined
-
# before other dependencies are loaded.
-
#
-
# /*= require "reset"
-
# *= require_self
-
# *= require_tree .
-
# */
-
#
-
1
def process_require_self_directive
-
if @has_written_body
-
raise ArgumentError, "require_self can only be called once per source file"
-
end
-
-
context.require_asset(pathname)
-
process_source
-
included_pathnames.clear
-
@has_written_body = true
-
end
-
-
# The `include` directive works similar to `require` but
-
# inserts the contents of the dependency even if it already
-
# has been required.
-
#
-
# //= include "header"
-
#
-
1
def process_include_directive(path)
-
pathname = context.resolve(path)
-
context.depend_on_asset(pathname)
-
included_pathnames << pathname
-
end
-
-
# `require_directory` requires all the files inside a single
-
# directory. It's similar to `path/*` since it does not follow
-
# nested directories.
-
#
-
# //= require_directory "./javascripts"
-
#
-
1
def process_require_directory_directive(path = ".")
-
if relative?(path)
-
root = pathname.dirname.join(path).expand_path
-
-
unless (stats = stat(root)) && stats.directory?
-
raise ArgumentError, "require_directory argument must be a directory"
-
end
-
-
context.depend_on(root)
-
-
entries(root).each do |pathname|
-
pathname = root.join(pathname)
-
if pathname.to_s == self.file
-
next
-
elsif context.asset_requirable?(pathname)
-
context.require_asset(pathname)
-
end
-
end
-
else
-
# The path must be relative and start with a `./`.
-
raise ArgumentError, "require_directory argument must be a relative path"
-
end
-
end
-
-
# `require_tree` requires all the nested files in a directory.
-
# Its glob equivalent is `path/**/*`.
-
#
-
# //= require_tree "./public"
-
#
-
1
def process_require_tree_directive(path = ".")
-
if relative?(path)
-
root = pathname.dirname.join(path).expand_path
-
-
unless (stats = stat(root)) && stats.directory?
-
raise ArgumentError, "require_tree argument must be a directory"
-
end
-
-
context.depend_on(root)
-
-
each_entry(root) do |pathname|
-
if pathname.to_s == self.file
-
next
-
elsif stat(pathname).directory?
-
context.depend_on(pathname)
-
elsif context.asset_requirable?(pathname)
-
context.require_asset(pathname)
-
end
-
end
-
else
-
# The path must be relative and start with a `./`.
-
raise ArgumentError, "require_tree argument must be a relative path"
-
end
-
end
-
-
# Allows you to state a dependency on a file without
-
# including it.
-
#
-
# This is used for caching purposes. Any changes made to
-
# the dependency file will invalidate the cache of the
-
# source file.
-
#
-
# This is useful if you are using ERB and File.read to pull
-
# in contents from another file.
-
#
-
# //= depend_on "foo.png"
-
#
-
1
def process_depend_on_directive(path)
-
context.depend_on(path)
-
end
-
-
# Allows you to state a dependency on an asset without including
-
# it.
-
#
-
# This is used for caching purposes. Any changes that would
-
# invalid the asset dependency will invalidate the cache our the
-
# source file.
-
#
-
# Unlike `depend_on`, the path must be a requirable asset.
-
#
-
# //= depend_on_asset "bar.js"
-
#
-
1
def process_depend_on_asset_directive(path)
-
context.depend_on_asset(path)
-
end
-
-
# Allows dependency to be excluded from the asset bundle.
-
#
-
# The `path` must be a valid asset and may or may not already
-
# be part of the bundle. Once stubbed, it is blacklisted and
-
# can't be brought back by any other `require`.
-
#
-
# //= stub "jquery"
-
#
-
1
def process_stub_directive(path)
-
context.stub_asset(path)
-
end
-
-
# Enable Sprockets 1.x compat mode.
-
#
-
# Makes it possible to use the same JavaScript source
-
# file in both Sprockets 1 and 2.
-
#
-
# //= compat
-
#
-
1
def process_compat_directive
-
@compat = true
-
end
-
-
# Checks if Sprockets 1.x compat mode enabled
-
1
def compat?
-
@compat
-
end
-
-
# Sprockets 1.x allowed for constant interpolation if a
-
# constants.yml was present. This is only available if
-
# compat mode is on.
-
1
def constants
-
if compat?
-
pathname = Pathname.new(context.root_path).join("constants.yml")
-
stat(pathname) ? YAML.load_file(pathname) : {}
-
else
-
{}
-
end
-
end
-
-
# `provide` is stubbed out for Sprockets 1.x compat.
-
# Mutating the path when an asset is being built is
-
# not permitted.
-
1
def process_provide_directive(path)
-
end
-
-
1
private
-
1
def relative?(path)
-
path =~ /^\.($|\.?\/)/
-
end
-
-
1
def stat(path)
-
context.environment.stat(path)
-
end
-
-
1
def entries(path)
-
context.environment.entries(path)
-
end
-
-
1
def each_entry(root, &block)
-
context.environment.each_entry(root, &block)
-
end
-
end
-
end
-
1
require 'tilt'
-
-
1
module Sprockets
-
# Tilt engine class for the Eco compiler. Depends on the `eco` gem.
-
#
-
# For more infomation see:
-
#
-
# https://github.com/sstephenson/ruby-eco
-
# https://github.com/sstephenson/eco
-
#
-
1
class EcoTemplate < Tilt::Template
-
# Check to see if Eco is loaded
-
1
def self.engine_initialized?
-
defined? ::Eco
-
end
-
-
# Autoload eco library. If the library isn't loaded, Tilt will produce
-
# a thread safetly warning. If you intend to use `.eco` files, you
-
# should explicitly require it.
-
1
def initialize_engine
-
require_template_library 'eco'
-
end
-
-
1
def prepare
-
end
-
-
# Compile template data with Eco compiler.
-
#
-
# Returns a JS function definition String. The result should be
-
# assigned to a JS variable.
-
#
-
# # => "function(...) {...}"
-
#
-
1
def evaluate(scope, locals, &block)
-
Eco.compile(data)
-
end
-
end
-
end
-
1
require 'tilt'
-
-
1
module Sprockets
-
# Tilt engine class for the EJS compiler. Depends on the `ejs` gem.
-
#
-
# For more infomation see:
-
#
-
# https://github.com/sstephenson/ruby-ejs
-
#
-
1
class EjsTemplate < Tilt::Template
-
# Check to see if EJS is loaded
-
1
def self.engine_initialized?
-
defined? ::EJS
-
end
-
-
# Autoload ejs library. If the library isn't loaded, Tilt will produce
-
# a thread safetly warning. If you intend to use `.ejs` files, you
-
# should explicitly require it.
-
1
def initialize_engine
-
require_template_library 'ejs'
-
end
-
-
1
def prepare
-
end
-
-
# Compile template data with EJS compiler.
-
#
-
# Returns a JS function definition String. The result should be
-
# assigned to a JS variable.
-
#
-
# # => "function(obj){...}"
-
#
-
1
def evaluate(scope, locals, &block)
-
EJS.compile(data)
-
end
-
end
-
end
-
1
require 'sprockets/eco_template'
-
1
require 'sprockets/ejs_template'
-
1
require 'sprockets/jst_processor'
-
1
require 'sprockets/utils'
-
1
require 'tilt'
-
-
1
module Sprockets
-
# `Engines` provides a global and `Environment` instance registry.
-
#
-
# An engine is a type of processor that is bound to an filename
-
# extension. `application.js.coffee` indicates that the
-
# `CoffeeScriptTemplate` engine will be ran on the file.
-
#
-
# Extensions can be stacked and will be evaulated from right to
-
# left. `application.js.coffee.erb` will first run `ERBTemplate`
-
# then `CoffeeScriptTemplate`.
-
#
-
# All `Engine`s must follow the `Tilt::Template` interface. It is
-
# recommended to subclass `Tilt::Template`.
-
#
-
# Its recommended that you register engine changes on your local
-
# `Environment` instance.
-
#
-
# environment.register_engine '.foo', FooProcessor
-
#
-
# The global registry is exposed for plugins to register themselves.
-
#
-
# Sprockets.register_engine '.sass', SassTemplate
-
#
-
1
module Engines
-
# Returns a `Hash` of `Engine`s registered on the `Environment`.
-
# If an `ext` argument is supplied, the `Engine` associated with
-
# that extension will be returned.
-
#
-
# environment.engines
-
# # => {".coffee" => CoffeeScriptTemplate, ".sass" => SassTemplate, ...}
-
#
-
# environment.engines('.coffee')
-
# # => CoffeeScriptTemplate
-
#
-
1
def engines(ext = nil)
-
2
if ext
-
ext = Sprockets::Utils.normalize_extension(ext)
-
@engines[ext]
-
else
-
2
@engines.dup
-
end
-
end
-
-
# Returns an `Array` of engine extension `String`s.
-
#
-
# environment.engine_extensions
-
# # => ['.coffee', '.sass', ...]
-
1
def engine_extensions
-
@engines.keys
-
end
-
-
# Registers a new Engine `klass` for `ext`. If the `ext` already
-
# has an engine registered, it will be overridden.
-
#
-
# environment.register_engine '.coffee', CoffeeScriptTemplate
-
#
-
1
def register_engine(ext, klass)
-
9
ext = Sprockets::Utils.normalize_extension(ext)
-
9
@engines[ext] = klass
-
end
-
-
1
private
-
1
def deep_copy_hash(hash)
-
9
initial = Hash.new { |h, k| h[k] = [] }
-
23
hash.inject(initial) { |h, (k, a)| h[k] = a.dup; h }
-
end
-
end
-
end
-
1
require 'sprockets/base'
-
1
require 'sprockets/context'
-
1
require 'sprockets/index'
-
-
1
require 'hike'
-
1
require 'logger'
-
1
require 'pathname'
-
1
require 'tilt'
-
-
1
module Sprockets
-
1
class Environment < Base
-
# `Environment` should initialized with your application's root
-
# directory. This should be the same as your Rails or Rack root.
-
#
-
# env = Environment.new(Rails.root)
-
#
-
1
def initialize(root = ".")
-
1
@trail = Hike::Trail.new(root)
-
-
1
self.logger = Logger.new($stderr)
-
1
self.logger.level = Logger::FATAL
-
-
1
if respond_to?(:default_external_encoding)
-
1
self.default_external_encoding = Encoding::UTF_8
-
end
-
-
# Create a safe `Context` subclass to mutate
-
1
@context_class = Class.new(Context)
-
-
# Set MD5 as the default digest
-
1
require 'digest/md5'
-
1
@digest_class = ::Digest::MD5
-
1
@version = ''
-
-
1
@mime_types = Sprockets.registered_mime_types
-
1
@engines = Sprockets.engines
-
1
@preprocessors = Sprockets.preprocessors
-
1
@postprocessors = Sprockets.postprocessors
-
1
@bundle_processors = Sprockets.bundle_processors
-
1
@compressors = Sprockets.compressors
-
-
1
Sprockets.paths.each do |path|
-
append_path(path)
-
end
-
-
1
@engines.each do |ext, klass|
-
9
add_engine_to_trail(ext, klass)
-
end
-
-
1
@mime_types.each do |ext, type|
-
2
@trail.append_extension(ext)
-
end
-
-
1
expire_index!
-
-
1
yield self if block_given?
-
end
-
-
# Returns a cached version of the environment.
-
#
-
# All its file system calls are cached which makes `index` much
-
# faster. This behavior is ideal in production since the file
-
# system only changes between deploys.
-
1
def index
-
1
Index.new(self)
-
end
-
-
# Cache `find_asset` calls
-
1
def find_asset(path, options = {})
-
options[:bundle] = true unless options.key?(:bundle)
-
-
# Ensure inmemory cached assets are still fresh on every lookup
-
if (asset = @assets[cache_key_for(path, options)]) && asset.fresh?(self)
-
asset
-
elsif asset = index.find_asset(path, options)
-
# Cache is pushed upstream by Index#find_asset
-
asset
-
end
-
end
-
-
1
protected
-
1
def expire_index!
-
# Clear digest to be recomputed
-
18
@digest = nil
-
18
@assets = {}
-
end
-
end
-
end
-
# Define some basic Sprockets error classes
-
1
module Sprockets
-
1
class Error < StandardError; end
-
1
class ArgumentError < Error; end
-
1
class CircularDependencyError < Error; end
-
1
class ContentTypeMismatch < Error; end
-
1
class EncodingError < Error; end
-
1
class FileNotFound < Error; end
-
1
class FileOutsidePaths < Error; end
-
1
class NotImplementedError < Error; end
-
1
class UnserializeError < Error; end
-
-
1
module EngineError
-
1
attr_accessor :sprockets_annotation
-
-
1
def message
-
[super, sprockets_annotation].compact.join("\n")
-
end
-
end
-
end
-
1
require 'sprockets/base'
-
-
1
module Sprockets
-
# `Index` is a special cached version of `Environment`.
-
#
-
# The expection is that all of its file system methods are cached
-
# for the instances lifetime. This makes `Index` much faster. This
-
# behavior is ideal in production environments where the file system
-
# is immutable.
-
#
-
# `Index` should not be initialized directly. Instead use
-
# `Environment#index`.
-
1
class Index < Base
-
1
def initialize(environment)
-
1
@environment = environment
-
-
1
if environment.respond_to?(:default_external_encoding)
-
1
@default_external_encoding = environment.default_external_encoding
-
end
-
-
# Copy environment attributes
-
1
@logger = environment.logger
-
1
@context_class = environment.context_class
-
1
@cache = environment.cache
-
1
@trail = environment.trail.index
-
1
@digest = environment.digest
-
1
@digest_class = environment.digest_class
-
1
@version = environment.version
-
1
@mime_types = environment.mime_types
-
1
@engines = environment.engines
-
1
@preprocessors = environment.preprocessors
-
1
@postprocessors = environment.postprocessors
-
1
@bundle_processors = environment.bundle_processors
-
1
@compressors = environment.compressors
-
-
# Initialize caches
-
1
@assets = {}
-
1
@digests = {}
-
end
-
-
# No-op return self as index
-
1
def index
-
self
-
end
-
-
# Cache calls to `file_digest`
-
1
def file_digest(pathname)
-
key = pathname.to_s
-
if @digests.key?(key)
-
@digests[key]
-
else
-
@digests[key] = super
-
end
-
end
-
-
# Cache `find_asset` calls
-
1
def find_asset(path, options = {})
-
options[:bundle] = true unless options.key?(:bundle)
-
if asset = @assets[cache_key_for(path, options)]
-
asset
-
elsif asset = super
-
logical_path_cache_key = cache_key_for(path, options)
-
full_path_cache_key = cache_key_for(asset.pathname, options)
-
-
# Cache on Index
-
@assets[logical_path_cache_key] = @assets[full_path_cache_key] = asset
-
-
# Push cache upstream to Environment
-
@environment.instance_eval do
-
@assets[logical_path_cache_key] = @assets[full_path_cache_key] = asset
-
end
-
-
asset
-
end
-
end
-
-
1
protected
-
# Index is immutable, any methods that try to clear the cache
-
# should bomb.
-
1
def expire_index!
-
raise TypeError, "can't modify immutable index"
-
end
-
-
# Cache asset building in memory and in persisted cache.
-
1
def build_asset(path, pathname, options)
-
# Memory cache
-
key = cache_key_for(pathname, options)
-
if @assets.key?(key)
-
@assets[key]
-
else
-
@assets[key] = begin
-
# Persisted cache
-
cache_asset(key) do
-
super
-
end
-
end
-
end
-
end
-
end
-
end
-
1
require 'tilt'
-
-
1
module Sprockets
-
1
class JstProcessor < Tilt::Template
-
1
self.default_mime_type = 'application/javascript'
-
-
1
def self.default_namespace
-
'this.JST'
-
end
-
-
1
def prepare
-
@namespace = self.class.default_namespace
-
end
-
-
1
attr_reader :namespace
-
-
1
def evaluate(scope, locals, &block)
-
<<-JST
-
(function() { #{namespace} || (#{namespace} = {}); #{namespace}[#{scope.logical_path.inspect}] = #{indent(data)};
-
}).call(this);
-
JST
-
end
-
-
1
private
-
1
def indent(string)
-
string.gsub(/$(.)/m, "\\1 ").strip
-
end
-
end
-
end
-
1
require 'multi_json'
-
1
require 'securerandom'
-
1
require 'time'
-
-
1
module Sprockets
-
# The Manifest logs the contents of assets compiled to a single
-
# directory. It records basic attributes about the asset for fast
-
# lookup without having to compile. A pointer from each logical path
-
# indicates with fingerprinted asset is the current one.
-
#
-
# The JSON is part of the public API and should be considered
-
# stable. This should make it easy to read from other programming
-
# languages and processes that don't have sprockets loaded. See
-
# `#assets` and `#files` for more infomation about the structure.
-
1
class Manifest
-
1
attr_reader :environment, :path, :dir
-
-
# Create new Manifest associated with an `environment`. `path` is
-
# a full path to the manifest json file. The file may or may not
-
# already exist. The dirname of the `path` will be used to write
-
# compiled assets to. Otherwise, if the path is a directory, the
-
# filename will default a random "manifest-123.json" file in that
-
# directory.
-
#
-
# Manifest.new(environment, "./public/assets/manifest.json")
-
#
-
1
def initialize(*args)
-
1
if args.first.is_a?(Base) || args.first.nil?
-
1
@environment = args.shift
-
end
-
-
1
@dir, @path = args[0], args[1]
-
-
# Expand paths
-
1
@dir = File.expand_path(@dir) if @dir
-
1
@path = File.expand_path(@path) if @path
-
-
# If path is given as the second arg
-
1
if @dir && File.extname(@dir) != ""
-
@dir, @path = nil, @dir
-
end
-
-
# Default dir to the directory of the path
-
1
@dir ||= File.dirname(@path) if @path
-
-
# If directory is given w/o path, pick a random manifest.json location
-
1
if @dir && @path.nil?
-
# Find the first manifest.json in the directory
-
1
paths = Dir[File.join(@dir, "manifest*.json")]
-
1
if paths.any?
-
1
@path = paths.first
-
else
-
@path = File.join(@dir, "manifest-#{SecureRandom.hex(16)}.json")
-
end
-
end
-
-
1
unless @dir && @path
-
raise ArgumentError, "manifest requires output path"
-
end
-
-
1
data = nil
-
-
1
begin
-
1
if File.exist?(@path)
-
1
data = json_decode(File.read(@path))
-
end
-
rescue MultiJson::DecodeError => e
-
logger.error "#{@path} is invalid: #{e.class} #{e.message}"
-
end
-
-
1
@data = data.is_a?(Hash) ? data : {}
-
end
-
-
# Returns internal assets mapping. Keys are logical paths which
-
# map to the latest fingerprinted filename.
-
#
-
# Logical path (String): Fingerprint path (String)
-
#
-
# { "application.js" => "application-2e8e9a7c6b0aafa0c9bdeec90ea30213.js",
-
# "jquery.js" => "jquery-ae0908555a245f8266f77df5a8edca2e.js" }
-
#
-
1
def assets
-
@data['assets'] ||= {}
-
end
-
-
# Returns internal file directory listing. Keys are filenames
-
# which map to an attributes array.
-
#
-
# Fingerprint path (String):
-
# logical_path: Logical path (String)
-
# mtime: ISO8601 mtime (String)
-
# digest: Base64 hex digest (String)
-
#
-
# { "application-2e8e9a7c6b0aafa0c9bdeec90ea30213.js" =>
-
# { 'logical_path' => "application.js",
-
# 'mtime' => "2011-12-13T21:47:08-06:00",
-
# 'digest' => "2e8e9a7c6b0aafa0c9bdeec90ea30213" } }
-
#
-
1
def files
-
@data['files'] ||= {}
-
end
-
-
# Compile and write asset to directory. The asset is written to a
-
# fingerprinted filename like
-
# `application-2e8e9a7c6b0aafa0c9bdeec90ea30213.js`. An entry is
-
# also inserted into the manifest file.
-
#
-
# compile("application.js")
-
#
-
1
def compile(*args)
-
unless environment
-
raise Error, "manifest requires environment for compilation"
-
end
-
-
paths = environment.each_logical_path(*args).to_a +
-
args.flatten.select { |fn| Pathname.new(fn).absolute? if fn.is_a?(String)}
-
-
paths.each do |path|
-
if asset = find_asset(path)
-
files[asset.digest_path] = {
-
'logical_path' => asset.logical_path,
-
'mtime' => asset.mtime.iso8601,
-
'size' => asset.bytesize,
-
'digest' => asset.digest
-
}
-
assets[asset.logical_path] = asset.digest_path
-
-
target = File.join(dir, asset.digest_path)
-
-
if File.exist?(target)
-
logger.debug "Skipping #{target}, already exists"
-
else
-
logger.info "Writing #{target}"
-
asset.write_to target
-
asset.write_to "#{target}.gz" if asset.is_a?(BundledAsset)
-
end
-
-
end
-
end
-
save
-
paths
-
end
-
-
# Removes file from directory and from manifest. `filename` must
-
# be the name with any directory path.
-
#
-
# manifest.remove("application-2e8e9a7c6b0aafa0c9bdeec90ea30213.js")
-
#
-
1
def remove(filename)
-
path = File.join(dir, filename)
-
gzip = "#{path}.gz"
-
logical_path = files[filename]['logical_path']
-
-
if assets[logical_path] == filename
-
assets.delete(logical_path)
-
end
-
-
files.delete(filename)
-
FileUtils.rm(path) if File.exist?(path)
-
FileUtils.rm(gzip) if File.exist?(gzip)
-
-
save
-
-
logger.info "Removed #{filename}"
-
-
nil
-
end
-
-
# Cleanup old assets in the compile directory. By default it will
-
# keep the latest version plus 2 backups.
-
1
def clean(keep = 2)
-
self.assets.keys.each do |logical_path|
-
# Get assets sorted by ctime, newest first
-
assets = backups_for(logical_path)
-
-
# Keep the last N backups
-
assets = assets[keep..-1] || []
-
-
# Remove old assets
-
assets.each { |path, _| remove(path) }
-
end
-
end
-
-
# Wipe directive
-
1
def clobber
-
FileUtils.rm_r(@dir) if File.exist?(@dir)
-
logger.info "Removed #{@dir}"
-
nil
-
end
-
-
1
protected
-
# Finds all the backup assets for a logical path. The latest
-
# version is always excluded. The return array is sorted by the
-
# assets mtime in descending order (Newest to oldest).
-
1
def backups_for(logical_path)
-
files.select { |filename, attrs|
-
# Matching logical paths
-
attrs['logical_path'] == logical_path &&
-
# Excluding whatever asset is the current
-
assets[logical_path] != filename
-
}.sort_by { |filename, attrs|
-
# Sort by timestamp
-
Time.parse(attrs['mtime'])
-
}.reverse
-
end
-
-
# Basic wrapper around Environment#find_asset. Logs compile time.
-
1
def find_asset(logical_path)
-
asset = nil
-
ms = benchmark do
-
asset = environment.find_asset(logical_path)
-
end
-
logger.debug "Compiled #{logical_path} (#{ms}ms)"
-
asset
-
end
-
-
# Persist manfiest back to FS
-
1
def save
-
FileUtils.mkdir_p dir
-
File.open(path, 'w') do |f|
-
f.write json_encode(@data)
-
end
-
end
-
-
1
private
-
# Feature detect newer MultiJson API
-
1
if MultiJson.respond_to?(:dump)
-
1
def json_decode(obj)
-
1
MultiJson.load(obj)
-
end
-
-
1
def json_encode(obj)
-
MultiJson.dump(obj)
-
end
-
else
-
def json_decode(obj)
-
MultiJson.decode(obj)
-
end
-
-
def json_encode(obj)
-
MultiJson.encode(obj)
-
end
-
end
-
-
1
def logger
-
if environment
-
environment.logger
-
else
-
logger = Logger.new($stderr)
-
logger.level = Logger::FATAL
-
logger
-
end
-
end
-
-
1
def benchmark
-
start_time = Time.now.to_f
-
yield
-
((Time.now.to_f - start_time) * 1000).to_i
-
end
-
end
-
end
-
1
require 'rack/mime'
-
-
1
module Sprockets
-
1
module Mime
-
# Returns a `Hash` of registered mime types registered on the
-
# environment and those part of `Rack::Mime`.
-
#
-
# If an `ext` is given, it will lookup the mime type for that extension.
-
1
def mime_types(ext = nil)
-
6
if ext.nil?
-
6
Rack::Mime::MIME_TYPES.merge(@mime_types)
-
else
-
ext = Sprockets::Utils.normalize_extension(ext)
-
@mime_types[ext] || Rack::Mime::MIME_TYPES[ext]
-
end
-
end
-
-
# Returns a `Hash` of explicitly registered mime types.
-
1
def registered_mime_types
-
1
@mime_types.dup
-
end
-
-
1
if {}.respond_to?(:key)
-
1
def extension_for_mime_type(type)
-
5
mime_types.key(type)
-
end
-
else
-
def extension_for_mime_type(type)
-
mime_types.index(type)
-
end
-
end
-
-
# Register a new mime type.
-
1
def register_mime_type(mime_type, ext)
-
2
ext = Sprockets::Utils.normalize_extension(ext)
-
2
@mime_types[ext] = mime_type
-
end
-
-
1
if defined? Encoding
-
# Returns the correct encoding for a given mime type, while falling
-
# back on the default external encoding, if it exists.
-
1
def encoding_for_mime_type(type)
-
encoding = Encoding::BINARY if type =~ %r{^(image|audio|video)/}
-
encoding ||= default_external_encoding if respond_to?(:default_external_encoding)
-
encoding
-
end
-
end
-
end
-
end
-
1
module Sprockets
-
1
module Paths
-
# Returns `Environment` root.
-
#
-
# All relative paths are expanded with root as its base. To be
-
# useful set this to your applications root directory. (`Rails.root`)
-
1
def root
-
@trail.root.dup
-
end
-
-
# Returns an `Array` of path `String`s.
-
#
-
# These paths will be used for asset logical path lookups.
-
#
-
# Note that a copy of the `Array` is returned so mutating will
-
# have no affect on the environment. See `append_path`,
-
# `prepend_path`, and `clear_paths`.
-
1
def paths
-
1
@trail.paths.dup
-
end
-
-
# Prepend a `path` to the `paths` list.
-
#
-
# Paths at the end of the `Array` have the least priority.
-
1
def prepend_path(path)
-
@trail.prepend_path(path)
-
end
-
-
# Append a `path` to the `paths` list.
-
#
-
# Paths at the beginning of the `Array` have a higher priority.
-
1
def append_path(path)
-
13
@trail.append_path(path)
-
end
-
-
# Clear all paths and start fresh.
-
#
-
# There is no mechanism for reordering paths, so its best to
-
# completely wipe the paths list and reappend them in the order
-
# you want.
-
1
def clear_paths
-
@trail.paths.dup.each { |path| @trail.remove_path(path) }
-
end
-
-
# Returns an `Array` of extensions.
-
#
-
# These extensions maybe omitted from logical path searches.
-
#
-
# # => [".js", ".css", ".coffee", ".sass", ...]
-
#
-
1
def extensions
-
@trail.extensions.dup
-
end
-
-
1
protected
-
1
attr_reader :trail
-
end
-
end
-
1
require 'sprockets/asset'
-
1
require 'sprockets/utils'
-
-
1
module Sprockets
-
1
class ProcessedAsset < Asset
-
1
def initialize(environment, logical_path, pathname)
-
super
-
-
start_time = Time.now.to_f
-
-
context = environment.context_class.new(environment, logical_path, pathname)
-
@source = context.evaluate(pathname)
-
@length = Rack::Utils.bytesize(source)
-
@digest = environment.digest.update(source).hexdigest
-
-
build_required_assets(environment, context)
-
build_dependency_paths(environment, context)
-
-
@dependency_digest = compute_dependency_digest(environment)
-
-
elapsed_time = ((Time.now.to_f - start_time) * 1000).to_i
-
environment.logger.debug "Compiled #{logical_path} (#{elapsed_time}ms) (pid #{Process.pid})"
-
end
-
-
# Interal: Used to check equality
-
1
attr_reader :dependency_digest
-
-
1
attr_reader :source
-
-
# Initialize `BundledAsset` from serialized `Hash`.
-
1
def init_with(environment, coder)
-
super
-
-
@source = coder['source']
-
@dependency_digest = coder['dependency_digest']
-
-
@required_assets = coder['required_paths'].map { |p|
-
p = expand_root_path(p)
-
-
unless environment.paths.detect { |path| p[path] }
-
raise UnserializeError, "#{p} isn't in paths"
-
end
-
-
p == pathname.to_s ? self : environment.find_asset(p, :bundle => false)
-
}
-
@dependency_paths = coder['dependency_paths'].map { |h|
-
DependencyFile.new(expand_root_path(h['path']), h['mtime'], h['digest'])
-
}
-
end
-
-
# Serialize custom attributes in `BundledAsset`.
-
1
def encode_with(coder)
-
super
-
-
coder['source'] = source
-
coder['dependency_digest'] = dependency_digest
-
-
coder['required_paths'] = required_assets.map { |a|
-
relativize_root_path(a.pathname).to_s
-
}
-
coder['dependency_paths'] = dependency_paths.map { |d|
-
{ 'path' => relativize_root_path(d.pathname).to_s,
-
'mtime' => d.mtime.iso8601,
-
'digest' => d.digest }
-
}
-
end
-
-
# Checks if Asset is stale by comparing the actual mtime and
-
# digest to the inmemory model.
-
1
def fresh?(environment)
-
# Check freshness of all declared dependencies
-
@dependency_paths.all? { |dep| dependency_fresh?(environment, dep) }
-
end
-
-
1
protected
-
1
class DependencyFile < Struct.new(:pathname, :mtime, :digest)
-
1
def initialize(pathname, mtime, digest)
-
pathname = Pathname.new(pathname) unless pathname.is_a?(Pathname)
-
mtime = Time.parse(mtime) if mtime.is_a?(String)
-
super
-
end
-
-
1
def eql?(other)
-
other.is_a?(DependencyFile) &&
-
pathname.eql?(other.pathname) &&
-
mtime.eql?(other.mtime) &&
-
digest.eql?(other.digest)
-
end
-
-
1
def hash
-
pathname.to_s.hash
-
end
-
end
-
-
1
private
-
1
def build_required_assets(environment, context)
-
@required_assets = resolve_dependencies(environment, context._required_paths + [pathname.to_s]) -
-
resolve_dependencies(environment, context._stubbed_assets.to_a)
-
end
-
-
1
def resolve_dependencies(environment, paths)
-
assets = []
-
cache = {}
-
-
paths.each do |path|
-
if path == self.pathname.to_s
-
unless cache[self]
-
cache[self] = true
-
assets << self
-
end
-
elsif asset = environment.find_asset(path, :bundle => false)
-
asset.required_assets.each do |asset_dependency|
-
unless cache[asset_dependency]
-
cache[asset_dependency] = true
-
assets << asset_dependency
-
end
-
end
-
end
-
end
-
-
assets
-
end
-
-
1
def build_dependency_paths(environment, context)
-
dependency_paths = {}
-
-
context._dependency_paths.each do |path|
-
dep = DependencyFile.new(path, environment.stat(path).mtime, environment.file_digest(path).hexdigest)
-
dependency_paths[dep] = true
-
end
-
-
context._dependency_assets.each do |path|
-
if path == self.pathname.to_s
-
dep = DependencyFile.new(pathname, environment.stat(path).mtime, environment.file_digest(path).hexdigest)
-
dependency_paths[dep] = true
-
elsif asset = environment.find_asset(path, :bundle => false)
-
asset.dependency_paths.each do |d|
-
dependency_paths[d] = true
-
end
-
end
-
end
-
-
@dependency_paths = dependency_paths.keys
-
end
-
-
1
def compute_dependency_digest(environment)
-
required_assets.inject(environment.digest) { |digest, asset|
-
digest.update asset.digest
-
}.hexdigest
-
end
-
end
-
end
-
1
require 'sprockets/engines'
-
1
require 'sprockets/mime'
-
1
require 'sprockets/processor'
-
1
require 'sprockets/utils'
-
-
1
module Sprockets
-
# `Processing` is an internal mixin whose public methods are exposed on
-
# the `Environment` and `Index` classes.
-
1
module Processing
-
# Returns an `Array` of format extension `String`s.
-
#
-
# format_extensions
-
# # => ['.js', '.css']
-
#
-
1
def format_extensions
-
@trail.extensions - @engines.keys
-
end
-
-
# Deprecated alias for `preprocessors`.
-
1
def processors(*args)
-
preprocessors(*args)
-
end
-
-
# Returns an `Array` of `Processor` classes. If a `mime_type`
-
# argument is supplied, the processors registered under that
-
# extension will be returned.
-
#
-
# Preprocessors are ran before Postprocessors and Engine
-
# processors.
-
#
-
# All `Processor`s must follow the `Tilt::Template` interface. It is
-
# recommended to subclass `Tilt::Template`.
-
1
def preprocessors(mime_type = nil)
-
2
if mime_type
-
@preprocessors[mime_type].dup
-
else
-
2
deep_copy_hash(@preprocessors)
-
end
-
end
-
-
# Returns an `Array` of `Processor` classes. If a `mime_type`
-
# argument is supplied, the processors registered under that
-
# extension will be returned.
-
#
-
# Postprocessors are ran after Preprocessors and Engine processors.
-
#
-
# All `Processor`s must follow the `Tilt::Template` interface. It is
-
# recommended to subclass `Tilt::Template`.
-
1
def postprocessors(mime_type = nil)
-
2
if mime_type
-
@postprocessors[mime_type].dup
-
else
-
2
deep_copy_hash(@postprocessors)
-
end
-
end
-
-
# Deprecated alias for `register_preprocessor`.
-
1
def register_processor(*args, &block)
-
register_preprocessor(*args, &block)
-
end
-
-
# Registers a new Preprocessor `klass` for `mime_type`.
-
#
-
# register_preprocessor 'text/css', Sprockets::DirectiveProcessor
-
#
-
# A block can be passed for to create a shorthand processor.
-
#
-
# register_preprocessor 'text/css', :my_processor do |context, data|
-
# data.gsub(...)
-
# end
-
#
-
1
def register_preprocessor(mime_type, klass, &block)
-
2
if block_given?
-
name = klass.to_s
-
klass = Class.new(Processor) do
-
@name = name
-
@processor = block
-
end
-
end
-
-
2
@preprocessors[mime_type].push(klass)
-
end
-
-
# Registers a new Postprocessor `klass` for `mime_type`.
-
#
-
# register_postprocessor 'text/css', Sprockets::CharsetNormalizer
-
#
-
# A block can be passed for to create a shorthand processor.
-
#
-
# register_postprocessor 'text/css', :my_processor do |context, data|
-
# data.gsub(...)
-
# end
-
#
-
1
def register_postprocessor(mime_type, klass, &block)
-
1
if block_given?
-
name = klass.to_s
-
klass = Class.new(Processor) do
-
@name = name
-
@processor = block
-
end
-
end
-
-
1
@postprocessors[mime_type].push(klass)
-
end
-
-
# Deprecated alias for `unregister_preprocessor`.
-
1
def unregister_processor(*args)
-
unregister_preprocessor(*args)
-
end
-
-
# Remove Preprocessor `klass` for `mime_type`.
-
#
-
# unregister_preprocessor 'text/css', Sprockets::DirectiveProcessor
-
#
-
1
def unregister_preprocessor(mime_type, klass)
-
if klass.is_a?(String) || klass.is_a?(Symbol)
-
klass = @preprocessors[mime_type].detect { |cls|
-
cls.respond_to?(:name) &&
-
cls.name == "Sprockets::Processor (#{klass})"
-
}
-
end
-
-
@preprocessors[mime_type].delete(klass)
-
end
-
-
# Remove Postprocessor `klass` for `mime_type`.
-
#
-
# unregister_postprocessor 'text/css', Sprockets::DirectiveProcessor
-
#
-
1
def unregister_postprocessor(mime_type, klass)
-
if klass.is_a?(String) || klass.is_a?(Symbol)
-
klass = @postprocessors[mime_type].detect { |cls|
-
cls.respond_to?(:name) &&
-
cls.name == "Sprockets::Processor (#{klass})"
-
}
-
end
-
-
@postprocessors[mime_type].delete(klass)
-
end
-
-
# Returns an `Array` of `Processor` classes. If a `mime_type`
-
# argument is supplied, the processors registered under that
-
# extension will be returned.
-
#
-
# Bundle Processors are ran on concatenated assets rather than
-
# individual files.
-
#
-
# All `Processor`s must follow the `Tilt::Template` interface. It is
-
# recommended to subclass `Tilt::Template`.
-
1
def bundle_processors(mime_type = nil)
-
2
if mime_type
-
@bundle_processors[mime_type].dup
-
else
-
2
deep_copy_hash(@bundle_processors)
-
end
-
end
-
-
# Registers a new Bundle Processor `klass` for `mime_type`.
-
#
-
# register_bundle_processor 'text/css', Sprockets::CharsetNormalizer
-
#
-
# A block can be passed for to create a shorthand processor.
-
#
-
# register_bundle_processor 'text/css', :my_processor do |context, data|
-
# data.gsub(...)
-
# end
-
#
-
1
def register_bundle_processor(mime_type, klass, &block)
-
2
if block_given?
-
name = klass.to_s
-
klass = Class.new(Processor) do
-
@name = name
-
@processor = block
-
end
-
end
-
-
2
@bundle_processors[mime_type].push(klass)
-
end
-
-
# Remove Bundle Processor `klass` for `mime_type`.
-
#
-
# unregister_bundle_processor 'text/css', Sprockets::CharsetNormalizer
-
#
-
1
def unregister_bundle_processor(mime_type, klass)
-
if klass.is_a?(String) || klass.is_a?(Symbol)
-
klass = @bundle_processors[mime_type].detect { |cls|
-
cls.respond_to?(:name) &&
-
cls.name == "Sprockets::Processor (#{klass})"
-
}
-
end
-
-
@bundle_processors[mime_type].delete(klass)
-
end
-
-
1
private
-
1
def add_engine_to_trail(ext, klass)
-
9
@trail.append_extension(ext.to_s)
-
-
9
if klass.respond_to?(:default_mime_type) && klass.default_mime_type
-
5
if format_ext = extension_for_mime_type(klass.default_mime_type)
-
5
@trail.alias_extension(ext.to_s, format_ext)
-
end
-
end
-
end
-
end
-
end
-
1
require 'tilt'
-
-
1
module Sprockets
-
# `Processor` creates an anonymous processor class from a block.
-
#
-
# register_preprocessor 'text/css', :my_processor do |context, data|
-
# # ...
-
# end
-
#
-
1
class Processor < Tilt::Template
-
# `processor` is a lambda or block
-
1
def self.processor
-
@processor
-
end
-
-
1
def self.name
-
"Sprockets::Processor (#{@name})"
-
end
-
-
1
def self.to_s
-
name
-
end
-
-
1
def prepare
-
end
-
-
# Call processor block with `context` and `data`.
-
1
def evaluate(context, locals)
-
self.class.processor.call(context, data)
-
end
-
end
-
end
-
1
require 'tilt'
-
-
1
module Sprockets
-
# For JS developers who are colonfobic, concatenating JS files using
-
# the module pattern usually leads to syntax errors.
-
#
-
# The `SafetyColons` processor will insert missing semicolons to the
-
# end of the file.
-
#
-
# This behavior can be disabled with:
-
#
-
# environment.unregister_postprocessor 'application/javascript', Sprockets::SafetyColons
-
#
-
1
class SafetyColons < Tilt::Template
-
1
def prepare
-
end
-
-
1
def evaluate(context, locals, &block)
-
# If the file is blank or ends in a semicolon, leave it as is
-
if data =~ /\A\s*\Z/m || data =~ /;\s*\Z/m
-
data
-
else
-
# Otherwise, append a semicolon and newline
-
"#{data};\n"
-
end
-
end
-
end
-
end
-
1
require 'tilt'
-
-
1
module Sprockets
-
1
class SassCompressor < Tilt::Template
-
1
self.default_mime_type = 'text/css'
-
-
1
def self.engine_initialized?
-
defined?(::Sass::Engine)
-
end
-
-
1
def initialize_engine
-
require_template_library 'sass'
-
end
-
-
1
def prepare
-
end
-
-
1
def evaluate(context, locals, &block)
-
::Sass::Engine.new(data, {
-
:syntax => :scss,
-
:cache => false,
-
:read_cache => false,
-
:style => :compressed
-
}).render
-
end
-
end
-
end
-
1
require 'sass'
-
-
1
module Sprockets
-
1
module SassFunctions
-
1
def asset_path(path)
-
Sass::Script::String.new(sprockets_context.asset_path(path.value), :string)
-
end
-
-
1
def asset_url(path)
-
Sass::Script::String.new("url(" + sprockets_context.asset_path(path.value) + ")")
-
end
-
-
1
def image_path(path)
-
Sass::Script::String.new(sprockets_context.image_path(path.value), :string)
-
end
-
-
1
def image_url(path)
-
Sass::Script::String.new("url(" + sprockets_context.image_path(path.value) + ")")
-
end
-
-
1
def video_path(path)
-
Sass::Script::String.new(sprockets_context.video_path(path.value), :string)
-
end
-
-
1
def video_url(path)
-
Sass::Script::String.new("url(" + sprockets_context.video_path(path.value) + ")")
-
end
-
-
1
def audio_path(path)
-
Sass::Script::String.new(sprockets_context.audio_path(path.value), :string)
-
end
-
-
1
def audio_url(path)
-
Sass::Script::String.new("url(" + sprockets_context.audio_path(path.value) + ")")
-
end
-
-
1
def font_path(path)
-
Sass::Script::String.new(sprockets_context.font_path(path.value), :string)
-
end
-
-
1
def font_url(path)
-
Sass::Script::String.new("url(" + sprockets_context.font_path(path.value) + ")")
-
end
-
-
1
def javascript_path(path)
-
Sass::Script::String.new(sprockets_context.javascript_path(path.value), :string)
-
end
-
-
1
def javascript_url(path)
-
Sass::Script::String.new("url(" + sprockets_context.javascript_path(path.value) + ")")
-
end
-
-
1
def stylesheet_path(path)
-
Sass::Script::String.new(sprockets_context.stylesheet_path(path.value), :string)
-
end
-
-
1
def stylesheet_url(path)
-
Sass::Script::String.new("url(" + sprockets_context.stylesheet_path(path.value) + ")")
-
end
-
-
1
protected
-
1
def sprockets_context
-
options[:sprockets][:context]
-
end
-
-
1
def sprockets_environment
-
options[:sprockets][:environment]
-
end
-
end
-
end
-
1
require 'sass'
-
-
1
module Sprockets
-
# This custom importer adds sprockets dependency tracking on to Sass
-
# `@import` statements. This makes the Sprockets and Sass caching
-
# systems work together.
-
1
class SassImporter < Sass::Importers::Filesystem
-
1
def initialize(context, root)
-
@context = context
-
super root.to_s
-
end
-
-
1
def find_relative(*args)
-
engine = super
-
if engine && (filename = engine.options[:filename])
-
@context.depend_on(filename)
-
end
-
engine
-
end
-
-
1
def find(*args)
-
engine = super
-
if engine && (filename = engine.options[:filename])
-
@context.depend_on(filename)
-
end
-
engine
-
end
-
end
-
end
-
1
require 'tilt'
-
-
1
module Sprockets
-
# This custom Tilt handler replaces the one built into Tilt. The
-
# main difference is that it uses a custom importer that plays nice
-
# with sprocket's caching system.
-
#
-
# See `SassImporter` for more infomation.
-
1
class SassTemplate < Tilt::Template
-
1
self.default_mime_type = 'text/css'
-
-
1
def self.engine_initialized?
-
defined?(::Sass::Engine) && defined?(::Sass::Script::Functions) &&
-
::Sass::Script::Functions < Sprockets::SassFunctions
-
end
-
-
1
def initialize_engine
-
# Double check constant to avoid tilt warning
-
unless defined? ::Sass
-
require_template_library 'sass'
-
end
-
-
# Install custom functions. It'd be great if this didn't need to
-
# be installed globally, but could be passed into Engine as an
-
# option.
-
::Sass::Script::Functions.send :include, Sprockets::SassFunctions
-
end
-
-
1
def prepare
-
end
-
-
1
def syntax
-
:sass
-
end
-
-
1
def evaluate(context, locals, &block)
-
# Use custom importer that knows about Sprockets Caching
-
cache_store = SassCacheStore.new(context.environment)
-
-
options = {
-
:filename => eval_file,
-
:line => line,
-
:syntax => syntax,
-
:cache_store => cache_store,
-
:importer => SassImporter.new(context, context.pathname),
-
:load_paths => context.environment.paths.map { |path| SassImporter.new(context, path) },
-
:sprockets => {
-
:context => context,
-
:environment => context.environment
-
}
-
}
-
-
::Sass::Engine.new(data, options).render
-
rescue ::Sass::SyntaxError => e
-
# Annotates exception message with parse line number
-
context.__LINE__ = e.sass_backtrace.first[:line]
-
raise e
-
end
-
end
-
end
-
1
require 'sprockets/sass_template'
-
-
1
module Sprockets
-
# Scss handler to replace Tilt's builtin one. See `SassTemplate` and
-
# `SassImporter` for more infomation.
-
1
class ScssTemplate < SassTemplate
-
1
self.default_mime_type = 'text/css'
-
-
1
def syntax
-
:scss
-
end
-
end
-
end
-
1
require 'time'
-
1
require 'uri'
-
-
1
module Sprockets
-
# `Server` is a concern mixed into `Environment` and
-
# `Index` that provides a Rack compatible `call`
-
# interface and url generation helpers.
-
1
module Server
-
# `call` implements the Rack 1.x specification which accepts an
-
# `env` Hash and returns a three item tuple with the status code,
-
# headers, and body.
-
#
-
# Mapping your environment at a url prefix will serve all assets
-
# in the path.
-
#
-
# map "/assets" do
-
# run Sprockets::Environment.new
-
# end
-
#
-
# A request for `"/assets/foo/bar.js"` will search your
-
# environment for `"foo/bar.js"`.
-
1
def call(env)
-
start_time = Time.now.to_f
-
time_elapsed = lambda { ((Time.now.to_f - start_time) * 1000).to_i }
-
-
msg = "Served asset #{env['PATH_INFO']} -"
-
-
# Mark session as "skipped" so no `Set-Cookie` header is set
-
env['rack.session.options'] ||= {}
-
env['rack.session.options'][:defer] = true
-
env['rack.session.options'][:skip] = true
-
-
# Extract the path from everything after the leading slash
-
path = unescape(env['PATH_INFO'].to_s.sub(/^\//, ''))
-
-
# URLs containing a `".."` are rejected for security reasons.
-
if forbidden_request?(path)
-
return forbidden_response
-
end
-
-
# Strip fingerprint
-
if fingerprint = path_fingerprint(path)
-
path = path.sub("-#{fingerprint}", '')
-
end
-
-
# Look up the asset.
-
asset = find_asset(path, :bundle => !body_only?(env))
-
-
# `find_asset` returns nil if the asset doesn't exist
-
if asset.nil?
-
logger.info "#{msg} 404 Not Found (#{time_elapsed.call}ms)"
-
-
# Return a 404 Not Found
-
not_found_response
-
-
# Check request headers `HTTP_IF_NONE_MATCH` against the asset digest
-
elsif etag_match?(asset, env)
-
logger.info "#{msg} 304 Not Modified (#{time_elapsed.call}ms)"
-
-
# Return a 304 Not Modified
-
not_modified_response(asset, env)
-
-
else
-
logger.info "#{msg} 200 OK (#{time_elapsed.call}ms)"
-
-
# Return a 200 with the asset contents
-
ok_response(asset, env)
-
end
-
rescue Exception => e
-
logger.error "Error compiling asset #{path}:"
-
logger.error "#{e.class.name}: #{e.message}"
-
-
case content_type_of(path)
-
when "application/javascript"
-
# Re-throw JavaScript asset exceptions to the browser
-
logger.info "#{msg} 500 Internal Server Error\n\n"
-
return javascript_exception_response(e)
-
when "text/css"
-
# Display CSS asset exceptions in the browser
-
logger.info "#{msg} 500 Internal Server Error\n\n"
-
return css_exception_response(e)
-
else
-
raise
-
end
-
end
-
-
1
private
-
1
def forbidden_request?(path)
-
# Prevent access to files elsewhere on the file system
-
#
-
# http://example.org/assets/../../../etc/passwd
-
#
-
path.include?("..")
-
end
-
-
# Returns a 403 Forbidden response tuple
-
1
def forbidden_response
-
[ 403, { "Content-Type" => "text/plain", "Content-Length" => "9" }, [ "Forbidden" ] ]
-
end
-
-
# Returns a 404 Not Found response tuple
-
1
def not_found_response
-
[ 404, { "Content-Type" => "text/plain", "Content-Length" => "9", "X-Cascade" => "pass" }, [ "Not found" ] ]
-
end
-
-
# Returns a JavaScript response that re-throws a Ruby exception
-
# in the browser
-
1
def javascript_exception_response(exception)
-
err = "#{exception.class.name}: #{exception.message}"
-
body = "throw Error(#{err.inspect})"
-
[ 200, { "Content-Type" => "application/javascript", "Content-Length" => Rack::Utils.bytesize(body).to_s }, [ body ] ]
-
end
-
-
# Returns a CSS response that hides all elements on the page and
-
# displays the exception
-
1
def css_exception_response(exception)
-
message = "\n#{exception.class.name}: #{exception.message}"
-
backtrace = "\n #{exception.backtrace.first}"
-
-
body = <<-CSS
-
html {
-
padding: 18px 36px;
-
}
-
-
head {
-
display: block;
-
}
-
-
body {
-
margin: 0;
-
padding: 0;
-
}
-
-
body > * {
-
display: none !important;
-
}
-
-
head:after, body:before, body:after {
-
display: block !important;
-
}
-
-
head:after {
-
font-family: sans-serif;
-
font-size: large;
-
font-weight: bold;
-
content: "Error compiling CSS asset";
-
}
-
-
body:before, body:after {
-
font-family: monospace;
-
white-space: pre-wrap;
-
}
-
-
body:before {
-
font-weight: bold;
-
content: "#{escape_css_content(message)}";
-
}
-
-
body:after {
-
content: "#{escape_css_content(backtrace)}";
-
}
-
CSS
-
-
[ 200, { "Content-Type" => "text/css;charset=utf-8", "Content-Length" => Rack::Utils.bytesize(body).to_s }, [ body ] ]
-
end
-
-
# Escape special characters for use inside a CSS content("...") string
-
1
def escape_css_content(content)
-
content.
-
gsub('\\', '\\\\005c ').
-
gsub("\n", '\\\\000a ').
-
gsub('"', '\\\\0022 ').
-
gsub('/', '\\\\002f ')
-
end
-
-
# Compare the requests `HTTP_IF_NONE_MATCH` against the assets digest
-
1
def etag_match?(asset, env)
-
env["HTTP_IF_NONE_MATCH"] == etag(asset)
-
end
-
-
# Test if `?body=1` or `body=true` query param is set
-
1
def body_only?(env)
-
env["QUERY_STRING"].to_s =~ /body=(1|t)/
-
end
-
-
# Returns a 304 Not Modified response tuple
-
1
def not_modified_response(asset, env)
-
[ 304, {}, [] ]
-
end
-
-
# Returns a 200 OK response tuple
-
1
def ok_response(asset, env)
-
[ 200, headers(env, asset, asset.length), asset ]
-
end
-
-
1
def headers(env, asset, length)
-
Hash.new.tap do |headers|
-
# Set content type and length headers
-
headers["Content-Type"] = asset.content_type
-
headers["Content-Length"] = length.to_s
-
-
# Set caching headers
-
headers["Cache-Control"] = "public"
-
headers["Last-Modified"] = asset.mtime.httpdate
-
headers["ETag"] = etag(asset)
-
-
# If the request url contains a fingerprint, set a long
-
# expires on the response
-
if path_fingerprint(env["PATH_INFO"])
-
headers["Cache-Control"] << ", max-age=31536000"
-
-
# Otherwise set `must-revalidate` since the asset could be modified.
-
else
-
headers["Cache-Control"] << ", must-revalidate"
-
end
-
end
-
end
-
-
# Gets digest fingerprint.
-
#
-
# "foo-0aa2105d29558f3eb790d411d7d8fb66.js"
-
# # => "0aa2105d29558f3eb790d411d7d8fb66"
-
#
-
1
def path_fingerprint(path)
-
path[/-([0-9a-f]{7,40})\.[^.]+$/, 1]
-
end
-
-
# URI.unescape is deprecated on 1.9. We need to use URI::Parser
-
# if its available.
-
1
if defined? URI::DEFAULT_PARSER
-
1
def unescape(str)
-
str = URI::DEFAULT_PARSER.unescape(str)
-
str.force_encoding(Encoding.default_internal) if Encoding.default_internal
-
str
-
end
-
else
-
def unescape(str)
-
URI.unescape(str)
-
end
-
end
-
-
# Helper to quote the assets digest for use as an ETag.
-
1
def etag(asset)
-
%("#{asset.digest}")
-
end
-
end
-
end
-
1
require 'sprockets/asset'
-
1
require 'fileutils'
-
1
require 'zlib'
-
-
1
module Sprockets
-
# `StaticAsset`s are used for files that are served verbatim without
-
# any processing or concatenation. These are typical images and
-
# other binary files.
-
1
class StaticAsset < Asset
-
# Returns file contents as its `source`.
-
1
def source
-
# File is read everytime to avoid memory bloat of large binary files
-
pathname.open('rb') { |f| f.read }
-
end
-
-
# Implemented for Rack SendFile support.
-
1
def to_path
-
pathname.to_s
-
end
-
-
# Save asset to disk.
-
1
def write_to(filename, options = {})
-
# Gzip contents if filename has '.gz'
-
options[:compress] ||= File.extname(filename) == '.gz'
-
-
FileUtils.mkdir_p File.dirname(filename)
-
-
if options[:compress]
-
# Open file and run it through `Zlib`
-
pathname.open('rb') do |rd|
-
File.open("#{filename}+", 'wb') do |wr|
-
gz = Zlib::GzipWriter.new(wr, Zlib::BEST_COMPRESSION)
-
gz.mtime = mtime.to_i
-
buf = ""
-
while rd.read(16384, buf)
-
gz.write(buf)
-
end
-
gz.close
-
end
-
end
-
else
-
# If no compression needs to be done, we can just copy it into place.
-
FileUtils.cp(pathname, "#{filename}+")
-
end
-
-
# Atomic write
-
FileUtils.mv("#{filename}+", filename)
-
-
# Set mtime correctly
-
File.utime(mtime, mtime, filename)
-
-
nil
-
ensure
-
# Ensure tmp file gets cleaned up
-
FileUtils.rm("#{filename}+") if File.exist?("#{filename}+")
-
end
-
end
-
end
-
1
require 'tilt'
-
-
1
module Sprockets
-
1
class UglifierCompressor < Tilt::Template
-
1
self.default_mime_type = 'application/javascript'
-
-
1
def self.engine_initialized?
-
defined?(::Uglifier)
-
end
-
-
1
def initialize_engine
-
require_template_library 'uglifier'
-
end
-
-
1
def prepare
-
end
-
-
1
def evaluate(context, locals, &block)
-
# Feature detect Uglifier 2.0 option support
-
if Uglifier::DEFAULTS[:copyright]
-
# Uglifier < 2.x
-
Uglifier.new(:copyright => false).compile(data)
-
else
-
# Uglifier >= 2.x
-
Uglifier.new(:comments => :none).compile(data)
-
end
-
end
-
end
-
end
-
1
module Sprockets
-
# `Utils`, we didn't know where else to put it!
-
1
module Utils
-
# If theres encoding support (aka Ruby 1.9)
-
1
if "".respond_to?(:valid_encoding?)
-
# Define UTF-8 BOM pattern matcher.
-
# Avoid using a Regexp literal because it inheirts the files
-
# encoding and we want to avoid syntax errors in other interpreters.
-
1
UTF8_BOM_PATTERN = Regexp.new("\\A\uFEFF".encode('utf-8'))
-
-
1
def self.read_unicode(pathname, external_encoding = Encoding.default_external)
-
pathname.open("r:#{external_encoding}") do |f|
-
f.read.tap do |data|
-
# Eager validate the file's encoding. In most cases we
-
# expect it to be UTF-8 unless `default_external` is set to
-
# something else. An error is usually raised if the file is
-
# saved as UTF-16 when we expected UTF-8.
-
if !data.valid_encoding?
-
raise EncodingError, "#{pathname} has a invalid " +
-
"#{data.encoding} byte sequence"
-
-
# If the file is UTF-8 and theres a BOM, strip it for safe concatenation.
-
elsif data.encoding.name == "UTF-8" && data =~ UTF8_BOM_PATTERN
-
data.sub!(UTF8_BOM_PATTERN, "")
-
end
-
end
-
end
-
end
-
-
else
-
# Define UTF-8 and UTF-16 BOM pattern matchers.
-
# Avoid using a Regexp literal to prevent syntax errors in other interpreters.
-
UTF8_BOM_PATTERN = Regexp.new("\\A\\xEF\\xBB\\xBF")
-
UTF16_BOM_PATTERN = Regexp.new("\\A(\\xFE\\xFF|\\xFF\\xFE)")
-
-
def self.read_unicode(pathname)
-
pathname.read.tap do |data|
-
# If the file is UTF-8 and theres a BOM, strip it for safe concatenation.
-
if data =~ UTF8_BOM_PATTERN
-
data.sub!(UTF8_BOM_PATTERN, "")
-
-
# If we find a UTF-16 BOM, theres nothing we can do on
-
# 1.8. Only UTF-8 is supported.
-
elsif data =~ UTF16_BOM_PATTERN
-
raise EncodingError, "#{pathname} has a UTF-16 BOM. " +
-
"Resave the file as UTF-8 or upgrade to Ruby 1.9."
-
end
-
end
-
end
-
end
-
-
# Prepends a leading "." to an extension if its missing.
-
#
-
# normalize_extension("js")
-
# # => ".js"
-
#
-
# normalize_extension(".css")
-
# # => ".css"
-
#
-
1
def self.normalize_extension(extension)
-
11
extension = extension.to_s
-
11
if extension[/^\./]
-
11
extension
-
else
-
".#{extension}"
-
end
-
end
-
end
-
end
-
1
module Sprockets
-
1
VERSION = "2.11.0"
-
end
-
1
require 'tilt'
-
-
1
module Sprockets
-
1
class YUICompressor < Tilt::Template
-
1
def self.engine_initialized?
-
defined?(::YUI)
-
end
-
-
1
def initialize_engine
-
require_template_library 'yui/compressor'
-
end
-
-
1
def prepare
-
end
-
-
1
def evaluate(context, locals, &block)
-
case context.content_type
-
when 'application/javascript'
-
YUI::JavaScriptCompressor.new.compress(data)
-
when 'text/css'
-
YUI::CssCompressor.new.compress(data)
-
else
-
data
-
end
-
end
-
end
-
end
-
1
require 'action_view'
-
1
require 'sprockets'
-
1
require 'active_support/core_ext/class/attribute'
-
-
1
module Sprockets
-
1
module Rails
-
1
module Helper
-
1
class << self
-
1
attr_accessor :precompile, :assets, :raise_runtime_errors
-
end
-
-
1
def precompile
-
Sprockets::Rails::Helper.precompile
-
end
-
-
1
def assets
-
Sprockets::Rails::Helper.assets
-
end
-
-
1
def raise_runtime_errors
-
Sprockets::Rails::Helper.raise_runtime_errors
-
end
-
-
1
class AssetFilteredError < StandardError
-
1
def initialize(source)
-
msg = "Asset filtered out and will not be served: " <<
-
"add `Rails.application.config.assets.precompile += %w( #{source} )` " <<
-
"to `config/initializers/assets.rb` and restart your server"
-
super(msg)
-
end
-
end
-
-
1
class AbsoluteAssetPathError < ArgumentError
-
1
def initialize(bad_path, good_path, prefix)
-
msg = "Asset names passed to helpers should not include the #{prefix.inspect} prefix. " <<
-
"Instead of #{bad_path.inspect}, use #{good_path.inspect}"
-
super(msg)
-
end
-
end
-
-
1
if defined? ActionView::Helpers::AssetUrlHelper
-
1
include ActionView::Helpers::AssetUrlHelper
-
1
include ActionView::Helpers::AssetTagHelper
-
else
-
require 'sprockets/rails/legacy_asset_tag_helper'
-
require 'sprockets/rails/legacy_asset_url_helper'
-
include LegacyAssetTagHelper
-
include LegacyAssetUrlHelper
-
end
-
-
1
VIEW_ACCESSORS = [:assets_environment, :assets_manifest,
-
:assets_prefix, :digest_assets, :debug_assets]
-
-
1
def self.included(klass)
-
2
if klass < Sprockets::Context
-
1
klass.class_eval do
-
1
alias_method :assets_environment, :environment
-
1
def assets_manifest; end
-
1
class_attribute :config, :assets_prefix, :digest_assets, :debug_assets
-
end
-
else
-
1
klass.class_attribute(*VIEW_ACCESSORS)
-
end
-
end
-
-
1
def self.extended(obj)
-
obj.class_eval do
-
attr_accessor(*VIEW_ACCESSORS)
-
end
-
end
-
-
1
def compute_asset_path(path, options = {})
-
# Check if we are inside Sprockets context before calling check_dependencies!.
-
check_dependencies!(path) if defined?(depend_on)
-
-
if digest_path = asset_digest_path(path)
-
path = digest_path if digest_assets
-
path += "?body=1" if options[:debug]
-
File.join(assets_prefix || "/", path)
-
else
-
super
-
end
-
end
-
-
# Computes the full URL to a asset in the public directory. This
-
# method checks for errors before returning path.
-
1
def asset_path(source, options = {})
-
unless options[:debug]
-
check_errors_for(source, options)
-
end
-
super(source, options)
-
end
-
1
alias :path_to_asset :asset_path
-
-
# Get digest for asset path.
-
#
-
# path - String path
-
# options - Hash options
-
#
-
# Returns String Hex digest or nil if digests are disabled.
-
1
def asset_digest(path, options = {})
-
return unless digest_assets
-
-
if digest_path = asset_digest_path(path, options)
-
digest_path[/-(.+)\./, 1]
-
end
-
end
-
-
# Expand asset path to digested form.
-
#
-
# path - String path
-
# options - Hash options
-
#
-
# Returns String path or nil if no asset was found.
-
1
def asset_digest_path(path, options = {})
-
if manifest = assets_manifest
-
if digest_path = manifest.assets[path]
-
return digest_path
-
end
-
end
-
-
if environment = assets_environment
-
if asset = environment[path]
-
return asset.digest_path
-
end
-
end
-
end
-
-
# Override javascript tag helper to provide debugging support.
-
#
-
# Eventually will be deprecated and replaced by source maps.
-
1
def javascript_include_tag(*sources)
-
options = sources.extract_options!.stringify_keys
-
-
if options["debug"] != false && request_debug_assets?
-
sources.map { |source|
-
check_errors_for(source, :type => :javascript)
-
if asset = lookup_asset_for_path(source, :type => :javascript)
-
asset.to_a.map do |a|
-
super(path_to_javascript(a.logical_path, :debug => true), options)
-
end
-
else
-
super(source, options)
-
end
-
}.flatten.uniq.join("\n").html_safe
-
else
-
sources.push(options)
-
super(*sources)
-
end
-
end
-
-
# Override stylesheet tag helper to provide debugging support.
-
#
-
# Eventually will be deprecated and replaced by source maps.
-
1
def stylesheet_link_tag(*sources)
-
options = sources.extract_options!.stringify_keys
-
if options["debug"] != false && request_debug_assets?
-
sources.map { |source|
-
check_errors_for(source, :type => :stylesheet)
-
if asset = lookup_asset_for_path(source, :type => :stylesheet)
-
asset.to_a.map do |a|
-
super(path_to_stylesheet(a.logical_path, :debug => true), options)
-
end
-
else
-
super(source, options)
-
end
-
}.flatten.uniq.join("\n").html_safe
-
else
-
sources.push(options)
-
super(*sources)
-
end
-
end
-
-
1
protected
-
# Ensures the asset is included in the dependencies list.
-
1
def check_dependencies!(dep)
-
depend_on(dep)
-
depend_on_asset(dep)
-
rescue Sprockets::FileNotFound
-
end
-
-
# Raise errors when source is not in the precompiled list, or
-
# incorrectly contains the assets_prefix.
-
1
def check_errors_for(source, options)
-
return unless self.raise_runtime_errors
-
-
source = source.to_s
-
return if source.blank? || source =~ URI_REGEXP
-
-
asset = lookup_asset_for_path(source, options)
-
-
if asset && asset_needs_precompile?(asset.logical_path, asset.pathname.to_s)
-
raise AssetFilteredError.new(asset.logical_path)
-
end
-
-
full_prefix = File.join(self.assets_prefix || "/", '')
-
if !asset && source.start_with?(full_prefix)
-
short_path = source[full_prefix.size, source.size]
-
if lookup_asset_for_path(short_path, options)
-
raise AbsoluteAssetPathError.new(source, short_path, full_prefix)
-
end
-
end
-
end
-
-
# Returns true when an asset will not be available after precompile is run
-
1
def asset_needs_precompile?(source, filename)
-
if assets_environment && assets_environment.send(:matches_filter, precompile || [], source, filename)
-
false
-
else
-
true
-
end
-
end
-
-
# Enable split asset debugging. Eventually will be deprecated
-
# and replaced by source maps in Sprockets 3.x.
-
1
def request_debug_assets?
-
debug_assets || (defined?(controller) && controller && params[:debug_assets])
-
rescue
-
return false
-
end
-
-
# Internal method to support multifile debugging. Will
-
# eventually be removed w/ Sprockets 3.x.
-
1
def lookup_asset_for_path(path, options = {})
-
return unless env = assets_environment
-
path = path.to_s
-
if extname = compute_asset_extname(path, options)
-
path = "#{path}#{extname}"
-
end
-
env[path]
-
end
-
end
-
end
-
end
-
1
module Sprockets
-
1
module Rails
-
1
VERSION = "2.2.0"
-
end
-
end
-
1
require 'rails'
-
1
require 'rails/railtie'
-
1
require 'action_controller/railtie'
-
1
require 'active_support/core_ext/module/remove_method'
-
1
require 'sprockets'
-
1
require 'sprockets/rails/helper'
-
1
require 'sprockets/rails/version'
-
-
1
module Rails
-
1
class Application
-
# Hack: We need to remove Rails' built in config.assets so we can
-
# do our own thing.
-
1
class Configuration
-
1
remove_possible_method :assets
-
end
-
-
# Undefine Rails' assets method before redefining it, to avoid warnings.
-
1
remove_possible_method :assets
-
1
remove_possible_method :assets=
-
-
# Returns Sprockets::Environment for app config.
-
1
def assets
-
@assets ||= Sprockets::Environment.new(root.to_s) do |env|
-
1
env.version = ::Rails.env
-
-
1
path = "#{config.root}/tmp/cache/assets/#{::Rails.env}"
-
1
env.cache = Sprockets::Cache::FileStore.new(path)
-
-
1
env.context_class.class_eval do
-
1
include ::Sprockets::Rails::Helper
-
end
-
26
end
-
end
-
1
attr_writer :assets
-
end
-
end
-
-
1
module Sprockets
-
1
class Railtie < ::Rails::Railtie
-
1
LOOSE_APP_ASSETS = lambda do |filename, path|
-
path =~ /app\/assets/ && !%w(.js .css).include?(File.extname(filename))
-
end
-
-
1
class OrderedOptions < ActiveSupport::OrderedOptions
-
1
def configure(&block)
-
self._blocks << block
-
end
-
end
-
-
1
config.assets = OrderedOptions.new
-
1
config.assets._blocks = []
-
1
config.assets.paths = []
-
1
config.assets.prefix = "/assets"
-
1
config.assets.manifest = nil
-
1
config.assets.precompile = [LOOSE_APP_ASSETS, /(?:\/|\\|\A)application\.(css|js)$/]
-
1
config.assets.version = ""
-
1
config.assets.debug = false
-
1
config.assets.compile = true
-
1
config.assets.digest = false
-
-
1
rake_tasks do |app|
-
require 'sprockets/rails/task'
-
Sprockets::Rails::Task.new(app)
-
end
-
-
1
config.after_initialize do |app|
-
1
config = app.config
-
-
1
manifest_assets_path = File.join(config.paths['public'].first, config.assets.prefix)
-
-
# Configuration options that should invalidate
-
# the Sprockets cache when changed.
-
1
app.assets.version = [
-
app.assets.version,
-
config.assets.version,
-
config.action_controller.relative_url_root,
-
1
(config.action_controller.asset_host unless config.action_controller.asset_host.respond_to?(:call)),
-
Sprockets::Rails::VERSION
-
].compact.join('-')
-
-
# Copy config.assets.paths to Sprockets
-
1
config.assets.paths.each do |path|
-
13
app.assets.append_path path
-
end
-
-
1
ActiveSupport.on_load(:action_view) do
-
1
include Sprockets::Rails::Helper
-
-
# Copy relevant config to AV context
-
1
self.debug_assets = config.assets.debug
-
1
self.digest_assets = config.assets.digest
-
1
self.assets_prefix = config.assets.prefix
-
-
# Copy over to Sprockets as well
-
1
context = app.assets.context_class
-
1
context.assets_prefix = config.assets.prefix
-
1
context.digest_assets = config.assets.digest
-
1
context.config = config.action_controller
-
-
1
if config.assets.compile
-
1
self.assets_environment = app.assets
-
1
self.assets_manifest = Sprockets::Manifest.new(app.assets, manifest_assets_path, config.assets.manifest)
-
else
-
self.assets_manifest = Sprockets::Manifest.new(manifest_assets_path, config.assets.manifest)
-
end
-
end
-
-
1
app.assets.js_compressor = config.assets.js_compressor
-
1
app.assets.css_compressor = config.assets.css_compressor
-
-
# Run app.assets.configure blocks
-
1
config.assets._blocks.each do |block|
-
block.call app.assets
-
end
-
-
# No more configuration changes at this point.
-
# With cache classes on, Sprockets won't check the FS when files
-
# change. Preferable in production when the FS only changes on
-
# deploys when the app restarts.
-
1
if config.cache_classes
-
1
app.assets = app.assets.index
-
end
-
-
-
1
Sprockets::Rails::Helper.precompile ||= app.config.assets.precompile
-
1
Sprockets::Rails::Helper.assets ||= app.assets
-
1
Sprockets::Rails::Helper.raise_runtime_errors = app.config.assets.raise_runtime_errors
-
-
1
if config.assets.compile
-
1
if app.routes.respond_to?(:prepend)
-
1
app.routes.prepend do
-
1
mount app.assets => config.assets.prefix
-
end
-
end
-
end
-
end
-
end
-
end
-
1
require "v8/version"
-
-
1
require 'v8/weak'
-
1
require 'v8/init'
-
1
require 'v8/error'
-
1
require 'v8/stack'
-
1
require 'v8/conversion/fundamental'
-
1
require 'v8/conversion/indentity'
-
1
require 'v8/conversion/reference'
-
1
require 'v8/conversion/primitive'
-
1
require 'v8/conversion/code'
-
1
require 'v8/conversion/class'
-
1
require 'v8/conversion/object'
-
1
require 'v8/conversion/time'
-
1
require 'v8/conversion/hash'
-
1
require 'v8/conversion/array'
-
1
require 'v8/conversion/proc'
-
1
require 'v8/conversion/method'
-
1
require 'v8/conversion/symbol'
-
1
require 'v8/conversion/string'
-
1
require 'v8/conversion/fixnum'
-
1
require 'v8/conversion'
-
1
require 'v8/access/names'
-
1
require 'v8/access/indices'
-
1
require 'v8/access/invocation'
-
1
require 'v8/access'
-
1
require 'v8/context'
-
1
require 'v8/object'
-
1
require 'v8/array'
-
1
require 'v8/function'
-
1
class V8::Access
-
1
include Names
-
1
include Indices
-
1
include Invocation
-
end
-
1
class V8::Access
-
1
module Indices
-
-
1
def indices(obj)
-
obj.respond_to?(:length) ? (0..obj.length).to_a : []
-
end
-
-
1
def iget(obj, index, &dontintercept)
-
if obj.respond_to?(:[])
-
obj.send(:[], index, &dontintercept)
-
else
-
yield
-
end
-
end
-
-
1
def iset(obj, index, value, &dontintercept)
-
if obj.respond_to?(:[]=)
-
obj.send(:[]=, index, value, &dontintercept)
-
else
-
yield
-
end
-
end
-
-
1
def iquery(obj, index, attributes, &dontintercept)
-
if obj.respond_to?(:[])
-
attributes.dont_delete
-
unless obj.respond_to?(:[]=)
-
attributes.read_only
-
end
-
else
-
yield
-
end
-
end
-
-
1
def idelete(obj, index, &dontintercept)
-
yield
-
end
-
-
end
-
end
-
1
class V8::Access
-
1
module Invocation
-
1
def methodcall(code, this, args)
-
code.methodcall this, args
-
end
-
-
1
module Aritize
-
1
def aritize(args)
-
arity < 0 ? args : Array.new(arity).to_enum(:each_with_index).map {|item, i| args[i]}
-
end
-
end
-
-
1
module Proc
-
1
include Aritize
-
1
def methodcall(this, args)
-
call *aritize([this].concat(args))
-
end
-
1
::Proc.send :include, self
-
end
-
-
1
module Method
-
1
include Aritize
-
1
def methodcall(this, args)
-
context = V8::Context.current
-
access = context.access
-
if this.equal? self.receiver
-
call *aritize(args)
-
elsif this.class <= self.receiver.class
-
access.methodcall(unbind, this, args)
-
elsif this.equal? context.scope
-
call *aritize(args)
-
else
-
fail TypeError, "cannot invoke #{self} on #{this}"
-
end
-
end
-
1
::Method.send :include, self
-
end
-
-
1
module UnboundMethod
-
1
def methodcall(this, args)
-
access = V8::Context.current.access
-
access.methodcall bind(this), this, args
-
end
-
1
::UnboundMethod.send :include, self
-
end
-
end
-
end
-
1
require 'set'
-
1
class V8::Access
-
1
module Names
-
1
def names(obj)
-
accessible_names(obj)
-
end
-
-
1
def get(obj, name, &dontintercept)
-
methods = accessible_names(obj)
-
if methods.include?(name)
-
method = obj.method(name)
-
method.arity == 0 ? method.call : method.unbind
-
elsif obj.respond_to?(:[]) && !special?(name)
-
obj.send(:[], name, &dontintercept)
-
else
-
yield
-
end
-
end
-
-
1
def set(obj, name, value, &dontintercept)
-
setter = name + "="
-
methods = accessible_names(obj, true)
-
if methods.include?(setter)
-
obj.send(setter, value)
-
elsif obj.respond_to?(:[]=) && !special?(name)
-
obj.send(:[]=, name, value, &dontintercept)
-
else
-
yield
-
end
-
end
-
-
1
def query(obj, name, attributes, &dontintercept)
-
if obj.respond_to?(name)
-
attributes.dont_delete
-
unless obj.respond_to?(name + "=")
-
attributes.read_only
-
end
-
else
-
yield
-
end
-
end
-
-
1
def delete(obj, name, &dontintercept)
-
yield
-
end
-
-
1
def accessible_names(obj, special_methods = false)
-
obj.public_methods(false).map {|m| m.to_s}.to_set.tap do |methods|
-
ancestors = obj.class.ancestors.dup
-
while ancestor = ancestors.shift
-
break if ancestor == ::Object
-
methods.merge(ancestor.public_instance_methods(false).map {|m| m.to_s})
-
end
-
methods.reject!(&special?) unless special_methods
-
end
-
end
-
-
1
private
-
-
1
def special?(name = nil)
-
@special ||= lambda {|m| m == "[]" || m == "[]=" || m =~ /=$/}
-
name.nil? ? @special : @special[name]
-
end
-
end
-
end
-
1
class V8::Array < V8::Object
-
-
1
def initialize(native_or_length = nil)
-
super do
-
if native_or_length.is_a?(Numeric)
-
V8::C::Array::New(native_or_length)
-
elsif native_or_length.is_a?(V8::C::Array)
-
native_or_length
-
else
-
V8::C::Array::New()
-
end
-
end
-
end
-
-
1
def each
-
@context.enter do
-
0.upto(@native.Length() - 1) do |i|
-
yield @context.to_ruby(@native.Get(i))
-
end
-
end
-
end
-
-
1
def length
-
@native.Length()
-
end
-
end
-
# -*- coding: utf-8 -*-
-
1
require 'stringio'
-
1
module V8
-
# All JavaScript must be executed in a context. This context consists of a global scope containing the
-
# standard JavaScript objects¨and functions like Object, String, Array, as well as any objects or
-
# functions from Ruby which have been embedded into it from the containing enviroment. E.g.
-
#
-
# V8::Context.new do |cxt|
-
# cxt['num'] = 5
-
# cxt.eval('num + 5') #=> 10
-
# end
-
#
-
# The same object may appear in any number of contexts, but only one context may be executing JavaScript code
-
# in any given thread. If a new context is opened in a thread in which a context is already opened, the second
-
# context will "mask" the old context e.g.
-
#
-
# six = 6
-
# Context.new do |cxt|
-
# cxt['num'] = 5
-
# cxt.eval('num') # => 5
-
# Context.new do |cxt|
-
# cxt['num'] = 10
-
# cxt.eval('num') # => 10
-
# cxt.eval('++num') # => 11
-
# end
-
# cxt.eval('num') # => 5
-
# end
-
1
class Context
-
1
include V8::Error::Try
-
-
# @!attribute [r] conversion
-
# @return [V8::Conversion] conversion behavior for this context
-
1
attr_reader :conversion
-
-
# @!attrribute [r] access
-
# @return [V8::Access] Ruby access behavior for this context
-
1
attr_reader :access
-
-
# @!attribute [r] native
-
# @return [V8::C::Context] the underlying C++ object
-
1
attr_reader :native
-
-
# @!attribute [r] timeout
-
# @return [Number] maximum execution time in milliseconds for scripts executed in this context
-
1
attr_reader :timeout
-
-
# Creates a new context.
-
#
-
# If passed the `:with` option, that object will be used as
-
# the global scope of the newly creating context. e.g.
-
#
-
# scope = Object.new
-
# def scope.hello; "Hi"; end
-
# V8::Context.new(:with => scope) do |cxt|
-
# cxt['hello'] #=> 'Hi'
-
# end
-
#
-
# If passed the `:timeout` option, every eval will timeout once
-
# N milliseconds elapse
-
#
-
# @param [Hash<Symbol, Object>] options initial context configuration
-
# * :with scope serves as the global scope of the new context
-
# @yield [V8::Context] the newly created context
-
1
def initialize(options = {})
-
@conversion = Conversion.new
-
@access = Access.new
-
@timeout = options[:timeout]
-
if global = options[:with]
-
Context.new.enter do
-
global_template = global.class.to_template.InstanceTemplate()
-
@native = V8::C::Context::New(nil, global_template)
-
end
-
enter {link global, @native.Global()}
-
else
-
V8::C::Locker() do
-
@native = V8::C::Context::New()
-
end
-
end
-
yield self if block_given?
-
end
-
-
# Compile and execute a string of JavaScript source.
-
#
-
# If `source` is an IO object it will be read fully before being evaluated
-
#
-
# @param [String,IO] source the source code to compile and execute
-
# @param [String] filename the name to use for this code when generating stack traces
-
# @param [Integer] line the line number to start with
-
# @return [Object] the result of the evaluation
-
1
def eval(source, filename = '<eval>', line = 1)
-
if IO === source || StringIO === source
-
source = source.read
-
end
-
enter do
-
script = try { V8::C::Script::New(source.to_s, filename.to_s) }
-
if @timeout
-
to_ruby try {script.RunWithTimeout(@timeout)}
-
else
-
to_ruby try {script.Run()}
-
end
-
end
-
end
-
-
# Read a value from the global scope of this context
-
#
-
# @param [Object] key the name of the value to read
-
# @return [Object] value the value at `key`
-
1
def [](key)
-
enter do
-
to_ruby(@native.Global().Get(to_v8(key)))
-
end
-
end
-
-
# Binds `value` to the name `key` in the global scope of this context.
-
#
-
# @param [Object] key the name to bind to
-
# @param [Object] value the value to bind
-
1
def []=(key, value)
-
enter do
-
@native.Global().Set(to_v8(key), to_v8(value))
-
end
-
return value
-
end
-
-
# Destroy this context and release any internal references it may
-
# contain to embedded Ruby objects.
-
#
-
# A disposed context may never again be used for anything, and all
-
# objects created with it will become unusable.
-
1
def dispose
-
return unless @native
-
@native.Dispose()
-
@native = nil
-
V8::C::V8::ContextDisposedNotification()
-
def self.enter
-
fail "cannot enter a context which has already been disposed"
-
end
-
end
-
-
# Returns this context's global object. This will be a `V8::Object`
-
# if no scope was provided or just an `Object` if a Ruby object
-
# is serving as the global scope.
-
#
-
# @return [Object] scope the context's global scope.
-
1
def scope
-
enter { to_ruby @native.Global() }
-
end
-
-
# Converts a v8 C++ object into its ruby counterpart. This is method
-
# is used to translate all values passed to Ruby from JavaScript, either
-
# as return values or as callback parameters.
-
#
-
# @param [V8::C::Object] v8_object the native c++ object to convert.
-
# @return [Object] to pass to Ruby
-
# @see V8::Conversion for how to customize and extend this mechanism
-
1
def to_ruby(v8_object)
-
@conversion.to_ruby(v8_object)
-
end
-
-
# Converts a Ruby object into a native v8 C++ object. This method is
-
# used to translate all values passed to JavaScript from Ruby, either
-
# as return value or as callback parameters.
-
#
-
# @param [Object] ruby_object the Ruby object to convert
-
# @return [V8::C::Object] to pass to V8
-
# @see V8::Conversion for customizing and extending this mechanism
-
1
def to_v8(ruby_object)
-
@conversion.to_v8(ruby_object)
-
end
-
-
# Marks a Ruby object and a v8 C++ Object as being the same. In other
-
# words whenever `ruby_object` is passed to v8, the result of the
-
# conversion should be `v8_object`. Conversely, whenever `v8_object`
-
# is passed to Ruby, the result of the conversion should be `ruby_object`.
-
# The Ruby Racer uses this mechanism to maintain referential integrity
-
# between Ruby and JavaScript peers
-
#
-
# @param [Object] ruby_object the Ruby half of the object identity
-
# @param [V8::C::Object] v8_object the V8 half of the object identity.
-
# @see V8::Conversion::Identity
-
1
def link(ruby_object, v8_object)
-
@conversion.equate ruby_object, v8_object
-
end
-
-
# Links `ruby_object` and `v8_object` inside the currently entered
-
# context. This is an error if no context has been entered.
-
#
-
# @param [Object] ruby_object the Ruby half of the object identity
-
# @param [V8::C::Object] v8_object the V8 half of the object identity.
-
1
def self.link(ruby_object, v8_object)
-
current.link ruby_object, v8_object
-
end
-
-
# Run some Ruby code in the context of this context.
-
#
-
# This will acquire the V8 interpreter lock (possibly blocking
-
# until it is available), and prepare V8 for JavaScript execution.
-
#
-
# Only one context may be running at a time per thread.
-
#
-
# @return [Object] the result of executing `block`
-
1
def enter(&block)
-
if !entered?
-
lock_scope_and_enter(&block)
-
else
-
yield
-
end
-
end
-
-
# Indicates if this context is the currently entered context
-
#
-
# @return true if this context is currently entered
-
1
def entered?
-
Context.current == self
-
end
-
-
# Get the currently entered context.
-
#
-
# @return [V8::Context] currently entered context, nil if none entered.
-
1
def self.current
-
Thread.current[:v8_context]
-
end
-
-
# Compile and execute the contents of the file with path `filename`
-
# as JavaScript code.
-
#
-
# @param [String] filename path to the file to execute.
-
# @return [Object] the result of the evaluation.
-
1
def load(filename)
-
File.open(filename) do |file|
-
self.eval file, filename
-
end
-
end
-
-
1
private
-
-
1
def self.current=(context)
-
Thread.current[:v8_context] = context
-
end
-
-
1
def lock_scope_and_enter
-
current = Context.current
-
Context.current = self
-
V8::C::Locker() do
-
V8::C::HandleScope() do
-
begin
-
@native.Enter()
-
yield if block_given?
-
ensure
-
@native.Exit()
-
end
-
end
-
end
-
ensure
-
Context.current = current
-
end
-
end
-
end
-
-
1
class V8::Conversion
-
1
include Fundamental
-
1
include Identity
-
-
1
def to_ruby(v8_object)
-
super v8_object
-
end
-
-
1
def to_v8(ruby_object)
-
super ruby_object
-
end
-
end
-
-
1
for type in [TrueClass, FalseClass, NilClass, Float] do
-
4
type.class_eval do
-
4
include V8::Conversion::Primitive
-
end
-
end
-
-
1
for type in [Class, Object, Array, Hash, String, Symbol, Time, Proc, Method, Fixnum] do
-
10
type.class_eval do
-
10
include V8::Conversion.const_get(type.name)
-
end
-
end
-
-
1
class UnboundMethod
-
1
include V8::Conversion::Method
-
end
-
-
1
for type in [:Object, :String, :Date] do
-
3
V8::C::const_get(type).class_eval do
-
3
include V8::Conversion::const_get("Native#{type}")
-
end
-
end
-
-
1
class V8::Conversion
-
1
module Array
-
1
def to_v8
-
array = V8::Array.new(length)
-
each_with_index do |item, i|
-
array[i] = item
-
end
-
return array.to_v8
-
end
-
end
-
end
-
1
class V8::Conversion
-
1
module Class
-
1
include V8::Conversion::Code
-
-
1
def to_template
-
weakcell(:constructor) do
-
template = V8::C::FunctionTemplate::New(V8::Conversion::Constructor.new(self))
-
prototype = template.InstanceTemplate()
-
prototype.SetNamedPropertyHandler(V8::Conversion::Get, V8::Conversion::Set)
-
prototype.SetIndexedPropertyHandler(V8::Conversion::IGet, V8::Conversion::ISet)
-
if self != ::Object && superclass != ::Object && superclass != ::Class
-
template.Inherit(superclass.to_template)
-
end
-
template
-
end
-
end
-
end
-
-
1
class Constructor
-
1
include V8::Error::Protect
-
-
1
def initialize(cls)
-
@class = cls
-
end
-
-
1
def call(arguments)
-
arguments.extend Args
-
protect do
-
if arguments.linkage_call?
-
arguments.link
-
else
-
arguments.construct @class
-
end
-
end
-
return arguments.This()
-
end
-
-
1
module Args
-
1
def linkage_call?
-
self.Length() == 1 && self[0].IsExternal()
-
end
-
-
1
def link
-
external = self[0]
-
This().SetHiddenValue("rr::implementation", external)
-
context.link external.Value(), This()
-
end
-
-
1
def construct(cls)
-
context.link cls.new(*to_args), This()
-
end
-
-
1
def context
-
V8::Context.current
-
end
-
-
1
def to_args
-
args = ::Array.new(Length())
-
0.upto(args.length - 1) do |i|
-
args[i] = self[i]
-
end
-
return args
-
end
-
end
-
end
-
-
1
module Accessor
-
1
include V8::Error::Protect
-
1
def intercept(info, key, &block)
-
context = V8::Context.current
-
access = context.access
-
object = context.to_ruby(info.This())
-
handles_property = true
-
dontintercept = proc do
-
handles_property = false
-
end
-
protect do
-
result = block.call(context, access, object, context.to_ruby(key), dontintercept)
-
handles_property ? context.to_v8(result) : V8::C::Value::Empty
-
end
-
end
-
end
-
-
1
class Get
-
1
extend Accessor
-
1
def self.call(property, info)
-
intercept(info, property) do |context, access, object, key, dontintercept|
-
access.get(object, key, &dontintercept)
-
end
-
end
-
end
-
-
1
class Set
-
1
extend Accessor
-
1
def self.call(property, value, info)
-
intercept(info, property) do |context, access, object, key, dontintercept|
-
access.set(object, key, context.to_ruby(value), &dontintercept)
-
end
-
end
-
end
-
-
1
class IGet
-
1
extend Accessor
-
1
def self.call(property, info)
-
intercept(info, property) do |context, access, object, key, dontintercept|
-
access.iget(object, key, &dontintercept)
-
end
-
end
-
end
-
-
1
class ISet
-
1
extend Accessor
-
1
def self.call(property, value, info)
-
intercept(info, property) do |context, access, object, key, dontintercept|
-
access.iset(object, key, context.to_ruby(value), &dontintercept)
-
end
-
end
-
end
-
end
-
1
class V8::Conversion
-
1
module Code
-
1
include V8::Weak::Cell
-
-
1
def to_v8
-
fn = to_template.GetFunction()
-
V8::Context.link self, fn
-
return fn
-
end
-
-
1
def to_template
-
weakcell(:template) {V8::C::FunctionTemplate::New(InvocationHandler.new(self))}
-
end
-
-
1
class InvocationHandler
-
1
include V8::Error::Protect
-
-
1
def initialize(code)
-
@code = code
-
end
-
-
1
def call(arguments)
-
protect do
-
context = V8::Context.current
-
access = context.access
-
args = ::Array.new(arguments.Length())
-
0.upto(args.length - 1) do |i|
-
if i < args.length
-
args[i] = context.to_ruby arguments[i]
-
end
-
end
-
this = context.to_ruby arguments.This()
-
context.to_v8 access.methodcall(@code, this, args)
-
end
-
end
-
end
-
end
-
end
-
1
class V8::Conversion
-
1
module Fixnum
-
1
def to_ruby
-
self
-
end
-
-
1
def to_v8
-
self.to_f.to_v8
-
end
-
end
-
end
-
1
class V8::Conversion
-
1
module Fundamental
-
1
def to_ruby(v8_object)
-
v8_object.to_ruby
-
end
-
-
1
def to_v8(ruby_object)
-
ruby_object.to_v8
-
end
-
end
-
end
-
1
class V8::Conversion
-
1
module Hash
-
1
def to_v8
-
object = V8::Object.new
-
each do |key, value|
-
object[key] = value
-
end
-
return object.to_v8
-
end
-
end
-
end
-
1
require 'ref'
-
-
1
class V8::Conversion
-
1
module Identity
-
1
def to_ruby(v8_object)
-
if v8_object.class <= V8::C::Object
-
v8_idmap[v8_object.GetIdentityHash()] || super(v8_object)
-
else
-
super(v8_object)
-
end
-
end
-
-
1
def to_v8(ruby_object)
-
return super(ruby_object) if ruby_object.is_a?(String) || ruby_object.is_a?(Primitive)
-
rb_idmap[ruby_object.object_id] || super(ruby_object)
-
end
-
-
1
def equate(ruby_object, v8_object)
-
v8_idmap[v8_object.GetIdentityHash()] = ruby_object
-
rb_idmap[ruby_object.object_id] = v8_object
-
end
-
-
1
def v8_idmap
-
@v8_idmap ||= V8::Weak::WeakValueMap.new
-
end
-
-
1
def rb_idmap
-
@ruby_idmap ||= V8::Weak::WeakValueMap.new
-
end
-
end
-
end
-
1
class V8::Conversion
-
1
module Method
-
1
include V8::Conversion::Code
-
-
1
def to_v8
-
template = @@method_cache[self] ||= to_template
-
template.GetFunction()
-
end
-
-
1
class MethodCache
-
1
def initialize
-
1
@map = V8::Weak::WeakValueMap.new
-
end
-
-
1
def [](method)
-
@map[method.to_s]
-
end
-
-
1
def []=(method, template)
-
@map[method.to_s] = template
-
end
-
end
-
-
1
@@method_cache = MethodCache.new
-
end
-
end
-
1
class V8::Conversion
-
1
module Object
-
1
def to_v8
-
Reference.construct! self
-
end
-
-
1
def to_ruby
-
self
-
end
-
end
-
-
1
module NativeObject
-
1
def to_ruby
-
wrap = if IsArray()
-
::V8::Array
-
elsif IsFunction()
-
::V8::Function
-
else
-
::V8::Object
-
end
-
wrap.new(self)
-
end
-
-
1
def to_v8
-
self
-
end
-
end
-
end
-
1
class V8::Conversion
-
1
module Primitive
-
1
def to_v8
-
return self
-
end
-
end
-
end
-
1
class V8::Conversion
-
1
module Proc
-
1
include V8::Conversion::Code
-
end
-
end
-
1
class V8::Conversion
-
1
module Reference
-
-
1
def self.construct!(object)
-
context = V8::Context.current
-
constructor = context.to_v8(object.class)
-
reference = constructor.NewInstance([V8::C::External::New(object)])
-
return reference
-
end
-
-
1
def to_v8
-
Reference.construct! self
-
end
-
-
end
-
end
-
1
class V8::Conversion
-
1
module String
-
1
def to_v8
-
V8::C::String::New(self)
-
end
-
end
-
1
module NativeString
-
1
def to_ruby
-
self.Utf8Value()
-
end
-
end
-
end
-
1
class V8::Conversion
-
1
module Symbol
-
1
def to_v8
-
V8::C::String::NewSymbol(to_s)
-
end
-
end
-
end
-
1
class V8::Conversion
-
1
module Time
-
1
def to_v8
-
V8::C::Date::New(to_f * 1000)
-
end
-
end
-
-
1
module NativeDate
-
1
def to_ruby
-
::Time.at(self.NumberValue() / 1000)
-
end
-
end
-
end
-
1
module V8
-
# capture 99 stack frames on exception with normal details.
-
# You can adjust these values for performance or turn of stack capture entirely
-
1
V8::C::V8::SetCaptureStackTraceForUncaughtExceptions(true, 99, V8::C::StackTrace::kOverview)
-
1
class Error < StandardError
-
1
include Enumerable
-
-
# @!attribute [r] value
-
# @return [Object] the JavaScript value passed to the `throw` statement
-
1
attr_reader :value
-
-
# @!attribute [r] cause
-
# @return [Exception] the underlying error (if any) that triggered this error to be raised
-
1
attr_reader :cause
-
-
# @!attribute [r] javascript_backtrace
-
# @return [V8::StackTrace] the complete JavaScript stack at the point this error was thrown
-
1
attr_reader :javascript_backtrace
-
-
# keep an alias to the StandardError#backtrace method so that we can capture
-
# just ruby backtrace frames
-
1
alias_method :standard_error_backtrace, :backtrace
-
-
1
def initialize(message, value, javascript_backtrace, cause = nil)
-
super(message)
-
@value = value
-
@cause = cause
-
@javascript_backtrace = javascript_backtrace
-
end
-
-
1
def causes
-
[].tap do |causes|
-
current = self
-
until current.nil? do
-
causes.push current
-
current = current.respond_to?(:cause) ? current.cause : nil
-
end
-
end
-
end
-
-
1
def backtrace(*modifiers)
-
return unless super()
-
trace_framework = modifiers.include?(:framework)
-
trace_ruby = modifiers.length == 0 || modifiers.include?(:ruby)
-
trace_javascript = modifiers.length == 0 || modifiers.include?(:javascript)
-
bilingual_backtrace(trace_ruby, trace_javascript).tap do |trace|
-
trace.reject! {|frame| frame =~ %r{(lib/v8/.*\.rb|ext/v8/.*\.cc)}} unless modifiers.include?(:framework)
-
end
-
end
-
-
1
def root_cause
-
causes.last
-
end
-
-
1
def in_javascript?
-
causes.last.is_a? self.class
-
end
-
-
1
def in_ruby?
-
!in_javascript?
-
end
-
-
1
def bilingual_backtrace(trace_ruby = true, trace_javascript = true)
-
backtrace = causes.reduce(:backtrace => [], :ruby => -1, :javascript => -1) { |accumulator, cause|
-
accumulator.tap do
-
if trace_ruby
-
backtrace_selector = cause.respond_to?(:standard_error_backtrace) ? :standard_error_backtrace : :backtrace
-
ruby_frames = cause.send(backtrace_selector)[0..accumulator[:ruby]]
-
accumulator[:backtrace].unshift *ruby_frames
-
accumulator[:ruby] -= ruby_frames.length
-
end
-
if trace_javascript && cause.respond_to?(:javascript_backtrace)
-
javascript_frames = cause.javascript_backtrace.to_a[0..accumulator[:javascript]].map(&:to_s)
-
accumulator[:backtrace].unshift *javascript_frames
-
accumulator[:javascript] -= javascript_frames.length
-
end
-
end
-
}[:backtrace]
-
end
-
-
1
module Try
-
1
def try
-
V8::C::TryCatch() do |trycatch|
-
result = yield
-
if trycatch.HasCaught()
-
raise V8::Error(trycatch)
-
else
-
result
-
end
-
end
-
end
-
end
-
-
1
module Protect
-
1
def protect
-
yield
-
rescue Exception => e
-
error = V8::C::Exception::Error(e.message)
-
error.SetHiddenValue("rr::Cause", V8::C::External::New(e))
-
V8::C::ThrowException(error)
-
end
-
end
-
-
end
-
-
# Convert the result of a triggered JavaScript try/catch block into
-
# a V8::Error
-
#
-
# This is a bit of a yak-shave because JavaScript let's you throw all
-
# kinds of things. We do our best to make sure that the message property
-
# of the resulting V8::Error is as helpful as possible, and that it
-
# contains as much source location information as we can put onto it.
-
#
-
# For example:
-
#
-
# throw 4
-
# throw 'four'
-
# throw {number: 4}
-
#
-
# are all valid cases, none of which actually reference an exception object
-
# with a stack trace and a message. only with something like:
-
#
-
# throw new Error('fail!')
-
#
-
# do you get the a proper stacktrace and a message property. However a lot of
-
# times JavaScript library authors are lazy and do this:
-
#
-
# throw {message: 'foo', otherMetadata: 'bar'}
-
#
-
# It's common enough so we do the courtesy of having the resulting V8::Error
-
# have as its message in ruby land the 'message' property of the value object
-
#
-
# To further complicate things, SyntaxErrors do not have a JavaScript stack
-
# (even if they occur during js execution). This can make debugging a nightmare
-
# so we copy in the source location of the syntax error into the message of
-
# the resulting V8::Error
-
#
-
# @param [V8::C::TryCatch] native trycatch object that has been triggered
-
# @return [V8::Error] the error generated by this try/catch
-
1
def self.Error(trycatch)
-
exception = trycatch.Exception()
-
-
value = exception.to_ruby
-
cause = nil
-
message = trycatch.Message()
-
javascript_backtrace = V8::StackTrace.new(message.GetStackTrace()) if message
-
-
message = if !exception.kind_of?(V8::C::Value)
-
exception.to_s==""?"Script Timed Out":exception.to_s
-
elsif exception.IsNativeError()
-
if cause = exception.GetHiddenValue("rr::Cause")
-
cause = cause.Value()
-
end
-
if value['constructor'] == V8::Context.current['SyntaxError']
-
info = trycatch.Message()
-
resource_name = info.GetScriptResourceName().to_ruby
-
"#{value['message']} at #{resource_name}:#{info.GetLineNumber()}:#{info.GetStartColumn() + 1}"
-
else
-
exception.Get("message").to_ruby
-
end
-
elsif exception.IsObject()
-
value['message'] || value.to_s
-
else
-
value.to_s
-
end
-
V8::Error.new(message, value, javascript_backtrace, cause)
-
end
-
1
const_set :JSError, Error
-
end
-
1
class V8::Function < V8::Object
-
1
include V8::Error::Try
-
-
1
def initialize(native = nil)
-
super do
-
native || V8::C::FunctionTemplate::New().GetFunction()
-
end
-
end
-
-
1
def methodcall(this, *args)
-
@context.enter do
-
this ||= @context.native.Global()
-
@context.to_ruby try {native.Call(@context.to_v8(this), args.map {|a| @context.to_v8 a})}
-
end
-
end
-
-
1
def call(*args)
-
@context.enter do
-
methodcall @context.native.Global(), *args
-
end
-
end
-
-
1
def new(*args)
-
@context.enter do
-
@context.to_ruby try {native.NewInstance(args.map {|a| @context.to_v8 a})}
-
end
-
end
-
end
-
1
class V8::Object
-
1
include Enumerable
-
1
attr_reader :native
-
1
alias_method :to_v8, :native
-
-
1
def initialize(native = nil)
-
@context = V8::Context.current or fail "tried to initialize a #{self.class} without being in an entered V8::Context"
-
@native = block_given? ? yield : native || V8::C::Object::New()
-
@context.link self, @native
-
end
-
-
1
def [](key)
-
@context.enter do
-
@context.to_ruby @native.Get(@context.to_v8(key))
-
end
-
end
-
-
1
def []=(key, value)
-
@context.enter do
-
@native.Set(@context.to_v8(key), @context.to_v8(value))
-
end
-
return value
-
end
-
-
1
def keys
-
@context.enter do
-
names = @native.GetPropertyNames()
-
0.upto( names.Length() - 1).to_enum.map {|i| @context.to_ruby names.Get(i)}
-
end
-
end
-
-
1
def values
-
@context.enter do
-
names = @native.GetPropertyNames()
-
0.upto( names.Length() - 1).to_enum.map {|i| @context.to_ruby @native.Get(names.Get(i))}
-
end
-
end
-
-
1
def each
-
@context.enter do
-
names = @native.GetPropertyNames()
-
0.upto(names.Length() - 1) do |i|
-
name = names.Get(i)
-
yield @context.to_ruby(name), @context.to_ruby(@native.Get(name))
-
end
-
end
-
end
-
-
1
def to_s
-
@context.enter do
-
@context.to_ruby @native.ToString()
-
end
-
end
-
-
1
def respond_to?(method)
-
super or self[method] != nil
-
end
-
-
1
def method_missing(name, *args, &block)
-
if name.to_s =~ /(.*)=$/
-
if args.length > 1
-
self[$1] = args
-
return args
-
else
-
self[$1] = args.first
-
return args
-
end
-
end
-
return super(name, *args, &block) unless self.respond_to?(name)
-
property = self[name]
-
if property.kind_of?(V8::Function)
-
property.methodcall(self, *args)
-
elsif args.empty?
-
property
-
else
-
raise ArgumentError, "wrong number of arguments (#{args.length} for 0)" unless args.empty?
-
end
-
end
-
end
-
-
1
module V8
-
-
1
class StackTrace
-
1
include Enumerable
-
-
1
def initialize(native)
-
@context = V8::Context.current
-
@native = native
-
end
-
-
1
def length
-
@context.enter do
-
@native ? @native.GetFrameCount() : 0
-
end
-
end
-
-
1
def each
-
return unless @native
-
@context.enter do
-
for i in 0..length - 1
-
yield V8::StackFrame.new(@native.GetFrame(i), @context)
-
end
-
end
-
end
-
-
1
def to_s
-
@native ? map(&:to_s).join("\n") : ""
-
end
-
end
-
-
1
class StackFrame
-
-
1
def initialize(native, context)
-
@context = context
-
@native = native
-
end
-
-
1
def script_name
-
@context.enter do
-
@context.to_ruby(@native.GetScriptName())
-
end
-
end
-
-
1
def function_name
-
@context.enter do
-
@context.to_ruby(@native.GetFunctionName())
-
end
-
end
-
-
1
def line_number
-
@context.enter do
-
@native.GetLineNumber()
-
end
-
end
-
-
1
def column
-
@context.enter do
-
@native.GetColumn()
-
end
-
end
-
-
1
def eval?
-
@context.enter do
-
@native.IsEval()
-
end
-
end
-
-
1
def constructor?
-
@context.enter do
-
@native.IsConstructor()
-
end
-
end
-
-
1
def to_s
-
@context.enter do
-
"at " + if !function_name.empty?
-
"#{function_name} (#{script_name}:#{line_number}:#{column})"
-
else
-
"#{script_name}:#{line_number}:#{column}"
-
end
-
end
-
end
-
end
-
end
-
1
module V8
-
1
VERSION = "0.12.1"
-
end
-
1
module V8
-
1
module Weak
-
# weak refs are broken on MRI 1.9 and merely slow on 1.8
-
# so we mitigate this by using the 'ref' gem. However, this
-
# only mitigates the problem. Under heavy load, you will still
-
# get different or terminated objects being returned. bad stuff.
-
#
-
# If you are having problems with this, an upgrade to 2.0 is *highly*
-
# recommended.
-
#
-
# for other platforms we just use the stdlib 'weakref'
-
# implementation
-
1
if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'ruby' && RUBY_VERSION < '2.0.0'
-
require 'ref'
-
Ref = ::Ref::WeakReference
-
WeakValueMap = ::Ref::WeakValueMap
-
else
-
1
require 'weakref'
-
1
class Ref
-
1
def initialize(object)
-
@ref = ::WeakRef.new(object)
-
end
-
1
def object
-
@ref.__getobj__
-
rescue ::WeakRef::RefError
-
nil
-
end
-
end
-
-
1
class WeakValueMap
-
1
def initialize
-
1
@values = {}
-
end
-
-
1
def [](key)
-
if ref = @values[key]
-
ref.object
-
end
-
end
-
-
1
def []=(key, value)
-
@values[key] = V8::Weak::Ref.new(value)
-
end
-
end
-
end
-
-
1
module Cell
-
1
def weakcell(name, &block)
-
unless storage = instance_variable_get("@#{name}")
-
storage = instance_variable_set("@#{name}", Storage.new)
-
end
-
storage.access(&block)
-
end
-
1
class Storage
-
1
def access(&block)
-
if @ref
-
@ref.object || populate(block)
-
else
-
populate(block)
-
end
-
end
-
-
1
private
-
-
1
def populate(block)
-
occupant = block.call()
-
@ref = V8::Weak::Ref.new(occupant)
-
return occupant
-
end
-
end
-
end
-
end
-
end
-
1
require 'thread_safe/version'
-
1
require 'thread_safe/synchronized_delegator'
-
-
1
module ThreadSafe
-
1
autoload :Cache, 'thread_safe/cache'
-
1
autoload :Util, 'thread_safe/util'
-
-
# Various classes within allows for +nil+ values to be stored, so a special +NULL+ token is required to indicate the "nil-ness".
-
1
NULL = Object.new
-
-
1
if defined?(JRUBY_VERSION)
-
require 'jruby/synchronized'
-
-
# A thread-safe subclass of Array. This version locks
-
# against the object itself for every method call,
-
# ensuring only one thread can be reading or writing
-
# at a time. This includes iteration methods like
-
# #each.
-
class Array < ::Array
-
include JRuby::Synchronized
-
end
-
-
# A thread-safe subclass of Hash. This version locks
-
# against the object itself for every method call,
-
# ensuring only one thread can be reading or writing
-
# at a time. This includes iteration methods like
-
# #each.
-
class Hash < ::Hash
-
include JRuby::Synchronized
-
end
-
elsif !defined?(RUBY_ENGINE) || RUBY_ENGINE == 'ruby'
-
# Because MRI never runs code in parallel, the existing
-
# non-thread-safe structures should usually work fine.
-
1
Array = ::Array
-
1
Hash = ::Hash
-
elsif defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx'
-
require 'monitor'
-
-
class Hash < ::Hash; end
-
class Array < ::Array; end
-
-
[Hash, Array].each do |klass|
-
klass.class_eval do
-
private
-
def _mon_initialize
-
@_monitor = Monitor.new unless @_monitor # avoid double initialisation
-
end
-
-
def self.allocate
-
obj = super
-
obj.send(:_mon_initialize)
-
obj
-
end
-
end
-
-
klass.superclass.instance_methods(false).each do |method|
-
klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
-
def #{method}(*args)
-
@_monitor.synchronize { super }
-
end
-
RUBY_EVAL
-
end
-
end
-
end
-
end
-
1
require 'thread'
-
-
1
module ThreadSafe
-
1
autoload :JRubyCacheBackend, 'thread_safe/jruby_cache_backend'
-
1
autoload :MriCacheBackend, 'thread_safe/mri_cache_backend'
-
1
autoload :NonConcurrentCacheBackend, 'thread_safe/non_concurrent_cache_backend'
-
1
autoload :AtomicReferenceCacheBackend, 'thread_safe/atomic_reference_cache_backend'
-
1
autoload :SynchronizedCacheBackend, 'thread_safe/synchronized_cache_backend'
-
-
1
ConcurrentCacheBackend = if defined?(RUBY_ENGINE)
-
1
case RUBY_ENGINE
-
when 'jruby'; JRubyCacheBackend
-
1
when 'ruby'; MriCacheBackend
-
when 'rbx'; AtomicReferenceCacheBackend
-
else
-
warn 'ThreadSafe: unsupported Ruby engine, using a fully synchronized ThreadSafe::Cache implementation' if $VERBOSE
-
SynchronizedCacheBackend
-
end
-
else
-
MriCacheBackend
-
end
-
-
1
class Cache < ConcurrentCacheBackend
-
1
KEY_ERROR = defined?(KeyError) ? KeyError : IndexError # there is no KeyError in 1.8 mode
-
-
1
def initialize(options = nil, &block)
-
93
if options.kind_of?(::Hash)
-
22
validate_options_hash!(options)
-
else
-
71
options = nil
-
end
-
-
93
super(options)
-
93
@default_proc = block
-
end
-
-
1
def [](key)
-
7925
if value = super # non-falsy value is an existing mapping, return it right away
-
7814
value
-
# re-check is done with get_or_default(key, NULL) instead of a simple !key?(key) in order to avoid a race condition, whereby by the time the current thread gets to the key?(key) call
-
# a key => value mapping might have already been created by a different thread (key?(key) would then return true, this elsif branch wouldn't be taken and an incorrent +nil+ value
-
# would be returned)
-
# note: nil == value check is not technically necessary
-
111
elsif @default_proc && nil == value && NULL == (value = get_or_default(key, NULL))
-
18
@default_proc.call(self, key)
-
else
-
93
value
-
end
-
end
-
-
1
alias_method :get, :[]
-
1
alias_method :put, :[]=
-
-
1
def fetch(key, default_value = NULL)
-
11
if NULL != (value = get_or_default(key, NULL))
-
6
value
-
5
elsif block_given?
-
5
yield key
-
elsif NULL != default_value
-
default_value
-
else
-
raise_fetch_no_key
-
end
-
end
-
-
1
def fetch_or_store(key, default_value = NULL)
-
fetch(key) do
-
put(key, block_given? ? yield(key) : (NULL == default_value ? raise_fetch_no_key : default_value))
-
end
-
end
-
-
def put_if_absent(key, value)
-
computed = false
-
result = compute_if_absent(key) do
-
computed = true
-
value
-
end
-
computed ? nil : result
-
1
end unless method_defined?(:put_if_absent)
-
-
def value?(value)
-
each_value do |v|
-
return true if value.equal?(v)
-
end
-
false
-
1
end unless method_defined?(:value?)
-
-
def keys
-
4
arr = []
-
4
each_pair {|k, v| arr << k}
-
4
arr
-
1
end unless method_defined?(:keys)
-
-
def values
-
13
arr = []
-
50
each_pair {|k, v| arr << v}
-
13
arr
-
1
end unless method_defined?(:values)
-
-
def each_key
-
each_pair {|k, v| yield k}
-
1
end unless method_defined?(:each_key)
-
-
def each_value
-
each_pair {|k, v| yield v}
-
1
end unless method_defined?(:each_value)
-
-
def key(value)
-
each_pair {|k, v| return k if v == value}
-
nil
-
1
end unless method_defined?(:key)
-
1
alias_method :index, :key if RUBY_VERSION < '1.9'
-
-
def empty?
-
each_pair {|k, v| return false}
-
true
-
1
end unless method_defined?(:empty?)
-
-
def size
-
count = 0
-
each_pair {|k, v| count += 1}
-
count
-
1
end unless method_defined?(:size)
-
-
1
def marshal_dump
-
raise TypeError, "can't dump hash with default proc" if @default_proc
-
h = {}
-
each_pair {|k, v| h[k] = v}
-
h
-
end
-
-
1
def marshal_load(hash)
-
initialize
-
populate_from(hash)
-
end
-
-
1
undef :freeze
-
-
1
private
-
1
def raise_fetch_no_key
-
raise KEY_ERROR, 'key not found'
-
end
-
-
1
def initialize_copy(other)
-
super
-
populate_from(other)
-
end
-
-
1
def populate_from(hash)
-
hash.each_pair {|k, v| self[k] = v}
-
self
-
end
-
-
1
def validate_options_hash!(options)
-
22
if (initial_capacity = options[:initial_capacity]) && (!initial_capacity.kind_of?(Fixnum) || initial_capacity < 0)
-
raise ArgumentError, ":initial_capacity must be a positive Fixnum"
-
end
-
22
if (load_factor = options[:load_factor]) && (!load_factor.kind_of?(Numeric) || load_factor <= 0 || load_factor > 1)
-
raise ArgumentError, ":load_factor must be a number between 0 and 1"
-
end
-
end
-
end
-
end
-
1
module ThreadSafe
-
1
class MriCacheBackend < NonConcurrentCacheBackend
-
# We can get away with a single global write lock (instead of a per-instance one) because of the GVL/green threads.
-
#
-
# The previous implementation used `Thread.critical` on 1.8 MRI to implement the 4 composed atomic operations (`put_if_absent`, `replace_pair`,
-
# `replace_if_exists`, `delete_pair`) this however doesn't work for `compute_if_absent` because on 1.8 the Mutex class is itself implemented
-
# via `Thread.critical` and a call to `Mutex#lock` does not restore the previous `Thread.critical` value (thus any synchronisation clears the
-
# `Thread.critical` flag and we loose control). This poses a problem as the provided block might use synchronisation on its own.
-
#
-
# NOTE: a neat idea of writing a c-ext to manually perform atomic put_if_absent, while relying on Ruby not releasing a GVL while calling
-
# a c-ext will not work because of the potentially Ruby implemented `#hash` and `#eql?` key methods.
-
1
WRITE_LOCK = Mutex.new
-
-
1
def []=(key, value)
-
160
WRITE_LOCK.synchronize { super }
-
end
-
-
1
def compute_if_absent(key)
-
48
if stored_value = _get(key) # fast non-blocking path for the most likely case
-
14
stored_value
-
else
-
68
WRITE_LOCK.synchronize { super }
-
end
-
end
-
-
1
def compute_if_present(key)
-
WRITE_LOCK.synchronize { super }
-
end
-
-
1
def compute(key)
-
WRITE_LOCK.synchronize { super }
-
end
-
-
1
def merge_pair(key, value)
-
WRITE_LOCK.synchronize { super }
-
end
-
-
1
def replace_pair(key, old_value, new_value)
-
WRITE_LOCK.synchronize { super }
-
end
-
-
1
def replace_if_exists(key, new_value)
-
WRITE_LOCK.synchronize { super }
-
end
-
-
1
def get_and_set(key, value)
-
WRITE_LOCK.synchronize { super }
-
end
-
-
1
def delete(key)
-
10
WRITE_LOCK.synchronize { super }
-
end
-
-
1
def delete_pair(key, value)
-
WRITE_LOCK.synchronize { super }
-
end
-
-
1
def clear
-
140
WRITE_LOCK.synchronize { super }
-
end
-
end
-
end
-
1
module ThreadSafe
-
1
class NonConcurrentCacheBackend
-
# WARNING: all public methods of the class must operate on the @backend directly without calling each other. This is important
-
# because of the SynchronizedCacheBackend which uses a non-reentrant mutex for perfomance reasons.
-
1
def initialize(options = nil)
-
93
@backend = {}
-
end
-
-
1
def [](key)
-
7973
@backend[key]
-
end
-
-
1
def []=(key, value)
-
80
@backend[key] = value
-
end
-
-
1
def compute_if_absent(key)
-
34
if NULL != (stored_value = @backend.fetch(key, NULL))
-
stored_value
-
else
-
34
@backend[key] = yield
-
end
-
end
-
-
1
def replace_pair(key, old_value, new_value)
-
if pair?(key, old_value)
-
@backend[key] = new_value
-
true
-
else
-
false
-
end
-
end
-
-
1
def replace_if_exists(key, new_value)
-
if NULL != (stored_value = @backend.fetch(key, NULL))
-
@backend[key] = new_value
-
stored_value
-
end
-
end
-
-
1
def compute_if_present(key)
-
if NULL != (stored_value = @backend.fetch(key, NULL))
-
store_computed_value(key, yield(stored_value))
-
end
-
end
-
-
1
def compute(key)
-
store_computed_value(key, yield(@backend[key]))
-
end
-
-
1
def merge_pair(key, value)
-
if NULL == (stored_value = @backend.fetch(key, NULL))
-
@backend[key] = value
-
else
-
store_computed_value(key, yield(stored_value))
-
end
-
end
-
-
1
def get_and_set(key, value)
-
stored_value = @backend[key]
-
@backend[key] = value
-
stored_value
-
end
-
-
1
def key?(key)
-
@backend.key?(key)
-
end
-
-
1
def value?(value)
-
@backend.value?(value)
-
end
-
-
1
def delete(key)
-
5
@backend.delete(key)
-
end
-
-
1
def delete_pair(key, value)
-
if pair?(key, value)
-
@backend.delete(key)
-
true
-
else
-
false
-
end
-
end
-
-
1
def clear
-
70
@backend.clear
-
70
self
-
end
-
-
1
def each_pair
-
17
dupped_backend.each_pair do |k, v|
-
37
yield k, v
-
end
-
17
self
-
end
-
-
1
def size
-
@backend.size
-
end
-
-
1
def get_or_default(key, default_value)
-
29
@backend.fetch(key, default_value)
-
end
-
-
1
alias_method :_get, :[]
-
1
alias_method :_set, :[]=
-
1
private :_get, :_set
-
1
private
-
1
def initialize_copy(other)
-
super
-
@backend = {}
-
self
-
end
-
-
1
def dupped_backend
-
17
@backend.dup
-
end
-
-
1
def pair?(key, expected_value)
-
NULL != (stored_value = @backend.fetch(key, NULL)) && expected_value.equal?(stored_value)
-
end
-
-
1
def store_computed_value(key, new_value)
-
if new_value.nil?
-
@backend.delete(key)
-
nil
-
else
-
@backend[key] = new_value
-
end
-
end
-
end
-
end
-
1
require 'delegate'
-
1
require 'monitor'
-
-
# This class provides a trivial way to synchronize all calls to a given object
-
# by wrapping it with a `Delegator` that performs `Monitor#enter/exit` calls
-
# around the delegated `#send`. Example:
-
#
-
# array = [] # not thread-safe on many impls
-
# array = SynchronizedDelegator.new([]) # thread-safe
-
#
-
# A simple `Monitor` provides a very coarse-grained way to synchronize a given
-
# object, in that it will cause synchronization for methods that have no
-
# need for it, but this is a trivial way to get thread-safety where none may
-
# exist currently on some implementations.
-
#
-
# This class is currently being considered for inclusion into stdlib, via
-
# https://bugs.ruby-lang.org/issues/8556
-
class SynchronizedDelegator < SimpleDelegator
-
1
def setup
-
@old_abort = Thread.abort_on_exception
-
Thread.abort_on_exception = true
-
end
-
-
1
def teardown
-
Thread.abort_on_exception = @old_abort
-
end
-
-
1
def initialize(obj)
-
__setobj__(obj)
-
@monitor = Monitor.new
-
end
-
-
1
def method_missing(method, *args, &block)
-
monitor = @monitor
-
begin
-
monitor.enter
-
super
-
ensure
-
monitor.exit
-
end
-
end
-
-
# Work-around for 1.8 std-lib not passing block around to delegate.
-
# @private
-
def method_missing(method, *args, &block)
-
monitor = @monitor
-
begin
-
monitor.enter
-
target = self.__getobj__
-
if target.respond_to?(method)
-
target.__send__(method, *args, &block)
-
else
-
super(method, *args, &block)
-
end
-
ensure
-
monitor.exit
-
end
-
1
end if RUBY_VERSION[0, 3] == '1.8'
-
-
1
end unless defined?(SynchronizedDelegator)
-
1
module ThreadSafe
-
1
VERSION = "0.3.4"
-
end
-
-
# NOTE: <= 0.2.0 used Threadsafe::VERSION
-
# @private
-
1
module Threadsafe
-
-
# @private
-
1
def self.const_missing(name)
-
name = name.to_sym
-
if ThreadSafe.const_defined?(name)
-
warn "[DEPRECATION] `Threadsafe::#{name}' is deprecated, use `ThreadSafe::#{name}' instead."
-
ThreadSafe.const_get(name)
-
else
-
warn "[DEPRECATION] the `Threadsafe' module is deprecated, please use `ThreadSafe` instead."
-
super
-
end
-
end
-
-
end
-
1
module Tilt
-
1
VERSION = '1.4.1'
-
-
1
@preferred_mappings = Hash.new
-
32
@template_mappings = Hash.new { |h, k| h[k] = [] }
-
-
# Hash of template path pattern => template implementation class mappings.
-
1
def self.mappings
-
52
@template_mappings
-
end
-
-
1
def self.normalize(ext)
-
52
ext.to_s.downcase.sub(/^\./, '')
-
end
-
-
# Register a template implementation by file extension.
-
1
def self.register(template_class, *extensions)
-
29
if template_class.respond_to?(:to_str)
-
# Support register(ext, template_class) too
-
extensions, template_class = [template_class], extensions[0]
-
end
-
-
29
extensions.each do |ext|
-
52
ext = normalize(ext)
-
52
mappings[ext].unshift(template_class).uniq!
-
end
-
end
-
-
# Makes a template class preferred for the given file extensions. If you
-
# don't provide any extensions, it will be preferred for all its already
-
# registered extensions:
-
#
-
# # Prefer RDiscount for its registered file extensions:
-
# Tilt.prefer(Tilt::RDiscountTemplate)
-
#
-
# # Prefer RDiscount only for the .md extensions:
-
# Tilt.prefer(Tilt::RDiscountTemplate, '.md')
-
1
def self.prefer(template_class, *extensions)
-
if extensions.empty?
-
mappings.each do |ext, klasses|
-
@preferred_mappings[ext] = template_class if klasses.include? template_class
-
end
-
else
-
extensions.each do |ext|
-
ext = normalize(ext)
-
register(template_class, ext)
-
@preferred_mappings[ext] = template_class
-
end
-
end
-
end
-
-
# Returns true when a template exists on an exact match of the provided file extension
-
1
def self.registered?(ext)
-
mappings.key?(ext.downcase) && !mappings[ext.downcase].empty?
-
end
-
-
# Create a new template for the given file using the file's extension
-
# to determine the the template mapping.
-
1
def self.new(file, line=nil, options={}, &block)
-
if template_class = self[file]
-
template_class.new(file, line, options, &block)
-
else
-
fail "No template engine registered for #{File.basename(file)}"
-
end
-
end
-
-
# Lookup a template class for the given filename or file
-
# extension. Return nil when no implementation is found.
-
1
def self.[](file)
-
pattern = file.to_s.downcase
-
until pattern.empty? || registered?(pattern)
-
pattern = File.basename(pattern)
-
pattern.sub!(/^[^.]*\.?/, '')
-
end
-
-
# Try to find a preferred engine.
-
preferred_klass = @preferred_mappings[pattern]
-
return preferred_klass if preferred_klass
-
-
# Fall back to the general list of mappings.
-
klasses = @template_mappings[pattern]
-
-
# Try to find an engine which is already loaded.
-
template = klasses.detect do |klass|
-
if klass.respond_to?(:engine_initialized?)
-
klass.engine_initialized?
-
end
-
end
-
-
return template if template
-
-
# Try each of the classes until one succeeds. If all of them fails,
-
# we'll raise the error of the first class.
-
first_failure = nil
-
-
klasses.each do |klass|
-
begin
-
klass.new { '' }
-
rescue Exception => ex
-
first_failure ||= ex
-
next
-
else
-
return klass
-
end
-
end
-
-
raise first_failure if first_failure
-
end
-
-
# Deprecated module.
-
1
module CompileSite
-
end
-
-
# Extremely simple template cache implementation. Calling applications
-
# create a Tilt::Cache instance and use #fetch with any set of hashable
-
# arguments (such as those to Tilt.new):
-
# cache = Tilt::Cache.new
-
# cache.fetch(path, line, options) { Tilt.new(path, line, options) }
-
#
-
# Subsequent invocations return the already loaded template object.
-
1
class Cache
-
1
def initialize
-
@cache = {}
-
end
-
-
1
def fetch(*key)
-
@cache[key] ||= yield
-
end
-
-
1
def clear
-
@cache = {}
-
end
-
end
-
-
-
# Template Implementations ================================================
-
-
1
require 'tilt/string'
-
1
register StringTemplate, 'str'
-
-
1
require 'tilt/erb'
-
1
register ERBTemplate, 'erb', 'rhtml'
-
1
register ErubisTemplate, 'erb', 'rhtml', 'erubis'
-
-
1
require 'tilt/etanni'
-
1
register EtanniTemplate, 'etn', 'etanni'
-
-
1
require 'tilt/haml'
-
1
register HamlTemplate, 'haml'
-
-
1
require 'tilt/css'
-
1
register SassTemplate, 'sass'
-
1
register ScssTemplate, 'scss'
-
1
register LessTemplate, 'less'
-
-
1
require 'tilt/csv'
-
1
register CSVTemplate, 'rcsv'
-
-
1
require 'tilt/coffee'
-
1
register CoffeeScriptTemplate, 'coffee'
-
-
1
require 'tilt/nokogiri'
-
1
register NokogiriTemplate, 'nokogiri'
-
-
1
require 'tilt/builder'
-
1
register BuilderTemplate, 'builder'
-
-
1
require 'tilt/markaby'
-
1
register MarkabyTemplate, 'mab'
-
-
1
require 'tilt/liquid'
-
1
register LiquidTemplate, 'liquid'
-
-
1
require 'tilt/radius'
-
1
register RadiusTemplate, 'radius'
-
-
1
require 'tilt/markdown'
-
1
register MarukuTemplate, 'markdown', 'mkd', 'md'
-
1
register KramdownTemplate, 'markdown', 'mkd', 'md'
-
1
register BlueClothTemplate, 'markdown', 'mkd', 'md'
-
1
register RDiscountTemplate, 'markdown', 'mkd', 'md'
-
1
register RedcarpetTemplate::Redcarpet1, 'markdown', 'mkd', 'md'
-
1
register RedcarpetTemplate::Redcarpet2, 'markdown', 'mkd', 'md'
-
1
register RedcarpetTemplate, 'markdown', 'mkd', 'md'
-
-
1
require 'tilt/textile'
-
1
register RedClothTemplate, 'textile'
-
-
1
require 'tilt/rdoc'
-
1
register RDocTemplate, 'rdoc'
-
-
1
require 'tilt/wiki'
-
1
register CreoleTemplate, 'wiki', 'creole'
-
1
register WikiClothTemplate, 'wiki', 'mediawiki', 'mw'
-
-
1
require 'tilt/yajl'
-
1
register YajlTemplate, 'yajl'
-
-
1
require 'tilt/asciidoc'
-
1
register AsciidoctorTemplate, 'ad', 'adoc', 'asciidoc'
-
-
1
require 'tilt/plain'
-
1
register PlainTemplate, 'html'
-
end
-
1
require 'tilt/template'
-
-
# AsciiDoc see: http://asciidoc.org/
-
1
module Tilt
-
# Asciidoctor implementation for AsciiDoc see:
-
# http://asciidoctor.github.com/
-
#
-
# Asciidoctor is an open source, pure-Ruby processor for
-
# converting AsciiDoc documents or strings into HTML 5,
-
# DocBook 4.5 and other formats.
-
1
class AsciidoctorTemplate < Template
-
1
self.default_mime_type = 'text/html'
-
-
1
def self.engine_initialized?
-
defined? ::Asciidoctor::Document
-
end
-
-
1
def initialize_engine
-
require_template_library 'asciidoctor'
-
end
-
-
1
def prepare
-
options[:header_footer] = false if options[:header_footer].nil?
-
end
-
-
1
def evaluate(scope, locals, &block)
-
@output ||= Asciidoctor.render(data, options, &block)
-
end
-
-
1
def allows_script?
-
false
-
end
-
end
-
end
-
1
require 'tilt/template'
-
-
1
module Tilt
-
# Builder template implementation. See:
-
# http://builder.rubyforge.org/
-
1
class BuilderTemplate < Template
-
1
self.default_mime_type = 'text/xml'
-
-
1
def self.engine_initialized?
-
defined? ::Builder
-
end
-
-
1
def initialize_engine
-
require_template_library 'builder'
-
end
-
-
1
def prepare; end
-
-
1
def evaluate(scope, locals, &block)
-
return super(scope, locals, &block) if data.respond_to?(:to_str)
-
xml = ::Builder::XmlMarkup.new(:indent => 2)
-
data.call(xml)
-
xml.target!
-
end
-
-
1
def precompiled_preamble(locals)
-
return super if locals.include? :xml
-
"xml = ::Builder::XmlMarkup.new(:indent => 2)\n#{super}"
-
end
-
-
1
def precompiled_postamble(locals)
-
"xml.target!"
-
end
-
-
1
def precompiled_template(locals)
-
data.to_str
-
end
-
end
-
end
-
-
1
require 'tilt/template'
-
-
1
module Tilt
-
# CoffeeScript template implementation. See:
-
# http://coffeescript.org/
-
#
-
# CoffeeScript templates do not support object scopes, locals, or yield.
-
1
class CoffeeScriptTemplate < Template
-
1
self.default_mime_type = 'application/javascript'
-
-
1
@@default_bare = false
-
-
1
def self.default_bare
-
@@default_bare
-
end
-
-
1
def self.default_bare=(value)
-
@@default_bare = value
-
end
-
-
# DEPRECATED
-
1
def self.default_no_wrap
-
@@default_bare
-
end
-
-
# DEPRECATED
-
1
def self.default_no_wrap=(value)
-
@@default_bare = value
-
end
-
-
1
def self.engine_initialized?
-
defined? ::CoffeeScript
-
end
-
-
1
def initialize_engine
-
require_template_library 'coffee_script'
-
end
-
-
1
def prepare
-
if !options.key?(:bare) and !options.key?(:no_wrap)
-
options[:bare] = self.class.default_bare
-
end
-
end
-
-
1
def evaluate(scope, locals, &block)
-
@output ||= CoffeeScript.compile(data, options)
-
end
-
-
1
def allows_script?
-
false
-
end
-
end
-
end
-
-
1
require 'tilt/template'
-
-
1
module Tilt
-
# Sass template implementation. See:
-
# http://haml.hamptoncatlin.com/
-
#
-
# Sass templates do not support object scopes, locals, or yield.
-
1
class SassTemplate < Template
-
1
self.default_mime_type = 'text/css'
-
-
1
def self.engine_initialized?
-
defined? ::Sass::Engine
-
end
-
-
1
def initialize_engine
-
require_template_library 'sass'
-
end
-
-
1
def prepare
-
@engine = ::Sass::Engine.new(data, sass_options)
-
end
-
-
1
def evaluate(scope, locals, &block)
-
@output ||= @engine.render
-
end
-
-
1
def allows_script?
-
false
-
end
-
-
1
private
-
1
def sass_options
-
options.merge(:filename => eval_file, :line => line, :syntax => :sass)
-
end
-
end
-
-
# Sass's new .scss type template implementation.
-
1
class ScssTemplate < SassTemplate
-
1
self.default_mime_type = 'text/css'
-
-
1
private
-
1
def sass_options
-
options.merge(:filename => eval_file, :line => line, :syntax => :scss)
-
end
-
end
-
-
# Lessscss template implementation. See:
-
# http://lesscss.org/
-
#
-
# Less templates do not support object scopes, locals, or yield.
-
1
class LessTemplate < Template
-
1
self.default_mime_type = 'text/css'
-
-
1
def self.engine_initialized?
-
defined? ::Less
-
end
-
-
1
def initialize_engine
-
require_template_library 'less'
-
end
-
-
1
def prepare
-
if ::Less.const_defined? :Engine
-
@engine = ::Less::Engine.new(data)
-
else
-
parser = ::Less::Parser.new(options.merge :filename => eval_file, :line => line)
-
@engine = parser.parse(data)
-
end
-
end
-
-
1
def evaluate(scope, locals, &block)
-
@output ||= @engine.to_css(options)
-
end
-
-
1
def allows_script?
-
false
-
end
-
end
-
end
-
-
1
require 'tilt/template'
-
-
1
module Tilt
-
-
# CSV Template implementation. See:
-
# http://ruby-doc.org/stdlib/libdoc/csv/rdoc/CSV.html
-
#
-
# == Example
-
#
-
# # Example of csv template
-
# tpl = <<-EOS
-
# # header
-
# csv << ['NAME', 'ID']
-
#
-
# # data rows
-
# @people.each do |person|
-
# csv << [person[:name], person[:id]]
-
# end
-
# EOS
-
#
-
# @people = [
-
# {:name => "Joshua Peek", :id => 1},
-
# {:name => "Ryan Tomayko", :id => 2},
-
# {:name => "Simone Carletti", :id => 3}
-
# ]
-
#
-
# template = Tilt::CSVTemplate.new { tpl }
-
# template.render(self)
-
#
-
1
class CSVTemplate < Template
-
1
self.default_mime_type = 'text/csv'
-
-
1
def self.engine_initialized?
-
engine
-
end
-
-
1
def self.engine
-
if RUBY_VERSION >= '1.9.0' && defined? ::CSV
-
::CSV
-
elsif defined? ::FasterCSV
-
::FasterCSV
-
end
-
end
-
-
1
def initialize_engine
-
if RUBY_VERSION >= '1.9.0'
-
require_template_library 'csv'
-
else
-
require_template_library 'fastercsv'
-
end
-
end
-
-
1
def prepare
-
@code =<<-RUBY
-
#{self.class.engine}.generate do |csv|
-
#{data}
-
end
-
RUBY
-
end
-
-
1
def precompiled_template(locals)
-
@code
-
end
-
-
1
def precompiled(locals)
-
source, offset = super
-
[source, offset + 1]
-
end
-
-
end
-
end
-
1
require 'tilt/template'
-
-
1
module Tilt
-
# ERB template implementation. See:
-
# http://www.ruby-doc.org/stdlib/libdoc/erb/rdoc/classes/ERB.html
-
1
class ERBTemplate < Template
-
1
@@default_output_variable = '_erbout'
-
-
1
def self.default_output_variable
-
@@default_output_variable
-
end
-
-
1
def self.default_output_variable=(name)
-
@@default_output_variable = name
-
end
-
-
1
def self.engine_initialized?
-
defined? ::ERB
-
end
-
-
1
def initialize_engine
-
require_template_library 'erb'
-
end
-
-
1
def prepare
-
@outvar = options[:outvar] || self.class.default_output_variable
-
options[:trim] = '<>' if !(options[:trim] == false) && (options[:trim].nil? || options[:trim] == true)
-
@engine = ::ERB.new(data, options[:safe], options[:trim], @outvar)
-
end
-
-
1
def precompiled_template(locals)
-
source = @engine.src
-
source
-
end
-
-
1
def precompiled_preamble(locals)
-
<<-RUBY
-
begin
-
__original_outvar = #{@outvar} if defined?(#{@outvar})
-
#{super}
-
RUBY
-
end
-
-
1
def precompiled_postamble(locals)
-
<<-RUBY
-
#{super}
-
ensure
-
#{@outvar} = __original_outvar
-
end
-
RUBY
-
end
-
-
# ERB generates a line to specify the character coding of the generated
-
# source in 1.9. Account for this in the line offset.
-
1
if RUBY_VERSION >= '1.9.0'
-
1
def precompiled(locals)
-
source, offset = super
-
[source, offset + 1]
-
end
-
end
-
end
-
-
# Erubis template implementation. See:
-
# http://www.kuwata-lab.com/erubis/
-
#
-
# ErubisTemplate supports the following additional options, which are not
-
# passed down to the Erubis engine:
-
#
-
# :engine_class allows you to specify a custom engine class to use
-
# instead of the default (which is ::Erubis::Eruby).
-
#
-
# :escape_html when true, ::Erubis::EscapedEruby will be used as
-
# the engine class instead of the default. All content
-
# within <%= %> blocks will be automatically html escaped.
-
1
class ErubisTemplate < ERBTemplate
-
1
def self.engine_initialized?
-
defined? ::Erubis::Eruby
-
end
-
-
1
def initialize_engine
-
require_template_library 'erubis'
-
end
-
-
1
def prepare
-
@outvar = options.delete(:outvar) || self.class.default_output_variable
-
@options.merge!(:preamble => false, :postamble => false, :bufvar => @outvar)
-
engine_class = options.delete(:engine_class)
-
engine_class = ::Erubis::EscapedEruby if options.delete(:escape_html)
-
@engine = (engine_class || ::Erubis::Eruby).new(data, options)
-
end
-
-
1
def precompiled_preamble(locals)
-
[super, "#{@outvar} = _buf = ''"].join("\n")
-
end
-
-
1
def precompiled_postamble(locals)
-
[@outvar, super].join("\n")
-
end
-
-
# Erubis doesn't have ERB's line-off-by-one under 1.9 problem.
-
# Override and adjust back.
-
1
if RUBY_VERSION >= '1.9.0'
-
1
def precompiled(locals)
-
source, offset = super
-
[source, offset - 1]
-
end
-
end
-
end
-
end
-
-
1
require 'tilt/template'
-
-
1
module Tilt
-
1
class EtanniTemplate < Template
-
1
def prepare
-
separator = data.hash.abs
-
chomp = "<<#{separator}.chomp!"
-
start = "\n_out_ << #{chomp}\n"
-
stop = "\n#{separator}\n"
-
replacement = "#{stop}\\1#{start}"
-
-
temp = data.strip
-
temp.gsub!(/<\?r\s+(.*?)\s+\?>/m, replacement)
-
-
@code = "_out_ = [<<#{separator}.chomp!]\n#{temp}#{stop}_out_.join"
-
end
-
-
1
def precompiled_template(locals)
-
@code
-
end
-
-
1
def precompiled(locals)
-
source, offset = super
-
[source, offset + 1]
-
end
-
end
-
end
-
1
require 'tilt/template'
-
-
1
module Tilt
-
# Haml template implementation. See:
-
# http://haml.hamptoncatlin.com/
-
1
class HamlTemplate < Template
-
1
self.default_mime_type = 'text/html'
-
-
1
def self.engine_initialized?
-
defined? ::Haml::Engine
-
end
-
-
1
def initialize_engine
-
require_template_library 'haml'
-
end
-
-
1
def prepare
-
options = @options.merge(:filename => eval_file, :line => line)
-
@engine = ::Haml::Engine.new(data, options)
-
end
-
-
1
def evaluate(scope, locals, &block)
-
if @engine.respond_to?(:precompiled_method_return_value, true)
-
super
-
else
-
@engine.render(scope, locals, &block)
-
end
-
end
-
-
# Precompiled Haml source. Taken from the precompiled_with_ambles
-
# method in Haml::Precompiler:
-
# http://github.com/nex3/haml/blob/master/lib/haml/precompiler.rb#L111-126
-
1
def precompiled_template(locals)
-
@engine.precompiled
-
end
-
-
1
def precompiled_preamble(locals)
-
local_assigns = super
-
@engine.instance_eval do
-
<<-RUBY
-
begin
-
extend Haml::Helpers
-
_hamlout = @haml_buffer = Haml::Buffer.new(@haml_buffer, #{options_for_buffer.inspect})
-
_erbout = _hamlout.buffer
-
__in_erb_template = true
-
_haml_locals = locals
-
#{local_assigns}
-
RUBY
-
end
-
end
-
-
1
def precompiled_postamble(locals)
-
@engine.instance_eval do
-
<<-RUBY
-
#{precompiled_method_return_value}
-
ensure
-
@haml_buffer = @haml_buffer.upper
-
end
-
RUBY
-
end
-
end
-
end
-
end
-
-
1
require 'tilt/template'
-
-
1
module Tilt
-
# Liquid template implementation. See:
-
# http://liquid.rubyforge.org/
-
#
-
# Liquid is designed to be a *safe* template system and threfore
-
# does not provide direct access to execuatable scopes. In order to
-
# support a +scope+, the +scope+ must be able to represent itself
-
# as a hash by responding to #to_h. If the +scope+ does not respond
-
# to #to_h it will be ignored.
-
#
-
# LiquidTemplate does not support yield blocks.
-
#
-
# It's suggested that your program require 'liquid' at load
-
# time when using this template engine.
-
1
class LiquidTemplate < Template
-
1
def self.engine_initialized?
-
defined? ::Liquid::Template
-
end
-
-
1
def initialize_engine
-
require_template_library 'liquid'
-
end
-
-
1
def prepare
-
@engine = ::Liquid::Template.parse(data)
-
end
-
-
1
def evaluate(scope, locals, &block)
-
locals = locals.inject({}){ |h,(k,v)| h[k.to_s] = v ; h }
-
if scope.respond_to?(:to_h)
-
scope = scope.to_h.inject({}){ |h,(k,v)| h[k.to_s] = v ; h }
-
locals = scope.merge(locals)
-
end
-
locals['yield'] = block.nil? ? '' : yield
-
locals['content'] = locals['yield']
-
@engine.render(locals)
-
end
-
-
1
def allows_script?
-
false
-
end
-
end
-
end
-
1
require 'tilt/template'
-
-
1
module Tilt
-
# Markaby
-
# http://github.com/markaby/markaby
-
1
class MarkabyTemplate < Template
-
1
def self.builder_class
-
@builder_class ||= Class.new(Markaby::Builder) do
-
def __capture_markaby_tilt__(&block)
-
__run_markaby_tilt__ do
-
text capture(&block)
-
end
-
end
-
end
-
end
-
-
1
def self.engine_initialized?
-
defined? ::Markaby
-
end
-
-
1
def initialize_engine
-
require_template_library 'markaby'
-
end
-
-
1
def prepare
-
end
-
-
1
def evaluate(scope, locals, &block)
-
builder = self.class.builder_class.new({}, scope)
-
builder.locals = locals
-
-
if data.kind_of? Proc
-
(class << builder; self end).send(:define_method, :__run_markaby_tilt__, &data)
-
else
-
builder.instance_eval <<-CODE, __FILE__, __LINE__
-
def __run_markaby_tilt__
-
#{data}
-
end
-
CODE
-
end
-
-
if block
-
builder.__capture_markaby_tilt__(&block)
-
else
-
builder.__run_markaby_tilt__
-
end
-
-
builder.to_s
-
end
-
end
-
end
-
-
1
require 'tilt/template'
-
-
1
module Tilt
-
# Discount Markdown implementation. See:
-
# http://github.com/rtomayko/rdiscount
-
#
-
# RDiscount is a simple text filter. It does not support +scope+ or
-
# +locals+. The +:smart+ and +:filter_html+ options may be set true
-
# to enable those flags on the underlying RDiscount object.
-
1
class RDiscountTemplate < Template
-
1
self.default_mime_type = 'text/html'
-
-
1
ALIAS = {
-
:escape_html => :filter_html,
-
:smartypants => :smart
-
}
-
-
1
FLAGS = [:smart, :filter_html, :smartypants, :escape_html]
-
-
1
def flags
-
FLAGS.select { |flag| options[flag] }.map { |flag| ALIAS[flag] || flag }
-
end
-
-
1
def self.engine_initialized?
-
defined? ::RDiscount
-
end
-
-
1
def initialize_engine
-
require_template_library 'rdiscount'
-
end
-
-
1
def prepare
-
@engine = RDiscount.new(data, *flags)
-
@output = nil
-
end
-
-
1
def evaluate(scope, locals, &block)
-
@output ||= @engine.to_html
-
end
-
-
1
def allows_script?
-
false
-
end
-
end
-
-
# Upskirt Markdown implementation. See:
-
# https://github.com/tanoku/redcarpet
-
#
-
# Supports both Redcarpet 1.x and 2.x
-
1
class RedcarpetTemplate < Template
-
1
def self.engine_initialized?
-
defined? ::Redcarpet
-
end
-
-
1
def initialize_engine
-
require_template_library 'redcarpet'
-
end
-
-
1
def prepare
-
klass = [Redcarpet2, Redcarpet1].detect { |e| e.engine_initialized? }
-
@engine = klass.new(file, line, options) { data }
-
end
-
-
1
def evaluate(scope, locals, &block)
-
@engine.evaluate(scope, locals, &block)
-
end
-
-
1
def allows_script?
-
false
-
end
-
-
# Compatibility mode for Redcarpet 1.x
-
1
class Redcarpet1 < RDiscountTemplate
-
1
self.default_mime_type = 'text/html'
-
-
1
def self.engine_initialized?
-
defined? ::RedcarpetCompat
-
end
-
-
1
def prepare
-
@engine = RedcarpetCompat.new(data, *flags)
-
@output = nil
-
end
-
end
-
-
# Future proof mode for Redcarpet 2.x (not yet released)
-
1
class Redcarpet2 < Template
-
1
self.default_mime_type = 'text/html'
-
-
1
def self.engine_initialized?
-
defined? ::Redcarpet::Render and defined? ::Redcarpet::Markdown
-
end
-
-
1
def generate_renderer
-
renderer = options.delete(:renderer) || ::Redcarpet::Render::HTML
-
return renderer unless options.delete(:smartypants)
-
return renderer if renderer.is_a?(Class) && renderer <= ::Redcarpet::Render::SmartyPants
-
-
if renderer == ::Redcarpet::Render::XHTML
-
::Redcarpet::Render::SmartyHTML.new(:xhtml => true)
-
elsif renderer == ::Redcarpet::Render::HTML
-
::Redcarpet::Render::SmartyHTML
-
elsif renderer.is_a? Class
-
Class.new(renderer) { include ::Redcarpet::Render::SmartyPants }
-
else
-
renderer.extend ::Redcarpet::Render::SmartyPants
-
end
-
end
-
-
1
def prepare
-
# try to support the same aliases
-
RDiscountTemplate::ALIAS.each do |opt, aka|
-
next if options.key? opt or not options.key? aka
-
options[opt] = options.delete(aka)
-
end
-
-
# only raise an exception if someone is trying to enable :escape_html
-
options.delete(:escape_html) unless options[:escape_html]
-
-
@engine = ::Redcarpet::Markdown.new(generate_renderer, options)
-
@output = nil
-
end
-
-
1
def evaluate(scope, locals, &block)
-
@output ||= @engine.render(data)
-
end
-
-
1
def allows_script?
-
false
-
end
-
end
-
end
-
-
# BlueCloth Markdown implementation. See:
-
# http://deveiate.org/projects/BlueCloth/
-
1
class BlueClothTemplate < Template
-
1
self.default_mime_type = 'text/html'
-
-
1
def self.engine_initialized?
-
defined? ::BlueCloth
-
end
-
-
1
def initialize_engine
-
require_template_library 'bluecloth'
-
end
-
-
1
def prepare
-
@engine = BlueCloth.new(data, options)
-
@output = nil
-
end
-
-
1
def evaluate(scope, locals, &block)
-
@output ||= @engine.to_html
-
end
-
-
1
def allows_script?
-
false
-
end
-
end
-
-
# Maruku markdown implementation. See:
-
# http://maruku.rubyforge.org/
-
1
class MarukuTemplate < Template
-
1
def self.engine_initialized?
-
defined? ::Maruku
-
end
-
-
1
def initialize_engine
-
require_template_library 'maruku'
-
end
-
-
1
def prepare
-
@engine = Maruku.new(data, options)
-
@output = nil
-
end
-
-
1
def evaluate(scope, locals, &block)
-
@output ||= @engine.to_html
-
end
-
-
1
def allows_script?
-
false
-
end
-
end
-
-
# Kramdown Markdown implementation. See:
-
# http://kramdown.rubyforge.org/
-
1
class KramdownTemplate < Template
-
1
DUMB_QUOTES = [39, 39, 34, 34]
-
-
1
def self.engine_initialized?
-
defined? ::Kramdown
-
end
-
-
1
def initialize_engine
-
require_template_library 'kramdown'
-
end
-
-
1
def prepare
-
options[:smart_quotes] = DUMB_QUOTES unless options[:smartypants]
-
@engine = Kramdown::Document.new(data, options)
-
@output = nil
-
end
-
-
1
def evaluate(scope, locals, &block)
-
@output ||= @engine.to_html
-
end
-
-
1
def allows_script?
-
false
-
end
-
end
-
end
-
-
1
require 'tilt/template'
-
-
1
module Tilt
-
# Nokogiri template implementation. See:
-
# http://nokogiri.org/
-
1
class NokogiriTemplate < Template
-
1
DOCUMENT_HEADER = /^<\?xml version=\"1\.0\"\?>\n?/
-
1
self.default_mime_type = 'text/xml'
-
-
1
def self.engine_initialized?
-
defined? ::Nokogiri
-
end
-
-
1
def initialize_engine
-
require_template_library 'nokogiri'
-
end
-
-
1
def prepare; end
-
-
1
def evaluate(scope, locals)
-
if data.respond_to?(:to_str)
-
wrapper = proc { yield.sub(DOCUMENT_HEADER, "") } if block_given?
-
super(scope, locals, &wrapper)
-
else
-
::Nokogiri::XML::Builder.new.tap(&data).to_xml
-
end
-
end
-
-
1
def precompiled_preamble(locals)
-
return super if locals.include? :xml
-
"xml = ::Nokogiri::XML::Builder.new { |xml| }\n#{super}"
-
end
-
-
1
def precompiled_postamble(locals)
-
"xml.to_xml"
-
end
-
-
1
def precompiled_template(locals)
-
data.to_str
-
end
-
end
-
end
-
-
1
require 'tilt/template'
-
-
-
1
module Tilt
-
# Raw text (no template functionality).
-
1
class PlainTemplate < Template
-
1
self.default_mime_type = 'text/html'
-
-
1
def self.engine_initialized?
-
true
-
end
-
-
1
def prepare
-
end
-
-
1
def evaluate(scope, locals, &block)
-
@output ||= data
-
end
-
end
-
end
-
1
require 'tilt/template'
-
-
1
module Tilt
-
# Radius Template
-
# http://github.com/jlong/radius/
-
1
class RadiusTemplate < Template
-
1
def self.engine_initialized?
-
defined? ::Radius
-
end
-
-
1
def self.context_class
-
@context_class ||= Class.new(Radius::Context) do
-
attr_accessor :tilt_scope
-
-
def tag_missing(name, attributes)
-
tilt_scope.__send__(name)
-
end
-
-
def dup
-
i = super
-
i.tilt_scope = tilt_scope
-
i
-
end
-
end
-
end
-
-
1
def initialize_engine
-
require_template_library 'radius'
-
end
-
-
1
def prepare
-
end
-
-
1
def evaluate(scope, locals, &block)
-
context = self.class.context_class.new
-
context.tilt_scope = scope
-
context.define_tag("yield") do
-
block.call
-
end
-
locals.each do |tag, value|
-
context.define_tag(tag) do
-
value
-
end
-
end
-
-
options = {:tag_prefix => 'r'}.merge(@options)
-
parser = Radius::Parser.new(context, options)
-
parser.parse(data)
-
end
-
-
1
def allows_script?
-
false
-
end
-
end
-
end
-
1
require 'tilt/template'
-
-
1
module Tilt
-
# RDoc template. See:
-
# http://rdoc.rubyforge.org/
-
#
-
# It's suggested that your program `require 'rdoc/markup'` and
-
# `require 'rdoc/markup/to_html'` at load time when using this template
-
# engine in a threaded environment.
-
1
class RDocTemplate < Template
-
1
self.default_mime_type = 'text/html'
-
-
1
def self.engine_initialized?
-
defined? ::RDoc::Markup::ToHtml
-
end
-
-
1
def initialize_engine
-
require_template_library 'rdoc'
-
require_template_library 'rdoc/markup'
-
require_template_library 'rdoc/markup/to_html'
-
end
-
-
1
def markup
-
begin
-
# RDoc 4.0
-
require 'rdoc/options'
-
RDoc::Markup::ToHtml.new(RDoc::Options.new, nil)
-
rescue ArgumentError
-
# RDoc < 4.0
-
RDoc::Markup::ToHtml.new
-
end
-
end
-
-
1
def prepare
-
@engine = markup.convert(data)
-
@output = nil
-
end
-
-
1
def evaluate(scope, locals, &block)
-
@output ||= @engine.to_s
-
end
-
-
1
def allows_script?
-
false
-
end
-
end
-
end
-
1
require 'tilt/template'
-
-
1
module Tilt
-
# The template source is evaluated as a Ruby string. The #{} interpolation
-
# syntax can be used to generated dynamic output.
-
1
class StringTemplate < Template
-
1
def prepare
-
hash = "TILT#{data.hash.abs}"
-
@code = "<<#{hash}.chomp\n#{data}\n#{hash}"
-
end
-
-
1
def precompiled_template(locals)
-
@code
-
end
-
-
1
def precompiled(locals)
-
source, offset = super
-
[source, offset + 1]
-
end
-
end
-
end
-
1
module Tilt
-
1
TOPOBJECT = Object.superclass || Object
-
-
# Base class for template implementations. Subclasses must implement
-
# the #prepare method and one of the #evaluate or #precompiled_template
-
# methods.
-
1
class Template
-
# Template source; loaded from a file or given directly.
-
1
attr_reader :data
-
-
# The name of the file where the template data was loaded from.
-
1
attr_reader :file
-
-
# The line number in #file where template data was loaded from.
-
1
attr_reader :line
-
-
# A Hash of template engine specific options. This is passed directly
-
# to the underlying engine and is not used by the generic template
-
# interface.
-
1
attr_reader :options
-
-
# Used to determine if this class's initialize_engine method has
-
# been called yet.
-
1
@engine_initialized = false
-
1
class << self
-
1
attr_accessor :engine_initialized
-
1
alias engine_initialized? engine_initialized
-
-
1
attr_accessor :default_mime_type
-
end
-
-
# Create a new template with the file, line, and options specified. By
-
# default, template data is read from the file. When a block is given,
-
# it should read template data and return as a String. When file is nil,
-
# a block is required.
-
#
-
# All arguments are optional.
-
1
def initialize(file=nil, line=1, options={}, &block)
-
@file, @line, @options = nil, 1, {}
-
-
[options, line, file].compact.each do |arg|
-
case
-
when arg.respond_to?(:to_str) ; @file = arg.to_str
-
when arg.respond_to?(:to_int) ; @line = arg.to_int
-
when arg.respond_to?(:to_hash) ; @options = arg.to_hash.dup
-
when arg.respond_to?(:path) ; @file = arg.path
-
else raise TypeError
-
end
-
end
-
-
raise ArgumentError, "file or block required" if (@file || block).nil?
-
-
# call the initialize_engine method if this is the very first time
-
# an instance of this class has been created.
-
if !self.class.engine_initialized?
-
initialize_engine
-
self.class.engine_initialized = true
-
end
-
-
# used to hold compiled template methods
-
@compiled_method = {}
-
-
# used on 1.9 to set the encoding if it is not set elsewhere (like a magic comment)
-
# currently only used if template compiles to ruby
-
@default_encoding = @options.delete :default_encoding
-
-
# load template data and prepare (uses binread to avoid encoding issues)
-
@reader = block || lambda { |t| read_template_file }
-
@data = @reader.call(self)
-
-
if @data.respond_to?(:force_encoding)
-
@data.force_encoding(default_encoding) if default_encoding
-
-
if !@data.valid_encoding?
-
raise Encoding::InvalidByteSequenceError, "#{eval_file} is not valid #{@data.encoding}"
-
end
-
end
-
-
prepare
-
end
-
-
# The encoding of the source data. Defaults to the
-
# default_encoding-option if present. You may override this method
-
# in your template class if you have a better hint of the data's
-
# encoding.
-
1
def default_encoding
-
@default_encoding
-
end
-
-
1
def read_template_file
-
data = File.open(file, 'rb') { |io| io.read }
-
if data.respond_to?(:force_encoding)
-
# Set it to the default external (without verifying)
-
data.force_encoding(Encoding.default_external) if Encoding.default_external
-
end
-
data
-
end
-
-
# Render the template in the given scope with the locals specified. If a
-
# block is given, it is typically available within the template via
-
# +yield+.
-
1
def render(scope=Object.new, locals={}, &block)
-
evaluate scope, locals || {}, &block
-
end
-
-
# The basename of the template file.
-
1
def basename(suffix='')
-
File.basename(file, suffix) if file
-
end
-
-
# The template file's basename with all extensions chomped off.
-
1
def name
-
basename.split('.', 2).first if basename
-
end
-
-
# The filename used in backtraces to describe the template.
-
1
def eval_file
-
file || '(__TEMPLATE__)'
-
end
-
-
# Whether or not this template engine allows executing Ruby script
-
# within the template. If this is false, +scope+ and +locals+ will
-
# generally not be used, nor will the provided block be avaiable
-
# via +yield+.
-
# This should be overridden by template subclasses.
-
1
def allows_script?
-
true
-
end
-
-
1
protected
-
# Called once and only once for each template subclass the first time
-
# the template class is initialized. This should be used to require the
-
# underlying template library and perform any initial setup.
-
1
def initialize_engine
-
end
-
-
# Like Kernel#require but issues a warning urging a manual require when
-
# running under a threaded environment.
-
1
def require_template_library(name)
-
if Thread.list.size > 1
-
warn "WARN: tilt autoloading '#{name}' in a non thread-safe way; " +
-
"explicit require '#{name}' suggested."
-
end
-
require name
-
end
-
-
# Do whatever preparation is necessary to setup the underlying template
-
# engine. Called immediately after template data is loaded. Instance
-
# variables set in this method are available when #evaluate is called.
-
#
-
# Subclasses must provide an implementation of this method.
-
1
def prepare
-
if respond_to?(:compile!)
-
# backward compat with tilt < 0.6; just in case
-
warn 'Tilt::Template#compile! is deprecated; implement #prepare instead.'
-
compile!
-
else
-
raise NotImplementedError
-
end
-
end
-
-
# Execute the compiled template and return the result string. Template
-
# evaluation is guaranteed to be performed in the scope object with the
-
# locals specified and with support for yielding to the block.
-
#
-
# This method is only used by source generating templates. Subclasses that
-
# override render() may not support all features.
-
1
def evaluate(scope, locals, &block)
-
method = compiled_method(locals.keys)
-
method.bind(scope).call(locals, &block)
-
end
-
-
# Generates all template source by combining the preamble, template, and
-
# postamble and returns a two-tuple of the form: [source, offset], where
-
# source is the string containing (Ruby) source code for the template and
-
# offset is the integer line offset where line reporting should begin.
-
#
-
# Template subclasses may override this method when they need complete
-
# control over source generation or want to adjust the default line
-
# offset. In most cases, overriding the #precompiled_template method is
-
# easier and more appropriate.
-
1
def precompiled(locals)
-
preamble = precompiled_preamble(locals)
-
template = precompiled_template(locals)
-
postamble = precompiled_postamble(locals)
-
source = ''
-
-
# Ensure that our generated source code has the same encoding as the
-
# the source code generated by the template engine.
-
if source.respond_to?(:force_encoding)
-
template_encoding = extract_encoding(template)
-
-
source.force_encoding(template_encoding)
-
template.force_encoding(template_encoding)
-
end
-
-
# https://github.com/rtomayko/tilt/issues/193
-
warn "precompiled_preamble should return String (not Array)" if preamble.is_a?(Array)
-
warn "precompiled_postamble should return String (not Array)" if postamble.is_a?(Array)
-
source << [preamble, template, postamble].join("\n")
-
-
[source, preamble.count("\n")+1]
-
end
-
-
# A string containing the (Ruby) source code for the template. The
-
# default Template#evaluate implementation requires either this
-
# method or the #precompiled method be overridden. When defined,
-
# the base Template guarantees correct file/line handling, locals
-
# support, custom scopes, proper encoding, and support for template
-
# compilation.
-
1
def precompiled_template(locals)
-
raise NotImplementedError
-
end
-
-
# Generates preamble code for initializing template state, and performing
-
# locals assignment. The default implementation performs locals
-
# assignment only. Lines included in the preamble are subtracted from the
-
# source line offset, so adding code to the preamble does not effect line
-
# reporting in Kernel::caller and backtraces.
-
1
def precompiled_preamble(locals)
-
locals.map do |k,v|
-
if k.to_s =~ /\A[a-z_][a-zA-Z_0-9]*\z/
-
"#{k} = locals[#{k.inspect}]"
-
else
-
raise "invalid locals key: #{k.inspect} (keys must be variable names)"
-
end
-
end.join("\n")
-
end
-
-
# Generates postamble code for the precompiled template source. The
-
# string returned from this method is appended to the precompiled
-
# template source.
-
1
def precompiled_postamble(locals)
-
''
-
end
-
-
# The compiled method for the locals keys provided.
-
1
def compiled_method(locals_keys)
-
@compiled_method[locals_keys] ||=
-
compile_template_method(locals_keys)
-
end
-
-
1
private
-
1
def compile_template_method(locals)
-
source, offset = precompiled(locals)
-
method_name = "__tilt_#{Thread.current.object_id.abs}"
-
method_source = ""
-
-
if method_source.respond_to?(:force_encoding)
-
method_source.force_encoding(source.encoding)
-
end
-
-
method_source << <<-RUBY
-
TOPOBJECT.class_eval do
-
def #{method_name}(locals)
-
Thread.current[:tilt_vars] = [self, locals]
-
class << self
-
this, locals = Thread.current[:tilt_vars]
-
this.instance_eval do
-
RUBY
-
offset += method_source.count("\n")
-
method_source << source
-
method_source << "\nend;end;end;end"
-
Object.class_eval method_source, eval_file, line - offset
-
unbind_compiled_method(method_name)
-
end
-
-
1
def unbind_compiled_method(method_name)
-
method = TOPOBJECT.instance_method(method_name)
-
TOPOBJECT.class_eval { remove_method(method_name) }
-
method
-
end
-
-
1
def extract_encoding(script)
-
extract_magic_comment(script) || script.encoding
-
end
-
-
1
def extract_magic_comment(script)
-
binary script do
-
script[/\A[ \t]*\#.*coding\s*[=:]\s*([[:alnum:]\-_]+).*$/n, 1]
-
end
-
end
-
-
1
def binary(string)
-
original_encoding = string.encoding
-
string.force_encoding(Encoding::BINARY)
-
yield
-
ensure
-
string.force_encoding(original_encoding)
-
end
-
end
-
end
-
1
require 'tilt/template'
-
-
1
module Tilt
-
# RedCloth implementation. See:
-
# http://redcloth.org/
-
1
class RedClothTemplate < Template
-
1
def self.engine_initialized?
-
defined? ::RedCloth
-
end
-
-
1
def initialize_engine
-
require_template_library 'redcloth'
-
end
-
-
1
def prepare
-
@engine = RedCloth.new(data)
-
options.each {|k, v| @engine.send("#{k}=", v) if @engine.respond_to? "#{k}="}
-
@output = nil
-
end
-
-
1
def evaluate(scope, locals, &block)
-
@output ||= @engine.to_html
-
end
-
-
1
def allows_script?
-
false
-
end
-
end
-
end
-
-
1
require 'tilt/template'
-
-
1
module Tilt
-
# Creole implementation. See:
-
# http://www.wikicreole.org/
-
1
class CreoleTemplate < Template
-
1
def self.engine_initialized?
-
defined? ::Creole
-
end
-
-
1
def initialize_engine
-
require_template_library 'creole'
-
end
-
-
1
def prepare
-
opts = {}
-
[:allowed_schemes, :extensions, :no_escape].each do |k|
-
opts[k] = options[k] if options[k]
-
end
-
@engine = Creole::Parser.new(data, opts)
-
@output = nil
-
end
-
-
1
def evaluate(scope, locals, &block)
-
@output ||= @engine.to_html
-
end
-
-
1
def allows_script?
-
false
-
end
-
end
-
-
# WikiCloth implementation. See:
-
# http://redcloth.org/
-
1
class WikiClothTemplate < Template
-
1
def self.engine_initialized?
-
defined? ::WikiCloth::Parser
-
end
-
-
1
def initialize_engine
-
require_template_library 'wikicloth'
-
end
-
-
1
def prepare
-
@parser = options.delete(:parser) || WikiCloth::Parser
-
@engine = @parser.new options.merge(:data => data)
-
@output = nil
-
end
-
-
1
def evaluate(scope, locals, &block)
-
@output ||= @engine.to_html
-
end
-
-
1
def allows_script?
-
false
-
end
-
end
-
end
-
1
require 'tilt/template'
-
-
1
module Tilt
-
-
# Yajl Template implementation
-
#
-
# Yajl is a fast JSON parsing and encoding library for Ruby
-
# See https://github.com/brianmario/yajl-ruby
-
#
-
# The template source is evaluated as a Ruby string,
-
# and the result is converted #to_json.
-
#
-
# == Example
-
#
-
# # This is a template example.
-
# # The template can contain any Ruby statement.
-
# tpl <<-EOS
-
# @counter = 0
-
#
-
# # The json variable represents the buffer
-
# # and holds the data to be serialized into json.
-
# # It defaults to an empty hash, but you can override it at any time.
-
# json = {
-
# :"user#{@counter += 1}" => { :name => "Joshua Peek", :id => @counter },
-
# :"user#{@counter += 1}" => { :name => "Ryan Tomayko", :id => @counter },
-
# :"user#{@counter += 1}" => { :name => "Simone Carletti", :id => @counter },
-
# }
-
#
-
# # Since the json variable is a Hash,
-
# # you can use conditional statements or any other Ruby statement
-
# # to populate it.
-
# json[:"user#{@counter += 1}"] = { :name => "Unknown" } if 1 == 2
-
#
-
# # The last line doesn't affect the returned value.
-
# nil
-
# EOS
-
#
-
# template = Tilt::YajlTemplate.new { tpl }
-
# template.render(self)
-
#
-
1
class YajlTemplate < Template
-
-
1
self.default_mime_type = 'application/json'
-
-
1
def self.engine_initialized?
-
defined? ::Yajl
-
end
-
-
1
def initialize_engine
-
require_template_library 'yajl'
-
end
-
-
1
def prepare
-
end
-
-
1
def evaluate(scope, locals, &block)
-
decorate super(scope, locals, &block)
-
end
-
-
1
def precompiled_preamble(locals)
-
return super if locals.include? :json
-
"json = {}\n#{super}"
-
end
-
-
1
def precompiled_postamble(locals)
-
"Yajl::Encoder.new.encode(json)"
-
end
-
-
1
def precompiled_template(locals)
-
data.to_str
-
end
-
-
-
# Decorates the +json+ input according to given +options+.
-
#
-
# json - The json String to decorate.
-
# options - The option Hash to customize the behavior.
-
#
-
# Returns the decorated String.
-
1
def decorate(json)
-
callback, variable = options[:callback], options[:variable]
-
if callback && variable
-
"var #{variable} = #{json}; #{callback}(#{variable});"
-
elsif variable
-
"var #{variable} = #{json};"
-
elsif callback
-
"#{callback}(#{json});"
-
else
-
json
-
end
-
end
-
end
-
-
end
-
# Top level module for TZInfo.
-
1
module TZInfo
-
end
-
-
1
require 'tzinfo/ruby_core_support'
-
1
require 'tzinfo/offset_rationals'
-
1
require 'tzinfo/time_or_datetime'
-
-
1
require 'tzinfo/timezone_definition'
-
-
1
require 'tzinfo/timezone_offset'
-
1
require 'tzinfo/timezone_transition'
-
1
require 'tzinfo/timezone_transition_definition'
-
-
1
require 'tzinfo/timezone_index_definition'
-
-
1
require 'tzinfo/timezone_info'
-
1
require 'tzinfo/data_timezone_info'
-
1
require 'tzinfo/linked_timezone_info'
-
1
require 'tzinfo/transition_data_timezone_info'
-
1
require 'tzinfo/zoneinfo_timezone_info'
-
-
1
require 'tzinfo/data_source'
-
1
require 'tzinfo/ruby_data_source'
-
1
require 'tzinfo/zoneinfo_data_source'
-
-
1
require 'tzinfo/timezone_period'
-
1
require 'tzinfo/timezone'
-
1
require 'tzinfo/info_timezone'
-
1
require 'tzinfo/data_timezone'
-
1
require 'tzinfo/linked_timezone'
-
1
require 'tzinfo/timezone_proxy'
-
-
1
require 'tzinfo/country_index_definition'
-
1
require 'tzinfo/country_info'
-
1
require 'tzinfo/ruby_country_info'
-
1
require 'tzinfo/zoneinfo_country_info'
-
-
1
require 'tzinfo/country'
-
1
require 'tzinfo/country_timezone'
-
1
require 'thread_safe'
-
-
1
module TZInfo
-
# Raised by Country#get if the code given is not valid.
-
1
class InvalidCountryCode < StandardError
-
end
-
-
# The Country class represents an ISO 3166-1 country. It can be used to
-
# obtain a list of Timezones for a country. For example:
-
#
-
# us = Country.get('US')
-
# us.zone_identifiers
-
# us.zones
-
# us.zone_info
-
#
-
# The Country class is thread-safe. It is safe to use class and instance
-
# methods of Country in concurrently executing threads. Instances of Country
-
# can be shared across thread boundaries.
-
#
-
# Country information available through TZInfo is intended as an aid for
-
# users, to help them select time zone data appropriate for their practical
-
# needs. It is not intended to take or endorse any position on legal or
-
# territorial claims.
-
1
class Country
-
1
include Comparable
-
-
# Defined countries.
-
#
-
# @!visibility private
-
1
@@countries = nil
-
-
# Whether the countries index has been loaded yet.
-
#
-
# @!visibility private
-
1
@@index_loaded = false
-
-
# Gets a Country by its ISO 3166-1 alpha-2 code. Raises an
-
# InvalidCountryCode exception if it couldn't be found.
-
1
def self.get(identifier)
-
instance = @@countries[identifier]
-
-
unless instance
-
# Thread-safety: It is possible that multiple equivalent Country
-
# instances could be created here in concurrently executing threads.
-
# The consequences of this are that the data may be loaded more than
-
# once (depending on the data source) and memoized calculations could
-
# be discarded. The performance benefit of ensuring that only a single
-
# instance is created is unlikely to be worth the overhead of only
-
# allowing one Country to be loaded at a time.
-
info = data_source.load_country_info(identifier)
-
instance = Country.new(info)
-
@@countries[identifier] = instance
-
end
-
-
instance
-
end
-
-
# If identifier is a CountryInfo object, initializes the Country instance,
-
# otherwise calls get(identifier).
-
1
def self.new(identifier)
-
if identifier.kind_of?(CountryInfo)
-
instance = super()
-
instance.send :setup, identifier
-
instance
-
else
-
get(identifier)
-
end
-
end
-
-
# Returns an Array of all the valid country codes.
-
1
def self.all_codes
-
data_source.country_codes
-
end
-
-
# Returns an Array of all the defined Countries.
-
1
def self.all
-
data_source.country_codes.collect {|code| get(code)}
-
end
-
-
# The ISO 3166-1 alpha-2 country code.
-
1
def code
-
@info.code
-
end
-
-
# The name of the country.
-
1
def name
-
@info.name
-
end
-
-
# Alias for name.
-
1
def to_s
-
name
-
end
-
-
# Returns internal object state as a programmer-readable string.
-
1
def inspect
-
"#<#{self.class}: #{@info.code}>"
-
end
-
-
# Returns a frozen array of all the zone identifiers for the country. These
-
# are in an order that
-
#
-
# 1. makes some geographical sense, and
-
# 2. puts the most populous zones first, where that does not contradict 1.
-
#
-
# Returned zone identifiers may refer to cities and regions outside of the
-
# country. This will occur if the zone covers multiple countries. Any zones
-
# referring to a city or region in a different country will be listed after
-
# those relating to this country.
-
1
def zone_identifiers
-
@info.zone_identifiers
-
end
-
1
alias zone_names zone_identifiers
-
-
# An array of all the Timezones for this country. Returns TimezoneProxy
-
# objects to avoid the overhead of loading Timezone definitions until
-
# a conversion is actually required. The Timezones are returned in an order
-
# that
-
#
-
# 1. makes some geographical sense, and
-
# 2. puts the most populous zones first, where that does not contradict 1.
-
#
-
# Identifiers of the zones returned may refer to cities and regions outside
-
# of the country. This will occur if the zone covers multiple countries. Any
-
# zones referring to a city or region in a different country will be listed
-
# after those relating to this country.
-
1
def zones
-
zone_identifiers.collect {|id|
-
Timezone.get_proxy(id)
-
}
-
end
-
-
# Returns a frozen array of all the timezones for the for the country as
-
# CountryTimezone instances (containing extra information about each zone).
-
# These are in an order that
-
#
-
# 1. makes some geographical sense, and
-
# 2. puts the most populous zones first, where that does not contradict 1.
-
#
-
# Identifiers and descriptions of the zones returned may refer to cities and
-
# regions outside of the country. This will occur if the zone covers
-
# multiple countries. Any zones referring to a city or region in a different
-
# country will be listed after those relating to this country.
-
1
def zone_info
-
@info.zones
-
end
-
-
# Compare two Countries based on their code. Returns -1 if c is less
-
# than self, 0 if c is equal to self and +1 if c is greater than self.
-
#
-
# Returns nil if c is not comparable with Country instances.
-
1
def <=>(c)
-
return nil unless c.is_a?(Country)
-
code <=> c.code
-
end
-
-
# Returns true if and only if the code of c is equal to the code of this
-
# Country.
-
1
def eql?(c)
-
self == c
-
end
-
-
# Returns a hash value for this Country.
-
1
def hash
-
code.hash
-
end
-
-
# Dumps this Country for marshalling.
-
1
def _dump(limit)
-
code
-
end
-
-
# Loads a marshalled Country.
-
1
def self._load(data)
-
Country.get(data)
-
end
-
-
1
private
-
# Called by Country.new to initialize a new Country instance. The info
-
# parameter is a CountryInfo that defines the country.
-
1
def setup(info)
-
@info = info
-
end
-
-
# Initializes @@countries.
-
1
def self.init_countries
-
1
@@countries = ThreadSafe::Cache.new
-
end
-
1
init_countries
-
-
# Returns the current DataSource
-
1
def self.data_source
-
DataSource.get
-
end
-
end
-
end
-
1
module TZInfo
-
# The country index file includes CountryIndexDefinition which provides
-
# a country method used to define each country in the index.
-
#
-
# @private
-
1
module CountryIndexDefinition #:nodoc:
-
1
def self.append_features(base)
-
super
-
base.extend(ClassMethods)
-
base.instance_eval { @countries = {} }
-
end
-
-
# Class methods for inclusion.
-
#
-
# @private
-
1
module ClassMethods #:nodoc:
-
# Defines a country with an ISO 3166 country code, name and block. The
-
# block will be evaluated to obtain all the timezones for the country.
-
# Calls Country.country_defined with the definition of each country.
-
1
def country(code, name, &block)
-
@countries[code] = RubyCountryInfo.new(code, name, &block)
-
end
-
-
# Returns a frozen hash of all the countries that have been defined in
-
# the index.
-
1
def countries
-
@countries.freeze
-
end
-
end
-
end
-
end
-
1
module TZInfo
-
# Represents a country and references to its timezones as returned by a
-
# DataSource.
-
1
class CountryInfo
-
# The ISO 3166 country code.
-
1
attr_reader :code
-
-
# The name of the country.
-
1
attr_reader :name
-
-
# Constructs a new CountryInfo with an ISO 3166 country code and name
-
1
def initialize(code, name)
-
249
@code = code
-
249
@name = name
-
end
-
-
# Returns internal object state as a programmer-readable string.
-
1
def inspect
-
"#<#{self.class}: #@code>"
-
end
-
-
# Returns a frozen array of all the zone identifiers for the country.
-
# The identifiers are ordered by importance according to the DataSource.
-
1
def zone_identifiers
-
raise_not_implemented('zone_identifiers')
-
end
-
-
# Returns a frozen array of all the timezones for the for the country as
-
# CountryTimezone instances.
-
#
-
# The timezones are ordered by importance according to the DataSource.
-
1
def zones
-
raise_not_implemented('zones')
-
end
-
-
1
private
-
-
1
def raise_not_implemented(method_name)
-
raise NotImplementedError, "Subclasses must override #{method_name}"
-
end
-
end
-
end
-
1
module TZInfo
-
# A Timezone within a Country. This contains extra information about the
-
# Timezone that is specific to the Country (a Timezone could be used by
-
# multiple countries).
-
1
class CountryTimezone
-
# The zone identifier.
-
1
attr_reader :identifier
-
-
# A description of this timezone in relation to the country, e.g.
-
# "Eastern Time". This is usually nil for countries having only a single
-
# Timezone.
-
1
attr_reader :description
-
-
1
class << self
-
# Creates a new CountryTimezone with a timezone identifier, latitude,
-
# longitude and description. The latitude and longitude are specified as
-
# rationals - a numerator and denominator. For performance reasons, the
-
# numerators and denominators must be specified in their lowest form.
-
#
-
# For use internally within TZInfo.
-
#
-
# @!visibility private
-
1
alias :new! :new
-
-
# Creates a new CountryTimezone with a timezone identifier, latitude,
-
# longitude and description. The latitude and longitude must be specified
-
# as instances of Rational.
-
#
-
# CountryTimezone instances should normally only be constructed when
-
# creating new DataSource implementations.
-
1
def new(identifier, latitude, longitude, description = nil)
-
415
super(identifier, latitude, nil, longitude, nil, description)
-
end
-
end
-
-
# Creates a new CountryTimezone with a timezone identifier, latitude,
-
# longitude and description. The latitude and longitude are specified as
-
# rationals - a numerator and denominator. For performance reasons, the
-
# numerators and denominators must be specified in their lowest form.
-
#
-
# @!visibility private
-
1
def initialize(identifier, latitude_numerator, latitude_denominator,
-
longitude_numerator, longitude_denominator, description = nil) #:nodoc:
-
415
@identifier = identifier
-
-
415
if latitude_numerator.kind_of?(Rational)
-
415
@latitude = latitude_numerator
-
else
-
@latitude = nil
-
@latitude_numerator = latitude_numerator
-
@latitude_denominator = latitude_denominator
-
end
-
-
415
if longitude_numerator.kind_of?(Rational)
-
415
@longitude = longitude_numerator
-
else
-
@longitude = nil
-
@longitude_numerator = longitude_numerator
-
@longitude_denominator = longitude_denominator
-
end
-
-
415
@description = description
-
end
-
-
# The Timezone (actually a TimezoneProxy for performance reasons).
-
1
def timezone
-
Timezone.get_proxy(@identifier)
-
end
-
-
# if description is not nil, this method returns description; otherwise it
-
# returns timezone.friendly_identifier(true).
-
1
def description_or_friendly_identifier
-
description || timezone.friendly_identifier(true)
-
end
-
-
# The latitude of this timezone in degrees as a Rational.
-
1
def latitude
-
# Thread-safety: It is possible that the value of @latitude may be
-
# calculated multiple times in concurrently executing threads. It is not
-
# worth the overhead of locking to ensure that @latitude is only
-
# calculated once.
-
@latitude ||= RubyCoreSupport.rational_new!(@latitude_numerator, @latitude_denominator)
-
end
-
-
# The longitude of this timezone in degrees as a Rational.
-
1
def longitude
-
# Thread-safety: It is possible that the value of @longitude may be
-
# calculated multiple times in concurrently executing threads. It is not
-
# worth the overhead of locking to ensure that @longitude is only
-
# calculated once.
-
@longitude ||= RubyCoreSupport.rational_new!(@longitude_numerator, @longitude_denominator)
-
end
-
-
# Returns true if and only if the given CountryTimezone is equal to the
-
# current CountryTimezone (has the same identifer, latitude, longitude
-
# and description).
-
1
def ==(ct)
-
ct.kind_of?(CountryTimezone) &&
-
identifier == ct.identifier && latitude == ct.latitude &&
-
longitude == ct.longitude && description == ct.description
-
end
-
-
# Returns true if and only if the given CountryTimezone is equal to the
-
# current CountryTimezone (has the same identifer, latitude, longitude
-
# and description).
-
1
def eql?(ct)
-
self == ct
-
end
-
-
# Returns a hash of this CountryTimezone.
-
1
def hash
-
@identifier.hash ^
-
(@latitude ? @latitude.numerator.hash ^ @latitude.denominator.hash : @latitude_numerator.hash ^ @latitude_denominator.hash) ^
-
(@longitude ? @longitude.numerator.hash ^ @longitude.denominator.hash : @longitude_numerator.hash ^ @longitude_denominator.hash) ^
-
@description.hash
-
end
-
-
# Returns internal object state as a programmer-readable string.
-
1
def inspect
-
"#<#{self.class}: #@identifier>"
-
end
-
end
-
end
-
1
require 'thread'
-
-
1
module TZInfo
-
# InvalidDataSource is raised if the DataSource is used doesn't implement one
-
# of the required methods.
-
1
class InvalidDataSource < StandardError
-
end
-
-
# DataSourceNotFound is raised if no data source could be found (i.e.
-
# if 'tzinfo/data' cannot be found on the load path and no valid zoneinfo
-
# directory can be found on the system).
-
1
class DataSourceNotFound < StandardError
-
end
-
-
# The base class for data sources of timezone and country data.
-
#
-
# Use DataSource.set to change the data source being used.
-
1
class DataSource
-
# The currently selected data source.
-
1
@@instance = nil
-
-
# Mutex used to ensure the default data source is only created once.
-
1
@@default_mutex = Mutex.new
-
-
# Returns the currently selected DataSource instance.
-
1
def self.get
-
# If a DataSource hasn't been manually set when the first request is
-
# made to obtain a DataSource, then a Default data source is created.
-
-
# This is done at the first request rather than when TZInfo is loaded to
-
# avoid unnecessary (or in some cases potentially harmful) attempts to
-
# find a suitable DataSource.
-
-
# A Mutex is used to ensure that only a single default instance is
-
# created (having two different DataSources in use simultaneously could
-
# cause unexpected results).
-
-
1
unless @@instance
-
1
@@default_mutex.synchronize do
-
1
set(create_default_data_source) unless @@instance
-
end
-
end
-
-
1
@@instance
-
end
-
-
# Sets the currently selected data source for Timezone and Country data.
-
#
-
# This should usually be set to one of the two standard data source types:
-
#
-
# * +:ruby+ - read data from the Ruby modules included in the TZInfo::Data
-
# library (tzinfo-data gem).
-
# * +:zoneinfo+ - read data from the zoneinfo files included with most
-
# Unix-like operating sytems (e.g. in /usr/share/zoneinfo).
-
#
-
# To set TZInfo to use one of the standard data source types, call
-
# \TZInfo::DataSource.set in one of the following ways:
-
#
-
# TZInfo::DataSource.set(:ruby)
-
# TZInfo::DataSource.set(:zoneinfo)
-
# TZInfo::DataSource.set(:zoneinfo, zoneinfo_dir)
-
# TZInfo::DataSource.set(:zoneinfo, zoneinfo_dir, iso3166_tab_file)
-
#
-
# \DataSource.set(:zoneinfo) will automatically search for the zoneinfo
-
# directory by checking the paths specified in
-
# ZoneinfoDataSource.search_paths. ZoneinfoDirectoryNotFound will be raised
-
# if no valid zoneinfo directory could be found.
-
#
-
# \DataSource.set(:zoneinfo, zoneinfo_dir) uses the specified zoneinfo
-
# directory as the data source. If the directory is not a valid zoneinfo
-
# directory, an InvalidZoneinfoDirectory exception will be raised.
-
#
-
# \DataSource.set(:zoneinfo, zoneinfo_dir, iso3166_tab_file) uses the
-
# specified zoneinfo directory as the data source, but loads the iso3166.tab
-
# file from an alternate path. If the directory is not a valid zoneinfo
-
# directory, an InvalidZoneinfoDirectory exception will be raised.
-
#
-
# Custom data sources can be created by subclassing TZInfo::DataSource and
-
# implementing the following methods:
-
#
-
# * \load_timezone_info
-
# * \timezone_identifiers
-
# * \data_timezone_identifiers
-
# * \linked_timezone_identifiers
-
# * \load_country_info
-
# * \country_codes
-
#
-
# To have TZInfo use the custom data source, call \DataSource.set
-
# as follows:
-
#
-
# TZInfo::DataSource.set(CustomDataSource.new)
-
#
-
# To avoid inconsistent data, \DataSource.set should be called before
-
# accessing any Timezone or Country data.
-
#
-
# If \DataSource.set is not called, TZInfo will by default use TZInfo::Data
-
# as the data source. If TZInfo::Data is not available (i.e. if require
-
# 'tzinfo/data' fails), then TZInfo will search for a zoneinfo directory
-
# instead (using the search path specified by
-
# TZInfo::ZoneinfoDataSource::DEFAULT_SEARCH_PATH).
-
1
def self.set(data_source_or_type, *args)
-
1
if data_source_or_type.kind_of?(DataSource)
-
1
@@instance = data_source_or_type
-
elsif data_source_or_type == :ruby
-
@@instance = RubyDataSource.new
-
elsif data_source_or_type == :zoneinfo
-
@@instance = ZoneinfoDataSource.new(*args)
-
else
-
raise ArgumentError, 'data_source_or_type must be a DataSource instance or a data source type (:ruby)'
-
end
-
end
-
-
# Returns a TimezoneInfo instance for a given identifier. The TimezoneInfo
-
# instance should derive from either DataTimzoneInfo for timezones that
-
# define their own data or LinkedTimezoneInfo for links or aliases to
-
# other timezones.
-
#
-
# Raises InvalidTimezoneIdentifier if the timezone is not found or the
-
# identifier is invalid.
-
1
def load_timezone_info(identifier)
-
raise_invalid_data_source('load_timezone_info')
-
end
-
-
# Returns an array of all the available timezone identifiers.
-
1
def timezone_identifiers
-
raise_invalid_data_source('timezone_identifiers')
-
end
-
-
# Returns an array of all the available timezone identifiers for
-
# data timezones (i.e. those that actually contain definitions).
-
1
def data_timezone_identifiers
-
raise_invalid_data_source('data_timezone_identifiers')
-
end
-
-
# Returns an array of all the available timezone identifiers that
-
# are links to other timezones.
-
1
def linked_timezone_identifiers
-
raise_invalid_data_source('linked_timezone_identifiers')
-
end
-
-
# Returns a CountryInfo instance for the given ISO 3166-1 alpha-2
-
# country code. Raises InvalidCountryCode if the country could not be found
-
# or the code is invalid.
-
1
def load_country_info(code)
-
raise_invalid_data_source('load_country_info')
-
end
-
-
# Returns an array of all the available ISO 3166-1 alpha-2
-
# country codes.
-
1
def country_codes
-
raise_invalid_data_source('country_codes')
-
end
-
-
# Returns the name of this DataSource.
-
1
def to_s
-
"Default DataSource"
-
end
-
-
# Returns internal object state as a programmer-readable string.
-
1
def inspect
-
"#<#{self.class}>"
-
end
-
-
1
private
-
-
# Creates a DataSource instance for use as the default. Used if
-
# no preference has been specified manually.
-
1
def self.create_default_data_source
-
1
has_tzinfo_data = false
-
-
1
begin
-
1
require 'tzinfo/data'
-
has_tzinfo_data = true
-
rescue LoadError
-
end
-
-
1
return RubyDataSource.new if has_tzinfo_data
-
-
1
begin
-
1
return ZoneinfoDataSource.new
-
rescue ZoneinfoDirectoryNotFound
-
raise DataSourceNotFound, "No source of timezone data could be found.\nPlease refer to http://tzinfo.github.io/datasourcenotfound for help resolving this error."
-
end
-
end
-
-
1
def raise_invalid_data_source(method_name)
-
raise InvalidDataSource, "#{method_name} not defined"
-
end
-
end
-
end
-
1
module TZInfo
-
-
# A Timezone based on a DataTimezoneInfo.
-
#
-
# @private
-
1
class DataTimezone < InfoTimezone #:nodoc:
-
-
# Returns the TimezonePeriod for the given UTC time. utc can either be
-
# a DateTime, Time or integer timestamp (Time.to_i). Any timezone
-
# information in utc is ignored (it is treated as a UTC time).
-
#
-
# If no TimezonePeriod could be found, PeriodNotFound is raised.
-
1
def period_for_utc(utc)
-
13
info.period_for_utc(utc)
-
end
-
-
# Returns the set of TimezonePeriod instances that are valid for the given
-
# local time as an array. If you just want a single period, use
-
# period_for_local instead and specify how abiguities should be resolved.
-
# Raises PeriodNotFound if no periods are found for the given time.
-
1
def periods_for_local(local)
-
info.periods_for_local(local)
-
end
-
-
# Returns an Array of TimezoneTransition instances representing the times
-
# where the UTC offset of the timezone changes.
-
#
-
# Transitions are returned up to a given date and time up to a given date
-
# and time, specified in UTC (utc_to).
-
#
-
# A from date and time may also be supplied using the utc_from parameter
-
# (also specified in UTC). If utc_from is not nil, only transitions from
-
# that date and time onwards will be returned.
-
#
-
# Comparisons with utc_to are exclusive. Comparisons with utc_from are
-
# inclusive. If a transition falls precisely on utc_to, it will be excluded.
-
# If a transition falls on utc_from, it will be included.
-
#
-
# Transitions returned are ordered by when they occur, from earliest to
-
# latest.
-
#
-
# utc_to and utc_from can be specified using either DateTime, Time or
-
# integer timestamps (Time.to_i).
-
#
-
# If utc_from is specified and utc_to is not greater than utc_from, then
-
# transitions_up_to raises an ArgumentError exception.
-
1
def transitions_up_to(utc_to, utc_from = nil)
-
info.transitions_up_to(utc_to, utc_from)
-
end
-
-
# Returns the canonical zone for this Timezone.
-
#
-
# For a DataTimezone, this is always self.
-
1
def canonical_zone
-
self
-
end
-
end
-
end
-
1
module TZInfo
-
# Represents a defined timezone containing transition data.
-
1
class DataTimezoneInfo < TimezoneInfo
-
-
# Returns the TimezonePeriod for the given UTC time.
-
1
def period_for_utc(utc)
-
raise_not_implemented('period_for_utc')
-
end
-
-
# Returns the set of TimezonePeriods for the given local time as an array.
-
# Results returned are ordered by increasing UTC start date.
-
# Returns an empty array if no periods are found for the given time.
-
1
def periods_for_local(local)
-
raise_not_implemented('periods_for_local')
-
end
-
-
# Returns an Array of TimezoneTransition instances representing the times
-
# where the UTC offset of the timezone changes.
-
#
-
# Transitions are returned up to a given date and time up to a given date
-
# and time, specified in UTC (utc_to).
-
#
-
# A from date and time may also be supplied using the utc_from parameter
-
# (also specified in UTC). If utc_from is not nil, only transitions from
-
# that date and time onwards will be returned.
-
#
-
# Comparisons with utc_to are exclusive. Comparisons with utc_from are
-
# inclusive. If a transition falls precisely on utc_to, it will be excluded.
-
# If a transition falls on utc_from, it will be included.
-
#
-
# Transitions returned are ordered by when they occur, from earliest to
-
# latest.
-
#
-
# utc_to and utc_from can be specified using either DateTime, Time or
-
# integer timestamps (Time.to_i).
-
#
-
# If utc_from is specified and utc_to is not greater than utc_from, then
-
# transitions_up_to raises an ArgumentError exception.
-
1
def transitions_up_to(utc_to, utc_from = nil)
-
raise_not_implemented('transitions_up_to')
-
end
-
-
# Constructs a Timezone instance for the timezone represented by this
-
# DataTimezoneInfo.
-
1
def create_timezone
-
1
DataTimezone.new(self)
-
end
-
-
1
private
-
-
1
def raise_not_implemented(method_name)
-
raise NotImplementedError, "Subclasses must override #{method_name}"
-
end
-
end
-
end
-
1
module TZInfo
-
-
# A Timezone based on a TimezoneInfo.
-
#
-
# @private
-
1
class InfoTimezone < Timezone #:nodoc:
-
-
# Constructs a new InfoTimezone with a TimezoneInfo instance.
-
1
def self.new(info)
-
1
tz = super()
-
1
tz.send(:setup, info)
-
1
tz
-
end
-
-
# The identifier of the timezone, e.g. "Europe/Paris".
-
1
def identifier
-
1
@info.identifier
-
end
-
-
1
protected
-
# The TimezoneInfo for this Timezone.
-
1
def info
-
13
@info
-
end
-
-
1
def setup(info)
-
1
@info = info
-
end
-
end
-
end
-
1
module TZInfo
-
-
# A Timezone based on a LinkedTimezoneInfo.
-
#
-
# @private
-
1
class LinkedTimezone < InfoTimezone #:nodoc:
-
# Returns the TimezonePeriod for the given UTC time. utc can either be
-
# a DateTime, Time or integer timestamp (Time.to_i). Any timezone
-
# information in utc is ignored (it is treated as a UTC time).
-
#
-
# If no TimezonePeriod could be found, PeriodNotFound is raised.
-
1
def period_for_utc(utc)
-
@linked_timezone.period_for_utc(utc)
-
end
-
-
# Returns the set of TimezonePeriod instances that are valid for the given
-
# local time as an array. If you just want a single period, use
-
# period_for_local instead and specify how abiguities should be resolved.
-
# Raises PeriodNotFound if no periods are found for the given time.
-
1
def periods_for_local(local)
-
@linked_timezone.periods_for_local(local)
-
end
-
-
# Returns an Array of TimezoneTransition instances representing the times
-
# where the UTC offset of the timezone changes.
-
#
-
# Transitions are returned up to a given date and time up to a given date
-
# and time, specified in UTC (utc_to).
-
#
-
# A from date and time may also be supplied using the utc_from parameter
-
# (also specified in UTC). If utc_from is not nil, only transitions from
-
# that date and time onwards will be returned.
-
#
-
# Comparisons with utc_to are exclusive. Comparisons with utc_from are
-
# inclusive. If a transition falls precisely on utc_to, it will be excluded.
-
# If a transition falls on utc_from, it will be included.
-
#
-
# Transitions returned are ordered by when they occur, from earliest to
-
# latest.
-
#
-
# utc_to and utc_from can be specified using either DateTime, Time or
-
# integer timestamps (Time.to_i).
-
#
-
# If utc_from is specified and utc_to is not greater than utc_from, then
-
# transitions_up_to raises an ArgumentError exception.
-
1
def transitions_up_to(utc_to, utc_from = nil)
-
@linked_timezone.transitions_up_to(utc_to, utc_from)
-
end
-
-
# Returns the canonical zone for this Timezone.
-
#
-
# For a LinkedTimezone, this is the canonical zone of the link target.
-
1
def canonical_zone
-
@linked_timezone.canonical_zone
-
end
-
-
1
protected
-
1
def setup(info)
-
super(info)
-
@linked_timezone = Timezone.get(info.link_to_identifier)
-
end
-
end
-
end
-
1
module TZInfo
-
# Represents a timezone that is defined as a link or alias to another zone.
-
1
class LinkedTimezoneInfo < TimezoneInfo
-
-
# The zone that provides the data (that this zone is an alias for).
-
1
attr_reader :link_to_identifier
-
-
# Constructs a new LinkedTimezoneInfo with an identifier and the identifier
-
# of the zone linked to.
-
1
def initialize(identifier, link_to_identifier)
-
super(identifier)
-
@link_to_identifier = link_to_identifier
-
end
-
-
# Returns internal object state as a programmer-readable string.
-
1
def inspect
-
"#<#{self.class}: #@identifier,#@link_to_identifier>"
-
end
-
-
# Constructs a Timezone instance for the timezone represented by this
-
# DataTimezoneInfo.
-
1
def create_timezone
-
LinkedTimezone.new(self)
-
end
-
end
-
end
-
1
require 'rational' unless defined?(Rational)
-
-
1
module TZInfo
-
-
# Provides a method for getting Rationals for a timezone offset in seconds.
-
# Pre-reduced rationals are returned for all the half-hour intervals between
-
# -14 and +14 hours to avoid having to call gcd at runtime.
-
#
-
# @private
-
1
module OffsetRationals #:nodoc:
-
1
@@rational_cache = {
-
-50400 => RubyCoreSupport.rational_new!(-7,12),
-
-48600 => RubyCoreSupport.rational_new!(-9,16),
-
-46800 => RubyCoreSupport.rational_new!(-13,24),
-
-45000 => RubyCoreSupport.rational_new!(-25,48),
-
-43200 => RubyCoreSupport.rational_new!(-1,2),
-
-41400 => RubyCoreSupport.rational_new!(-23,48),
-
-39600 => RubyCoreSupport.rational_new!(-11,24),
-
-37800 => RubyCoreSupport.rational_new!(-7,16),
-
-36000 => RubyCoreSupport.rational_new!(-5,12),
-
-34200 => RubyCoreSupport.rational_new!(-19,48),
-
-32400 => RubyCoreSupport.rational_new!(-3,8),
-
-30600 => RubyCoreSupport.rational_new!(-17,48),
-
-28800 => RubyCoreSupport.rational_new!(-1,3),
-
-27000 => RubyCoreSupport.rational_new!(-5,16),
-
-25200 => RubyCoreSupport.rational_new!(-7,24),
-
-23400 => RubyCoreSupport.rational_new!(-13,48),
-
-21600 => RubyCoreSupport.rational_new!(-1,4),
-
-19800 => RubyCoreSupport.rational_new!(-11,48),
-
-18000 => RubyCoreSupport.rational_new!(-5,24),
-
-16200 => RubyCoreSupport.rational_new!(-3,16),
-
-14400 => RubyCoreSupport.rational_new!(-1,6),
-
-12600 => RubyCoreSupport.rational_new!(-7,48),
-
-10800 => RubyCoreSupport.rational_new!(-1,8),
-
-9000 => RubyCoreSupport.rational_new!(-5,48),
-
-7200 => RubyCoreSupport.rational_new!(-1,12),
-
-5400 => RubyCoreSupport.rational_new!(-1,16),
-
-3600 => RubyCoreSupport.rational_new!(-1,24),
-
-1800 => RubyCoreSupport.rational_new!(-1,48),
-
0 => RubyCoreSupport.rational_new!(0,1),
-
1800 => RubyCoreSupport.rational_new!(1,48),
-
3600 => RubyCoreSupport.rational_new!(1,24),
-
5400 => RubyCoreSupport.rational_new!(1,16),
-
7200 => RubyCoreSupport.rational_new!(1,12),
-
9000 => RubyCoreSupport.rational_new!(5,48),
-
10800 => RubyCoreSupport.rational_new!(1,8),
-
12600 => RubyCoreSupport.rational_new!(7,48),
-
14400 => RubyCoreSupport.rational_new!(1,6),
-
16200 => RubyCoreSupport.rational_new!(3,16),
-
18000 => RubyCoreSupport.rational_new!(5,24),
-
19800 => RubyCoreSupport.rational_new!(11,48),
-
21600 => RubyCoreSupport.rational_new!(1,4),
-
23400 => RubyCoreSupport.rational_new!(13,48),
-
25200 => RubyCoreSupport.rational_new!(7,24),
-
27000 => RubyCoreSupport.rational_new!(5,16),
-
28800 => RubyCoreSupport.rational_new!(1,3),
-
30600 => RubyCoreSupport.rational_new!(17,48),
-
32400 => RubyCoreSupport.rational_new!(3,8),
-
34200 => RubyCoreSupport.rational_new!(19,48),
-
36000 => RubyCoreSupport.rational_new!(5,12),
-
37800 => RubyCoreSupport.rational_new!(7,16),
-
39600 => RubyCoreSupport.rational_new!(11,24),
-
41400 => RubyCoreSupport.rational_new!(23,48),
-
43200 => RubyCoreSupport.rational_new!(1,2),
-
45000 => RubyCoreSupport.rational_new!(25,48),
-
46800 => RubyCoreSupport.rational_new!(13,24),
-
48600 => RubyCoreSupport.rational_new!(9,16),
-
50400 => RubyCoreSupport.rational_new!(7,12)}.freeze
-
-
# Returns a Rational expressing the fraction of a day that offset in
-
# seconds represents (i.e. equivalent to Rational(offset, 86400)).
-
1
def rational_for_offset(offset)
-
@@rational_cache[offset] || Rational(offset, 86400)
-
end
-
1
module_function :rational_for_offset
-
end
-
end
-
1
require 'date'
-
1
require 'rational' unless defined?(Rational)
-
-
1
module TZInfo
-
-
# Methods to support different versions of Ruby.
-
#
-
# @private
-
1
module RubyCoreSupport #:nodoc:
-
-
# Use Rational.new! for performance reasons in Ruby 1.8.
-
# This has been removed from 1.9, but Rational performs better.
-
1
if Rational.respond_to? :new!
-
def self.rational_new!(numerator, denominator = 1)
-
Rational.new!(numerator, denominator)
-
end
-
else
-
1
def self.rational_new!(numerator, denominator = 1)
-
58
Rational(numerator, denominator)
-
end
-
end
-
-
# Ruby 1.8.6 introduced new! and deprecated new0.
-
# Ruby 1.9.0 removed new0.
-
# Ruby trunk revision 31668 removed the new! method.
-
# Still support new0 for better performance on older versions of Ruby (new0 indicates
-
# that the rational has already been reduced to its lowest terms).
-
# Fallback to jd with conversion from ajd if new! and new0 are unavailable.
-
1
if DateTime.respond_to? :new!
-
def self.datetime_new!(ajd = 0, of = 0, sg = Date::ITALY)
-
DateTime.new!(ajd, of, sg)
-
end
-
elsif DateTime.respond_to? :new0
-
def self.datetime_new!(ajd = 0, of = 0, sg = Date::ITALY)
-
DateTime.new0(ajd, of, sg)
-
end
-
else
-
1
HALF_DAYS_IN_DAY = rational_new!(1, 2)
-
-
1
def self.datetime_new!(ajd = 0, of = 0, sg = Date::ITALY)
-
# Convert from an Astronomical Julian Day number to a civil Julian Day number.
-
jd = ajd + of + HALF_DAYS_IN_DAY
-
-
# Ruby trunk revision 31862 changed the behaviour of DateTime.jd so that it will no
-
# longer accept a fractional civil Julian Day number if further arguments are specified.
-
# Calculate the hours, minutes and seconds to pass to jd.
-
-
jd_i = jd.to_i
-
jd_i -= 1 if jd < 0
-
hours = (jd - jd_i) * 24
-
hours_i = hours.to_i
-
minutes = (hours - hours_i) * 60
-
minutes_i = minutes.to_i
-
seconds = (minutes - minutes_i) * 60
-
-
DateTime.jd(jd_i, hours_i, minutes_i, seconds, of, sg)
-
end
-
end
-
-
# DateTime in Ruby 1.8.6 doesn't consider times within the 60th second to be
-
# valid. When attempting to specify such a DateTime, subtract the fractional
-
# part and then add it back later
-
1
if Date.respond_to?(:valid_time?) && !Date.valid_time?(0, 0, rational_new!(59001, 1000)) # 0:0:59.001
-
def self.datetime_new(y=-4712, m=1, d=1, h=0, min=0, s=0, of=0, sg=Date::ITALY)
-
if !s.kind_of?(Integer) && s > 59
-
dt = DateTime.new(y, m, d, h, min, 59, of, sg)
-
dt + (s - 59) / 86400
-
else
-
DateTime.new(y, m, d, h, min, s, of, sg)
-
end
-
end
-
else
-
1
def self.datetime_new(y=-4712, m=1, d=1, h=0, min=0, s=0, of=0, sg=Date::ITALY)
-
DateTime.new(y, m, d, h, min, s, of, sg)
-
end
-
end
-
-
# Returns true if Time on the runtime platform supports Times defined
-
# by negative 32-bit timestamps, otherwise false.
-
1
begin
-
1
Time.at(-1)
-
1
Time.at(-2147483648)
-
-
1
def self.time_supports_negative
-
1
true
-
end
-
rescue ArgumentError
-
def self.time_supports_negative
-
false
-
end
-
end
-
-
# Returns true if Time on the runtime platform supports Times defined by
-
# 64-bit timestamps, otherwise false.
-
1
begin
-
1
Time.at(-2147483649)
-
1
Time.at(2147483648)
-
-
1
def self.time_supports_64bit
-
true
-
end
-
rescue RangeError
-
def self.time_supports_64bit
-
false
-
end
-
end
-
-
# Return the result of Time#nsec if it exists, otherwise return the
-
# result of Time#usec * 1000.
-
1
if Time.method_defined?(:nsec)
-
1
def self.time_nsec(time)
-
12
time.nsec
-
end
-
else
-
def self.time_nsec(time)
-
time.usec * 1000
-
end
-
end
-
-
# Call String#force_encoding if this version of Ruby has encoding support
-
# otherwise treat as a no-op.
-
1
if String.method_defined?(:force_encoding)
-
1
def self.force_encoding(str, encoding)
-
1
str.force_encoding(encoding)
-
end
-
else
-
def self.force_encoding(str, encoding)
-
str
-
end
-
end
-
-
-
# Wrapper for File.open that supports passing hash options for specifying
-
# encodings on Ruby 1.9+. The options are ignored on earlier versions of
-
# Ruby.
-
1
if RUBY_VERSION =~ /\A1\.[0-8]\./
-
def self.open_file(file_name, mode, opts, &block)
-
File.open(file_name, mode, &block)
-
end
-
else
-
1
def self.open_file(file_name, mode, opts, &block)
-
2
File.open(file_name, mode, opts, &block)
-
end
-
end
-
end
-
end
-
1
module TZInfo
-
# Represents information about a country returned by RubyDataSource.
-
#
-
# @private
-
1
class RubyCountryInfo < CountryInfo #:nodoc:
-
# Constructs a new CountryInfo with an ISO 3166 country code, name and
-
# block. The block will be evaluated to obtain the timezones for the
-
# country when the zones are first needed.
-
1
def initialize(code, name, &block)
-
super(code, name)
-
@block = block
-
@zones = nil
-
@zone_identifiers = nil
-
end
-
-
# Returns a frozen array of all the zone identifiers for the country. These
-
# are in the order they were added using the timezone method.
-
1
def zone_identifiers
-
# Thread-safety: It is possible that the value of @zone_identifiers may be
-
# calculated multiple times in concurrently executing threads. It is not
-
# worth the overhead of locking to ensure that @zone_identifiers is only
-
# calculated once.
-
-
unless @zone_identifiers
-
@zone_identifiers = zones.collect {|zone| zone.identifier}.freeze
-
end
-
-
@zone_identifiers
-
end
-
-
# Returns a frozen array of all the timezones for the for the country as
-
# CountryTimezone instances. These are in the order they were added using
-
# the timezone method.
-
1
def zones
-
# Thread-safety: It is possible that the value of @zones may be
-
# calculated multiple times in concurrently executing threads. It is not
-
# worth the overhead of locking to ensure that @zones is only
-
# calculated once.
-
-
unless @zones
-
zones = Zones.new
-
@block.call(zones) if @block
-
@block = nil
-
@zones = zones.list.freeze
-
end
-
-
@zones
-
end
-
-
# An instance of the Zones class is passed to the block used to define
-
# timezones.
-
#
-
# @private
-
1
class Zones #:nodoc:
-
1
attr_reader :list
-
-
1
def initialize
-
@list = []
-
end
-
-
# Called by the index data to define a timezone for the country.
-
1
def timezone(identifier, latitude_numerator, latitude_denominator,
-
longitude_numerator, longitude_denominator, description = nil)
-
@list << CountryTimezone.new!(identifier, latitude_numerator,
-
latitude_denominator, longitude_numerator, longitude_denominator,
-
description)
-
end
-
end
-
end
-
end
-
1
module TZInfo
-
# A DataSource that loads data from the set of Ruby modules included in the
-
# TZInfo::Data library (tzinfo-data gem).
-
#
-
# To have TZInfo use this DataSource, call TZInfo::DataSource.set as follows:
-
#
-
# TZInfo::DataSource.set(:ruby)
-
1
class RubyDataSource < DataSource
-
# Base path for require.
-
1
REQUIRE_PATH = File.join('tzinfo', 'data', 'definitions')
-
-
# Whether the timezone index has been loaded yet.
-
1
@@timezone_index_loaded = false
-
-
# Whether the country index has been loaded yet.
-
1
@@country_index_loaded = false
-
-
# Returns a TimezoneInfo instance for a given identifier.
-
# Raises InvalidTimezoneIdentifier if the timezone is not found or the
-
# identifier is invalid.
-
1
def load_timezone_info(identifier)
-
raise InvalidTimezoneIdentifier, 'Invalid identifier' if identifier !~ /^[A-Za-z0-9\+\-_]+(\/[A-Za-z0-9\+\-_]+)*$/
-
-
identifier = identifier.gsub(/-/, '__m__').gsub(/\+/, '__p__')
-
-
# Untaint identifier after it has been reassigned to a new string. We
-
# don't want to modify the original identifier. identifier may also be
-
# frozen and therefore cannot be untainted.
-
identifier.untaint
-
-
identifier = identifier.split('/')
-
begin
-
require_definition(identifier)
-
-
m = Data::Definitions
-
identifier.each {|part|
-
m = m.const_get(part)
-
}
-
-
m.get
-
rescue LoadError, NameError => e
-
raise InvalidTimezoneIdentifier, e.message
-
end
-
end
-
-
# Returns an array of all the available timezone identifiers.
-
1
def timezone_identifiers
-
load_timezone_index
-
Data::Indexes::Timezones.timezones
-
end
-
-
# Returns an array of all the available timezone identifiers for
-
# data timezones (i.e. those that actually contain definitions).
-
1
def data_timezone_identifiers
-
load_timezone_index
-
Data::Indexes::Timezones.data_timezones
-
end
-
-
# Returns an array of all the available timezone identifiers that
-
# are links to other timezones.
-
1
def linked_timezone_identifiers
-
load_timezone_index
-
Data::Indexes::Timezones.linked_timezones
-
end
-
-
# Returns a CountryInfo instance for the given ISO 3166-1 alpha-2
-
# country code. Raises InvalidCountryCode if the country could not be found
-
# or the code is invalid.
-
1
def load_country_info(code)
-
load_country_index
-
info = Data::Indexes::Countries.countries[code]
-
raise InvalidCountryCode, 'Invalid country code' unless info
-
info
-
end
-
-
# Returns an array of all the available ISO 3166-1 alpha-2
-
# country codes.
-
1
def country_codes
-
load_country_index
-
Data::Indexes::Countries.countries.keys.freeze
-
end
-
-
# Returns the name of this DataSource.
-
1
def to_s
-
"Ruby DataSource"
-
end
-
-
1
private
-
-
# Requires a zone definition by its identifier (split on /).
-
1
def require_definition(identifier)
-
require_data(*(['definitions'] + identifier))
-
end
-
-
# Requires an index by its name.
-
1
def self.require_index(name)
-
require_data(*['indexes', name])
-
end
-
-
# Requires a file from tzinfo/data.
-
1
def require_data(*file)
-
self.class.require_data(*file)
-
end
-
-
# Requires a file from tzinfo/data.
-
1
def self.require_data(*file)
-
require File.join('tzinfo', 'data', *file)
-
end
-
-
# Loads in the index of timezones if it hasn't already been loaded.
-
1
def load_timezone_index
-
self.class.load_timezone_index
-
end
-
-
# Loads in the index of timezones if it hasn't already been loaded.
-
1
def self.load_timezone_index
-
unless @@timezone_index_loaded
-
require_index('timezones')
-
@@timezone_index_loaded = true
-
end
-
end
-
-
# Loads in the index of countries if it hasn't already been loaded.
-
1
def load_country_index
-
self.class.load_country_index
-
end
-
-
# Loads in the index of countries if it hasn't already been loaded.
-
1
def self.load_country_index
-
unless @@country_index_loaded
-
require_index('countries')
-
@@country_index_loaded = true
-
end
-
end
-
end
-
end
-
1
require 'date'
-
1
require 'rational' unless defined?(Rational)
-
1
require 'time'
-
-
1
module TZInfo
-
# Used by TZInfo internally to represent either a Time, DateTime or
-
# an Integer timestamp (seconds since 1970-01-01 00:00:00).
-
1
class TimeOrDateTime
-
1
include Comparable
-
-
# Constructs a new TimeOrDateTime. timeOrDateTime can be a Time, DateTime
-
# or Integer. If using a Time or DateTime, any time zone information
-
# is ignored.
-
#
-
# Integer timestamps must be within the range supported by Time on the
-
# platform being used.
-
1
def initialize(timeOrDateTime)
-
12
@time = nil
-
12
@datetime = nil
-
12
@timestamp = nil
-
-
12
if timeOrDateTime.is_a?(Time)
-
12
@time = timeOrDateTime
-
-
# Avoid using the slower Rational class unless necessary.
-
12
nsec = RubyCoreSupport.time_nsec(@time)
-
12
usec = nsec % 1000 == 0 ? nsec / 1000 : Rational(nsec, 1000)
-
-
12
@time = Time.utc(@time.year, @time.mon, @time.mday, @time.hour, @time.min, @time.sec, usec) unless @time.utc?
-
12
@orig = @time
-
elsif timeOrDateTime.is_a?(DateTime)
-
@datetime = timeOrDateTime
-
@datetime = @datetime.new_offset(0) unless @datetime.offset == 0
-
@orig = @datetime
-
else
-
@timestamp = timeOrDateTime.to_i
-
-
if !RubyCoreSupport.time_supports_64bit && (@timestamp > 2147483647 || @timestamp < -2147483648 || (@timestamp < 0 && !RubyCoreSupport.time_supports_negative))
-
raise RangeError, 'Timestamp is outside the supported range of Time on this platform'
-
end
-
-
@orig = @timestamp
-
end
-
end
-
-
# Returns the time as a Time.
-
#
-
# When converting from a DateTime, the result is truncated to microsecond
-
# precision.
-
1
def to_time
-
# Thread-safety: It is possible that the value of @time may be
-
# calculated multiple times in concurrently executing threads. It is not
-
# worth the overhead of locking to ensure that @time is only
-
# calculated once.
-
-
12
unless @time
-
if @timestamp
-
@time = Time.at(@timestamp).utc
-
else
-
@time = Time.utc(year, mon, mday, hour, min, sec, usec)
-
end
-
end
-
-
12
@time
-
end
-
-
# Returns the time as a DateTime.
-
#
-
# When converting from a Time, the result is truncated to microsecond
-
# precision.
-
1
def to_datetime
-
# Thread-safety: It is possible that the value of @datetime may be
-
# calculated multiple times in concurrently executing threads. It is not
-
# worth the overhead of locking to ensure that @datetime is only
-
# calculated once.
-
-
unless @datetime
-
# Avoid using Rational unless necessary.
-
u = usec
-
s = u == 0 ? sec : Rational(sec * 1000000 + u, 1000000)
-
@datetime = RubyCoreSupport.datetime_new(year, mon, mday, hour, min, s)
-
end
-
-
@datetime
-
end
-
-
# Returns the time as an integer timestamp.
-
1
def to_i
-
# Thread-safety: It is possible that the value of @timestamp may be
-
# calculated multiple times in concurrently executing threads. It is not
-
# worth the overhead of locking to ensure that @timestamp is only
-
# calculated once.
-
-
unless @timestamp
-
@timestamp = to_time.to_i
-
end
-
-
@timestamp
-
end
-
-
# Returns the time as the original time passed to new.
-
1
def to_orig
-
@orig
-
end
-
-
# Returns a string representation of the TimeOrDateTime.
-
1
def to_s
-
if @orig.is_a?(Time)
-
"Time: #{@orig.to_s}"
-
elsif @orig.is_a?(DateTime)
-
"DateTime: #{@orig.to_s}"
-
else
-
"Timestamp: #{@orig.to_s}"
-
end
-
end
-
-
# Returns internal object state as a programmer-readable string.
-
1
def inspect
-
"#<#{self.class}: #{@orig.inspect}>"
-
end
-
-
# Returns the year.
-
1
def year
-
if @time
-
@time.year
-
elsif @datetime
-
@datetime.year
-
else
-
to_time.year
-
end
-
end
-
-
# Returns the month of the year (1..12).
-
1
def mon
-
if @time
-
@time.mon
-
elsif @datetime
-
@datetime.mon
-
else
-
to_time.mon
-
end
-
end
-
1
alias :month :mon
-
-
# Returns the day of the month (1..n).
-
1
def mday
-
if @time
-
@time.mday
-
elsif @datetime
-
@datetime.mday
-
else
-
to_time.mday
-
end
-
end
-
1
alias :day :mday
-
-
# Returns the hour of the day (0..23).
-
1
def hour
-
if @time
-
@time.hour
-
elsif @datetime
-
@datetime.hour
-
else
-
to_time.hour
-
end
-
end
-
-
# Returns the minute of the hour (0..59).
-
1
def min
-
if @time
-
@time.min
-
elsif @datetime
-
@datetime.min
-
else
-
to_time.min
-
end
-
end
-
-
# Returns the second of the minute (0..60). (60 for a leap second).
-
1
def sec
-
if @time
-
@time.sec
-
elsif @datetime
-
@datetime.sec
-
else
-
to_time.sec
-
end
-
end
-
-
# Returns the number of microseconds for the time.
-
1
def usec
-
if @time
-
@time.usec
-
elsif @datetime
-
# Ruby 1.8 has sec_fraction (of which the documentation says
-
# 'I do NOT recommend you to use this method'). sec_fraction no longer
-
# exists in Ruby 1.9.
-
-
# Calculate the sec_fraction from the day_fraction.
-
((@datetime.day_fraction - OffsetRationals.rational_for_offset(@datetime.hour * 3600 + @datetime.min * 60 + @datetime.sec)) * 86400000000).to_i
-
else
-
0
-
end
-
end
-
-
# Compares this TimeOrDateTime with another Time, DateTime, timestamp
-
# (Integer) or TimeOrDateTime. Returns -1, 0 or +1 depending
-
# whether the receiver is less than, equal to, or greater than
-
# timeOrDateTime.
-
#
-
# Returns nil if the passed in timeOrDateTime is not comparable with
-
# TimeOrDateTime instances.
-
#
-
# Comparisons involving a DateTime will be performed using DateTime#<=>.
-
# Comparisons that don't involve a DateTime, but include a Time will be
-
# performed with Time#<=>. Otherwise comparisons will be performed with
-
# Integer#<=>.
-
1
def <=>(timeOrDateTime)
-
return nil unless timeOrDateTime.is_a?(TimeOrDateTime) ||
-
timeOrDateTime.is_a?(Time) ||
-
timeOrDateTime.is_a?(DateTime) ||
-
timeOrDateTime.respond_to?(:to_i)
-
-
unless timeOrDateTime.is_a?(TimeOrDateTime)
-
timeOrDateTime = TimeOrDateTime.wrap(timeOrDateTime)
-
end
-
-
orig = timeOrDateTime.to_orig
-
-
if @orig.is_a?(DateTime) || orig.is_a?(DateTime)
-
# If either is a DateTime, assume it is there for a reason
-
# (i.e. for its larger range of acceptable values on 32-bit systems).
-
to_datetime <=> timeOrDateTime.to_datetime
-
elsif @orig.is_a?(Time) || orig.is_a?(Time)
-
to_time <=> timeOrDateTime.to_time
-
else
-
to_i <=> timeOrDateTime.to_i
-
end
-
end
-
-
# Adds a number of seconds to the TimeOrDateTime. Returns a new
-
# TimeOrDateTime, preserving what the original constructed type was.
-
# If the original type is a Time and the resulting calculation goes out of
-
# range for Times, then an exception will be raised by the Time class.
-
1
def +(seconds)
-
12
if seconds == 0
-
12
self
-
else
-
if @orig.is_a?(DateTime)
-
TimeOrDateTime.new(@orig + OffsetRationals.rational_for_offset(seconds))
-
else
-
# + defined for Time and Integer
-
TimeOrDateTime.new(@orig + seconds)
-
end
-
end
-
end
-
-
# Subtracts a number of seconds from the TimeOrDateTime. Returns a new
-
# TimeOrDateTime, preserving what the original constructed type was.
-
# If the original type is a Time and the resulting calculation goes out of
-
# range for Times, then an exception will be raised by the Time class.
-
1
def -(seconds)
-
self + (-seconds)
-
end
-
-
# Similar to the + operator, but converts to a DateTime based TimeOrDateTime
-
# where the Time or Integer timestamp to go out of the allowed range for a
-
# Time, converts to a DateTime based TimeOrDateTime.
-
#
-
# Note that the range of Time varies based on the platform.
-
1
def add_with_convert(seconds)
-
if seconds == 0
-
self
-
else
-
if @orig.is_a?(DateTime)
-
TimeOrDateTime.new(@orig + OffsetRationals.rational_for_offset(seconds))
-
else
-
# A Time or timestamp.
-
result = to_i + seconds
-
-
if ((result > 2147483647 || result < -2147483648) && !RubyCoreSupport.time_supports_64bit) || (result < 0 && !RubyCoreSupport.time_supports_negative)
-
result = TimeOrDateTime.new(to_datetime + OffsetRationals.rational_for_offset(seconds))
-
else
-
result = TimeOrDateTime.new(@orig + seconds)
-
end
-
end
-
end
-
end
-
-
# Returns true if todt represents the same time and was originally
-
# constructed with the same type (DateTime, Time or timestamp) as this
-
# TimeOrDateTime.
-
1
def eql?(todt)
-
todt.kind_of?(TimeOrDateTime) && to_orig.eql?(todt.to_orig)
-
end
-
-
# Returns a hash of this TimeOrDateTime.
-
1
def hash
-
@orig.hash
-
end
-
-
# If no block is given, returns a TimeOrDateTime wrapping the given
-
# timeOrDateTime. If a block is specified, a TimeOrDateTime is constructed
-
# and passed to the block. The result of the block must be a TimeOrDateTime.
-
#
-
# The result of the block will be converted to the type of the originally
-
# passed in timeOrDateTime and then returned as the result of wrap.
-
#
-
# timeOrDateTime can be a Time, DateTime, timestamp (Integer) or
-
# TimeOrDateTime. If a TimeOrDateTime is passed in, no new TimeOrDateTime
-
# will be constructed and the value passed to wrap will be used when
-
# calling the block.
-
1
def self.wrap(timeOrDateTime)
-
12
t = timeOrDateTime.is_a?(TimeOrDateTime) ? timeOrDateTime : TimeOrDateTime.new(timeOrDateTime)
-
-
12
if block_given?
-
12
t = yield t
-
-
12
if timeOrDateTime.is_a?(TimeOrDateTime)
-
t
-
12
elsif timeOrDateTime.is_a?(Time)
-
12
t.to_time
-
elsif timeOrDateTime.is_a?(DateTime)
-
t.to_datetime
-
else
-
t.to_i
-
end
-
else
-
t
-
end
-
end
-
end
-
end
-
1
require 'date'
-
1
require 'set'
-
1
require 'thread_safe'
-
-
1
module TZInfo
-
# AmbiguousTime is raised to indicates that a specified time in a local
-
# timezone has more than one possible equivalent UTC time. This happens when
-
# transitioning from daylight savings time to standard time where the clocks
-
# are rolled back.
-
#
-
# AmbiguousTime is raised by period_for_local and local_to_utc when using an
-
# ambiguous time and not specifying any means to resolve the ambiguity.
-
1
class AmbiguousTime < StandardError
-
end
-
-
# PeriodNotFound is raised to indicate that no TimezonePeriod matching a given
-
# time could be found.
-
1
class PeriodNotFound < StandardError
-
end
-
-
# Raised by Timezone#get if the identifier given is not valid.
-
1
class InvalidTimezoneIdentifier < StandardError
-
end
-
-
# Raised if an attempt is made to use a timezone created with
-
# Timezone.new(nil).
-
1
class UnknownTimezone < StandardError
-
end
-
-
# Timezone is the base class of all timezones. It provides a factory method,
-
# 'get', to access timezones by identifier. Once a specific Timezone has been
-
# retrieved, DateTimes, Times and timestamps can be converted between the UTC
-
# and the local time for the zone. For example:
-
#
-
# tz = TZInfo::Timezone.get('America/New_York')
-
# puts tz.utc_to_local(DateTime.new(2005,8,29,15,35,0)).to_s
-
# puts tz.local_to_utc(Time.utc(2005,8,29,11,35,0)).to_s
-
# puts tz.utc_to_local(1125315300).to_s
-
#
-
# Each time conversion method returns an object of the same type it was
-
# passed.
-
#
-
# The Timezone class is thread-safe. It is safe to use class and instance
-
# methods of Timezone in concurrently executing threads. Instances of Timezone
-
# can be shared across thread boundaries.
-
1
class Timezone
-
1
include Comparable
-
-
# Cache of loaded zones by identifier to avoid using require if a zone
-
# has already been loaded.
-
#
-
# @!visibility private
-
1
@@loaded_zones = nil
-
-
# Default value of the dst parameter of the local_to_utc and
-
# period_for_local methods.
-
#
-
# @!visibility private
-
1
@@default_dst = nil
-
-
# Sets the default value of the optional dst parameter of the
-
# local_to_utc and period_for_local methods. Can be set to nil, true or
-
# false.
-
#
-
# The value of default_dst defaults to nil if unset.
-
1
def self.default_dst=(value)
-
@@default_dst = value.nil? ? nil : !!value
-
end
-
-
# Gets the default value of the optional dst parameter of the
-
# local_to_utc and period_for_local methods. Can be set to nil, true or
-
# false.
-
1
def self.default_dst
-
@@default_dst
-
end
-
-
# Returns a timezone by its identifier (e.g. "Europe/London",
-
# "America/Chicago" or "UTC").
-
#
-
# Raises InvalidTimezoneIdentifier if the timezone couldn't be found.
-
1
def self.get(identifier)
-
1
instance = @@loaded_zones[identifier]
-
-
1
unless instance
-
# Thread-safety: It is possible that multiple equivalent Timezone
-
# instances could be created here in concurrently executing threads.
-
# The consequences of this are that the data may be loaded more than
-
# once (depending on the data source) and memoized calculations could
-
# be discarded. The performance benefit of ensuring that only a single
-
# instance is created is unlikely to be worth the overhead of only
-
# allowing one Timezone to be loaded at a time.
-
1
info = data_source.load_timezone_info(identifier)
-
1
instance = info.create_timezone
-
1
@@loaded_zones[instance.identifier] = instance
-
end
-
-
1
instance
-
end
-
-
# Returns a proxy for the Timezone with the given identifier. The proxy
-
# will cause the real timezone to be loaded when an attempt is made to
-
# find a period or convert a time. get_proxy will not validate the
-
# identifier. If an invalid identifier is specified, no exception will be
-
# raised until the proxy is used.
-
1
def self.get_proxy(identifier)
-
TimezoneProxy.new(identifier)
-
end
-
-
# If identifier is nil calls super(), otherwise calls get. An identfier
-
# should always be passed in when called externally.
-
1
def self.new(identifier = nil)
-
2
if identifier
-
get(identifier)
-
else
-
2
super()
-
end
-
end
-
-
# Returns an array containing all the available Timezones.
-
#
-
# Returns TimezoneProxy objects to avoid the overhead of loading Timezone
-
# definitions until a conversion is actually required.
-
1
def self.all
-
get_proxies(all_identifiers)
-
end
-
-
# Returns an array containing the identifiers of all the available
-
# Timezones.
-
1
def self.all_identifiers
-
data_source.timezone_identifiers
-
end
-
-
# Returns an array containing all the available Timezones that are based
-
# on data (are not links to other Timezones).
-
#
-
# Returns TimezoneProxy objects to avoid the overhead of loading Timezone
-
# definitions until a conversion is actually required.
-
1
def self.all_data_zones
-
get_proxies(all_data_zone_identifiers)
-
end
-
-
# Returns an array containing the identifiers of all the available
-
# Timezones that are based on data (are not links to other Timezones)..
-
1
def self.all_data_zone_identifiers
-
data_source.data_timezone_identifiers
-
end
-
-
# Returns an array containing all the available Timezones that are links
-
# to other Timezones.
-
#
-
# Returns TimezoneProxy objects to avoid the overhead of loading Timezone
-
# definitions until a conversion is actually required.
-
1
def self.all_linked_zones
-
get_proxies(all_linked_zone_identifiers)
-
end
-
-
# Returns an array containing the identifiers of all the available
-
# Timezones that are links to other Timezones.
-
1
def self.all_linked_zone_identifiers
-
data_source.linked_timezone_identifiers
-
end
-
-
# Returns all the Timezones defined for all Countries. This is not the
-
# complete set of Timezones as some are not country specific (e.g.
-
# 'Etc/GMT').
-
#
-
# Returns TimezoneProxy objects to avoid the overhead of loading Timezone
-
# definitions until a conversion is actually required.
-
1
def self.all_country_zones
-
Country.all_codes.inject([]) do |zones,country|
-
zones += Country.get(country).zones
-
end.uniq
-
end
-
-
# Returns all the zone identifiers defined for all Countries. This is not the
-
# complete set of zone identifiers as some are not country specific (e.g.
-
# 'Etc/GMT'). You can obtain a Timezone instance for a given identifier
-
# with the get method.
-
1
def self.all_country_zone_identifiers
-
Country.all_codes.inject([]) do |zones,country|
-
zones += Country.get(country).zone_identifiers
-
end.uniq
-
end
-
-
# Returns all US Timezone instances. A shortcut for
-
# TZInfo::Country.get('US').zones.
-
#
-
# Returns TimezoneProxy objects to avoid the overhead of loading Timezone
-
# definitions until a conversion is actually required.
-
1
def self.us_zones
-
Country.get('US').zones
-
end
-
-
# Returns all US zone identifiers. A shortcut for
-
# TZInfo::Country.get('US').zone_identifiers.
-
1
def self.us_zone_identifiers
-
Country.get('US').zone_identifiers
-
end
-
-
# The identifier of the timezone, e.g. "Europe/Paris".
-
1
def identifier
-
raise_unknown_timezone
-
end
-
-
# An alias for identifier.
-
1
def name
-
# Don't use alias, as identifier gets overridden.
-
identifier
-
end
-
-
# Returns a friendlier version of the identifier.
-
1
def to_s
-
friendly_identifier
-
end
-
-
# Returns internal object state as a programmer-readable string.
-
1
def inspect
-
"#<#{self.class}: #{identifier}>"
-
end
-
-
# Returns a friendlier version of the identifier. Set skip_first_part to
-
# omit the first part of the identifier (typically a region name) where
-
# there is more than one part.
-
#
-
# For example:
-
#
-
# Timezone.get('Europe/Paris').friendly_identifier(false) #=> "Europe - Paris"
-
# Timezone.get('Europe/Paris').friendly_identifier(true) #=> "Paris"
-
# Timezone.get('America/Indiana/Knox').friendly_identifier(false) #=> "America - Knox, Indiana"
-
# Timezone.get('America/Indiana/Knox').friendly_identifier(true) #=> "Knox, Indiana"
-
1
def friendly_identifier(skip_first_part = false)
-
parts = identifier.split('/')
-
if parts.empty?
-
# shouldn't happen
-
identifier
-
elsif parts.length == 1
-
parts[0]
-
else
-
if skip_first_part
-
result = ''
-
else
-
result = parts[0] + ' - '
-
end
-
-
parts[1, parts.length - 1].reverse_each {|part|
-
part.gsub!(/_/, ' ')
-
-
if part.index(/[a-z]/)
-
# Missing a space if a lower case followed by an upper case and the
-
# name isn't McXxxx.
-
part.gsub!(/([^M][a-z])([A-Z])/, '\1 \2')
-
part.gsub!(/([M][a-bd-z])([A-Z])/, '\1 \2')
-
-
# Missing an apostrophe if two consecutive upper case characters.
-
part.gsub!(/([A-Z])([A-Z])/, '\1\'\2')
-
end
-
-
result << part
-
result << ', '
-
}
-
-
result.slice!(result.length - 2, 2)
-
result
-
end
-
end
-
-
# Returns the TimezonePeriod for the given UTC time. utc can either be
-
# a DateTime, Time or integer timestamp (Time.to_i). Any timezone
-
# information in utc is ignored (it is treated as a UTC time).
-
1
def period_for_utc(utc)
-
raise_unknown_timezone
-
end
-
-
# Returns the set of TimezonePeriod instances that are valid for the given
-
# local time as an array. If you just want a single period, use
-
# period_for_local instead and specify how ambiguities should be resolved.
-
# Returns an empty array if no periods are found for the given time.
-
1
def periods_for_local(local)
-
raise_unknown_timezone
-
end
-
-
# Returns an Array of TimezoneTransition instances representing the times
-
# where the UTC offset of the timezone changes.
-
#
-
# Transitions are returned up to a given date and time up to a given date
-
# and time, specified in UTC (utc_to).
-
#
-
# A from date and time may also be supplied using the utc_from parameter
-
# (also specified in UTC). If utc_from is not nil, only transitions from
-
# that date and time onwards will be returned.
-
#
-
# Comparisons with utc_to are exclusive. Comparisons with utc_from are
-
# inclusive. If a transition falls precisely on utc_to, it will be excluded.
-
# If a transition falls on utc_from, it will be included.
-
#
-
# Transitions returned are ordered by when they occur, from earliest to
-
# latest.
-
#
-
# utc_to and utc_from can be specified using either DateTime, Time or
-
# integer timestamps (Time.to_i).
-
#
-
# If utc_from is specified and utc_to is not greater than utc_from, then
-
# transitions_up_to raises an ArgumentError exception.
-
1
def transitions_up_to(utc_to, utc_from = nil)
-
raise_unknown_timezone
-
end
-
-
# Returns the canonical Timezone instance for this Timezone.
-
#
-
# The IANA Time Zone database contains two types of definition: Zones and
-
# Links. Zones are defined by rules that set out when transitions occur.
-
# Links are just references to fully defined Zone, creating an alias for
-
# that Zone.
-
#
-
# Links are commonly used where a time zone has been renamed in a
-
# release of the Time Zone database. For example, the Zone US/Eastern was
-
# renamed as America/New_York. A US/Eastern Link was added in its place,
-
# linking to (and creating an alias for) for America/New_York.
-
#
-
# Links are also used for time zones that are currently identical to a full
-
# Zone, but that are administered seperately. For example, Europe/Vatican is
-
# a Link to (and alias for) Europe/Rome.
-
#
-
# For a full Zone, canonical_zone returns self.
-
#
-
# For a Link, canonical_zone returns a Timezone instance representing the
-
# full Zone that the link targets.
-
#
-
# TZInfo can be used with different data sources (see the documentation for
-
# TZInfo::DataSource). Please note that some DataSource implementations may
-
# not support distinguishing between full Zones and Links and will treat all
-
# time zones as full Zones. In this case, the canonical_zone will always
-
# return self.
-
#
-
# There are two built-in DataSource implementations. RubyDataSource (which
-
# will be used if the tzinfo-data gem is available) supports Link zones.
-
# ZoneinfoDataSource returns Link zones as if they were full Zones. If the
-
# canonical_zone or canonical_identifier methods are required, the
-
# tzinfo-data gem should be installed.
-
#
-
# The TZInfo::DataSource.get method can be used to check which DataSource
-
# implementation is being used.
-
1
def canonical_zone
-
raise_unknown_timezone
-
end
-
-
# Returns the TimezonePeriod for the given local time. local can either be
-
# a DateTime, Time or integer timestamp (Time.to_i). Any timezone
-
# information in local is ignored (it is treated as a time in the current
-
# timezone).
-
#
-
# Warning: There are local times that have no equivalent UTC times (e.g.
-
# in the transition from standard time to daylight savings time). There are
-
# also local times that have more than one UTC equivalent (e.g. in the
-
# transition from daylight savings time to standard time).
-
#
-
# In the first case (no equivalent UTC time), a PeriodNotFound exception
-
# will be raised.
-
#
-
# In the second case (more than one equivalent UTC time), an AmbiguousTime
-
# exception will be raised unless the optional dst parameter or block
-
# handles the ambiguity.
-
#
-
# If the ambiguity is due to a transition from daylight savings time to
-
# standard time, the dst parameter can be used to select whether the
-
# daylight savings time or local time is used. For example,
-
#
-
# Timezone.get('America/New_York').period_for_local(DateTime.new(2004,10,31,1,30,0))
-
#
-
# would raise an AmbiguousTime exception.
-
#
-
# Specifying dst=true would the daylight savings period from April to
-
# October 2004. Specifying dst=false would return the standard period
-
# from October 2004 to April 2005.
-
#
-
# If the dst parameter does not resolve the ambiguity, and a block is
-
# specified, it is called. The block must take a single parameter - an
-
# array of the periods that need to be resolved. The block can select and
-
# return a single period or return nil or an empty array
-
# to cause an AmbiguousTime exception to be raised.
-
#
-
# The default value of the dst parameter can be specified by setting
-
# Timezone.default_dst. If default_dst is not set, or is set to nil, then
-
# an AmbiguousTime exception will be raised in ambiguous situations unless
-
# a block is given to resolve the ambiguity.
-
1
def period_for_local(local, dst = Timezone.default_dst)
-
results = periods_for_local(local)
-
-
if results.empty?
-
raise PeriodNotFound
-
elsif results.size < 2
-
results.first
-
else
-
# ambiguous result try to resolve
-
-
if !dst.nil?
-
matches = results.find_all {|period| period.dst? == dst}
-
results = matches if !matches.empty?
-
end
-
-
if results.size < 2
-
results.first
-
else
-
# still ambiguous, try the block
-
-
if block_given?
-
results = yield results
-
end
-
-
if results.is_a?(TimezonePeriod)
-
results
-
elsif results && results.size == 1
-
results.first
-
else
-
raise AmbiguousTime, "#{local} is an ambiguous local time."
-
end
-
end
-
end
-
end
-
-
# Converts a time in UTC to the local timezone. utc can either be
-
# a DateTime, Time or timestamp (Time.to_i). The returned time has the same
-
# type as utc. Any timezone information in utc is ignored (it is treated as
-
# a UTC time).
-
1
def utc_to_local(utc)
-
TimeOrDateTime.wrap(utc) {|wrapped|
-
period_for_utc(wrapped).to_local(wrapped)
-
}
-
end
-
-
# Converts a time in the local timezone to UTC. local can either be
-
# a DateTime, Time or timestamp (Time.to_i). The returned time has the same
-
# type as local. Any timezone information in local is ignored (it is treated
-
# as a local time).
-
#
-
# Warning: There are local times that have no equivalent UTC times (e.g.
-
# in the transition from standard time to daylight savings time). There are
-
# also local times that have more than one UTC equivalent (e.g. in the
-
# transition from daylight savings time to standard time).
-
#
-
# In the first case (no equivalent UTC time), a PeriodNotFound exception
-
# will be raised.
-
#
-
# In the second case (more than one equivalent UTC time), an AmbiguousTime
-
# exception will be raised unless the optional dst parameter or block
-
# handles the ambiguity.
-
#
-
# If the ambiguity is due to a transition from daylight savings time to
-
# standard time, the dst parameter can be used to select whether the
-
# daylight savings time or local time is used. For example,
-
#
-
# Timezone.get('America/New_York').local_to_utc(DateTime.new(2004,10,31,1,30,0))
-
#
-
# would raise an AmbiguousTime exception.
-
#
-
# Specifying dst=true would return 2004-10-31 5:30:00. Specifying dst=false
-
# would return 2004-10-31 6:30:00.
-
#
-
# If the dst parameter does not resolve the ambiguity, and a block is
-
# specified, it is called. The block must take a single parameter - an
-
# array of the periods that need to be resolved. The block can return a
-
# single period to use to convert the time or return nil or an empty array
-
# to cause an AmbiguousTime exception to be raised.
-
#
-
# The default value of the dst parameter can be specified by setting
-
# Timezone.default_dst. If default_dst is not set, or is set to nil, then
-
# an AmbiguousTime exception will be raised in ambiguous situations unless
-
# a block is given to resolve the ambiguity.
-
1
def local_to_utc(local, dst = Timezone.default_dst)
-
TimeOrDateTime.wrap(local) {|wrapped|
-
if block_given?
-
period = period_for_local(wrapped, dst) {|periods| yield periods }
-
else
-
period = period_for_local(wrapped, dst)
-
end
-
-
period.to_utc(wrapped)
-
}
-
end
-
-
# Returns information about offsets used by the Timezone up to a given
-
# date and time, specified using UTC (utc_to). The information is returned
-
# as an Array of TimezoneOffset instances.
-
#
-
# A from date and time may also be supplied using the utc_from parameter
-
# (also specified in UTC). If utc_from is not nil, only offsets used from
-
# that date and time forward will be returned.
-
#
-
# Comparisons with utc_to are exclusive. Comparisons with utc_from are
-
# inclusive.
-
#
-
# Offsets may be returned in any order.
-
#
-
# utc_to and utc_from can be specified using either DateTime, Time or
-
# integer timestamps (Time.to_i).
-
#
-
# If utc_from is specified and utc_to is not greater than utc_from, then
-
# offsets_up_to raises an ArgumentError exception.
-
1
def offsets_up_to(utc_to, utc_from = nil)
-
utc_to = TimeOrDateTime.wrap(utc_to)
-
transitions = transitions_up_to(utc_to, utc_from)
-
-
if transitions.empty?
-
# No transitions in the range, find the period that covers it.
-
-
if utc_from
-
# Use the from date as it is inclusive.
-
period = period_for_utc(utc_from)
-
else
-
# utc_to is exclusive, so this can't be used with period_for_utc.
-
# However, any time earlier than utc_to can be used.
-
-
# Subtract 1 hour (since this is one of the cached OffsetRationals).
-
# Use add_with_convert so that conversion to DateTime is performed if
-
# required.
-
period = period_for_utc(utc_to.add_with_convert(-3600))
-
end
-
-
[period.offset]
-
else
-
result = Set.new
-
-
first = transitions.first
-
result << first.previous_offset unless utc_from && first.at == utc_from
-
-
transitions.each do |t|
-
result << t.offset
-
end
-
-
result.to_a
-
end
-
end
-
-
# Returns the canonical identifier for this Timezone.
-
#
-
# This is a shortcut for calling canonical_zone.identifier. Please refer
-
# to the canonical_zone documentation for further information.
-
1
def canonical_identifier
-
canonical_zone.identifier
-
end
-
-
# Returns the current time in the timezone as a Time.
-
1
def now
-
utc_to_local(Time.now.utc)
-
end
-
-
# Returns the TimezonePeriod for the current time.
-
1
def current_period
-
1
period_for_utc(Time.now.utc)
-
end
-
-
# Returns the current Time and TimezonePeriod as an array. The first element
-
# is the time, the second element is the period.
-
1
def current_period_and_time
-
utc = Time.now.utc
-
period = period_for_utc(utc)
-
[period.to_local(utc), period]
-
end
-
-
1
alias :current_time_and_period :current_period_and_time
-
-
# Converts a time in UTC to local time and returns it as a string
-
# according to the given format. The formatting is identical to
-
# Time.strftime and DateTime.strftime, except %Z is replaced with the
-
# timezone abbreviation for the specified time (for example, EST or EDT).
-
1
def strftime(format, utc = Time.now.utc)
-
period = period_for_utc(utc)
-
local = period.to_local(utc)
-
local = Time.at(local).utc unless local.kind_of?(Time) || local.kind_of?(DateTime)
-
abbreviation = period.abbreviation.to_s.gsub(/%/, '%%')
-
-
format = format.gsub(/(.?)%Z/) do
-
if $1 == '%'
-
# return %%Z so the real strftime treats it as a literal %Z too
-
'%%Z'
-
else
-
"#$1#{abbreviation}"
-
end
-
end
-
-
local.strftime(format)
-
end
-
-
# Compares two Timezones based on their identifier. Returns -1 if tz is less
-
# than self, 0 if tz is equal to self and +1 if tz is greater than self.
-
#
-
# Returns nil if tz is not comparable with Timezone instances.
-
1
def <=>(tz)
-
return nil unless tz.is_a?(Timezone)
-
identifier <=> tz.identifier
-
end
-
-
# Returns true if and only if the identifier of tz is equal to the
-
# identifier of this Timezone.
-
1
def eql?(tz)
-
self == tz
-
end
-
-
# Returns a hash of this Timezone.
-
1
def hash
-
identifier.hash
-
end
-
-
# Dumps this Timezone for marshalling.
-
1
def _dump(limit)
-
identifier
-
end
-
-
# Loads a marshalled Timezone.
-
1
def self._load(data)
-
Timezone.get(data)
-
end
-
-
1
private
-
# Initializes @@loaded_zones.
-
1
def self.init_loaded_zones
-
1
@@loaded_zones = ThreadSafe::Cache.new
-
end
-
1
init_loaded_zones
-
-
# Returns an array of proxies corresponding to the given array of
-
# identifiers.
-
1
def self.get_proxies(identifiers)
-
identifiers.collect {|identifier| get_proxy(identifier)}
-
end
-
-
# Returns the current DataSource.
-
1
def self.data_source
-
1
DataSource.get
-
end
-
-
# Raises an UnknownTimezone exception.
-
1
def raise_unknown_timezone
-
raise UnknownTimezone, 'TZInfo::Timezone constructed directly'
-
end
-
end
-
end
-
1
module TZInfo
-
-
# TimezoneDefinition is included into Timezone definition modules.
-
# TimezoneDefinition provides the methods for defining timezones.
-
#
-
# @private
-
1
module TimezoneDefinition #:nodoc:
-
# Add class methods to the includee.
-
1
def self.append_features(base)
-
super
-
base.extend(ClassMethods)
-
end
-
-
# Class methods for inclusion.
-
#
-
# @private
-
1
module ClassMethods #:nodoc:
-
# Returns and yields a TransitionDataTimezoneInfo object to define a
-
# timezone.
-
1
def timezone(identifier)
-
yield @timezone = TransitionDataTimezoneInfo.new(identifier)
-
end
-
-
# Defines a linked timezone.
-
1
def linked_timezone(identifier, link_to_identifier)
-
@timezone = LinkedTimezoneInfo.new(identifier, link_to_identifier)
-
end
-
-
# Returns the last TimezoneInfo to be defined with timezone or
-
# linked_timezone.
-
1
def get
-
@timezone
-
end
-
end
-
end
-
end
-
1
module TZInfo
-
# The timezone index file includes TimezoneIndexDefinition which provides
-
# methods used to define timezones in the index.
-
#
-
# @private
-
1
module TimezoneIndexDefinition #:nodoc:
-
# Add class methods to the includee and initialize class instance variables.
-
1
def self.append_features(base)
-
super
-
base.extend(ClassMethods)
-
base.instance_eval do
-
@timezones = []
-
@data_timezones = []
-
@linked_timezones = []
-
end
-
end
-
-
# Class methods for inclusion.
-
#
-
# @private
-
1
module ClassMethods #:nodoc:
-
# Defines a timezone based on data.
-
1
def timezone(identifier)
-
@timezones << identifier
-
@data_timezones << identifier
-
end
-
-
# Defines a timezone which is a link to another timezone.
-
1
def linked_timezone(identifier)
-
@timezones << identifier
-
@linked_timezones << identifier
-
end
-
-
# Returns a frozen array containing the identifiers of all the timezones.
-
# Identifiers appear in the order they were defined in the index.
-
1
def timezones
-
@timezones.freeze
-
end
-
-
# Returns a frozen array containing the identifiers of all data timezones.
-
# Identifiers appear in the order they were defined in the index.
-
1
def data_timezones
-
@data_timezones.freeze
-
end
-
-
# Returns a frozen array containing the identifiers of all linked
-
# timezones. Identifiers appear in the order they were defined in
-
# the index.
-
1
def linked_timezones
-
@linked_timezones.freeze
-
end
-
end
-
end
-
end
-
1
module TZInfo
-
# Represents a timezone defined by a data source.
-
1
class TimezoneInfo
-
-
# The timezone identifier.
-
1
attr_reader :identifier
-
-
# Constructs a new TimezoneInfo with an identifier.
-
1
def initialize(identifier)
-
1
@identifier = identifier
-
end
-
-
# Returns internal object state as a programmer-readable string.
-
1
def inspect
-
"#<#{self.class}: #@identifier>"
-
end
-
-
# Constructs a Timezone instance for the timezone represented by this
-
# TimezoneInfo.
-
1
def create_timezone
-
raise_not_implemented('create_timezone')
-
end
-
-
1
private
-
-
1
def raise_not_implemented(method_name)
-
raise NotImplementedError, "Subclasses must override #{method_name}"
-
end
-
end
-
end
-
1
module TZInfo
-
# Represents an offset defined in a Timezone data file.
-
1
class TimezoneOffset
-
# The base offset of the timezone from UTC in seconds.
-
1
attr_reader :utc_offset
-
-
# The offset from standard time for the zone in seconds (i.e. non-zero if
-
# daylight savings is being observed).
-
1
attr_reader :std_offset
-
-
# The total offset of this observance from UTC in seconds
-
# (utc_offset + std_offset).
-
1
attr_reader :utc_total_offset
-
-
# The abbreviation that identifies this observance, e.g. "GMT"
-
# (Greenwich Mean Time) or "BST" (British Summer Time) for "Europe/London". The returned identifier is a
-
# symbol.
-
1
attr_reader :abbreviation
-
-
# Constructs a new TimezoneOffset. utc_offset and std_offset are specified
-
# in seconds.
-
1
def initialize(utc_offset, std_offset, abbreviation)
-
1
@utc_offset = utc_offset
-
1
@std_offset = std_offset
-
1
@abbreviation = abbreviation
-
-
1
@utc_total_offset = @utc_offset + @std_offset
-
end
-
-
# True if std_offset is non-zero.
-
1
def dst?
-
@std_offset != 0
-
end
-
-
# Converts a UTC Time, DateTime or integer timestamp to local time, based on
-
# the offset of this period.
-
1
def to_local(utc)
-
12
TimeOrDateTime.wrap(utc) {|wrapped|
-
12
wrapped + @utc_total_offset
-
}
-
end
-
-
# Converts a local Time, DateTime or integer timestamp to UTC, based on the
-
# offset of this period.
-
1
def to_utc(local)
-
TimeOrDateTime.wrap(local) {|wrapped|
-
wrapped - @utc_total_offset
-
}
-
end
-
-
# Returns true if and only if toi has the same utc_offset, std_offset
-
# and abbreviation as this TimezoneOffset.
-
1
def ==(toi)
-
toi.kind_of?(TimezoneOffset) &&
-
utc_offset == toi.utc_offset && std_offset == toi.std_offset && abbreviation == toi.abbreviation
-
end
-
-
# Returns true if and only if toi has the same utc_offset, std_offset
-
# and abbreviation as this TimezoneOffset.
-
1
def eql?(toi)
-
self == toi
-
end
-
-
# Returns a hash of this TimezoneOffset.
-
1
def hash
-
utc_offset.hash ^ std_offset.hash ^ abbreviation.hash
-
end
-
-
# Returns internal object state as a programmer-readable string.
-
1
def inspect
-
"#<#{self.class}: #@utc_offset,#@std_offset,#@abbreviation>"
-
end
-
end
-
end
-
1
module TZInfo
-
# A period of time in a timezone where the same offset from UTC applies.
-
#
-
# All the methods that take times accept instances of Time or DateTime as well
-
# as Integer timestamps.
-
1
class TimezonePeriod
-
# The TimezoneTransition that defines the start of this TimezonePeriod
-
# (may be nil if unbounded).
-
1
attr_reader :start_transition
-
-
# The TimezoneTransition that defines the end of this TimezonePeriod
-
# (may be nil if unbounded).
-
1
attr_reader :end_transition
-
-
# The TimezoneOffset for this period.
-
1
attr_reader :offset
-
-
# Initializes a new TimezonePeriod.
-
#
-
# TimezonePeriod instances should not normally be constructed manually.
-
1
def initialize(start_transition, end_transition, offset = nil)
-
13
@start_transition = start_transition
-
13
@end_transition = end_transition
-
-
13
if offset
-
13
raise ArgumentError, 'Offset specified with transitions' if @start_transition || @end_transition
-
13
@offset = offset
-
else
-
if @start_transition
-
@offset = @start_transition.offset
-
elsif @end_transition
-
@offset = @end_transition.previous_offset
-
else
-
raise ArgumentError, 'No offset specified and no transitions to determine it from'
-
end
-
end
-
-
13
@utc_total_offset_rational = nil
-
end
-
-
# Base offset of the timezone from UTC (seconds).
-
1
def utc_offset
-
1
@offset.utc_offset
-
end
-
-
# Offset from the local time where daylight savings is in effect (seconds).
-
# E.g.: utc_offset could be -5 hours. Normally, std_offset would be 0.
-
# During daylight savings, std_offset would typically become +1 hours.
-
1
def std_offset
-
@offset.std_offset
-
end
-
-
# The identifier of this period, e.g. "GMT" (Greenwich Mean Time) or "BST"
-
# (British Summer Time) for "Europe/London". The returned identifier is a
-
# symbol.
-
1
def abbreviation
-
@offset.abbreviation
-
end
-
1
alias :zone_identifier :abbreviation
-
-
# Total offset from UTC (seconds). Equal to utc_offset + std_offset.
-
1
def utc_total_offset
-
@offset.utc_total_offset
-
end
-
-
# Total offset from UTC (days). Result is a Rational.
-
1
def utc_total_offset_rational
-
# Thread-safety: It is possible that the value of
-
# @utc_total_offset_rational may be calculated multiple times in
-
# concurrently executing threads. It is not worth the overhead of locking
-
# to ensure that @zone_identifiers is only calculated once.
-
-
unless @utc_total_offset_rational
-
@utc_total_offset_rational = OffsetRationals.rational_for_offset(utc_total_offset)
-
end
-
@utc_total_offset_rational
-
end
-
-
# The start time of the period in UTC as a DateTime. May be nil if unbounded.
-
1
def utc_start
-
@start_transition ? @start_transition.at.to_datetime : nil
-
end
-
-
# The start time of the period in UTC as a Time. May be nil if unbounded.
-
1
def utc_start_time
-
@start_transition ? @start_transition.at.to_time : nil
-
end
-
-
# The end time of the period in UTC as a DateTime. May be nil if unbounded.
-
1
def utc_end
-
@end_transition ? @end_transition.at.to_datetime : nil
-
end
-
-
# The end time of the period in UTC as a Time. May be nil if unbounded.
-
1
def utc_end_time
-
@end_transition ? @end_transition.at.to_time : nil
-
end
-
-
# The start time of the period in local time as a DateTime. May be nil if
-
# unbounded.
-
1
def local_start
-
@start_transition ? @start_transition.local_start_at.to_datetime : nil
-
end
-
-
# The start time of the period in local time as a Time. May be nil if
-
# unbounded.
-
1
def local_start_time
-
@start_transition ? @start_transition.local_start_at.to_time : nil
-
end
-
-
# The end time of the period in local time as a DateTime. May be nil if
-
# unbounded.
-
1
def local_end
-
@end_transition ? @end_transition.local_end_at.to_datetime : nil
-
end
-
-
# The end time of the period in local time as a Time. May be nil if
-
# unbounded.
-
1
def local_end_time
-
@end_transition ? @end_transition.local_end_at.to_time : nil
-
end
-
-
# true if daylight savings is in effect for this period; otherwise false.
-
1
def dst?
-
@offset.dst?
-
end
-
-
# true if this period is valid for the given UTC DateTime; otherwise false.
-
1
def valid_for_utc?(utc)
-
utc_after_start?(utc) && utc_before_end?(utc)
-
end
-
-
# true if the given UTC DateTime is after the start of the period
-
# (inclusive); otherwise false.
-
1
def utc_after_start?(utc)
-
!@start_transition || @start_transition.at <= utc
-
end
-
-
# true if the given UTC DateTime is before the end of the period
-
# (exclusive); otherwise false.
-
1
def utc_before_end?(utc)
-
!@end_transition || @end_transition.at > utc
-
end
-
-
# true if this period is valid for the given local DateTime; otherwise false.
-
1
def valid_for_local?(local)
-
local_after_start?(local) && local_before_end?(local)
-
end
-
-
# true if the given local DateTime is after the start of the period
-
# (inclusive); otherwise false.
-
1
def local_after_start?(local)
-
!@start_transition || @start_transition.local_start_at <= local
-
end
-
-
# true if the given local DateTime is before the end of the period
-
# (exclusive); otherwise false.
-
1
def local_before_end?(local)
-
!@end_transition || @end_transition.local_end_at > local
-
end
-
-
# Converts a UTC DateTime to local time based on the offset of this period.
-
1
def to_local(utc)
-
12
@offset.to_local(utc)
-
end
-
-
# Converts a local DateTime to UTC based on the offset of this period.
-
1
def to_utc(local)
-
@offset.to_utc(local)
-
end
-
-
# Returns true if this TimezonePeriod is equal to p. This compares the
-
# start_transition, end_transition and offset using ==.
-
1
def ==(p)
-
p.kind_of?(TimezonePeriod) &&
-
start_transition == p.start_transition &&
-
end_transition == p.end_transition &&
-
offset == p.offset
-
end
-
-
# Returns true if this TimezonePeriods is equal to p. This compares the
-
# start_transition, end_transition and offset using eql?
-
1
def eql?(p)
-
p.kind_of?(TimezonePeriod) &&
-
start_transition.eql?(p.start_transition) &&
-
end_transition.eql?(p.end_transition) &&
-
offset.eql?(p.offset)
-
end
-
-
# Returns a hash of this TimezonePeriod.
-
1
def hash
-
result = @start_transition.hash ^ @end_transition.hash
-
result ^= @offset.hash unless @start_transition || @end_transition
-
result
-
end
-
-
# Returns internal object state as a programmer-readable string.
-
1
def inspect
-
result = "#<#{self.class}: #{@start_transition.inspect},#{@end_transition.inspect}"
-
result << ",#{@offset.inspect}>" unless @start_transition || @end_transition
-
result + '>'
-
end
-
end
-
end
-
1
module TZInfo
-
-
# A proxy class representing a timezone with a given identifier. TimezoneProxy
-
# inherits from Timezone and can be treated like any Timezone loaded with
-
# Timezone.get.
-
#
-
# The first time an attempt is made to access the data for the timezone, the
-
# real Timezone is loaded. If the proxy's identifier was not valid, then an
-
# exception will be raised at this point.
-
1
class TimezoneProxy < Timezone
-
# Construct a new TimezoneProxy for the given identifier. The identifier
-
# is not checked when constructing the proxy. It will be validated on the
-
# when the real Timezone is loaded.
-
1
def self.new(identifier)
-
# Need to override new to undo the behaviour introduced in Timezone#new.
-
1
tzp = super()
-
1
tzp.send(:setup, identifier)
-
1
tzp
-
end
-
-
# The identifier of the timezone, e.g. "Europe/Paris".
-
1
def identifier
-
@real_timezone ? @real_timezone.identifier : @identifier
-
end
-
-
# Returns the TimezonePeriod for the given UTC time. utc can either be
-
# a DateTime, Time or integer timestamp (Time.to_i). Any timezone
-
# information in utc is ignored (it is treated as a UTC time).
-
1
def period_for_utc(utc)
-
13
real_timezone.period_for_utc(utc)
-
end
-
-
# Returns the set of TimezonePeriod instances that are valid for the given
-
# local time as an array. If you just want a single period, use
-
# period_for_local instead and specify how abiguities should be resolved.
-
# Returns an empty array if no periods are found for the given time.
-
1
def periods_for_local(local)
-
real_timezone.periods_for_local(local)
-
end
-
-
# Returns the canonical zone for this Timezone.
-
1
def canonical_zone
-
real_timezone.canonical_zone
-
end
-
-
# Dumps this TimezoneProxy for marshalling.
-
1
def _dump(limit)
-
identifier
-
end
-
-
# Loads a marshalled TimezoneProxy.
-
1
def self._load(data)
-
TimezoneProxy.new(data)
-
end
-
-
1
private
-
1
def setup(identifier)
-
1
@identifier = identifier
-
1
@real_timezone = nil
-
end
-
-
1
def real_timezone
-
# Thread-safety: It is possible that the value of @real_timezone may be
-
# calculated multiple times in concurrently executing threads. It is not
-
# worth the overhead of locking to ensure that @real_timezone is only
-
# calculated once.
-
13
@real_timezone ||= Timezone.get(@identifier)
-
end
-
end
-
end
-
1
module TZInfo
-
# Represents a transition from one timezone offset to another at a particular
-
# date and time.
-
1
class TimezoneTransition
-
# The offset this transition changes to (a TimezoneOffset instance).
-
1
attr_reader :offset
-
-
# The offset this transition changes from (a TimezoneOffset instance).
-
1
attr_reader :previous_offset
-
-
# Initializes a new TimezoneTransition.
-
#
-
# TimezoneTransition instances should not normally be constructed manually.
-
1
def initialize(offset, previous_offset)
-
@offset = offset
-
@previous_offset = previous_offset
-
@local_end_at = nil
-
@local_start_at = nil
-
end
-
-
# A TimeOrDateTime instance representing the UTC time when this transition
-
# occurs.
-
1
def at
-
raise_not_implemented('at')
-
end
-
-
# The UTC time when this transition occurs, returned as a DateTime instance.
-
1
def datetime
-
at.to_datetime
-
end
-
-
# The UTC time when this transition occurs, returned as a Time instance.
-
1
def time
-
at.to_time
-
end
-
-
# A TimeOrDateTime instance representing the local time when this transition
-
# causes the previous observance to end (calculated from at using
-
# previous_offset).
-
1
def local_end_at
-
# Thread-safety: It is possible that the value of @local_end_at may be
-
# calculated multiple times in concurrently executing threads. It is not
-
# worth the overhead of locking to ensure that @local_end_at is only
-
# calculated once.
-
-
@local_end_at = at.add_with_convert(@previous_offset.utc_total_offset) unless @local_end_at
-
@local_end_at
-
end
-
-
# The local time when this transition causes the previous observance to end,
-
# returned as a DateTime instance.
-
1
def local_end
-
local_end_at.to_datetime
-
end
-
-
# The local time when this transition causes the previous observance to end,
-
# returned as a Time instance.
-
1
def local_end_time
-
local_end_at.to_time
-
end
-
-
# A TimeOrDateTime instance representing the local time when this transition
-
# causes the next observance to start (calculated from at using offset).
-
1
def local_start_at
-
# Thread-safety: It is possible that the value of @local_start_at may be
-
# calculated multiple times in concurrently executing threads. It is not
-
# worth the overhead of locking to ensure that @local_start_at is only
-
# calculated once.
-
-
@local_start_at = at.add_with_convert(@offset.utc_total_offset) unless @local_start_at
-
@local_start_at
-
end
-
-
# The local time when this transition causes the next observance to start,
-
# returned as a DateTime instance.
-
1
def local_start
-
local_start_at.to_datetime
-
end
-
-
# The local time when this transition causes the next observance to start,
-
# returned as a Time instance.
-
1
def local_start_time
-
local_start_at.to_time
-
end
-
-
# Returns true if this TimezoneTransition is equal to the given
-
# TimezoneTransition. Two TimezoneTransition instances are
-
# considered to be equal by == if offset, previous_offset and at are all
-
# equal.
-
1
def ==(tti)
-
tti.kind_of?(TimezoneTransition) &&
-
offset == tti.offset && previous_offset == tti.previous_offset && at == tti.at
-
end
-
-
# Returns true if this TimezoneTransition is equal to the given
-
# TimezoneTransition. Two TimezoneTransition instances are
-
# considered to be equal by eql? if offset, previous_offset and at are all
-
# equal and the type used to define at in both instances is the same.
-
1
def eql?(tti)
-
tti.kind_of?(TimezoneTransition) &&
-
offset == tti.offset && previous_offset == tti.previous_offset && at.eql?(tti.at)
-
end
-
-
# Returns a hash of this TimezoneTransition instance.
-
1
def hash
-
@offset.hash ^ @previous_offset.hash ^ at.hash
-
end
-
-
# Returns internal object state as a programmer-readable string.
-
1
def inspect
-
"#<#{self.class}: #{at.inspect},#{@offset.inspect}>"
-
end
-
-
1
private
-
-
1
def raise_not_implemented(method_name)
-
raise NotImplementedError, "Subclasses must override #{method_name}"
-
end
-
end
-
end
-
1
module TZInfo
-
# A TimezoneTransition defined by as integer timestamp, as a rational to
-
# create a DateTime or as both.
-
#
-
# @private
-
1
class TimezoneTransitionDefinition < TimezoneTransition #:nodoc:
-
# The numerator of the DateTime if the transition time is defined as a
-
# DateTime, otherwise the transition time as a timestamp.
-
1
attr_reader :numerator_or_time
-
1
protected :numerator_or_time
-
-
# Either the denominator of the DateTime if the transition time is defined
-
# as a DateTime, otherwise nil.
-
1
attr_reader :denominator
-
1
protected :denominator
-
-
# Creates a new TimezoneTransitionDefinition with the given offset,
-
# previous_offset (both TimezoneOffset instances) and UTC time.
-
#
-
# The time can be specified as a timestamp, as a rational to create a
-
# DateTime, or as both.
-
#
-
# If both a timestamp and rational are given, then the rational will only
-
# be used if the timestamp falls outside of the range of Time on the
-
# platform being used at runtime.
-
#
-
# DateTimes are created from the rational as follows:
-
#
-
# RubyCoreSupport.datetime_new!(RubyCoreSupport.rational_new!(numerator, denominator), 0, Date::ITALY)
-
#
-
# For performance reasons, the numerator and denominator must be specified
-
# in their lowest form.
-
1
def initialize(offset, previous_offset, numerator_or_timestamp, denominator_or_numerator = nil, denominator = nil)
-
super(offset, previous_offset)
-
-
if denominator
-
numerator = denominator_or_numerator
-
timestamp = numerator_or_timestamp
-
elsif denominator_or_numerator
-
numerator = numerator_or_timestamp
-
denominator = denominator_or_numerator
-
timestamp = nil
-
else
-
numerator = nil
-
denominator = nil
-
timestamp = numerator_or_timestamp
-
end
-
-
# Determine whether to use the timestamp or the numerator and denominator.
-
if numerator && (
-
!timestamp ||
-
(timestamp < 0 && !RubyCoreSupport.time_supports_negative) ||
-
((timestamp < -2147483648 || timestamp > 2147483647) && !RubyCoreSupport.time_supports_64bit)
-
)
-
-
@numerator_or_time = numerator
-
@denominator = denominator
-
else
-
@numerator_or_time = timestamp
-
@denominator = nil
-
end
-
-
@at = nil
-
end
-
-
# A TimeOrDateTime instance representing the UTC time when this transition
-
# occurs.
-
1
def at
-
# Thread-safety: It is possible that the value of @at may be calculated
-
# multiple times in concurrently executing threads. It is not worth the
-
# overhead of locking to ensure that @at is only calculated once.
-
-
unless @at
-
unless @denominator
-
@at = TimeOrDateTime.new(@numerator_or_time)
-
else
-
r = RubyCoreSupport.rational_new!(@numerator_or_time, @denominator)
-
dt = RubyCoreSupport.datetime_new!(r, 0, Date::ITALY)
-
@at = TimeOrDateTime.new(dt)
-
end
-
end
-
-
@at
-
end
-
-
# Returns true if this TimezoneTransitionDefinition is equal to the given
-
# TimezoneTransitionDefinition. Two TimezoneTransitionDefinition instances
-
# are considered to be equal by eql? if offset, previous_offset,
-
# numerator_or_time and denominator are all equal.
-
1
def eql?(tti)
-
tti.kind_of?(TimezoneTransitionDefinition) &&
-
offset == tti.offset && previous_offset == tti.previous_offset &&
-
numerator_or_time == tti.numerator_or_time && denominator == tti.denominator
-
end
-
-
# Returns a hash of this TimezoneTransitionDefinition instance.
-
1
def hash
-
@offset.hash ^ @previous_offset.hash ^ @numerator_or_time.hash ^ @denominator.hash
-
end
-
end
-
end
-
1
module TZInfo
-
# Raised if no offsets have been defined when calling period_for_utc or
-
# periods_for_local. Indicates an error in the timezone data.
-
1
class NoOffsetsDefined < StandardError
-
end
-
-
# Represents a data timezone defined by a set of offsets and a set
-
# of transitions.
-
#
-
# @private
-
1
class TransitionDataTimezoneInfo < DataTimezoneInfo #:nodoc:
-
-
# Constructs a new TransitionDataTimezoneInfo with its identifier.
-
1
def initialize(identifier)
-
1
super(identifier)
-
1
@offsets = {}
-
1
@transitions = []
-
1
@previous_offset = nil
-
1
@transitions_index = nil
-
end
-
-
# Defines a offset. The id uniquely identifies this offset within the
-
# timezone. utc_offset and std_offset define the offset in seconds of
-
# standard time from UTC and daylight savings from standard time
-
# respectively. abbreviation describes the timezone offset (e.g. GMT, BST,
-
# EST or EDT).
-
#
-
# The first offset to be defined is treated as the offset that applies
-
# until the first transition. This will usually be in Local Mean Time (LMT).
-
#
-
# ArgumentError will be raised if the id is already defined.
-
1
def offset(id, utc_offset, std_offset, abbreviation)
-
1
raise ArgumentError, 'Offset already defined' if @offsets.has_key?(id)
-
-
1
offset = TimezoneOffset.new(utc_offset, std_offset, abbreviation)
-
1
@offsets[id] = offset
-
1
@previous_offset = offset unless @previous_offset
-
end
-
-
# Defines a transition. Transitions must be defined in chronological order.
-
# ArgumentError will be raised if a transition is added out of order.
-
# offset_id refers to an id defined with offset. ArgumentError will be
-
# raised if the offset_id cannot be found. numerator_or_time and
-
# denomiator specify the time the transition occurs as. See
-
# TimezoneTransition for more detail about specifying times.
-
1
def transition(year, month, offset_id, numerator_or_timestamp, denominator_or_numerator = nil, denominator = nil)
-
offset = @offsets[offset_id]
-
raise ArgumentError, 'Offset not found' unless offset
-
-
if @transitions_index
-
if year < @last_year || (year == @last_year && month < @last_month)
-
raise ArgumentError, 'Transitions must be increasing date order'
-
end
-
-
# Record the position of the first transition with this index.
-
index = transition_index(year, month)
-
@transitions_index[index] ||= @transitions.length
-
-
# Fill in any gaps
-
(index - 1).downto(0) do |i|
-
break if @transitions_index[i]
-
@transitions_index[i] = @transitions.length
-
end
-
else
-
@transitions_index = [@transitions.length]
-
@start_year = year
-
@start_month = month
-
end
-
-
@transitions << TimezoneTransitionDefinition.new(offset, @previous_offset,
-
numerator_or_timestamp, denominator_or_numerator, denominator)
-
@last_year = year
-
@last_month = month
-
@previous_offset = offset
-
end
-
-
# Returns the TimezonePeriod for the given UTC time.
-
# Raises NoOffsetsDefined if no offsets have been defined.
-
1
def period_for_utc(utc)
-
13
unless @transitions.empty?
-
utc = TimeOrDateTime.wrap(utc)
-
index = transition_index(utc.year, utc.mon)
-
-
start_transition = nil
-
start = transition_before_end(index)
-
if start
-
start.downto(0) do |i|
-
if @transitions[i].at <= utc
-
start_transition = @transitions[i]
-
break
-
end
-
end
-
end
-
-
end_transition = nil
-
start = transition_after_start(index)
-
if start
-
start.upto(@transitions.length - 1) do |i|
-
if @transitions[i].at > utc
-
end_transition = @transitions[i]
-
break
-
end
-
end
-
end
-
-
if start_transition || end_transition
-
TimezonePeriod.new(start_transition, end_transition)
-
else
-
# Won't happen since there are transitions. Must always find one
-
# transition that is either >= or < the specified time.
-
raise 'No transitions found in search'
-
end
-
else
-
13
raise NoOffsetsDefined, 'No offsets have been defined' unless @previous_offset
-
13
TimezonePeriod.new(nil, nil, @previous_offset)
-
end
-
end
-
-
# Returns the set of TimezonePeriods for the given local time as an array.
-
# Results returned are ordered by increasing UTC start date.
-
# Returns an empty array if no periods are found for the given time.
-
# Raises NoOffsetsDefined if no offsets have been defined.
-
1
def periods_for_local(local)
-
unless @transitions.empty?
-
local = TimeOrDateTime.wrap(local)
-
index = transition_index(local.year, local.mon)
-
-
result = []
-
-
start_index = transition_after_start(index - 1)
-
if start_index && @transitions[start_index].local_end_at > local
-
if start_index > 0
-
if @transitions[start_index - 1].local_start_at <= local
-
result << TimezonePeriod.new(@transitions[start_index - 1], @transitions[start_index])
-
end
-
else
-
result << TimezonePeriod.new(nil, @transitions[start_index])
-
end
-
end
-
-
end_index = transition_before_end(index + 1)
-
-
if end_index
-
start_index = end_index unless start_index
-
-
start_index.upto(transition_before_end(index + 1)) do |i|
-
if @transitions[i].local_start_at <= local
-
if i + 1 < @transitions.length
-
if @transitions[i + 1].local_end_at > local
-
result << TimezonePeriod.new(@transitions[i], @transitions[i + 1])
-
end
-
else
-
result << TimezonePeriod.new(@transitions[i], nil)
-
end
-
end
-
end
-
end
-
-
result
-
else
-
raise NoOffsetsDefined, 'No offsets have been defined' unless @previous_offset
-
[TimezonePeriod.new(nil, nil, @previous_offset)]
-
end
-
end
-
-
# Returns an Array of TimezoneTransition instances representing the times
-
# where the UTC offset of the timezone changes.
-
#
-
# Transitions are returned up to a given date and time up to a given date
-
# and time, specified in UTC (utc_to).
-
#
-
# A from date and time may also be supplied using the utc_from parameter
-
# (also specified in UTC). If utc_from is not nil, only transitions from
-
# that date and time onwards will be returned.
-
#
-
# Comparisons with utc_to are exclusive. Comparisons with utc_from are
-
# inclusive. If a transition falls precisely on utc_to, it will be excluded.
-
# If a transition falls on utc_from, it will be included.
-
#
-
# Transitions returned are ordered by when they occur, from earliest to
-
# latest.
-
#
-
# utc_to and utc_from can be specified using either DateTime, Time or
-
# integer timestamps (Time.to_i).
-
#
-
# If utc_from is specified and utc_to is not greater than utc_from, then
-
# transitions_up_to raises an ArgumentError exception.
-
1
def transitions_up_to(utc_to, utc_from = nil)
-
utc_to = TimeOrDateTime.wrap(utc_to)
-
utc_from = utc_from ? TimeOrDateTime.wrap(utc_from) : nil
-
-
if utc_from && utc_to <= utc_from
-
raise ArgumentError, 'utc_to must be greater than utc_from'
-
end
-
-
unless @transitions.empty?
-
if utc_from
-
from = transition_after_start(transition_index(utc_from.year, utc_from.mon))
-
-
if from
-
while from < @transitions.length && @transitions[from].at < utc_from
-
from += 1
-
end
-
-
if from >= @transitions.length
-
return []
-
end
-
else
-
# utc_from is later than last transition.
-
return []
-
end
-
else
-
from = 0
-
end
-
-
to = transition_before_end(transition_index(utc_to.year, utc_to.mon))
-
-
if to
-
while to >= 0 && @transitions[to].at >= utc_to
-
to -= 1
-
end
-
-
if to < 0
-
return []
-
end
-
else
-
# utc_to is earlier than first transition.
-
return []
-
end
-
-
@transitions[from..to]
-
else
-
[]
-
end
-
end
-
-
1
private
-
# Returns the index into the @transitions_index array for a given year
-
# and month.
-
1
def transition_index(year, month)
-
index = (year - @start_year) * 2
-
index += 1 if month > 6
-
index -= 1 if @start_month > 6
-
index
-
end
-
-
# Returns the index into @transitions of the first transition that occurs
-
# on or after the start of the given index into @transitions_index.
-
# Returns nil if there are no such transitions.
-
1
def transition_after_start(index)
-
if index >= @transitions_index.length
-
nil
-
else
-
index = 0 if index < 0
-
@transitions_index[index]
-
end
-
end
-
-
# Returns the index into @transitions of the first transition that occurs
-
# before the end of the given index into @transitions_index.
-
# Returns nil if there are no such transitions.
-
1
def transition_before_end(index)
-
index = index + 1
-
-
if index <= 0
-
nil
-
elsif index >= @transitions_index.length
-
@transitions.length - 1
-
else
-
@transitions_index[index] - 1
-
end
-
end
-
end
-
end
-
1
module TZInfo
-
# Represents information about a country returned by ZoneinfoDataSource.
-
#
-
# @private
-
1
class ZoneinfoCountryInfo < CountryInfo #:nodoc:
-
# Constructs a new CountryInfo with an ISO 3166 country code, name and
-
# an array of CountryTimezones.
-
1
def initialize(code, name, zones)
-
249
super(code, name)
-
249
@zones = zones.dup.freeze
-
249
@zone_identifiers = nil
-
end
-
-
# Returns a frozen array of all the zone identifiers for the country ordered
-
# geographically, most populous first.
-
1
def zone_identifiers
-
# Thread-safety: It is possible that the value of @zone_identifiers may be
-
# calculated multiple times in concurrently executing threads. It is not
-
# worth the overhead of locking to ensure that @zone_identifiers is only
-
# calculated once.
-
-
unless @zone_identifiers
-
@zone_identifiers = zones.collect {|zone| zone.identifier}.freeze
-
end
-
-
@zone_identifiers
-
end
-
-
# Returns a frozen array of all the timezones for the for the country
-
# ordered geographically, most populous first.
-
1
def zones
-
@zones
-
end
-
end
-
end
-
1
module TZInfo
-
# An InvalidZoneinfoDirectory exception is raised if the DataSource is
-
# set to a specific zoneinfo path, which is not a valid zoneinfo directory
-
# (i.e. a directory containing index files named iso3166.tab and zone.tab
-
# as well as other timezone files).
-
1
class InvalidZoneinfoDirectory < StandardError
-
end
-
-
# A ZoneinfoDirectoryNotFound exception is raised if no valid zoneinfo
-
# directory could be found when checking the paths listed in
-
# ZoneinfoDataSource.search_path. A valid zoneinfo directory is one that
-
# contains timezone files, a country code index file named iso3166.tab and a
-
# timezone index file named zone1970.tab or zone.tab.
-
1
class ZoneinfoDirectoryNotFound < StandardError
-
end
-
-
# A DataSource that loads data from a 'zoneinfo' directory containing
-
# compiled "TZif" version 3 (or earlier) files in addition to iso3166.tab and
-
# zone1970.tab or zone.tab index files.
-
#
-
# To have TZInfo load the system zoneinfo files, call TZInfo::DataSource.set
-
# as follows:
-
#
-
# TZInfo::DataSource.set(:zoneinfo)
-
#
-
# To load zoneinfo files from a particular directory, pass the directory to
-
# TZInfo::DataSource.set:
-
#
-
# TZInfo::DataSource.set(:zoneinfo, directory)
-
#
-
# Note that the platform used at runtime may limit the range of available
-
# transition data that can be loaded from zoneinfo files. There are two
-
# factors to consider:
-
#
-
# First of all, the zoneinfo support in TZInfo makes use of Ruby's Time class.
-
# On 32-bit builds of Ruby 1.8, the Time class only supports 32-bit
-
# timestamps. This means that only Times between 1901-12-13 20:45:52 and
-
# 2038-01-19 03:14:07 can be represented. Furthermore, certain platforms only
-
# allow for positive 32-bit timestamps (notably Windows), making the earliest
-
# representable time 1970-01-01 00:00:00.
-
#
-
# 64-bit builds of Ruby 1.8 and all builds of Ruby 1.9 support 64-bit
-
# timestamps. This means that there is no practical restriction on the range
-
# of the Time class on these platforms.
-
#
-
# TZInfo will only load transitions that fall within the supported range of
-
# the Time class. Any queries performed on times outside of this range may
-
# give inaccurate results.
-
#
-
# The second factor concerns the zoneinfo files. Versions of the 'zic' tool
-
# (used to build zoneinfo files) that were released prior to February 2006
-
# created zoneinfo files that used 32-bit integers for transition timestamps.
-
# Later versions of zic produce zoneinfo files that use 64-bit integers. If
-
# you have 32-bit zoneinfo files on your system, then any queries falling
-
# outside of the range 1901-12-13 20:45:52 to 2038-01-19 03:14:07 may be
-
# inaccurate.
-
#
-
# Most modern platforms include 64-bit zoneinfo files. However, Mac OS X (up
-
# to at least 10.8.4) still uses 32-bit zoneinfo files.
-
#
-
# To check whether your zoneinfo files contain 32-bit or 64-bit transition
-
# data, you can run the following code (substituting the identifier of the
-
# zone you want to test for zone_identifier):
-
#
-
# TZInfo::DataSource.set(:zoneinfo)
-
# dir = TZInfo::DataSource.get.zoneinfo_dir
-
# File.open(File.join(dir, zone_identifier), 'r') {|f| f.read(5) }
-
#
-
# If the last line returns "TZif\\x00", then you have a 32-bit zoneinfo file.
-
# If it returns "TZif2" or "TZif3" then you have a 64-bit zoneinfo file.
-
#
-
# If you require support for 64-bit transitions, but are restricted to 32-bit
-
# zoneinfo support, then you may want to consider using TZInfo::RubyDataSource
-
# instead.
-
1
class ZoneinfoDataSource < DataSource
-
# The default value of ZoneinfoDataSource.search_path.
-
1
DEFAULT_SEARCH_PATH = ['/usr/share/zoneinfo', '/usr/share/lib/zoneinfo', '/etc/zoneinfo'].freeze
-
-
# The default value of ZoneinfoDataSource.alternate_iso3166_tab_search_path.
-
1
DEFAULT_ALTERNATE_ISO3166_TAB_SEARCH_PATH = ['/usr/share/misc/iso3166.tab', '/usr/share/misc/iso3166'].freeze
-
-
# Paths to be checked to find the system zoneinfo directory.
-
1
@@search_path = DEFAULT_SEARCH_PATH.dup
-
-
# Paths to possible alternate iso3166.tab files (used to locate the
-
# system-wide iso3166.tab files on FreeBSD and OpenBSD).
-
1
@@alternate_iso3166_tab_search_path = DEFAULT_ALTERNATE_ISO3166_TAB_SEARCH_PATH.dup
-
-
# An Array of directories that will be checked to find the system zoneinfo
-
# directory.
-
#
-
# Directories are checked in the order they appear in the Array.
-
#
-
# The default value is ['/usr/share/zoneinfo', '/usr/share/lib/zoneinfo', '/etc/zoneinfo'].
-
1
def self.search_path
-
1
@@search_path
-
end
-
-
# Sets the directories to be checked when locating the system zoneinfo
-
# directory.
-
#
-
# Can be set to an Array of directories or a String containing directories
-
# separated with File::PATH_SEPARATOR.
-
#
-
# Directories are checked in the order they appear in the Array or String.
-
#
-
# Set to nil to revert to the default paths.
-
1
def self.search_path=(search_path)
-
@@search_path = process_search_path(search_path, DEFAULT_SEARCH_PATH)
-
end
-
-
# An Array of paths that will be checked to find an alternate iso3166.tab
-
# file if one was not included in the zoneinfo directory (for example, on
-
# FreeBSD and OpenBSD systems).
-
#
-
# Paths are checked in the order they appear in the array.
-
#
-
# The default value is ['/usr/share/misc/iso3166.tab', '/usr/share/misc/iso3166'].
-
1
def self.alternate_iso3166_tab_search_path
-
1
@@alternate_iso3166_tab_search_path
-
end
-
-
# Sets the paths to check to locate an alternate iso3166.tab file if one was
-
# not included in the zoneinfo directory.
-
#
-
# Can be set to an Array of directories or a String containing directories
-
# separated with File::PATH_SEPARATOR.
-
#
-
# Paths are checked in the order they appear in the array.
-
#
-
# Set to nil to revert to the default paths.
-
1
def self.alternate_iso3166_tab_search_path=(alternate_iso3166_tab_search_path)
-
@@alternate_iso3166_tab_search_path = process_search_path(alternate_iso3166_tab_search_path, DEFAULT_ALTERNATE_ISO3166_TAB_SEARCH_PATH)
-
end
-
-
# The zoneinfo directory being used.
-
1
attr_reader :zoneinfo_dir
-
-
# Creates a new ZoneinfoDataSource.
-
#
-
# If zoneinfo_dir is specified, it will be checked and used as the source
-
# of zoneinfo files.
-
#
-
# The directory must contain a file named iso3166.tab and a file named
-
# either zone1970.tab or zone.tab. These may either be included in the root
-
# of the directory or in a 'tab' sub-directory and named 'country.tab' and
-
# 'zone_sun.tab' respectively (as is the case on Solaris.
-
#
-
# Additionally, the path to iso3166.tab can be overridden using the
-
# alternate_iso3166_tab_path parameter.
-
#
-
# InvalidZoneinfoDirectory will be raised if the iso3166.tab and
-
# zone1970.tab or zone.tab files cannot be found using the zoneinfo_dir and
-
# alternate_iso3166_tab_path parameters.
-
#
-
# If zoneinfo_dir is not specified or nil, the paths referenced in
-
# search_path are searched in order to find a valid zoneinfo directory
-
# (one that contains zone1970.tab or zone.tab and iso3166.tab files as
-
# above).
-
#
-
# The paths referenced in alternate_iso3166_tab_search_path are also
-
# searched to find an iso3166.tab file if one of the searched zoneinfo
-
# directories doesn't contain an iso3166.tab file.
-
#
-
# If no valid directory can be found by searching, ZoneinfoDirectoryNotFound
-
# will be raised.
-
1
def initialize(zoneinfo_dir = nil, alternate_iso3166_tab_path = nil)
-
1
if zoneinfo_dir
-
iso3166_tab_path, zone_tab_path = validate_zoneinfo_dir(zoneinfo_dir, alternate_iso3166_tab_path)
-
-
unless iso3166_tab_path && zone_tab_path
-
raise InvalidZoneinfoDirectory, "#{zoneinfo_dir} is not a directory or doesn't contain a iso3166.tab file and a zone1970.tab or zone.tab file."
-
end
-
-
@zoneinfo_dir = zoneinfo_dir
-
else
-
1
@zoneinfo_dir, iso3166_tab_path, zone_tab_path = find_zoneinfo_dir
-
-
1
unless @zoneinfo_dir && iso3166_tab_path && zone_tab_path
-
raise ZoneinfoDirectoryNotFound, "None of the paths included in TZInfo::ZoneinfoDataSource.search_path are valid zoneinfo directories."
-
end
-
end
-
-
1
@zoneinfo_dir = File.expand_path(@zoneinfo_dir).freeze
-
1
@timezone_index = load_timezone_index.freeze
-
1
@country_index = load_country_index(iso3166_tab_path, zone_tab_path).freeze
-
end
-
-
# Returns a TimezoneInfo instance for a given identifier.
-
# Raises InvalidTimezoneIdentifier if the timezone is not found or the
-
# identifier is invalid.
-
1
def load_timezone_info(identifier)
-
1
begin
-
1
if @timezone_index.include?(identifier)
-
1
path = File.join(@zoneinfo_dir, identifier)
-
-
# Untaint path rather than identifier. We don't want to modify
-
# identifier. identifier may also be frozen and therefore cannot be
-
# untainted.
-
1
path.untaint
-
-
1
begin
-
1
ZoneinfoTimezoneInfo.new(identifier, path)
-
rescue InvalidZoneinfoFile => e
-
raise InvalidTimezoneIdentifier, e.message
-
end
-
else
-
raise InvalidTimezoneIdentifier, 'Invalid identifier'
-
end
-
rescue Errno::ENOENT, Errno::ENAMETOOLONG, Errno::ENOTDIR
-
raise InvalidTimezoneIdentifier, 'Invalid identifier'
-
rescue Errno::EACCES => e
-
raise InvalidTimezoneIdentifier, e.message
-
end
-
end
-
-
# Returns an array of all the available timezone identifiers.
-
1
def timezone_identifiers
-
@timezone_index
-
end
-
-
# Returns an array of all the available timezone identifiers for
-
# data timezones (i.e. those that actually contain definitions).
-
#
-
# For ZoneinfoDataSource, this will always be identical to
-
# timezone_identifers.
-
1
def data_timezone_identifiers
-
@timezone_index
-
end
-
-
# Returns an array of all the available timezone identifiers that
-
# are links to other timezones.
-
#
-
# For ZoneinfoDataSource, this will always be an empty array.
-
1
def linked_timezone_identifiers
-
[].freeze
-
end
-
-
# Returns a CountryInfo instance for the given ISO 3166-1 alpha-2
-
# country code. Raises InvalidCountryCode if the country could not be found
-
# or the code is invalid.
-
1
def load_country_info(code)
-
info = @country_index[code]
-
raise InvalidCountryCode, 'Invalid country code' unless info
-
info
-
end
-
-
# Returns an array of all the available ISO 3166-1 alpha-2
-
# country codes.
-
1
def country_codes
-
@country_index.keys.freeze
-
end
-
-
# Returns the name and information about this DataSource.
-
1
def to_s
-
"Zoneinfo DataSource: #{@zoneinfo_dir}"
-
end
-
-
# Returns internal object state as a programmer-readable string.
-
1
def inspect
-
"#<#{self.class}: #{@zoneinfo_dir}>"
-
end
-
-
1
private
-
-
# Processes a path for use as the search_path or
-
# alternate_iso3166_tab_search_path.
-
1
def self.process_search_path(path, default)
-
if path
-
if path.kind_of?(String)
-
path.split(File::PATH_SEPARATOR)
-
else
-
path.collect {|p| p.to_s}
-
end
-
else
-
default.dup
-
end
-
end
-
-
# Validates a zoneinfo directory and returns the paths to the iso3166.tab
-
# and zone1970.tab or zone.tab files if valid. If the directory is not
-
# valid, returns nil.
-
#
-
# The path to the iso3166.tab file may be overriden by passing in a path.
-
# This is treated as either absolute or relative to the current working
-
# directory.
-
1
def validate_zoneinfo_dir(path, iso3166_tab_path = nil)
-
1
if File.directory?(path)
-
1
if iso3166_tab_path
-
return nil unless File.file?(iso3166_tab_path)
-
else
-
1
iso3166_tab_path = resolve_tab_path(path, ['iso3166.tab'], 'country.tab')
-
1
return nil unless iso3166_tab_path
-
end
-
-
1
zone_tab_path = resolve_tab_path(path, ['zone1970.tab', 'zone.tab'], 'zone_sun.tab')
-
1
return nil unless zone_tab_path
-
-
1
[iso3166_tab_path, zone_tab_path]
-
else
-
nil
-
end
-
end
-
-
# Attempts to resolve the path to a tab file given its standard names and
-
# tab sub-directory name (as used on Solaris).
-
1
def resolve_tab_path(zoneinfo_path, standard_names, tab_name)
-
2
standard_names.each do |standard_name|
-
3
path = File.join(zoneinfo_path, standard_name)
-
3
return path if File.file?(path)
-
end
-
-
path = File.join(zoneinfo_path, 'tab', tab_name)
-
return path if File.file?(path)
-
-
nil
-
end
-
-
# Finds a zoneinfo directory using search_path and
-
# alternate_iso3166_tab_search_path. Returns the paths to the directory,
-
# the iso3166.tab file and the zone.tab file or nil if not found.
-
1
def find_zoneinfo_dir
-
1
alternate_iso3166_tab_path = self.class.alternate_iso3166_tab_search_path.detect do |path|
-
2
File.file?(path)
-
end
-
-
1
self.class.search_path.each do |path|
-
# Try without the alternate_iso3166_tab_path first.
-
1
iso3166_tab_path, zone_tab_path = validate_zoneinfo_dir(path)
-
1
return path, iso3166_tab_path, zone_tab_path if iso3166_tab_path && zone_tab_path
-
-
if alternate_iso3166_tab_path
-
iso3166_tab_path, zone_tab_path = validate_zoneinfo_dir(path, alternate_iso3166_tab_path)
-
return path, iso3166_tab_path, zone_tab_path if iso3166_tab_path && zone_tab_path
-
end
-
end
-
-
# Not found.
-
nil
-
end
-
-
# Scans @zoneinfo_dir and returns an Array of available timezone
-
# identifiers.
-
1
def load_timezone_index
-
1
index = []
-
-
# Ignoring particular files:
-
# +VERSION is included on Mac OS X.
-
# localtime current local timezone (may be a link).
-
# posix, posixrules and right are directories containing other versions of the zoneinfo files.
-
# src is a directory containing the tzdata source included on Solaris.
-
# Factory is the compiled in default timezone.
-
-
1
enum_timezones(nil, ['+VERSION', 'localtime', 'posix', 'posixrules', 'right', 'src', 'Factory']) do |identifier|
-
581
index << identifier
-
end
-
-
1
index.sort
-
end
-
-
# Recursively scans a directory of timezones, calling the passed in block
-
# for each identifier found.
-
1
def enum_timezones(dir, exclude = [], &block)
-
21
Dir.foreach(dir ? File.join(@zoneinfo_dir, dir) : @zoneinfo_dir) do |entry|
-
648
unless entry =~ /\./ || exclude.include?(entry)
-
601
entry.untaint
-
601
path = dir ? File.join(dir, entry) : entry
-
601
full_path = File.join(@zoneinfo_dir, path)
-
-
601
if File.directory?(full_path)
-
20
enum_timezones(path, [], &block)
-
elsif File.file?(full_path)
-
581
yield path
-
end
-
end
-
end
-
end
-
-
# Uses the iso3166.tab and zone1970.tab or zone.tab files to build an index
-
# of the available countries and their timezones.
-
1
def load_country_index(iso3166_tab_path, zone_tab_path)
-
-
# Handle standard 3 to 4 column zone.tab files as well as the 4 to 5
-
# column format used by Solaris.
-
#
-
# On Solaris, an extra column before the comment gives an optional
-
# linked/alternate timezone identifier (or '-' if not set).
-
#
-
# Additionally, there is a section at the end of the file for timezones
-
# covering regions. These are given lower-case "country" codes. The timezone
-
# identifier column refers to a continent instead of an identifier. These
-
# lines will be ignored by TZInfo.
-
#
-
# Since the last column is optional in both formats, testing for the
-
# Solaris format is done in two passes. The first pass identifies if there
-
# are any lines using 5 columns.
-
-
-
# The first column is allowed to be a comma separated list of country
-
# codes, as used in zone1970.tab (introduced in tzdata 2014f).
-
#
-
# The first country code in the comma-separated list is the country that
-
# contains the city the zone identifer is based on. The first country
-
# code on each line is considered to be primary with the others
-
# secondary.
-
#
-
# The zones for each country are ordered primary first, then secondary.
-
# Within the primary and secondary groups, the zones are ordered by their
-
# order in the file.
-
-
1
file_is_5_column = false
-
1
zone_tab = []
-
-
1
RubyCoreSupport.open_file(zone_tab_path, 'r', :external_encoding => 'UTF-8', :internal_encoding => 'UTF-8') do |file|
-
1
file.each_line do |line|
-
439
line.chomp!
-
-
439
if line =~ /\A([A-Z]{2}(?:,[A-Z]{2})*)\t(?:([+\-])(\d{2})(\d{2})([+\-])(\d{3})(\d{2})|([+\-])(\d{2})(\d{2})(\d{2})([+\-])(\d{3})(\d{2})(\d{2}))\t([^\t]+)(?:\t([^\t]+))?(?:\t([^\t]+))?\z/
-
415
codes = $1
-
-
415
if $2
-
366
latitude = dms_to_rational($2, $3, $4)
-
366
longitude = dms_to_rational($5, $6, $7)
-
else
-
49
latitude = dms_to_rational($8, $9, $10, $11)
-
49
longitude = dms_to_rational($12, $13, $14, $15)
-
end
-
-
415
zone_identifier = $16
-
415
column4 = $17
-
415
column5 = $18
-
-
415
file_is_5_column = true if column5
-
-
415
zone_tab << [codes.split(','), zone_identifier, latitude, longitude, column4, column5]
-
end
-
end
-
end
-
-
1
primary_zones = {}
-
1
secondary_zones = {}
-
-
1
zone_tab.each do |codes, zone_identifier, latitude, longitude, column4, column5|
-
415
description = file_is_5_column ? column5 : column4
-
415
country_timezone = CountryTimezone.new(zone_identifier, latitude, longitude, description)
-
-
# codes will always have at least one element
-
-
415
(primary_zones[codes.first] ||= []) << country_timezone
-
-
415
codes[1..-1].each do |code|
-
(secondary_zones[code] ||= []) << country_timezone
-
end
-
end
-
-
1
countries = {}
-
-
1
RubyCoreSupport.open_file(iso3166_tab_path, 'r', :external_encoding => 'UTF-8', :internal_encoding => 'UTF-8') do |file|
-
1
file.each_line do |line|
-
275
line.chomp!
-
-
# Handle both the two column alpha-2 and name format used in the tz
-
# database as well as the 4 column alpha-2, alpha-3, numeric-3 and
-
# name format used by FreeBSD and OpenBSD.
-
-
275
if line =~ /\A([A-Z]{2})(?:\t[A-Z]{3}\t[0-9]{3})?\t(.+)\z/
-
249
code = $1
-
249
name = $2
-
249
zones = (primary_zones[code] || []) + (secondary_zones[code] || [])
-
-
249
countries[code] = ZoneinfoCountryInfo.new(code, name, zones)
-
end
-
end
-
end
-
-
1
countries
-
end
-
-
# Converts degrees, minutes and seconds to a Rational.
-
1
def dms_to_rational(sign, degrees, minutes, seconds = nil)
-
830
result = degrees.to_i + Rational(minutes.to_i, 60)
-
830
result += Rational(seconds.to_i, 3600) if seconds
-
830
result = -result if sign == '-'
-
830
result
-
end
-
end
-
end
-
1
module TZInfo
-
# An InvalidZoneinfoFile exception is raised if an attempt is made to load an
-
# invalid zoneinfo file.
-
1
class InvalidZoneinfoFile < StandardError
-
end
-
-
# Represents a timezone defined by a compiled zoneinfo TZif (\0, 2 or 3) file.
-
#
-
# @private
-
1
class ZoneinfoTimezoneInfo < TransitionDataTimezoneInfo #:nodoc:
-
-
# Minimum supported timestamp (inclusive).
-
#
-
# Time.utc(1700, 1, 1).to_i
-
1
MIN_TIMESTAMP = -8520336000
-
-
# Maximum supported timestamp (exclusive).
-
#
-
# Time.utc(2500, 1, 1).to_i
-
1
MAX_TIMESTAMP = 16725225600
-
-
# Constructs the new ZoneinfoTimezoneInfo with an identifier and path
-
# to the file.
-
1
def initialize(identifier, file_path)
-
1
super(identifier)
-
-
1
File.open(file_path, 'rb') do |file|
-
1
parse(file)
-
end
-
end
-
-
1
private
-
# Unpack will return unsigned 32-bit integers. Translate to
-
# signed 32-bit.
-
1
def make_signed_int32(long)
-
1
long >= 0x80000000 ? long - 0x100000000 : long
-
end
-
-
# Unpack will return a 64-bit integer as two unsigned 32-bit integers
-
# (most significant first). Translate to signed 64-bit
-
1
def make_signed_int64(high, low)
-
unsigned = (high << 32) | low
-
unsigned >= 0x8000000000000000 ? unsigned - 0x10000000000000000 : unsigned
-
end
-
-
# Read bytes from file and check that the correct number of bytes could
-
# be read. Raises InvalidZoneinfoFile if the number of bytes didn't match
-
# the number requested.
-
1
def check_read(file, bytes)
-
3
result = file.read(bytes)
-
-
3
unless result && result.length == bytes
-
raise InvalidZoneinfoFile, "Expected #{bytes} bytes reading '#{file.path}', but got #{result ? result.length : 0} bytes"
-
end
-
-
3
result
-
end
-
-
# Zoneinfo doesn't include the offset from standard time (std_offset).
-
# Derive the missing offsets by looking at changes in the total UTC
-
# offset.
-
#
-
# This will be run through forwards and then backwards by the parse
-
# method.
-
1
def derive_offsets(transitions, offsets)
-
2
previous_offset = nil
-
-
2
transitions.each do |t|
-
offset = offsets[t[:offset]]
-
-
if !offset[:std_offset] && offset[:is_dst] && previous_offset
-
difference = offset[:utc_total_offset] - previous_offset[:utc_total_offset]
-
-
if previous_offset[:is_dst]
-
if previous_offset[:std_offset]
-
std_offset = previous_offset[:std_offset] + difference
-
else
-
std_offset = nil
-
end
-
else
-
std_offset = difference
-
end
-
-
if std_offset && std_offset > 0
-
offset[:std_offset] = std_offset
-
offset[:utc_offset] = offset[:utc_total_offset] - std_offset
-
end
-
end
-
-
previous_offset = offset
-
end
-
end
-
-
# Parses a zoneinfo file and intializes the DataTimezoneInfo structures.
-
1
def parse(file)
-
1
magic, version, ttisgmtcnt, ttisstdcnt, leapcnt, timecnt, typecnt, charcnt =
-
check_read(file, 44).unpack('a4 a x15 NNNNNN')
-
-
1
if magic != 'TZif'
-
raise InvalidZoneinfoFile, "The file '#{file.path}' does not start with the expected header."
-
end
-
-
1
if (version == '2' || version == '3') && RubyCoreSupport.time_supports_64bit
-
# Skip the first 32-bit section and read the header of the second 64-bit section
-
file.seek(timecnt * 5 + typecnt * 6 + charcnt + leapcnt * 8 + ttisgmtcnt + ttisstdcnt, IO::SEEK_CUR)
-
-
prev_version = version
-
-
magic, version, ttisgmtcnt, ttisstdcnt, leapcnt, timecnt, typecnt, charcnt =
-
check_read(file, 44).unpack('a4 a x15 NNNNNN')
-
-
unless magic == 'TZif' && (version == prev_version)
-
raise InvalidZoneinfoFile, "The file '#{file.path}' contains an invalid 64-bit section header."
-
end
-
-
using_64bit = true
-
elsif version != '3' && version != '2' && version != "\0"
-
raise InvalidZoneinfoFile, "The file '#{file.path}' contains a version of the zoneinfo format that is not currently supported."
-
else
-
1
using_64bit = false
-
end
-
-
1
unless leapcnt == 0
-
raise InvalidZoneinfoFile, "The zoneinfo file '#{file.path}' contains leap second data. TZInfo requires zoneinfo files that omit leap seconds."
-
end
-
-
1
transitions = []
-
-
1
if using_64bit
-
(0...timecnt).each do |i|
-
high, low = check_read(file, 8).unpack('NN')
-
transition_time = make_signed_int64(high, low)
-
transitions << {:at => transition_time}
-
end
-
else
-
1
(0...timecnt).each do |i|
-
transition_time = make_signed_int32(check_read(file, 4).unpack('N')[0])
-
transitions << {:at => transition_time}
-
end
-
end
-
-
1
(0...timecnt).each do |i|
-
localtime_type = check_read(file, 1).unpack('C')[0]
-
transitions[i][:offset] = localtime_type
-
end
-
-
1
offsets = []
-
-
1
(0...typecnt).each do |i|
-
1
gmtoff, isdst, abbrind = check_read(file, 6).unpack('NCC')
-
1
gmtoff = make_signed_int32(gmtoff)
-
1
isdst = isdst == 1
-
1
offset = {:utc_total_offset => gmtoff, :is_dst => isdst, :abbr_index => abbrind}
-
-
1
unless isdst
-
1
offset[:utc_offset] = gmtoff
-
1
offset[:std_offset] = 0
-
end
-
-
1
offsets << offset
-
end
-
-
1
abbrev = check_read(file, charcnt)
-
-
1
offsets.each do |o|
-
1
abbrev_start = o[:abbr_index]
-
1
raise InvalidZoneinfoFile, "Abbreviation index is out of range in file '#{file.path}'" unless abbrev_start < abbrev.length
-
-
1
abbrev_end = abbrev.index("\0", abbrev_start)
-
1
raise InvalidZoneinfoFile, "Missing abbreviation null terminator in file '#{file.path}'" unless abbrev_end
-
-
1
o[:abbr] = RubyCoreSupport.force_encoding(abbrev[abbrev_start...abbrev_end], 'UTF-8')
-
end
-
-
1
transitions.each do |t|
-
if t[:offset] < 0 || t[:offset] >= offsets.length
-
raise InvalidZoneinfoFile, "Invalid offset referenced by transition in file '#{file.path}'."
-
end
-
end
-
-
# Derive the offsets from standard time (std_offset).
-
1
derive_offsets(transitions, offsets)
-
1
derive_offsets(transitions.reverse, offsets)
-
-
# Assign anything left a standard offset of one hour
-
1
offsets.each do |o|
-
1
if !o[:std_offset] && o[:is_dst]
-
o[:std_offset] = 3600
-
o[:utc_offset] = o[:utc_total_offset] - 3600
-
end
-
end
-
-
# Find the first non-dst offset. This is used as the offset for the time
-
# before the first transition.
-
1
first = nil
-
1
offsets.each_with_index do |o, i|
-
1
if !o[:is_dst]
-
1
first = i
-
1
break
-
end
-
end
-
-
1
if first
-
1
offset first, offsets[first][:utc_offset], offsets[first][:std_offset], offsets[first][:abbr].untaint.to_sym
-
end
-
-
1
offsets.each_with_index do |o, i|
-
1
offset i, o[:utc_offset], o[:std_offset], o[:abbr].untaint.to_sym unless i == first
-
end
-
-
1
if !using_64bit && !RubyCoreSupport.time_supports_negative
-
# Filter out transitions that are not supported by Time on this
-
# platform.
-
-
# Move the last transition before the epoch up to the epoch. This
-
# allows for accurate conversions for all supported timestamps on the
-
# platform.
-
-
before_epoch, after_epoch = transitions.partition {|t| t[:at] < 0}
-
-
if before_epoch.length > 0 && after_epoch.length > 0 && after_epoch.first[:at] != 0
-
last_before = before_epoch.last
-
last_before[:at] = 0
-
transitions = [last_before] + after_epoch
-
else
-
transitions = after_epoch
-
end
-
end
-
-
# Ignore transitions that occur outside of a defined window. The
-
# transition index cannot handle a large range of transition times.
-
#
-
# This is primarily intended to ignore the far in the past transition
-
# added in zic 2014c (at timestamp -2**63 in zic 2014c and at the
-
# approximate time of the big bang from zic 2014d).
-
1
transitions.each do |t|
-
at = t[:at]
-
if at >= MIN_TIMESTAMP && at < MAX_TIMESTAMP
-
time = Time.at(at).utc
-
transition time.year, time.mon, t[:offset], at
-
end
-
end
-
end
-
end
-
end
-
# encoding: UTF-8
-
-
1
require "execjs"
-
1
require "json"
-
1
require "uglifier/version"
-
-
# A wrapper around the UglifyJS interface
-
1
class Uglifier
-
1
Error = ExecJS::Error
-
1
JS = <<-JS
-
function comments(option) {
-
if (Object.prototype.toString.call(option) === '[object Array]') {
-
return new RegExp(option[0], option[1]);
-
} else if (option == "jsdoc") {
-
return function(node, comment) {
-
if (comment.type == "comment2") {
-
return /@preserve|@license|@cc_on/i.test(comment.value);
-
} else {
-
return false;
-
}
-
}
-
} else {
-
return option;
-
}
-
}
-
-
var options = %s;
-
var source = options.source;
-
var ast = UglifyJS.parse(source, options.parse_options);
-
ast.figure_out_scope();
-
-
if (options.compress) {
-
var compressor = UglifyJS.Compressor(options.compress);
-
ast = ast.transform(compressor);
-
ast.figure_out_scope();
-
}
-
-
if (options.mangle) {
-
ast.compute_char_frequency();
-
ast.mangle_names(options.mangle);
-
}
-
-
if (options.enclose) {
-
ast = ast.wrap_enclose(options.enclose);
-
}
-
-
var gen_code_options = options.output;
-
gen_code_options.comments = comments(options.output.comments);
-
-
if (options.generate_map) {
-
var source_map = UglifyJS.SourceMap(options.source_map_options);
-
gen_code_options.source_map = source_map;
-
}
-
-
var stream = UglifyJS.OutputStream(gen_code_options);
-
-
ast.print(stream);
-
if (options.generate_map) {
-
return [stream.toString(), source_map.toString()];
-
} else {
-
return stream.toString();
-
}
-
JS
-
-
# UglifyJS source patch
-
1
SourcePath = File.expand_path("../uglify.js", __FILE__)
-
# ES5 shims source path
-
1
ES5FallbackPath = File.expand_path("../es5.js", __FILE__)
-
# String.split shim source path
-
1
SplitFallbackPath = File.expand_path("../split.js", __FILE__)
-
-
# Default options for compilation
-
1
DEFAULTS = {
-
# rubocop:disable LineLength
-
:output => {
-
:ascii_only => true, # Escape non-ASCII characterss
-
:comments => :copyright, # Preserve comments (:all, :jsdoc, :copyright, :none)
-
:inline_script => false, # Escape occurrences of </script in strings
-
:quote_keys => false, # Quote keys in object literals
-
:max_line_len => 32 * 1024, # Maximum line length in minified code
-
:bracketize => false, # Bracketize if, for, do, while or with statements, even if their body is a single statement
-
:semicolons => true, # Separate statements with semicolons
-
:preserve_line => false, # Preserve line numbers in outputs
-
:beautify => false, # Beautify output
-
:indent_level => 4, # Indent level in spaces
-
:indent_start => 0, # Starting indent level
-
:space_colon => false, # Insert space before colons (only with beautifier)
-
:width => 80, # Specify line width when beautifier is used (only with beautifier)
-
:preamble => nil # Preamble for the generated JS file. Can be used to insert any code or comment.
-
},
-
:mangle => {
-
:eval => false, # Mangle names when eval of when is used in scope
-
:except => ["$super"], # Argument names to be excluded from mangling
-
:sort => false, # Assign shorter names to most frequently used variables. Often results in bigger output after gzip.
-
:toplevel => false # Mangle names declared in the toplevel scope
-
}, # Mangle variable and function names, set to false to skip mangling
-
:compress => {
-
:sequences => true, # Allow statements to be joined by commas
-
:properties => true, # Rewrite property access using the dot notation
-
:dead_code => true, # Remove unreachable code
-
:drop_debugger => true, # Remove debugger; statements
-
:unsafe => false, # Apply "unsafe" transformations
-
:conditionals => true, # Optimize for if-s and conditional expressions
-
:comparisons => true, # Apply binary node optimizations for comparisons
-
:evaluate => true, # Attempt to evaluate constant expressions
-
:booleans => true, # Various optimizations to boolean contexts
-
:loops => true, # Optimize loops when condition can be statically determined
-
:unused => true, # Drop unreferenced functions and variables
-
:hoist_funs => true, # Hoist function declarations
-
:hoist_vars => false, # Hoist var declarations
-
:if_return => true, # Optimizations for if/return and if/continue
-
:join_vars => true, # Join consecutive var statements
-
:cascade => true, # Cascade sequences
-
:negate_iife => true, # Negate immediately invoked function expressions to avoid extra parens
-
:pure_getters => false, # Assume that object property access does not have any side-effects
-
:pure_funcs => nil, # List of functions without side-effects. Can safely discard function calls when the result value is not used
-
:drop_console => false, # Drop calls to console.* functions
-
:angular => false, # Process @ngInject annotations
-
:keep_fargs => false # Preserve unused function arguments
-
}, # Apply transformations to code, set to false to skip
-
:define => {}, # Define values for symbol replacement
-
:enclose => false, # Enclose in output function wrapper, define replacements as key-value pairs
-
:source_filename => nil, # The filename of the input file
-
:source_root => nil, # The URL of the directory which contains :source_filename
-
:output_filename => nil, # The filename or URL where the minified output can be found
-
:input_source_map => nil, # The contents of the source map describing the input
-
:screw_ie8 => false # Don't bother to generate safe code for IE8
-
}
-
# rubocop:enable LineLength
-
-
# Minifies JavaScript code using implicit context.
-
#
-
# source should be a String or IO object containing valid JavaScript.
-
# options contain optional overrides to Uglifier::DEFAULTS
-
#
-
# Returns minified code as String
-
1
def self.compile(source, options = {})
-
new(options).compile(source)
-
end
-
-
# Minifies JavaScript code and generates a source map using implicit context.
-
#
-
# source should be a String or IO object containing valid JavaScript.
-
# options contain optional overrides to Uglifier::DEFAULTS
-
#
-
# Returns a pair of [minified code as String, source map as a String]
-
1
def self.compile_with_map(source, options = {})
-
new(options).compile_with_map(source)
-
end
-
-
# Initialize new context for Uglifier with given options
-
#
-
# options - Hash of options to override Uglifier::DEFAULTS
-
1
def initialize(options = {})
-
(options.keys - DEFAULTS.keys - [:comments, :squeeze, :copyright])[0..1].each do |missing|
-
raise ArgumentError, "Invalid option: #{missing}"
-
end
-
@options = options
-
@context = ExecJS.compile(File.open(ES5FallbackPath, "r:UTF-8").read +
-
File.open(SplitFallbackPath, "r:UTF-8").read +
-
File.open(SourcePath, "r:UTF-8").read)
-
end
-
-
# Minifies JavaScript code
-
#
-
# source should be a String or IO object containing valid JavaScript.
-
#
-
# Returns minified code as String
-
1
def compile(source)
-
run_uglifyjs(source, false)
-
end
-
1
alias_method :compress, :compile
-
-
# Minifies JavaScript code and generates a source map
-
#
-
# source should be a String or IO object containing valid JavaScript.
-
#
-
# Returns a pair of [minified code as String, source map as a String]
-
1
def compile_with_map(source)
-
run_uglifyjs(source, true)
-
end
-
-
1
private
-
-
# Run UglifyJS for given source code
-
1
def run_uglifyjs(source, generate_map)
-
@context.exec(Uglifier::JS % json_encode(
-
:source => read_source(source),
-
:output => output_options,
-
:compress => compressor_options,
-
:mangle => mangle_options,
-
:parse_options => parse_options,
-
:source_map_options => source_map_options,
-
:generate_map => generate_map,
-
:enclose => enclose_options
-
))
-
end
-
-
1
def read_source(source)
-
if source.respond_to?(:read)
-
source.read
-
else
-
source.to_s
-
end
-
end
-
-
1
def mangle_options
-
conditional_option(@options[:mangle], DEFAULTS[:mangle])
-
end
-
-
1
def compressor_options
-
defaults = conditional_option(
-
DEFAULTS[:compress],
-
:global_defs => @options[:define] || {},
-
:screw_ie8 => @options[:screw_ie8] || DEFAULTS[:screw_ie8]
-
)
-
conditional_option(@options[:compress] || @options[:squeeze], defaults)
-
end
-
-
1
def comment_options
-
case comment_setting
-
when :all, true
-
true
-
when :jsdoc
-
"jsdoc"
-
when :copyright
-
encode_regexp(/Copyright/i)
-
when Regexp
-
encode_regexp(comment_setting)
-
else
-
false
-
end
-
end
-
-
1
def comment_setting
-
if @options.has_key?(:output) && @options[:output].has_key?(:comments)
-
@options[:output][:comments]
-
elsif @options.has_key?(:comments)
-
@options[:comments]
-
elsif @options[:copyright] == false
-
:none
-
else
-
DEFAULTS[:output][:comments]
-
end
-
end
-
-
1
def output_options
-
DEFAULTS[:output].merge(@options[:output] || {}).merge(
-
:comments => comment_options,
-
:screw_ie8 => screw_ie8?
-
).reject { |key, _| key == :ie_proof }
-
end
-
-
1
def screw_ie8?
-
if (@options[:output] || {}).has_key?(:ie_proof)
-
false
-
else
-
@options[:screw_ie8] || DEFAULTS[:screw_ie8]
-
end
-
end
-
-
1
def source_map_options
-
{
-
:file => @options[:output_filename],
-
:root => @options[:source_root],
-
:orig => @options[:input_source_map]
-
}
-
end
-
-
1
def parse_options
-
{ :filename => @options[:source_filename] }
-
end
-
-
1
def enclose_options
-
if @options[:enclose]
-
@options[:enclose].map do |pair|
-
pair.first + ':' + pair.last
-
end
-
else
-
false
-
end
-
end
-
-
1
def json_encode(obj)
-
JSON.dump(obj)
-
end
-
-
1
def encode_regexp(regexp)
-
modifiers = if regexp.casefold?
-
"i"
-
else
-
""
-
end
-
-
[regexp.source, modifiers]
-
end
-
-
1
def conditional_option(value, defaults)
-
if value == true || value.nil?
-
defaults
-
elsif value
-
defaults.merge(value)
-
else
-
false
-
end
-
end
-
end
-
1
class Uglifier
-
1
VERSION = "2.5.3"
-
end
-
1
require 'vcr/util/logger'
-
1
require 'vcr/util/variable_args_block_caller'
-
-
1
require 'vcr/cassette'
-
1
require 'vcr/cassette/serializers'
-
1
require 'vcr/cassette/persisters'
-
1
require 'vcr/configuration'
-
1
require 'vcr/deprecations'
-
1
require 'vcr/errors'
-
1
require 'vcr/library_hooks'
-
1
require 'vcr/request_ignorer'
-
1
require 'vcr/request_matcher_registry'
-
1
require 'vcr/structs'
-
1
require 'vcr/version'
-
-
# The main entry point for VCR.
-
# @note This module is extended onto itself; thus, the methods listed
-
# here as instance methods are available directly off of VCR.
-
1
module VCR
-
1
include VariableArgsBlockCaller
-
1
include Errors
-
-
1
extend self
-
-
1
autoload :CucumberTags, 'vcr/test_frameworks/cucumber'
-
1
autoload :InternetConnection, 'vcr/util/internet_connection'
-
-
1
module RSpec
-
1
autoload :Metadata, 'vcr/test_frameworks/rspec'
-
1
autoload :Macros, 'vcr/deprecations'
-
end
-
-
1
module Middleware
-
1
autoload :Faraday, 'vcr/middleware/faraday'
-
1
autoload :Rack, 'vcr/middleware/rack'
-
end
-
-
# The currently active cassette.
-
#
-
# @return [nil, VCR::Cassette] The current cassette or nil if there is
-
# no current cassette.
-
1
def current_cassette
-
cassettes.last
-
end
-
-
# Inserts the named cassette using the given cassette options.
-
# New HTTP interactions, if allowed by the cassette's `:record` option, will
-
# be recorded to the cassette. The cassette's existing HTTP interactions
-
# will be used to stub requests, unless prevented by the cassete's
-
# `:record` option.
-
#
-
# @example
-
# VCR.insert_cassette('twitter', :record => :new_episodes)
-
#
-
# # ...later, after making an HTTP request:
-
#
-
# VCR.eject_cassette
-
#
-
# @param name [#to_s] The name of the cassette. VCR will sanitize
-
# this to ensure it is a valid file name.
-
# @param options [Hash] The cassette options. The given options will
-
# be merged with the configured default_cassette_options.
-
# @option options :record [:all, :none, :new_episodes, :once] The record mode.
-
# @option options :erb [Boolean, Hash] Whether or not to evaluate the
-
# cassette as an ERB template. Defaults to false. A hash can be used
-
# to provide the ERB template with local variables.
-
# @option options :match_requests_on [Array<Symbol, #call>] List of request matchers
-
# to use to determine what recorded HTTP interaction to replay. Defaults to
-
# [:method, :uri]. The built-in matchers are :method, :uri, :host, :path, :headers
-
# and :body. You can also pass the name of a registered custom request matcher or
-
# any object that responds to #call.
-
# @option options :re_record_interval [Integer] When given, the
-
# cassette will be re-recorded at the given interval, in seconds.
-
# @option options :tag [Symbol] Used to apply tagged `before_record`
-
# and `before_playback` hooks to the cassette.
-
# @option options :tags [Array<Symbol>] Used to apply multiple tags to
-
# a cassette so that tagged `before_record` and `before_playback` hooks
-
# will apply to the cassette.
-
# @option options :update_content_length_header [Boolean] Whether or
-
# not to overwrite the Content-Length header of the responses to
-
# match the length of the response body. Defaults to false.
-
# @option options :decode_compressed_response [Boolean] Whether or
-
# not to decode compressed responses before recording the cassette.
-
# This makes the cassette more human readable. Defaults to false.
-
# @option options :allow_playback_repeats [Boolean] Whether or not to
-
# allow a single HTTP interaction to be played back multiple times.
-
# Defaults to false.
-
# @option options :allow_unused_http_interactions [Boolean] If set to
-
# false, an error will be raised if a cassette is ejected before all
-
# previously recorded HTTP interactions have been used.
-
# Defaults to true. Note that when an error has already occurred
-
# (as indicated by the `$!` variable) unused interactions will be
-
# allowed so that we don't silence the original error (which is almost
-
# certainly more interesting/important).
-
# @option options :exclusive [Boolean] Whether or not to use only this
-
# cassette and to completely ignore any cassettes in the cassettes stack.
-
# Defaults to false.
-
# @option options :serialize_with [Symbol] Which serializer to use.
-
# Valid values are :yaml, :syck, :psych, :json or any registered
-
# custom serializer. Defaults to :yaml.
-
# @option options :persist_with [Symbol] Which cassette persister to
-
# use. Defaults to :file_system. You can also register and use a
-
# custom persister.
-
# @option options :preserve_exact_body_bytes [Boolean] Whether or not
-
# to base64 encode the bytes of the requests and responses for this cassette
-
# when serializing it. See also `VCR::Configuration#preserve_exact_body_bytes`.
-
#
-
# @return [VCR::Cassette] the inserted cassette
-
#
-
# @raise [ArgumentError] when the given cassette is already being used.
-
# @raise [VCR::Errors::TurnedOffError] when VCR has been turned off
-
# without using the :ignore_cassettes option.
-
# @raise [VCR::Errors::MissingERBVariableError] when the `:erb` option
-
# is used and the ERB template requires variables that you did not provide.
-
#
-
# @note If you use this method you _must_ call `eject_cassette` when you
-
# are done. It is generally recommended that you use {#use_cassette}
-
# unless your code-under-test cannot be run as a block.
-
#
-
1
def insert_cassette(name, options = {})
-
if turned_on?
-
if cassettes.any? { |c| c.name == name }
-
raise ArgumentError.new("There is already a cassette with the same name (#{name}). You cannot nest multiple cassettes with the same name.")
-
end
-
-
cassette = Cassette.new(name, options)
-
cassettes.push(cassette)
-
cassette
-
elsif !ignore_cassettes?
-
message = "VCR is turned off. You must turn it on before you can insert a cassette. " +
-
"Or you can use the `:ignore_cassettes => true` option to completely ignore cassette insertions."
-
raise TurnedOffError.new(message)
-
end
-
end
-
-
# Ejects the current cassette. The cassette will no longer be used.
-
# In addition, any newly recorded HTTP interactions will be written to
-
# disk.
-
#
-
# @param options [Hash] Eject options.
-
# @option options :skip_no_unused_interactions_assertion [Boolean]
-
# If `true` is given, this will skip the "no unused HTTP interactions"
-
# assertion enabled by the `:allow_unused_http_interactions => false`
-
# cassette option. This is intended for use when your test has had
-
# an error, but your test framework has already handled it.
-
# @return [VCR::Cassette, nil] the ejected cassette if there was one
-
1
def eject_cassette(options = {})
-
cassette = cassettes.last
-
cassette.eject(options) if cassette
-
cassette
-
ensure
-
cassettes.pop
-
end
-
-
# Inserts a cassette using the given name and options, runs the given
-
# block, and ejects the cassette.
-
#
-
# @example
-
# VCR.use_cassette('twitter', :record => :new_episodes) do
-
# # make an HTTP request
-
# end
-
#
-
# @param (see #insert_cassette)
-
# @option (see #insert_cassette)
-
# @yield Block to run while this cassette is in use.
-
# @yieldparam cassette [(optional) VCR::Cassette] the cassette that has
-
# been inserted.
-
# @raise (see #insert_cassette)
-
# @return [void]
-
# @see #insert_cassette
-
# @see #eject_cassette
-
1
def use_cassette(name, options = {}, &block)
-
unless block
-
raise ArgumentError, "`VCR.use_cassette` requires a block. " +
-
"If you cannot wrap your code in a block, use " +
-
"`VCR.insert_cassette` / `VCR.eject_cassette` instead."
-
end
-
-
cassette = insert_cassette(name, options)
-
-
begin
-
call_block(block, cassette)
-
ensure
-
eject_cassette
-
end
-
end
-
-
# Used to configure VCR.
-
#
-
# @example
-
# VCR.configure do |c|
-
# c.some_config_option = true
-
# end
-
#
-
# @yield the configuration block
-
# @yieldparam config [VCR::Configuration] the configuration object
-
# @return [void]
-
1
def configure
-
1
yield configuration
-
end
-
-
# @return [VCR::Configuration] the VCR configuration.
-
1
def configuration
-
1
@configuration ||= Configuration.new
-
end
-
-
# Sets up `Before` and `After` cucumber hooks in order to
-
# use VCR with particular cucumber tags.
-
#
-
# @example
-
# VCR.cucumber_tags do |t|
-
# t.tags "tag1", "tag2"
-
# t.tag "@some_other_tag", :record => :new_episodes
-
# end
-
#
-
# @yield the cucumber tags configuration block
-
# @yieldparam t [VCR::CucumberTags] Cucumber tags config object
-
# @return [void]
-
# @see VCR::CucumberTags#tags
-
1
def cucumber_tags(&block)
-
main_object = eval('self', block.binding)
-
yield VCR::CucumberTags.new(main_object)
-
end
-
-
# Turns VCR off for the duration of a block.
-
#
-
# @param (see #turn_off!)
-
# @return [void]
-
# @raise (see #turn_off!)
-
# @see #turn_off!
-
# @see #turn_on!
-
# @see #turned_on?
-
1
def turned_off(options = {})
-
turn_off!(options)
-
-
begin
-
yield
-
ensure
-
turn_on!
-
end
-
end
-
-
# Turns VCR off, so that it no longer handles every HTTP request.
-
#
-
# @param options [Hash] hash of options
-
# @option options :ignore_cassettes [Boolean] controls what happens when a cassette is
-
# inserted while VCR is turned off. If `true` is passed, the cassette insertion
-
# will be ignored; otherwise a {VCR::Errors::TurnedOffError} will be raised.
-
#
-
# @return [void]
-
# @raise [VCR::Errors::CassetteInUseError] if there is currently a cassette in use
-
# @raise [ArgumentError] if you pass an invalid option
-
1
def turn_off!(options = {})
-
if VCR.current_cassette
-
raise CassetteInUseError, "A VCR cassette is currently in use (#{VCR.current_cassette.name}). " +
-
"You must eject it before you can turn VCR off."
-
end
-
-
@ignore_cassettes = options[:ignore_cassettes]
-
invalid_options = options.keys - [:ignore_cassettes]
-
if invalid_options.any?
-
raise ArgumentError.new("You passed some invalid options: #{invalid_options.inspect}")
-
end
-
-
@turned_off = true
-
end
-
-
# Turns on VCR, if it has previously been turned off.
-
# @return [void]
-
# @see #turn_off!
-
# @see #turned_off
-
# @see #turned_on?
-
1
def turn_on!
-
@turned_off = false
-
end
-
-
# @return whether or not VCR is turned on
-
# @note Normally VCR is _always_ turned on; it will only be off if you have
-
# explicitly turned it off.
-
# @see #turn_on!
-
# @see #turn_off!
-
# @see #turned_off
-
1
def turned_on?
-
!@turned_off
-
end
-
-
# @private
-
1
def http_interactions
-
return current_cassette.http_interactions if current_cassette
-
VCR::Cassette::HTTPInteractionList::NullList
-
end
-
-
# @private
-
1
def real_http_connections_allowed?
-
return current_cassette.recording? if current_cassette
-
!!(configuration.allow_http_connections_when_no_cassette? || @turned_off)
-
end
-
-
# @return [RequestMatcherRegistry] the request matcher registry
-
1
def request_matchers
-
@request_matchers ||= RequestMatcherRegistry.new
-
end
-
-
# @private
-
1
def request_ignorer
-
@request_ignorer ||= RequestIgnorer.new
-
end
-
-
# @private
-
1
def library_hooks
-
@library_hooks ||= LibraryHooks.new
-
end
-
-
# @private
-
1
def cassette_serializers
-
@cassette_serializers ||= Cassette::Serializers.new
-
end
-
-
# @private
-
1
def cassette_persisters
-
1
@cassette_persisters ||= Cassette::Persisters.new
-
end
-
-
# @private
-
1
def record_http_interaction(interaction)
-
return unless cassette = current_cassette
-
return if VCR.request_ignorer.ignore?(interaction.request)
-
-
cassette.record_http_interaction(interaction)
-
end
-
-
1
private
-
-
1
def ignore_cassettes?
-
@ignore_cassettes
-
end
-
-
1
def cassettes
-
@cassettes ||= []
-
end
-
-
1
def initialize_ivars
-
1
@turned_off = false
-
end
-
-
1
initialize_ivars # to avoid warnings
-
end
-
1
require 'vcr/cassette/http_interaction_list'
-
1
require 'vcr/cassette/erb_renderer'
-
1
require 'vcr/cassette/serializers'
-
-
1
module VCR
-
# The media VCR uses to store HTTP interactions for later re-use.
-
1
class Cassette
-
1
include Logger::Mixin
-
-
# The supported record modes.
-
#
-
# * :all -- Record every HTTP interactions; do not play any back.
-
# * :none -- Do not record any HTTP interactions; play them back.
-
# * :new_episodes -- Playback previously recorded HTTP interactions and record new ones.
-
# * :once -- Record the HTTP interactions if the cassette has not already been recorded;
-
# otherwise, playback the HTTP interactions.
-
1
VALID_RECORD_MODES = [:all, :none, :new_episodes, :once]
-
-
# @return [#to_s] The name of the cassette. Used to determine the cassette's file name.
-
# @see #file
-
1
attr_reader :name
-
-
# @return [Symbol] The record mode. Determines whether the cassette records HTTP interactions,
-
# plays them back, or does both.
-
1
attr_reader :record_mode
-
-
# @return [Array<Symbol, #call>] List of request matchers. Used to find a response from an
-
# existing HTTP interaction to play back.
-
1
attr_reader :match_requests_on
-
-
# @return [Boolean, Hash] The cassette's ERB option. The file will be treated as an
-
# ERB template if this has a truthy value. A hash, if provided, will be used as local
-
# variables for the ERB template.
-
1
attr_reader :erb
-
-
# @return [Integer, nil] How frequently (in seconds) the cassette should be re-recorded.
-
1
attr_reader :re_record_interval
-
-
# @return [Array<Symbol>] If set, {VCR::Configuration#before_record} and
-
# {VCR::Configuration#before_playback} hooks with a corresponding tag will apply.
-
1
attr_reader :tags
-
-
# @param (see VCR#insert_cassette)
-
# @see VCR#insert_cassette
-
1
def initialize(name, options = {})
-
@name = name
-
@options = VCR.configuration.default_cassette_options.merge(options)
-
-
assert_valid_options!
-
extract_options
-
raise_error_unless_valid_record_mode
-
-
log "Initialized with options: #{@options.inspect}"
-
end
-
-
# Ejects the current cassette. The cassette will no longer be used.
-
# In addition, any newly recorded HTTP interactions will be written to
-
# disk.
-
#
-
# @note This is not intended to be called directly. Use `VCR.eject_cassette` instead.
-
#
-
# @param (see VCR#eject_casssette)
-
# @see VCR#eject_cassette
-
1
def eject(options = {})
-
write_recorded_interactions_to_disk
-
-
if should_assert_no_unused_interactions? && !options[:skip_no_unused_interactions_assertion]
-
http_interactions.assert_no_unused_interactions!
-
end
-
end
-
-
# @private
-
1
def http_interactions
-
@http_interactions ||= HTTPInteractionList.new \
-
should_stub_requests? ? previously_recorded_interactions : [],
-
match_requests_on,
-
@allow_playback_repeats,
-
@parent_list,
-
log_prefix
-
end
-
-
# @private
-
1
def record_http_interaction(interaction)
-
log "Recorded HTTP interaction #{request_summary(interaction.request)} => #{response_summary(interaction.response)}"
-
new_recorded_interactions << interaction
-
end
-
-
# @private
-
1
def new_recorded_interactions
-
@new_recorded_interactions ||= []
-
end
-
-
# @return [String] The file for this cassette.
-
# @raise [NotImplementedError] if the configured cassette persister
-
# does not support resolving file paths.
-
# @note VCR will take care of sanitizing the cassette name to make it a valid file name.
-
1
def file
-
unless @persister.respond_to?(:absolute_path_to_file)
-
raise NotImplementedError, "The configured cassette persister does not support resolving file paths"
-
end
-
@persister.absolute_path_to_file(storage_key)
-
end
-
-
# @return [Boolean] Whether or not the cassette is recording.
-
1
def recording?
-
case record_mode
-
when :none; false
-
when :once; raw_cassette_bytes.to_s.empty?
-
else true
-
end
-
end
-
-
# @return [Hash] The hash that will be serialized when the cassette is written to disk.
-
1
def serializable_hash
-
{
-
"http_interactions" => interactions_to_record.map(&:to_hash),
-
"recorded_with" => "VCR #{VCR.version}"
-
}
-
end
-
-
# @return [Time, nil] The `recorded_at` time of the first HTTP interaction
-
# or nil if the cassette has no prior HTTP interactions.
-
#
-
# @example
-
#
-
# VCR.use_cassette("some cassette") do |cassette|
-
# Timecop.freeze(cassette.originally_recorded_at || Time.now) do
-
# # ...
-
# end
-
# end
-
1
def originally_recorded_at
-
@originally_recorded_at ||= previously_recorded_interactions.map(&:recorded_at).min
-
end
-
-
1
private
-
-
1
def assert_valid_options!
-
invalid_options = @options.keys - [
-
:record, :erb, :match_requests_on, :re_record_interval, :tag, :tags,
-
:update_content_length_header, :allow_playback_repeats, :allow_unused_http_interactions,
-
:exclusive, :serialize_with, :preserve_exact_body_bytes, :decode_compressed_response,
-
:persist_with
-
]
-
-
if invalid_options.size > 0
-
raise ArgumentError.new("You passed the following invalid options to VCR::Cassette.new: #{invalid_options.inspect}.")
-
end
-
end
-
-
1
def extract_options
-
[:erb, :match_requests_on, :re_record_interval,
-
:allow_playback_repeats, :allow_unused_http_interactions, :exclusive].each do |name|
-
instance_variable_set("@#{name}", @options[name])
-
end
-
-
assign_tags
-
-
@record_mode = @options[:record]
-
@serializer = VCR.cassette_serializers[@options[:serialize_with]]
-
@persister = VCR.cassette_persisters[@options[:persist_with]]
-
@record_mode = :all if should_re_record?
-
@parent_list = @exclusive ? HTTPInteractionList::NullList : VCR.http_interactions
-
end
-
-
1
def assign_tags
-
@tags = Array(@options.fetch(:tags) { @options[:tag] })
-
-
[:update_content_length_header, :preserve_exact_body_bytes, :decode_compressed_response].each do |tag|
-
@tags << tag if @options[tag]
-
end
-
end
-
-
1
def previously_recorded_interactions
-
@previously_recorded_interactions ||= if !raw_cassette_bytes.to_s.empty?
-
deserialized_hash['http_interactions'].map { |h| HTTPInteraction.from_hash(h) }.tap do |interactions|
-
invoke_hook(:before_playback, interactions)
-
-
interactions.reject! do |i|
-
i.request.uri.is_a?(String) && VCR.request_ignorer.ignore?(i.request)
-
end
-
end
-
else
-
[]
-
end
-
end
-
-
1
def storage_key
-
@storage_key ||= [name, @serializer.file_extension].join('.')
-
end
-
-
1
def raise_error_unless_valid_record_mode
-
unless VALID_RECORD_MODES.include?(record_mode)
-
raise ArgumentError.new("#{record_mode} is not a valid cassette record mode. Valid modes are: #{VALID_RECORD_MODES.inspect}")
-
end
-
end
-
-
1
def should_re_record?
-
return false unless @re_record_interval
-
return false unless originally_recorded_at
-
-
now = Time.now
-
-
(originally_recorded_at + @re_record_interval < now).tap do |value|
-
info = "previously recorded at: '#{originally_recorded_at}'; now: '#{now}'; interval: #{@re_record_interval} seconds"
-
-
if !value
-
log "Not re-recording since the interval has not elapsed (#{info})."
-
elsif InternetConnection.available?
-
log "re-recording (#{info})."
-
else
-
log "Not re-recording because no internet connection is available (#{info})."
-
return false
-
end
-
end
-
end
-
-
1
def should_stub_requests?
-
record_mode != :all
-
end
-
-
1
def should_remove_matching_existing_interactions?
-
record_mode == :all
-
end
-
-
1
def should_assert_no_unused_interactions?
-
!(@allow_unused_http_interactions || $!)
-
end
-
-
1
def raw_cassette_bytes
-
@raw_cassette_bytes ||= VCR::Cassette::ERBRenderer.new(@persister[storage_key], erb, name).render
-
end
-
-
1
def merged_interactions
-
old_interactions = previously_recorded_interactions
-
-
if should_remove_matching_existing_interactions?
-
new_interaction_list = HTTPInteractionList.new(new_recorded_interactions, match_requests_on)
-
old_interactions = old_interactions.reject do |i|
-
new_interaction_list.response_for(i.request)
-
end
-
end
-
-
old_interactions + new_recorded_interactions
-
end
-
-
1
def interactions_to_record
-
# We deep-dup the interactions by roundtripping them to/from a hash.
-
# This is necessary because `before_record` can mutate the interactions.
-
merged_interactions.map { |i| HTTPInteraction.from_hash(i.to_hash) }.tap do |interactions|
-
invoke_hook(:before_record, interactions)
-
end
-
end
-
-
1
def write_recorded_interactions_to_disk
-
return if new_recorded_interactions.none?
-
hash = serializable_hash
-
return if hash["http_interactions"].none?
-
-
@persister[storage_key] = @serializer.serialize(hash)
-
end
-
-
1
def invoke_hook(type, interactions)
-
interactions.delete_if do |i|
-
i.hook_aware.tap do |hw|
-
VCR.configuration.invoke_hook(type, hw, self)
-
end.ignored?
-
end
-
end
-
-
1
def deserialized_hash
-
@deserialized_hash ||= @serializer.deserialize(raw_cassette_bytes).tap do |hash|
-
unless hash.is_a?(Hash) && hash['http_interactions'].is_a?(Array)
-
raise Errors::InvalidCassetteFormatError.new \
-
"#{file} does not appear to be a valid VCR 2.0 cassette. " +
-
"VCR 1.x cassettes are not valid with VCR 2.0. When upgrading from " +
-
"VCR 1.x, it is recommended that you delete all your existing cassettes and " +
-
"re-record them, or use the provided vcr:migrate_cassettes rake task to migrate " +
-
"them. For more info, see the VCR upgrade guide."
-
end
-
end
-
end
-
-
1
def log_prefix
-
@log_prefix ||= "[Cassette: '#{name}'] "
-
end
-
-
1
def request_summary(request)
-
super(request, match_requests_on)
-
end
-
end
-
end
-
1
require 'erb'
-
-
1
module VCR
-
1
class Cassette
-
# @private
-
1
class ERBRenderer
-
1
def initialize(raw_template, erb, cassette_name=nil)
-
@raw_template, @erb, @cassette_name = raw_template, erb, cassette_name
-
end
-
-
1
def render
-
return @raw_template if @raw_template.nil? || !use_erb?
-
binding = binding_for_variables if erb_variables
-
template.result(binding)
-
rescue NameError => e
-
handle_name_error(e)
-
end
-
-
1
private
-
-
1
def handle_name_error(e)
-
example_hash = (erb_variables || {}).merge(e.name => 'some value')
-
-
raise Errors::MissingERBVariableError.new(
-
"The ERB in the #{@cassette_name} cassette file references undefined variable #{e.name}. " +
-
"Pass it to the cassette using :erb => #{ example_hash.inspect }."
-
)
-
end
-
-
1
def use_erb?
-
!!@erb
-
end
-
-
1
def erb_variables
-
@erb if @erb.is_a?(Hash)
-
end
-
-
1
def template
-
@template ||= ERB.new(@raw_template)
-
end
-
-
1
@@struct_cache = Hash.new do |hash, attributes|
-
hash[attributes] = Struct.new(*attributes)
-
end
-
-
1
def variables_object
-
@variables_object ||= @@struct_cache[erb_variables.keys].new(*erb_variables.values)
-
end
-
-
1
def binding_for_variables
-
@binding_for_variables ||= variables_object.instance_eval { binding }
-
end
-
end
-
end
-
end
-
1
module VCR
-
1
class Cassette
-
# @private
-
1
class HTTPInteractionList
-
1
include Logger::Mixin
-
-
# @private
-
1
module NullList
-
1
extend self
-
1
def response_for(*a); nil; end
-
1
def has_interaction_matching?(*a); false; end
-
1
def has_used_interaction_matching?(*a); false; end
-
1
def remaining_unused_interaction_count(*a); 0; end
-
end
-
-
1
attr_reader :interactions, :request_matchers, :allow_playback_repeats, :parent_list
-
-
1
def initialize(interactions, request_matchers, allow_playback_repeats = false, parent_list = NullList, log_prefix = '')
-
@interactions = interactions.dup
-
@request_matchers = request_matchers
-
@allow_playback_repeats = allow_playback_repeats
-
@parent_list = parent_list
-
@used_interactions = []
-
@log_prefix = log_prefix
-
-
interaction_summaries = interactions.map { |i| "#{request_summary(i.request)} => #{response_summary(i.response)}" }
-
log "Initialized HTTPInteractionList with request matchers #{request_matchers.inspect} and #{interactions.size} interaction(s): { #{interaction_summaries.join(', ')} }", 1
-
end
-
-
1
def response_for(request)
-
if index = matching_interaction_index_for(request)
-
interaction = @interactions.delete_at(index)
-
@used_interactions.unshift interaction
-
log "Found matching interaction for #{request_summary(request)} at index #{index}: #{response_summary(interaction.response)}", 1
-
interaction.response
-
elsif interaction = matching_used_interaction_for(request)
-
interaction.response
-
else
-
@parent_list.response_for(request)
-
end
-
end
-
-
1
def has_interaction_matching?(request)
-
!!matching_interaction_index_for(request) ||
-
!!matching_used_interaction_for(request) ||
-
@parent_list.has_interaction_matching?(request)
-
end
-
-
1
def has_used_interaction_matching?(request)
-
@used_interactions.any? { |i| interaction_matches_request?(request, i) }
-
end
-
-
1
def remaining_unused_interaction_count
-
@interactions.size
-
end
-
-
# Checks if there are no unused interactions left.
-
#
-
# @raise [VCR::Errors::UnusedHTTPInteractionError] if not all interactions were played back.
-
1
def assert_no_unused_interactions!
-
return unless has_unused_interactions?
-
logger = Logger.new(nil)
-
-
descriptions = @interactions.map do |i|
-
" - #{logger.request_summary(i.request, @request_matchers)} => #{logger.response_summary(i.response)}"
-
end.join("\n")
-
-
raise Errors::UnusedHTTPInteractionError, "There are unused HTTP interactions left in the cassette:\n#{descriptions}"
-
end
-
-
1
private
-
-
# @return [Boolean] Whether or not there are unused interactions left in the list.
-
1
def has_unused_interactions?
-
@interactions.size > 0
-
end
-
-
1
def request_summary(request)
-
super(request, @request_matchers)
-
end
-
-
1
def matching_interaction_index_for(request)
-
@interactions.index { |i| interaction_matches_request?(request, i) }
-
end
-
-
1
def matching_used_interaction_for(request)
-
return nil unless @allow_playback_repeats
-
@used_interactions.find { |i| interaction_matches_request?(request, i) }
-
end
-
-
1
def interaction_matches_request?(request, interaction)
-
log "Checking if #{request_summary(request)} matches #{request_summary(interaction.request)} using #{@request_matchers.inspect}", 1
-
@request_matchers.all? do |matcher_name|
-
matcher = VCR.request_matchers[matcher_name]
-
matcher.matches?(request, interaction.request).tap do |matched|
-
matched = matched ? 'matched' : 'did not match'
-
log "#{matcher_name} (#{matched}): current request #{request_summary(request)} vs #{request_summary(interaction.request)}", 2
-
end
-
end
-
end
-
-
1
def log_prefix
-
@log_prefix
-
end
-
end
-
end
-
end
-
-
1
module VCR
-
1
class Cassette
-
# Keeps track of the cassette persisters in a hash-like object.
-
1
class Persisters
-
1
autoload :FileSystem, 'vcr/cassette/persisters/file_system'
-
-
# @private
-
1
def initialize
-
1
@persisters = {}
-
end
-
-
# Gets the named persister.
-
#
-
# @param name [Symbol] the name of the persister
-
# @return the named persister
-
# @raise [ArgumentError] if there is not a persister for the given name
-
1
def [](name)
-
1
@persisters.fetch(name) do |_|
-
1
@persisters[name] = case name
-
1
when :file_system then FileSystem
-
else raise ArgumentError, "The requested VCR cassette persister " +
-
"(#{name.inspect}) is not registered."
-
end
-
end
-
end
-
-
# Registers a persister.
-
#
-
# @param name [Symbol] the name of the persister
-
# @param value [#[], #[]=] the persister object. It must implement `[]` and `[]=`.
-
1
def []=(name, value)
-
if @persisters.has_key?(name)
-
warn "WARNING: There is already a VCR cassette persister " +
-
"registered for #{name.inspect}. Overriding it."
-
end
-
-
@persisters[name] = value
-
end
-
-
end
-
end
-
end
-
1
require 'fileutils'
-
-
1
module VCR
-
1
class Cassette
-
1
class Persisters
-
# The only built-in cassette persister. Persists cassettes to the file system.
-
1
module FileSystem
-
1
extend self
-
-
# @private
-
1
attr_reader :storage_location
-
-
# @private
-
1
def storage_location=(dir)
-
1
FileUtils.mkdir_p(dir) if dir
-
1
@storage_location = dir ? absolute_path_for(dir) : nil
-
end
-
-
# Gets the cassette for the given storage key (file name).
-
#
-
# @param [String] file_name the file name
-
# @return [String] the cassette content
-
1
def [](file_name)
-
path = absolute_path_to_file(file_name)
-
return nil unless File.exist?(path)
-
File.read(path)
-
end
-
-
# Sets the cassette for the given storage key (file name).
-
#
-
# @param [String] file_name the file name
-
# @param [String] content the content to store
-
1
def []=(file_name, content)
-
path = absolute_path_to_file(file_name)
-
directory = File.dirname(path)
-
FileUtils.mkdir_p(directory) unless File.exist?(directory)
-
File.open(path, 'w') { |f| f.write(content) }
-
end
-
-
# @private
-
1
def absolute_path_to_file(file_name)
-
return nil unless storage_location
-
File.join(storage_location, sanitized_file_name_from(file_name))
-
end
-
-
1
private
-
-
1
def absolute_path_for(path)
-
2
Dir.chdir(path) { Dir.pwd }
-
end
-
-
1
def sanitized_file_name_from(file_name)
-
parts = file_name.to_s.split('.')
-
-
if parts.size > 1 && !parts.last.include?(File::SEPARATOR)
-
file_extension = '.' + parts.pop
-
end
-
-
parts.join('.').gsub(/[^\w\-\/]+/, '_') + file_extension.to_s
-
end
-
end
-
end
-
end
-
end
-
1
module VCR
-
1
class Cassette
-
# Keeps track of the cassette serializers in a hash-like object.
-
1
class Serializers
-
1
autoload :YAML, 'vcr/cassette/serializers/yaml'
-
1
autoload :Syck, 'vcr/cassette/serializers/syck'
-
1
autoload :Psych, 'vcr/cassette/serializers/psych'
-
1
autoload :JSON, 'vcr/cassette/serializers/json'
-
-
# @private
-
1
def initialize
-
@serializers = {}
-
end
-
-
# Gets the named serializer.
-
#
-
# @param name [Symbol] the name of the serializer
-
# @return the named serializer
-
# @raise [ArgumentError] if there is not a serializer for the given name
-
1
def [](name)
-
@serializers.fetch(name) do |_|
-
@serializers[name] = case name
-
when :yaml then YAML
-
when :syck then Syck
-
when :psych then Psych
-
when :json then JSON
-
else raise ArgumentError.new("The requested VCR cassette serializer (#{name.inspect}) is not registered.")
-
end
-
end
-
end
-
-
# Registers a serializer.
-
#
-
# @param name [Symbol] the name of the serializer
-
# @param value [#file_extension, #serialize, #deserialize] the serializer object. It must implement
-
# `file_extension()`, `serialize(Hash)` and `deserialize(String)`.
-
1
def []=(name, value)
-
if @serializers.has_key?(name)
-
warn "WARNING: There is already a VCR cassette serializer registered for #{name.inspect}. Overriding it."
-
end
-
-
@serializers[name] = value
-
end
-
end
-
-
# @private
-
1
module EncodingErrorHandling
-
1
def handle_encoding_errors
-
yield
-
rescue *self::ENCODING_ERRORS => e
-
e.message << "\nNote: Using VCR's `:preserve_exact_body_bytes` option may help prevent this error in the future."
-
raise
-
end
-
end
-
end
-
end
-
-
1
require 'vcr/util/hooks'
-
1
require 'uri'
-
1
require 'cgi'
-
-
1
module VCR
-
# Stores the VCR configuration.
-
1
class Configuration
-
1
include Hooks
-
1
include VariableArgsBlockCaller
-
1
include Logger::Mixin
-
-
# Gets the directory to read cassettes from and write cassettes to.
-
#
-
# @return [String] the directory to read cassettes from and write cassettes to
-
1
def cassette_library_dir
-
VCR.cassette_persisters[:file_system].storage_location
-
end
-
-
# Sets the directory to read cassettes from and writes cassettes to.
-
#
-
# @example
-
# VCR.configure do |c|
-
# c.cassette_library_dir = 'spec/cassettes'
-
# end
-
#
-
# @param dir [String] the directory to read cassettes from and write cassettes to
-
# @return [void]
-
# @note This is only necessary if you use the `:file_system`
-
# cassette persister (the default).
-
1
def cassette_library_dir=(dir)
-
1
VCR.cassette_persisters[:file_system].storage_location = dir
-
end
-
-
# Default options to apply to every cassette.
-
#
-
# @overload default_cassette_options
-
# @return [Hash] default options to apply to every cassette
-
# @overload default_cassette_options=(options)
-
# @param options [Hash] default options to apply to every cassette
-
# @return [void]
-
# @example
-
# VCR.configure do |c|
-
# c.default_cassette_options = { :record => :new_episodes }
-
# end
-
# @note {VCR#insert_cassette} for the list of valid options.
-
1
attr_reader :default_cassette_options
-
-
# Sets the default options that apply to every cassette.
-
1
def default_cassette_options=(overrides)
-
@default_cassette_options.merge!(overrides)
-
end
-
-
# Configures which libraries VCR will hook into to intercept HTTP requests.
-
#
-
# @example
-
# VCR.configure do |c|
-
# c.hook_into :fakeweb, :typhoeus
-
# end
-
#
-
# @param hooks [Array<Symbol>] List of libraries. Valid values are
-
# `:fakeweb`, `:webmock`, `:typhoeus`, `:excon` and `:faraday`.
-
# @raise [ArgumentError] when given an unsupported library name.
-
# @raise [VCR::Errors::LibraryVersionTooLowError] when the version
-
# of a library you are using is too low for VCR to support.
-
# @note `:fakeweb` and `:webmock` cannot both be used since they both monkey patch
-
# `Net::HTTP`. Otherwise, you can use any combination of these.
-
1
def hook_into(*hooks)
-
2
hooks.each { |a| load_library_hook(a) }
-
1
invoke_hook(:after_library_hooks_loaded)
-
end
-
-
# Specifies host(s) that VCR should ignore.
-
#
-
# @param hosts [Array<String>] List of hosts to ignore
-
# @see #ignore_localhost=
-
# @see #ignore_request
-
1
def ignore_hosts(*hosts)
-
VCR.request_ignorer.ignore_hosts(*hosts)
-
end
-
1
alias ignore_host ignore_hosts
-
-
# Sets whether or not VCR should ignore localhost requests.
-
#
-
# @param value [Boolean] the value to set
-
# @see #ignore_hosts
-
# @see #ignore_request
-
1
def ignore_localhost=(value)
-
VCR.request_ignorer.ignore_localhost = value
-
end
-
-
# Defines what requests to ignore using a block.
-
#
-
# @example
-
# VCR.configure do |c|
-
# c.ignore_request do |request|
-
# uri = URI(request.uri)
-
# # ignore only localhost requests to port 7500
-
# uri.host == 'localhost' && uri.port == 7500
-
# end
-
# end
-
#
-
# @yield the callback
-
# @yieldparam request [VCR::Request] the HTTP request
-
# @yieldreturn [Boolean] whether or not to ignore the request
-
1
def ignore_request(&block)
-
VCR.request_ignorer.ignore_request(&block)
-
end
-
-
# Determines how VCR treats HTTP requests that are made when
-
# no VCR cassette is in use. When set to `true`, requests made
-
# when there is no VCR cassette in use will be allowed. When set
-
# to `false` (the default), an {VCR::Errors::UnhandledHTTPRequestError}
-
# will be raised for any HTTP request made when there is no
-
# cassette in use.
-
#
-
# @overload allow_http_connections_when_no_cassette?
-
# @return [Boolean] whether or not HTTP connections are allowed
-
# when there is no cassette.
-
# @overload allow_http_connections_when_no_cassette=
-
# @param value [Boolean] sets whether or not to allow HTTP
-
# connections when there is no cassette.
-
1
attr_writer :allow_http_connections_when_no_cassette
-
# @private (documented above)
-
1
def allow_http_connections_when_no_cassette?
-
!!@allow_http_connections_when_no_cassette
-
end
-
-
# Sets a parser for VCR to use when parsing query strings for request
-
# comparisons. The new parser must implement a method `call` that returns
-
# an object which is both equalivant and consistent when given an HTTP
-
# query string of possibly differing value ordering.
-
#
-
# * `#== # => Boolean`
-
#
-
# The `#==` method must return true if both objects represent the
-
# same query string.
-
#
-
# This defaults to `CGI.parse` from the ruby standard library.
-
#
-
# @overload query_parser
-
# @return [#call] the current query string parser object
-
# @overload query_parser=
-
# @param value [#call] sets the query_parser
-
1
attr_accessor :query_parser
-
-
# Sets a parser for VCR to use when parsing URIs. The new parser
-
# must implement a method `parse` that returns an instance of the
-
# URI object. This URI object must implement the following
-
# interface:
-
#
-
# * `scheme # => String`
-
# * `host # => String`
-
# * `port # => Fixnum`
-
# * `path # => String`
-
# * `query # => String`
-
# * `#port=`
-
# * `#query=`
-
# * `#to_s # => String`
-
# * `#== # => Boolean`
-
#
-
# The `#==` method must return true if both URI objects represent the
-
# same URI.
-
#
-
# This defaults to `URI` from the ruby standard library.
-
#
-
# @overload uri_parser
-
# @return [#parse] the current URI parser object
-
# @overload uri_parser=
-
# @param value [#parse] sets the uri_parser
-
1
attr_accessor :uri_parser
-
-
# Registers a request matcher for later use.
-
#
-
# @example
-
# VCR.configure do |c|
-
# c.register_request_matcher :port do |request_1, request_2|
-
# URI(request_1.uri).port == URI(request_2.uri).port
-
# end
-
# end
-
#
-
# VCR.use_cassette("my_cassette", :match_requests_on => [:method, :host, :port]) do
-
# # ...
-
# end
-
#
-
# @param name [Symbol] the name of the request matcher
-
# @yield the request matcher
-
# @yieldparam request_1 [VCR::Request] One request
-
# @yieldparam request_2 [VCR::Request] The other request
-
# @yieldreturn [Boolean] whether or not these two requests should be considered
-
# equivalent
-
1
def register_request_matcher(name, &block)
-
VCR.request_matchers.register(name, &block)
-
end
-
-
# Sets up a {#before_record} and a {#before_playback} hook that will
-
# insert a placeholder string in the cassette in place of another string.
-
# You can use this as a generic way to interpolate a variable into the
-
# cassette for a unique string. It's particularly useful for unique
-
# sensitive strings like API keys and passwords.
-
#
-
# @example
-
# VCR.configure do |c|
-
# # Put "<GITHUB_API_KEY>" in place of the actual API key in
-
# # our cassettes so we don't have to commit to source control.
-
# c.filter_sensitive_data('<GITHUB_API_KEY>') { GithubClient.api_key }
-
#
-
# # Put a "<USER_ID>" placeholder variable in our cassettes tagged with
-
# # :user_cassette since it can be different for different test runs.
-
# c.define_cassette_placeholder('<USER_ID>', :user_cassette) { User.last.id }
-
# end
-
#
-
# @param placeholder [String] The placeholder string.
-
# @param tag [Symbol] Set this to apply this only to cassettes
-
# with a matching tag; otherwise it will apply to every cassette.
-
# @yield block that determines what string to replace
-
# @yieldparam interaction [(optional) VCR::HTTPInteraction::HookAware] the HTTP interaction
-
# @yieldreturn the string to replace
-
1
def define_cassette_placeholder(placeholder, tag = nil, &block)
-
before_record(tag) do |interaction|
-
orig_text = call_block(block, interaction)
-
log "before_record: replacing #{orig_text.inspect} with #{placeholder.inspect}"
-
interaction.filter!(orig_text, placeholder)
-
end
-
-
before_playback(tag) do |interaction|
-
orig_text = call_block(block, interaction)
-
log "before_playback: replacing #{placeholder.inspect} with #{orig_text.inspect}"
-
interaction.filter!(placeholder, orig_text)
-
end
-
end
-
1
alias filter_sensitive_data define_cassette_placeholder
-
-
# Gets the registry of cassette serializers. Use it to register a custom serializer.
-
#
-
# @example
-
# VCR.configure do |c|
-
# c.cassette_serializers[:my_custom_serializer] = my_custom_serializer
-
# end
-
#
-
# @return [VCR::Cassette::Serializers] the cassette serializer registry object.
-
# @note Custom serializers must implement the following interface:
-
#
-
# * `file_extension # => String`
-
# * `serialize(Hash) # => String`
-
# * `deserialize(String) # => Hash`
-
1
def cassette_serializers
-
VCR.cassette_serializers
-
end
-
-
# Gets the registry of cassette persisters. Use it to register a custom persister.
-
#
-
# @example
-
# VCR.configure do |c|
-
# c.cassette_persisters[:my_custom_persister] = my_custom_persister
-
# end
-
#
-
# @return [VCR::Cassette::Persisters] the cassette persister registry object.
-
# @note Custom persisters must implement the following interface:
-
#
-
# * `persister[storage_key]` # returns previously persisted content
-
# * `persister[storage_key] = content` # persists given content
-
1
def cassette_persisters
-
VCR.cassette_persisters
-
end
-
-
1
define_hook :before_record
-
# Adds a callback that will be called before the recorded HTTP interactions
-
# are serialized and written to disk.
-
#
-
# @example
-
# VCR.configure do |c|
-
# # Don't record transient 5xx errors
-
# c.before_record do |interaction|
-
# interaction.ignore! if interaction.response.status.code >= 500
-
# end
-
#
-
# # Modify the response body for cassettes tagged with :twilio
-
# c.before_record(:twilio) do |interaction|
-
# interaction.response.body.downcase!
-
# end
-
# end
-
#
-
# @param tag [(optional) Symbol] Used to apply this hook to only cassettes that match
-
# the given tag.
-
# @yield the callback
-
# @yieldparam interaction [VCR::HTTPInteraction::HookAware] The interaction that will be
-
# serialized and written to disk.
-
# @yieldparam cassette [(optional) VCR::Cassette] The current cassette.
-
# @see #before_playback
-
1
def before_record(tag = nil, &block)
-
1
super(tag_filter_from(tag), &block)
-
end
-
-
1
define_hook :before_playback
-
# Adds a callback that will be called before a previously recorded
-
# HTTP interaction is loaded for playback.
-
#
-
# @example
-
# VCR.configure do |c|
-
# # Don't playback transient 5xx errors
-
# c.before_playback do |interaction|
-
# interaction.ignore! if interaction.response.status.code >= 500
-
# end
-
#
-
# # Change a response header for playback
-
# c.before_playback(:twilio) do |interaction|
-
# interaction.response.headers['X-Foo-Bar'] = 'Bazz'
-
# end
-
# end
-
#
-
# @param tag [(optional) Symbol] Used to apply this hook to only cassettes that match
-
# the given tag.
-
# @yield the callback
-
# @yieldparam interaction [VCR::HTTPInteraction::HookAware] The interaction that is being
-
# loaded.
-
# @yieldparam cassette [(optional) VCR::Cassette] The current cassette.
-
# @see #before_record
-
1
def before_playback(tag = nil, &block)
-
1
super(tag_filter_from(tag), &block)
-
end
-
-
# Adds a callback that will be called with each HTTP request before it is made.
-
#
-
# @example
-
# VCR.configure do |c|
-
# c.before_http_request(:real?) do |request|
-
# puts "Request: #{request.method} #{request.uri}"
-
# end
-
# end
-
#
-
# @param filters [optional splat of #to_proc] one or more filters to apply.
-
# The objects provided will be converted to procs using `#to_proc`. If provided,
-
# the callback will only be invoked if these procs all return `true`.
-
# @yield the callback
-
# @yieldparam request [VCR::Request::Typed] the request that is being made
-
# @see #after_http_request
-
# @see #around_http_request
-
1
define_hook :before_http_request
-
-
1
define_hook :after_http_request, :prepend
-
# Adds a callback that will be called with each HTTP request after it is complete.
-
#
-
# @example
-
# VCR.configure do |c|
-
# c.after_http_request(:ignored?) do |request, response|
-
# puts "Request: #{request.method} #{request.uri}"
-
# puts "Response: #{response.status.code}"
-
# end
-
# end
-
#
-
# @param filters [optional splat of #to_proc] one or more filters to apply.
-
# The objects provided will be converted to procs using `#to_proc`. If provided,
-
# the callback will only be invoked if these procs all return `true`.
-
# @yield the callback
-
# @yieldparam request [VCR::Request::Typed] the request that is being made
-
# @yieldparam response [VCR::Response] the response from the request
-
# @see #before_http_request
-
# @see #around_http_request
-
1
def after_http_request(*filters)
-
super(*filters.map { |f| request_filter_from(f) })
-
end
-
-
# Adds a callback that will be executed around each HTTP request.
-
#
-
# @example
-
# VCR.configure do |c|
-
# c.around_http_request(lambda {|r| r.uri =~ /api.geocoder.com/}) do |request|
-
# # extract an address like "1700 E Pine St, Seattle, WA"
-
# # from a query like "address=1700+E+Pine+St%2C+Seattle%2C+WA"
-
# address = CGI.unescape(URI(request.uri).query.split('=').last)
-
# VCR.use_cassette("geocoding/#{address}", &request)
-
# end
-
# end
-
#
-
# @yield the callback
-
# @yieldparam request [VCR::Request::FiberAware] the request that is being made
-
# @raise [VCR::Errors::NotSupportedError] if the fiber library cannot be loaded.
-
# @param filters [optional splat of #to_proc] one or more filters to apply.
-
# The objects provided will be converted to procs using `#to_proc`. If provided,
-
# the callback will only be invoked if these procs all return `true`.
-
# @note This method can only be used on ruby interpreters that support
-
# fibers (i.e. 1.9+). On 1.8 you can use separate `before_http_request` and
-
# `after_http_request` hooks.
-
# @note You _must_ call `request.proceed` or pass the request as a proc on to a
-
# method that yields to a block (i.e. `some_method(&request)`).
-
# @see #before_http_request
-
# @see #after_http_request
-
1
def around_http_request(*filters, &block)
-
require 'fiber'
-
rescue LoadError
-
raise Errors::NotSupportedError.new \
-
"VCR::Configuration#around_http_request requires fibers, " +
-
"which are not available on your ruby intepreter."
-
else
-
fibers = {}
-
hook_allowed, hook_decaration = false, caller.first
-
before_http_request(*filters) do |request|
-
hook_allowed = true
-
fiber = start_new_fiber_for(request, block)
-
fibers[Thread.current] = fiber
-
end
-
-
after_http_request(lambda { hook_allowed }) do |request, response|
-
fiber = fibers.delete(Thread.current)
-
resume_fiber(fiber, response, hook_decaration)
-
end
-
end
-
-
# Configures RSpec to use a VCR cassette for any example
-
# tagged with `:vcr`.
-
1
def configure_rspec_metadata!
-
1
unless @rspec_metadata_configured
-
1
VCR::RSpec::Metadata.configure!
-
1
@rspec_metadata_configured = true
-
end
-
end
-
-
# An object to log debug output to.
-
#
-
# @overload debug_logger
-
# @return [#puts] the logger
-
# @overload debug_logger=(logger)
-
# @param logger [#puts] the logger
-
# @return [void]
-
# @example
-
# VCR.configure do |c|
-
# c.debug_logger = $stderr
-
# end
-
# @example
-
# VCR.configure do |c|
-
# c.debug_logger = File.open('vcr.log', 'w')
-
# end
-
1
attr_reader :debug_logger
-
# @private (documented above)
-
1
def debug_logger=(value)
-
1
@debug_logger = value
-
-
1
if value
-
@logger = Logger.new(value)
-
else
-
1
@logger = Logger::Null
-
end
-
end
-
-
# @private
-
# Logger object that provides logging APIs and helper methods.
-
1
attr_reader :logger
-
-
# Sets a callback that determines whether or not to base64 encode
-
# the bytes of a request or response body during serialization in
-
# order to preserve them exactly.
-
#
-
# @example
-
# VCR.configure do |c|
-
# c.preserve_exact_body_bytes do |http_message|
-
# http_message.body.encoding.name == 'ASCII-8BIT' ||
-
# !http_message.body.valid_encoding?
-
# end
-
# end
-
#
-
# @yield the callback
-
# @yieldparam http_message [#body, #headers] the `VCR::Request` or `VCR::Response` object being serialized
-
# @yieldparam cassette [VCR::Cassette] the cassette the http message belongs to
-
# @yieldreturn [Boolean] whether or not to preserve the exact bytes for the body of the given HTTP message
-
# @return [void]
-
# @see #preserve_exact_body_bytes_for?
-
# @note This is usually only necessary when the HTTP server returns a response
-
# with a non-standard encoding or with a body containing invalid bytes for the given
-
# encoding. Note that when you set this, and the block returns true, you sacrifice
-
# the human readability of the data in the cassette.
-
1
define_hook :preserve_exact_body_bytes
-
-
# @return [Boolean] whether or not the body of the given HTTP message should
-
# be base64 encoded during serialization in order to preserve the bytes exactly.
-
# @param http_message [#body, #headers] the `VCR::Request` or `VCR::Response` object being serialized
-
# @see #preserve_exact_body_bytes
-
1
def preserve_exact_body_bytes_for?(http_message)
-
invoke_hook(:preserve_exact_body_bytes, http_message, VCR.current_cassette).any?
-
end
-
-
1
private
-
-
1
def initialize
-
1
@allow_http_connections_when_no_cassette = nil
-
1
@rspec_metadata_configured = false
-
1
@default_cassette_options = {
-
:record => :once,
-
:match_requests_on => RequestMatcherRegistry::DEFAULT_MATCHERS,
-
:allow_unused_http_interactions => true,
-
:serialize_with => :yaml,
-
:persist_with => :file_system
-
}
-
-
1
self.uri_parser = URI
-
1
self.query_parser = CGI.method(:parse)
-
1
self.debug_logger = nil
-
-
1
register_built_in_hooks
-
end
-
-
1
def load_library_hook(hook)
-
1
file = "vcr/library_hooks/#{hook}"
-
1
require file
-
rescue LoadError => e
-
raise e unless e.message.include?(file) # in case FakeWeb/WebMock/etc itself is not available
-
raise ArgumentError.new("#{hook.inspect} is not a supported VCR HTTP library hook.")
-
end
-
-
1
def resume_fiber(fiber, response, hook_declaration)
-
fiber.resume(response)
-
rescue FiberError
-
raise Errors::AroundHTTPRequestHookError.new \
-
"Your around_http_request hook declared at #{hook_declaration}" +
-
" must call #proceed on the yielded request but did not."
-
end
-
-
1
def start_new_fiber_for(request, block)
-
Fiber.new(&block).tap do |fiber|
-
fiber.resume(Request::FiberAware.new(request))
-
end
-
end
-
-
1
def tag_filter_from(tag)
-
2
return lambda { true } unless tag
-
2
lambda { |_, cassette| cassette.tags.include?(tag) }
-
end
-
-
1
def request_filter_from(object)
-
return object unless object.is_a?(Symbol)
-
lambda { |arg| arg.send(object) }
-
end
-
-
1
def register_built_in_hooks
-
1
before_playback(:update_content_length_header) do |interaction|
-
interaction.response.update_content_length_header
-
end
-
-
1
before_record(:decode_compressed_response) do |interaction|
-
interaction.response.decompress if interaction.response.compressed?
-
end
-
-
1
preserve_exact_body_bytes do |http_message, cassette|
-
cassette && cassette.tags.include?(:preserve_exact_body_bytes)
-
end
-
end
-
-
1
def log_prefix
-
"[VCR::Configuration] "
-
end
-
-
# @private
-
1
define_hook :after_library_hooks_loaded
-
end
-
end
-
-
1
module VCR
-
# Namespace for VCR errors.
-
1
module Errors
-
# Base class for all VCR errors.
-
1
class Error < StandardError; end
-
-
# Error raised when VCR is turned off while a cassette is in use.
-
# @see VCR#turn_off!
-
# @see VCR#turned_off
-
1
class CassetteInUseError < Error; end
-
-
# Error raised when a VCR cassette is inserted while VCR is turned off.
-
# @see VCR#insert_cassette
-
# @see VCR#use_cassette
-
1
class TurnedOffError < Error; end
-
-
# Error raised when an cassette ERB template is rendered and a
-
# variable is missing.
-
# @see VCR#insert_cassette
-
# @see VCR#use_cassette
-
1
class MissingERBVariableError < Error; end
-
-
# Error raised when the version of one of the libraries that VCR hooks into
-
# is too low for VCR to support.
-
# @see VCR::Configuration#hook_into
-
1
class LibraryVersionTooLowError < Error; end
-
-
# Error raised when a request matcher is requested that is not registered.
-
1
class UnregisteredMatcherError < Error; end
-
-
# Error raised when a VCR 1.x cassette is used with VCR 2.
-
1
class InvalidCassetteFormatError < Error; end
-
-
# Error raised when an `around_http_request` hook is used improperly.
-
# @see VCR::Configuration#around_http_request
-
1
class AroundHTTPRequestHookError < Error; end
-
-
# Error raised when you attempt to use a VCR feature that is not
-
# supported on your ruby interpreter.
-
# @see VCR::Configuration#around_http_request
-
1
class NotSupportedError < Error; end
-
-
# Error raised when you ask VCR to decode a compressed response
-
# body but the content encoding isn't one of the known ones.
-
# @see VCR::Response#decompress
-
1
class UnknownContentEncodingError < Error; end
-
-
# Error raised when you eject a cassette before all previously
-
# recorded HTTP interactions are played back.
-
# @note Only applicable when :allow_episode_skipping is false.
-
# @see VCR::HTTPInteractionList#assert_no_unused_interactions!
-
1
class UnusedHTTPInteractionError < Error; end
-
-
# Error raised when an HTTP request is made that VCR is unable to handle.
-
# @note VCR will raise this to force you to do something about the
-
# HTTP request. The idea is that you want to handle _every_ HTTP
-
# request in your test suite. The error message will give you
-
# suggestions for how to deal with the request.
-
1
class UnhandledHTTPRequestError < Error
-
# The HTTP request.
-
1
attr_reader :request
-
-
# Constructs the error.
-
#
-
# @param [VCR::Request] request the unhandled request.
-
1
def initialize(request)
-
@request = request
-
super construct_message
-
end
-
-
1
private
-
-
1
def relish_version_slug
-
@relish_version_slug ||= VCR.version.gsub(/\W/, '-')
-
end
-
-
1
def construct_message
-
["", "", "=" * 80,
-
"An HTTP request has been made that VCR does not know how to handle:",
-
"#{request_description}\n",
-
cassette_description,
-
formatted_suggestions,
-
"=" * 80, "", ""].join("\n")
-
end
-
-
1
def request_description
-
lines = []
-
-
lines << " #{request.method.to_s.upcase} #{request.uri}"
-
-
if match_request_on_body?
-
lines << " Body: #{request.body}"
-
end
-
-
lines.join("\n")
-
end
-
-
1
def match_request_on_body?
-
current_matchers.include?(:body)
-
end
-
-
1
def current_matchers
-
if VCR.current_cassette
-
VCR.current_cassette.match_requests_on
-
else
-
VCR.configuration.default_cassette_options[:match_requests_on]
-
end
-
end
-
-
1
def cassette_description
-
if cassette = VCR.current_cassette
-
["VCR is currently using the following cassette:",
-
" - #{cassette.file}",
-
" - :record => #{cassette.record_mode.inspect}",
-
" - :match_requests_on => #{cassette.match_requests_on.inspect}\n",
-
"Under the current configuration VCR can not find a suitable HTTP interaction",
-
"to replay and is prevented from recording new requests. There are a few ways",
-
"you can deal with this:\n"].join("\n")
-
else
-
["There is currently no cassette in use. There are a few ways",
-
"you can configure VCR to handle this request:\n"].join("\n")
-
end
-
end
-
-
1
def formatted_suggestions
-
formatted_points, formatted_foot_notes = [], []
-
-
suggestions.each_with_index do |suggestion, index|
-
bullet_point, foot_note = suggestion.first, suggestion.last
-
formatted_points << format_bullet_point(bullet_point, index)
-
formatted_foot_notes << format_foot_note(foot_note, index)
-
end
-
-
[
-
formatted_points.join("\n"),
-
formatted_foot_notes.join("\n")
-
].join("\n\n")
-
end
-
-
1
def format_bullet_point(lines, index)
-
lines.first.insert(0, " * ")
-
lines.last << " [#{index + 1}]."
-
lines.join("\n ")
-
end
-
-
1
def format_foot_note(url, index)
-
"[#{index + 1}] #{url % relish_version_slug}"
-
end
-
-
# List of suggestions for how to configure VCR to handle the request.
-
1
ALL_SUGGESTIONS = {
-
:use_new_episodes => [
-
["You can use the :new_episodes record mode to allow VCR to",
-
"record this new request to the existing cassette"],
-
"https://www.relishapp.com/vcr/vcr/v/%s/docs/record-modes/new-episodes"
-
],
-
-
:delete_cassette_for_once => [
-
["The current record mode (:once) does not allow new requests to be recorded",
-
"to a previously recorded cassette. You can delete the cassette file and re-run",
-
"your tests to allow the cassette to be recorded with this request"],
-
"https://www.relishapp.com/vcr/vcr/v/%s/docs/record-modes/once"
-
],
-
-
:deal_with_none => [
-
["The current record mode (:none) does not allow requests to be recorded. You",
-
"can temporarily change the record mode to :once, delete the cassette file ",
-
"and re-run your tests to allow the cassette to be recorded with this request"],
-
"https://www.relishapp.com/vcr/vcr/v/%s/docs/record-modes/none"
-
],
-
-
:use_a_cassette => [
-
["If you want VCR to record this request and play it back during future test",
-
"runs, you should wrap your test (or this portion of your test) in a",
-
"`VCR.use_cassette` block"],
-
"https://www.relishapp.com/vcr/vcr/v/%s/docs/getting-started"
-
],
-
-
:allow_http_connections_when_no_cassette => [
-
["If you only want VCR to handle requests made while a cassette is in use,",
-
"configure `allow_http_connections_when_no_cassette = true`. VCR will",
-
"ignore this request since it is made when there is no cassette"],
-
"https://www.relishapp.com/vcr/vcr/v/%s/docs/configuration/allow-http-connections-when-no-cassette"
-
],
-
-
:ignore_request => [
-
["If you want VCR to ignore this request (and others like it), you can",
-
"set an `ignore_request` callback"],
-
"https://www.relishapp.com/vcr/vcr/v/%s/docs/configuration/ignore-request"
-
],
-
-
:allow_playback_repeats => [
-
["The cassette contains an HTTP interaction that matches this request,",
-
"but it has already been played back. If you wish to allow a single HTTP",
-
"interaction to be played back multiple times, set the `:allow_playback_repeats`",
-
"cassette option"],
-
"https://www.relishapp.com/vcr/vcr/v/%s/docs/request-matching/playback-repeats"
-
],
-
-
:match_requests_on => [
-
["The cassette contains %s not been",
-
"played back. If your request is non-deterministic, you may need to",
-
"change your :match_requests_on cassette option to be more lenient",
-
"or use a custom request matcher to allow it to match"],
-
"https://www.relishapp.com/vcr/vcr/v/%s/docs/request-matching"
-
],
-
-
:try_debug_logger => [
-
["If you're surprised VCR is raising this error",
-
"and want insight about how VCR attempted to handle the request,",
-
"you can use the debug_logger configuration option to log more details"],
-
"https://www.relishapp.com/vcr/vcr/v/%s/docs/configuration/debug-logging"
-
]
-
}
-
-
1
def suggestion_for(key)
-
bullet_point_lines, url = ALL_SUGGESTIONS[key]
-
bullet_point_lines = bullet_point_lines.map(&:dup)
-
url = url.dup
-
[bullet_point_lines, url]
-
end
-
-
1
def suggestions
-
return no_cassette_suggestions unless cassette = VCR.current_cassette
-
-
[:try_debug_logger, :use_new_episodes, :ignore_request].tap do |suggestions|
-
suggestions.push(*record_mode_suggestion)
-
suggestions << :allow_playback_repeats if cassette.http_interactions.has_used_interaction_matching?(request)
-
suggestions.map! { |k| suggestion_for(k) }
-
suggestions.push(*match_requests_on_suggestion)
-
end
-
end
-
-
1
def no_cassette_suggestions
-
[:try_debug_logger, :use_a_cassette, :allow_http_connections_when_no_cassette, :ignore_request].map do |key|
-
suggestion_for(key)
-
end
-
end
-
-
1
def record_mode_suggestion
-
case VCR.current_cassette.record_mode
-
when :none then [:deal_with_none]
-
when :once then [:delete_cassette_for_once]
-
else []
-
end
-
end
-
-
1
def match_requests_on_suggestion
-
num_remaining_interactions = VCR.current_cassette.http_interactions.remaining_unused_interaction_count
-
return [] if num_remaining_interactions.zero?
-
-
interaction_description = if num_remaining_interactions == 1
-
"1 HTTP interaction that has"
-
else
-
"#{num_remaining_interactions} HTTP interactions that have"
-
end
-
-
description_lines, link = suggestion_for(:match_requests_on)
-
description_lines[0] = description_lines[0] % interaction_description
-
[[description_lines, link]]
-
end
-
-
end
-
end
-
end
-
-
1
module VCR
-
# @private
-
1
class LibraryHooks
-
1
attr_accessor :exclusive_hook
-
-
1
def disabled?(hook)
-
![nil, hook].include?(exclusive_hook)
-
end
-
-
1
def exclusively_enabled(hook)
-
self.exclusive_hook = hook
-
yield
-
ensure
-
self.exclusive_hook = nil
-
end
-
end
-
end
-
-
1
require 'vcr/util/version_checker'
-
1
require 'vcr/request_handler'
-
1
require 'webmock'
-
-
1
VCR::VersionChecker.new('WebMock', WebMock.version, '1.8.0').check_version!
-
-
1
module VCR
-
1
class LibraryHooks
-
# @private
-
1
module WebMock
-
1
extend self
-
-
1
attr_accessor :global_hook_disabled
-
1
alias global_hook_disabled? global_hook_disabled
-
-
1
def with_global_hook_disabled
-
self.global_hook_disabled = true
-
-
begin
-
yield
-
ensure
-
self.global_hook_disabled = false
-
end
-
end
-
-
# @private
-
1
module Helpers
-
1
def vcr_request_for(webmock_request)
-
VCR::Request.new \
-
webmock_request.method,
-
webmock_request.uri.to_s,
-
webmock_request.body,
-
request_headers_for(webmock_request)
-
end
-
-
# @private
-
1
def vcr_response_for(webmock_response)
-
VCR::Response.new \
-
VCR::ResponseStatus.new(*webmock_response.status),
-
webmock_response.headers,
-
webmock_response.body,
-
nil
-
end
-
-
1
if defined?(::Excon)
-
# @private
-
def request_headers_for(webmock_request)
-
return nil unless webmock_request.headers
-
-
# WebMock hooks deeply into a Excon at a place where it manually adds a "Host"
-
# header, but this isn't a header we actually care to store...
-
webmock_request.headers.dup.tap do |headers|
-
headers.delete("Host")
-
end
-
end
-
else
-
# @private
-
1
def request_headers_for(webmock_request)
-
webmock_request.headers
-
end
-
end
-
-
1
def typed_request_for(webmock_request, remove = false)
-
if webmock_request.instance_variables.find { |v| v.to_sym == :@__typed_vcr_request }
-
meth = remove ? :remove_instance_variable : :instance_variable_get
-
return webmock_request.send(meth, :@__typed_vcr_request)
-
end
-
-
warn <<-EOS.gsub(/^\s+\|/, '')
-
|WARNING: There appears to be a bug in WebMock's after_request hook
-
| and VCR is attempting to work around it. Some VCR features
-
| may not work properly.
-
EOS
-
-
Request::Typed.new(vcr_request_for(webmock_request), :unknown)
-
end
-
end
-
-
1
class RequestHandler < ::VCR::RequestHandler
-
1
include Helpers
-
-
1
attr_reader :request
-
1
def initialize(request)
-
@request = request
-
end
-
-
1
private
-
-
1
def externally_stubbed?
-
# prevent infinite recursion...
-
VCR::LibraryHooks::WebMock.with_global_hook_disabled do
-
::WebMock.registered_request?(request)
-
end
-
end
-
-
1
def set_typed_request_for_after_hook(*args)
-
super
-
request.instance_variable_set(:@__typed_vcr_request, @after_hook_typed_request)
-
end
-
-
1
def vcr_request
-
@vcr_request ||= vcr_request_for(request)
-
end
-
-
1
def on_externally_stubbed_request
-
# nil allows WebMock to handle the request
-
nil
-
end
-
-
1
def on_unhandled_request
-
invoke_after_request_hook(nil)
-
super
-
end
-
-
1
def on_stubbed_by_vcr_request
-
{
-
:body => stubbed_response.body.dup, # Excon mutates the body, so we must dup it :-(
-
:status => [stubbed_response.status.code.to_i, stubbed_response.status.message],
-
:headers => stubbed_response.headers
-
}
-
end
-
end
-
-
1
extend Helpers
-
-
1
::WebMock.globally_stub_request do |req|
-
global_hook_disabled? ? nil : RequestHandler.new(req).handle
-
end
-
-
1
::WebMock.after_request(:real_requests_only => true) do |request, response|
-
unless VCR.library_hooks.disabled?(:webmock)
-
http_interaction = VCR::HTTPInteraction.new \
-
typed_request_for(request), vcr_response_for(response)
-
-
VCR.record_http_interaction(http_interaction)
-
end
-
end
-
-
1
::WebMock.after_request do |request, response|
-
unless VCR.library_hooks.disabled?(:webmock)
-
VCR.configuration.invoke_hook \
-
:after_http_request,
-
typed_request_for(request, :remove),
-
vcr_response_for(response)
-
end
-
end
-
end
-
end
-
end
-
-
# @private
-
1
module WebMock
-
class << self
-
# ensure HTTP requests are always allowed; VCR takes care of disallowing
-
# them at the appropriate times in its hook
-
1
def net_connect_allowed_with_vcr?(*args)
-
VCR.turned_on? ? true : net_connect_allowed_without_vcr?(*args)
-
end
-
-
1
alias net_connect_allowed_without_vcr? net_connect_allowed?
-
1
alias net_connect_allowed? net_connect_allowed_with_vcr?
-
1
end unless respond_to?(:net_connect_allowed_with_vcr?)
-
end
-
-
1
require 'set'
-
1
require 'vcr/util/hooks'
-
-
1
module VCR
-
# @private
-
1
class RequestIgnorer
-
1
include VCR::Hooks
-
-
1
define_hook :ignore_request
-
-
1
LOCALHOST_ALIASES = %w( localhost 127.0.0.1 0.0.0.0 )
-
-
1
def initialize
-
ignore_request do |request|
-
host = request.parsed_uri.host
-
ignored_hosts.include?(host)
-
end
-
end
-
-
1
def ignore_localhost=(value)
-
if value
-
ignore_hosts(*LOCALHOST_ALIASES)
-
else
-
ignored_hosts.reject! { |h| LOCALHOST_ALIASES.include?(h) }
-
end
-
end
-
-
1
def ignore_hosts(*hosts)
-
ignored_hosts.merge(hosts)
-
end
-
-
1
def ignore?(request)
-
invoke_hook(:ignore_request, request).any?
-
end
-
-
1
private
-
-
1
def ignored_hosts
-
@ignored_hosts ||= Set.new
-
end
-
end
-
end
-
-
1
require 'vcr/errors'
-
-
1
module VCR
-
# Keeps track of the different request matchers.
-
1
class RequestMatcherRegistry
-
-
# The default request matchers used for any cassette that does not
-
# specify request matchers.
-
1
DEFAULT_MATCHERS = [:method, :uri]
-
-
# @private
-
1
class Matcher < Struct.new(:callable)
-
1
def matches?(request_1, request_2)
-
callable.call(request_1, request_2)
-
end
-
end
-
-
# @private
-
1
class URIWithoutParamsMatcher < Struct.new(:params_to_ignore)
-
1
def partial_uri_from(request)
-
request.parsed_uri.tap do |uri|
-
return uri unless uri.query # ignore uris without params, e.g. "http://example.com/"
-
-
uri.query = uri.query.split('&').tap { |params|
-
params.map! do |p|
-
key, value = p.split('=')
-
key.gsub!(/\[\]\z/, '') # handle params like tag[]=
-
[key, value]
-
end
-
-
params.reject! { |p| params_to_ignore.include?(p.first) }
-
params.map! { |p| p.join('=') }
-
}.join('&')
-
-
uri.query = nil if uri.query.empty?
-
end
-
end
-
-
1
def call(request_1, request_2)
-
partial_uri_from(request_1) == partial_uri_from(request_2)
-
end
-
-
1
def to_proc
-
lambda { |r1, r2| call(r1, r2) }
-
end
-
end
-
-
# @private
-
1
def initialize
-
@registry = {}
-
register_built_ins
-
end
-
-
# @private
-
1
def register(name, &block)
-
if @registry.has_key?(name)
-
warn "WARNING: There is already a VCR request matcher registered for #{name.inspect}. Overriding it."
-
end
-
-
@registry[name] = Matcher.new(block)
-
end
-
-
# @private
-
1
def [](matcher)
-
@registry.fetch(matcher) do
-
matcher.respond_to?(:call) ?
-
Matcher.new(matcher) :
-
raise_unregistered_matcher_error(matcher)
-
end
-
end
-
-
# Builds a dynamic request matcher that matches on a URI while ignoring the
-
# named query parameters. This is useful for dealing with non-deterministic
-
# URIs (i.e. that have a timestamp or request signature parameter).
-
#
-
# @example
-
# without_timestamp = VCR.request_matchers.uri_without_param(:timestamp)
-
#
-
# # use it directly...
-
# VCR.use_cassette('example', :match_requests_on => [:method, without_timestamp]) { }
-
#
-
# # ...or register it as a named matcher
-
# VCR.configure do |c|
-
# c.register_request_matcher(:uri_without_timestamp, &without_timestamp)
-
# end
-
#
-
# VCR.use_cassette('example', :match_requests_on => [:method, :uri_without_timestamp]) { }
-
#
-
# @param ignores [Array<#to_s>] The names of the query parameters to ignore
-
# @return [#call] the request matcher
-
1
def uri_without_params(*ignores)
-
uri_without_param_matchers[ignores]
-
end
-
1
alias uri_without_param uri_without_params
-
-
1
private
-
-
1
def uri_without_param_matchers
-
@uri_without_param_matchers ||= Hash.new do |hash, params|
-
params = params.map(&:to_s)
-
hash[params] = URIWithoutParamsMatcher.new(params)
-
end
-
end
-
-
1
def raise_unregistered_matcher_error(name)
-
raise Errors::UnregisteredMatcherError.new \
-
"There is no matcher registered for #{name.inspect}. " +
-
"Did you mean one of #{@registry.keys.map(&:inspect).join(', ')}?"
-
end
-
-
1
def register_built_ins
-
register(:method) { |r1, r2| r1.method == r2.method }
-
register(:uri) { |r1, r2| r1.uri == r2.uri }
-
register(:body) { |r1, r2| r1.body == r2.body }
-
register(:headers) { |r1, r2| r1.headers == r2.headers }
-
-
register(:host) do |r1, r2|
-
r1.parsed_uri.host == r2.parsed_uri.host
-
end
-
register(:path) do |r1, r2|
-
r1.parsed_uri.path == r2.parsed_uri.path
-
end
-
-
register(:query) do |r1, r2|
-
VCR.configuration.query_parser.call(r1.parsed_uri.query.to_s) ==
-
VCR.configuration.query_parser.call(r2.parsed_uri.query.to_s)
-
end
-
-
try_to_register_body_as_json
-
end
-
-
1
def try_to_register_body_as_json
-
begin
-
require 'json'
-
rescue LoadError
-
return
-
end
-
-
register(:body_as_json) do |r1, r2|
-
begin
-
JSON.parse(r1.body) == JSON.parse(r2.body)
-
rescue JSON::ParserError
-
false
-
end
-
end
-
end
-
end
-
end
-
-
1
require 'base64'
-
1
require 'delegate'
-
1
require 'time'
-
-
1
module VCR
-
# @private
-
1
module Normalizers
-
# @private
-
1
module Body
-
1
def self.included(klass)
-
2
klass.extend ClassMethods
-
end
-
-
# @private
-
1
module ClassMethods
-
1
def body_from(hash_or_string)
-
return hash_or_string unless hash_or_string.is_a?(Hash)
-
hash = hash_or_string
-
-
if hash.has_key?('base64_string')
-
string = Base64.decode64(hash['base64_string'])
-
force_encode_string(string, hash['encoding'])
-
else
-
try_encode_string(hash['string'], hash['encoding'])
-
end
-
end
-
-
1
if "".respond_to?(:encoding)
-
1
def force_encode_string(string, encoding)
-
return string unless encoding
-
string.force_encoding(encoding)
-
end
-
-
1
def try_encode_string(string, encoding)
-
return string if encoding.nil? || string.encoding.name == encoding
-
-
# ASCII-8BIT just means binary, so encoding to it is nonsensical
-
# and yet "\u00f6".encode("ASCII-8BIT") raises an error.
-
# Instead, we'll force encode it (essentially just tagging it as binary)
-
return string.force_encoding(encoding) if encoding == "ASCII-8BIT"
-
-
string.encode(encoding)
-
rescue EncodingError => e
-
struct_type = name.split('::').last.downcase
-
warn "VCR: got `#{e.class.name}: #{e.message}` while trying to encode the #{string.encoding.name} " +
-
"#{struct_type} body to the original body encoding (#{encoding}). Consider using the " +
-
"`:preserve_exact_body_bytes` option to work around this."
-
return string
-
end
-
else
-
def force_encode_string(string, encoding)
-
string
-
end
-
-
def try_encode_string(string, encoding)
-
string
-
end
-
end
-
end
-
-
1
def initialize(*args)
-
super
-
-
if body && !body.is_a?(String)
-
raise ArgumentError, "#{self.class} initialized with an invalid body: #{body.inspect}."
-
end
-
-
# Ensure that the body is a raw string, in case the string instance
-
# has been subclassed or extended with additional instance variables
-
# or attributes, so that it is serialized to YAML as a raw string.
-
# This is needed for rest-client. See this ticket for more info:
-
# http://github.com/myronmarston/vcr/issues/4
-
self.body = String.new(body.to_s)
-
end
-
-
1
private
-
-
1
def serializable_body
-
# Ensure it's just a string, and not a string with some
-
# extra state, as such strings serialize to YAML with
-
# all the extra state.
-
body = String.new(self.body.to_s)
-
-
if VCR.configuration.preserve_exact_body_bytes_for?(self)
-
base_body_hash(body).merge('base64_string' => Base64.encode64(body))
-
else
-
base_body_hash(body).merge('string' => body)
-
end
-
end
-
-
1
if ''.respond_to?(:encoding)
-
1
def base_body_hash(body)
-
{ 'encoding' => body.encoding.name }
-
end
-
else
-
def base_body_hash(body)
-
{ }
-
end
-
end
-
end
-
-
# @private
-
1
module Header
-
1
def initialize(*args)
-
super
-
normalize_headers
-
end
-
-
1
private
-
-
1
def normalize_headers
-
new_headers = {}
-
@normalized_header_keys = Hash.new {|h,k| k }
-
-
headers.each do |k, v|
-
val_array = case v
-
when Array then v
-
when nil then []
-
else [v]
-
end
-
-
new_headers[String.new(k)] = convert_to_raw_strings(val_array)
-
@normalized_header_keys[k.downcase] = k
-
end if headers
-
-
self.headers = new_headers
-
end
-
-
1
def header_key(key)
-
key = @normalized_header_keys[key.downcase]
-
key if headers.has_key? key
-
end
-
-
1
def get_header(key)
-
key = header_key(key)
-
headers[key] if key
-
end
-
-
1
def edit_header(key, value = nil)
-
if key = header_key(key)
-
value ||= yield headers[key]
-
headers[key] = Array(value)
-
end
-
end
-
-
1
def delete_header(key)
-
if key = header_key(key)
-
@normalized_header_keys.delete key.downcase
-
headers.delete key
-
end
-
end
-
-
1
def convert_to_raw_strings(array)
-
# Ensure the values are raw strings.
-
# Apparently for Paperclip uploads to S3, headers
-
# get serialized with some extra stuff which leads
-
# to a seg fault. See this issue for more info:
-
# https://github.com/myronmarston/vcr/issues#issue/39
-
array.map do |v|
-
case v
-
when String; String.new(v)
-
when Array; convert_to_raw_strings(v)
-
else v
-
end
-
end
-
end
-
end
-
end
-
-
# @private
-
1
module OrderedHashSerializer
-
1
def each
-
@ordered_keys.each do |key|
-
yield key, self[key] if has_key?(key)
-
end
-
end
-
-
1
if RUBY_VERSION.to_f > 1.8
-
# 1.9+ hashes are already ordered.
-
1
def self.apply_to(*args); end
-
else
-
def self.apply_to(hash, keys)
-
hash.instance_variable_set(:@ordered_keys, keys)
-
hash.extend self
-
end
-
end
-
end
-
-
# The request of an {HTTPInteraction}.
-
#
-
# @attr [Symbol] method the HTTP method (i.e. :head, :options, :get, :post, :put, :patch or :delete)
-
# @attr [String] uri the request URI
-
# @attr [String, nil] body the request body
-
# @attr [Hash{String => Array<String>}] headers the request headers
-
1
class Request < Struct.new(:method, :uri, :body, :headers)
-
1
include Normalizers::Header
-
1
include Normalizers::Body
-
-
1
def initialize(*args)
-
skip_port_stripping = false
-
if args.last == :skip_port_stripping
-
skip_port_stripping = true
-
args.pop
-
end
-
-
super(*args)
-
self.method = self.method.to_s.downcase.to_sym if self.method
-
self.uri = without_standard_port(self.uri) unless skip_port_stripping
-
end
-
-
# Builds a serializable hash from the request data.
-
#
-
# @return [Hash] hash that represents this request and can be easily
-
# serialized.
-
# @see Request.from_hash
-
1
def to_hash
-
{
-
'method' => method.to_s,
-
'uri' => uri,
-
'body' => serializable_body,
-
'headers' => headers
-
}.tap { |h| OrderedHashSerializer.apply_to(h, members) }
-
end
-
-
# Constructs a new instance from a hash.
-
#
-
# @param [Hash] hash the hash to use to construct the instance.
-
# @return [Request] the request
-
1
def self.from_hash(hash)
-
method = hash['method']
-
method &&= method.to_sym
-
new method,
-
hash['uri'],
-
body_from(hash['body']),
-
hash['headers'],
-
:skip_port_stripping
-
end
-
-
# Parses the URI using the configured `uri_parser`.
-
#
-
# @return [#schema, #host, #port, #path, #query] A parsed URI object.
-
1
def parsed_uri
-
VCR.configuration.uri_parser.parse(uri)
-
end
-
-
1
@@object_method = Object.instance_method(:method)
-
1
def method(*args)
-
return super if args.empty?
-
@@object_method.bind(self).call(*args)
-
end
-
-
# Decorates a {Request} with its current type.
-
1
class Typed < DelegateClass(self)
-
# @return [Symbol] One of `:ignored`, `:stubbed`, `:recordable` or `:unhandled`.
-
1
attr_reader :type
-
-
# @param [Request] request the request
-
# @param [Symbol] type the type. Should be one of `:ignored`, `:stubbed`, `:recordable` or `:unhandled`.
-
1
def initialize(request, type)
-
@type = type
-
super(request)
-
end
-
-
# @return [Boolean] whether or not this request is being ignored
-
1
def ignored?
-
type == :ignored
-
end
-
-
# @return [Boolean] whether or not this request is being stubbed by VCR
-
# @see #externally_stubbed?
-
# @see #stubbed?
-
1
def stubbed_by_vcr?
-
type == :stubbed_by_vcr
-
end
-
-
# @return [Boolean] whether or not this request is being stubbed by an
-
# external library (such as WebMock or FakeWeb).
-
# @see #stubbed_by_vcr?
-
# @see #stubbed?
-
1
def externally_stubbed?
-
type == :externally_stubbed
-
end
-
-
# @return [Boolean] whether or not this request will be recorded.
-
1
def recordable?
-
type == :recordable
-
end
-
-
# @return [Boolean] whether or not VCR knows how to handle this request.
-
1
def unhandled?
-
type == :unhandled
-
end
-
-
# @return [Boolean] whether or not this request will be made for real.
-
# @note VCR allows `:ignored` and `:recordable` requests to be made for real.
-
1
def real?
-
ignored? || recordable?
-
end
-
-
# @return [Boolean] whether or not this request will be stubbed.
-
# It may be stubbed by an external library or by VCR.
-
# @see #stubbed_by_vcr?
-
# @see #externally_stubbed?
-
1
def stubbed?
-
stubbed_by_vcr? || externally_stubbed?
-
end
-
-
1
undef method
-
end
-
-
# Provides fiber-awareness for the {VCR::Configuration#around_http_request} hook.
-
1
class FiberAware < DelegateClass(Typed)
-
# Yields the fiber so the request can proceed.
-
#
-
# @return [VCR::Response] the response from the request
-
1
def proceed
-
Fiber.yield
-
end
-
-
# Builds a proc that allows the request to proceed when called.
-
# This allows you to treat the request as a proc and pass it on
-
# to a method that yields (at which point the request will proceed).
-
#
-
# @return [Proc] the proc
-
1
def to_proc
-
lambda { proceed }
-
end
-
-
1
undef method
-
end
-
-
1
private
-
-
1
def without_standard_port(uri)
-
return uri if uri.nil?
-
u = parsed_uri
-
return uri unless [['http', 80], ['https', 443]].include?([u.scheme, u.port])
-
u.port = nil
-
u.to_s
-
end
-
end
-
-
# The response of an {HTTPInteraction}.
-
#
-
# @attr [ResponseStatus] status the status of the response
-
# @attr [Hash{String => Array<String>}] headers the response headers
-
# @attr [String] body the response body
-
# @attr [nil, String] http_version the HTTP version
-
# @attr [Hash] adapter_metadata Additional metadata used by a specific VCR adapter.
-
1
class Response < Struct.new(:status, :headers, :body, :http_version, :adapter_metadata)
-
1
include Normalizers::Header
-
1
include Normalizers::Body
-
-
1
def initialize(*args)
-
super(*args)
-
self.adapter_metadata ||= {}
-
end
-
-
# Builds a serializable hash from the response data.
-
#
-
# @return [Hash] hash that represents this response
-
# and can be easily serialized.
-
# @see Response.from_hash
-
1
def to_hash
-
{
-
'status' => status.to_hash,
-
'headers' => headers,
-
'body' => serializable_body,
-
'http_version' => http_version
-
}.tap do |hash|
-
hash['adapter_metadata'] = adapter_metadata unless adapter_metadata.empty?
-
OrderedHashSerializer.apply_to(hash, members)
-
end
-
end
-
-
# Constructs a new instance from a hash.
-
#
-
# @param [Hash] hash the hash to use to construct the instance.
-
# @return [Response] the response
-
1
def self.from_hash(hash)
-
new ResponseStatus.from_hash(hash.fetch('status', {})),
-
hash['headers'],
-
body_from(hash['body']),
-
hash['http_version'],
-
hash['adapter_metadata']
-
end
-
-
# Updates the Content-Length response header so that it is
-
# accurate for the response body.
-
1
def update_content_length_header
-
edit_header('Content-Length') { body ? body.bytesize.to_s : '0' }
-
end
-
-
# The type of encoding.
-
#
-
# @return [String] encoding type
-
1
def content_encoding
-
enc = get_header('Content-Encoding') and enc.first
-
end
-
-
# Checks if the type of encoding is one of "gzip" or "deflate".
-
1
def compressed?
-
%w[ gzip deflate ].include? content_encoding
-
end
-
-
# Decodes the compressed body and deletes evidence that it was ever compressed.
-
#
-
# @return self
-
# @raise [VCR::Errors::UnknownContentEncodingError] if the content encoding
-
# is not a known encoding.
-
1
def decompress
-
self.class.decompress(body, content_encoding) { |new_body|
-
self.body = new_body
-
update_content_length_header
-
delete_header('Content-Encoding')
-
}
-
return self
-
end
-
-
1
begin
-
1
require 'zlib'
-
1
require 'stringio'
-
1
HAVE_ZLIB = true
-
rescue LoadError
-
HAVE_ZLIB = false
-
end
-
-
# Decode string compressed with gzip or deflate
-
#
-
# @raise [VCR::Errors::UnknownContentEncodingError] if the content encoding
-
# is not a known encoding.
-
1
def self.decompress(body, type)
-
unless HAVE_ZLIB
-
warn "VCR: cannot decompress response; Zlib not available"
-
return
-
end
-
-
case type
-
when 'gzip'
-
args = [StringIO.new(body)]
-
args << { :encoding => 'ASCII-8BIT' } if ''.respond_to?(:encoding)
-
yield Zlib::GzipReader.new(*args).read
-
when 'deflate'
-
yield Zlib::Inflate.inflate(body)
-
when 'identity', NilClass
-
return
-
else
-
raise Errors::UnknownContentEncodingError, "unknown content encoding: #{type}"
-
end
-
end
-
end
-
-
# The response status of an {HTTPInteraction}.
-
#
-
# @attr [Integer] code the HTTP status code
-
# @attr [String] message the HTTP status message (e.g. "OK" for a status of 200)
-
1
class ResponseStatus < Struct.new(:code, :message)
-
# Builds a serializable hash from the response status data.
-
#
-
# @return [Hash] hash that represents this response status
-
# and can be easily serialized.
-
# @see ResponseStatus.from_hash
-
1
def to_hash
-
{
-
'code' => code, 'message' => message
-
}.tap { |h| OrderedHashSerializer.apply_to(h, members) }
-
end
-
-
# Constructs a new instance from a hash.
-
#
-
# @param [Hash] hash the hash to use to construct the instance.
-
# @return [ResponseStatus] the response status
-
1
def self.from_hash(hash)
-
new hash['code'], hash['message']
-
end
-
end
-
-
# Represents a single interaction over HTTP, containing a request and a response.
-
#
-
# @attr [Request] request the request
-
# @attr [Response] response the response
-
# @attr [Time] recorded_at when this HTTP interaction was recorded
-
1
class HTTPInteraction < Struct.new(:request, :response, :recorded_at)
-
1
def initialize(*args)
-
super
-
self.recorded_at ||= Time.now
-
end
-
-
# Builds a serializable hash from the HTTP interaction data.
-
#
-
# @return [Hash] hash that represents this HTTP interaction
-
# and can be easily serialized.
-
# @see HTTPInteraction.from_hash
-
1
def to_hash
-
{
-
'request' => request.to_hash,
-
'response' => response.to_hash,
-
'recorded_at' => recorded_at.httpdate
-
}.tap do |hash|
-
OrderedHashSerializer.apply_to(hash, members)
-
end
-
end
-
-
# Constructs a new instance from a hash.
-
#
-
# @param [Hash] hash the hash to use to construct the instance.
-
# @return [HTTPInteraction] the HTTP interaction
-
1
def self.from_hash(hash)
-
new Request.from_hash(hash.fetch('request', {})),
-
Response.from_hash(hash.fetch('response', {})),
-
Time.httpdate(hash.fetch('recorded_at'))
-
end
-
-
# @return [HookAware] an instance with additional capabilities
-
# suitable for use in `before_record` and `before_playback` hooks.
-
1
def hook_aware
-
HookAware.new(self)
-
end
-
-
# Decorates an {HTTPInteraction} with additional methods useful
-
# for a `before_record` or `before_playback` hook.
-
1
class HookAware < DelegateClass(HTTPInteraction)
-
1
def initialize(http_interaction)
-
@ignored = false
-
super
-
end
-
-
# Flags the HTTP interaction so that VCR ignores it. This is useful in
-
# a {VCR::Configuration#before_record} or {VCR::Configuration#before_playback}
-
# hook so that VCR does not record or play it back.
-
# @see #ignored?
-
1
def ignore!
-
@ignored = true
-
end
-
-
# @return [Boolean] whether or not this HTTP interaction should be ignored.
-
# @see #ignore!
-
1
def ignored?
-
!!@ignored
-
end
-
-
# Replaces a string in any part of the HTTP interaction (headers, request body,
-
# response body, etc) with the given replacement text.
-
#
-
# @param [#to_s] text the text to replace
-
# @param [#to_s] replacement_text the text to put in its place
-
1
def filter!(text, replacement_text)
-
text, replacement_text = text.to_s, replacement_text.to_s
-
return self if [text, replacement_text].any? { |t| t.empty? }
-
filter_object!(self, text, replacement_text)
-
end
-
-
1
private
-
-
1
def filter_object!(object, text, replacement_text)
-
if object.respond_to?(:gsub)
-
object.gsub!(text, replacement_text) if object.include?(text)
-
elsif Hash === object
-
filter_hash!(object, text, replacement_text)
-
elsif object.respond_to?(:each)
-
# This handles nested arrays and structs
-
object.each { |o| filter_object!(o, text, replacement_text) }
-
end
-
-
object
-
end
-
-
1
def filter_hash!(hash, text, replacement_text)
-
filter_object!(hash.values, text, replacement_text)
-
-
hash.keys.each do |k|
-
new_key = filter_object!(k.dup, text, replacement_text)
-
hash[new_key] = hash.delete(k) unless k == new_key
-
end
-
end
-
end
-
end
-
end
-
1
module VCR
-
# Integrates VCR with RSpec.
-
1
module RSpec
-
# @private
-
1
module Metadata
-
1
extend self
-
-
1
def configure!
-
1
::RSpec.configure do |config|
-
1
vcr_cassette_name_for = lambda do |metadata|
-
description = metadata[:description]
-
example_group = if metadata.key?(:example_group)
-
metadata[:example_group]
-
else
-
metadata[:parent_example_group]
-
end
-
-
if example_group
-
[vcr_cassette_name_for[example_group], description].join('/')
-
else
-
description
-
end
-
end
-
-
1
when_tagged_with_vcr = { :vcr => lambda { |v| !!v } }
-
-
1
config.before(:each, when_tagged_with_vcr) do |ex|
-
example = ex.respond_to?(:metadata) ? ex : ex.example
-
-
options = example.metadata[:vcr]
-
options = options.is_a?(Hash) ? options.dup : {} # in case it's just :vcr => true
-
-
cassette_name = options.delete(:cassette_name) ||
-
vcr_cassette_name_for[example.metadata]
-
VCR.insert_cassette(cassette_name, options)
-
end
-
-
1
config.after(:each, when_tagged_with_vcr) do |ex|
-
example = ex.respond_to?(:metadata) ? ex : ex.example
-
VCR.eject_cassette(:skip_no_unused_interactions_assertion => !!example.exception)
-
end
-
end
-
end
-
end
-
end
-
end
-
-
1
require 'vcr/util/variable_args_block_caller'
-
-
1
module VCR
-
# @private
-
1
module Hooks
-
1
include VariableArgsBlockCaller
-
-
1
FilteredHook = Struct.new(:hook, :filters) do
-
1
include VariableArgsBlockCaller
-
-
1
def conditionally_invoke(*args)
-
filters = Array(self.filters)
-
return if filters.any? { |f| !call_block(f.to_proc, *args) }
-
call_block(hook, *args)
-
end
-
end
-
-
1
def self.included(klass)
-
2
klass.class_eval do
-
2
extend ClassMethods
-
2
hooks_module = Module.new
-
2
const_set("DefinedHooks", hooks_module)
-
2
include hooks_module
-
end
-
end
-
-
1
def invoke_hook(hook_type, *args)
-
1
hooks[hook_type].map do |hook|
-
hook.conditionally_invoke(*args)
-
end
-
end
-
-
1
def clear_hooks
-
hooks.clear
-
end
-
-
1
def hooks
-
@hooks ||= Hash.new do |hash, hook_type|
-
4
hash[hook_type] = []
-
4
end
-
end
-
-
1
def has_hooks_for?(hook_type)
-
hooks[hook_type].any?
-
end
-
-
# @private
-
1
module ClassMethods
-
1
def define_hook(hook_type, prepend = false)
-
7
placement_method = prepend ? :unshift : :<<
-
-
# Put the hook methods in a module so we can override and super to these methods.
-
7
self::DefinedHooks.module_eval do
-
7
define_method hook_type do |*filters, &hook|
-
3
hooks[hook_type].send(placement_method, FilteredHook.new(hook, filters))
-
end
-
end
-
end
-
end
-
end
-
end
-
1
module VCR
-
# @private
-
# Provides log message formatting helper methods.
-
1
class Logger
-
1
def initialize(stream)
-
@stream = stream
-
end
-
-
1
def log(message, log_prefix, indentation_level = 0)
-
indentation = ' ' * indentation_level
-
log_message = indentation + log_prefix + message
-
@stream.puts log_message
-
end
-
-
1
def request_summary(request, request_matchers)
-
attributes = [request.method, request.uri]
-
attributes << request.body.to_s[0, 80].inspect if request_matchers.include?(:body)
-
attributes << request.headers.inspect if request_matchers.include?(:headers)
-
"[#{attributes.join(" ")}]"
-
end
-
-
1
def response_summary(response)
-
"[#{response.status.code} #{response.body[0, 80].inspect}]"
-
end
-
-
# @private
-
# A null-object version of the Logger. Used when
-
# a `debug_logger` has not been set.
-
#
-
# @note We used to use a null object for the `debug_logger` itself,
-
# but some users noticed a negative perf impact from having the
-
# logger formatting logic still executing in that case, so we
-
# moved the null object interface up a layer to here.
-
1
module Null
-
1
module_function
-
-
1
def log(*); end
-
1
def request_summary(*); end
-
1
def response_summary(*); end
-
end
-
-
# @private
-
# Provides common logger helper methods that simply delegate to
-
# the underlying logger object.
-
1
module Mixin
-
1
def log(message, indentation_level = 0)
-
VCR.configuration.logger.log(message, log_prefix, indentation_level)
-
end
-
-
1
def request_summary(*args)
-
VCR.configuration.logger.request_summary(*args)
-
end
-
-
1
def response_summary(*args)
-
VCR.configuration.logger.response_summary(*args)
-
end
-
end
-
end
-
end
-
1
module VCR
-
# @private
-
1
module VariableArgsBlockCaller
-
1
def call_block(block, *args)
-
if block.arity >= 0
-
args = args.first([args.size, block.arity].min)
-
end
-
-
block.call(*args)
-
end
-
end
-
end
-
-
1
module VCR
-
# @private
-
1
class VersionChecker
-
1
def initialize(library_name, library_version, min_version)
-
1
@library_name = library_name
-
1
@library_version = library_version
-
1
@min_version = min_version
-
-
1
@major, @minor, @patch = parse_version(library_version)
-
1
@min_major, @min_minor, @min_patch = parse_version(min_version)
-
end
-
-
1
def check_version!
-
1
raise_too_low_error if too_low?
-
end
-
-
1
private
-
-
1
def too_low?
-
1
compare_version == :too_low
-
end
-
-
1
def raise_too_low_error
-
raise Errors::LibraryVersionTooLowError,
-
"You are using #{@library_name} #{@library_version}. " +
-
"VCR requires version #{version_requirement}."
-
end
-
-
1
def compare_version
-
case
-
when @major < @min_major then :too_low
-
when @major > @min_major then :ok
-
when @minor < @min_minor then :too_low
-
1
when @minor > @min_minor then :ok
-
when @patch < @min_patch then :too_low
-
1
end
-
end
-
-
1
def version_requirement
-
">= #{@min_version}"
-
end
-
-
1
def parse_version(version)
-
8
version.split('.').map { |v| v.to_i }
-
end
-
end
-
end
-
-
1
module VCR
-
1
extend self
-
-
# @return [String] the current VCR version.
-
# @note This string also has singleton methods:
-
#
-
# * `major` [Integer] The major version.
-
# * `minor` [Integer] The minor version.
-
# * `patch` [Integer] The patch version.
-
# * `parts` [Array<Integer>] List of the version parts.
-
1
def version
-
@version ||= begin
-
string = '2.9.3'
-
-
def string.parts
-
split('.').map { |p| p.to_i }
-
end
-
-
def string.major
-
parts[0]
-
end
-
-
def string.minor
-
parts[1]
-
end
-
-
def string.patch
-
parts[2]
-
end
-
-
string
-
end
-
end
-
end
-
1
require 'singleton'
-
-
1
require 'addressable/uri'
-
1
require 'addressable/template'
-
1
require 'crack'
-
-
1
require 'webmock/deprecation'
-
1
require 'webmock/version'
-
-
1
require 'webmock/errors'
-
-
1
require 'webmock/util/query_mapper'
-
1
require 'webmock/util/uri'
-
1
require 'webmock/util/headers'
-
1
require 'webmock/util/hash_counter'
-
1
require 'webmock/util/hash_keys_stringifier'
-
1
require 'webmock/util/json'
-
1
require 'webmock/util/version_checker'
-
-
1
require 'webmock/matchers/hash_including_matcher'
-
-
1
require 'webmock/request_pattern'
-
1
require 'webmock/request_signature'
-
1
require 'webmock/responses_sequence'
-
1
require 'webmock/request_stub'
-
1
require 'webmock/response'
-
1
require 'webmock/rack_response'
-
-
1
require 'webmock/stub_request_snippet'
-
-
1
require 'webmock/assertion_failure'
-
1
require 'webmock/request_execution_verifier'
-
1
require 'webmock/config'
-
1
require 'webmock/callback_registry'
-
1
require 'webmock/request_registry'
-
1
require 'webmock/stub_registry'
-
1
require 'webmock/api'
-
-
1
require 'webmock/http_lib_adapters/http_lib_adapter_registry'
-
1
require 'webmock/http_lib_adapters/http_lib_adapter'
-
1
require 'webmock/http_lib_adapters/net_http'
-
1
require 'webmock/http_lib_adapters/http_gem_adapter'
-
1
require 'webmock/http_lib_adapters/httpclient_adapter'
-
1
require 'webmock/http_lib_adapters/patron_adapter'
-
1
require 'webmock/http_lib_adapters/curb_adapter'
-
1
require 'webmock/http_lib_adapters/em_http_request_adapter'
-
1
require 'webmock/http_lib_adapters/typhoeus_hydra_adapter'
-
1
require 'webmock/http_lib_adapters/excon_adapter'
-
-
1
require 'webmock/webmock'
-
-
-
1
module WebMock
-
1
module API
-
1
extend self
-
-
1
def stub_request(method, uri)
-
WebMock::StubRegistry.instance.
-
register_request_stub(WebMock::RequestStub.new(method, uri))
-
end
-
-
1
alias_method :stub_http_request, :stub_request
-
-
1
def a_request(method, uri)
-
WebMock::RequestPattern.new(method, uri)
-
end
-
-
1
class << self
-
1
alias :request :a_request
-
end
-
-
1
def assert_requested(*args, &block)
-
if not args[0].is_a?(WebMock::RequestStub)
-
args = convert_uri_method_and_options_to_request_and_options(*args, &block)
-
elsif block
-
raise ArgumentError, "assert_requested with a stub object, doesn't accept blocks"
-
end
-
assert_request_requested(*args)
-
end
-
-
1
def assert_not_requested(*args, &block)
-
if not args[0].is_a?(WebMock::RequestStub)
-
args = convert_uri_method_and_options_to_request_and_options(*args, &block)
-
elsif block
-
raise ArgumentError, "assert_not_requested with a stub object, doesn't accept blocks"
-
end
-
assert_request_not_requested(*args)
-
end
-
-
# Similar to RSpec::Mocks::ArgumentMatchers#hash_including()
-
#
-
# Matches a hash that includes the specified key(s) or key/value pairs.
-
# Ignores any additional keys.
-
#
-
# @example
-
#
-
# object.should_receive(:message).with(hash_including(:key => val))
-
# object.should_receive(:message).with(hash_including(:key))
-
# object.should_receive(:message).with(hash_including(:key, :key2 => val2))
-
1
def hash_including(*args)
-
if defined?(super)
-
super
-
else
-
WebMock::Matchers::HashIncludingMatcher.new(anythingize_lonely_keys(*args))
-
end
-
end
-
-
1
def remove_request_stub(stub)
-
WebMock::StubRegistry.instance.remove_request_stub(stub)
-
end
-
-
1
private
-
-
1
def convert_uri_method_and_options_to_request_and_options(*args, &block)
-
request = WebMock::RequestPattern.new(*args).with(&block)
-
[request, args[2] || {}]
-
end
-
-
1
def assert_request_requested(request, options = {})
-
verifier = WebMock::RequestExecutionVerifier.new(request, options.delete(:times) || 1)
-
WebMock::AssertionFailure.failure(verifier.failure_message) unless verifier.matches?
-
end
-
-
1
def assert_request_not_requested(request, options = {})
-
verifier = WebMock::RequestExecutionVerifier.new(request, options.delete(:times))
-
WebMock::AssertionFailure.failure(verifier.failure_message_when_negated) unless verifier.does_not_match?
-
end
-
-
#this is a based on RSpec::Mocks::ArgumentMatchers#anythingize_lonely_keys
-
1
def anythingize_lonely_keys(*args)
-
hash = args.last.class == Hash ? args.delete_at(-1) : {}
-
args.each { | arg | hash[arg] = WebMock::Matchers::AnyArgMatcher.new(nil) }
-
hash
-
end
-
-
end
-
end
-
1
module WebMock
-
1
class AssertionFailure
-
1
@error_class = RuntimeError
-
1
class << self
-
1
attr_accessor :error_class
-
1
def failure(message)
-
raise @error_class.new(message)
-
end
-
end
-
end
-
end
-
1
module WebMock
-
1
class CallbackRegistry
-
1
@@callbacks = []
-
-
1
def self.add_callback(options, block)
-
2
@@callbacks << {:options => options, :block => block}
-
end
-
-
1
def self.callbacks
-
@@callbacks
-
end
-
-
1
def self.invoke_callbacks(options, request_signature, response)
-
return if @@callbacks.empty?
-
CallbackRegistry.callbacks.each do |callback|
-
except = callback[:options][:except]
-
real_only = callback[:options][:real_requests_only]
-
unless except && except.include?(options[:lib])
-
if !real_only || options[:real_request]
-
callback[:block].call(request_signature, response)
-
end
-
end
-
end
-
end
-
-
1
def self.reset
-
@@callbacks = []
-
end
-
-
1
def self.any_callbacks?
-
!@@callbacks.empty?
-
end
-
-
end
-
end
-
1
module WebMock
-
1
class Config
-
1
include Singleton
-
-
1
def initialize
-
@show_stubbing_instructions = true
-
end
-
-
1
attr_accessor :allow_net_connect
-
1
attr_accessor :allow_localhost
-
1
attr_accessor :allow
-
1
attr_accessor :net_http_connect_on_start
-
1
attr_accessor :show_stubbing_instructions
-
1
attr_accessor :query_values_notation
-
end
-
end
-
1
module WebMock
-
1
class Deprecation
-
1
class << self
-
1
def warning(message)
-
warn "WebMock deprecation warning: #{message}"
-
end
-
end
-
end
-
end
-
1
module WebMock
-
-
1
class NetConnectNotAllowedError < Exception
-
1
def initialize(request_signature)
-
text = [
-
"Real HTTP connections are disabled. Unregistered request: #{request_signature}",
-
stubbing_instructions(request_signature),
-
request_stubs,
-
"="*60
-
].compact.join("\n\n")
-
super(text)
-
end
-
-
1
private
-
-
1
def request_stubs
-
return if WebMock::StubRegistry.instance.request_stubs.empty?
-
text = "registered request stubs:\n"
-
WebMock::StubRegistry.instance.request_stubs.each do |stub|
-
text << "\n#{WebMock::StubRequestSnippet.new(stub).to_s(false)}"
-
end
-
text
-
end
-
-
1
def stubbing_instructions(request_signature)
-
return unless WebMock.show_stubbing_instructions?
-
text = ""
-
request_stub = RequestStub.from_request_signature(request_signature)
-
text << "You can stub this request with the following snippet:\n\n"
-
text << WebMock::StubRequestSnippet.new(request_stub).to_s
-
text
-
end
-
end
-
-
end
-
1
begin
-
1
require 'curb'
-
rescue LoadError
-
# curb not found
-
end
-
-
1
if defined?(Curl)
-
WebMock::VersionChecker.new('Curb', Curl::CURB_VERSION, '0.7.16').check_version!
-
-
module WebMock
-
module HttpLibAdapters
-
class CurbAdapter < HttpLibAdapter
-
adapter_for :curb
-
-
OriginalCurlEasy = Curl::Easy unless const_defined?(:OriginalCurlEasy)
-
-
def self.enable!
-
Curl.send(:remove_const, :Easy)
-
Curl.send(:const_set, :Easy, Curl::WebMockCurlEasy)
-
end
-
-
def self.disable!
-
Curl.send(:remove_const, :Easy)
-
Curl.send(:const_set, :Easy, OriginalCurlEasy)
-
end
-
-
# Borrowed from Patron:
-
# http://github.com/toland/patron/blob/master/lib/patron/response.rb
-
def self.parse_header_string(header_string)
-
status, headers = nil, {}
-
-
header_string.split(/\r\n/).each do |header|
-
if header =~ %r|^HTTP/1.[01] \d\d\d (.*)|
-
status = $1
-
else
-
parts = header.split(':', 2)
-
unless parts.empty?
-
parts[1].strip! unless parts[1].nil?
-
if headers.has_key?(parts[0])
-
headers[parts[0]] = [headers[parts[0]]] unless headers[parts[0]].kind_of? Array
-
headers[parts[0]] << parts[1]
-
else
-
headers[parts[0]] = parts[1]
-
end
-
end
-
end
-
end
-
-
return status, headers
-
end
-
end
-
end
-
end
-
-
module Curl
-
class WebMockCurlEasy < Curl::Easy
-
def curb_or_webmock
-
-
request_signature = build_request_signature
-
WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)
-
-
if webmock_response = WebMock::StubRegistry.instance.response_for_request(request_signature)
-
build_curb_response(webmock_response)
-
WebMock::CallbackRegistry.invoke_callbacks(
-
{:lib => :curb}, request_signature, webmock_response)
-
invoke_curb_callbacks
-
true
-
elsif WebMock.net_connect_allowed?(request_signature.uri)
-
res = yield
-
if WebMock::CallbackRegistry.any_callbacks?
-
webmock_response = build_webmock_response
-
WebMock::CallbackRegistry.invoke_callbacks(
-
{:lib => :curb, :real_request => true}, request_signature,
-
webmock_response)
-
end
-
res
-
else
-
raise WebMock::NetConnectNotAllowedError.new(request_signature)
-
end
-
end
-
-
def build_request_signature
-
method = @webmock_method.to_s.downcase.to_sym
-
-
uri = WebMock::Util::URI.heuristic_parse(self.url)
-
uri.path = uri.normalized_path.gsub("[^:]//","/")
-
uri.user = self.username
-
uri.password = self.password
-
-
request_body = case method
-
when :post
-
self.post_body || @post_body
-
when :put
-
@put_data
-
else
-
nil
-
end
-
-
request_signature = WebMock::RequestSignature.new(
-
method,
-
uri.to_s,
-
:body => request_body,
-
:headers => self.headers
-
)
-
request_signature
-
end
-
-
def build_curb_response(webmock_response)
-
raise Curl::Err::TimeoutError if webmock_response.should_timeout
-
webmock_response.raise_error_if_any
-
-
@body_str = webmock_response.body
-
@response_code = webmock_response.status[0]
-
-
@header_str = "HTTP/1.1 #{webmock_response.status[0]} #{webmock_response.status[1]}\r\n"
-
if webmock_response.headers
-
@header_str << webmock_response.headers.map do |k,v|
-
"#{k}: #{v.is_a?(Array) ? v.join(", ") : v}"
-
end.join("\r\n")
-
-
location = webmock_response.headers['Location']
-
if self.follow_location? && location
-
@last_effective_url = location
-
webmock_follow_location(location)
-
end
-
-
@content_type = webmock_response.headers["Content-Type"]
-
@transfer_encoding = webmock_response.headers["Transfer-Encoding"]
-
end
-
-
@last_effective_url ||= self.url
-
end
-
-
def webmock_follow_location(location)
-
first_url = self.url
-
self.url = location
-
-
curb_or_webmock do
-
send( "http_#{@webmock_method}_without_webmock" )
-
end
-
-
self.url = first_url
-
end
-
-
def invoke_curb_callbacks
-
@on_progress.call(0.0,1.0,0.0,1.0) if @on_progress
-
self.header_str.lines.each { |header_line| @on_header.call header_line } if @on_header
-
if @on_body
-
if chunked_response?
-
self.body_str.each do |chunk|
-
@on_body.call(chunk)
-
end
-
else
-
@on_body.call(self.body_str)
-
end
-
end
-
@on_complete.call(self) if @on_complete
-
-
case response_code
-
when 200..299
-
@on_success.call(self) if @on_success
-
when 400..499
-
@on_missing.call(self, self.response_code) if @on_missing
-
when 500..599
-
@on_failure.call(self, self.response_code) if @on_failure
-
end
-
end
-
-
def chunked_response?
-
@transfer_encoding == 'chunked' && self.body_str.respond_to?(:each)
-
end
-
-
def build_webmock_response
-
status, headers =
-
WebMock::HttpLibAdapters::CurbAdapter.parse_header_string(self.header_str)
-
-
webmock_response = WebMock::Response.new
-
webmock_response.status = [self.response_code, status]
-
webmock_response.body = self.body_str
-
webmock_response.headers = headers
-
webmock_response
-
end
-
-
###
-
### Mocks of Curl::Easy methods below here.
-
###
-
-
def http(method)
-
@webmock_method = method
-
super
-
end
-
-
%w[ get head delete ].each do |verb|
-
define_method "http_#{verb}" do
-
@webmock_method = verb
-
super()
-
end
-
end
-
-
def http_put data = nil
-
@webmock_method = :put
-
@put_data = data if data
-
super
-
end
-
alias put http_put
-
-
def http_post *data
-
@webmock_method = :post
-
@post_body = data.join('&') if data && !data.empty?
-
super
-
end
-
alias post http_post
-
-
def perform
-
@webmock_method ||= :get
-
curb_or_webmock { super }
-
end
-
-
def put_data= data
-
@webmock_method = :put
-
@put_data = data
-
super
-
end
-
-
def post_body= data
-
@webmock_method = :post
-
super
-
end
-
-
def delete= value
-
@webmock_method = :delete if value
-
super
-
end
-
-
def head= value
-
@webmock_method = :head if value
-
super
-
end
-
-
def body_str
-
@body_str || super
-
end
-
alias body body_str
-
-
def response_code
-
@response_code || super
-
end
-
-
def header_str
-
@header_str || super
-
end
-
alias head header_str
-
-
def last_effective_url
-
@last_effective_url || super
-
end
-
-
def content_type
-
@content_type || super
-
end
-
-
%w[ success failure missing header body complete progress ].each do |callback|
-
class_eval <<-METHOD, __FILE__, __LINE__
-
def on_#{callback} &block
-
@on_#{callback} = block
-
super
-
end
-
METHOD
-
end
-
end
-
end
-
end
-
1
if defined?(EventMachine::HttpRequest)
-
module WebMock
-
module HttpLibAdapters
-
class EmHttpRequestAdapter < HttpLibAdapter
-
adapter_for :em_http_request
-
-
OriginalHttpRequest = EventMachine::HttpRequest unless const_defined?(:OriginalHttpRequest)
-
-
def self.enable!
-
EventMachine.send(:remove_const, :HttpRequest)
-
EventMachine.send(:const_set, :HttpRequest, EventMachine::WebMockHttpRequest)
-
end
-
-
def self.disable!
-
EventMachine.send(:remove_const, :HttpRequest)
-
EventMachine.send(:const_set, :HttpRequest, OriginalHttpRequest)
-
end
-
end
-
end
-
end
-
-
-
module EventMachine
-
class WebMockHttpRequest < EventMachine::HttpRequest
-
-
include HttpEncoding
-
-
class WebMockHttpClient < EventMachine::HttpClient
-
-
def setup(response, uri, error = nil)
-
@last_effective_url = @uri = uri
-
if error
-
on_error(error)
-
fail(self)
-
else
-
EM.next_tick do
-
receive_data(response)
-
succeed(self)
-
end
-
end
-
end
-
-
def unbind
-
end
-
-
def close_connection
-
end
-
end
-
-
def send_request(&block)
-
request_signature = build_request_signature
-
-
WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)
-
-
if webmock_response = WebMock::StubRegistry.instance.response_for_request(request_signature)
-
WebMock::CallbackRegistry.invoke_callbacks(
-
{:lib => :em_http_request}, request_signature, webmock_response)
-
client = WebMockHttpClient.new(nil)
-
client.on_error("WebMock timeout error") if webmock_response.should_timeout
-
client.setup(make_raw_response(webmock_response), @uri,
-
webmock_response.should_timeout ? "WebMock timeout error" : nil)
-
client
-
elsif WebMock.net_connect_allowed?(request_signature.uri)
-
http = super
-
http.callback {
-
if WebMock::CallbackRegistry.any_callbacks?
-
webmock_response = build_webmock_response(http)
-
WebMock::CallbackRegistry.invoke_callbacks(
-
{:lib => :em_http_request, :real_request => true}, request_signature,
-
webmock_response)
-
end
-
}
-
http
-
else
-
raise WebMock::NetConnectNotAllowedError.new(request_signature)
-
end
-
end
-
-
private
-
-
def build_webmock_response(http)
-
webmock_response = WebMock::Response.new
-
webmock_response.status = [http.response_header.status, http.response_header.http_reason]
-
webmock_response.headers = http.response_header
-
webmock_response.body = http.response
-
webmock_response
-
end
-
-
def build_request_signature
-
if @req
-
options = @req.options
-
method = @req.method
-
uri = @req.uri.dup
-
else
-
options = @options
-
method = @method
-
uri = @uri.dup
-
end
-
-
if options[:authorization] || options['authorization']
-
auth = (options[:authorization] || options['authorization'])
-
userinfo = auth.join(':')
-
userinfo = WebMock::Util::URI.encode_unsafe_chars_in_userinfo(userinfo)
-
options.reject! {|k,v| k.to_s == 'authorization' } #we added it to url userinfo
-
uri.userinfo = userinfo
-
end
-
-
uri.query = encode_query(@req.uri, options[:query]).slice(/\?(.*)/, 1)
-
-
body = options[:body] || options['body']
-
body = form_encode_body(body) if body.is_a?(Hash)
-
-
WebMock::RequestSignature.new(
-
method.downcase.to_sym,
-
uri.to_s,
-
:body => body,
-
:headers => (options[:head] || options['head'])
-
)
-
end
-
-
-
def make_raw_response(response)
-
response.raise_error_if_any
-
-
status, headers, body = response.status, response.headers, response.body
-
-
response_string = []
-
response_string << "HTTP/1.1 #{status[0]} #{status[1]}"
-
-
headers.each do |header, value|
-
value = value.join(", ") if value.is_a?(Array)
-
-
# WebMock's internal processing will not handle the body
-
# correctly if the header indicates that it is chunked, unless
-
# we also create all the chunks.
-
# It's far easier just to remove the header.
-
next if header =~ /transfer-encoding/i && value =~/chunked/i
-
-
response_string << "#{header}: #{value}"
-
end if headers
-
-
response_string << "" << body
-
response_string.join("\n")
-
end
-
end
-
end
-
end
-
1
begin
-
1
require 'em-http-request'
-
rescue LoadError
-
# em-http-request not found
-
end
-
-
1
if defined?(EventMachine::HttpConnection)
-
require File.expand_path(File.dirname(__FILE__) + '/em_http_request/em_http_request_1_x')
-
else
-
1
require File.expand_path(File.dirname(__FILE__) + '/em_http_request/em_http_request_0_x')
-
end
-
1
begin
-
1
require 'excon'
-
rescue LoadError
-
# excon not found
-
end
-
-
1
if defined?(Excon)
-
WebMock::VersionChecker.new('Excon', Excon::VERSION, '0.27.5').check_version!
-
-
module WebMock
-
module HttpLibAdapters
-
-
class ExconAdapter < HttpLibAdapter
-
PARAMS_TO_DELETE = [:expects, :idempotent,
-
:instrumentor_name, :instrumentor,
-
:response_block,
-
:__construction_args, :stack,
-
:connection, :response]
-
-
adapter_for :excon
-
-
def self.enable!
-
self.add_excon_stub
-
end
-
-
def self.disable!
-
self.remove_excon_stub
-
end
-
-
def self.add_excon_stub
-
if not @stub
-
@original_excon_mock_default = ::Excon.defaults[:mock]
-
::Excon.defaults[:mock] = true
-
@stub = ::Excon.stub({}) do |params|
-
self.handle_request(params)
-
end
-
end
-
end
-
-
def self.remove_excon_stub
-
::Excon.defaults[:mock] = @original_excon_mock_default
-
@original_excon_mock_default = nil
-
Excon.stubs.delete(@stub)
-
@stub = nil
-
end
-
-
def self.handle_request(params)
-
mock_request = self.build_request params.dup
-
WebMock::RequestRegistry.instance.requested_signatures.put(mock_request)
-
-
if mock_response = WebMock::StubRegistry.instance.response_for_request(mock_request)
-
self.perform_callbacks(mock_request, mock_response, :real_request => false)
-
response = self.real_response(mock_response)
-
response
-
elsif WebMock.net_connect_allowed?(mock_request.uri)
-
conn = new_excon_connection(params)
-
real_response = conn.request(request_params_from(params.merge(:mock => false)))
-
-
ExconAdapter.perform_callbacks(mock_request, ExconAdapter.mock_response(real_response), :real_request => true)
-
-
real_response.data
-
else
-
raise WebMock::NetConnectNotAllowedError.new(mock_request)
-
end
-
end
-
-
def self.new_excon_connection(params)
-
# Ensure the connection is constructed with the exact same args
-
# that the orginal connection was constructed with.
-
args = params.fetch(:__construction_args)
-
::Excon::Connection.new(connection_params_from args.merge(:mock => false))
-
end
-
-
def self.connection_params_from(hash)
-
hash = hash.dup
-
PARAMS_TO_DELETE.each { |key| hash.delete(key) }
-
hash
-
end
-
-
def self.request_params_from(hash)
-
hash = hash.dup
-
if defined?(Excon::VALID_REQUEST_KEYS)
-
hash.reject! {|key,_| !Excon::VALID_REQUEST_KEYS.include?(key) }
-
end
-
PARAMS_TO_DELETE.each { |key| hash.delete(key) }
-
hash
-
end
-
-
def self.to_query(hash)
-
string = ""
-
for key, values in hash
-
if values.nil?
-
string << key.to_s << '&'
-
else
-
for value in [*values]
-
string << key.to_s << '=' << CGI.escape(value.to_s) << '&'
-
end
-
end
-
end
-
string.chop! # remove trailing '&'
-
end
-
-
def self.build_request(params)
-
params = params.dup
-
method = (params.delete(:method) || :get).to_s.downcase.to_sym
-
params[:query] = to_query(params[:query]) if params[:query].is_a?(Hash)
-
uri = Addressable::URI.new(params).to_s
-
WebMock::RequestSignature.new method, uri, :body => body_from(params), :headers => params[:headers]
-
end
-
-
def self.body_from(params)
-
body = params[:body]
-
return body unless body.respond_to?(:read)
-
-
contents = body.read
-
body.rewind if body.respond_to?(:rewind)
-
contents
-
end
-
-
def self.real_response(mock)
-
raise Excon::Errors::Timeout if mock.should_timeout
-
mock.raise_error_if_any
-
{
-
:body => mock.body,
-
:status => mock.status[0].to_i,
-
:headers => mock.headers || {}
-
}
-
end
-
-
def self.mock_response(real)
-
mock = WebMock::Response.new
-
mock.status = real.status
-
mock.headers = real.headers
-
mock.body = real.body.dup
-
mock
-
end
-
-
def self.perform_callbacks(request, response, options = {})
-
return unless WebMock::CallbackRegistry.any_callbacks?
-
WebMock::CallbackRegistry.invoke_callbacks(options.merge(:lib => :excon), request, response)
-
end
-
end
-
end
-
end
-
-
Excon::Connection.class_eval do
-
def self.new(args)
-
args.delete(:__construction_args)
-
super(args).tap do |instance|
-
instance.data[:__construction_args] = args
-
end
-
end
-
end
-
end
-
1
begin
-
1
require "http"
-
__http_gem_found__ = true
-
rescue LoadError
-
1
__http_gem_found__ = false
-
end
-
-
1
if __http_gem_found__
-
WebMock::VersionChecker.new("HTTP Gem", HTTP::VERSION, "0.6.0").check_version!
-
-
module WebMock
-
module HttpLibAdapters
-
class HttpGemAdapter < HttpLibAdapter
-
adapter_for :http_gem
-
-
class << self
-
def enable!
-
@enabled = true
-
end
-
-
def disable!
-
@enabled = false
-
end
-
-
def enabled?
-
@enabled
-
end
-
end
-
end
-
end
-
end
-
-
require "webmock/http_lib_adapters/http_gem/client"
-
require "webmock/http_lib_adapters/http_gem/request"
-
require "webmock/http_lib_adapters/http_gem/response"
-
require "webmock/http_lib_adapters/http_gem/streamer"
-
require "webmock/http_lib_adapters/http_gem/webmock"
-
end
-
1
module WebMock
-
1
class HttpLibAdapter
-
1
def self.adapter_for(lib)
-
1
WebMock::HttpLibAdapterRegistry.instance.register(lib, self)
-
end
-
end
-
end
-
1
module WebMock
-
1
class HttpLibAdapterRegistry
-
1
include Singleton
-
-
1
attr_accessor :http_lib_adapters
-
-
1
def initialize
-
1
@http_lib_adapters = {}
-
end
-
-
1
def register(lib, adapter)
-
1
@http_lib_adapters[lib] = adapter
-
end
-
-
1
def each_adapter(&block)
-
1
@http_lib_adapters.each(&block)
-
end
-
end
-
end
-
1
begin
-
1
require 'httpclient'
-
rescue LoadError
-
# httpclient not found
-
end
-
-
1
if defined?(::HTTPClient)
-
-
module WebMock
-
module HttpLibAdapters
-
class HTTPClientAdapter < HttpLibAdapter
-
adapter_for :httpclient
-
-
OriginalHttpClient = ::HTTPClient unless const_defined?(:OriginalHttpClient)
-
-
def self.enable!
-
Object.send(:remove_const, :HTTPClient)
-
Object.send(:const_set, :HTTPClient, WebMockHTTPClient)
-
end
-
-
def self.disable!
-
Object.send(:remove_const, :HTTPClient)
-
Object.send(:const_set, :HTTPClient, OriginalHttpClient)
-
end
-
end
-
end
-
end
-
-
-
class WebMockHTTPClient < HTTPClient
-
alias_method :do_get_block_without_webmock, :do_get_block
-
alias_method :do_get_stream_without_webmock, :do_get_stream
-
-
def do_get_block(req, proxy, conn, &block)
-
do_get(req, proxy, conn, false, &block)
-
end
-
-
def do_get_stream(req, proxy, conn, &block)
-
do_get(req, proxy, conn, true, &block)
-
end
-
-
def do_get(req, proxy, conn, stream = false, &block)
-
request_signature = build_request_signature(req, :reuse_existing)
-
-
WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)
-
-
if webmock_responses[request_signature]
-
webmock_response = webmock_responses.delete(request_signature)
-
response = build_httpclient_response(webmock_response, stream, req.header, &block)
-
@request_filter.each do |filter|
-
filter.filter_response(req, response)
-
end
-
res = conn.push(response)
-
WebMock::CallbackRegistry.invoke_callbacks(
-
{:lib => :httpclient}, request_signature, webmock_response)
-
res
-
elsif WebMock.net_connect_allowed?(request_signature.uri)
-
# in case there is a nil entry in the hash...
-
webmock_responses.delete(request_signature)
-
-
res = if stream
-
do_get_stream_without_webmock(req, proxy, conn, &block)
-
else
-
do_get_block_without_webmock(req, proxy, conn, &block)
-
end
-
res = conn.pop
-
conn.push(res)
-
if WebMock::CallbackRegistry.any_callbacks?
-
webmock_response = build_webmock_response(res)
-
WebMock::CallbackRegistry.invoke_callbacks(
-
{:lib => :httpclient, :real_request => true}, request_signature,
-
webmock_response)
-
end
-
res
-
else
-
raise WebMock::NetConnectNotAllowedError.new(request_signature)
-
end
-
end
-
-
def do_request_async(method, uri, query, body, extheader)
-
req = create_request(method, uri, query, body, extheader)
-
request_signature = build_request_signature(req)
-
webmock_request_signatures << request_signature
-
-
if webmock_responses[request_signature] || WebMock.net_connect_allowed?(request_signature.uri)
-
super
-
else
-
raise WebMock::NetConnectNotAllowedError.new(request_signature)
-
end
-
end
-
-
def build_httpclient_response(webmock_response, stream = false, req_header = nil, &block)
-
body = stream ? StringIO.new(webmock_response.body) : webmock_response.body
-
response = HTTP::Message.new_response(body, req_header)
-
response.header.init_response(webmock_response.status[0])
-
response.reason=webmock_response.status[1]
-
webmock_response.headers.to_a.each { |name, value| response.header.set(name, value) }
-
-
raise HTTPClient::TimeoutError if webmock_response.should_timeout
-
webmock_response.raise_error_if_any
-
-
block.call(response, body) if block
-
-
response
-
end
-
end
-
-
def build_webmock_response(httpclient_response)
-
webmock_response = WebMock::Response.new
-
webmock_response.status = [httpclient_response.status, httpclient_response.reason]
-
-
webmock_response.headers = {}.tap do |hash|
-
httpclient_response.header.all.each do |(key, value)|
-
if hash.has_key?(key)
-
hash[key] = Array(hash[key]) + [value]
-
else
-
hash[key] = value
-
end
-
end
-
end
-
-
if httpclient_response.content.respond_to?(:read)
-
webmock_response.body = httpclient_response.content.read
-
body = HTTP::Message::Body.new
-
body.init_response(StringIO.new(webmock_response.body))
-
httpclient_response.body = body
-
else
-
webmock_response.body = httpclient_response.content
-
end
-
webmock_response
-
end
-
-
def build_request_signature(req, reuse_existing = false)
-
uri = WebMock::Util::URI.heuristic_parse(req.header.request_uri.to_s)
-
uri.query = WebMock::Util::QueryMapper.values_to_query(req.header.request_query, :notation => WebMock::Config.instance.query_values_notation) if req.header.request_query
-
uri.port = req.header.request_uri.port
-
uri = uri.omit(:userinfo)
-
-
auth = www_auth.basic_auth
-
auth.challenge(req.header.request_uri, nil)
-
-
@request_filter.each do |filter|
-
filter.filter_request(req)
-
end
-
-
headers = req.header.all.inject({}) do |hdrs, header|
-
hdrs[header[0]] ||= []
-
hdrs[header[0]] << header[1]
-
hdrs
-
end
-
headers = headers_from_session(uri).merge(headers)
-
-
if (auth_cred = auth.get(req)) && auth.scheme == 'Basic'
-
userinfo = WebMock::Util::Headers.decode_userinfo_from_header(auth_cred)
-
userinfo = WebMock::Util::URI.encode_unsafe_chars_in_userinfo(userinfo)
-
headers.reject! {|k,v| k =~ /[Aa]uthorization/ && v =~ /^Basic / } #we added it to url userinfo
-
uri.userinfo = userinfo
-
end
-
-
signature = WebMock::RequestSignature.new(
-
req.header.request_method.downcase.to_sym,
-
uri.to_s,
-
:body => req.http_body.dump,
-
:headers => headers
-
)
-
-
# reuse a previous identical signature object if we stored one for later use
-
if reuse_existing && previous_signature = previous_signature_for(signature)
-
return previous_signature
-
end
-
-
signature
-
end
-
-
def webmock_responses
-
@webmock_responses ||= Hash.new do |hash, request_signature|
-
hash[request_signature] = WebMock::StubRegistry.instance.response_for_request(request_signature)
-
end
-
end
-
-
def webmock_request_signatures
-
@webmock_request_signatures ||= []
-
end
-
-
def previous_signature_for(signature)
-
return nil unless index = webmock_request_signatures.index(signature)
-
webmock_request_signatures.delete_at(index)
-
end
-
-
private
-
-
# some of the headers sent by HTTPClient are derived from
-
# the client session
-
def headers_from_session(uri)
-
session_headers = HTTP::Message::Headers.new
-
@session_manager.send(:open, uri).send(:set_header, MessageMock.new(session_headers))
-
session_headers.all.inject({}) do |hdrs, header|
-
hdrs[header[0]] = header[1]
-
hdrs
-
end
-
end
-
-
# Mocks a HTTPClient HTTP::Message
-
class MessageMock
-
attr_reader :header
-
-
def initialize(headers)
-
@header = headers
-
end
-
-
def http_version=(value);end
-
end
-
-
end
-
1
require 'net/http'
-
1
require 'net/https'
-
1
require 'stringio'
-
1
require File.join(File.dirname(__FILE__), 'net_http_response')
-
-
-
1
module WebMock
-
1
module HttpLibAdapters
-
1
class NetHttpAdapter < HttpLibAdapter
-
1
adapter_for :net_http
-
-
1
OriginalNetHTTP = Net::HTTP unless const_defined?(:OriginalNetHTTP)
-
1
OriginalNetBufferedIO = Net::BufferedIO unless const_defined?(:OriginalNetBufferedIO)
-
-
1
def self.enable!
-
1
Net.send(:remove_const, :BufferedIO)
-
1
Net.send(:remove_const, :HTTP)
-
1
Net.send(:remove_const, :HTTPSession)
-
1
Net.send(:const_set, :HTTP, @webMockNetHTTP)
-
1
Net.send(:const_set, :HTTPSession, @webMockNetHTTP)
-
1
Net.send(:const_set, :BufferedIO, Net::WebMockNetBufferedIO)
-
end
-
-
1
def self.disable!
-
1
Net.send(:remove_const, :BufferedIO)
-
1
Net.send(:remove_const, :HTTP)
-
1
Net.send(:remove_const, :HTTPSession)
-
1
Net.send(:const_set, :HTTP, OriginalNetHTTP)
-
1
Net.send(:const_set, :HTTPSession, OriginalNetHTTP)
-
1
Net.send(:const_set, :BufferedIO, OriginalNetBufferedIO)
-
-
#copy all constants from @webMockNetHTTP to original Net::HTTP
-
#in case any constants were added to @webMockNetHTTP instead of Net::HTTP
-
#after WebMock was enabled.
-
#i.e Net::HTTP::DigestAuth
-
1
@webMockNetHTTP.constants.each do |constant|
-
23
if !OriginalNetHTTP.constants.map(&:to_s).include?(constant.to_s)
-
OriginalNetHTTP.send(:const_set, constant, @webMockNetHTTP.const_get(constant))
-
end
-
end
-
end
-
-
1
@webMockNetHTTP = Class.new(Net::HTTP) do
-
1
class << self
-
1
def socket_type
-
StubSocket
-
end
-
-
1
if Module.method(:const_defined?).arity == 1
-
def const_defined?(name)
-
super || self.superclass.const_defined?(name)
-
end
-
else
-
1
def const_defined?(name, inherit=true)
-
super || self.superclass.const_defined?(name, inherit)
-
end
-
end
-
-
1
if Module.method(:const_get).arity != 1
-
1
def const_get(name, inherit=true)
-
super
-
rescue NameError
-
self.superclass.const_get(name, inherit)
-
end
-
end
-
-
1
if Module.method(:constants).arity != 0
-
1
def constants(inherit=true)
-
1
(super + self.superclass.constants(inherit)).uniq
-
end
-
end
-
end
-
-
1
def request(request, body = nil, &block)
-
request_signature = WebMock::NetHTTPUtility.request_signature_from_request(self, request, body)
-
-
WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)
-
-
if webmock_response = WebMock::StubRegistry.instance.response_for_request(request_signature)
-
@socket = Net::HTTP.socket_type.new
-
WebMock::CallbackRegistry.invoke_callbacks(
-
{:lib => :net_http}, request_signature, webmock_response)
-
build_net_http_response(webmock_response, &block)
-
elsif WebMock.net_connect_allowed?(request_signature.uri)
-
check_right_http_connection
-
after_request = lambda do |response|
-
if WebMock::CallbackRegistry.any_callbacks?
-
webmock_response = build_webmock_response(response)
-
WebMock::CallbackRegistry.invoke_callbacks(
-
{:lib => :net_http, :real_request => true}, request_signature, webmock_response)
-
end
-
response.extend Net::WebMockHTTPResponse
-
block.call response if block
-
response
-
end
-
super_with_after_request = lambda {
-
response = super(request, nil, &nil)
-
after_request.call(response)
-
}
-
if started?
-
if WebMock::Config.instance.net_http_connect_on_start
-
super_with_after_request.call
-
else
-
start_with_connect_without_finish {
-
super_with_after_request.call
-
}
-
end
-
else
-
start_with_connect {
-
super_with_after_request.call
-
}
-
end
-
else
-
raise WebMock::NetConnectNotAllowedError.new(request_signature)
-
end
-
end
-
-
1
def start_without_connect
-
raise IOError, 'HTTP session already opened' if @started
-
if block_given?
-
begin
-
@started = true
-
return yield(self)
-
ensure
-
do_finish
-
end
-
end
-
@started = true
-
self
-
end
-
-
-
1
def start_with_connect_without_finish # :yield: http
-
if block_given?
-
begin
-
do_start
-
return yield(self)
-
end
-
end
-
do_start
-
self
-
end
-
-
1
alias_method :start_with_connect, :start
-
-
1
def start(&block)
-
if WebMock::Config.instance.net_http_connect_on_start
-
super(&block)
-
else
-
start_without_connect(&block)
-
end
-
end
-
-
1
def build_net_http_response(webmock_response, &block)
-
response = Net::HTTPResponse.send(:response_class, webmock_response.status[0].to_s).new("1.0", webmock_response.status[0].to_s, webmock_response.status[1])
-
body = webmock_response.body
-
body = nil if body.to_s == ''
-
-
response.instance_variable_set(:@body, body)
-
webmock_response.headers.to_a.each do |name, values|
-
values = [values] unless values.is_a?(Array)
-
values.each do |value|
-
response.add_field(name, value)
-
end
-
end
-
-
response.instance_variable_set(:@read, true)
-
-
response.extend Net::WebMockHTTPResponse
-
-
raise Timeout::Error, "execution expired" if webmock_response.should_timeout
-
-
webmock_response.raise_error_if_any
-
-
yield response if block_given?
-
-
response
-
end
-
-
1
def build_webmock_response(net_http_response)
-
webmock_response = WebMock::Response.new
-
webmock_response.status = [
-
net_http_response.code.to_i,
-
net_http_response.message]
-
webmock_response.headers = net_http_response.to_hash
-
webmock_response.body = net_http_response.body
-
webmock_response
-
end
-
-
-
1
def check_right_http_connection
-
unless @@alredy_checked_for_right_http_connection ||= false
-
WebMock::NetHTTPUtility.puts_warning_for_right_http_if_needed
-
@@alredy_checked_for_right_http_connection = true
-
end
-
end
-
end
-
1
@webMockNetHTTP.version_1_2
-
[
-
[:Get, Net::HTTP::Get],
-
[:Post, Net::HTTP::Post],
-
[:Put, Net::HTTP::Put],
-
[:Delete, Net::HTTP::Delete],
-
[:Head, Net::HTTP::Head],
-
[:Options, Net::HTTP::Options]
-
1
].each do |c|
-
6
@webMockNetHTTP.const_set(c[0], c[1])
-
end
-
end
-
end
-
end
-
-
1
class StubSocket #:nodoc:
-
-
1
attr_accessor :read_timeout, :continue_timeout
-
-
1
def initialize(*args)
-
end
-
-
1
def closed?
-
@closed ||= true
-
end
-
-
1
def readuntil(*args)
-
end
-
-
end
-
-
1
module Net #:nodoc: all
-
-
1
class WebMockNetBufferedIO < BufferedIO
-
1
def initialize(io, debug_output = nil)
-
@read_timeout = 60
-
@rbuf = ''
-
@debug_output = debug_output
-
-
@io = case io
-
when Socket, OpenSSL::SSL::SSLSocket, IO, StringIO
-
io
-
when String
-
StringIO.new(io)
-
end
-
raise "Unable to create local socket" unless @io
-
end
-
end
-
-
end
-
-
-
1
module WebMock
-
1
module NetHTTPUtility
-
-
1
def self.request_signature_from_request(net_http, request, body = nil)
-
protocol = net_http.use_ssl? ? "https" : "http"
-
-
path = request.path
-
-
if path.respond_to?(:request_uri) #https://github.com/bblimke/webmock/issues/288
-
path = path.request_uri
-
end
-
-
path = WebMock::Util::URI.heuristic_parse(path).request_uri if path =~ /^http/
-
-
if request["authorization"] =~ /^Basic /
-
userinfo = WebMock::Util::Headers.decode_userinfo_from_header(request["authorization"])
-
userinfo = WebMock::Util::URI.encode_unsafe_chars_in_userinfo(userinfo) + "@"
-
else
-
userinfo = ""
-
end
-
-
uri = "#{protocol}://#{userinfo}#{net_http.address}:#{net_http.port}#{path}"
-
method = request.method.downcase.to_sym
-
-
headers = Hash[*request.to_hash.map {|k,v| [k, v]}.inject([]) {|r,x| r + x}]
-
-
headers.reject! {|k,v| k =~ /[Aa]uthorization/ && v.first =~ /^Basic / } #we added it to url userinfo
-
-
-
if request.body_stream
-
body = request.body_stream.read
-
request.body_stream = nil
-
end
-
-
if body != nil && body.respond_to?(:read)
-
request.set_body_internal body.read
-
else
-
request.set_body_internal body
-
end
-
-
WebMock::RequestSignature.new(method, uri, :body => request.body, :headers => headers)
-
end
-
-
-
1
def self.check_right_http_connection
-
1
@was_right_http_connection_loaded = defined?(RightHttpConnection)
-
end
-
-
1
def self.puts_warning_for_right_http_if_needed
-
if !@was_right_http_connection_loaded && defined?(RightHttpConnection)
-
$stderr.puts "\nWarning: RightHttpConnection has to be required before WebMock is required !!!\n"
-
end
-
end
-
-
end
-
end
-
-
1
WebMock::NetHTTPUtility.check_right_http_connection
-
# This code is entierly copied from VCR (http://github.com/myronmarston/vcr) by courtesy of Myron Marston
-
-
# A Net::HTTP response that has already been read raises an IOError when #read_body
-
# is called with a destination string or block.
-
#
-
# This causes a problem when VCR records a response--it reads the body before yielding
-
# the response, and if the code that is consuming the HTTP requests uses #read_body, it
-
# can cause an error.
-
#
-
# This is a bit of a hack, but it allows a Net::HTTP response to be "re-read"
-
# after it has aleady been read. This attemps to preserve the behavior of
-
# #read_body, acting just as if it had never been read.
-
-
-
1
module Net
-
1
module WebMockHTTPResponse
-
1
def read_body(dest = nil, &block)
-
if !(defined?(@__read_body_previously_called).nil?) && @__read_body_previously_called
-
return super
-
end
-
return @body if dest.nil? && block.nil?
-
raise ArgumentError.new("both arg and block given for HTTP method") if dest && block
-
return nil if @body.nil?
-
-
dest ||= ::Net::ReadAdapter.new(block)
-
dest << @body
-
@body = dest
-
ensure
-
# allow subsequent calls to #read_body to proceed as normal, without our hack...
-
@__read_body_previously_called = true
-
end
-
end
-
end
-
-
1
begin
-
1
require 'patron'
-
rescue LoadError
-
# patron not found
-
end
-
-
1
if defined?(::Patron)
-
module WebMock
-
module HttpLibAdapters
-
class PatronAdapter < ::WebMock::HttpLibAdapter
-
adapter_for :patron
-
-
OriginalPatronSession = ::Patron::Session unless const_defined?(:OriginalPatronSession)
-
-
class WebMockPatronSession < ::Patron::Session
-
def handle_request(req)
-
request_signature =
-
WebMock::HttpLibAdapters::PatronAdapter.build_request_signature(req)
-
-
WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)
-
-
if webmock_response = WebMock::StubRegistry.instance.response_for_request(request_signature)
-
WebMock::HttpLibAdapters::PatronAdapter.
-
handle_file_name(req, webmock_response)
-
res = WebMock::HttpLibAdapters::PatronAdapter.
-
build_patron_response(webmock_response, default_response_charset)
-
WebMock::CallbackRegistry.invoke_callbacks(
-
{:lib => :patron}, request_signature, webmock_response)
-
res
-
elsif WebMock.net_connect_allowed?(request_signature.uri)
-
res = super
-
if WebMock::CallbackRegistry.any_callbacks?
-
webmock_response = WebMock::HttpLibAdapters::PatronAdapter.
-
build_webmock_response(res)
-
WebMock::CallbackRegistry.invoke_callbacks(
-
{:lib => :patron, :real_request => true}, request_signature,
-
webmock_response)
-
end
-
res
-
else
-
raise WebMock::NetConnectNotAllowedError.new(request_signature)
-
end
-
end
-
end
-
-
def self.enable!
-
Patron.send(:remove_const, :Session)
-
Patron.send(:const_set, :Session, WebMockPatronSession)
-
end
-
-
def self.disable!
-
Patron.send(:remove_const, :Session)
-
Patron.send(:const_set, :Session, OriginalPatronSession)
-
end
-
-
def self.handle_file_name(req, webmock_response)
-
if req.action == :get && req.file_name
-
begin
-
File.open(req.file_name, "w") do |f|
-
f.write webmock_response.body
-
end
-
rescue Errno::EACCES
-
raise ArgumentError.new("Unable to open specified file.")
-
end
-
end
-
end
-
-
def self.build_request_signature(req)
-
uri = WebMock::Util::URI.heuristic_parse(req.url)
-
uri.path = uri.normalized_path.gsub("[^:]//","/")
-
uri.user = req.username
-
uri.password = req.password
-
-
if [:put, :post].include?(req.action)
-
if req.file_name
-
if !File.exist?(req.file_name) || !File.readable?(req.file_name)
-
raise ArgumentError.new("Unable to open specified file.")
-
end
-
request_body = File.read(req.file_name)
-
elsif req.upload_data
-
request_body = req.upload_data
-
else
-
raise ArgumentError.new("Must provide either data or a filename when doing a PUT or POST")
-
end
-
end
-
-
request_signature = WebMock::RequestSignature.new(
-
req.action,
-
uri.to_s,
-
:body => request_body,
-
:headers => req.headers
-
)
-
request_signature
-
end
-
-
def self.build_patron_response(webmock_response, default_response_charset)
-
raise ::Patron::TimeoutError if webmock_response.should_timeout
-
webmock_response.raise_error_if_any
-
-
header_fields = (webmock_response.headers || []).map { |(k, vs)| Array(vs).map { |v| "#{k}: #{v}" } }.flatten
-
status_line = "HTTP/1.1 #{webmock_response.status[0]} #{webmock_response.status[1]}"
-
header_data = ([status_line] + header_fields).join("\r\n")
-
-
::Patron::Response.new(
-
"",
-
webmock_response.status[0],
-
0,
-
header_data,
-
webmock_response.body,
-
default_response_charset
-
)
-
end
-
-
def self.build_webmock_response(patron_response)
-
webmock_response = WebMock::Response.new
-
reason = patron_response.status_line.
-
scan(%r(\AHTTP/(\d+\.\d+)\s+(\d\d\d)\s*([^\r\n]+)?))[0][2]
-
webmock_response.status = [patron_response.status, reason]
-
webmock_response.body = patron_response.body
-
webmock_response.headers = patron_response.headers
-
webmock_response
-
end
-
end
-
end
-
end
-
end
-
1
begin
-
1
require 'typhoeus'
-
rescue LoadError
-
# typhoeus not found
-
end
-
-
1
if defined?(Typhoeus)
-
WebMock::VersionChecker.new('Typhoeus', Typhoeus::VERSION, '0.3.2').check_version!
-
-
module WebMock
-
module HttpLibAdapters
-
class TyphoeusAdapter < HttpLibAdapter
-
adapter_for :typhoeus
-
-
def self.enable!
-
@disabled = false
-
add_before_callback
-
add_after_request_callback
-
::Typhoeus::Config.block_connection = true
-
end
-
-
def self.disable!
-
@disabled = true
-
remove_after_request_callback
-
remove_before_callback
-
::Typhoeus::Config.block_connection = false
-
end
-
-
def self.disabled?
-
!!@disabled
-
end
-
-
def self.add_before_callback
-
unless Typhoeus.before.include?(BEFORE_CALLBACK)
-
Typhoeus.before << BEFORE_CALLBACK
-
end
-
end
-
-
def self.remove_before_callback
-
Typhoeus.before.delete_if {|v| v == BEFORE_CALLBACK }
-
end
-
-
def self.add_after_request_callback
-
unless Typhoeus.on_complete.include?(AFTER_REQUEST_CALLBACK)
-
Typhoeus.on_complete << AFTER_REQUEST_CALLBACK
-
end
-
end
-
-
def self.remove_after_request_callback
-
Typhoeus.on_complete.delete_if {|v| v == AFTER_REQUEST_CALLBACK }
-
end
-
-
def self.build_request_signature(req)
-
uri = WebMock::Util::URI.heuristic_parse(req.url)
-
uri.path = uri.normalized_path.gsub("[^:]//","/")
-
if req.options[:userpwd]
-
uri.user, uri.password = req.options[:userpwd].split(':')
-
end
-
-
body = req.options[:body]
-
-
if body.is_a?(Hash)
-
body = WebMock::Util::QueryMapper.values_to_query(body)
-
end
-
-
request_signature = WebMock::RequestSignature.new(
-
req.options[:method] || :get,
-
uri.to_s,
-
:body => body,
-
:headers => req.options[:headers]
-
)
-
-
req.instance_variable_set(:@__webmock_request_signature, request_signature)
-
-
request_signature
-
end
-
-
def self.build_webmock_response(typhoeus_response)
-
webmock_response = WebMock::Response.new
-
webmock_response.status = [typhoeus_response.code, typhoeus_response.status_message]
-
webmock_response.body = typhoeus_response.body
-
webmock_response.headers = typhoeus_response.headers
-
webmock_response
-
end
-
-
def self.generate_typhoeus_response(request_signature, webmock_response)
-
response = if webmock_response.should_timeout
-
::Typhoeus::Response.new(
-
:code => 0,
-
:status_message => "",
-
:body => "",
-
:headers => {},
-
:return_code => :operation_timedout
-
)
-
else
-
::Typhoeus::Response.new(
-
:code => webmock_response.status[0],
-
:status_message => webmock_response.status[1],
-
:body => webmock_response.body,
-
:headers => webmock_response.headers
-
)
-
end
-
response.mock = :webmock
-
response
-
end
-
-
def self.request_hash(request_signature)
-
hash = {}
-
-
hash[:body] = request_signature.body
-
hash[:headers] = request_signature.headers
-
-
hash
-
end
-
-
AFTER_REQUEST_CALLBACK = Proc.new do |response|
-
request = response.request
-
request_signature = request.instance_variable_get(:@__webmock_request_signature)
-
webmock_response =
-
::WebMock::HttpLibAdapters::TyphoeusAdapter.
-
build_webmock_response(response)
-
if response.mock
-
WebMock::CallbackRegistry.invoke_callbacks(
-
{:lib => :typhoeus},
-
request_signature,
-
webmock_response
-
)
-
else
-
WebMock::CallbackRegistry.invoke_callbacks(
-
{:lib => :typhoeus, :real_request => true},
-
request_signature,
-
webmock_response
-
)
-
end
-
end
-
-
BEFORE_CALLBACK = Proc.new do |request|
-
Typhoeus::Expectation.all.delete_if {|e| e.from == :webmock }
-
res = true
-
-
unless WebMock::HttpLibAdapters::TyphoeusAdapter.disabled?
-
request_signature = ::WebMock::HttpLibAdapters::TyphoeusAdapter.build_request_signature(request)
-
request.block_connection = false;
-
-
::WebMock::RequestRegistry.instance.requested_signatures.put(request_signature)
-
-
if webmock_response = ::WebMock::StubRegistry.instance.response_for_request(request_signature)
-
# ::WebMock::HttpLibAdapters::TyphoeusAdapter.stub_typhoeus(request_signature, webmock_response, self)
-
response = ::WebMock::HttpLibAdapters::TyphoeusAdapter.generate_typhoeus_response(request_signature, webmock_response)
-
if request.respond_to?(:on_headers)
-
request.execute_headers_callbacks(response)
-
end
-
if request.respond_to?(:streaming?) && request.streaming?
-
response.options[:response_body] = ""
-
request.on_body.each { |callback| callback.call(webmock_response.body, response) }
-
end
-
request.finish(response)
-
webmock_response.raise_error_if_any
-
res = false
-
elsif !WebMock.net_connect_allowed?(request_signature.uri)
-
raise WebMock::NetConnectNotAllowedError.new(request_signature)
-
end
-
end
-
res
-
end
-
end
-
end
-
end
-
end
-
1
module WebMock
-
1
module Matchers
-
#this is a based on RSpec::Mocks::ArgumentMatchers::HashIncludingMatcher
-
#https://github.com/rspec/rspec-mocks/blob/master/lib/rspec/mocks/argument_matchers.rb
-
1
class HashIncludingMatcher
-
1
def initialize(expected)
-
@expected = Hash[WebMock::Util::HashKeysStringifier.stringify_keys!(expected, :deep => true).sort]
-
end
-
-
1
def ==(actual)
-
@expected.all? {|k,v| actual.has_key?(k) && v == actual[k]}
-
rescue NoMethodError
-
false
-
end
-
-
1
def inspect
-
"hash_including(#{@expected.inspect})"
-
end
-
-
1
def self.from_rspec_matcher(matcher)
-
new(matcher.instance_variable_get(:@expected))
-
end
-
end
-
-
#this is a based on RSpec::Mocks::ArgumentMatchers::AnyArgMatcher
-
1
class AnyArgMatcher
-
1
def initialize(ignore)
-
end
-
-
1
def ==(other)
-
true
-
end
-
end
-
-
end
-
end
-
1
module WebMock
-
1
class RackResponse < Response
-
1
def initialize(app)
-
@app = app
-
end
-
-
1
def evaluate(request)
-
env = build_rack_env(request)
-
-
status, headers, response = @app.call(env)
-
-
Response.new(
-
:body => body_from_rack_response(response),
-
:headers => headers,
-
:status => status
-
)
-
end
-
-
1
def body_from_rack_response(response)
-
body = ""
-
response.each { |line| body << line }
-
response.close if response.respond_to?(:close)
-
return body
-
end
-
-
1
def build_rack_env(request)
-
uri = request.uri
-
headers = (request.headers || {}).dup
-
body = request.body || ''
-
-
env = {
-
# CGI variables specified by Rack
-
'REQUEST_METHOD' => request.method.to_s.upcase,
-
'CONTENT_TYPE' => headers.delete('Content-Type'),
-
'CONTENT_LENGTH' => body.bytesize,
-
'PATH_INFO' => uri.path,
-
'QUERY_STRING' => uri.query || '',
-
'SERVER_NAME' => uri.host,
-
'SERVER_PORT' => uri.port,
-
'SCRIPT_NAME' => ""
-
}
-
-
env['HTTP_AUTHORIZATION'] = 'Basic ' + [uri.userinfo].pack('m').delete("\r\n") if uri.userinfo
-
-
# Rack-specific variables
-
env['rack.input'] = StringIO.new(body)
-
env['rack.errors'] = $stderr
-
env['rack.version'] = Rack::VERSION
-
env['rack.url_scheme'] = uri.scheme
-
env['rack.run_once'] = true
-
env['rack.session'] = session
-
env['rack.session.options'] = session_options
-
-
headers.each do |k, v|
-
env["HTTP_#{k.tr('-','_').upcase}"] = v
-
end
-
-
env
-
end
-
-
1
def session
-
@session ||= {}
-
end
-
-
1
def session_options
-
@session_options ||= {}
-
end
-
end
-
end
-
1
module WebMock
-
1
class RequestExecutionVerifier
-
-
1
attr_accessor :request_pattern, :expected_times_executed, :times_executed, :at_least_times_executed, :at_most_times_executed
-
-
1
def initialize(request_pattern = nil, expected_times_executed = nil, at_least_times_executed = nil, at_most_times_executed = nil)
-
@request_pattern = request_pattern
-
@expected_times_executed = expected_times_executed
-
@at_least_times_executed = at_least_times_executed
-
@at_most_times_executed = at_most_times_executed
-
end
-
-
1
def matches?
-
@times_executed =
-
RequestRegistry.instance.times_executed(@request_pattern)
-
-
if @at_least_times_executed
-
@times_executed >= @at_least_times_executed
-
elsif @at_most_times_executed
-
@times_executed <= @at_most_times_executed
-
else
-
@times_executed == (@expected_times_executed || 1)
-
end
-
end
-
-
1
def does_not_match?
-
@times_executed =
-
RequestRegistry.instance.times_executed(@request_pattern)
-
if @expected_times_executed
-
@times_executed != @expected_times_executed
-
else
-
@times_executed == 0
-
end
-
end
-
-
-
1
def failure_message
-
expected_times_executed = @expected_times_executed || 1
-
text = if @at_least_times_executed
-
%Q(The request #{request_pattern.to_s} was expected to execute at least #{times(@at_least_times_executed)} but it executed #{times(times_executed)})
-
elsif @at_most_times_executed
-
%Q(The request #{request_pattern.to_s} was expected to execute at most #{times(@at_most_times_executed)} but it executed #{times(times_executed)})
-
else
-
%Q(The request #{request_pattern.to_s} was expected to execute #{times(expected_times_executed)} but it executed #{times(times_executed)})
-
end
-
text << self.class.executed_requests_message
-
text
-
end
-
-
1
def failure_message_when_negated
-
text = if @at_least_times_executed
-
%Q(The request #{request_pattern.to_s} was not expected to execute at least #{times(@at_least_times_executed)} but it executed #{times(times_executed)})
-
elsif @at_most_times_executed
-
%Q(The request #{request_pattern.to_s} was not expected to execute at most #{times(@at_most_times_executed)} but it executed #{times(times_executed)})
-
elsif @expected_times_executed
-
%Q(The request #{request_pattern.to_s} was not expected to execute #{times(expected_times_executed)} but it executed #{times(times_executed)})
-
else
-
%Q(The request #{request_pattern.to_s} was expected to execute 0 times but it executed #{times(times_executed)})
-
end
-
text << self.class.executed_requests_message
-
text
-
end
-
-
1
def self.executed_requests_message
-
"\n\nThe following requests were made:\n\n#{RequestRegistry.instance.to_s}\n" + "="*60
-
end
-
-
1
private
-
-
1
def times(times)
-
"#{times} time#{ (times == 1) ? '' : 's'}"
-
end
-
-
end
-
end
-
1
module WebMock
-
-
1
module RSpecMatcherDetector
-
1
def rSpecHashIncludingMatcher?(matcher)
-
matcher.class.name =~ /R?Spec::Mocks::ArgumentMatchers::HashIncludingMatcher/
-
end
-
end
-
-
1
class RequestPattern
-
-
1
attr_reader :method_pattern, :uri_pattern, :body_pattern, :headers_pattern
-
-
1
def initialize(method, uri, options = {})
-
1
@method_pattern = MethodPattern.new(method)
-
1
@uri_pattern = create_uri_pattern(uri)
-
1
@body_pattern = nil
-
1
@headers_pattern = nil
-
1
@with_block = nil
-
1
assign_options(options)
-
end
-
-
1
def with(options = {}, &block)
-
1
assign_options(options)
-
1
@with_block = block
-
1
self
-
end
-
-
1
def matches?(request_signature)
-
content_type = request_signature.headers['Content-Type'] if request_signature.headers
-
content_type = content_type.split(';').first if content_type
-
@method_pattern.matches?(request_signature.method) &&
-
@uri_pattern.matches?(request_signature.uri) &&
-
(@body_pattern.nil? || @body_pattern.matches?(request_signature.body, content_type || "")) &&
-
(@headers_pattern.nil? || @headers_pattern.matches?(request_signature.headers)) &&
-
(@with_block.nil? || @with_block.call(request_signature))
-
end
-
-
1
def to_s
-
string = "#{@method_pattern.to_s.upcase}"
-
string << " #{@uri_pattern.to_s}"
-
string << " with body #{@body_pattern.to_s}" if @body_pattern
-
string << " with headers #{@headers_pattern.to_s}" if @headers_pattern
-
string << " with given block" if @with_block
-
string
-
end
-
-
1
private
-
-
-
1
def assign_options(options)
-
2
options = WebMock::Util::HashKeysStringifier.stringify_keys!(options, :deep => true)
-
2
@body_pattern = BodyPattern.new(options['body']) if options.has_key?('body')
-
2
@headers_pattern = HeadersPattern.new(options['headers']) if options.has_key?('headers')
-
2
@uri_pattern.add_query_params(options['query']) if options.has_key?('query')
-
end
-
-
1
def create_uri_pattern(uri)
-
1
if uri.is_a?(Regexp)
-
1
URIRegexpPattern.new(uri)
-
elsif uri.is_a?(Addressable::Template)
-
URIAddressablePattern.new(uri)
-
else
-
URIStringPattern.new(uri)
-
end
-
end
-
-
end
-
-
-
1
class MethodPattern
-
1
def initialize(pattern)
-
1
@pattern = pattern
-
end
-
-
1
def matches?(method)
-
@pattern == method || @pattern == :any
-
end
-
-
1
def to_s
-
@pattern.to_s
-
end
-
end
-
-
-
1
class URIPattern
-
1
include RSpecMatcherDetector
-
-
1
def initialize(pattern)
-
1
@pattern = case pattern
-
when Addressable::URI, Addressable::Template
-
pattern
-
else
-
1
WebMock::Util::URI.normalize_uri(pattern)
-
end
-
1
@query_params = nil
-
end
-
-
1
def add_query_params(query_params)
-
@query_params = if query_params.is_a?(Hash)
-
query_params
-
elsif query_params.is_a?(WebMock::Matchers::HashIncludingMatcher)
-
query_params
-
elsif rSpecHashIncludingMatcher?(query_params)
-
WebMock::Matchers::HashIncludingMatcher.from_rspec_matcher(query_params)
-
else
-
WebMock::Util::QueryMapper.query_to_values(query_params, :notation => Config.instance.query_values_notation)
-
end
-
end
-
-
1
def to_s
-
str = @pattern.inspect
-
str += " with query params #{@query_params.inspect}" if @query_params
-
str
-
end
-
end
-
-
1
class URIRegexpPattern < URIPattern
-
1
def matches?(uri)
-
WebMock::Util::URI.variations_of_uri_as_strings(uri).any? { |u| u.match(@pattern) } &&
-
(@query_params.nil? || @query_params == WebMock::Util::QueryMapper.query_to_values(uri.query, :notation => Config.instance.query_values_notation))
-
end
-
-
1
def to_s
-
str = @pattern.inspect
-
str += " with query params #{@query_params.inspect}" if @query_params
-
str
-
end
-
end
-
-
1
class URIAddressablePattern < URIPattern
-
1
def matches?(uri)
-
if @query_params.nil?
-
# Let Addressable check the whole URI
-
WebMock::Util::URI.variations_of_uri_as_strings(uri).any? { |u| @pattern.match(u) }
-
else
-
# WebMock checks the query, Addressable checks everything else
-
WebMock::Util::URI.variations_of_uri_as_strings(uri.omit(:query)).any? { |u| @pattern.match(u) } &&
-
(@query_params.nil? || @query_params == WebMock::Util::QueryMapper.query_to_values(uri.query))
-
end
-
end
-
-
1
def add_query_params(query_params)
-
warn "WebMock warning: ignoring query params in RFC 6570 template and checking them with WebMock"
-
super(query_params)
-
end
-
-
1
def to_s
-
str = @pattern.pattern.inspect
-
str += " with variables #{@pattern.variables.inspect}" if @pattern.variables
-
str
-
end
-
end
-
-
1
class URIStringPattern < URIPattern
-
1
def matches?(uri)
-
if @pattern.is_a?(Addressable::URI)
-
if @query_params
-
uri.omit(:query) === @pattern &&
-
(@query_params.nil? || @query_params == WebMock::Util::QueryMapper.query_to_values(uri.query, :notation => Config.instance.query_values_notation))
-
else
-
uri === @pattern
-
end
-
else
-
false
-
end
-
end
-
-
1
def add_query_params(query_params)
-
super
-
if @query_params.is_a?(Hash) || @query_params.is_a?(String)
-
query_hash = (WebMock::Util::QueryMapper.query_to_values(@pattern.query, :notation => Config.instance.query_values_notation) || {}).merge(@query_params)
-
@pattern.query = WebMock::Util::QueryMapper.values_to_query(query_hash, :notation => WebMock::Config.instance.query_values_notation)
-
@query_params = nil
-
end
-
end
-
-
1
def to_s
-
str = WebMock::Util::URI.strip_default_port_from_uri_string(@pattern.to_s)
-
str += " with query params #{@query_params.inspect}" if @query_params
-
str
-
end
-
end
-
-
-
1
class BodyPattern
-
1
include RSpecMatcherDetector
-
-
1
BODY_FORMATS = {
-
'text/xml' => :xml,
-
'application/xml' => :xml,
-
'application/json' => :json,
-
'text/json' => :json,
-
'application/javascript' => :json,
-
'text/javascript' => :json,
-
'text/html' => :html,
-
'application/x-yaml' => :yaml,
-
'text/yaml' => :yaml,
-
'text/plain' => :plain
-
}
-
-
1
def initialize(pattern)
-
@pattern = if pattern.is_a?(Hash)
-
normalize_hash(pattern)
-
elsif rSpecHashIncludingMatcher?(pattern)
-
WebMock::Matchers::HashIncludingMatcher.from_rspec_matcher(pattern)
-
else
-
pattern
-
end
-
end
-
-
1
def matches?(body, content_type = "")
-
if (@pattern).is_a?(Hash)
-
return true if @pattern.empty?
-
matching_hashes?(body_as_hash(body, content_type), @pattern)
-
elsif (@pattern).is_a?(WebMock::Matchers::HashIncludingMatcher)
-
@pattern == body_as_hash(body, content_type)
-
else
-
empty_string?(@pattern) && empty_string?(body) ||
-
@pattern == body ||
-
@pattern === body
-
end
-
end
-
-
1
def to_s
-
@pattern.inspect
-
end
-
-
1
private
-
1
def body_as_hash(body, content_type)
-
case BODY_FORMATS[content_type]
-
when :json then
-
WebMock::Util::JSON.parse(body)
-
when :xml then
-
Crack::XML.parse(body)
-
else
-
WebMock::Util::QueryMapper.query_to_values(body, :notation => Config.instance.query_values_notation)
-
end
-
end
-
-
# Compare two hashes for equality
-
#
-
# For two hashes to match they must have the same length and all
-
# values must match when compared using `#===`.
-
#
-
# The following hashes are examples of matches:
-
#
-
# {a: /\d+/} and {a: '123'}
-
#
-
# {a: '123'} and {a: '123'}
-
#
-
# {a: {b: /\d+/}} and {a: {b: '123'}}
-
#
-
# {a: {b: 'wow'}} and {a: {b: 'wow'}}
-
#
-
# @param [Hash] query_parameters typically the result of parsing
-
# JSON, XML or URL encoded parameters.
-
#
-
# @param [Hash] pattern which contains keys with a string, hash or
-
# regular expression value to use for comparison.
-
#
-
# @return [Boolean] true if the paramaters match the comparison
-
# hash, false if not.
-
1
def matching_hashes?(query_parameters, pattern)
-
return false unless query_parameters.is_a?(Hash)
-
return false unless query_parameters.keys.sort == pattern.keys.sort
-
query_parameters.each do |key, actual|
-
expected = pattern[key]
-
-
if actual.is_a?(Hash) && expected.is_a?(Hash)
-
return false unless matching_hashes?(actual, expected)
-
else
-
return false unless expected === actual
-
end
-
end
-
true
-
end
-
-
1
def empty_string?(string)
-
string.nil? || string == ""
-
end
-
-
1
def normalize_hash(hash)
-
Hash[WebMock::Util::HashKeysStringifier.stringify_keys!(hash, :deep => true).sort]
-
end
-
-
end
-
-
1
class HeadersPattern
-
1
def initialize(pattern)
-
@pattern = WebMock::Util::Headers.normalize_headers(pattern) || {}
-
end
-
-
1
def matches?(headers)
-
if empty_headers?(@pattern)
-
empty_headers?(headers)
-
else
-
return false if empty_headers?(headers)
-
@pattern.each do |key, value|
-
return false unless headers.has_key?(key) && value === headers[key]
-
end
-
true
-
end
-
end
-
-
1
def to_s
-
WebMock::Util::Headers.sorted_headers_string(@pattern)
-
end
-
-
1
private
-
-
1
def empty_headers?(headers)
-
headers.nil? || headers == {}
-
end
-
end
-
-
end
-
1
module WebMock
-
-
1
class RequestRegistry
-
1
include Singleton
-
-
1
attr_accessor :requested_signatures
-
-
1
def initialize
-
reset!
-
end
-
-
1
def reset!
-
self.requested_signatures = Util::HashCounter.new
-
end
-
-
1
def times_executed(request_pattern)
-
self.requested_signatures.hash.select { |request_signature, times_executed|
-
request_pattern.matches?(request_signature)
-
}.inject(0) {|sum, (_, times_executed)| sum + times_executed }
-
end
-
-
1
def to_s
-
if requested_signatures.hash.empty?
-
"No requests were made."
-
else
-
text = ""
-
self.requested_signatures.each do |request_signature, times_executed|
-
text << "#{request_signature} was made #{times_executed} time#{times_executed == 1 ? '' : 's' }\n"
-
end
-
text
-
end
-
end
-
-
end
-
end
-
1
module WebMock
-
-
1
class RequestSignature
-
-
1
attr_accessor :method, :uri, :body
-
1
attr_reader :headers
-
-
1
def initialize(method, uri, options = {})
-
self.method = method.to_sym
-
self.uri = uri.is_a?(Addressable::URI) ? uri : WebMock::Util::URI.normalize_uri(uri)
-
assign_options(options)
-
end
-
-
1
def to_s
-
string = "#{self.method.to_s.upcase}"
-
string << " #{WebMock::Util::URI.strip_default_port_from_uri_string(self.uri.to_s)}"
-
string << " with body '#{body.to_s}'" if body && body.to_s != ''
-
if headers && !headers.empty?
-
string << " with headers #{WebMock::Util::Headers.sorted_headers_string(headers)}"
-
end
-
string
-
end
-
-
1
def headers=(headers)
-
@headers = WebMock::Util::Headers.normalize_headers(headers)
-
end
-
-
1
def hash
-
self.to_s.hash
-
end
-
-
1
def eql?(other)
-
self.to_s == other.to_s
-
end
-
1
alias == eql?
-
-
1
def url_encoded?
-
headers && headers['Content-Type'] == 'application/x-www-form-urlencoded'
-
end
-
-
1
private
-
-
1
def assign_options(options)
-
self.body = options[:body] if options.has_key?(:body)
-
self.headers = options[:headers] if options.has_key?(:headers)
-
end
-
-
end
-
-
end
-
1
module WebMock
-
1
class RequestStub
-
-
1
attr_accessor :request_pattern
-
-
1
def initialize(method, uri)
-
1
@request_pattern = RequestPattern.new(method, uri)
-
1
@responses_sequences = []
-
1
self
-
end
-
-
1
def with(params = {}, &block)
-
1
@request_pattern.with(params, &block)
-
1
self
-
end
-
-
1
def to_return(*response_hashes, &block)
-
1
if block
-
@responses_sequences << ResponsesSequence.new([ResponseFactory.response_for(block)])
-
else
-
2
@responses_sequences << ResponsesSequence.new([*response_hashes].flatten.map {|r| ResponseFactory.response_for(r)})
-
end
-
1
self
-
end
-
-
1
def to_rack(app, options={})
-
@responses_sequences << ResponsesSequence.new([RackResponse.new(app)])
-
end
-
-
1
def to_raise(*exceptions)
-
@responses_sequences << ResponsesSequence.new([*exceptions].flatten.map {|e|
-
ResponseFactory.response_for(:exception => e)
-
})
-
self
-
end
-
-
1
def to_timeout
-
@responses_sequences << ResponsesSequence.new([ResponseFactory.response_for(:should_timeout => true)])
-
self
-
end
-
-
1
def response
-
if @responses_sequences.empty?
-
WebMock::Response.new
-
elsif @responses_sequences.length > 1
-
@responses_sequences.shift if @responses_sequences.first.end?
-
@responses_sequences.first.next_response
-
else
-
@responses_sequences[0].next_response
-
end
-
end
-
-
1
def has_responses?
-
!@responses_sequences.empty?
-
end
-
-
1
def then
-
self
-
end
-
-
1
def times(number)
-
raise "times(N) accepts integers >= 1 only" if !number.is_a?(Fixnum) || number < 1
-
if @responses_sequences.empty?
-
raise "Invalid WebMock stub declaration." +
-
" times(N) can be declared only after response declaration."
-
end
-
@responses_sequences.last.times_to_repeat += number-1
-
self
-
end
-
-
1
def matches?(request_signature)
-
self.request_pattern.matches?(request_signature)
-
end
-
-
1
def to_s
-
self.request_pattern.to_s
-
end
-
-
1
def self.from_request_signature(signature)
-
stub = self.new(signature.method.to_sym, signature.uri.to_s)
-
-
if signature.body.to_s != ''
-
body = if signature.url_encoded?
-
WebMock::Util::QueryMapper.query_to_values(signature.body, :notation => Config.instance.query_values_notation)
-
else
-
signature.body
-
end
-
stub.with(:body => body)
-
end
-
-
if (signature.headers && !signature.headers.empty?)
-
stub.with(:headers => signature.headers)
-
end
-
stub
-
end
-
end
-
end
-
1
module WebMock
-
-
1
class ResponsesSequence
-
-
1
attr_accessor :times_to_repeat
-
-
1
def initialize(responses)
-
1
@times_to_repeat = 1
-
1
@responses = responses
-
1
@current_position = 0
-
end
-
-
1
def end?
-
@times_to_repeat == 0
-
end
-
-
1
def next_response
-
if @times_to_repeat > 0
-
response = @responses[@current_position]
-
increase_position
-
response
-
else
-
@responses.last
-
end
<<<<<<< HEAD
| config/routes.rb |
100.0 % |
33 |
22 |
22 |
0 |
1.0 |
| db/schema.rb |
100.0 % |
76 |
45 |
45 |
0 |
1.0 |
| spec/build_helpers.rb |
66.67 % |
7 |
3 |
2 |
1 |
0.7 |
=======
-
end
-
>>>>>>> a12140be7a3cf52c46d4dbfc6422fa2f15074ae5
-
1
private
-
-
1
def increase_position
-
if @current_position == (@responses.length - 1)
-
@current_position = 0
-
@times_to_repeat -= 1
-
else
-
@current_position += 1
-
end
-
end
-
-
end
-
-
end
-
1
module WebMock
-
-
1
class StubRegistry
-
1
include Singleton
-
-
1
attr_accessor :request_stubs
-
-
1
def initialize
-
1
reset!
-
end
-
-
1
def global_stubs
-
1
@global_stubs ||= []
-
end
-
-
1
def reset!
-
1
self.request_stubs = []
-
end
-
-
1
def register_global_stub(&block)
-
# This hash contains the responses returned by the block,
-
# keyed by the exact request (using the object_id).
-
# That way, there's no race condition in case #to_return
-
# doesn't run immediately after stub.with.
-
1
responses = {}
-
-
1
stub = ::WebMock::RequestStub.new(:any, /.*/).with { |request|
-
responses[request.object_id] = block.call(request)
-
}.to_return(lambda { |request| responses.delete(request.object_id) })
-
-
1
global_stubs.push stub
-
end
-
-
1
def register_request_stub(stub)
-
request_stubs.insert(0, stub)
-
stub
-
end
-
-
1
def remove_request_stub(stub)
-
if not request_stubs.delete(stub)
-
raise "Request stub \n\n #{stub.to_s} \n\n is not registered."
-
end
-
end
-
-
1
def registered_request?(request_signature)
-
request_stub_for(request_signature)
-
end
-
-
1
def response_for_request(request_signature)
-
stub = request_stub_for(request_signature)
-
stub ? evaluate_response_for_request(stub.response, request_signature) : nil
-
end
-
-
1
private
-
-
1
def request_stub_for(request_signature)
-
(global_stubs + request_stubs).detect { |registered_request_stub|
-
registered_request_stub.request_pattern.matches?(request_signature)
-
}
-
end
-
-
1
def evaluate_response_for_request(response, request_signature)
-
response.dup.evaluate(request_signature)
-
end
-
-
end
-
end
-
1
module WebMock
-
1
class StubRequestSnippet
-
1
def initialize(request_stub)
-
@request_stub = request_stub
-
end
-
-
1
def to_s(with_response = true)
-
request_pattern = @request_stub.request_pattern
-
string = "stub_request(:#{request_pattern.method_pattern.to_s},"
-
string << " \"#{request_pattern.uri_pattern.to_s}\")"
-
-
with = ""
-
-
if (request_pattern.body_pattern)
-
with << ":body => #{request_pattern.body_pattern.to_s}"
-
end
-
-
if (request_pattern.headers_pattern)
-
with << ",\n " unless with.empty?
-
-
with << ":headers => #{request_pattern.headers_pattern.to_s}"
-
end
-
string << ".\n with(#{with})" unless with.empty?
-
if with_response
-
string << ".\n to_return(:status => 200, :body => \"\", :headers => {})"
-
end
-
string
-
end
-
end
-
end
-
1
require 'thread'
-
-
1
module WebMock
-
1
module Util
<<<<<<< HEAD
-
2
@trips = @user.trips.all
-
=======
-
1
>>>>>>> a12140be7a3cf52c46d4dbfc6422fa2f15074ae5
class Util::HashCounter
-
1
attr_accessor :hash
<<<<<<< HEAD
-
=======
-
1
def initialize
-
self.hash = {}
-
@order = {}
-
@max = 0
-
@lock = ::Mutex.new
-
end
-
1
def put key, num=1
-
@lock.synchronize do
-
hash[key] = (hash[key] || 0) + num
-
@order[key] = @max = @max + 1
-
end
-
end
-
1
def get key
-
@lock.synchronize do
-
hash[key] || 0
-
end
-
end
-
>>>>>>> a12140be7a3cf52c46d4dbfc6422fa2f15074ae5
<<<<<<< HEAD
-
=======
-
1
def each(&block)
-
@order.to_a.sort {|a, b| a[1] <=> b[1]}.each do |a|
-
block.call(a[0], hash[a[0]])
-
end
-
end
-
end
-
end
-
>>>>>>> a12140be7a3cf52c46d4dbfc6422fa2f15074ae5
end
-
1
module WebMock
-
1
module Util
-
1
class HashKeysStringifier
-
-
1
def self.stringify_keys!(arg, options = {})
-
2
case arg
-
when Array
-
arg.map { |elem|
-
options[:deep] ? stringify_keys!(elem, options) : elem
-
}
-
when Hash
-
2
Hash[
-
*arg.map { |key, value|
-
k = key.is_a?(Symbol) ? key.to_s : key
-
v = (options[:deep] ? stringify_keys!(value, options) : value)
-
[k,v]
-
}.inject([]) {|r,x| r + x}]
-
else
-
arg
-
end
-
end
-
-
end
-
end
-
end
-
1
module WebMock
-
-
1
module Util
-
-
1
class Headers
-
-
1
def self.normalize_headers(headers)
-
return nil unless headers
-
array = headers.map { |name, value|
-
[name.to_s.split(/_|-/).map { |segment| segment.capitalize }.join("-"),
-
case value
-
when Regexp then value
-
when Array then (value.size == 1) ? value.first : value.map {|v| v.to_s}.sort
-
else value.to_s
-
end
-
]
-
}
-
Hash[*array.inject([]) {|r,x| r + x}]
-
end
-
-
1
def self.sorted_headers_string(headers)
-
headers = WebMock::Util::Headers.normalize_headers(headers)
-
str = '{'
-
str << headers.map do |k,v|
-
v = case v
-
when Regexp then v.inspect
-
when Array then "["+v.map{|w| "'#{w.to_s}'"}.join(", ")+"]"
-
else "'#{v.to_s}'"
-
end
-
"'#{k}'=>#{v}"
-
end.sort.join(", ")
-
str << '}'
-
end
-
-
1
def self.decode_userinfo_from_header(header)
-
header.sub(/^Basic /, "").unpack("m").first
-
end
-
-
end
-
-
end
-
-
end
-
# This is a copy of https://github.com/jnunemaker/crack/blob/master/lib/crack/json.rb
-
# with date parsing removed
-
# Copyright (c) 2004-2008 David Heinemeier Hansson
-
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
-
# The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-
1
module WebMock
-
1
module Util
-
1
class JSON
-
1
def self.parse(json)
-
YAML.load(unescape(convert_json_to_yaml(json)))
-
rescue ArgumentError
-
raise ParseError, "Invalid JSON string"
-
end
-
-
1
protected
-
1
def self.unescape(str)
-
str.gsub(/\\u([0-9a-f]{4})/) { [$1.hex].pack("U") }
-
end
-
-
# Ensure that ":" and "," are always followed by a space
-
1
def self.convert_json_to_yaml(json) #:nodoc:
-
scanner, quoting, marks, pos, times = StringScanner.new(json), false, [], nil, []
-
while scanner.scan_until(/(\\['"]|['":,\\]|\\.)/)
-
case char = scanner[1]
-
when '"', "'"
-
if !quoting
-
quoting = char
-
pos = scanner.pos
-
elsif quoting == char
-
quoting = false
-
end
-
when ":",","
-
marks << scanner.pos - 1 unless quoting
-
when "\\"
-
scanner.skip(/\\/)
-
end
-
end
-
-
if marks.empty?
-
json.gsub(/\\\//, '/')
-
else
-
left_pos = [-1].push(*marks)
-
right_pos = marks << json.length
-
output = []
-
left_pos.each_with_index do |left, i|
-
output << json[left.succ..right_pos[i]]
-
end
-
output = output * " "
-
-
times.each { |i| output[i-1] = ' ' }
-
output.gsub!(/\\\//, '/')
-
output
-
end
-
end
-
end
-
end
-
end
-
1
module WebMock::Util
-
1
class QueryMapper
-
1
class << self
-
#This class is based on Addressable::URI pre 2.3.0
-
-
##
-
# Converts the query component to a Hash value.
-
#
-
# @option [Symbol] notation
-
# May be one of <code>:flat</code>, <code>:dot</code>, or
-
# <code>:subscript</code>. The <code>:dot</code> notation is not
-
# supported for assignment. Default value is <code>:subscript</code>.
-
#
-
# @return [Hash, Array] The query string parsed as a Hash or Array object.
-
#
-
# @example
-
# WebMock::Util::QueryMapper.query_to_values("?one=1&two=2&three=3")
-
# #=> {"one" => "1", "two" => "2", "three" => "3"}
-
# WebMock::Util::QueryMapper("?one[two][three]=four").query_values
-
# #=> {"one" => {"two" => {"three" => "four"}}}
-
# WebMock::Util::QueryMapper.query_to_values("?one.two.three=four",
-
# :notation => :dot
-
# )
-
# #=> {"one" => {"two" => {"three" => "four"}}}
-
# WebMock::Util::QueryMapper.query_to_values("?one[two][three]=four",
-
# :notation => :flat
-
# )
-
# #=> {"one[two][three]" => "four"}
-
# WebMock::Util::QueryMapper.query_to_values("?one.two.three=four",
-
# :notation => :flat
-
# )
-
# #=> {"one.two.three" => "four"}
-
# WebMock::Util::QueryMapper(
-
# "?one[two][three][]=four&one[two][three][]=five"
-
# )
-
# #=> {"one" => {"two" => {"three" => ["four", "five"]}}}
-
# WebMock::Util::QueryMapper.query_to_values(
-
# "?one=two&one=three").query_values(:notation => :flat_array)
-
# #=> [['one', 'two'], ['one', 'three']]
-
1
def query_to_values(query, options={})
-
return nil if query.nil?
-
query.force_encoding('utf-8') if query.respond_to?(:force_encoding)
-
-
options[:notation] ||= :subscript
-
-
if ![:flat, :dot, :subscript, :flat_array].include?(options[:notation])
-
raise ArgumentError,
-
'Invalid notation. Must be one of: ' +
-
'[:flat, :dot, :subscript, :flat_array].'
-
end
-
-
empty_accumulator = :flat_array == options[:notation] ? [] : {}
-
-
query_array = collect_query_parts(query)
-
-
query_hash = collect_query_hash(query_array, empty_accumulator, options)
-
-
normalize_query_hash(query_hash, empty_accumulator, options)
-
end
-
-
1
def normalize_query_hash(query_hash, empty_accumulator, options)
-
query_hash.inject(empty_accumulator.dup) do |accumulator, (key, value)|
-
if options[:notation] == :flat_array
-
accumulator << [key, value]
-
else
-
accumulator[key] = value.kind_of?(Hash) ? dehash(value) : value
-
end
-
accumulator
-
end
-
end
-
-
1
def collect_query_parts(query)
-
query_parts = query.split('&').map do |pair|
-
pair.split('=', 2) if pair && !pair.empty?
-
end
-
query_parts.compact
-
end
-
-
1
def collect_query_hash(query_array, empty_accumulator, options)
-
query_array.compact.inject(empty_accumulator.dup) do |accumulator, (key, value)|
-
value = if value.nil?
-
true
-
else
-
::Addressable::URI.unencode_component(value.gsub(/\+/, ' '))
-
end
-
key = Addressable::URI.unencode_component(key)
-
key = key.dup.force_encoding(Encoding::ASCII_8BIT) if key.respond_to?(:force_encoding)
-
self.__send__("fill_accumulator_for_#{options[:notation]}", accumulator, key, value)
-
accumulator
-
end
-
end
-
-
1
def fill_accumulator_for_flat(accumulator, key, value)
-
if accumulator[key]
-
raise ArgumentError, "Key was repeated: #{key.inspect}"
-
end
-
accumulator[key] = value
-
end
-
-
1
def fill_accumulator_for_flat_array(accumulator, key, value)
-
accumulator << [key, value]
-
end
-
-
1
def fill_accumulator_for_dot(accumulator, key, value)
-
array_value = false
-
subkeys = key.split(".")
-
current_hash = accumulator
-
subkeys[0..-2].each do |subkey|
-
current_hash[subkey] = {} unless current_hash[subkey]
-
current_hash = current_hash[subkey]
-
end
-
if array_value
-
if current_hash[subkeys.last] && !current_hash[subkeys.last].is_a?(Array)
-
current_hash[subkeys.last] = [current_hash[subkeys.last]]
-
end
-
current_hash[subkeys.last] = [] unless current_hash[subkeys.last]
-
current_hash[subkeys.last] << value
-
else
-
current_hash[subkeys.last] = value
-
end
-
end
-
-
1
def fill_accumulator_for_subscript(accumulator, key, value)
-
current_node = accumulator
-
subkeys = key.split(/(?=\[\w)/)
-
subkeys[0..-2].each do |subkey|
-
node = subkey =~ /\[\]\z/ ? [] : {}
-
subkey = subkey.gsub(/[\[\]]/, '')
-
if current_node.is_a? Array
-
container = current_node.find { |n| n.is_a?(Hash) && n.has_key?(subkey) }
-
if container
-
current_node = container[subkey]
-
else
-
current_node << {subkey => node}
-
current_node = node
-
end
-
else
-
current_node[subkey] = node unless current_node[subkey]
-
current_node = current_node[subkey]
-
end
-
end
-
last_key = subkeys.last
-
array_value = !!(last_key =~ /\[\]$/)
-
last_key = last_key.gsub(/[\[\]]/, '')
-
if current_node.is_a? Array
-
container = current_node.find { |n| n.is_a?(Hash) && n.has_key?(last_key) }
-
if container
-
if array_value
-
container[last_key] << value
-
else
-
container[last_key] = value
-
end
-
else
-
if array_value
-
current_node << {last_key => [value]}
-
else
-
current_node << {last_key => value}
-
end
-
end
-
else
-
if array_value
-
current_node[last_key] = [] unless current_node[last_key]
-
current_node[last_key] << value
-
else
-
current_node[last_key] = value
-
end
-
end
-
end
-
-
##
-
# Sets the query component for this URI from a Hash object.
-
# This method produces a query string using the :subscript notation.
-
# An empty Hash will result in a nil query.
-
#
-
# @param [Hash, #to_hash, Array] new_query_values The new query values.
-
1
def values_to_query(new_query_values, options = {})
-
options[:notation] ||= :subscript
-
return if new_query_values.nil?
-
-
unless new_query_values.is_a?(Array)
-
unless new_query_values.respond_to?(:to_hash)
-
raise TypeError,
-
"Can't convert #{new_query_values.class} into Hash."
-
end
-
new_query_values = new_query_values.to_hash
-
new_query_values = new_query_values.inject([]) do |object, (key, value)|
-
key = key.to_s if key.is_a?(::Symbol) || key.nil?
-
if value.is_a?(Array)
-
value.each { |v| object << [key.to_s + '[]', v] }
-
elsif value.is_a?(Hash)
-
value.each { |k, v| object << ["#{key.to_s}[#{k}]", v]}
-
else
-
object << [key.to_s, value]
-
end
-
object
-
end
-
# Useful default for OAuth and caching.
-
# Only to be used for non-Array inputs. Arrays should preserve order.
-
new_query_values.sort!
-
end
-
-
buffer = ''
-
new_query_values.each do |parent, value|
-
encoded_parent = ::Addressable::URI.encode_component(
-
parent.dup, ::Addressable::URI::CharacterClasses::UNRESERVED
-
)
-
buffer << "#{to_query(encoded_parent, value, options)}&"
-
end
-
buffer.chop
-
end
-
-
1
def dehash(hash)
-
hash.each do |(key, value)|
-
if value.is_a?(::Hash)
-
hash[key] = self.dehash(value)
-
end
-
end
-
if hash != {} && hash.keys.all? { |key| key =~ /^\d+$/ }
-
hash.sort.inject([]) do |accu, (_, value)|
-
accu << value; accu
-
end
-
else
-
hash
-
end
-
end
-
-
##
-
# Joins and converts parent and value into a properly encoded and
-
# ordered URL query.
-
#
-
# @private
-
# @param [String] parent an URI encoded component.
-
# @param [Array, Hash, Symbol, #to_str] value
-
#
-
# @return [String] a properly escaped and ordered URL query.
-
-
# new_query_values have form [['key1', 'value1'], ['key2', 'value2']]
-
1
def to_query(parent, value, options = {})
-
options[:notation] ||= :subscript
-
case value
-
when ::Hash
-
value = value.map do |key, val|
-
[
-
::Addressable::URI.encode_component(key.dup, ::Addressable::URI::CharacterClasses::UNRESERVED),
-
val
-
]
-
end
-
value.sort!
-
buffer = ''
-
value.each do |key, val|
-
new_parent = options[:notation] != :flat_array ? "#{parent}[#{key}]" : parent
-
buffer << "#{to_query(new_parent, val, options)}&"
-
end
-
buffer.chop
-
when ::Array
-
buffer = ''
-
value.each_with_index do |val, i|
-
new_parent = options[:notation] != :flat_array ? "#{parent}[#{i}]" : parent
-
buffer << "#{to_query(new_parent, val, options)}&"
-
end
-
buffer.chop
-
when TrueClass
-
parent
-
else
-
encoded_value = Addressable::URI.encode_component(
-
value.to_s.dup, Addressable::URI::CharacterClasses::UNRESERVED
-
)
-
"#{parent}=#{encoded_value}"
-
end
-
end
-
end
-
-
end
-
end
-
1
module WebMock
-
-
1
module Util
-
-
1
class URI
-
1
module CharacterClasses
-
1
USERINFO = Addressable::URI::CharacterClasses::UNRESERVED + Addressable::URI::CharacterClasses::SUB_DELIMS + "\\:"
-
end
-
-
1
ADDRESSABLE_URIS = Hash.new do |hash, key|
-
hash[key] = Addressable::URI.heuristic_parse(key)
-
end
-
-
1
NORMALIZED_URIS = Hash.new do |hash, uri|
-
normalized_uri = WebMock::Util::URI.heuristic_parse(uri)
-
if normalized_uri.query_values
-
sorted_query_values = sort_query_values(WebMock::Util::QueryMapper.query_to_values(normalized_uri.query, :notation => Config.instance.query_values_notation) || {})
-
normalized_uri.query = WebMock::Util::QueryMapper.values_to_query(sorted_query_values, :notation => WebMock::Config.instance.query_values_notation)
-
end
-
normalized_uri = normalized_uri.normalize #normalize! is slower
-
normalized_uri.query = normalized_uri.query.gsub("+", "%2B") if normalized_uri.query
-
normalized_uri.port = normalized_uri.inferred_port unless normalized_uri.port
-
hash[uri] = normalized_uri
-
end
-
-
1
def self.heuristic_parse(uri)
-
ADDRESSABLE_URIS[uri].dup
-
end
-
-
1
def self.normalize_uri(uri)
-
1
return uri if uri.is_a?(Regexp)
-
uri = 'http://' + uri unless uri.match('^https?://') if uri.is_a?(String)
-
NORMALIZED_URIS[uri].dup
-
end
-
-
1
def self.variations_of_uri_as_strings(uri_object)
-
normalized_uri = normalize_uri(uri_object.dup).freeze
-
uris = [ normalized_uri ]
-
-
if normalized_uri.path == '/'
-
uris = uris_with_trailing_slash_and_without(uris)
-
end
-
-
uris = uris_encoded_and_unencoded(uris)
-
-
if normalized_uri.port == Addressable::URI.port_mapping[normalized_uri.scheme]
-
uris = uris_with_inferred_port_and_without(uris)
-
end
-
-
if normalized_uri.scheme == "http"
-
uris = uris_with_scheme_and_without(uris)
-
end
-
-
uris.map {|uri| uri.to_s.gsub(/^\/\//,'') }.uniq
-
end
-
-
1
def self.strip_default_port_from_uri_string(uri_string)
-
case uri_string
-
when %r{^http://} then uri_string.sub(%r{:80(/|$)}, '\1')
-
when %r{^https://} then uri_string.sub(%r{:443(/|$)}, '\1')
-
else uri_string
-
end
-
end
-
-
1
def self.encode_unsafe_chars_in_userinfo(userinfo)
-
Addressable::URI.encode_component(userinfo, WebMock::Util::URI::CharacterClasses::USERINFO)
-
end
-
-
1
def self.is_uri_localhost?(uri)
-
uri.is_a?(Addressable::URI) &&
-
%w(localhost 127.0.0.1 0.0.0.0).include?(uri.host)
-
end
-
-
1
private
-
-
1
def self.sort_query_values(query_values)
-
sorted_query_values = query_values.sort
-
query_values.is_a?(Hash) ? Hash[*sorted_query_values.inject([]) { |values, pair| values + pair}] : sorted_query_values
-
end
-
-
1
def self.uris_with_inferred_port_and_without(uris)
-
uris.map { |uri|
-
uri = uri.dup.force_encoding(Encoding::ASCII_8BIT) if uri.respond_to?(:force_encoding)
-
[ uri, uri.gsub(%r{(:80)|(:443)}, "").freeze ]
-
}.flatten
-
end
-
-
1
def self.uris_encoded_and_unencoded(uris)
-
uris.map do |uri|
-
[ uri.to_s, Addressable::URI.unencode(uri, String).freeze ]
-
end.flatten
-
end
-
-
1
def self.uris_with_scheme_and_without(uris)
-
uris.map { |uri|
-
uri = uri.dup.force_encoding(Encoding::ASCII_8BIT) if uri.respond_to?(:force_encoding)
-
[ uri, uri.gsub(%r{^https?://},"").freeze ]
-
}.flatten
-
end
-
-
1
def self.uris_with_trailing_slash_and_without(uris)
-
uris = uris.map { |uri|
-
uri = uri.dup.force_encoding(Encoding::ASCII_8BIT) if uri.respond_to?(:force_encoding)
-
[ uri, uri.omit(:path).freeze ]
-
}.flatten
-
end
-
-
end
-
end
-
-
end
-
# This code was created based on https://github.com/myronmarston/vcr/blob/master/lib/vcr/util/version_checker.rb
-
# Thanks to @myronmarston
-
-
# Copyright (c) 2010-2012 Myron Marston
-
-
# Permission is hereby granted, free of charge, to any person obtaining
-
# a copy of this software and associated documentation files (the
-
# "Software"), to deal in the Software without restriction, including
-
# without limitation the rights to use, copy, modify, merge, publish,
-
# distribute, sublicense, and/or sell copies of the Software, and to
-
# permit persons to whom the Software is furnished to do so, subject to
-
# the following conditions:
-
-
# The above copyright notice and this permission notice shall be
-
# included in all copies or substantial portions of the Software.
-
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-
1
module WebMock
-
1
class VersionChecker
-
1
def initialize(library_name, library_version, min_patch_level, max_minor_version = nil)
-
@library_name, @library_version = library_name, library_version
-
@min_patch_level, @max_minor_version = min_patch_level, max_minor_version
-
-
@major, @minor, @patch = parse_version(library_version)
-
@min_major, @min_minor, @min_patch = parse_version(min_patch_level)
-
@max_major, @max_minor = parse_version(max_minor_version) if max_minor_version
-
-
@comparison_result = compare_version
-
end
-
-
1
def check_version!
-
warn_about_too_low if too_low?
-
warn_about_too_high if too_high?
-
end
-
-
1
private
-
-
1
def too_low?
-
@comparison_result == :too_low
-
end
-
-
1
def too_high?
-
@comparison_result == :too_high
-
end
-
-
1
def warn_about_too_low
-
warn_in_red "You are using #{@library_name} #{@library_version}. " +
-
"WebMock supports version #{version_requirement}."
-
end
-
-
1
def warn_about_too_high
-
warn_in_red "You are using #{@library_name} #{@library_version}. " +
-
"WebMock is known to work with #{@library_name} #{version_requirement}. " +
-
"It may not work with this version."
-
end
-
-
1
def warn_in_red(text)
-
Kernel.warn colorize(text, "\e[31m")
-
end
-
-
1
def compare_version
-
case
-
when @major < @min_major then :too_low
-
when @max_major && @major > @max_major then :too_high
-
when @major > @min_major then :ok
-
when @minor < @min_minor then :too_low
-
when @max_minor && @minor > @max_minor then :too_high
<<<<<<< HEAD
-
-
1
get '/profile/:id', to: 'profiles#show', as: 'profile'
-
=======
-
when @minor > @min_minor then :ok
-
when @patch < @min_patch then :too_low
-
end
-
end
-
>>>>>>> a12140be7a3cf52c46d4dbfc6422fa2f15074ae5
<<<<<<< HEAD
-
=======
-
>>>>>>> a12140be7a3cf52c46d4dbfc6422fa2f15074ae5
1
def version_requirement
<<<<<<< HEAD
-
1
=======
-
>>>>>>> a12140be7a3cf52c46d4dbfc6422fa2f15074ae5
req = ">= #{@min_patch_level}"
<<<<<<< HEAD
-
1
=======
-
>>>>>>> a12140be7a3cf52c46d4dbfc6422fa2f15074ae5
req += ", < #{@max_major}.#{@max_minor + 1}" if @max_minor
<<<<<<< HEAD
-
1
=======
-
>>>>>>> a12140be7a3cf52c46d4dbfc6422fa2f15074ae5
req
<<<<<<< HEAD
-
1
=======
-
>>>>>>> a12140be7a3cf52c46d4dbfc6422fa2f15074ae5
end
<<<<<<< HEAD
-
=======
-
-
>>>>>>> a12140be7a3cf52c46d4dbfc6422fa2f15074ae5
1
def parse_version(version)
<<<<<<< HEAD
-
=======
-
>>>>>>> a12140be7a3cf52c46d4dbfc6422fa2f15074ae5
version.split('.').map { |v| v.to_i }
<<<<<<< HEAD
-
=======
-
>>>>>>> a12140be7a3cf52c46d4dbfc6422fa2f15074ae5
end
<<<<<<< HEAD
-
=======
-
>>>>>>> a12140be7a3cf52c46d4dbfc6422fa2f15074ae5
<<<<<<< HEAD
-
=======
-
1
def colorize(text, color_code)
-
>>>>>>> a12140be7a3cf52c46d4dbfc6422fa2f15074ae5
"#{color_code}#{text}\e[0m"
<<<<<<< HEAD
-
end
-
# encoding: UTF-8
-
# This file is auto-generated from the current state of the database. Instead
-
# of editing this file, please use the migrations feature of Active Record to
-
# incrementally modify your database, and then regenerate this schema definition.
-
#
-
# Note that this schema.rb definition is the authoritative source for your
-
# database schema. If you need to create the application database on another
-
# system, you should be using db:schema:load, not running all the migrations
-
# from scratch. The latter is a flawed and unsustainable approach (the more migrations
-
# you'll amass, the slower it'll run and the greater likelihood for issues).
-
#
-
# It's strongly recommended that you check this file into your version control system.
-
-
1
ActiveRecord::Schema.define(version: 20141112175052) do
-
-
# These are extensions that must be enabled in order to support this database
-
1
enable_extension "plpgsql"
-
1
enable_extension "hstore"
-
-
1
create_table "comments", force: true do |t|
-
1
t.integer "post_id"
-
1
t.integer "user_id"
-
1
t.text "body"
-
1
t.datetime "created_at"
-
1
t.datetime "updated_at"
-
end
-
-
1
create_table "posts", force: true do |t|
-
1
t.integer "user_id"
-
1
t.datetime "created_at"
-
1
t.datetime "updated_at"
-
1
t.hstore "properties"
-
1
t.string "source"
-
1
t.string "source_id"
-
1
t.boolean "hide", default: false
-
1
t.date "created_date"
-
=======
-
end
-
>>>>>>> a12140be7a3cf52c46d4dbfc6422fa2f15074ae5
end
<<<<<<< HEAD
-
-
1
create_table "travelings", force: true do |t|
-
1
t.integer "user_id"
-
1
t.integer "trip_id"
-
1
t.datetime "created_at"
-
1
t.datetime "updated_at"
-
end
-
-
1
add_index "travelings", ["trip_id"], name: "index_travelings_on_trip_id", using: :btree
-
1
add_index "travelings", ["user_id"], name: "index_travelings_on_user_id", using: :btree
-
-
1
create_table "travels", id: false, force: true do |t|
-
1
t.integer "post_id", null: false
-
1
t.integer "trip_id", null: false
-
end
-
-
1
add_index "travels", ["post_id", "trip_id"], name: "index_travels_on_post_id_and_trip_id", using: :btree
-
1
add_index "travels", ["trip_id", "post_id"], name: "index_travels_on_trip_id_and_post_id", using: :btree
-
-
1
create_table "trips", force: true do |t|
-
1
t.string "title"
-
1
t.date "start_date"
-
1
t.date "end_date"
-
1
t.datetime "created_at"
-
1
t.datetime "updated_at"
-
end
-
-
1
create_table "users", force: true do |t|
-
1
t.string "name"
-
1
t.string "nickname"
-
1
t.string "image"
-
1
t.datetime "created_at"
-
1
t.datetime "updated_at"
-
1
t.string "email"
-
1
t.integer "uid"
-
1
t.string "provider"
-
end
-
-
=======
-
>>>>>>> a12140be7a3cf52c46d4dbfc6422fa2f15074ae5
end
-
1
module WebMock
-
1
VERSION = '1.20.0' unless defined?(::WebMock::VERSION)
-
end
-
1
module WebMock
-
-
1
def self.included(clazz)
-
WebMock::Deprecation.warning("include WebMock is deprecated. Please include WebMock::API instead")
-
if clazz.instance_methods.map(&:to_s).include?('request')
-
warn "WebMock#request was not included in #{clazz} to avoid name collision"
-
else
-
clazz.class_eval do
-
def request(method, uri)
-
WebMock::Deprecation.warning("WebMock#request is deprecated. Please use WebMock::API#a_request method instead")
-
WebMock.a_request(method, uri)
-
end
-
end
-
end
-
end
-
-
1
include WebMock::API
-
1
extend WebMock::API
-
-
1
class << self
-
1
alias :request :a_request
-
end
-
-
1
def self.version
-
1
VERSION
-
end
-
-
1
def self.disable!(options = {})
-
except = [options[:except]].flatten.compact
-
HttpLibAdapterRegistry.instance.each_adapter do |name, adapter|
-
adapter.enable!
-
adapter.disable! unless except.include?(name)
-
end
-
end
-
-
1
def self.enable!(options = {})
-
1
except = [options[:except]].flatten.compact
-
1
HttpLibAdapterRegistry.instance.each_adapter do |name, adapter|
-
1
adapter.disable!
-
1
adapter.enable! unless except.include?(name)
-
end
-
end
-
-
1
def self.allow_net_connect!(options = {})
-
Config.instance.allow_net_connect = true
-
Config.instance.net_http_connect_on_start = options[:net_http_connect_on_start]
-
end
-
-
1
def self.disable_net_connect!(options = {})
-
Config.instance.allow_net_connect = false
-
Config.instance.allow_localhost = options[:allow_localhost]
-
Config.instance.allow = options[:allow]
-
Config.instance.net_http_connect_on_start = options[:net_http_connect_on_start]
-
end
-
-
1
def self.net_connect_allowed?(uri = nil)
-
if uri.is_a?(String)
-
uri = WebMock::Util::URI.normalize_uri(uri)
-
end
-
-
Config.instance.allow_net_connect ||
-
( Config.instance.allow_localhost && WebMock::Util::URI.is_uri_localhost?(uri) ||
-
Config.instance.allow && net_connect_explicit_allowed?(Config.instance.allow, uri) )
-
end
-
-
1
def self.net_connect_explicit_allowed?(allowed, uri=nil)
-
case allowed
-
when Array
-
allowed.any? { |allowed_item| net_connect_explicit_allowed?(allowed_item, uri) }
-
when Regexp
-
uri.to_s =~ allowed
-
when String
-
allowed == uri.host ||
-
allowed == "#{uri.host}:#{uri.port}"
-
end
-
end
-
-
1
def self.hide_stubbing_instructions!
-
Config.instance.show_stubbing_instructions = false
-
end
-
-
1
def self.show_stubbing_instructions!
-
Config.instance.show_stubbing_instructions = true
-
end
-
-
1
def self.show_stubbing_instructions?
-
Config.instance.show_stubbing_instructions
-
end
-
-
1
def self.reset!
-
WebMock::RequestRegistry.instance.reset!
-
WebMock::StubRegistry.instance.reset!
-
end
-
-
1
def self.reset_webmock
-
WebMock::Deprecation.warning("WebMock.reset_webmock is deprecated. Please use WebMock.reset! method instead")
-
reset!
-
end
-
-
1
def self.reset_callbacks
-
WebMock::CallbackRegistry.reset
-
end
-
-
1
def self.after_request(options={}, &block)
-
2
WebMock::CallbackRegistry.add_callback(options, block)
-
end
-
-
1
def self.registered_request?(request_signature)
-
WebMock::StubRegistry.instance.registered_request?(request_signature)
-
end
-
-
1
def self.print_executed_requests
-
puts WebMock::RequestExecutionVerifier.executed_requests_message
-
end
-
-
1
def self.globally_stub_request(&block)
-
1
WebMock::StubRegistry.instance.register_global_stub(&block)
-
end
-
-
%w(
-
allow_net_connect!
-
disable_net_connect!
-
net_connect_allowed?
-
reset_webmock
-
reset_callbacks
-
after_request
-
registered_request?
-
1
).each do |method|
-
7
self.class_eval(%Q(
-
def #{method}(*args, &block)
-
WebMock::Deprecation.warning("WebMock##{method} instance method is deprecated. Please use WebMock.#{method} class method instead")
-
WebMock.#{method}(*args, &block)
-
end
-
))
-
end
-
-
1
self.enable!
-
end
-
1
require 'nokogiri'
-
-
1
require 'xpath/dsl'
-
1
require 'xpath/expression'
-
1
require 'xpath/literal'
-
1
require 'xpath/union'
-
1
require 'xpath/renderer'
-
1
require 'xpath/html'
-
-
1
module XPath
-
-
1
extend XPath::DSL::TopLevel
-
1
include XPath::DSL::TopLevel
-
-
1
def self.generate
-
yield(self)
-
end
-
end
-
1
module XPath
-
1
module DSL
-
1
module TopLevel
-
1
def current
-
Expression.new(:this_node)
-
end
-
-
1
def name
-
Expression.new(:node_name, current)
-
end
-
-
1
def descendant(*expressions)
-
Expression.new(:descendant, current, expressions)
-
end
-
-
1
def child(*expressions)
-
Expression.new(:child, current, expressions)
-
end
-
-
1
def axis(name, tag_name=:*)
-
Expression.new(:axis, current, name, tag_name)
-
end
-
-
1
def next_sibling(*expressions)
-
Expression.new(:next_sibling, current, expressions)
-
end
-
-
1
def previous_sibling(*expressions)
-
Expression.new(:previous_sibling, current, expressions)
-
end
-
-
1
def anywhere(*expressions)
-
Expression.new(:anywhere, expressions)
-
end
-
-
1
def attr(expression)
-
Expression.new(:attribute, current, expression)
-
end
-
-
1
def contains(expression)
-
Expression.new(:contains, current, expression)
-
end
-
-
1
def starts_with(expression)
-
Expression.new(:starts_with, current, expression)
-
end
-
-
1
def text
-
Expression.new(:text, current)
-
end
-
-
1
def string
-
Expression.new(:string_function, current)
-
end
-
-
1
def css(selector)
-
Expression.new(:css, current, Literal.new(selector))
-
end
-
end
-
-
1
module ExpressionLevel
-
1
include XPath::DSL::TopLevel
-
-
1
def where(expression)
-
Expression.new(:where, current, expression)
-
end
-
1
alias_method :[], :where
-
-
1
def one_of(*expressions)
-
Expression.new(:one_of, current, expressions)
-
end
-
-
1
def equals(expression)
-
Expression.new(:equality, current, expression)
-
end
-
1
alias_method :==, :equals
-
-
1
def is(expression)
-
Expression.new(:is, current, expression)
-
end
-
-
1
def or(expression)
-
Expression.new(:or, current, expression)
-
end
-
1
alias_method :|, :or
-
-
1
def and(expression)
-
Expression.new(:and, current, expression)
-
end
-
1
alias_method :&, :and
-
-
1
def union(*expressions)
-
Union.new(*[self, expressions].flatten)
-
end
-
1
alias_method :+, :union
-
-
1
def inverse
-
Expression.new(:inverse, current)
-
end
-
1
alias_method :~, :inverse
-
-
1
def string_literal
-
Expression.new(:string_literal, self)
-
end
-
-
1
def normalize
-
Expression.new(:normalized_space, current)
-
end
-
1
alias_method :n, :normalize
-
end
-
end
-
end
-
1
module XPath
-
1
class Expression
-
1
attr_accessor :expression, :arguments
-
1
include XPath::DSL::ExpressionLevel
-
-
1
def initialize(expression, *arguments)
-
@expression = expression
-
@arguments = arguments
-
end
-
-
1
def current
-
self
-
end
-
-
1
def to_xpath(type=nil)
-
Renderer.render(self, type)
-
end
-
1
alias_method :to_s, :to_xpath
-
end
-
end
-
1
module XPath
-
1
module HTML
-
1
include XPath::DSL::TopLevel
-
1
extend self
-
-
# Match an `a` link element.
-
#
-
# @param [String] locator
-
# Text, id, title, or image alt attribute of the link
-
#
-
1
def link(locator)
-
locator = locator.to_s
-
link = descendant(:a)[attr(:href)]
-
link[attr(:id).equals(locator) | string.n.is(locator) | attr(:title).is(locator) | descendant(:img)[attr(:alt).is(locator)]]
-
end
-
-
# Match a `submit`, `image`, or `button` element.
-
#
-
# @param [String] locator
-
# Value, title, id, or image alt attribute of the button
-
#
-
1
def button(locator)
-
locator = locator.to_s
-
button = descendant(:input)[attr(:type).one_of('submit', 'reset', 'image', 'button')][attr(:id).equals(locator) | attr(:value).is(locator) | attr(:title).is(locator)]
-
button += descendant(:button)[attr(:id).equals(locator) | attr(:value).is(locator) | string.n.is(locator) | attr(:title).is(locator)]
-
button += descendant(:input)[attr(:type).equals('image')][attr(:alt).is(locator)]
-
end
-
-
-
# Match anything returned by either {#link} or {#button}.
-
#
-
# @param [String] locator
-
# Text, id, title, or image alt attribute of the link or button
-
#
-
1
def link_or_button(locator)
-
link(locator) + button(locator)
-
end
-
-
-
# Match any `fieldset` element.
-
#
-
# @param [String] locator
-
# Legend or id of the fieldset
-
#
-
1
def fieldset(locator)
-
locator = locator.to_s
-
descendant(:fieldset)[attr(:id).equals(locator) | child(:legend)[string.n.is(locator)]]
-
end
-
-
-
# Match any `input`, `textarea`, or `select` element that doesn't have a
-
# type of `submit`, `image`, or `hidden`.
-
#
-
# @param [String] locator
-
# Label, id, or name of field to match
-
#
-
1
def field(locator)
-
locator = locator.to_s
-
xpath = descendant(:input, :textarea, :select)[~attr(:type).one_of('submit', 'image', 'hidden')]
-
xpath = locate_field(xpath, locator)
-
xpath
-
end
-
-
-
# Match any `input` or `textarea` element that can be filled with text.
-
# This excludes any inputs with a type of `submit`, `image`, `radio`,
-
# `checkbox`, `hidden`, or `file`.
-
#
-
# @param [String] locator
-
# Label, id, or name of field to match
-
#
-
1
def fillable_field(locator)
-
locator = locator.to_s
-
xpath = descendant(:input, :textarea)[~attr(:type).one_of('submit', 'image', 'radio', 'checkbox', 'hidden', 'file')]
-
xpath = locate_field(xpath, locator)
-
xpath
-
end
-
-
-
# Match any `select` element.
-
#
-
# @param [String] locator
-
# Label, id, or name of the field to match
-
#
-
1
def select(locator)
-
locator = locator.to_s
-
locate_field(descendant(:select), locator)
-
end
-
-
-
# Match any `input` element of type `checkbox`.
-
#
-
# @param [String] locator
-
# Label, id, or name of the checkbox to match
-
#
-
1
def checkbox(locator)
-
locator = locator.to_s
-
locate_field(descendant(:input)[attr(:type).equals('checkbox')], locator)
-
end
-
-
-
# Match any `input` element of type `radio`.
-
#
-
# @param [String] locator
-
# Label, id, or name of the radio button to match
-
#
-
1
def radio_button(locator)
-
locator = locator.to_s
-
locate_field(descendant(:input)[attr(:type).equals('radio')], locator)
-
end
-
-
-
# Match any `input` element of type `file`.
-
#
-
# @param [String] locator
-
# Label, id, or name of the file field to match
-
#
-
1
def file_field(locator)
-
locator = locator.to_s
-
locate_field(descendant(:input)[attr(:type).equals('file')], locator)
-
end
-
-
-
# Match an `optgroup` element.
-
#
-
# @param [String] name
-
# Label for the option group
-
#
-
1
def optgroup(locator)
-
locator = locator.to_s
-
descendant(:optgroup)[attr(:label).is(locator)]
-
end
-
-
-
# Match an `option` element.
-
#
-
# @param [String] name
-
# Visible text of the option
-
#
-
1
def option(locator)
-
locator = locator.to_s
-
descendant(:option)[string.n.is(locator)]
-
end
-
-
-
# Match any `table` element.
-
#
-
# @param [String] locator
-
# Caption or id of the table to match
-
# @option options [Array] :rows
-
# Content of each cell in each row to match
-
#
-
1
def table(locator)
-
locator = locator.to_s
-
descendant(:table)[attr(:id).equals(locator) | descendant(:caption).is(locator)]
-
end
-
-
# Match any 'dd' element.
-
#
-
# @param [String] locator
-
# Id of the 'dd' element or text from preciding 'dt' element content
-
1
def definition_description(locator)
-
locator = locator.to_s
-
descendant(:dd)[attr(:id).equals(locator) | previous_sibling(:dt)[string.n.equals(locator)] ]
-
end
-
-
1
protected
-
-
1
def locate_field(xpath, locator)
-
locate_field = xpath[attr(:id).equals(locator) | attr(:name).equals(locator) | attr(:placeholder).equals(locator) | attr(:id).equals(anywhere(:label)[string.n.is(locator)].attr(:for))]
-
locate_field += descendant(:label)[string.n.is(locator)].descendant(xpath)
-
locate_field
-
end
-
end
-
end
-
1
module XPath
-
1
class Literal
-
1
attr_reader :value
-
1
def initialize(value)
-
@value = value
-
end
-
end
-
end
-
1
module XPath
-
1
class Renderer
-
1
def self.render(node, type)
-
new(type).render(node)
-
end
-
-
1
def initialize(type)
-
@type = type
-
end
-
-
1
def render(node)
-
arguments = node.arguments.map { |argument| convert_argument(argument) }
-
send(node.expression, *arguments)
-
end
-
-
1
def convert_argument(argument)
-
case argument
-
when Expression, Union then render(argument)
-
when Array then argument.map { |element| convert_argument(element) }
-
when String then string_literal(argument)
-
when Literal then argument.value
-
else argument.to_s
-
end
-
end
-
-
1
def string_literal(string)
-
if string.include?("'")
-
string = string.split("'", -1).map do |substr|
-
"'#{substr}'"
-
end.join(%q{,"'",})
-
"concat(#{string})"
-
else
-
"'#{string}'"
-
end
-
end
-
-
1
def this_node
-
'.'
-
end
-
-
1
def descendant(parent, element_names)
-
if element_names.length == 1
-
"#{parent}//#{element_names.first}"
-
elsif element_names.length > 1
-
"#{parent}//*[#{element_names.map { |e| "self::#{e}" }.join(" | ")}]"
-
else
-
"#{parent}//*"
-
end
-
end
-
-
1
def child(parent, element_names)
-
if element_names.length == 1
-
"#{parent}/#{element_names.first}"
-
elsif element_names.length > 1
-
"#{parent}/*[#{element_names.map { |e| "self::#{e}" }.join(" | ")}]"
-
else
-
"#{parent}/*"
-
end
-
end
-
-
1
def axis(parent, name, tag_name)
-
"#{parent}/#{name}::#{tag_name}"
-
end
-
-
1
def node_name(current)
-
"name(#{current})"
-
end
-
-
1
def where(on, condition)
-
"#{on}[#{condition}]"
-
end
-
-
1
def attribute(current, name)
-
"#{current}/@#{name}"
-
end
-
-
1
def equality(one, two)
-
"#{one} = #{two}"
-
end
-
-
1
def is(one, two)
-
if @type == :exact
-
equality(one, two)
-
else
-
contains(one, two)
-
end
-
end
-
-
1
def variable(name)
-
"%{#{name}}"
-
end
-
-
1
def text(current)
-
"#{current}/text()"
-
end
-
-
1
def normalized_space(current)
-
"normalize-space(#{current})"
-
end
-
-
1
def literal(node)
-
node
-
end
-
-
1
def css(current, selector)
-
paths = Nokogiri::CSS.xpath_for(selector).map do |xpath_selector|
-
"#{current}#{xpath_selector}"
-
end
-
union(paths)
-
end
-
-
1
def union(*expressions)
-
expressions.join(' | ')
-
end
-
-
1
def anywhere(element_names)
-
if element_names.length == 1
-
"//#{element_names.first}"
-
elsif element_names.length > 1
-
"//*[#{element_names.map { |e| "self::#{e}" }.join(" | ")}]"
-
else
-
"//*"
-
end
-
end
-
-
1
def contains(current, value)
-
"contains(#{current}, #{value})"
-
end
-
-
1
def starts_with(current, value)
-
"starts-with(#{current}, #{value})"
-
end
-
-
1
def and(one, two)
-
"(#{one} and #{two})"
-
end
-
-
1
def or(one, two)
-
"(#{one} or #{two})"
-
end
-
-
1
def one_of(current, values)
-
values.map { |value| "#{current} = #{value}" }.join(' or ')
-
end
-
-
1
def next_sibling(current, element_names)
-
if element_names.length == 1
-
"#{current}/following-sibling::*[1]/self::#{element_names.first}"
-
elsif element_names.length > 1
-
"#{current}/following-sibling::*[1]/self::*[#{element_names.map { |e| "self::#{e}" }.join(" | ")}]"
-
else
-
"#{current}/following-sibling::*[1]/self::*"
-
end
-
end
-
-
1
def previous_sibling(current, element_names)
-
if element_names.length == 1
-
"#{current}/preceding-sibling::*[1]/self::#{element_names.first}"
-
elsif element_names.length > 1
-
"#{current}/preceding-sibling::*[1]/self::*[#{element_names.map { |e| "self::#{e}" }.join(" | ")}]"
-
else
-
"#{current}/preceding-sibling::*[1]/self::*"
-
end
-
end
-
-
1
def inverse(current)
-
"not(#{current})"
-
end
-
-
1
def string_function(current)
-
"string(#{current})"
-
end
-
end
-
end
-
1
module XPath
-
1
class Union
-
1
include Enumerable
-
-
1
attr_reader :expressions
-
1
alias_method :arguments, :expressions
-
-
1
def initialize(*expressions)
-
@expressions = expressions
-
end
-
-
1
def expression
-
:union
-
end
-
-
1
def each(&block)
-
arguments.each(&block)
-
end
-
-
1
def method_missing(*args)
-
XPath::Union.new(*arguments.map { |e| e.send(*args) })
-
end
-
-
1
def to_xpath(type=nil)
-
Renderer.render(self, type)
-
end
-
1
alias_method :to_s, :to_xpath
-
end
-
end